ViewVC Help
View File | Revision Log | Show Annotations | Download File | Root Listing
root/jsr166/jsr166/src/test/tck/ScheduledExecutorSubclassTest.java
(Generate patch)

Comparing jsr166/src/test/tck/ScheduledExecutorSubclassTest.java (file contents):
Revision 1.2 by jsr166, Mon Nov 2 20:28:31 2009 UTC vs.
Revision 1.30 by jsr166, Mon Dec 5 04:08:46 2011 UTC

# Line 1 | Line 1
1   /*
2   * Written by Doug Lea with assistance from members of JCP JSR-166
3   * Expert Group and released to the public domain, as explained at
4 < * http://creativecommons.org/licenses/publicdomain
4 > * http://creativecommons.org/publicdomain/zero/1.0/
5   */
6  
7   import junit.framework.*;
8   import java.util.*;
9   import java.util.concurrent.*;
10 < import java.util.concurrent.atomic.*;
10 > import static java.util.concurrent.TimeUnit.MILLISECONDS;
11 > import java.util.concurrent.atomic.AtomicInteger;
12  
13   public class ScheduledExecutorSubclassTest extends JSR166TestCase {
14      public static void main(String[] args) {
15 <        junit.textui.TestRunner.run (suite());
15 >        junit.textui.TestRunner.run(suite());
16      }
17      public static Test suite() {
18 <        return new TestSuite(ScheduledExecutorTest.class);
18 >        return new TestSuite(ScheduledExecutorSubclassTest.class);
19      }
20  
21      static class CustomTask<V> implements RunnableScheduledFuture<V> {
# Line 35 | Line 36 | public class ScheduledExecutorSubclassTe
36          }
37          public boolean isCancelled() { return task.isCancelled(); }
38          public boolean isDone() { return task.isDone(); }
39 <        public V get() throws InterruptedException,  ExecutionException {
39 >        public V get() throws InterruptedException, ExecutionException {
40              V v = task.get();
41              assertTrue(ran);
42              return v;
43          }
44 <        public V get(long time, TimeUnit unit) throws InterruptedException,  ExecutionException, TimeoutException {
44 >        public V get(long time, TimeUnit unit) throws InterruptedException, ExecutionException, TimeoutException {
45              V v = task.get(time, unit);
46              assertTrue(ran);
47              return v;
48          }
49      }
50  
50
51      public class CustomExecutor extends ScheduledThreadPoolExecutor {
52  
53          protected <V> RunnableScheduledFuture<V> decorateTask(Runnable r, RunnableScheduledFuture<V> task) {
# Line 57 | Line 57 | public class ScheduledExecutorSubclassTe
57          protected <V> RunnableScheduledFuture<V> decorateTask(Callable<V> c, RunnableScheduledFuture<V> task) {
58              return new CustomTask<V>(task);
59          }
60 <        CustomExecutor(int corePoolSize) { super(corePoolSize);}
60 >        CustomExecutor(int corePoolSize) { super(corePoolSize); }
61          CustomExecutor(int corePoolSize, RejectedExecutionHandler handler) {
62              super(corePoolSize, handler);
63          }
# Line 72 | Line 72 | public class ScheduledExecutorSubclassTe
72  
73      }
74  
75
76
75      /**
76       * execute successfully executes a runnable
77       */
78 <    public void testExecute() {
79 <        try {
80 <            TrackedShortRunnable runnable =new TrackedShortRunnable();
81 <            CustomExecutor p1 = new CustomExecutor(1);
82 <            p1.execute(runnable);
83 <            assertFalse(runnable.done);
84 <            Thread.sleep(SHORT_DELAY_MS);
85 <            try { p1.shutdown(); } catch(SecurityException ok) { return; }
86 <            try {
87 <                Thread.sleep(MEDIUM_DELAY_MS);
88 <            } catch(InterruptedException e){
89 <                unexpectedException();
92 <            }
93 <            assertTrue(runnable.done);
94 <            try { p1.shutdown(); } catch(SecurityException ok) { return; }
95 <            joinPool(p1);
96 <        }
97 <        catch(Exception e){
98 <            unexpectedException();
78 >    public void testExecute() throws InterruptedException {
79 >        CustomExecutor p = new CustomExecutor(1);
80 >        final CountDownLatch done = new CountDownLatch(1);
81 >        final Runnable task = new CheckedRunnable() {
82 >            public void realRun() {
83 >                done.countDown();
84 >            }};
85 >        try {
86 >            p.execute(task);
87 >            assertTrue(done.await(SMALL_DELAY_MS, MILLISECONDS));
88 >        } finally {
89 >            joinPool(p);
90          }
100
91      }
92  
103
93      /**
94       * delayed schedule of callable successfully executes after delay
95       */
96 <    public void testSchedule1() {
97 <        try {
98 <            TrackedCallable callable = new TrackedCallable();
99 <            CustomExecutor p1 = new CustomExecutor(1);
100 <            Future f = p1.schedule(callable, SHORT_DELAY_MS, TimeUnit.MILLISECONDS);
101 <            assertFalse(callable.done);
102 <            Thread.sleep(MEDIUM_DELAY_MS);
103 <            assertTrue(callable.done);
104 <            assertEquals(Boolean.TRUE, f.get());
105 <            try { p1.shutdown(); } catch(SecurityException ok) { return; }
106 <            joinPool(p1);
107 <        } catch(RejectedExecutionException e){}
108 <        catch(Exception e){
109 <            e.printStackTrace();
110 <            unexpectedException();
96 >    public void testSchedule1() throws Exception {
97 >        CustomExecutor p = new CustomExecutor(1);
98 >        final long startTime = System.nanoTime();
99 >        final CountDownLatch done = new CountDownLatch(1);
100 >        try {
101 >            Callable task = new CheckedCallable<Boolean>() {
102 >                public Boolean realCall() {
103 >                    done.countDown();
104 >                    assertTrue(millisElapsedSince(startTime) >= timeoutMillis());
105 >                    return Boolean.TRUE;
106 >                }};
107 >            Future f = p.schedule(task, timeoutMillis(), MILLISECONDS);
108 >            assertSame(Boolean.TRUE, f.get());
109 >            assertTrue(millisElapsedSince(startTime) >= timeoutMillis());
110 >            assertTrue(done.await(0L, MILLISECONDS));
111 >        } finally {
112 >            joinPool(p);
113          }
114      }
115  
116      /**
117 <     *  delayed schedule of runnable successfully executes after delay
118 <     */
119 <    public void testSchedule3() {
120 <        try {
121 <            TrackedShortRunnable runnable = new TrackedShortRunnable();
122 <            CustomExecutor p1 = new CustomExecutor(1);
123 <            p1.schedule(runnable, SMALL_DELAY_MS, TimeUnit.MILLISECONDS);
124 <            Thread.sleep(SHORT_DELAY_MS);
125 <            assertFalse(runnable.done);
126 <            Thread.sleep(MEDIUM_DELAY_MS);
127 <            assertTrue(runnable.done);
128 <            try { p1.shutdown(); } catch(SecurityException ok) { return; }
129 <            joinPool(p1);
130 <        } catch(Exception e){
131 <            unexpectedException();
117 >     * delayed schedule of runnable successfully executes after delay
118 >     */
119 >    public void testSchedule3() throws Exception {
120 >        CustomExecutor p = new CustomExecutor(1);
121 >        final long startTime = System.nanoTime();
122 >        final CountDownLatch done = new CountDownLatch(1);
123 >        try {
124 >            Runnable task = new CheckedRunnable() {
125 >                public void realRun() {
126 >                    done.countDown();
127 >                    assertTrue(millisElapsedSince(startTime) >= timeoutMillis());
128 >                }};
129 >            Future f = p.schedule(task, timeoutMillis(), MILLISECONDS);
130 >            await(done);
131 >            assertNull(f.get(LONG_DELAY_MS, MILLISECONDS));
132 >            assertTrue(millisElapsedSince(startTime) >= timeoutMillis());
133 >        } finally {
134 >            joinPool(p);
135          }
136      }
137  
138      /**
139       * scheduleAtFixedRate executes runnable after given initial delay
140       */
141 <    public void testSchedule4() {
142 <        try {
143 <            TrackedShortRunnable runnable = new TrackedShortRunnable();
144 <            CustomExecutor p1 = new CustomExecutor(1);
145 <            ScheduledFuture h = p1.scheduleAtFixedRate(runnable, SHORT_DELAY_MS, SHORT_DELAY_MS, TimeUnit.MILLISECONDS);
146 <            assertFalse(runnable.done);
147 <            Thread.sleep(MEDIUM_DELAY_MS);
148 <            assertTrue(runnable.done);
149 <            h.cancel(true);
150 <            joinPool(p1);
151 <        } catch(Exception e){
152 <            unexpectedException();
141 >    public void testSchedule4() throws InterruptedException {
142 >        CustomExecutor p = new CustomExecutor(1);
143 >        final long startTime = System.nanoTime();
144 >        final CountDownLatch done = new CountDownLatch(1);
145 >        try {
146 >            Runnable task = new CheckedRunnable() {
147 >                public void realRun() {
148 >                    done.countDown();
149 >                    assertTrue(millisElapsedSince(startTime) >= timeoutMillis());
150 >                }};
151 >            ScheduledFuture f =
152 >                p.scheduleAtFixedRate(task, timeoutMillis(),
153 >                                      LONG_DELAY_MS, MILLISECONDS);
154 >            await(done);
155 >            assertTrue(millisElapsedSince(startTime) >= timeoutMillis());
156 >            f.cancel(true);
157 >        } finally {
158 >            joinPool(p);
159          }
160      }
161  
162    static class RunnableCounter implements Runnable {
163        AtomicInteger count = new AtomicInteger(0);
164        public void run() { count.getAndIncrement(); }
165    }
166
162      /**
163       * scheduleWithFixedDelay executes runnable after given initial delay
164       */
165 <    public void testSchedule5() {
166 <        try {
167 <            TrackedShortRunnable runnable = new TrackedShortRunnable();
168 <            CustomExecutor p1 = new CustomExecutor(1);
169 <            ScheduledFuture h = p1.scheduleWithFixedDelay(runnable, SHORT_DELAY_MS, SHORT_DELAY_MS, TimeUnit.MILLISECONDS);
170 <            assertFalse(runnable.done);
171 <            Thread.sleep(MEDIUM_DELAY_MS);
172 <            assertTrue(runnable.done);
173 <            h.cancel(true);
174 <            joinPool(p1);
175 <        } catch(Exception e){
176 <            unexpectedException();
165 >    public void testSchedule5() throws InterruptedException {
166 >        CustomExecutor p = new CustomExecutor(1);
167 >        final long startTime = System.nanoTime();
168 >        final CountDownLatch done = new CountDownLatch(1);
169 >        try {
170 >            Runnable task = new CheckedRunnable() {
171 >                public void realRun() {
172 >                    done.countDown();
173 >                    assertTrue(millisElapsedSince(startTime) >= timeoutMillis());
174 >                }};
175 >            ScheduledFuture f =
176 >                p.scheduleWithFixedDelay(task, timeoutMillis(),
177 >                                         LONG_DELAY_MS, MILLISECONDS);
178 >            await(done);
179 >            assertTrue(millisElapsedSince(startTime) >= timeoutMillis());
180 >            f.cancel(true);
181 >        } finally {
182 >            joinPool(p);
183          }
184      }
185  
186 +    static class RunnableCounter implements Runnable {
187 +        AtomicInteger count = new AtomicInteger(0);
188 +        public void run() { count.getAndIncrement(); }
189 +    }
190 +
191      /**
192       * scheduleAtFixedRate executes series of tasks at given rate
193       */
194 <    public void testFixedRateSequence() {
195 <        try {
196 <            CustomExecutor p1 = new CustomExecutor(1);
197 <            RunnableCounter counter = new RunnableCounter();
198 <            ScheduledFuture h =
199 <                p1.scheduleAtFixedRate(counter, 0, 1, TimeUnit.MILLISECONDS);
200 <            Thread.sleep(SMALL_DELAY_MS);
201 <            h.cancel(true);
202 <            int c = counter.count.get();
203 <            // By time scaling conventions, we must have at least
204 <            // an execution per SHORT delay, but no more than one SHORT more
205 <            assertTrue(c >= SMALL_DELAY_MS / SHORT_DELAY_MS);
206 <            assertTrue(c <= SMALL_DELAY_MS + SHORT_DELAY_MS);
201 <            joinPool(p1);
202 <        } catch(Exception e){
203 <            unexpectedException();
204 <        }
194 >    public void testFixedRateSequence() throws InterruptedException {
195 >        CustomExecutor p = new CustomExecutor(1);
196 >        RunnableCounter counter = new RunnableCounter();
197 >        ScheduledFuture h =
198 >            p.scheduleAtFixedRate(counter, 0, 1, MILLISECONDS);
199 >        delay(SMALL_DELAY_MS);
200 >        h.cancel(true);
201 >        int c = counter.count.get();
202 >        // By time scaling conventions, we must have at least
203 >        // an execution per SHORT delay, but no more than one SHORT more
204 >        assertTrue(c >= SMALL_DELAY_MS / SHORT_DELAY_MS);
205 >        assertTrue(c <= SMALL_DELAY_MS + SHORT_DELAY_MS);
206 >        joinPool(p);
207      }
208  
209      /**
210       * scheduleWithFixedDelay executes series of tasks with given period
211       */
212 <    public void testFixedDelaySequence() {
213 <        try {
214 <            CustomExecutor p1 = new CustomExecutor(1);
215 <            RunnableCounter counter = new RunnableCounter();
216 <            ScheduledFuture h =
217 <                p1.scheduleWithFixedDelay(counter, 0, 1, TimeUnit.MILLISECONDS);
218 <            Thread.sleep(SMALL_DELAY_MS);
219 <            h.cancel(true);
220 <            int c = counter.count.get();
221 <            assertTrue(c >= SMALL_DELAY_MS / SHORT_DELAY_MS);
222 <            assertTrue(c <= SMALL_DELAY_MS + SHORT_DELAY_MS);
221 <            joinPool(p1);
222 <        } catch(Exception e){
223 <            unexpectedException();
224 <        }
212 >    public void testFixedDelaySequence() throws InterruptedException {
213 >        CustomExecutor p = new CustomExecutor(1);
214 >        RunnableCounter counter = new RunnableCounter();
215 >        ScheduledFuture h =
216 >            p.scheduleWithFixedDelay(counter, 0, 1, MILLISECONDS);
217 >        delay(SMALL_DELAY_MS);
218 >        h.cancel(true);
219 >        int c = counter.count.get();
220 >        assertTrue(c >= SMALL_DELAY_MS / SHORT_DELAY_MS);
221 >        assertTrue(c <= SMALL_DELAY_MS + SHORT_DELAY_MS);
222 >        joinPool(p);
223      }
224  
227
225      /**
226 <     *  execute (null) throws NPE
226 >     * execute(null) throws NPE
227       */
228 <    public void testExecuteNull() {
229 <        CustomExecutor se = null;
228 >    public void testExecuteNull() throws InterruptedException {
229 >        CustomExecutor se = new CustomExecutor(1);
230          try {
231 <            se = new CustomExecutor(1);
235 <            se.execute(null);
231 >            se.execute(null);
232              shouldThrow();
233 <        } catch(NullPointerException success){}
234 <        catch(Exception e){
239 <            unexpectedException();
240 <        }
241 <
242 <        joinPool(se);
233 >        } catch (NullPointerException success) {}
234 >        joinPool(se);
235      }
236  
237      /**
238 <     * schedule (null) throws NPE
238 >     * schedule(null) throws NPE
239       */
240 <    public void testScheduleNull() {
240 >    public void testScheduleNull() throws InterruptedException {
241          CustomExecutor se = new CustomExecutor(1);
242 <        try {
242 >        try {
243              TrackedCallable callable = null;
244 <            Future f = se.schedule(callable, SHORT_DELAY_MS, TimeUnit.MILLISECONDS);
244 >            Future f = se.schedule(callable, SHORT_DELAY_MS, MILLISECONDS);
245              shouldThrow();
246 <        } catch(NullPointerException success){}
247 <        catch(Exception e){
256 <            unexpectedException();
257 <        }
258 <        joinPool(se);
246 >        } catch (NullPointerException success) {}
247 >        joinPool(se);
248      }
249  
250      /**
# Line 266 | Line 255 | public class ScheduledExecutorSubclassTe
255          try {
256              se.shutdown();
257              se.schedule(new NoOpRunnable(),
258 <                        MEDIUM_DELAY_MS, TimeUnit.MILLISECONDS);
258 >                        MEDIUM_DELAY_MS, MILLISECONDS);
259              shouldThrow();
260 <        } catch(RejectedExecutionException success){
260 >        } catch (RejectedExecutionException success) {
261          } catch (SecurityException ok) {
262          }
263  
264          joinPool(se);
276
265      }
266  
267      /**
# Line 284 | Line 272 | public class ScheduledExecutorSubclassTe
272          try {
273              se.shutdown();
274              se.schedule(new NoOpCallable(),
275 <                        MEDIUM_DELAY_MS, TimeUnit.MILLISECONDS);
275 >                        MEDIUM_DELAY_MS, MILLISECONDS);
276              shouldThrow();
277 <        } catch(RejectedExecutionException success){
277 >        } catch (RejectedExecutionException success) {
278          } catch (SecurityException ok) {
279          }
280          joinPool(se);
# Line 295 | Line 283 | public class ScheduledExecutorSubclassTe
283      /**
284       * schedule callable throws RejectedExecutionException if shutdown
285       */
286 <     public void testSchedule3_RejectedExecutionException() {
287 <         CustomExecutor se = new CustomExecutor(1);
288 <         try {
286 >    public void testSchedule3_RejectedExecutionException() {
287 >        CustomExecutor se = new CustomExecutor(1);
288 >        try {
289              se.shutdown();
290              se.schedule(new NoOpCallable(),
291 <                        MEDIUM_DELAY_MS, TimeUnit.MILLISECONDS);
291 >                        MEDIUM_DELAY_MS, MILLISECONDS);
292              shouldThrow();
293 <        } catch(RejectedExecutionException success){
293 >        } catch (RejectedExecutionException success) {
294          } catch (SecurityException ok) {
295          }
296 <         joinPool(se);
296 >        joinPool(se);
297      }
298  
299      /**
300 <     *  scheduleAtFixedRate throws RejectedExecutionException if shutdown
300 >     * scheduleAtFixedRate throws RejectedExecutionException if shutdown
301       */
302      public void testScheduleAtFixedRate1_RejectedExecutionException() {
303          CustomExecutor se = new CustomExecutor(1);
304          try {
305              se.shutdown();
306              se.scheduleAtFixedRate(new NoOpRunnable(),
307 <                                   MEDIUM_DELAY_MS, MEDIUM_DELAY_MS, TimeUnit.MILLISECONDS);
307 >                                   MEDIUM_DELAY_MS, MEDIUM_DELAY_MS, MILLISECONDS);
308              shouldThrow();
309 <        } catch(RejectedExecutionException success){
309 >        } catch (RejectedExecutionException success) {
310          } catch (SecurityException ok) {
311          }
312          joinPool(se);
# Line 332 | Line 320 | public class ScheduledExecutorSubclassTe
320          try {
321              se.shutdown();
322              se.scheduleWithFixedDelay(new NoOpRunnable(),
323 <                                      MEDIUM_DELAY_MS, MEDIUM_DELAY_MS, TimeUnit.MILLISECONDS);
323 >                                      MEDIUM_DELAY_MS, MEDIUM_DELAY_MS, MILLISECONDS);
324              shouldThrow();
325 <        } catch(RejectedExecutionException success){
325 >        } catch (RejectedExecutionException success) {
326          } catch (SecurityException ok) {
327          }
328          joinPool(se);
329      }
330  
331      /**
332 <     *  getActiveCount increases but doesn't overestimate, when a
333 <     *  thread becomes active
332 >     * getActiveCount increases but doesn't overestimate, when a
333 >     * thread becomes active
334       */
335 <    public void testGetActiveCount() {
336 <        CustomExecutor p2 = new CustomExecutor(2);
337 <        assertEquals(0, p2.getActiveCount());
338 <        p2.execute(new SmallRunnable());
339 <        try {
340 <            Thread.sleep(SHORT_DELAY_MS);
341 <        } catch(Exception e){
342 <            unexpectedException();
335 >    public void testGetActiveCount() throws InterruptedException {
336 >        final ThreadPoolExecutor p = new CustomExecutor(2);
337 >        final CountDownLatch threadStarted = new CountDownLatch(1);
338 >        final CountDownLatch done = new CountDownLatch(1);
339 >        try {
340 >            assertEquals(0, p.getActiveCount());
341 >            p.execute(new CheckedRunnable() {
342 >                public void realRun() throws InterruptedException {
343 >                    threadStarted.countDown();
344 >                    assertEquals(1, p.getActiveCount());
345 >                    done.await();
346 >                }});
347 >            assertTrue(threadStarted.await(SMALL_DELAY_MS, MILLISECONDS));
348 >            assertEquals(1, p.getActiveCount());
349 >        } finally {
350 >            done.countDown();
351 >            joinPool(p);
352          }
356        assertEquals(1, p2.getActiveCount());
357        joinPool(p2);
353      }
354  
355      /**
356 <     *    getCompletedTaskCount increases, but doesn't overestimate,
357 <     *   when tasks complete
356 >     * getCompletedTaskCount increases, but doesn't overestimate,
357 >     * when tasks complete
358       */
359 <    public void testGetCompletedTaskCount() {
360 <        CustomExecutor p2 = new CustomExecutor(2);
361 <        assertEquals(0, p2.getCompletedTaskCount());
362 <        p2.execute(new SmallRunnable());
363 <        try {
364 <            Thread.sleep(MEDIUM_DELAY_MS);
365 <        } catch(Exception e){
366 <            unexpectedException();
359 >    public void testGetCompletedTaskCount() throws InterruptedException {
360 >        final ThreadPoolExecutor p = new CustomExecutor(2);
361 >        final CountDownLatch threadStarted = new CountDownLatch(1);
362 >        final CountDownLatch threadProceed = new CountDownLatch(1);
363 >        final CountDownLatch threadDone = new CountDownLatch(1);
364 >        try {
365 >            assertEquals(0, p.getCompletedTaskCount());
366 >            p.execute(new CheckedRunnable() {
367 >                public void realRun() throws InterruptedException {
368 >                    threadStarted.countDown();
369 >                    assertEquals(0, p.getCompletedTaskCount());
370 >                    threadProceed.await();
371 >                    threadDone.countDown();
372 >                }});
373 >            await(threadStarted);
374 >            assertEquals(0, p.getCompletedTaskCount());
375 >            threadProceed.countDown();
376 >            threadDone.await();
377 >            long startTime = System.nanoTime();
378 >            while (p.getCompletedTaskCount() != 1) {
379 >                if (millisElapsedSince(startTime) > LONG_DELAY_MS)
380 >                    fail("timed out");
381 >                Thread.yield();
382 >            }
383 >        } finally {
384 >            joinPool(p);
385          }
373        assertEquals(1, p2.getCompletedTaskCount());
374        joinPool(p2);
386      }
387  
388      /**
389 <     *  getCorePoolSize returns size given in constructor if not otherwise set
389 >     * getCorePoolSize returns size given in constructor if not otherwise set
390       */
391      public void testGetCorePoolSize() {
392 <        CustomExecutor p1 = new CustomExecutor(1);
393 <        assertEquals(1, p1.getCorePoolSize());
394 <        joinPool(p1);
392 >        CustomExecutor p = new CustomExecutor(1);
393 >        assertEquals(1, p.getCorePoolSize());
394 >        joinPool(p);
395      }
396  
397      /**
398 <     *    getLargestPoolSize increases, but doesn't overestimate, when
399 <     *   multiple threads active
398 >     * getLargestPoolSize increases, but doesn't overestimate, when
399 >     * multiple threads active
400       */
401 <    public void testGetLargestPoolSize() {
402 <        CustomExecutor p2 = new CustomExecutor(2);
403 <        assertEquals(0, p2.getLargestPoolSize());
404 <        p2.execute(new SmallRunnable());
405 <        p2.execute(new SmallRunnable());
406 <        try {
407 <            Thread.sleep(SHORT_DELAY_MS);
408 <        } catch(Exception e){
409 <            unexpectedException();
401 >    public void testGetLargestPoolSize() throws InterruptedException {
402 >        final int THREADS = 3;
403 >        final ThreadPoolExecutor p = new CustomExecutor(THREADS);
404 >        final CountDownLatch threadsStarted = new CountDownLatch(THREADS);
405 >        final CountDownLatch done = new CountDownLatch(1);
406 >        try {
407 >            assertEquals(0, p.getLargestPoolSize());
408 >            for (int i = 0; i < THREADS; i++)
409 >                p.execute(new CheckedRunnable() {
410 >                    public void realRun() throws InterruptedException {
411 >                        threadsStarted.countDown();
412 >                        done.await();
413 >                        assertEquals(THREADS, p.getLargestPoolSize());
414 >                    }});
415 >            assertTrue(threadsStarted.await(SMALL_DELAY_MS, MILLISECONDS));
416 >            assertEquals(THREADS, p.getLargestPoolSize());
417 >        } finally {
418 >            done.countDown();
419 >            joinPool(p);
420 >            assertEquals(THREADS, p.getLargestPoolSize());
421          }
400        assertEquals(2, p2.getLargestPoolSize());
401        joinPool(p2);
422      }
423  
424      /**
425 <     *   getPoolSize increases, but doesn't overestimate, when threads
426 <     *   become active
425 >     * getPoolSize increases, but doesn't overestimate, when threads
426 >     * become active
427       */
428 <    public void testGetPoolSize() {
429 <        CustomExecutor p1 = new CustomExecutor(1);
430 <        assertEquals(0, p1.getPoolSize());
431 <        p1.execute(new SmallRunnable());
432 <        assertEquals(1, p1.getPoolSize());
433 <        joinPool(p1);
428 >    public void testGetPoolSize() throws InterruptedException {
429 >        final ThreadPoolExecutor p = new CustomExecutor(1);
430 >        final CountDownLatch threadStarted = new CountDownLatch(1);
431 >        final CountDownLatch done = new CountDownLatch(1);
432 >        try {
433 >            assertEquals(0, p.getPoolSize());
434 >            p.execute(new CheckedRunnable() {
435 >                public void realRun() throws InterruptedException {
436 >                    threadStarted.countDown();
437 >                    assertEquals(1, p.getPoolSize());
438 >                    done.await();
439 >                }});
440 >            assertTrue(threadStarted.await(SMALL_DELAY_MS, MILLISECONDS));
441 >            assertEquals(1, p.getPoolSize());
442 >        } finally {
443 >            done.countDown();
444 >            joinPool(p);
445 >        }
446      }
447  
448      /**
449 <     *    getTaskCount increases, but doesn't overestimate, when tasks
450 <     *    submitted
449 >     * getTaskCount increases, but doesn't overestimate, when tasks
450 >     * submitted
451       */
452 <    public void testGetTaskCount() {
453 <        CustomExecutor p1 = new CustomExecutor(1);
454 <        assertEquals(0, p1.getTaskCount());
455 <        for(int i = 0; i < 5; i++)
456 <            p1.execute(new SmallRunnable());
457 <        try {
458 <            Thread.sleep(SHORT_DELAY_MS);
459 <        } catch(Exception e){
460 <            unexpectedException();
452 >    public void testGetTaskCount() throws InterruptedException {
453 >        final ThreadPoolExecutor p = new CustomExecutor(1);
454 >        final CountDownLatch threadStarted = new CountDownLatch(1);
455 >        final CountDownLatch done = new CountDownLatch(1);
456 >        final int TASKS = 5;
457 >        try {
458 >            assertEquals(0, p.getTaskCount());
459 >            for (int i = 0; i < TASKS; i++)
460 >                p.execute(new CheckedRunnable() {
461 >                    public void realRun() throws InterruptedException {
462 >                        threadStarted.countDown();
463 >                        done.await();
464 >                    }});
465 >            assertTrue(threadStarted.await(SMALL_DELAY_MS, MILLISECONDS));
466 >            assertEquals(TASKS, p.getTaskCount());
467 >        } finally {
468 >            done.countDown();
469 >            joinPool(p);
470          }
430        assertEquals(5, p1.getTaskCount());
431        joinPool(p1);
471      }
472  
473      /**
# Line 436 | Line 475 | public class ScheduledExecutorSubclassTe
475       */
476      public void testGetThreadFactory() {
477          ThreadFactory tf = new SimpleThreadFactory();
478 <        CustomExecutor p = new CustomExecutor(1, tf);
478 >        CustomExecutor p = new CustomExecutor(1, tf);
479          assertSame(tf, p.getThreadFactory());
480          joinPool(p);
481      }
# Line 446 | Line 485 | public class ScheduledExecutorSubclassTe
485       */
486      public void testSetThreadFactory() {
487          ThreadFactory tf = new SimpleThreadFactory();
488 <        CustomExecutor p = new CustomExecutor(1);
488 >        CustomExecutor p = new CustomExecutor(1);
489          p.setThreadFactory(tf);
490          assertSame(tf, p.getThreadFactory());
491          joinPool(p);
# Line 456 | Line 495 | public class ScheduledExecutorSubclassTe
495       * setThreadFactory(null) throws NPE
496       */
497      public void testSetThreadFactoryNull() {
498 <        CustomExecutor p = new CustomExecutor(1);
498 >        CustomExecutor p = new CustomExecutor(1);
499          try {
500              p.setThreadFactory(null);
501              shouldThrow();
# Line 467 | Line 506 | public class ScheduledExecutorSubclassTe
506      }
507  
508      /**
509 <     *   is isShutDown is false before shutdown, true after
509 >     * isShutdown is false before shutdown, true after
510       */
511      public void testIsShutdown() {
512 <
474 <        CustomExecutor p1 = new CustomExecutor(1);
512 >        CustomExecutor p = new CustomExecutor(1);
513          try {
514 <            assertFalse(p1.isShutdown());
514 >            assertFalse(p.isShutdown());
515          }
516          finally {
517 <            try { p1.shutdown(); } catch(SecurityException ok) { return; }
517 >            try { p.shutdown(); } catch (SecurityException ok) { return; }
518          }
519 <        assertTrue(p1.isShutdown());
519 >        assertTrue(p.isShutdown());
520      }
521  
484
522      /**
523 <     *   isTerminated is false before termination, true after
523 >     * isTerminated is false before termination, true after
524       */
525 <    public void testIsTerminated() {
526 <        CustomExecutor p1 = new CustomExecutor(1);
525 >    public void testIsTerminated() throws InterruptedException {
526 >        final ThreadPoolExecutor p = new CustomExecutor(1);
527 >        final CountDownLatch threadStarted = new CountDownLatch(1);
528 >        final CountDownLatch done = new CountDownLatch(1);
529 >        assertFalse(p.isTerminated());
530          try {
531 <            p1.execute(new SmallRunnable());
531 >            p.execute(new CheckedRunnable() {
532 >                public void realRun() throws InterruptedException {
533 >                    assertFalse(p.isTerminated());
534 >                    threadStarted.countDown();
535 >                    done.await();
536 >                }});
537 >            assertTrue(threadStarted.await(SMALL_DELAY_MS, MILLISECONDS));
538 >            assertFalse(p.isTerminating());
539 >            done.countDown();
540          } finally {
541 <            try { p1.shutdown(); } catch(SecurityException ok) { return; }
494 <        }
495 <        try {
496 <            assertTrue(p1.awaitTermination(LONG_DELAY_MS, TimeUnit.MILLISECONDS));
497 <            assertTrue(p1.isTerminated());
498 <        } catch(Exception e){
499 <            unexpectedException();
541 >            try { p.shutdown(); } catch (SecurityException ok) { return; }
542          }
543 +        assertTrue(p.awaitTermination(LONG_DELAY_MS, MILLISECONDS));
544 +        assertTrue(p.isTerminated());
545      }
546  
547      /**
548 <     *  isTerminating is not true when running or when terminated
548 >     * isTerminating is not true when running or when terminated
549       */
550 <    public void testIsTerminating() {
551 <        CustomExecutor p1 = new CustomExecutor(1);
552 <        assertFalse(p1.isTerminating());
550 >    public void testIsTerminating() throws InterruptedException {
551 >        final ThreadPoolExecutor p = new CustomExecutor(1);
552 >        final CountDownLatch threadStarted = new CountDownLatch(1);
553 >        final CountDownLatch done = new CountDownLatch(1);
554          try {
555 <            p1.execute(new SmallRunnable());
556 <            assertFalse(p1.isTerminating());
555 >            assertFalse(p.isTerminating());
556 >            p.execute(new CheckedRunnable() {
557 >                public void realRun() throws InterruptedException {
558 >                    assertFalse(p.isTerminating());
559 >                    threadStarted.countDown();
560 >                    done.await();
561 >                }});
562 >            assertTrue(threadStarted.await(SMALL_DELAY_MS, MILLISECONDS));
563 >            assertFalse(p.isTerminating());
564 >            done.countDown();
565          } finally {
566 <            try { p1.shutdown(); } catch(SecurityException ok) { return; }
514 <        }
515 <        try {
516 <            assertTrue(p1.awaitTermination(LONG_DELAY_MS, TimeUnit.MILLISECONDS));
517 <            assertTrue(p1.isTerminated());
518 <            assertFalse(p1.isTerminating());
519 <        } catch(Exception e){
520 <            unexpectedException();
566 >            try { p.shutdown(); } catch (SecurityException ok) { return; }
567          }
568 +        assertTrue(p.awaitTermination(LONG_DELAY_MS, MILLISECONDS));
569 +        assertTrue(p.isTerminated());
570 +        assertFalse(p.isTerminating());
571      }
572  
573      /**
574       * getQueue returns the work queue, which contains queued tasks
575       */
576 <    public void testGetQueue() {
577 <        CustomExecutor p1 = new CustomExecutor(1);
578 <        ScheduledFuture[] tasks = new ScheduledFuture[5];
579 <        for(int i = 0; i < 5; i++){
531 <            tasks[i] = p1.schedule(new SmallPossiblyInterruptedRunnable(), 1, TimeUnit.MILLISECONDS);
532 <        }
576 >    public void testGetQueue() throws InterruptedException {
577 >        ScheduledThreadPoolExecutor p = new CustomExecutor(1);
578 >        final CountDownLatch threadStarted = new CountDownLatch(1);
579 >        final CountDownLatch done = new CountDownLatch(1);
580          try {
581 <            Thread.sleep(SHORT_DELAY_MS);
582 <            BlockingQueue<Runnable> q = p1.getQueue();
583 <            assertTrue(q.contains(tasks[4]));
581 >            ScheduledFuture[] tasks = new ScheduledFuture[5];
582 >            for (int i = 0; i < tasks.length; i++) {
583 >                Runnable r = new CheckedRunnable() {
584 >                    public void realRun() throws InterruptedException {
585 >                        threadStarted.countDown();
586 >                        done.await();
587 >                    }};
588 >                tasks[i] = p.schedule(r, 1, MILLISECONDS);
589 >            }
590 >            assertTrue(threadStarted.await(SMALL_DELAY_MS, MILLISECONDS));
591 >            BlockingQueue<Runnable> q = p.getQueue();
592 >            assertTrue(q.contains(tasks[tasks.length - 1]));
593              assertFalse(q.contains(tasks[0]));
538        } catch(Exception e) {
539            unexpectedException();
594          } finally {
595 <            joinPool(p1);
595 >            done.countDown();
596 >            joinPool(p);
597          }
598      }
599  
600      /**
601       * remove(task) removes queued task, and fails to remove active task
602       */
603 <    public void testRemove() {
604 <        CustomExecutor p1 = new CustomExecutor(1);
603 >    public void testRemove() throws InterruptedException {
604 >        final ScheduledThreadPoolExecutor p = new CustomExecutor(1);
605          ScheduledFuture[] tasks = new ScheduledFuture[5];
606 <        for(int i = 0; i < 5; i++){
607 <            tasks[i] = p1.schedule(new SmallPossiblyInterruptedRunnable(), 1, TimeUnit.MILLISECONDS);
553 <        }
606 >        final CountDownLatch threadStarted = new CountDownLatch(1);
607 >        final CountDownLatch done = new CountDownLatch(1);
608          try {
609 <            Thread.sleep(SHORT_DELAY_MS);
610 <            BlockingQueue<Runnable> q = p1.getQueue();
611 <            assertFalse(p1.remove((Runnable)tasks[0]));
609 >            for (int i = 0; i < tasks.length; i++) {
610 >                Runnable r = new CheckedRunnable() {
611 >                    public void realRun() throws InterruptedException {
612 >                        threadStarted.countDown();
613 >                        done.await();
614 >                    }};
615 >                tasks[i] = p.schedule(r, 1, MILLISECONDS);
616 >            }
617 >            assertTrue(threadStarted.await(SMALL_DELAY_MS, MILLISECONDS));
618 >            BlockingQueue<Runnable> q = p.getQueue();
619 >            assertFalse(p.remove((Runnable)tasks[0]));
620              assertTrue(q.contains((Runnable)tasks[4]));
621              assertTrue(q.contains((Runnable)tasks[3]));
622 <            assertTrue(p1.remove((Runnable)tasks[4]));
623 <            assertFalse(p1.remove((Runnable)tasks[4]));
622 >            assertTrue(p.remove((Runnable)tasks[4]));
623 >            assertFalse(p.remove((Runnable)tasks[4]));
624              assertFalse(q.contains((Runnable)tasks[4]));
625              assertTrue(q.contains((Runnable)tasks[3]));
626 <            assertTrue(p1.remove((Runnable)tasks[3]));
626 >            assertTrue(p.remove((Runnable)tasks[3]));
627              assertFalse(q.contains((Runnable)tasks[3]));
566        } catch(Exception e) {
567            unexpectedException();
628          } finally {
629 <            joinPool(p1);
629 >            done.countDown();
630 >            joinPool(p);
631          }
632      }
633  
634      /**
635 <     *  purge removes cancelled tasks from the queue
635 >     * purge removes cancelled tasks from the queue
636       */
637 <    public void testPurge() {
638 <        CustomExecutor p1 = new CustomExecutor(1);
637 >    public void testPurge() throws InterruptedException {
638 >        CustomExecutor p = new CustomExecutor(1);
639          ScheduledFuture[] tasks = new ScheduledFuture[5];
640 <        for(int i = 0; i < 5; i++){
641 <            tasks[i] = p1.schedule(new SmallPossiblyInterruptedRunnable(), SHORT_DELAY_MS, TimeUnit.MILLISECONDS);
642 <        }
640 >        for (int i = 0; i < tasks.length; i++)
641 >            tasks[i] = p.schedule(new SmallPossiblyInterruptedRunnable(),
642 >                                  LONG_DELAY_MS, MILLISECONDS);
643          try {
644 <            int max = 5;
644 >            int max = tasks.length;
645              if (tasks[4].cancel(true)) --max;
646              if (tasks[3].cancel(true)) --max;
647              // There must eventually be an interference-free point at
648              // which purge will not fail. (At worst, when queue is empty.)
649 <            int k;
650 <            for (k = 0; k < SMALL_DELAY_MS; ++k) {
651 <                p1.purge();
652 <                long count = p1.getTaskCount();
653 <                if (count >= 0 && count <= max)
654 <                    break;
655 <                Thread.sleep(1);
656 <            }
596 <            assertTrue(k < SMALL_DELAY_MS);
597 <        } catch(Exception e) {
598 <            unexpectedException();
649 >            long startTime = System.nanoTime();
650 >            do {
651 >                p.purge();
652 >                long count = p.getTaskCount();
653 >                if (count == max)
654 >                    return;
655 >            } while (millisElapsedSince(startTime) < MEDIUM_DELAY_MS);
656 >            fail("Purge failed to remove cancelled tasks");
657          } finally {
658 <            joinPool(p1);
658 >            for (ScheduledFuture task : tasks)
659 >                task.cancel(true);
660 >            joinPool(p);
661          }
662      }
663  
664      /**
665 <     *  shutDownNow returns a list containing tasks that were not run
665 >     * shutdownNow returns a list containing tasks that were not run
666       */
667 <    public void testShutDownNow() {
668 <        CustomExecutor p1 = new CustomExecutor(1);
669 <        for(int i = 0; i < 5; i++)
670 <            p1.schedule(new SmallPossiblyInterruptedRunnable(), SHORT_DELAY_MS, TimeUnit.MILLISECONDS);
671 <        List l;
672 <        try {
673 <            l = p1.shutdownNow();
667 >    public void testShutdownNow() {
668 >        CustomExecutor p = new CustomExecutor(1);
669 >        for (int i = 0; i < 5; i++)
670 >            p.schedule(new SmallPossiblyInterruptedRunnable(),
671 >                       LONG_DELAY_MS, MILLISECONDS);
672 >        try {
673 >            List<Runnable> l = p.shutdownNow();
674 >            assertTrue(p.isShutdown());
675 >            assertEquals(5, l.size());
676          } catch (SecurityException ok) {
677 <            return;
677 >            // Allowed in case test doesn't have privs
678 >        } finally {
679 >            joinPool(p);
680          }
617        assertTrue(p1.isShutdown());
618        assertTrue(l.size() > 0 && l.size() <= 5);
619        joinPool(p1);
681      }
682  
683      /**
684       * In default setting, shutdown cancels periodic but not delayed
685       * tasks at shutdown
686       */
687 <    public void testShutDown1() {
688 <        try {
689 <            CustomExecutor p1 = new CustomExecutor(1);
690 <            assertTrue(p1.getExecuteExistingDelayedTasksAfterShutdownPolicy());
630 <            assertFalse(p1.getContinueExistingPeriodicTasksAfterShutdownPolicy());
631 <
632 <            ScheduledFuture[] tasks = new ScheduledFuture[5];
633 <            for(int i = 0; i < 5; i++)
634 <                tasks[i] = p1.schedule(new NoOpRunnable(), SHORT_DELAY_MS, TimeUnit.MILLISECONDS);
635 <            try { p1.shutdown(); } catch(SecurityException ok) { return; }
636 <            BlockingQueue q = p1.getQueue();
637 <            for (Iterator it = q.iterator(); it.hasNext();) {
638 <                ScheduledFuture t = (ScheduledFuture)it.next();
639 <                assertFalse(t.isCancelled());
640 <            }
641 <            assertTrue(p1.isShutdown());
642 <            Thread.sleep(SMALL_DELAY_MS);
643 <            for (int i = 0; i < 5; ++i) {
644 <                assertTrue(tasks[i].isDone());
645 <                assertFalse(tasks[i].isCancelled());
646 <            }
687 >    public void testShutdown1() throws InterruptedException {
688 >        CustomExecutor p = new CustomExecutor(1);
689 >        assertTrue(p.getExecuteExistingDelayedTasksAfterShutdownPolicy());
690 >        assertFalse(p.getContinueExistingPeriodicTasksAfterShutdownPolicy());
691  
692 +        ScheduledFuture[] tasks = new ScheduledFuture[5];
693 +        for (int i = 0; i < tasks.length; i++)
694 +            tasks[i] = p.schedule(new NoOpRunnable(),
695 +                                  SHORT_DELAY_MS, MILLISECONDS);
696 +        try { p.shutdown(); } catch (SecurityException ok) { return; }
697 +        BlockingQueue<Runnable> q = p.getQueue();
698 +        for (ScheduledFuture task : tasks) {
699 +            assertFalse(task.isDone());
700 +            assertFalse(task.isCancelled());
701 +            assertTrue(q.contains(task));
702          }
703 <        catch(Exception ex) {
704 <            unexpectedException();
703 >        assertTrue(p.isShutdown());
704 >        assertTrue(p.awaitTermination(SMALL_DELAY_MS, MILLISECONDS));
705 >        assertTrue(p.isTerminated());
706 >        for (ScheduledFuture task : tasks) {
707 >            assertTrue(task.isDone());
708 >            assertFalse(task.isCancelled());
709          }
710      }
711  
654
712      /**
713       * If setExecuteExistingDelayedTasksAfterShutdownPolicy is false,
714       * delayed tasks are cancelled at shutdown
715       */
716 <    public void testShutDown2() {
717 <        try {
718 <            CustomExecutor p1 = new CustomExecutor(1);
719 <            p1.setExecuteExistingDelayedTasksAfterShutdownPolicy(false);
720 <            ScheduledFuture[] tasks = new ScheduledFuture[5];
721 <            for(int i = 0; i < 5; i++)
722 <                tasks[i] = p1.schedule(new NoOpRunnable(), SHORT_DELAY_MS, TimeUnit.MILLISECONDS);
723 <            try { p1.shutdown(); } catch(SecurityException ok) { return; }
724 <            assertTrue(p1.isShutdown());
725 <            BlockingQueue q = p1.getQueue();
726 <            assertTrue(q.isEmpty());
727 <            Thread.sleep(SMALL_DELAY_MS);
728 <            assertTrue(p1.isTerminated());
729 <        }
730 <        catch(Exception ex) {
731 <            unexpectedException();
716 >    public void testShutdown2() throws InterruptedException {
717 >        CustomExecutor p = new CustomExecutor(1);
718 >        p.setExecuteExistingDelayedTasksAfterShutdownPolicy(false);
719 >        assertFalse(p.getExecuteExistingDelayedTasksAfterShutdownPolicy());
720 >        assertFalse(p.getContinueExistingPeriodicTasksAfterShutdownPolicy());
721 >        ScheduledFuture[] tasks = new ScheduledFuture[5];
722 >        for (int i = 0; i < tasks.length; i++)
723 >            tasks[i] = p.schedule(new NoOpRunnable(),
724 >                                  SHORT_DELAY_MS, MILLISECONDS);
725 >        BlockingQueue q = p.getQueue();
726 >        assertEquals(tasks.length, q.size());
727 >        try { p.shutdown(); } catch (SecurityException ok) { return; }
728 >        assertTrue(p.isShutdown());
729 >        assertTrue(q.isEmpty());
730 >        assertTrue(p.awaitTermination(SMALL_DELAY_MS, MILLISECONDS));
731 >        assertTrue(p.isTerminated());
732 >        for (ScheduledFuture task : tasks) {
733 >            assertTrue(task.isDone());
734 >            assertTrue(task.isCancelled());
735          }
736      }
737  
678
738      /**
739       * If setContinueExistingPeriodicTasksAfterShutdownPolicy is set false,
740 <     * periodic tasks are not cancelled at shutdown
740 >     * periodic tasks are cancelled at shutdown
741       */
742 <    public void testShutDown3() {
743 <        try {
744 <            CustomExecutor p1 = new CustomExecutor(1);
745 <            p1.setContinueExistingPeriodicTasksAfterShutdownPolicy(false);
746 <            ScheduledFuture task =
747 <                p1.scheduleAtFixedRate(new NoOpRunnable(), 5, 5, TimeUnit.MILLISECONDS);
748 <            try { p1.shutdown(); } catch(SecurityException ok) { return; }
749 <            assertTrue(p1.isShutdown());
750 <            BlockingQueue q = p1.getQueue();
751 <            assertTrue(q.isEmpty());
752 <            Thread.sleep(SHORT_DELAY_MS);
753 <            assertTrue(p1.isTerminated());
754 <        }
755 <        catch(Exception ex) {
756 <            unexpectedException();
757 <        }
742 >    public void testShutdown3() throws InterruptedException {
743 >        CustomExecutor p = new CustomExecutor(1);
744 >        assertTrue(p.getExecuteExistingDelayedTasksAfterShutdownPolicy());
745 >        assertFalse(p.getContinueExistingPeriodicTasksAfterShutdownPolicy());
746 >        p.setContinueExistingPeriodicTasksAfterShutdownPolicy(false);
747 >        assertTrue(p.getExecuteExistingDelayedTasksAfterShutdownPolicy());
748 >        assertFalse(p.getContinueExistingPeriodicTasksAfterShutdownPolicy());
749 >        long initialDelay = LONG_DELAY_MS;
750 >        ScheduledFuture task =
751 >            p.scheduleAtFixedRate(new NoOpRunnable(), initialDelay,
752 >                                  5, MILLISECONDS);
753 >        try { p.shutdown(); } catch (SecurityException ok) { return; }
754 >        assertTrue(p.isShutdown());
755 >        assertTrue(p.getQueue().isEmpty());
756 >        assertTrue(task.isDone());
757 >        assertTrue(task.isCancelled());
758 >        joinPool(p);
759      }
760  
761      /**
762       * if setContinueExistingPeriodicTasksAfterShutdownPolicy is true,
763 <     * periodic tasks are cancelled at shutdown
763 >     * periodic tasks are not cancelled at shutdown
764       */
765 <    public void testShutDown4() {
766 <        CustomExecutor p1 = new CustomExecutor(1);
767 <        try {
768 <            p1.setContinueExistingPeriodicTasksAfterShutdownPolicy(true);
765 >    public void testShutdown4() throws InterruptedException {
766 >        CustomExecutor p = new CustomExecutor(1);
767 >        final CountDownLatch counter = new CountDownLatch(2);
768 >        try {
769 >            p.setContinueExistingPeriodicTasksAfterShutdownPolicy(true);
770 >            assertTrue(p.getExecuteExistingDelayedTasksAfterShutdownPolicy());
771 >            assertTrue(p.getContinueExistingPeriodicTasksAfterShutdownPolicy());
772 >            final Runnable r = new CheckedRunnable() {
773 >                public void realRun() {
774 >                    counter.countDown();
775 >                }};
776              ScheduledFuture task =
777 <                p1.scheduleAtFixedRate(new NoOpRunnable(), 1, 1, TimeUnit.MILLISECONDS);
777 >                p.scheduleAtFixedRate(r, 1, 1, MILLISECONDS);
778 >            assertFalse(task.isDone());
779              assertFalse(task.isCancelled());
780 <            try { p1.shutdown(); } catch(SecurityException ok) { return; }
780 >            try { p.shutdown(); } catch (SecurityException ok) { return; }
781              assertFalse(task.isCancelled());
782 <            assertFalse(p1.isTerminated());
783 <            assertTrue(p1.isShutdown());
784 <            Thread.sleep(SHORT_DELAY_MS);
782 >            assertFalse(p.isTerminated());
783 >            assertTrue(p.isShutdown());
784 >            assertTrue(counter.await(SMALL_DELAY_MS, MILLISECONDS));
785              assertFalse(task.isCancelled());
786 <            assertTrue(task.cancel(true));
786 >            assertTrue(task.cancel(false));
787              assertTrue(task.isDone());
788 <            Thread.sleep(SHORT_DELAY_MS);
789 <            assertTrue(p1.isTerminated());
790 <        }
723 <        catch(Exception ex) {
724 <            unexpectedException();
788 >            assertTrue(task.isCancelled());
789 >            assertTrue(p.awaitTermination(SMALL_DELAY_MS, MILLISECONDS));
790 >            assertTrue(p.isTerminated());
791          }
792          finally {
793 <            joinPool(p1);
793 >            joinPool(p);
794          }
795      }
796  
797      /**
798       * completed submit of callable returns result
799       */
800 <    public void testSubmitCallable() {
800 >    public void testSubmitCallable() throws Exception {
801          ExecutorService e = new CustomExecutor(2);
802          try {
803              Future<String> future = e.submit(new StringTask());
804              String result = future.get();
805              assertSame(TEST_STRING, result);
740        }
741        catch (ExecutionException ex) {
742            unexpectedException();
743        }
744        catch (InterruptedException ex) {
745            unexpectedException();
806          } finally {
807              joinPool(e);
808          }
# Line 751 | Line 811 | public class ScheduledExecutorSubclassTe
811      /**
812       * completed submit of runnable returns successfully
813       */
814 <    public void testSubmitRunnable() {
814 >    public void testSubmitRunnable() throws Exception {
815          ExecutorService e = new CustomExecutor(2);
816          try {
817              Future<?> future = e.submit(new NoOpRunnable());
818              future.get();
819              assertTrue(future.isDone());
760        }
761        catch (ExecutionException ex) {
762            unexpectedException();
763        }
764        catch (InterruptedException ex) {
765            unexpectedException();
820          } finally {
821              joinPool(e);
822          }
# Line 771 | Line 825 | public class ScheduledExecutorSubclassTe
825      /**
826       * completed submit of (runnable, result) returns result
827       */
828 <    public void testSubmitRunnable2() {
828 >    public void testSubmitRunnable2() throws Exception {
829          ExecutorService e = new CustomExecutor(2);
830          try {
831              Future<String> future = e.submit(new NoOpRunnable(), TEST_STRING);
832              String result = future.get();
833              assertSame(TEST_STRING, result);
780        }
781        catch (ExecutionException ex) {
782            unexpectedException();
783        }
784        catch (InterruptedException ex) {
785            unexpectedException();
834          } finally {
835              joinPool(e);
836          }
# Line 791 | Line 839 | public class ScheduledExecutorSubclassTe
839      /**
840       * invokeAny(null) throws NPE
841       */
842 <    public void testInvokeAny1() {
842 >    public void testInvokeAny1() throws Exception {
843          ExecutorService e = new CustomExecutor(2);
844          try {
845              e.invokeAny(null);
846 +            shouldThrow();
847          } catch (NullPointerException success) {
799        } catch(Exception ex) {
800            unexpectedException();
848          } finally {
849              joinPool(e);
850          }
# Line 806 | Line 853 | public class ScheduledExecutorSubclassTe
853      /**
854       * invokeAny(empty collection) throws IAE
855       */
856 <    public void testInvokeAny2() {
856 >    public void testInvokeAny2() throws Exception {
857          ExecutorService e = new CustomExecutor(2);
858          try {
859              e.invokeAny(new ArrayList<Callable<String>>());
860 +            shouldThrow();
861          } catch (IllegalArgumentException success) {
814        } catch(Exception ex) {
815            unexpectedException();
862          } finally {
863              joinPool(e);
864          }
# Line 821 | Line 867 | public class ScheduledExecutorSubclassTe
867      /**
868       * invokeAny(c) throws NPE if c has null elements
869       */
870 <    public void testInvokeAny3() {
870 >    public void testInvokeAny3() throws Exception {
871 >        CountDownLatch latch = new CountDownLatch(1);
872          ExecutorService e = new CustomExecutor(2);
873 +        List<Callable<String>> l = new ArrayList<Callable<String>>();
874 +        l.add(latchAwaitingStringTask(latch));
875 +        l.add(null);
876          try {
827            ArrayList<Callable<String>> l = new ArrayList<Callable<String>>();
828            l.add(new StringTask());
829            l.add(null);
877              e.invokeAny(l);
878 +            shouldThrow();
879          } catch (NullPointerException success) {
832        } catch(Exception ex) {
833            unexpectedException();
880          } finally {
881 +            latch.countDown();
882              joinPool(e);
883          }
884      }
# Line 839 | Line 886 | public class ScheduledExecutorSubclassTe
886      /**
887       * invokeAny(c) throws ExecutionException if no task completes
888       */
889 <    public void testInvokeAny4() {
889 >    public void testInvokeAny4() throws Exception {
890          ExecutorService e = new CustomExecutor(2);
891 +        List<Callable<String>> l = new ArrayList<Callable<String>>();
892 +        l.add(new NPETask());
893          try {
845            ArrayList<Callable<String>> l = new ArrayList<Callable<String>>();
846            l.add(new NPETask());
894              e.invokeAny(l);
895 +            shouldThrow();
896          } catch (ExecutionException success) {
897 <        } catch(Exception ex) {
850 <            unexpectedException();
897 >            assertTrue(success.getCause() instanceof NullPointerException);
898          } finally {
899              joinPool(e);
900          }
# Line 856 | Line 903 | public class ScheduledExecutorSubclassTe
903      /**
904       * invokeAny(c) returns result of some task
905       */
906 <    public void testInvokeAny5() {
906 >    public void testInvokeAny5() throws Exception {
907          ExecutorService e = new CustomExecutor(2);
908          try {
909 <            ArrayList<Callable<String>> l = new ArrayList<Callable<String>>();
909 >            List<Callable<String>> l = new ArrayList<Callable<String>>();
910              l.add(new StringTask());
911              l.add(new StringTask());
912              String result = e.invokeAny(l);
913              assertSame(TEST_STRING, result);
867        } catch (ExecutionException success) {
868        } catch(Exception ex) {
869            unexpectedException();
914          } finally {
915              joinPool(e);
916          }
# Line 875 | Line 919 | public class ScheduledExecutorSubclassTe
919      /**
920       * invokeAll(null) throws NPE
921       */
922 <    public void testInvokeAll1() {
922 >    public void testInvokeAll1() throws Exception {
923          ExecutorService e = new CustomExecutor(2);
924          try {
925              e.invokeAll(null);
926 +            shouldThrow();
927          } catch (NullPointerException success) {
883        } catch(Exception ex) {
884            unexpectedException();
928          } finally {
929              joinPool(e);
930          }
# Line 890 | Line 933 | public class ScheduledExecutorSubclassTe
933      /**
934       * invokeAll(empty collection) returns empty collection
935       */
936 <    public void testInvokeAll2() {
936 >    public void testInvokeAll2() throws Exception {
937          ExecutorService e = new CustomExecutor(2);
938          try {
939              List<Future<String>> r = e.invokeAll(new ArrayList<Callable<String>>());
940              assertTrue(r.isEmpty());
898        } catch(Exception ex) {
899            unexpectedException();
941          } finally {
942              joinPool(e);
943          }
# Line 905 | Line 946 | public class ScheduledExecutorSubclassTe
946      /**
947       * invokeAll(c) throws NPE if c has null elements
948       */
949 <    public void testInvokeAll3() {
949 >    public void testInvokeAll3() throws Exception {
950          ExecutorService e = new CustomExecutor(2);
951 +        List<Callable<String>> l = new ArrayList<Callable<String>>();
952 +        l.add(new StringTask());
953 +        l.add(null);
954          try {
911            ArrayList<Callable<String>> l = new ArrayList<Callable<String>>();
912            l.add(new StringTask());
913            l.add(null);
955              e.invokeAll(l);
956 +            shouldThrow();
957          } catch (NullPointerException success) {
916        } catch(Exception ex) {
917            unexpectedException();
958          } finally {
959              joinPool(e);
960          }
# Line 923 | Line 963 | public class ScheduledExecutorSubclassTe
963      /**
964       * get of invokeAll(c) throws exception on failed task
965       */
966 <    public void testInvokeAll4() {
966 >    public void testInvokeAll4() throws Exception {
967          ExecutorService e = new CustomExecutor(2);
968 +        List<Callable<String>> l = new ArrayList<Callable<String>>();
969 +        l.add(new NPETask());
970 +        List<Future<String>> futures = e.invokeAll(l);
971 +        assertEquals(1, futures.size());
972          try {
973 <            ArrayList<Callable<String>> l = new ArrayList<Callable<String>>();
974 <            l.add(new NPETask());
975 <            List<Future<String>> result = e.invokeAll(l);
976 <            assertEquals(1, result.size());
933 <            for (Iterator<Future<String>> it = result.iterator(); it.hasNext();)
934 <                it.next().get();
935 <        } catch(ExecutionException success) {
936 <        } catch(Exception ex) {
937 <            unexpectedException();
973 >            futures.get(0).get();
974 >            shouldThrow();
975 >        } catch (ExecutionException success) {
976 >            assertTrue(success.getCause() instanceof NullPointerException);
977          } finally {
978              joinPool(e);
979          }
# Line 943 | Line 982 | public class ScheduledExecutorSubclassTe
982      /**
983       * invokeAll(c) returns results of all completed tasks
984       */
985 <    public void testInvokeAll5() {
985 >    public void testInvokeAll5() throws Exception {
986          ExecutorService e = new CustomExecutor(2);
987          try {
988 <            ArrayList<Callable<String>> l = new ArrayList<Callable<String>>();
988 >            List<Callable<String>> l = new ArrayList<Callable<String>>();
989              l.add(new StringTask());
990              l.add(new StringTask());
991 <            List<Future<String>> result = e.invokeAll(l);
992 <            assertEquals(2, result.size());
993 <            for (Iterator<Future<String>> it = result.iterator(); it.hasNext();)
994 <                assertSame(TEST_STRING, it.next().get());
956 <        } catch (ExecutionException success) {
957 <        } catch(Exception ex) {
958 <            unexpectedException();
991 >            List<Future<String>> futures = e.invokeAll(l);
992 >            assertEquals(2, futures.size());
993 >            for (Future<String> future : futures)
994 >                assertSame(TEST_STRING, future.get());
995          } finally {
996              joinPool(e);
997          }
# Line 964 | Line 1000 | public class ScheduledExecutorSubclassTe
1000      /**
1001       * timed invokeAny(null) throws NPE
1002       */
1003 <    public void testTimedInvokeAny1() {
1003 >    public void testTimedInvokeAny1() throws Exception {
1004          ExecutorService e = new CustomExecutor(2);
1005          try {
1006 <            e.invokeAny(null, MEDIUM_DELAY_MS, TimeUnit.MILLISECONDS);
1006 >            e.invokeAny(null, MEDIUM_DELAY_MS, MILLISECONDS);
1007 >            shouldThrow();
1008          } catch (NullPointerException success) {
972        } catch(Exception ex) {
973            unexpectedException();
1009          } finally {
1010              joinPool(e);
1011          }
# Line 979 | Line 1014 | public class ScheduledExecutorSubclassTe
1014      /**
1015       * timed invokeAny(,,null) throws NPE
1016       */
1017 <    public void testTimedInvokeAnyNullTimeUnit() {
1017 >    public void testTimedInvokeAnyNullTimeUnit() throws Exception {
1018          ExecutorService e = new CustomExecutor(2);
1019 +        List<Callable<String>> l = new ArrayList<Callable<String>>();
1020 +        l.add(new StringTask());
1021          try {
985            ArrayList<Callable<String>> l = new ArrayList<Callable<String>>();
986            l.add(new StringTask());
1022              e.invokeAny(l, MEDIUM_DELAY_MS, null);
1023 +            shouldThrow();
1024          } catch (NullPointerException success) {
989        } catch(Exception ex) {
990            unexpectedException();
1025          } finally {
1026              joinPool(e);
1027          }
# Line 996 | Line 1030 | public class ScheduledExecutorSubclassTe
1030      /**
1031       * timed invokeAny(empty collection) throws IAE
1032       */
1033 <    public void testTimedInvokeAny2() {
1033 >    public void testTimedInvokeAny2() throws Exception {
1034          ExecutorService e = new CustomExecutor(2);
1035          try {
1036 <            e.invokeAny(new ArrayList<Callable<String>>(), MEDIUM_DELAY_MS, TimeUnit.MILLISECONDS);
1036 >            e.invokeAny(new ArrayList<Callable<String>>(), MEDIUM_DELAY_MS, MILLISECONDS);
1037 >            shouldThrow();
1038          } catch (IllegalArgumentException success) {
1004        } catch(Exception ex) {
1005            unexpectedException();
1039          } finally {
1040              joinPool(e);
1041          }
# Line 1011 | Line 1044 | public class ScheduledExecutorSubclassTe
1044      /**
1045       * timed invokeAny(c) throws NPE if c has null elements
1046       */
1047 <    public void testTimedInvokeAny3() {
1047 >    public void testTimedInvokeAny3() throws Exception {
1048 >        CountDownLatch latch = new CountDownLatch(1);
1049          ExecutorService e = new CustomExecutor(2);
1050 +        List<Callable<String>> l = new ArrayList<Callable<String>>();
1051 +        l.add(latchAwaitingStringTask(latch));
1052 +        l.add(null);
1053          try {
1054 <            ArrayList<Callable<String>> l = new ArrayList<Callable<String>>();
1055 <            l.add(new StringTask());
1019 <            l.add(null);
1020 <            e.invokeAny(l, MEDIUM_DELAY_MS, TimeUnit.MILLISECONDS);
1054 >            e.invokeAny(l, MEDIUM_DELAY_MS, MILLISECONDS);
1055 >            shouldThrow();
1056          } catch (NullPointerException success) {
1022        } catch(Exception ex) {
1023            ex.printStackTrace();
1024            unexpectedException();
1057          } finally {
1058 +            latch.countDown();
1059              joinPool(e);
1060          }
1061      }
# Line 1030 | Line 1063 | public class ScheduledExecutorSubclassTe
1063      /**
1064       * timed invokeAny(c) throws ExecutionException if no task completes
1065       */
1066 <    public void testTimedInvokeAny4() {
1066 >    public void testTimedInvokeAny4() throws Exception {
1067          ExecutorService e = new CustomExecutor(2);
1068 +        List<Callable<String>> l = new ArrayList<Callable<String>>();
1069 +        l.add(new NPETask());
1070          try {
1071 <            ArrayList<Callable<String>> l = new ArrayList<Callable<String>>();
1072 <            l.add(new NPETask());
1073 <            e.invokeAny(l, MEDIUM_DELAY_MS, TimeUnit.MILLISECONDS);
1074 <        } catch(ExecutionException success) {
1040 <        } catch(Exception ex) {
1041 <            unexpectedException();
1071 >            e.invokeAny(l, MEDIUM_DELAY_MS, MILLISECONDS);
1072 >            shouldThrow();
1073 >        } catch (ExecutionException success) {
1074 >            assertTrue(success.getCause() instanceof NullPointerException);
1075          } finally {
1076              joinPool(e);
1077          }
# Line 1047 | Line 1080 | public class ScheduledExecutorSubclassTe
1080      /**
1081       * timed invokeAny(c) returns result of some task
1082       */
1083 <    public void testTimedInvokeAny5() {
1083 >    public void testTimedInvokeAny5() throws Exception {
1084          ExecutorService e = new CustomExecutor(2);
1085          try {
1086 <            ArrayList<Callable<String>> l = new ArrayList<Callable<String>>();
1086 >            List<Callable<String>> l = new ArrayList<Callable<String>>();
1087              l.add(new StringTask());
1088              l.add(new StringTask());
1089 <            String result = e.invokeAny(l, MEDIUM_DELAY_MS, TimeUnit.MILLISECONDS);
1089 >            String result = e.invokeAny(l, MEDIUM_DELAY_MS, MILLISECONDS);
1090              assertSame(TEST_STRING, result);
1058        } catch (ExecutionException success) {
1059        } catch(Exception ex) {
1060            unexpectedException();
1091          } finally {
1092              joinPool(e);
1093          }
# Line 1066 | Line 1096 | public class ScheduledExecutorSubclassTe
1096      /**
1097       * timed invokeAll(null) throws NPE
1098       */
1099 <    public void testTimedInvokeAll1() {
1099 >    public void testTimedInvokeAll1() throws Exception {
1100          ExecutorService e = new CustomExecutor(2);
1101          try {
1102 <            e.invokeAll(null, MEDIUM_DELAY_MS, TimeUnit.MILLISECONDS);
1102 >            e.invokeAll(null, MEDIUM_DELAY_MS, MILLISECONDS);
1103 >            shouldThrow();
1104          } catch (NullPointerException success) {
1074        } catch(Exception ex) {
1075            unexpectedException();
1105          } finally {
1106              joinPool(e);
1107          }
# Line 1081 | Line 1110 | public class ScheduledExecutorSubclassTe
1110      /**
1111       * timed invokeAll(,,null) throws NPE
1112       */
1113 <    public void testTimedInvokeAllNullTimeUnit() {
1113 >    public void testTimedInvokeAllNullTimeUnit() throws Exception {
1114          ExecutorService e = new CustomExecutor(2);
1115 +        List<Callable<String>> l = new ArrayList<Callable<String>>();
1116 +        l.add(new StringTask());
1117          try {
1087            ArrayList<Callable<String>> l = new ArrayList<Callable<String>>();
1088            l.add(new StringTask());
1118              e.invokeAll(l, MEDIUM_DELAY_MS, null);
1119 +            shouldThrow();
1120          } catch (NullPointerException success) {
1091        } catch(Exception ex) {
1092            unexpectedException();
1121          } finally {
1122              joinPool(e);
1123          }
# Line 1098 | Line 1126 | public class ScheduledExecutorSubclassTe
1126      /**
1127       * timed invokeAll(empty collection) returns empty collection
1128       */
1129 <    public void testTimedInvokeAll2() {
1129 >    public void testTimedInvokeAll2() throws Exception {
1130          ExecutorService e = new CustomExecutor(2);
1131          try {
1132 <            List<Future<String>> r = e.invokeAll(new ArrayList<Callable<String>>(), MEDIUM_DELAY_MS, TimeUnit.MILLISECONDS);
1132 >            List<Future<String>> r = e.invokeAll(new ArrayList<Callable<String>>(), MEDIUM_DELAY_MS, MILLISECONDS);
1133              assertTrue(r.isEmpty());
1106        } catch(Exception ex) {
1107            unexpectedException();
1134          } finally {
1135              joinPool(e);
1136          }
# Line 1113 | Line 1139 | public class ScheduledExecutorSubclassTe
1139      /**
1140       * timed invokeAll(c) throws NPE if c has null elements
1141       */
1142 <    public void testTimedInvokeAll3() {
1142 >    public void testTimedInvokeAll3() throws Exception {
1143          ExecutorService e = new CustomExecutor(2);
1144 +        List<Callable<String>> l = new ArrayList<Callable<String>>();
1145 +        l.add(new StringTask());
1146 +        l.add(null);
1147          try {
1148 <            ArrayList<Callable<String>> l = new ArrayList<Callable<String>>();
1149 <            l.add(new StringTask());
1121 <            l.add(null);
1122 <            e.invokeAll(l, MEDIUM_DELAY_MS, TimeUnit.MILLISECONDS);
1148 >            e.invokeAll(l, MEDIUM_DELAY_MS, MILLISECONDS);
1149 >            shouldThrow();
1150          } catch (NullPointerException success) {
1124        } catch(Exception ex) {
1125            unexpectedException();
1151          } finally {
1152              joinPool(e);
1153          }
# Line 1131 | Line 1156 | public class ScheduledExecutorSubclassTe
1156      /**
1157       * get of element of invokeAll(c) throws exception on failed task
1158       */
1159 <    public void testTimedInvokeAll4() {
1159 >    public void testTimedInvokeAll4() throws Exception {
1160          ExecutorService e = new CustomExecutor(2);
1161 +        List<Callable<String>> l = new ArrayList<Callable<String>>();
1162 +        l.add(new NPETask());
1163 +        List<Future<String>> futures =
1164 +            e.invokeAll(l, MEDIUM_DELAY_MS, MILLISECONDS);
1165 +        assertEquals(1, futures.size());
1166          try {
1167 <            ArrayList<Callable<String>> l = new ArrayList<Callable<String>>();
1168 <            l.add(new NPETask());
1169 <            List<Future<String>> result = e.invokeAll(l, MEDIUM_DELAY_MS, TimeUnit.MILLISECONDS);
1170 <            assertEquals(1, result.size());
1141 <            for (Iterator<Future<String>> it = result.iterator(); it.hasNext();)
1142 <                it.next().get();
1143 <        } catch(ExecutionException success) {
1144 <        } catch(Exception ex) {
1145 <            unexpectedException();
1167 >            futures.get(0).get();
1168 >            shouldThrow();
1169 >        } catch (ExecutionException success) {
1170 >            assertTrue(success.getCause() instanceof NullPointerException);
1171          } finally {
1172              joinPool(e);
1173          }
# Line 1151 | Line 1176 | public class ScheduledExecutorSubclassTe
1176      /**
1177       * timed invokeAll(c) returns results of all completed tasks
1178       */
1179 <    public void testTimedInvokeAll5() {
1179 >    public void testTimedInvokeAll5() throws Exception {
1180          ExecutorService e = new CustomExecutor(2);
1181          try {
1182 <            ArrayList<Callable<String>> l = new ArrayList<Callable<String>>();
1182 >            List<Callable<String>> l = new ArrayList<Callable<String>>();
1183              l.add(new StringTask());
1184              l.add(new StringTask());
1185 <            List<Future<String>> result = e.invokeAll(l, MEDIUM_DELAY_MS, TimeUnit.MILLISECONDS);
1186 <            assertEquals(2, result.size());
1187 <            for (Iterator<Future<String>> it = result.iterator(); it.hasNext();)
1188 <                assertSame(TEST_STRING, it.next().get());
1189 <        } catch (ExecutionException success) {
1165 <        } catch(Exception ex) {
1166 <            unexpectedException();
1185 >            List<Future<String>> futures =
1186 >                e.invokeAll(l, MEDIUM_DELAY_MS, MILLISECONDS);
1187 >            assertEquals(2, futures.size());
1188 >            for (Future<String> future : futures)
1189 >                assertSame(TEST_STRING, future.get());
1190          } finally {
1191              joinPool(e);
1192          }
# Line 1172 | Line 1195 | public class ScheduledExecutorSubclassTe
1195      /**
1196       * timed invokeAll(c) cancels tasks not completed by timeout
1197       */
1198 <    public void testTimedInvokeAll6() {
1198 >    public void testTimedInvokeAll6() throws Exception {
1199          ExecutorService e = new CustomExecutor(2);
1200          try {
1201 <            ArrayList<Callable<String>> l = new ArrayList<Callable<String>>();
1201 >            List<Callable<String>> l = new ArrayList<Callable<String>>();
1202              l.add(new StringTask());
1203              l.add(Executors.callable(new MediumPossiblyInterruptedRunnable(), TEST_STRING));
1204              l.add(new StringTask());
1205 <            List<Future<String>> result = e.invokeAll(l, SHORT_DELAY_MS, TimeUnit.MILLISECONDS);
1206 <            assertEquals(3, result.size());
1207 <            Iterator<Future<String>> it = result.iterator();
1208 <            Future<String> f1 = it.next();
1209 <            Future<String> f2 = it.next();
1210 <            Future<String> f3 = it.next();
1211 <            assertTrue(f1.isDone());
1189 <            assertTrue(f2.isDone());
1190 <            assertTrue(f3.isDone());
1191 <            assertFalse(f1.isCancelled());
1192 <            assertTrue(f2.isCancelled());
1193 <        } catch(Exception ex) {
1194 <            unexpectedException();
1205 >            List<Future<String>> futures =
1206 >                e.invokeAll(l, SHORT_DELAY_MS, MILLISECONDS);
1207 >            assertEquals(l.size(), futures.size());
1208 >            for (Future future : futures)
1209 >                assertTrue(future.isDone());
1210 >            assertFalse(futures.get(0).isCancelled());
1211 >            assertTrue(futures.get(1).isCancelled());
1212          } finally {
1213              joinPool(e);
1214          }
1215      }
1216  
1200
1217   }

Diff Legend

Removed lines
+ Added lines
< Changed lines
> Changed lines