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