View Javadoc
1   /*
2    * Licensed to the Apache Software Foundation (ASF) under one or more
3    * contributor license agreements.  See the NOTICE file distributed with
4    * this work for additional information regarding copyright ownership.
5    * The ASF licenses this file to You under the Apache License, Version 2.0
6    * (the "License"); you may not use this file except in compliance with
7    * the License.  You may obtain a copy of the License at
8    *
9    *      http://www.apache.org/licenses/LICENSE-2.0
10   *
11   * Unless required by applicable law or agreed to in writing, software
12   * distributed under the License is distributed on an "AS IS" BASIS,
13   * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
14   * See the License for the specific language governing permissions and
15   * limitations under the License.
16   */
17  package org.apache.commons.lang3.concurrent;
18  
19  import static org.junit.jupiter.api.Assertions.assertEquals;
20  import static org.junit.jupiter.api.Assertions.assertFalse;
21  import static org.junit.jupiter.api.Assertions.assertNotNull;
22  import static org.junit.jupiter.api.Assertions.assertTrue;
23  
24  import java.util.concurrent.CountDownLatch;
25  
26  import org.apache.commons.lang3.AbstractLangTest;
27  import org.junit.jupiter.api.Test;
28  
29  /**
30   * <p>
31   * An abstract base class for tests of concrete {@code ConcurrentInitializer}
32   * implementations.
33   * </p>
34   * <p>
35   * This class provides some basic tests for initializer implementations. Derived
36   * class have to create a {@link ConcurrentInitializer} object on which the
37   * tests are executed.
38   * </p>
39   */
40  public abstract class AbstractConcurrentInitializerTest extends AbstractLangTest {
41      /**
42       * Creates the {@link ConcurrentInitializer} object to be tested. This
43       * method is called whenever the test fixture needs to be obtained.
44       *
45       * @return the initializer object to be tested
46       */
47      protected abstract ConcurrentInitializer<Object> createInitializer();
48  
49      /**
50       * Tests a simple invocation of the get() method.
51       *
52       * @throws org.apache.commons.lang3.concurrent.ConcurrentException because the object under test may throw it.
53       */
54      @Test
55      public void testGet() throws ConcurrentException {
56          assertNotNull(createInitializer().get(), "No managed object");
57      }
58  
59      /**
60       * Tests whether get() can be invoked from multiple threads concurrently.
61       * Always the same object should be returned.
62       *
63       * @throws org.apache.commons.lang3.concurrent.ConcurrentException because the object under test may throw it.
64       * @throws InterruptedException because the threading API my throw it.
65       */
66      @Test
67      public void testGetConcurrent() throws ConcurrentException,
68              InterruptedException {
69          final ConcurrentInitializer<Object> initializer = createInitializer();
70          final int threadCount = 20;
71          final CountDownLatch startLatch = new CountDownLatch(1);
72          final class GetThread extends Thread {
73              Object object;
74  
75              @Override
76              public void run() {
77                  try {
78                      // wait until all threads are ready for maximum parallelism
79                      startLatch.await();
80                      // access the initializer
81                      object = initializer.get();
82                  } catch (final InterruptedException iex) {
83                      // ignore
84                  } catch (final ConcurrentException cex) {
85                      object = cex;
86                  }
87              }
88          }
89  
90          final GetThread[] threads = new GetThread[threadCount];
91          for (int i = 0; i < threadCount; i++) {
92              threads[i] = new GetThread();
93              threads[i].start();
94          }
95  
96          // fire all threads and wait until they are ready
97          startLatch.countDown();
98          for (final Thread t : threads) {
99              t.join();
100         }
101 
102         // check results
103         final Object managedObject = initializer.get();
104         for (final GetThread t : threads) {
105             assertEquals(managedObject, t.object, "Wrong object");
106         }
107     }
108 
109     /**
110      * Tests whether sequential get() invocations always return the same
111      * instance.
112      *
113      * @throws org.apache.commons.lang3.concurrent.ConcurrentException because the object under test may throw it.
114      */
115     @Test
116     public void testGetMultipleTimes() throws ConcurrentException {
117         final ConcurrentInitializer<Object> initializer = createInitializer();
118         final Object obj = initializer.get();
119         for (int i = 0; i < 10; i++) {
120             assertEquals(obj, initializer.get(), "Got different object at " + i);
121         }
122     }
123 
124     /**
125      * Tests a simple invocation of the isInitialized() method.
126      */
127     @Test
128     public void testisInitialized() throws Throwable {
129         final ConcurrentInitializer<Object> initializer = createInitializer();
130         if (initializer instanceof AbstractConcurrentInitializer) {
131             AbstractConcurrentInitializer castedInitializer = (AbstractConcurrentInitializer) initializer;
132             assertFalse(castedInitializer.isInitialized(), "was initialized before get()");
133             assertNotNull(castedInitializer.get(), "No managed object");
134             assertTrue(castedInitializer.isInitialized(), "was not initialized after get()");
135         }
136     }
137 }