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 
  38 import java.security.AccessControlContext;
  39 import java.security.AccessControlException;
  40 import java.security.AccessController;
  41 import java.security.PrivilegedAction;
  42 import java.security.PrivilegedExceptionAction;
  43 import java.util.ArrayList;
  44 import java.util.List;
  45 import java.util.concurrent.Callable;
  46 import java.util.concurrent.CountDownLatch;
  47 import java.util.concurrent.Executors;
  48 import java.util.concurrent.ExecutorService;
  49 import java.util.concurrent.Future;
  50 import java.util.concurrent.ScheduledExecutorService;
  51 import java.util.concurrent.ThreadPoolExecutor;
  52 
  53 import junit.framework.Test;
  54 import junit.framework.TestSuite;
  55 
  56 public class ExecutorsTest extends JSR166TestCase {
  57     public static void main(String[] args) {
  58         main(suite(), args);
  59     }
  60     public static Test suite() {
  61         return new TestSuite(ExecutorsTest.class);
  62     }
  63 
  64     /**
  65      * A newCachedThreadPool can execute runnables
  66      */
  67     public void testNewCachedThreadPool1() {
  68         final ExecutorService e = Executors.newCachedThreadPool();
  69         try (PoolCleaner cleaner = cleaner(e)) {
  70             e.execute(new NoOpRunnable());
  71             e.execute(new NoOpRunnable());
  72             e.execute(new NoOpRunnable());
  73         }
  74     }
  75 
  76     /**
  77      * A newCachedThreadPool with given ThreadFactory can execute runnables
  78      */
  79     public void testNewCachedThreadPool2() {
  80         final ExecutorService e = Executors.newCachedThreadPool(new SimpleThreadFactory());
  81         try (PoolCleaner cleaner = cleaner(e)) {
  82             e.execute(new NoOpRunnable());
  83             e.execute(new NoOpRunnable());
  84             e.execute(new NoOpRunnable());
  85         }
  86     }
  87 
  88     /**
  89      * A newCachedThreadPool with null ThreadFactory throws NPE
  90      */
  91     public void testNewCachedThreadPool3() {
  92         try {
  93             ExecutorService unused = Executors.newCachedThreadPool(null);
  94             shouldThrow();
  95         } catch (NullPointerException success) {}
  96     }
  97 
  98     /**
  99      * A new SingleThreadExecutor can execute runnables
 100      */
 101     public void testNewSingleThreadExecutor1() {
 102         final ExecutorService e = Executors.newSingleThreadExecutor();
 103         try (PoolCleaner cleaner = cleaner(e)) {
 104             e.execute(new NoOpRunnable());
 105             e.execute(new NoOpRunnable());
 106             e.execute(new NoOpRunnable());
 107         }
 108     }
 109 
 110     /**
 111      * A new SingleThreadExecutor with given ThreadFactory can execute runnables
 112      */
 113     public void testNewSingleThreadExecutor2() {
 114         final ExecutorService e = Executors.newSingleThreadExecutor(new SimpleThreadFactory());
 115         try (PoolCleaner cleaner = cleaner(e)) {
 116             e.execute(new NoOpRunnable());
 117             e.execute(new NoOpRunnable());
 118             e.execute(new NoOpRunnable());
 119         }
 120     }
 121 
 122     /**
 123      * A new SingleThreadExecutor with null ThreadFactory throws NPE
 124      */
 125     public void testNewSingleThreadExecutor3() {
 126         try {
 127             ExecutorService unused = Executors.newSingleThreadExecutor(null);
 128             shouldThrow();
 129         } catch (NullPointerException success) {}
 130     }
 131 
 132     /**
 133      * A new SingleThreadExecutor cannot be casted to concrete implementation
 134      */
 135     public void testCastNewSingleThreadExecutor() {
 136         final ExecutorService e = Executors.newSingleThreadExecutor();
 137         try (PoolCleaner cleaner = cleaner(e)) {
 138             try {
 139                 ThreadPoolExecutor tpe = (ThreadPoolExecutor)e;
 140                 shouldThrow();
 141             } catch (ClassCastException success) {}
 142         }
 143     }
 144 
 145     /**
 146      * A new newFixedThreadPool can execute runnables
 147      */
 148     public void testNewFixedThreadPool1() {
 149         final ExecutorService e = Executors.newFixedThreadPool(2);
 150         try (PoolCleaner cleaner = cleaner(e)) {
 151             e.execute(new NoOpRunnable());
 152             e.execute(new NoOpRunnable());
 153             e.execute(new NoOpRunnable());
 154         }
 155     }
 156 
 157     /**
 158      * A new newFixedThreadPool with given ThreadFactory can execute runnables
 159      */
 160     public void testNewFixedThreadPool2() {
 161         final ExecutorService e = Executors.newFixedThreadPool(2, new SimpleThreadFactory());
 162         try (PoolCleaner cleaner = cleaner(e)) {
 163             e.execute(new NoOpRunnable());
 164             e.execute(new NoOpRunnable());
 165             e.execute(new NoOpRunnable());
 166         }
 167     }
 168 
 169     /**
 170      * A new newFixedThreadPool with null ThreadFactory throws
 171      * NullPointerException
 172      */
 173     public void testNewFixedThreadPool3() {
 174         try {
 175             ExecutorService unused = Executors.newFixedThreadPool(2, null);
 176             shouldThrow();
 177         } catch (NullPointerException success) {}
 178     }
 179 
 180     /**
 181      * A new newFixedThreadPool with 0 threads throws IllegalArgumentException
 182      */
 183     public void testNewFixedThreadPool4() {
 184         try {
 185             ExecutorService unused = Executors.newFixedThreadPool(0);
 186             shouldThrow();
 187         } catch (IllegalArgumentException success) {}
 188     }
 189 
 190     /**
 191      * An unconfigurable newFixedThreadPool can execute runnables
 192      */
 193     public void testUnconfigurableExecutorService() {
 194         final ExecutorService e = Executors.unconfigurableExecutorService(Executors.newFixedThreadPool(2));
 195         try (PoolCleaner cleaner = cleaner(e)) {
 196             e.execute(new NoOpRunnable());
 197             e.execute(new NoOpRunnable());
 198             e.execute(new NoOpRunnable());
 199         }
 200     }
 201 
 202     /**
 203      * unconfigurableExecutorService(null) throws NPE
 204      */
 205     public void testUnconfigurableExecutorServiceNPE() {
 206         try {
 207             ExecutorService unused =
 208                 Executors.unconfigurableExecutorService(null);
 209             shouldThrow();
 210         } catch (NullPointerException success) {}
 211     }
 212 
 213     /**
 214      * unconfigurableScheduledExecutorService(null) throws NPE
 215      */
 216     public void testUnconfigurableScheduledExecutorServiceNPE() {
 217         try {
 218             ExecutorService unused =
 219                 Executors.unconfigurableScheduledExecutorService(null);
 220             shouldThrow();
 221         } catch (NullPointerException success) {}
 222     }
 223 
 224     /**
 225      * a newSingleThreadScheduledExecutor successfully runs delayed task
 226      */
 227     public void testNewSingleThreadScheduledExecutor() throws Exception {
 228         final ScheduledExecutorService p = Executors.newSingleThreadScheduledExecutor();
 229         try (PoolCleaner cleaner = cleaner(p)) {
 230             final CountDownLatch proceed = new CountDownLatch(1);
 231             final Runnable task = new CheckedRunnable() {
 232                 public void realRun() {
 233                     await(proceed);
 234                 }};
 235             long startTime = System.nanoTime();
 236             Future f = p.schedule(Executors.callable(task, Boolean.TRUE),
 237                                   timeoutMillis(), MILLISECONDS);
 238             assertFalse(f.isDone());
 239             proceed.countDown();
 240             assertSame(Boolean.TRUE, f.get(LONG_DELAY_MS, MILLISECONDS));
 241             assertSame(Boolean.TRUE, f.get());
 242             assertTrue(f.isDone());
 243             assertFalse(f.isCancelled());
 244             assertTrue(millisElapsedSince(startTime) >= timeoutMillis());
 245         }
 246     }
 247 
 248     /**
 249      * a newScheduledThreadPool successfully runs delayed task
 250      */
 251     public void testNewScheduledThreadPool() throws Exception {
 252         final ScheduledExecutorService p = Executors.newScheduledThreadPool(2);
 253         try (PoolCleaner cleaner = cleaner(p)) {
 254             final CountDownLatch proceed = new CountDownLatch(1);
 255             final Runnable task = new CheckedRunnable() {
 256                 public void realRun() {
 257                     await(proceed);
 258                 }};
 259             long startTime = System.nanoTime();
 260             Future f = p.schedule(Executors.callable(task, Boolean.TRUE),
 261                                   timeoutMillis(), MILLISECONDS);
 262             assertFalse(f.isDone());
 263             proceed.countDown();
 264             assertSame(Boolean.TRUE, f.get(LONG_DELAY_MS, MILLISECONDS));
 265             assertSame(Boolean.TRUE, f.get());
 266             assertTrue(f.isDone());
 267             assertFalse(f.isCancelled());
 268             assertTrue(millisElapsedSince(startTime) >= timeoutMillis());
 269         }
 270     }
 271 
 272     /**
 273      * an unconfigurable newScheduledThreadPool successfully runs delayed task
 274      */
 275     public void testUnconfigurableScheduledExecutorService() throws Exception {
 276         final ScheduledExecutorService p =
 277             Executors.unconfigurableScheduledExecutorService
 278             (Executors.newScheduledThreadPool(2));
 279         try (PoolCleaner cleaner = cleaner(p)) {
 280             final CountDownLatch proceed = new CountDownLatch(1);
 281             final Runnable task = new CheckedRunnable() {
 282                 public void realRun() {
 283                     await(proceed);
 284                 }};
 285             long startTime = System.nanoTime();
 286             Future f = p.schedule(Executors.callable(task, Boolean.TRUE),
 287                                   timeoutMillis(), MILLISECONDS);
 288             assertFalse(f.isDone());
 289             proceed.countDown();
 290             assertSame(Boolean.TRUE, f.get(LONG_DELAY_MS, MILLISECONDS));
 291             assertSame(Boolean.TRUE, f.get());
 292             assertTrue(f.isDone());
 293             assertFalse(f.isCancelled());
 294             assertTrue(millisElapsedSince(startTime) >= timeoutMillis());
 295         }
 296     }
 297 
 298     /**
 299      * Future.get on submitted tasks will time out if they compute too long.
 300      */
 301     public void testTimedCallable() throws Exception {
 302         final ExecutorService[] executors = {
 303             Executors.newSingleThreadExecutor(),
 304             Executors.newCachedThreadPool(),
 305             Executors.newFixedThreadPool(2),
 306             Executors.newScheduledThreadPool(2),
 307         };
 308 
 309         final CountDownLatch done = new CountDownLatch(1);
 310 
 311         final Runnable sleeper = new CheckedRunnable() {
 312             public void realRun() throws InterruptedException {
 313                 done.await(LONG_DELAY_MS, MILLISECONDS);
 314             }};
 315 
 316         List<Thread> threads = new ArrayList<>();
 317         for (final ExecutorService executor : executors) {
 318             threads.add(newStartedThread(new CheckedRunnable() {
 319                 public void realRun() {
 320                     Future future = executor.submit(sleeper);
 321                     assertFutureTimesOut(future);
 322                 }}));
 323         }
 324         for (Thread thread : threads)
 325             awaitTermination(thread);
 326         done.countDown();
 327         for (ExecutorService executor : executors)
 328             joinPool(executor);
 329     }
 330 
 331     /**
 332      * ThreadPoolExecutor using defaultThreadFactory has
 333      * specified group, priority, daemon status, and name
 334      */
 335     public void testDefaultThreadFactory() throws Exception {
 336         final ThreadGroup egroup = Thread.currentThread().getThreadGroup();
 337         final CountDownLatch done = new CountDownLatch(1);
 338         Runnable r = new CheckedRunnable() {
 339             public void realRun() {
 340                 try {
 341                     Thread current = Thread.currentThread();
 342                     assertFalse(current.isDaemon());
 343                     assertTrue(current.getPriority() <= Thread.NORM_PRIORITY);
 344                     SecurityManager s = System.getSecurityManager();
 345                     assertSame(current.getThreadGroup(),
 346                                (s == null) ? egroup : s.getThreadGroup());
 347                     assertTrue(current.getName().endsWith("thread-1"));
 348                 } catch (SecurityException ok) {
 349                     // Also pass if not allowed to change setting
 350                 }
 351                 done.countDown();
 352             }};
 353         ExecutorService e = Executors.newSingleThreadExecutor(Executors.defaultThreadFactory());
 354         try (PoolCleaner cleaner = cleaner(e)) {
 355             e.execute(r);
 356             await(done);
 357         }
 358     }
 359 
 360     /**
 361      * ThreadPoolExecutor using privilegedThreadFactory has
 362      * specified group, priority, daemon status, name,
 363      * access control context and context class loader
 364      */
 365     public void testPrivilegedThreadFactory() throws Exception {
 366         final CountDownLatch done = new CountDownLatch(1);
 367         Runnable r = new CheckedRunnable() {
 368             public void realRun() throws Exception {
 369                 final ThreadGroup egroup = Thread.currentThread().getThreadGroup();
 370                 final ClassLoader thisccl = Thread.currentThread().getContextClassLoader();
 371                 final AccessControlContext thisacc = AccessController.getContext();
 372                 Runnable r = new CheckedRunnable() {
 373                     public void realRun() {
 374                         Thread current = Thread.currentThread();
 375                         assertFalse(current.isDaemon());
 376                         assertTrue(current.getPriority() <= Thread.NORM_PRIORITY);
 377                         SecurityManager s = System.getSecurityManager();
 378                         assertSame(current.getThreadGroup(),
 379                                    (s == null) ? egroup : s.getThreadGroup());
 380                         assertTrue(current.getName().endsWith("thread-1"));
 381                         assertSame(thisccl, current.getContextClassLoader());
 382                         assertEquals(thisacc, AccessController.getContext());
 383                         done.countDown();
 384                     }};
 385                 ExecutorService e = Executors.newSingleThreadExecutor(Executors.privilegedThreadFactory());
 386                 try (PoolCleaner cleaner = cleaner(e)) {
 387                     e.execute(r);
 388                     await(done);
 389                 }
 390             }};
 391 
 392         runWithPermissions(r,
 393                            new RuntimePermission("getClassLoader"),
 394                            new RuntimePermission("setContextClassLoader"),
 395                            new RuntimePermission("modifyThread"));
 396     }
 397 
 398     boolean haveCCLPermissions() {
 399         SecurityManager sm = System.getSecurityManager();
 400         if (sm != null) {
 401             try {
 402                 sm.checkPermission(new RuntimePermission("setContextClassLoader"));
 403                 sm.checkPermission(new RuntimePermission("getClassLoader"));
 404             } catch (AccessControlException e) {
 405                 return false;
 406             }
 407         }
 408         return true;
 409     }
 410 
 411     void checkCCL() {
 412         SecurityManager sm = System.getSecurityManager();
 413         if (sm != null) {
 414             sm.checkPermission(new RuntimePermission("setContextClassLoader"));
 415             sm.checkPermission(new RuntimePermission("getClassLoader"));
 416         }
 417     }
 418 
 419     class CheckCCL implements Callable<Object> {
 420         public Object call() {
 421             checkCCL();
 422             return null;
 423         }
 424     }
 425 
 426     /**
 427      * Without class loader permissions, creating
 428      * privilegedCallableUsingCurrentClassLoader throws ACE
 429      */
 430     public void testCreatePrivilegedCallableUsingCCLWithNoPrivs() {
 431         Runnable r = new CheckedRunnable() {
 432             public void realRun() throws Exception {
 433                 if (System.getSecurityManager() == null)
 434                     return;
 435                 try {
 436                     Executors.privilegedCallableUsingCurrentClassLoader(new NoOpCallable());
 437                     shouldThrow();
 438                 } catch (AccessControlException success) {}
 439             }};
 440 
 441         runWithoutPermissions(r);
 442     }
 443 
 444     /**
 445      * With class loader permissions, calling
 446      * privilegedCallableUsingCurrentClassLoader does not throw ACE
 447      */
 448     public void testPrivilegedCallableUsingCCLWithPrivs() throws Exception {
 449         Runnable r = new CheckedRunnable() {
 450             public void realRun() throws Exception {
 451                 Executors.privilegedCallableUsingCurrentClassLoader
 452                     (new NoOpCallable())
 453                     .call();
 454             }};
 455 
 456         runWithPermissions(r,
 457                            new RuntimePermission("getClassLoader"),
 458                            new RuntimePermission("setContextClassLoader"));
 459     }
 460 
 461     /**
 462      * Without permissions, calling privilegedCallable throws ACE
 463      */
 464     public void testPrivilegedCallableWithNoPrivs() throws Exception {
 465         // Avoid classloader-related SecurityExceptions in swingui.TestRunner
 466         Executors.privilegedCallable(new CheckCCL());
 467 
 468         Runnable r = new CheckedRunnable() {
 469             public void realRun() throws Exception {
 470                 if (System.getSecurityManager() == null)
 471                     return;
 472                 Callable task = Executors.privilegedCallable(new CheckCCL());
 473                 try {
 474                     task.call();
 475                     shouldThrow();
 476                 } catch (AccessControlException success) {}
 477             }};
 478 
 479         runWithoutPermissions(r);
 480 
 481         // It seems rather difficult to test that the
 482         // AccessControlContext of the privilegedCallable is used
 483         // instead of its caller.  Below is a failed attempt to do
 484         // that, which does not work because the AccessController
 485         // cannot capture the internal state of the current Policy.
 486         // It would be much more work to differentiate based on,
 487         // e.g. CodeSource.
 488 
 489 //         final AccessControlContext[] noprivAcc = new AccessControlContext[1];
 490 //         final Callable[] task = new Callable[1];
 491 
 492 //         runWithPermissions
 493 //             (new CheckedRunnable() {
 494 //                 public void realRun() {
 495 //                     if (System.getSecurityManager() == null)
 496 //                         return;
 497 //                     noprivAcc[0] = AccessController.getContext();
 498 //                     task[0] = Executors.privilegedCallable(new CheckCCL());
 499 //                     try {
 500 //                         AccessController.doPrivileged(new PrivilegedAction<Void>() {
 501 //                                                           public Void run() {
 502 //                                                               checkCCL();
 503 //                                                               return null;
 504 //                                                           }}, noprivAcc[0]);
 505 //                         shouldThrow();
 506 //                     } catch (AccessControlException success) {}
 507 //                 }});
 508 
 509 //         runWithPermissions
 510 //             (new CheckedRunnable() {
 511 //                 public void realRun() throws Exception {
 512 //                     if (System.getSecurityManager() == null)
 513 //                         return;
 514 //                     // Verify that we have an underprivileged ACC
 515 //                     try {
 516 //                         AccessController.doPrivileged(new PrivilegedAction<Void>() {
 517 //                                                           public Void run() {
 518 //                                                               checkCCL();
 519 //                                                               return null;
 520 //                                                           }}, noprivAcc[0]);
 521 //                         shouldThrow();
 522 //                     } catch (AccessControlException success) {}
 523 
 524 //                     try {
 525 //                         task[0].call();
 526 //                         shouldThrow();
 527 //                     } catch (AccessControlException success) {}
 528 //                 }},
 529 //              new RuntimePermission("getClassLoader"),
 530 //              new RuntimePermission("setContextClassLoader"));
 531     }
 532 
 533     /**
 534      * With permissions, calling privilegedCallable succeeds
 535      */
 536     public void testPrivilegedCallableWithPrivs() throws Exception {
 537         Runnable r = new CheckedRunnable() {
 538             public void realRun() throws Exception {
 539                 Executors.privilegedCallable(new CheckCCL()).call();
 540             }};
 541 
 542         runWithPermissions(r,
 543                            new RuntimePermission("getClassLoader"),
 544                            new RuntimePermission("setContextClassLoader"));
 545     }
 546 
 547     /**
 548      * callable(Runnable) returns null when called
 549      */
 550     public void testCallable1() throws Exception {
 551         Callable c = Executors.callable(new NoOpRunnable());
 552         assertNull(c.call());
 553     }
 554 
 555     /**
 556      * callable(Runnable, result) returns result when called
 557      */
 558     public void testCallable2() throws Exception {
 559         Callable c = Executors.callable(new NoOpRunnable(), one);
 560         assertSame(one, c.call());
 561     }
 562 
 563     /**
 564      * callable(PrivilegedAction) returns its result when called
 565      */
 566     public void testCallable3() throws Exception {
 567         Callable c = Executors.callable(new PrivilegedAction() {
 568                 public Object run() { return one; }});
 569         assertSame(one, c.call());
 570     }
 571 
 572     /**
 573      * callable(PrivilegedExceptionAction) returns its result when called
 574      */
 575     public void testCallable4() throws Exception {
 576         Callable c = Executors.callable(new PrivilegedExceptionAction() {
 577                 public Object run() { return one; }});
 578         assertSame(one, c.call());
 579     }
 580 
 581     /**
 582      * callable(null Runnable) throws NPE
 583      */
 584     public void testCallableNPE1() {
 585         try {
 586             Callable unused = Executors.callable((Runnable) null);
 587             shouldThrow();
 588         } catch (NullPointerException success) {}
 589     }
 590 
 591     /**
 592      * callable(null, result) throws NPE
 593      */
 594     public void testCallableNPE2() {
 595         try {
 596             Callable unused = Executors.callable((Runnable) null, one);
 597             shouldThrow();
 598         } catch (NullPointerException success) {}
 599     }
 600 
 601     /**
 602      * callable(null PrivilegedAction) throws NPE
 603      */
 604     public void testCallableNPE3() {
 605         try {
 606             Callable unused = Executors.callable((PrivilegedAction) null);
 607             shouldThrow();
 608         } catch (NullPointerException success) {}
 609     }
 610 
 611     /**
 612      * callable(null PrivilegedExceptionAction) throws NPE
 613      */
 614     public void testCallableNPE4() {
 615         try {
 616             Callable unused = Executors.callable((PrivilegedExceptionAction) null);
 617             shouldThrow();
 618         } catch (NullPointerException success) {}
 619     }
 620 
 621     /**
 622      * callable(runnable, x).toString() contains toString of wrapped task
 623      */
 624     public void testCallable_withResult_toString() {
 625         if (testImplementationDetails) {
 626             Runnable r = () -> {};
 627             Callable<String> c = Executors.callable(r, "");
 628             assertEquals(
 629                 identityString(c) + "[Wrapped task = " + r.toString() + "]",
 630                 c.toString());
 631         }
 632     }
 633 
 634     /**
 635      * callable(runnable).toString() contains toString of wrapped task
 636      */
 637     public void testCallable_toString() {
 638         if (testImplementationDetails) {
 639             Runnable r = () -> {};
 640             Callable<Object> c = Executors.callable(r);
 641             assertEquals(
 642                 identityString(c) + "[Wrapped task = " + r.toString() + "]",
 643                 c.toString());
 644         }
 645     }
 646 
 647     /**
 648      * privilegedCallable(callable).toString() contains toString of wrapped task
 649      */
 650     public void testPrivilegedCallable_toString() {
 651         if (testImplementationDetails) {
 652             Callable<String> c = () -> "";
 653             Callable<String> priv = Executors.privilegedCallable(c);
 654             assertEquals(
 655                 identityString(priv) + "[Wrapped task = " + c.toString() + "]",
 656                 priv.toString());
 657         }
 658     }
 659 
 660     /**
 661      * privilegedCallableUsingCurrentClassLoader(callable).toString()
 662      * contains toString of wrapped task
 663      */
 664     public void testPrivilegedCallableUsingCurrentClassLoader_toString() {
 665         if (testImplementationDetails) {
 666             Callable<String> c = () -> "";
 667             Callable<String> priv = Executors.privilegedCallableUsingCurrentClassLoader(c);
 668             assertEquals(
 669                 identityString(priv) + "[Wrapped task = " + c.toString() + "]",
 670                 priv.toString());
 671         }
 672     }
 673 }