author | dl |
Sat, 14 Sep 2019 11:26:26 -0700 | |
changeset 58138 | 1e4270f875ee |
parent 51951 | 1239bfca87f8 |
permissions | -rw-r--r-- |
35394 | 1 |
/* |
2 |
* DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. |
|
3 |
* |
|
4 |
* This code is free software; you can redistribute it and/or modify it |
|
5 |
* under the terms of the GNU General Public License version 2 only, as |
|
6 |
* published by the Free Software Foundation. |
|
7 |
* |
|
8 |
* This code is distributed in the hope that it will be useful, but WITHOUT |
|
9 |
* ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or |
|
10 |
* FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License |
|
11 |
* version 2 for more details (a copy is included in the LICENSE file that |
|
12 |
* accompanied this code). |
|
13 |
* |
|
14 |
* You should have received a copy of the GNU General Public License version |
|
15 |
* 2 along with this work; if not, write to the Free Software Foundation, |
|
16 |
* Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. |
|
17 |
* |
|
18 |
* Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA |
|
19 |
* or visit www.oracle.com if you need additional information or have any |
|
20 |
* questions. |
|
21 |
*/ |
|
22 |
||
23 |
/* |
|
24 |
* This file is available under and governed by the GNU General Public |
|
25 |
* License version 2 only, as published by the Free Software Foundation. |
|
26 |
* However, the following notice accompanied the original version of this |
|
27 |
* file: |
|
28 |
* |
|
29 |
* Written by Doug Lea with assistance from members of JCP JSR-166 |
|
30 |
* Expert Group and released to the public domain, as explained at |
|
31 |
* http://creativecommons.org/publicdomain/zero/1.0/ |
|
32 |
* Other contributors include Andrew Wright, Jeffrey Hayes, |
|
33 |
* Pat Fisher, Mike Judd. |
|
34 |
*/ |
|
35 |
||
36 |
import static java.util.concurrent.TimeUnit.MILLISECONDS; |
|
37 |
import static java.util.concurrent.TimeUnit.NANOSECONDS; |
|
38 |
||
39 |
import java.util.ArrayList; |
|
40 |
import java.util.List; |
|
41 |
import java.util.NoSuchElementException; |
|
42 |
import java.util.concurrent.Callable; |
|
43 |
import java.util.concurrent.CancellationException; |
|
44 |
import java.util.concurrent.CountDownLatch; |
|
45 |
import java.util.concurrent.ExecutionException; |
|
46 |
import java.util.concurrent.Executors; |
|
47 |
import java.util.concurrent.ExecutorService; |
|
48 |
import java.util.concurrent.Future; |
|
49 |
import java.util.concurrent.FutureTask; |
|
50 |
import java.util.concurrent.TimeoutException; |
|
51 |
import java.util.concurrent.atomic.AtomicInteger; |
|
52 |
||
53 |
import junit.framework.Test; |
|
54 |
import junit.framework.TestSuite; |
|
55 |
||
56 |
public class FutureTaskTest extends JSR166TestCase { |
|
57 |
||
58 |
public static void main(String[] args) { |
|
59 |
main(suite(), args); |
|
60 |
} |
|
61 |
public static Test suite() { |
|
62 |
return new TestSuite(FutureTaskTest.class); |
|
63 |
} |
|
64 |
||
65 |
void checkIsDone(Future<?> f) { |
|
66 |
assertTrue(f.isDone()); |
|
67 |
assertFalse(f.cancel(false)); |
|
68 |
assertFalse(f.cancel(true)); |
|
69 |
if (f instanceof PublicFutureTask) { |
|
70 |
PublicFutureTask pf = (PublicFutureTask) f; |
|
71 |
assertEquals(1, pf.doneCount()); |
|
72 |
assertFalse(pf.runAndReset()); |
|
73 |
assertEquals(1, pf.doneCount()); |
|
74 |
Object r = null; Object exInfo = null; |
|
75 |
try { |
|
76 |
r = f.get(); |
|
77 |
} catch (CancellationException t) { |
|
78 |
exInfo = CancellationException.class; |
|
79 |
} catch (ExecutionException t) { |
|
80 |
exInfo = t.getCause(); |
|
81 |
} catch (Throwable t) { |
|
82 |
threadUnexpectedException(t); |
|
83 |
} |
|
84 |
||
85 |
// Check that run and runAndReset have no effect. |
|
86 |
int savedRunCount = pf.runCount(); |
|
87 |
pf.run(); |
|
88 |
pf.runAndReset(); |
|
89 |
assertEquals(savedRunCount, pf.runCount()); |
|
51951
1239bfca87f8
8207003: Miscellaneous changes imported from jsr166 CVS 2018-09
dl
parents:
48541
diff
changeset
|
90 |
Object r2 = null; |
35394 | 91 |
try { |
51951
1239bfca87f8
8207003: Miscellaneous changes imported from jsr166 CVS 2018-09
dl
parents:
48541
diff
changeset
|
92 |
r2 = f.get(); |
35394 | 93 |
} catch (CancellationException t) { |
94 |
assertSame(exInfo, CancellationException.class); |
|
95 |
} catch (ExecutionException t) { |
|
96 |
assertSame(exInfo, t.getCause()); |
|
97 |
} catch (Throwable t) { |
|
98 |
threadUnexpectedException(t); |
|
99 |
} |
|
51951
1239bfca87f8
8207003: Miscellaneous changes imported from jsr166 CVS 2018-09
dl
parents:
48541
diff
changeset
|
100 |
if (exInfo == null) |
1239bfca87f8
8207003: Miscellaneous changes imported from jsr166 CVS 2018-09
dl
parents:
48541
diff
changeset
|
101 |
assertSame(r, r2); |
35394 | 102 |
assertTrue(f.isDone()); |
103 |
} |
|
104 |
} |
|
105 |
||
106 |
void checkNotDone(Future<?> f) { |
|
107 |
assertFalse(f.isDone()); |
|
108 |
assertFalse(f.isCancelled()); |
|
109 |
if (f instanceof PublicFutureTask) { |
|
110 |
PublicFutureTask pf = (PublicFutureTask) f; |
|
111 |
assertEquals(0, pf.doneCount()); |
|
112 |
assertEquals(0, pf.setCount()); |
|
113 |
assertEquals(0, pf.setExceptionCount()); |
|
114 |
} |
|
115 |
} |
|
116 |
||
117 |
void checkIsRunning(Future<?> f) { |
|
118 |
checkNotDone(f); |
|
119 |
if (f instanceof FutureTask) { |
|
120 |
FutureTask ft = (FutureTask<?>) f; |
|
121 |
// Check that run methods do nothing |
|
122 |
ft.run(); |
|
123 |
if (f instanceof PublicFutureTask) { |
|
124 |
PublicFutureTask pf = (PublicFutureTask) f; |
|
125 |
int savedRunCount = pf.runCount(); |
|
126 |
pf.run(); |
|
127 |
assertFalse(pf.runAndReset()); |
|
128 |
assertEquals(savedRunCount, pf.runCount()); |
|
129 |
} |
|
130 |
checkNotDone(f); |
|
131 |
} |
|
132 |
} |
|
133 |
||
51951
1239bfca87f8
8207003: Miscellaneous changes imported from jsr166 CVS 2018-09
dl
parents:
48541
diff
changeset
|
134 |
<T> void checkCompletedNormally(Future<T> f, T expectedValue) { |
35394 | 135 |
checkIsDone(f); |
136 |
assertFalse(f.isCancelled()); |
|
137 |
||
51951
1239bfca87f8
8207003: Miscellaneous changes imported from jsr166 CVS 2018-09
dl
parents:
48541
diff
changeset
|
138 |
T v1 = null, v2 = null; |
35394 | 139 |
try { |
51951
1239bfca87f8
8207003: Miscellaneous changes imported from jsr166 CVS 2018-09
dl
parents:
48541
diff
changeset
|
140 |
v1 = f.get(); |
1239bfca87f8
8207003: Miscellaneous changes imported from jsr166 CVS 2018-09
dl
parents:
48541
diff
changeset
|
141 |
v2 = f.get(randomTimeout(), randomTimeUnit()); |
35394 | 142 |
} catch (Throwable fail) { threadUnexpectedException(fail); } |
51951
1239bfca87f8
8207003: Miscellaneous changes imported from jsr166 CVS 2018-09
dl
parents:
48541
diff
changeset
|
143 |
assertSame(expectedValue, v1); |
1239bfca87f8
8207003: Miscellaneous changes imported from jsr166 CVS 2018-09
dl
parents:
48541
diff
changeset
|
144 |
assertSame(expectedValue, v2); |
35394 | 145 |
} |
146 |
||
147 |
void checkCancelled(Future<?> f) { |
|
148 |
checkIsDone(f); |
|
149 |
assertTrue(f.isCancelled()); |
|
150 |
||
151 |
try { |
|
152 |
f.get(); |
|
153 |
shouldThrow(); |
|
154 |
} catch (CancellationException success) { |
|
155 |
} catch (Throwable fail) { threadUnexpectedException(fail); } |
|
156 |
||
157 |
try { |
|
45937
646816090183
8178409: Miscellaneous changes imported from jsr166 CVS 2017-07
dl
parents:
35394
diff
changeset
|
158 |
f.get(randomTimeout(), randomTimeUnit()); |
35394 | 159 |
shouldThrow(); |
160 |
} catch (CancellationException success) { |
|
161 |
} catch (Throwable fail) { threadUnexpectedException(fail); } |
|
162 |
} |
|
163 |
||
164 |
void tryToConfuseDoneTask(PublicFutureTask pf) { |
|
165 |
pf.set(new Object()); |
|
166 |
pf.setException(new Error()); |
|
167 |
for (boolean mayInterruptIfRunning : new boolean[] { true, false }) { |
|
168 |
pf.cancel(mayInterruptIfRunning); |
|
169 |
} |
|
170 |
} |
|
171 |
||
172 |
void checkCompletedAbnormally(Future<?> f, Throwable t) { |
|
173 |
checkIsDone(f); |
|
174 |
assertFalse(f.isCancelled()); |
|
175 |
||
176 |
try { |
|
177 |
f.get(); |
|
178 |
shouldThrow(); |
|
179 |
} catch (ExecutionException success) { |
|
180 |
assertSame(t, success.getCause()); |
|
181 |
} catch (Throwable fail) { threadUnexpectedException(fail); } |
|
182 |
||
183 |
try { |
|
45937
646816090183
8178409: Miscellaneous changes imported from jsr166 CVS 2017-07
dl
parents:
35394
diff
changeset
|
184 |
f.get(randomTimeout(), randomTimeUnit()); |
35394 | 185 |
shouldThrow(); |
186 |
} catch (ExecutionException success) { |
|
187 |
assertSame(t, success.getCause()); |
|
188 |
} catch (Throwable fail) { threadUnexpectedException(fail); } |
|
189 |
} |
|
190 |
||
191 |
/** |
|
192 |
* Subclass to expose protected methods |
|
193 |
*/ |
|
194 |
static class PublicFutureTask extends FutureTask { |
|
195 |
private final AtomicInteger runCount; |
|
196 |
private final AtomicInteger doneCount = new AtomicInteger(0); |
|
197 |
private final AtomicInteger runAndResetCount = new AtomicInteger(0); |
|
198 |
private final AtomicInteger setCount = new AtomicInteger(0); |
|
199 |
private final AtomicInteger setExceptionCount = new AtomicInteger(0); |
|
200 |
public int runCount() { return runCount.get(); } |
|
201 |
public int doneCount() { return doneCount.get(); } |
|
202 |
public int runAndResetCount() { return runAndResetCount.get(); } |
|
203 |
public int setCount() { return setCount.get(); } |
|
204 |
public int setExceptionCount() { return setExceptionCount.get(); } |
|
205 |
||
206 |
PublicFutureTask(Runnable runnable) { |
|
207 |
this(runnable, seven); |
|
208 |
} |
|
209 |
PublicFutureTask(Runnable runnable, Object result) { |
|
210 |
this(runnable, result, new AtomicInteger(0)); |
|
211 |
} |
|
212 |
private PublicFutureTask(final Runnable runnable, Object result, |
|
213 |
final AtomicInteger runCount) { |
|
214 |
super(new Runnable() { |
|
215 |
public void run() { |
|
216 |
runCount.getAndIncrement(); |
|
217 |
runnable.run(); |
|
218 |
}}, result); |
|
219 |
this.runCount = runCount; |
|
220 |
} |
|
221 |
PublicFutureTask(Callable callable) { |
|
222 |
this(callable, new AtomicInteger(0)); |
|
223 |
} |
|
224 |
private PublicFutureTask(final Callable callable, |
|
225 |
final AtomicInteger runCount) { |
|
226 |
super(new Callable() { |
|
227 |
public Object call() throws Exception { |
|
228 |
runCount.getAndIncrement(); |
|
229 |
return callable.call(); |
|
230 |
}}); |
|
231 |
this.runCount = runCount; |
|
232 |
} |
|
233 |
@Override public void done() { |
|
234 |
assertTrue(isDone()); |
|
235 |
doneCount.incrementAndGet(); |
|
236 |
super.done(); |
|
237 |
} |
|
238 |
@Override public boolean runAndReset() { |
|
239 |
runAndResetCount.incrementAndGet(); |
|
240 |
return super.runAndReset(); |
|
241 |
} |
|
242 |
@Override public void set(Object x) { |
|
243 |
setCount.incrementAndGet(); |
|
244 |
super.set(x); |
|
245 |
} |
|
246 |
@Override public void setException(Throwable t) { |
|
247 |
setExceptionCount.incrementAndGet(); |
|
248 |
super.setException(t); |
|
249 |
} |
|
250 |
} |
|
251 |
||
252 |
class Counter extends CheckedRunnable { |
|
253 |
final AtomicInteger count = new AtomicInteger(0); |
|
254 |
public int get() { return count.get(); } |
|
255 |
public void realRun() { |
|
256 |
count.getAndIncrement(); |
|
257 |
} |
|
258 |
} |
|
259 |
||
260 |
/** |
|
261 |
* creating a future with a null callable throws NullPointerException |
|
262 |
*/ |
|
263 |
public void testConstructor() { |
|
264 |
try { |
|
265 |
new FutureTask(null); |
|
266 |
shouldThrow(); |
|
267 |
} catch (NullPointerException success) {} |
|
268 |
} |
|
269 |
||
270 |
/** |
|
271 |
* creating a future with null runnable throws NullPointerException |
|
272 |
*/ |
|
273 |
public void testConstructor2() { |
|
274 |
try { |
|
275 |
new FutureTask(null, Boolean.TRUE); |
|
276 |
shouldThrow(); |
|
277 |
} catch (NullPointerException success) {} |
|
278 |
} |
|
279 |
||
280 |
/** |
|
281 |
* isDone is true when a task completes |
|
282 |
*/ |
|
283 |
public void testIsDone() { |
|
284 |
PublicFutureTask task = new PublicFutureTask(new NoOpCallable()); |
|
285 |
assertFalse(task.isDone()); |
|
286 |
task.run(); |
|
287 |
assertTrue(task.isDone()); |
|
288 |
checkCompletedNormally(task, Boolean.TRUE); |
|
289 |
assertEquals(1, task.runCount()); |
|
290 |
} |
|
291 |
||
292 |
/** |
|
293 |
* runAndReset of a non-cancelled task succeeds |
|
294 |
*/ |
|
295 |
public void testRunAndReset() { |
|
296 |
PublicFutureTask task = new PublicFutureTask(new NoOpCallable()); |
|
297 |
for (int i = 0; i < 3; i++) { |
|
298 |
assertTrue(task.runAndReset()); |
|
299 |
checkNotDone(task); |
|
300 |
assertEquals(i + 1, task.runCount()); |
|
301 |
assertEquals(i + 1, task.runAndResetCount()); |
|
302 |
assertEquals(0, task.setCount()); |
|
303 |
assertEquals(0, task.setExceptionCount()); |
|
304 |
} |
|
305 |
} |
|
306 |
||
307 |
/** |
|
308 |
* runAndReset after cancellation fails |
|
309 |
*/ |
|
310 |
public void testRunAndResetAfterCancel() { |
|
311 |
for (boolean mayInterruptIfRunning : new boolean[] { true, false }) { |
|
312 |
PublicFutureTask task = new PublicFutureTask(new NoOpCallable()); |
|
313 |
assertTrue(task.cancel(mayInterruptIfRunning)); |
|
314 |
for (int i = 0; i < 3; i++) { |
|
315 |
assertFalse(task.runAndReset()); |
|
316 |
assertEquals(0, task.runCount()); |
|
317 |
assertEquals(i + 1, task.runAndResetCount()); |
|
318 |
assertEquals(0, task.setCount()); |
|
319 |
assertEquals(0, task.setExceptionCount()); |
|
320 |
} |
|
321 |
tryToConfuseDoneTask(task); |
|
322 |
checkCancelled(task); |
|
323 |
} |
|
324 |
} |
|
325 |
||
326 |
/** |
|
327 |
* setting value causes get to return it |
|
328 |
*/ |
|
329 |
public void testSet() throws Exception { |
|
330 |
PublicFutureTask task = new PublicFutureTask(new NoOpCallable()); |
|
331 |
task.set(one); |
|
332 |
for (int i = 0; i < 3; i++) { |
|
333 |
assertSame(one, task.get()); |
|
334 |
assertSame(one, task.get(LONG_DELAY_MS, MILLISECONDS)); |
|
335 |
assertEquals(1, task.setCount()); |
|
336 |
} |
|
337 |
tryToConfuseDoneTask(task); |
|
338 |
checkCompletedNormally(task, one); |
|
339 |
assertEquals(0, task.runCount()); |
|
340 |
} |
|
341 |
||
342 |
/** |
|
343 |
* setException causes get to throw ExecutionException |
|
344 |
*/ |
|
345 |
public void testSetException_get() throws Exception { |
|
346 |
Exception nse = new NoSuchElementException(); |
|
347 |
PublicFutureTask task = new PublicFutureTask(new NoOpCallable()); |
|
348 |
task.setException(nse); |
|
349 |
||
350 |
try { |
|
351 |
task.get(); |
|
352 |
shouldThrow(); |
|
353 |
} catch (ExecutionException success) { |
|
354 |
assertSame(nse, success.getCause()); |
|
355 |
checkCompletedAbnormally(task, nse); |
|
356 |
} |
|
357 |
||
358 |
try { |
|
359 |
task.get(LONG_DELAY_MS, MILLISECONDS); |
|
360 |
shouldThrow(); |
|
361 |
} catch (ExecutionException success) { |
|
362 |
assertSame(nse, success.getCause()); |
|
363 |
checkCompletedAbnormally(task, nse); |
|
364 |
} |
|
365 |
||
366 |
assertEquals(1, task.setExceptionCount()); |
|
367 |
assertEquals(0, task.setCount()); |
|
368 |
tryToConfuseDoneTask(task); |
|
369 |
checkCompletedAbnormally(task, nse); |
|
370 |
assertEquals(0, task.runCount()); |
|
371 |
} |
|
372 |
||
373 |
/** |
|
374 |
* cancel(false) before run succeeds |
|
375 |
*/ |
|
376 |
public void testCancelBeforeRun() { |
|
377 |
PublicFutureTask task = new PublicFutureTask(new NoOpCallable()); |
|
378 |
assertTrue(task.cancel(false)); |
|
379 |
task.run(); |
|
380 |
assertEquals(0, task.runCount()); |
|
381 |
assertEquals(0, task.setCount()); |
|
382 |
assertEquals(0, task.setExceptionCount()); |
|
383 |
assertTrue(task.isCancelled()); |
|
384 |
assertTrue(task.isDone()); |
|
385 |
tryToConfuseDoneTask(task); |
|
386 |
assertEquals(0, task.runCount()); |
|
387 |
checkCancelled(task); |
|
388 |
} |
|
389 |
||
390 |
/** |
|
391 |
* cancel(true) before run succeeds |
|
392 |
*/ |
|
393 |
public void testCancelBeforeRun2() { |
|
394 |
PublicFutureTask task = new PublicFutureTask(new NoOpCallable()); |
|
395 |
assertTrue(task.cancel(true)); |
|
396 |
task.run(); |
|
397 |
assertEquals(0, task.runCount()); |
|
398 |
assertEquals(0, task.setCount()); |
|
399 |
assertEquals(0, task.setExceptionCount()); |
|
400 |
assertTrue(task.isCancelled()); |
|
401 |
assertTrue(task.isDone()); |
|
402 |
tryToConfuseDoneTask(task); |
|
403 |
assertEquals(0, task.runCount()); |
|
404 |
checkCancelled(task); |
|
405 |
} |
|
406 |
||
407 |
/** |
|
408 |
* cancel(false) of a completed task fails |
|
409 |
*/ |
|
410 |
public void testCancelAfterRun() { |
|
411 |
PublicFutureTask task = new PublicFutureTask(new NoOpCallable()); |
|
412 |
task.run(); |
|
413 |
assertFalse(task.cancel(false)); |
|
414 |
assertEquals(1, task.runCount()); |
|
415 |
assertEquals(1, task.setCount()); |
|
416 |
assertEquals(0, task.setExceptionCount()); |
|
417 |
tryToConfuseDoneTask(task); |
|
418 |
checkCompletedNormally(task, Boolean.TRUE); |
|
419 |
assertEquals(1, task.runCount()); |
|
420 |
} |
|
421 |
||
422 |
/** |
|
423 |
* cancel(true) of a completed task fails |
|
424 |
*/ |
|
425 |
public void testCancelAfterRun2() { |
|
426 |
PublicFutureTask task = new PublicFutureTask(new NoOpCallable()); |
|
427 |
task.run(); |
|
428 |
assertFalse(task.cancel(true)); |
|
429 |
assertEquals(1, task.runCount()); |
|
430 |
assertEquals(1, task.setCount()); |
|
431 |
assertEquals(0, task.setExceptionCount()); |
|
432 |
tryToConfuseDoneTask(task); |
|
433 |
checkCompletedNormally(task, Boolean.TRUE); |
|
434 |
assertEquals(1, task.runCount()); |
|
435 |
} |
|
436 |
||
437 |
/** |
|
438 |
* cancel(true) interrupts a running task that subsequently succeeds |
|
439 |
*/ |
|
440 |
public void testCancelInterrupt() { |
|
441 |
final CountDownLatch pleaseCancel = new CountDownLatch(1); |
|
442 |
final PublicFutureTask task = |
|
443 |
new PublicFutureTask(new CheckedRunnable() { |
|
444 |
public void realRun() { |
|
445 |
pleaseCancel.countDown(); |
|
446 |
try { |
|
447 |
delay(LONG_DELAY_MS); |
|
448 |
shouldThrow(); |
|
449 |
} catch (InterruptedException success) {} |
|
45937
646816090183
8178409: Miscellaneous changes imported from jsr166 CVS 2017-07
dl
parents:
35394
diff
changeset
|
450 |
assertFalse(Thread.interrupted()); |
35394 | 451 |
}}); |
452 |
||
453 |
Thread t = newStartedThread(task); |
|
454 |
await(pleaseCancel); |
|
455 |
assertTrue(task.cancel(true)); |
|
456 |
assertTrue(task.isCancelled()); |
|
457 |
assertTrue(task.isDone()); |
|
458 |
awaitTermination(t); |
|
459 |
assertEquals(1, task.runCount()); |
|
460 |
assertEquals(1, task.setCount()); |
|
461 |
assertEquals(0, task.setExceptionCount()); |
|
462 |
tryToConfuseDoneTask(task); |
|
463 |
checkCancelled(task); |
|
464 |
} |
|
465 |
||
466 |
/** |
|
467 |
* cancel(true) tries to interrupt a running task, but |
|
468 |
* Thread.interrupt throws (simulating a restrictive security |
|
469 |
* manager) |
|
470 |
*/ |
|
471 |
public void testCancelInterrupt_ThrowsSecurityException() { |
|
472 |
final CountDownLatch pleaseCancel = new CountDownLatch(1); |
|
473 |
final CountDownLatch cancelled = new CountDownLatch(1); |
|
474 |
final PublicFutureTask task = |
|
475 |
new PublicFutureTask(new CheckedRunnable() { |
|
476 |
public void realRun() { |
|
477 |
pleaseCancel.countDown(); |
|
478 |
await(cancelled); |
|
479 |
assertFalse(Thread.interrupted()); |
|
480 |
}}); |
|
481 |
||
482 |
final Thread t = new Thread(task) { |
|
483 |
// Simulate a restrictive security manager. |
|
484 |
@Override public void interrupt() { |
|
485 |
throw new SecurityException(); |
|
486 |
}}; |
|
487 |
t.setDaemon(true); |
|
488 |
t.start(); |
|
489 |
||
490 |
await(pleaseCancel); |
|
491 |
try { |
|
492 |
task.cancel(true); |
|
493 |
shouldThrow(); |
|
51951
1239bfca87f8
8207003: Miscellaneous changes imported from jsr166 CVS 2018-09
dl
parents:
48541
diff
changeset
|
494 |
} catch (SecurityException success) {} |
35394 | 495 |
|
496 |
// We failed to deliver the interrupt, but the world retains |
|
497 |
// its sanity, as if we had done task.cancel(false) |
|
498 |
assertTrue(task.isCancelled()); |
|
499 |
assertTrue(task.isDone()); |
|
500 |
assertEquals(1, task.runCount()); |
|
501 |
assertEquals(1, task.doneCount()); |
|
502 |
assertEquals(0, task.setCount()); |
|
503 |
assertEquals(0, task.setExceptionCount()); |
|
504 |
cancelled.countDown(); |
|
505 |
awaitTermination(t); |
|
506 |
assertEquals(1, task.setCount()); |
|
507 |
assertEquals(0, task.setExceptionCount()); |
|
508 |
tryToConfuseDoneTask(task); |
|
509 |
checkCancelled(task); |
|
510 |
} |
|
511 |
||
512 |
/** |
|
513 |
* cancel(true) interrupts a running task that subsequently throws |
|
514 |
*/ |
|
515 |
public void testCancelInterrupt_taskFails() { |
|
516 |
final CountDownLatch pleaseCancel = new CountDownLatch(1); |
|
517 |
final PublicFutureTask task = |
|
518 |
new PublicFutureTask(new Runnable() { |
|
519 |
public void run() { |
|
520 |
pleaseCancel.countDown(); |
|
521 |
try { |
|
522 |
delay(LONG_DELAY_MS); |
|
523 |
threadShouldThrow(); |
|
524 |
} catch (InterruptedException success) { |
|
525 |
} catch (Throwable t) { threadUnexpectedException(t); } |
|
526 |
throw new RuntimeException(); |
|
527 |
}}); |
|
528 |
||
529 |
Thread t = newStartedThread(task); |
|
530 |
await(pleaseCancel); |
|
531 |
assertTrue(task.cancel(true)); |
|
532 |
assertTrue(task.isCancelled()); |
|
533 |
awaitTermination(t); |
|
534 |
assertEquals(1, task.runCount()); |
|
535 |
assertEquals(0, task.setCount()); |
|
536 |
assertEquals(1, task.setExceptionCount()); |
|
537 |
tryToConfuseDoneTask(task); |
|
538 |
checkCancelled(task); |
|
539 |
} |
|
540 |
||
541 |
/** |
|
542 |
* cancel(false) does not interrupt a running task |
|
543 |
*/ |
|
544 |
public void testCancelNoInterrupt() { |
|
545 |
final CountDownLatch pleaseCancel = new CountDownLatch(1); |
|
546 |
final CountDownLatch cancelled = new CountDownLatch(1); |
|
547 |
final PublicFutureTask task = |
|
548 |
new PublicFutureTask(new CheckedCallable<Boolean>() { |
|
549 |
public Boolean realCall() { |
|
550 |
pleaseCancel.countDown(); |
|
551 |
await(cancelled); |
|
552 |
assertFalse(Thread.interrupted()); |
|
553 |
return Boolean.TRUE; |
|
554 |
}}); |
|
555 |
||
556 |
Thread t = newStartedThread(task); |
|
557 |
await(pleaseCancel); |
|
558 |
assertTrue(task.cancel(false)); |
|
559 |
assertTrue(task.isCancelled()); |
|
560 |
cancelled.countDown(); |
|
561 |
awaitTermination(t); |
|
562 |
assertEquals(1, task.runCount()); |
|
563 |
assertEquals(1, task.setCount()); |
|
564 |
assertEquals(0, task.setExceptionCount()); |
|
565 |
tryToConfuseDoneTask(task); |
|
566 |
checkCancelled(task); |
|
567 |
} |
|
568 |
||
569 |
/** |
|
570 |
* run in one thread causes get in another thread to retrieve value |
|
571 |
*/ |
|
572 |
public void testGetRun() { |
|
573 |
final CountDownLatch pleaseRun = new CountDownLatch(2); |
|
574 |
||
575 |
final PublicFutureTask task = |
|
576 |
new PublicFutureTask(new CheckedCallable<Object>() { |
|
577 |
public Object realCall() { |
|
578 |
return two; |
|
579 |
}}); |
|
580 |
||
581 |
Thread t1 = newStartedThread(new CheckedRunnable() { |
|
582 |
public void realRun() throws Exception { |
|
583 |
pleaseRun.countDown(); |
|
584 |
assertSame(two, task.get()); |
|
585 |
}}); |
|
586 |
||
587 |
Thread t2 = newStartedThread(new CheckedRunnable() { |
|
588 |
public void realRun() throws Exception { |
|
589 |
pleaseRun.countDown(); |
|
590 |
assertSame(two, task.get(2*LONG_DELAY_MS, MILLISECONDS)); |
|
591 |
}}); |
|
592 |
||
593 |
await(pleaseRun); |
|
594 |
checkNotDone(task); |
|
595 |
assertTrue(t1.isAlive()); |
|
596 |
assertTrue(t2.isAlive()); |
|
597 |
task.run(); |
|
598 |
checkCompletedNormally(task, two); |
|
599 |
assertEquals(1, task.runCount()); |
|
600 |
assertEquals(1, task.setCount()); |
|
601 |
assertEquals(0, task.setExceptionCount()); |
|
602 |
awaitTermination(t1); |
|
603 |
awaitTermination(t2); |
|
604 |
tryToConfuseDoneTask(task); |
|
605 |
checkCompletedNormally(task, two); |
|
606 |
} |
|
607 |
||
608 |
/** |
|
609 |
* set in one thread causes get in another thread to retrieve value |
|
610 |
*/ |
|
611 |
public void testGetSet() { |
|
612 |
final CountDownLatch pleaseSet = new CountDownLatch(2); |
|
613 |
||
614 |
final PublicFutureTask task = |
|
615 |
new PublicFutureTask(new CheckedCallable<Object>() { |
|
616 |
public Object realCall() throws InterruptedException { |
|
617 |
return two; |
|
618 |
}}); |
|
619 |
||
620 |
Thread t1 = newStartedThread(new CheckedRunnable() { |
|
621 |
public void realRun() throws Exception { |
|
622 |
pleaseSet.countDown(); |
|
623 |
assertSame(two, task.get()); |
|
624 |
}}); |
|
625 |
||
626 |
Thread t2 = newStartedThread(new CheckedRunnable() { |
|
627 |
public void realRun() throws Exception { |
|
628 |
pleaseSet.countDown(); |
|
629 |
assertSame(two, task.get(2*LONG_DELAY_MS, MILLISECONDS)); |
|
630 |
}}); |
|
631 |
||
632 |
await(pleaseSet); |
|
633 |
checkNotDone(task); |
|
634 |
assertTrue(t1.isAlive()); |
|
635 |
assertTrue(t2.isAlive()); |
|
636 |
task.set(two); |
|
637 |
assertEquals(0, task.runCount()); |
|
638 |
assertEquals(1, task.setCount()); |
|
639 |
assertEquals(0, task.setExceptionCount()); |
|
640 |
tryToConfuseDoneTask(task); |
|
641 |
checkCompletedNormally(task, two); |
|
642 |
awaitTermination(t1); |
|
643 |
awaitTermination(t2); |
|
644 |
} |
|
645 |
||
646 |
/** |
|
647 |
* Cancelling a task causes timed get in another thread to throw |
|
648 |
* CancellationException |
|
649 |
*/ |
|
650 |
public void testTimedGet_Cancellation() { |
|
651 |
testTimedGet_Cancellation(false); |
|
652 |
} |
|
653 |
public void testTimedGet_Cancellation_interrupt() { |
|
654 |
testTimedGet_Cancellation(true); |
|
655 |
} |
|
656 |
public void testTimedGet_Cancellation(final boolean mayInterruptIfRunning) { |
|
657 |
final CountDownLatch pleaseCancel = new CountDownLatch(3); |
|
658 |
final CountDownLatch cancelled = new CountDownLatch(1); |
|
659 |
final Callable<Object> callable = |
|
660 |
new CheckedCallable<Object>() { |
|
661 |
public Object realCall() throws InterruptedException { |
|
662 |
pleaseCancel.countDown(); |
|
663 |
if (mayInterruptIfRunning) { |
|
664 |
try { |
|
665 |
delay(2*LONG_DELAY_MS); |
|
666 |
} catch (InterruptedException success) {} |
|
667 |
} else { |
|
668 |
await(cancelled); |
|
669 |
} |
|
670 |
return two; |
|
671 |
}}; |
|
672 |
final PublicFutureTask task = new PublicFutureTask(callable); |
|
673 |
||
674 |
Thread t1 = new ThreadShouldThrow(CancellationException.class) { |
|
675 |
public void realRun() throws Exception { |
|
676 |
pleaseCancel.countDown(); |
|
677 |
task.get(); |
|
678 |
}}; |
|
679 |
Thread t2 = new ThreadShouldThrow(CancellationException.class) { |
|
680 |
public void realRun() throws Exception { |
|
681 |
pleaseCancel.countDown(); |
|
682 |
task.get(2*LONG_DELAY_MS, MILLISECONDS); |
|
683 |
}}; |
|
684 |
t1.start(); |
|
685 |
t2.start(); |
|
686 |
Thread t3 = newStartedThread(task); |
|
687 |
await(pleaseCancel); |
|
688 |
checkIsRunning(task); |
|
689 |
task.cancel(mayInterruptIfRunning); |
|
690 |
checkCancelled(task); |
|
691 |
awaitTermination(t1); |
|
692 |
awaitTermination(t2); |
|
693 |
cancelled.countDown(); |
|
694 |
awaitTermination(t3); |
|
695 |
assertEquals(1, task.runCount()); |
|
696 |
assertEquals(1, task.setCount()); |
|
697 |
assertEquals(0, task.setExceptionCount()); |
|
698 |
tryToConfuseDoneTask(task); |
|
699 |
checkCancelled(task); |
|
700 |
} |
|
701 |
||
702 |
/** |
|
703 |
* A runtime exception in task causes get to throw ExecutionException |
|
704 |
*/ |
|
705 |
public void testGet_ExecutionException() throws InterruptedException { |
|
706 |
final ArithmeticException e = new ArithmeticException(); |
|
707 |
final PublicFutureTask task = new PublicFutureTask(new Callable() { |
|
708 |
public Object call() { |
|
709 |
throw e; |
|
710 |
}}); |
|
711 |
||
712 |
task.run(); |
|
713 |
assertEquals(1, task.runCount()); |
|
714 |
assertEquals(0, task.setCount()); |
|
715 |
assertEquals(1, task.setExceptionCount()); |
|
716 |
try { |
|
717 |
task.get(); |
|
718 |
shouldThrow(); |
|
719 |
} catch (ExecutionException success) { |
|
720 |
assertSame(e, success.getCause()); |
|
721 |
tryToConfuseDoneTask(task); |
|
722 |
checkCompletedAbnormally(task, success.getCause()); |
|
723 |
} |
|
724 |
} |
|
725 |
||
726 |
/** |
|
727 |
* A runtime exception in task causes timed get to throw ExecutionException |
|
728 |
*/ |
|
729 |
public void testTimedGet_ExecutionException2() throws Exception { |
|
730 |
final ArithmeticException e = new ArithmeticException(); |
|
731 |
final PublicFutureTask task = new PublicFutureTask(new Callable() { |
|
732 |
public Object call() { |
|
733 |
throw e; |
|
734 |
}}); |
|
735 |
||
736 |
task.run(); |
|
737 |
try { |
|
738 |
task.get(LONG_DELAY_MS, MILLISECONDS); |
|
739 |
shouldThrow(); |
|
740 |
} catch (ExecutionException success) { |
|
741 |
assertSame(e, success.getCause()); |
|
742 |
tryToConfuseDoneTask(task); |
|
743 |
checkCompletedAbnormally(task, success.getCause()); |
|
744 |
} |
|
745 |
} |
|
746 |
||
747 |
/** |
|
748 |
* get is interruptible |
|
749 |
*/ |
|
58138
1e4270f875ee
8225490: Miscellaneous changes imported from jsr166 CVS 2019-09
dl
parents:
51951
diff
changeset
|
750 |
public void testGet_Interruptible() { |
35394 | 751 |
final CountDownLatch pleaseInterrupt = new CountDownLatch(1); |
752 |
final FutureTask task = new FutureTask(new NoOpCallable()); |
|
753 |
Thread t = newStartedThread(new CheckedRunnable() { |
|
754 |
public void realRun() throws Exception { |
|
755 |
Thread.currentThread().interrupt(); |
|
756 |
try { |
|
757 |
task.get(); |
|
758 |
shouldThrow(); |
|
759 |
} catch (InterruptedException success) {} |
|
760 |
assertFalse(Thread.interrupted()); |
|
761 |
||
762 |
pleaseInterrupt.countDown(); |
|
763 |
try { |
|
764 |
task.get(); |
|
765 |
shouldThrow(); |
|
766 |
} catch (InterruptedException success) {} |
|
767 |
assertFalse(Thread.interrupted()); |
|
768 |
}}); |
|
769 |
||
770 |
await(pleaseInterrupt); |
|
771 |
t.interrupt(); |
|
772 |
awaitTermination(t); |
|
773 |
checkNotDone(task); |
|
774 |
} |
|
775 |
||
776 |
/** |
|
777 |
* timed get is interruptible |
|
778 |
*/ |
|
58138
1e4270f875ee
8225490: Miscellaneous changes imported from jsr166 CVS 2019-09
dl
parents:
51951
diff
changeset
|
779 |
public void testTimedGet_Interruptible() { |
35394 | 780 |
final CountDownLatch pleaseInterrupt = new CountDownLatch(1); |
781 |
final FutureTask task = new FutureTask(new NoOpCallable()); |
|
782 |
Thread t = newStartedThread(new CheckedRunnable() { |
|
783 |
public void realRun() throws Exception { |
|
784 |
Thread.currentThread().interrupt(); |
|
785 |
try { |
|
58138
1e4270f875ee
8225490: Miscellaneous changes imported from jsr166 CVS 2019-09
dl
parents:
51951
diff
changeset
|
786 |
task.get(randomTimeout(), randomTimeUnit()); |
35394 | 787 |
shouldThrow(); |
788 |
} catch (InterruptedException success) {} |
|
789 |
assertFalse(Thread.interrupted()); |
|
790 |
||
791 |
pleaseInterrupt.countDown(); |
|
792 |
try { |
|
58138
1e4270f875ee
8225490: Miscellaneous changes imported from jsr166 CVS 2019-09
dl
parents:
51951
diff
changeset
|
793 |
task.get(LONGER_DELAY_MS, MILLISECONDS); |
35394 | 794 |
shouldThrow(); |
795 |
} catch (InterruptedException success) {} |
|
796 |
assertFalse(Thread.interrupted()); |
|
797 |
}}); |
|
798 |
||
799 |
await(pleaseInterrupt); |
|
58138
1e4270f875ee
8225490: Miscellaneous changes imported from jsr166 CVS 2019-09
dl
parents:
51951
diff
changeset
|
800 |
if (randomBoolean()) assertThreadBlocks(t, Thread.State.TIMED_WAITING); |
35394 | 801 |
t.interrupt(); |
802 |
awaitTermination(t); |
|
803 |
checkNotDone(task); |
|
804 |
} |
|
805 |
||
806 |
/** |
|
807 |
* A timed out timed get throws TimeoutException |
|
808 |
*/ |
|
809 |
public void testGet_TimeoutException() throws Exception { |
|
810 |
FutureTask task = new FutureTask(new NoOpCallable()); |
|
811 |
long startTime = System.nanoTime(); |
|
812 |
try { |
|
813 |
task.get(timeoutMillis(), MILLISECONDS); |
|
814 |
shouldThrow(); |
|
815 |
} catch (TimeoutException success) { |
|
816 |
assertTrue(millisElapsedSince(startTime) >= timeoutMillis()); |
|
817 |
} |
|
818 |
} |
|
819 |
||
820 |
/** |
|
821 |
* timed get with null TimeUnit throws NullPointerException |
|
822 |
*/ |
|
823 |
public void testGet_NullTimeUnit() throws Exception { |
|
824 |
FutureTask task = new FutureTask(new NoOpCallable()); |
|
825 |
long[] timeouts = { Long.MIN_VALUE, 0L, Long.MAX_VALUE }; |
|
826 |
||
827 |
for (long timeout : timeouts) { |
|
828 |
try { |
|
829 |
task.get(timeout, null); |
|
830 |
shouldThrow(); |
|
831 |
} catch (NullPointerException success) {} |
|
832 |
} |
|
833 |
||
834 |
task.run(); |
|
835 |
||
836 |
for (long timeout : timeouts) { |
|
837 |
try { |
|
838 |
task.get(timeout, null); |
|
839 |
shouldThrow(); |
|
840 |
} catch (NullPointerException success) {} |
|
841 |
} |
|
842 |
} |
|
843 |
||
844 |
/** |
|
845 |
* timed get with most negative timeout works correctly (i.e. no |
|
846 |
* underflow bug) |
|
847 |
*/ |
|
848 |
public void testGet_NegativeInfinityTimeout() throws Exception { |
|
849 |
final ExecutorService pool = Executors.newFixedThreadPool(10); |
|
850 |
final Runnable nop = new Runnable() { public void run() {}}; |
|
851 |
final FutureTask<Void> task = new FutureTask<>(nop, null); |
|
852 |
final List<Future<?>> futures = new ArrayList<>(); |
|
853 |
Runnable r = new Runnable() { public void run() { |
|
854 |
for (long timeout : new long[] { 0L, -1L, Long.MIN_VALUE }) { |
|
855 |
try { |
|
856 |
task.get(timeout, NANOSECONDS); |
|
857 |
shouldThrow(); |
|
858 |
} catch (TimeoutException success) { |
|
859 |
} catch (Throwable fail) {threadUnexpectedException(fail);}}}}; |
|
860 |
for (int i = 0; i < 10; i++) |
|
861 |
futures.add(pool.submit(r)); |
|
862 |
try { |
|
863 |
joinPool(pool); |
|
864 |
for (Future<?> future : futures) |
|
865 |
checkCompletedNormally(future, null); |
|
866 |
} finally { |
|
867 |
task.run(); // last resort to help terminate |
|
868 |
} |
|
869 |
} |
|
870 |
||
47306
90b7465b9ac7
8186265: Make toString() methods of "task" objects more useful
dl
parents:
47216
diff
changeset
|
871 |
/** |
90b7465b9ac7
8186265: Make toString() methods of "task" objects more useful
dl
parents:
47216
diff
changeset
|
872 |
* toString indicates current completion state |
90b7465b9ac7
8186265: Make toString() methods of "task" objects more useful
dl
parents:
47216
diff
changeset
|
873 |
*/ |
90b7465b9ac7
8186265: Make toString() methods of "task" objects more useful
dl
parents:
47216
diff
changeset
|
874 |
public void testToString_incomplete() { |
48541
946e34c2dec9
8193300: Miscellaneous changes imported from jsr166 CVS 2018-01
dl
parents:
47306
diff
changeset
|
875 |
FutureTask<String> f = new FutureTask<>(() -> ""); |
47306
90b7465b9ac7
8186265: Make toString() methods of "task" objects more useful
dl
parents:
47216
diff
changeset
|
876 |
assertTrue(f.toString().matches(".*\\[.*Not completed.*\\]")); |
90b7465b9ac7
8186265: Make toString() methods of "task" objects more useful
dl
parents:
47216
diff
changeset
|
877 |
if (testImplementationDetails) |
90b7465b9ac7
8186265: Make toString() methods of "task" objects more useful
dl
parents:
47216
diff
changeset
|
878 |
assertTrue(f.toString().startsWith( |
90b7465b9ac7
8186265: Make toString() methods of "task" objects more useful
dl
parents:
47216
diff
changeset
|
879 |
identityString(f) + "[Not completed, task =")); |
90b7465b9ac7
8186265: Make toString() methods of "task" objects more useful
dl
parents:
47216
diff
changeset
|
880 |
} |
90b7465b9ac7
8186265: Make toString() methods of "task" objects more useful
dl
parents:
47216
diff
changeset
|
881 |
|
90b7465b9ac7
8186265: Make toString() methods of "task" objects more useful
dl
parents:
47216
diff
changeset
|
882 |
public void testToString_normal() { |
48541
946e34c2dec9
8193300: Miscellaneous changes imported from jsr166 CVS 2018-01
dl
parents:
47306
diff
changeset
|
883 |
FutureTask<String> f = new FutureTask<>(() -> ""); |
47306
90b7465b9ac7
8186265: Make toString() methods of "task" objects more useful
dl
parents:
47216
diff
changeset
|
884 |
f.run(); |
90b7465b9ac7
8186265: Make toString() methods of "task" objects more useful
dl
parents:
47216
diff
changeset
|
885 |
assertTrue(f.toString().matches(".*\\[.*Completed normally.*\\]")); |
90b7465b9ac7
8186265: Make toString() methods of "task" objects more useful
dl
parents:
47216
diff
changeset
|
886 |
if (testImplementationDetails) |
90b7465b9ac7
8186265: Make toString() methods of "task" objects more useful
dl
parents:
47216
diff
changeset
|
887 |
assertEquals(identityString(f) + "[Completed normally]", |
90b7465b9ac7
8186265: Make toString() methods of "task" objects more useful
dl
parents:
47216
diff
changeset
|
888 |
f.toString()); |
90b7465b9ac7
8186265: Make toString() methods of "task" objects more useful
dl
parents:
47216
diff
changeset
|
889 |
} |
90b7465b9ac7
8186265: Make toString() methods of "task" objects more useful
dl
parents:
47216
diff
changeset
|
890 |
|
90b7465b9ac7
8186265: Make toString() methods of "task" objects more useful
dl
parents:
47216
diff
changeset
|
891 |
public void testToString_exception() { |
48541
946e34c2dec9
8193300: Miscellaneous changes imported from jsr166 CVS 2018-01
dl
parents:
47306
diff
changeset
|
892 |
FutureTask<String> f = new FutureTask<>( |
47306
90b7465b9ac7
8186265: Make toString() methods of "task" objects more useful
dl
parents:
47216
diff
changeset
|
893 |
() -> { throw new ArithmeticException(); }); |
90b7465b9ac7
8186265: Make toString() methods of "task" objects more useful
dl
parents:
47216
diff
changeset
|
894 |
f.run(); |
90b7465b9ac7
8186265: Make toString() methods of "task" objects more useful
dl
parents:
47216
diff
changeset
|
895 |
assertTrue(f.toString().matches(".*\\[.*Completed exceptionally.*\\]")); |
90b7465b9ac7
8186265: Make toString() methods of "task" objects more useful
dl
parents:
47216
diff
changeset
|
896 |
if (testImplementationDetails) |
90b7465b9ac7
8186265: Make toString() methods of "task" objects more useful
dl
parents:
47216
diff
changeset
|
897 |
assertTrue(f.toString().startsWith( |
90b7465b9ac7
8186265: Make toString() methods of "task" objects more useful
dl
parents:
47216
diff
changeset
|
898 |
identityString(f) + "[Completed exceptionally: ")); |
90b7465b9ac7
8186265: Make toString() methods of "task" objects more useful
dl
parents:
47216
diff
changeset
|
899 |
} |
90b7465b9ac7
8186265: Make toString() methods of "task" objects more useful
dl
parents:
47216
diff
changeset
|
900 |
|
90b7465b9ac7
8186265: Make toString() methods of "task" objects more useful
dl
parents:
47216
diff
changeset
|
901 |
public void testToString_cancelled() { |
90b7465b9ac7
8186265: Make toString() methods of "task" objects more useful
dl
parents:
47216
diff
changeset
|
902 |
for (boolean mayInterruptIfRunning : new boolean[] { true, false }) { |
48541
946e34c2dec9
8193300: Miscellaneous changes imported from jsr166 CVS 2018-01
dl
parents:
47306
diff
changeset
|
903 |
FutureTask<String> f = new FutureTask<>(() -> ""); |
47306
90b7465b9ac7
8186265: Make toString() methods of "task" objects more useful
dl
parents:
47216
diff
changeset
|
904 |
assertTrue(f.cancel(mayInterruptIfRunning)); |
90b7465b9ac7
8186265: Make toString() methods of "task" objects more useful
dl
parents:
47216
diff
changeset
|
905 |
assertTrue(f.toString().matches(".*\\[.*Cancelled.*\\]")); |
90b7465b9ac7
8186265: Make toString() methods of "task" objects more useful
dl
parents:
47216
diff
changeset
|
906 |
if (testImplementationDetails) |
90b7465b9ac7
8186265: Make toString() methods of "task" objects more useful
dl
parents:
47216
diff
changeset
|
907 |
assertEquals(identityString(f) + "[Cancelled]", |
90b7465b9ac7
8186265: Make toString() methods of "task" objects more useful
dl
parents:
47216
diff
changeset
|
908 |
f.toString()); |
90b7465b9ac7
8186265: Make toString() methods of "task" objects more useful
dl
parents:
47216
diff
changeset
|
909 |
} |
90b7465b9ac7
8186265: Make toString() methods of "task" objects more useful
dl
parents:
47216
diff
changeset
|
910 |
} |
90b7465b9ac7
8186265: Make toString() methods of "task" objects more useful
dl
parents:
47216
diff
changeset
|
911 |
|
35394 | 912 |
} |