/* * Copyright (c) 2021, 2023, Oracle and/or its affiliates. All rights reserved. * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. * * This code is free software; you can redistribute it and/or modify it * under the terms of the GNU General Public License version 2 only, as * published by the Free Software Foundation. * * This code is distributed in the hope that it will be useful, but WITHOUT * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License * version 2 for more details (a copy is included in the LICENSE file that * accompanied this code). * * You should have received a copy of the GNU General Public License version * 2 along with this work; if not, write to the Free Software Foundation, * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. * * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA * or visit www.oracle.com if you need additional information or have any * questions. */ /* * @test * @summary Test Future's default methods * @library ../lib * @run junit DefaultMethods */ import java.util.concurrent.CompletableFuture; import java.util.concurrent.CountDownLatch; import java.util.concurrent.ExecutorService; import java.util.concurrent.Executors; import java.util.concurrent.ForkJoinPool; import java.util.concurrent.Future; import java.util.stream.Stream; import static java.util.concurrent.Future.State.*; import org.junit.jupiter.api.Test; import org.junit.jupiter.params.ParameterizedTest; import org.junit.jupiter.params.provider.MethodSource; import static org.junit.jupiter.api.Assertions.*; class DefaultMethods { static Stream executors() { return Stream.of( // ensures that default close method is tested new DelegatingExecutorService(Executors.newCachedThreadPool()), // executors that may return a Future that overrides the methods Executors.newCachedThreadPool(), Executors.newVirtualThreadPerTaskExecutor(), new ForkJoinPool() ); } /** * Test methods when the task has not completed. */ @ParameterizedTest @MethodSource("executors") void testRunningTask(ExecutorService executor) { try (executor) { var latch = new CountDownLatch(1); Future future = executor.submit(() -> { latch.await(); return null; }); try { assertTrue(future.state() == RUNNING); assertThrows(IllegalStateException.class, future::resultNow); assertThrows(IllegalStateException.class, future::exceptionNow); } finally { latch.countDown(); } } } /** * Test methods when the task has already completed with a result. */ @ParameterizedTest @MethodSource("executors") void testCompletedTask1(ExecutorService executor) { try (executor) { Future future = executor.submit(() -> "foo"); awaitDone(future); assertTrue(future.state() == SUCCESS); assertEquals("foo", future.resultNow()); assertThrows(IllegalStateException.class, future::exceptionNow); } } /** * Test methods when the task has already completed with null. */ @ParameterizedTest @MethodSource("executors") void testCompletedTask2(ExecutorService executor) { try (executor) { Future future = executor.submit(() -> null); awaitDone(future); assertTrue(future.state() == SUCCESS); assertNull(future.resultNow()); assertThrows(IllegalStateException.class, future::exceptionNow); } } /** * Test methods when the task has completed with an exception. */ @ParameterizedTest @MethodSource("executors") void testFailedTask(ExecutorService executor) { try (executor) { Future future = executor.submit(() -> { throw new ArithmeticException(); }); awaitDone(future); assertTrue(future.state() == FAILED); assertThrows(IllegalStateException.class, future::resultNow); Throwable ex = future.exceptionNow(); assertTrue(ex instanceof ArithmeticException); } } /** * Test methods when the task has been cancelled (mayInterruptIfRunning=false) */ @ParameterizedTest @MethodSource("executors") void testCancelledTask1(ExecutorService executor) { try (executor) { var latch = new CountDownLatch(1); Future future = executor.submit(() -> { latch.await(); return null; }); future.cancel(false); try { assertTrue(future.state() == CANCELLED); assertThrows(IllegalStateException.class, future::resultNow); assertThrows(IllegalStateException.class, future::exceptionNow); } finally { latch.countDown(); } } } /** * Test methods when the task has been cancelled (mayInterruptIfRunning=true) */ @ParameterizedTest @MethodSource("executors") void testCancelledTask2(ExecutorService executor) { try (executor) { var latch = new CountDownLatch(1); Future future = executor.submit(() -> { latch.await(); return null; }); future.cancel(true); try { assertTrue(future.state() == CANCELLED); assertThrows(IllegalStateException.class, future::resultNow); assertThrows(IllegalStateException.class, future::exceptionNow); } finally { latch.countDown(); } } } /** * Test CompletableFuture with the task has not completed. */ @Test void testCompletableFuture1() { var future = new CompletableFuture(); assertTrue(future.state() == RUNNING); assertThrows(IllegalStateException.class, future::resultNow); assertThrows(IllegalStateException.class, future::exceptionNow); } /** * Test CompletableFuture with the task that completed with result. */ @Test void testCompletableFuture2() { var future = new CompletableFuture(); future.complete("foo"); assertTrue(future.state() == SUCCESS); assertEquals("foo", future.resultNow()); assertThrows(IllegalStateException.class, future::exceptionNow); } /** * Test CompletableFuture with the task that completed with null. */ @Test void testCompletableFuture3() { var future = new CompletableFuture(); future.complete(null); assertTrue(future.state() == SUCCESS); assertNull(future.resultNow()); assertThrows(IllegalStateException.class, future::exceptionNow); } /** * Test CompletableFuture with the task that completed with exception. */ @Test void testCompletableFuture4() { var future = new CompletableFuture(); future.completeExceptionally(new ArithmeticException()); assertTrue(future.state() == FAILED); assertThrows(IllegalStateException.class, future::resultNow); Throwable ex = future.exceptionNow(); assertTrue(ex instanceof ArithmeticException); } /** * Test CompletableFuture with the task that was cancelled. */ @Test void testCompletableFuture5() { var future = new CompletableFuture(); future.cancel(false); assertTrue(future.state() == CANCELLED); assertThrows(IllegalStateException.class, future::resultNow); assertThrows(IllegalStateException.class, future::exceptionNow); } /** * Waits for the future to be done. */ private static void awaitDone(Future future) { boolean interrupted = false; while (!future.isDone()) { try { Thread.sleep(10); } catch (InterruptedException e) { interrupted = true; } } if (interrupted) { Thread.currentThread().interrupt(); } } }