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 e = 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 e = 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 e = 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 e = 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 e = Executors.unconfigurableExecutorService(null);
 208             shouldThrow();
 209         } catch (NullPointerException success) {}
 210     }
 211 
 212     /**
 213      * unconfigurableScheduledExecutorService(null) throws NPE
 214      */
 215     public void testUnconfigurableScheduledExecutorServiceNPE() {
 216         try {
 217             ExecutorService e = Executors.unconfigurableScheduledExecutorService(null);
 218             shouldThrow();
 219         } catch (NullPointerException success) {}
 220     }
 221 
 222     /**
 223      * a newSingleThreadScheduledExecutor successfully runs delayed task
 224      */
 225     public void testNewSingleThreadScheduledExecutor() throws Exception {
 226         final ScheduledExecutorService p = Executors.newSingleThreadScheduledExecutor();
 227         try (PoolCleaner cleaner = cleaner(p)) {
 228             final CountDownLatch proceed = new CountDownLatch(1);
 229             final Runnable task = new CheckedRunnable() {
 230                 public void realRun() {
 231                     await(proceed);
 232                 }};
 233             long startTime = System.nanoTime();
 234             Future f = p.schedule(Executors.callable(task, Boolean.TRUE),
 235                                   timeoutMillis(), MILLISECONDS);
 236             assertFalse(f.isDone());
 237             proceed.countDown();
 238             assertSame(Boolean.TRUE, f.get(LONG_DELAY_MS, MILLISECONDS));
 239             assertSame(Boolean.TRUE, f.get());
 240             assertTrue(f.isDone());
 241             assertFalse(f.isCancelled());
 242             assertTrue(millisElapsedSince(startTime) >= timeoutMillis());
 243         }
 244     }
 245 
 246     /**
 247      * a newScheduledThreadPool successfully runs delayed task
 248      */
 249     public void testNewScheduledThreadPool() throws Exception {
 250         final ScheduledExecutorService p = Executors.newScheduledThreadPool(2);
 251         try (PoolCleaner cleaner = cleaner(p)) {
 252             final CountDownLatch proceed = new CountDownLatch(1);
 253             final Runnable task = new CheckedRunnable() {
 254                 public void realRun() {
 255                     await(proceed);
 256                 }};
 257             long startTime = System.nanoTime();
 258             Future f = p.schedule(Executors.callable(task, Boolean.TRUE),
 259                                   timeoutMillis(), MILLISECONDS);
 260             assertFalse(f.isDone());
 261             proceed.countDown();
 262             assertSame(Boolean.TRUE, f.get(LONG_DELAY_MS, MILLISECONDS));
 263             assertSame(Boolean.TRUE, f.get());
 264             assertTrue(f.isDone());
 265             assertFalse(f.isCancelled());
 266             assertTrue(millisElapsedSince(startTime) >= timeoutMillis());
 267         }
 268     }
 269 
 270     /**
 271      * an unconfigurable newScheduledThreadPool successfully runs delayed task
 272      */
 273     public void testUnconfigurableScheduledExecutorService() throws Exception {
 274         final ScheduledExecutorService p =
 275             Executors.unconfigurableScheduledExecutorService
 276             (Executors.newScheduledThreadPool(2));
 277         try (PoolCleaner cleaner = cleaner(p)) {
 278             final CountDownLatch proceed = new CountDownLatch(1);
 279             final Runnable task = new CheckedRunnable() {
 280                 public void realRun() {
 281                     await(proceed);
 282                 }};
 283             long startTime = System.nanoTime();
 284             Future f = p.schedule(Executors.callable(task, Boolean.TRUE),
 285                                   timeoutMillis(), MILLISECONDS);
 286             assertFalse(f.isDone());
 287             proceed.countDown();
 288             assertSame(Boolean.TRUE, f.get(LONG_DELAY_MS, MILLISECONDS));
 289             assertSame(Boolean.TRUE, f.get());
 290             assertTrue(f.isDone());
 291             assertFalse(f.isCancelled());
 292             assertTrue(millisElapsedSince(startTime) >= timeoutMillis());
 293         }
 294     }
 295 
 296     /**
 297      * Future.get on submitted tasks will time out if they compute too long.
 298      */
 299     public void testTimedCallable() throws Exception {
 300         final ExecutorService[] executors = {
 301             Executors.newSingleThreadExecutor(),
 302             Executors.newCachedThreadPool(),
 303             Executors.newFixedThreadPool(2),
 304             Executors.newScheduledThreadPool(2),
 305         };
 306 
 307         final Runnable sleeper = new CheckedInterruptedRunnable() {
 308             public void realRun() throws InterruptedException {
 309                 delay(LONG_DELAY_MS);
 310             }};
 311 
 312         List<Thread> threads = new ArrayList<>();
 313         for (final ExecutorService executor : executors) {
 314             threads.add(newStartedThread(new CheckedRunnable() {
 315                 public void realRun() {
 316                     Future future = executor.submit(sleeper);
 317                     assertFutureTimesOut(future);
 318                 }}));
 319         }
 320         for (Thread thread : threads)
 321             awaitTermination(thread);
 322         for (ExecutorService executor : executors)
 323             joinPool(executor);
 324     }
 325 
 326     /**
 327      * ThreadPoolExecutor using defaultThreadFactory has
 328      * specified group, priority, daemon status, and name
 329      */
 330     public void testDefaultThreadFactory() throws Exception {
 331         final ThreadGroup egroup = Thread.currentThread().getThreadGroup();
 332         final CountDownLatch done = new CountDownLatch(1);
 333         Runnable r = new CheckedRunnable() {
 334             public void realRun() {
 335                 try {
 336                     Thread current = Thread.currentThread();
 337                     assertFalse(current.isDaemon());
 338                     assertTrue(current.getPriority() <= Thread.NORM_PRIORITY);
 339                     SecurityManager s = System.getSecurityManager();
 340                     assertSame(current.getThreadGroup(),
 341                                (s == null) ? egroup : s.getThreadGroup());
 342                     assertTrue(current.getName().endsWith("thread-1"));
 343                 } catch (SecurityException ok) {
 344                     // Also pass if not allowed to change setting
 345                 }
 346                 done.countDown();
 347             }};
 348         ExecutorService e = Executors.newSingleThreadExecutor(Executors.defaultThreadFactory());
 349         try (PoolCleaner cleaner = cleaner(e)) {
 350             e.execute(r);
 351             await(done);
 352         }
 353     }
 354 
 355     /**
 356      * ThreadPoolExecutor using privilegedThreadFactory has
 357      * specified group, priority, daemon status, name,
 358      * access control context and context class loader
 359      */
 360     public void testPrivilegedThreadFactory() throws Exception {
 361         final CountDownLatch done = new CountDownLatch(1);
 362         Runnable r = new CheckedRunnable() {
 363             public void realRun() throws Exception {
 364                 final ThreadGroup egroup = Thread.currentThread().getThreadGroup();
 365                 final ClassLoader thisccl = Thread.currentThread().getContextClassLoader();
 366                 final AccessControlContext thisacc = AccessController.getContext();
 367                 Runnable r = new CheckedRunnable() {
 368                     public void realRun() {
 369                         Thread current = Thread.currentThread();
 370                         assertFalse(current.isDaemon());
 371                         assertTrue(current.getPriority() <= Thread.NORM_PRIORITY);
 372                         SecurityManager s = System.getSecurityManager();
 373                         assertSame(current.getThreadGroup(),
 374                                    (s == null) ? egroup : s.getThreadGroup());
 375                         assertTrue(current.getName().endsWith("thread-1"));
 376                         assertSame(thisccl, current.getContextClassLoader());
 377                         assertEquals(thisacc, AccessController.getContext());
 378                         done.countDown();
 379                     }};
 380                 ExecutorService e = Executors.newSingleThreadExecutor(Executors.privilegedThreadFactory());
 381                 try (PoolCleaner cleaner = cleaner(e)) {
 382                     e.execute(r);
 383                     await(done);
 384                 }
 385             }};
 386 
 387         runWithPermissions(r,
 388                            new RuntimePermission("getClassLoader"),
 389                            new RuntimePermission("setContextClassLoader"),
 390                            new RuntimePermission("modifyThread"));
 391     }
 392 
 393     boolean haveCCLPermissions() {
 394         SecurityManager sm = System.getSecurityManager();
 395         if (sm != null) {
 396             try {
 397                 sm.checkPermission(new RuntimePermission("setContextClassLoader"));
 398                 sm.checkPermission(new RuntimePermission("getClassLoader"));
 399             } catch (AccessControlException e) {
 400                 return false;
 401             }
 402         }
 403         return true;
 404     }
 405 
 406     void checkCCL() {
 407         SecurityManager sm = System.getSecurityManager();
 408         if (sm != null) {
 409             sm.checkPermission(new RuntimePermission("setContextClassLoader"));
 410             sm.checkPermission(new RuntimePermission("getClassLoader"));
 411         }
 412     }
 413 
 414     class CheckCCL implements Callable<Object> {
 415         public Object call() {
 416             checkCCL();
 417             return null;
 418         }
 419     }
 420 
 421     /**
 422      * Without class loader permissions, creating
 423      * privilegedCallableUsingCurrentClassLoader throws ACE
 424      */
 425     public void testCreatePrivilegedCallableUsingCCLWithNoPrivs() {
 426         Runnable r = new CheckedRunnable() {
 427             public void realRun() throws Exception {
 428                 if (System.getSecurityManager() == null)
 429                     return;
 430                 try {
 431                     Executors.privilegedCallableUsingCurrentClassLoader(new NoOpCallable());
 432                     shouldThrow();
 433                 } catch (AccessControlException success) {}
 434             }};
 435 
 436         runWithoutPermissions(r);
 437     }
 438 
 439     /**
 440      * With class loader permissions, calling
 441      * privilegedCallableUsingCurrentClassLoader does not throw ACE
 442      */
 443     public void testPrivilegedCallableUsingCCLWithPrivs() throws Exception {
 444         Runnable r = new CheckedRunnable() {
 445             public void realRun() throws Exception {
 446                 Executors.privilegedCallableUsingCurrentClassLoader
 447                     (new NoOpCallable())
 448                     .call();
 449             }};
 450 
 451         runWithPermissions(r,
 452                            new RuntimePermission("getClassLoader"),
 453                            new RuntimePermission("setContextClassLoader"));
 454     }
 455 
 456     /**
 457      * Without permissions, calling privilegedCallable throws ACE
 458      */
 459     public void testPrivilegedCallableWithNoPrivs() throws Exception {
 460         // Avoid classloader-related SecurityExceptions in swingui.TestRunner
 461         Executors.privilegedCallable(new CheckCCL());
 462 
 463         Runnable r = new CheckedRunnable() {
 464             public void realRun() throws Exception {
 465                 if (System.getSecurityManager() == null)
 466                     return;
 467                 Callable task = Executors.privilegedCallable(new CheckCCL());
 468                 try {
 469                     task.call();
 470                     shouldThrow();
 471                 } catch (AccessControlException success) {}
 472             }};
 473 
 474         runWithoutPermissions(r);
 475 
 476         // It seems rather difficult to test that the
 477         // AccessControlContext of the privilegedCallable is used
 478         // instead of its caller.  Below is a failed attempt to do
 479         // that, which does not work because the AccessController
 480         // cannot capture the internal state of the current Policy.
 481         // It would be much more work to differentiate based on,
 482         // e.g. CodeSource.
 483 
 484 //         final AccessControlContext[] noprivAcc = new AccessControlContext[1];
 485 //         final Callable[] task = new Callable[1];
 486 
 487 //         runWithPermissions
 488 //             (new CheckedRunnable() {
 489 //                 public void realRun() {
 490 //                     if (System.getSecurityManager() == null)
 491 //                         return;
 492 //                     noprivAcc[0] = AccessController.getContext();
 493 //                     task[0] = Executors.privilegedCallable(new CheckCCL());
 494 //                     try {
 495 //                         AccessController.doPrivileged(new PrivilegedAction<Void>() {
 496 //                                                           public Void run() {
 497 //                                                               checkCCL();
 498 //                                                               return null;
 499 //                                                           }}, noprivAcc[0]);
 500 //                         shouldThrow();
 501 //                     } catch (AccessControlException success) {}
 502 //                 }});
 503 
 504 //         runWithPermissions
 505 //             (new CheckedRunnable() {
 506 //                 public void realRun() throws Exception {
 507 //                     if (System.getSecurityManager() == null)
 508 //                         return;
 509 //                     // Verify that we have an underprivileged ACC
 510 //                     try {
 511 //                         AccessController.doPrivileged(new PrivilegedAction<Void>() {
 512 //                                                           public Void run() {
 513 //                                                               checkCCL();
 514 //                                                               return null;
 515 //                                                           }}, noprivAcc[0]);
 516 //                         shouldThrow();
 517 //                     } catch (AccessControlException success) {}
 518 
 519 //                     try {
 520 //                         task[0].call();
 521 //                         shouldThrow();
 522 //                     } catch (AccessControlException success) {}
 523 //                 }},
 524 //              new RuntimePermission("getClassLoader"),
 525 //              new RuntimePermission("setContextClassLoader"));
 526     }
 527 
 528     /**
 529      * With permissions, calling privilegedCallable succeeds
 530      */
 531     public void testPrivilegedCallableWithPrivs() throws Exception {
 532         Runnable r = new CheckedRunnable() {
 533             public void realRun() throws Exception {
 534                 Executors.privilegedCallable(new CheckCCL()).call();
 535             }};
 536 
 537         runWithPermissions(r,
 538                            new RuntimePermission("getClassLoader"),
 539                            new RuntimePermission("setContextClassLoader"));
 540     }
 541 
 542     /**
 543      * callable(Runnable) returns null when called
 544      */
 545     public void testCallable1() throws Exception {
 546         Callable c = Executors.callable(new NoOpRunnable());
 547         assertNull(c.call());
 548     }
 549 
 550     /**
 551      * callable(Runnable, result) returns result when called
 552      */
 553     public void testCallable2() throws Exception {
 554         Callable c = Executors.callable(new NoOpRunnable(), one);
 555         assertSame(one, c.call());
 556     }
 557 
 558     /**
 559      * callable(PrivilegedAction) returns its result when called
 560      */
 561     public void testCallable3() throws Exception {
 562         Callable c = Executors.callable(new PrivilegedAction() {
 563                 public Object run() { return one; }});
 564         assertSame(one, c.call());
 565     }
 566 
 567     /**
 568      * callable(PrivilegedExceptionAction) returns its result when called
 569      */
 570     public void testCallable4() throws Exception {
 571         Callable c = Executors.callable(new PrivilegedExceptionAction() {
 572                 public Object run() { return one; }});
 573         assertSame(one, c.call());
 574     }
 575 
 576     /**
 577      * callable(null Runnable) throws NPE
 578      */
 579     public void testCallableNPE1() {
 580         try {
 581             Callable c = Executors.callable((Runnable) null);
 582             shouldThrow();
 583         } catch (NullPointerException success) {}
 584     }
 585 
 586     /**
 587      * callable(null, result) throws NPE
 588      */
 589     public void testCallableNPE2() {
 590         try {
 591             Callable c = Executors.callable((Runnable) null, one);
 592             shouldThrow();
 593         } catch (NullPointerException success) {}
 594     }
 595 
 596     /**
 597      * callable(null PrivilegedAction) throws NPE
 598      */
 599     public void testCallableNPE3() {
 600         try {
 601             Callable c = Executors.callable((PrivilegedAction) null);
 602             shouldThrow();
 603         } catch (NullPointerException success) {}
 604     }
 605 
 606     /**
 607      * callable(null PrivilegedExceptionAction) throws NPE
 608      */
 609     public void testCallableNPE4() {
 610         try {
 611             Callable c = Executors.callable((PrivilegedExceptionAction) null);
 612             shouldThrow();
 613         } catch (NullPointerException success) {}
 614     }
 615 
 616     /**
 617      * callable(runnable, x).toString() contains toString of wrapped task
 618      */
 619     public void testCallable_withResult_toString() {
 620         if (testImplementationDetails) {
 621             Runnable r = () -> {};
 622             Callable<String> c = Executors.callable(r, "");
 623             assertEquals(
 624                 identityString(c) + "[Wrapped task = " + r.toString() + "]",
 625                 c.toString());
 626         }
 627     }
 628 
 629     /**
 630      * callable(runnable).toString() contains toString of wrapped task
 631      */
 632     public void testCallable_toString() {
 633         if (testImplementationDetails) {
 634             Runnable r = () -> {};
 635             Callable<Object> c = Executors.callable(r);
 636             assertEquals(
 637                 identityString(c) + "[Wrapped task = " + r.toString() + "]",
 638                 c.toString());
 639         }
 640     }
 641 
 642     /**
 643      * privilegedCallable(callable).toString() contains toString of wrapped task
 644      */
 645     public void testPrivilegedCallable_toString() {
 646         if (testImplementationDetails) {
 647             Callable<String> c = () -> "";
 648             Callable<String> priv = Executors.privilegedCallable(c);
 649             assertEquals(
 650                 identityString(priv) + "[Wrapped task = " + c.toString() + "]",
 651                 priv.toString());
 652         }
 653     }
 654 
 655     /**
 656      * privilegedCallableUsingCurrentClassLoader(callable).toString()
 657      * contains toString of wrapped task
 658      */
 659     public void testPrivilegedCallableUsingCurrentClassLoader_toString() {
 660         if (testImplementationDetails) {
 661             Callable<String> c = () -> "";
 662             Callable<String> priv = Executors.privilegedCallableUsingCurrentClassLoader(c);
 663             assertEquals(
 664                 identityString(priv) + "[Wrapped task = " + c.toString() + "]",
 665                 priv.toString());
 666         }
 667     }
 668 }