1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17 package com.atlassian.util.concurrent;
18
19 import static org.junit.Assert.assertEquals;
20 import static org.junit.Assert.fail;
21
22 import org.junit.Test;
23
24 import java.util.concurrent.Callable;
25 import java.util.concurrent.CountDownLatch;
26 import java.util.concurrent.ExecutionException;
27 import java.util.concurrent.atomic.AtomicInteger;
28
29
30
31
32 public class ConcurrentOperationMapImplTest {
33
34 @Test public void runOperationsConcurrently() throws InterruptedException {
35 final AtomicInteger counter = new AtomicInteger(0);
36 final CountDownLatch runSignal = new CountDownLatch(2);
37 final CountDownLatch startSignal = new CountDownLatch(1);
38 final CountDownLatch doneSignal = new CountDownLatch(2);
39
40 final ConcurrentOperationMap<String, Integer> concurrentOperationMap = new ConcurrentOperationMapImpl<String, Integer>(new Function<Callable<Integer>, ConcurrentOperationMapImpl.CallerRunsFuture<Integer>>() {
41 public ConcurrentOperationMapImpl.CallerRunsFuture<Integer> get(final Callable<Integer> input) {
42 return new ConcurrentOperationMapImpl.CallerRunsFuture<Integer>(input) {
43 @Override public Integer get() throws ExecutionException {
44 runSignal.countDown();
45 try {
46 runSignal.await();
47 }
48 catch (final InterruptedException e) {
49 throw new RuntimeException(e);
50 }
51 return super.get();
52 }
53 };
54 }
55 });
56
57
58
59 new Thread(new Worker(startSignal, doneSignal) {
60 @Override void doWork() {
61 try {
62 assertEquals(Integer.valueOf(1), concurrentOperationMap.runOperation("same-key", new Callable<Integer>() {
63 public Integer call() {
64 return counter.incrementAndGet();
65 }
66 }));
67 }
68 catch (final ExecutionException e) {
69 fail(e.toString());
70 }
71 }
72 }).start();
73 new Thread(new Worker(startSignal, doneSignal) {
74 @Override void doWork() {
75 try {
76 assertEquals(Integer.valueOf(1), concurrentOperationMap.runOperation("same-key", new Callable<Integer>() {
77 public Integer call() {
78 return counter.incrementAndGet();
79 }
80 }));
81 }
82 catch (final ExecutionException e) {
83 fail(e.toString());
84 }
85 }
86 }).start();
87
88
89
90
91
92 startSignal.countDown();
93
94
95
96
97
98 doneSignal.await();
99 assertEquals(1, counter.get());
100 }
101
102 @Test public void exceptionsGetRemoved() throws Exception {
103 final AtomicInteger counter = new AtomicInteger();
104 final ConcurrentOperationMap<String, Integer> concurrentOperationMap = new ConcurrentOperationMapImpl<String, Integer>();
105
106
107
108
109 class MyException extends RuntimeException {
110 private static final long serialVersionUID = 1472906008827102127L;
111 }
112
113 final Callable<Integer> operation = new Callable<Integer>() {
114 public Integer call() {
115 counter.incrementAndGet();
116 throw new MyException();
117 }
118 };
119 try {
120 concurrentOperationMap.runOperation("same-key", operation);
121 fail("MyException expected");
122 }
123 catch (final MyException expected) {}
124 try {
125 concurrentOperationMap.runOperation("same-key", operation);
126 fail("MyException expected");
127 }
128 catch (final MyException expected) {}
129 try {
130 concurrentOperationMap.runOperation("same-key", operation);
131 fail("MyException expected");
132 }
133 catch (final MyException expected) {}
134
135 assertEquals(3, counter.get());
136 }
137
138 @Test public void runtimeExceptionGetsReThrown() throws Exception {
139 final ConcurrentOperationMap<String, Integer> concurrentOperationMap = new ConcurrentOperationMapImpl<String, Integer>();
140
141
142
143
144 class MyException extends RuntimeException {
145 private static final long serialVersionUID = -9171222011228163934L;
146 }
147
148 final Callable<Integer> operation = new Callable<Integer>() {
149 public Integer call() {
150 throw new MyException();
151 }
152 };
153 try {
154 concurrentOperationMap.runOperation("same-key", operation);
155 fail("MyException expected");
156 }
157 catch (final MyException expected) {}
158 }
159
160 @Test public void errorGetsReThrown() throws Exception {
161 final ConcurrentOperationMap<String, Integer> concurrentOperationMap = new ConcurrentOperationMapImpl<String, Integer>();
162
163
164
165
166 class MyError extends Error {
167 private static final long serialVersionUID = -1416631799712180762L;
168 }
169
170 final Callable<Integer> operation = new Callable<Integer>() {
171 public Integer call() {
172 throw new MyError();
173 }
174 };
175 try {
176 concurrentOperationMap.runOperation("same-key", operation);
177 fail("MyException expected");
178 }
179 catch (final MyError expected) {}
180 }
181
182 @Test public void checkedExceptionGetsWrapped() throws Exception {
183 final ConcurrentOperationMap<String, Integer> concurrentOperationMap = new ConcurrentOperationMapImpl<String, Integer>();
184
185
186
187
188 class MyException extends Exception {
189 private static final long serialVersionUID = 22367874459914044L;
190 }
191
192 final Callable<Integer> operation = new Callable<Integer>() {
193 public Integer call() throws MyException {
194 throw new MyException();
195 }
196 };
197 try {
198 concurrentOperationMap.runOperation("same-key", operation);
199 fail("MyException expected");
200 }
201 catch (final ExecutionException expected) {
202 assertEquals(MyException.class, expected.getCause().getClass());
203 }
204 }
205
206 abstract class Worker implements Runnable {
207 private final CountDownLatch startSignal;
208 private final CountDownLatch doneSignal;
209
210 Worker(final CountDownLatch startSignal, final CountDownLatch doneSignal) {
211 this.startSignal = startSignal;
212 this.doneSignal = doneSignal;
213 }
214
215 public void run() {
216 try {
217 startSignal.await();
218 doWork();
219 doneSignal.countDown();
220 }
221 catch (final InterruptedException ex) {}
222 }
223
224 abstract void doWork();
225 }
226 }