aboutsummaryrefslogtreecommitdiff
path: root/src/test/java/org/apache/commons/lang3/concurrent/BackgroundInitializerTest.java
diff options
context:
space:
mode:
Diffstat (limited to 'src/test/java/org/apache/commons/lang3/concurrent/BackgroundInitializerTest.java')
-rw-r--r--src/test/java/org/apache/commons/lang3/concurrent/BackgroundInitializerTest.java309
1 files changed, 309 insertions, 0 deletions
diff --git a/src/test/java/org/apache/commons/lang3/concurrent/BackgroundInitializerTest.java b/src/test/java/org/apache/commons/lang3/concurrent/BackgroundInitializerTest.java
new file mode 100644
index 000000000..d1a80da81
--- /dev/null
+++ b/src/test/java/org/apache/commons/lang3/concurrent/BackgroundInitializerTest.java
@@ -0,0 +1,309 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to You under the Apache License, Version 2.0
+ * (the "License"); you may not use this file except in compliance with
+ * the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package org.apache.commons.lang3.concurrent;
+
+import static org.junit.jupiter.api.Assertions.assertEquals;
+import static org.junit.jupiter.api.Assertions.assertFalse;
+import static org.junit.jupiter.api.Assertions.assertNotNull;
+import static org.junit.jupiter.api.Assertions.assertNull;
+import static org.junit.jupiter.api.Assertions.assertSame;
+import static org.junit.jupiter.api.Assertions.assertThrows;
+import static org.junit.jupiter.api.Assertions.assertTrue;
+
+import java.time.Duration;
+import java.util.concurrent.CountDownLatch;
+import java.util.concurrent.ExecutorService;
+import java.util.concurrent.Executors;
+import java.util.concurrent.TimeUnit;
+import java.util.concurrent.atomic.AtomicReference;
+
+import org.apache.commons.lang3.AbstractLangTest;
+import org.apache.commons.lang3.ThreadUtils;
+import org.junit.jupiter.api.Test;
+
+public class BackgroundInitializerTest extends AbstractLangTest {
+ /**
+ * Helper method for checking whether the initialize() method was correctly
+ * called. start() must already have been invoked.
+ *
+ * @param init the initializer to test
+ */
+ private void checkInitialize(final BackgroundInitializerTestImpl init) throws ConcurrentException {
+ final Integer result = init.get();
+ assertEquals(1, result.intValue(), "Wrong result");
+ assertEquals(1, init.initializeCalls, "Wrong number of invocations");
+ assertNotNull(init.getFuture(), "No future");
+ }
+
+ /**
+ * Tests whether initialize() is invoked.
+ */
+ @Test
+ public void testInitialize() throws ConcurrentException {
+ final BackgroundInitializerTestImpl init = new BackgroundInitializerTestImpl();
+ init.start();
+ checkInitialize(init);
+ }
+
+ /**
+ * Tries to obtain the executor before start(). It should not have been
+ * initialized yet.
+ */
+ @Test
+ public void testGetActiveExecutorBeforeStart() {
+ final BackgroundInitializerTestImpl init = new BackgroundInitializerTestImpl();
+ assertNull(init.getActiveExecutor(), "Got an executor");
+ }
+
+ /**
+ * Tests whether an external executor is correctly detected.
+ */
+ @Test
+ public void testGetActiveExecutorExternal() throws InterruptedException, ConcurrentException {
+ final ExecutorService exec = Executors.newSingleThreadExecutor();
+ try {
+ final BackgroundInitializerTestImpl init = new BackgroundInitializerTestImpl(
+ exec);
+ init.start();
+ assertSame(exec, init.getActiveExecutor(), "Wrong executor");
+ checkInitialize(init);
+ } finally {
+ exec.shutdown();
+ exec.awaitTermination(1, TimeUnit.SECONDS);
+ }
+ }
+
+ /**
+ * Tests getActiveExecutor() for a temporary executor.
+ */
+ @Test
+ public void testGetActiveExecutorTemp() throws ConcurrentException {
+ final BackgroundInitializerTestImpl init = new BackgroundInitializerTestImpl();
+ init.start();
+ assertNotNull(init.getActiveExecutor(), "No active executor");
+ checkInitialize(init);
+ }
+
+ /**
+ * Tests the execution of the background task if a temporary executor has to
+ * be created.
+ */
+ @Test
+ public void testInitializeTempExecutor() throws ConcurrentException {
+ final BackgroundInitializerTestImpl init = new BackgroundInitializerTestImpl();
+ assertTrue(init.start(), "Wrong result of start()");
+ checkInitialize(init);
+ assertTrue(init.getActiveExecutor().isShutdown(), "Executor not shutdown");
+ }
+
+ /**
+ * Tests whether an external executor can be set using the
+ * setExternalExecutor() method.
+ */
+ @Test
+ public void testSetExternalExecutor() throws ConcurrentException {
+ final ExecutorService exec = Executors.newCachedThreadPool();
+ try {
+ final BackgroundInitializerTestImpl init = new BackgroundInitializerTestImpl();
+ init.setExternalExecutor(exec);
+ assertEquals(exec, init.getExternalExecutor(), "Wrong executor service");
+ assertTrue(init.start(), "Wrong result of start()");
+ assertSame(exec, init.getActiveExecutor(), "Wrong active executor");
+ checkInitialize(init);
+ assertFalse(exec.isShutdown(), "Executor was shutdown");
+ } finally {
+ exec.shutdown();
+ }
+ }
+
+ /**
+ * Tests that setting an executor after start() causes an exception.
+ *
+ * @throws org.apache.commons.lang3.concurrent.ConcurrentException because the test implementation may throw it
+ */
+ @Test
+ public void testSetExternalExecutorAfterStart() throws ConcurrentException, InterruptedException {
+ final BackgroundInitializerTestImpl init = new BackgroundInitializerTestImpl();
+ init.start();
+ final ExecutorService exec = Executors.newSingleThreadExecutor();
+ try {
+ assertThrows(IllegalStateException.class, () -> init.setExternalExecutor(exec));
+ init.get();
+ } finally {
+ exec.shutdown();
+ exec.awaitTermination(1, TimeUnit.SECONDS);
+ }
+ }
+
+ /**
+ * Tests invoking start() multiple times. Only the first invocation should
+ * have an effect.
+ */
+ @Test
+ public void testStartMultipleTimes() throws ConcurrentException {
+ final BackgroundInitializerTestImpl init = new BackgroundInitializerTestImpl();
+ assertTrue(init.start(), "Wrong result for start()");
+ for (int i = 0; i < 10; i++) {
+ assertFalse(init.start(), "Could start again");
+ }
+ checkInitialize(init);
+ }
+
+ /**
+ * Tests calling get() before start(). This should cause an exception.
+ */
+ @Test
+ public void testGetBeforeStart() {
+ final BackgroundInitializerTestImpl init = new BackgroundInitializerTestImpl();
+ assertThrows(IllegalStateException.class, init::get);
+ }
+
+ /**
+ * Tests the get() method if background processing causes a runtime
+ * exception.
+ */
+ @Test
+ public void testGetRuntimeException() {
+ final BackgroundInitializerTestImpl init = new BackgroundInitializerTestImpl();
+ final RuntimeException rex = new RuntimeException();
+ init.ex = rex;
+ init.start();
+ final Exception ex = assertThrows(Exception.class, init::get);
+ assertEquals(rex, ex, "Runtime exception not thrown");
+ }
+
+ /**
+ * Tests the get() method if background processing causes a checked
+ * exception.
+ */
+ @Test
+ public void testGetCheckedException() {
+ final BackgroundInitializerTestImpl init = new BackgroundInitializerTestImpl();
+ final Exception ex = new Exception();
+ init.ex = ex;
+ init.start();
+ final ConcurrentException cex = assertThrows(ConcurrentException.class, init::get);
+ assertEquals(ex, cex.getCause(), "Exception not thrown");
+ }
+
+ /**
+ * Tests the get() method if waiting for the initialization is interrupted.
+ *
+ * @throws InterruptedException because we're making use of Java's concurrent API
+ */
+ @Test
+ public void testGetInterruptedException() throws InterruptedException {
+ final ExecutorService exec = Executors.newSingleThreadExecutor();
+ final BackgroundInitializerTestImpl init = new BackgroundInitializerTestImpl(
+ exec);
+ final CountDownLatch latch1 = new CountDownLatch(1);
+ init.shouldSleep = true;
+ init.start();
+ final AtomicReference<InterruptedException> iex = new AtomicReference<>();
+ final Thread getThread = new Thread() {
+ @Override
+ public void run() {
+ try {
+ init.get();
+ } catch (final ConcurrentException cex) {
+ if (cex.getCause() instanceof InterruptedException) {
+ iex.set((InterruptedException) cex.getCause());
+ }
+ } finally {
+ assertTrue(isInterrupted(), "Thread not interrupted");
+ latch1.countDown();
+ }
+ }
+ };
+ getThread.start();
+ getThread.interrupt();
+ latch1.await();
+ exec.shutdownNow();
+ exec.awaitTermination(1, TimeUnit.SECONDS);
+ assertNotNull(iex.get(), "No interrupted exception");
+ }
+
+ /**
+ * Tests isStarted() before start() was called.
+ */
+ @Test
+ public void testIsStartedFalse() {
+ final BackgroundInitializerTestImpl init = new BackgroundInitializerTestImpl();
+ assertFalse(init.isStarted(), "Already started");
+ }
+
+ /**
+ * Tests isStarted() after start().
+ */
+ @Test
+ public void testIsStartedTrue() {
+ final BackgroundInitializerTestImpl init = new BackgroundInitializerTestImpl();
+ init.start();
+ assertTrue(init.isStarted(), "Not started");
+ }
+
+ /**
+ * Tests isStarted() after the background task has finished.
+ */
+ @Test
+ public void testIsStartedAfterGet() throws ConcurrentException {
+ final BackgroundInitializerTestImpl init = new BackgroundInitializerTestImpl();
+ init.start();
+ checkInitialize(init);
+ assertTrue(init.isStarted(), "Not started");
+ }
+
+ /**
+ * A concrete implementation of BackgroundInitializer. It also overloads
+ * some methods that simplify testing.
+ */
+ private static class BackgroundInitializerTestImpl extends
+ BackgroundInitializer<Integer> {
+ /** An exception to be thrown by initialize(). */
+ Exception ex;
+
+ /** A flag whether the background task should sleep a while. */
+ boolean shouldSleep;
+
+ /** The number of invocations of initialize(). */
+ volatile int initializeCalls;
+
+ BackgroundInitializerTestImpl() {
+ }
+
+ BackgroundInitializerTestImpl(final ExecutorService exec) {
+ super(exec);
+ }
+
+ /**
+ * Records this invocation. Optionally throws an exception or sleeps a
+ * while.
+ *
+ * @throws Exception in case of an error
+ */
+ @Override
+ protected Integer initialize() throws Exception {
+ if (ex != null) {
+ throw ex;
+ }
+ if (shouldSleep) {
+ ThreadUtils.sleep(Duration.ofMinutes(1));
+ }
+ return Integer.valueOf(++initializeCalls);
+ }
+ }
+}