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    *      https://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} implementations.
32   * </p>
33   * <p>
34   * This class provides some basic tests for initializer implementations. Derived class have to create a {@link ConcurrentInitializer} object on which the tests
35   * are executed.
36   * </p>
37   *
38   * @param <T> Domain type.
39   */
40  public abstract class AbstractConcurrentInitializerTest<T> extends AbstractLangTest {
41  
42      static final class GetThread extends Thread {
43  
44          private Object object;
45          private final CountDownLatch startLatch;
46          private final ConcurrentInitializer<?> initializer;
47  
48          GetThread(final CountDownLatch startLatch, final ConcurrentInitializer<?> initializer) {
49              this.startLatch = startLatch;
50              this.initializer = initializer;
51          }
52  
53          @Override
54          public void run() {
55              try {
56                  // wait until all threads are ready for maximum parallelism
57                  startLatch.await();
58                  // access the initializer
59                  object = initializer.get();
60              } catch (final InterruptedException iex) {
61                  // ignore
62              } catch (final ConcurrentException cex) {
63                  object = cex;
64              }
65          }
66      }
67  
68      /**
69       * Creates the {@link ConcurrentInitializer} object to be tested. This method is called whenever the test fixture needs to be obtained.
70       *
71       * @return the initializer object to be tested
72       */
73      protected abstract ConcurrentInitializer<T> createInitializer();
74  
75      /**
76       * Tests a simple invocation of the get() method.
77       *
78       * @throws org.apache.commons.lang3.concurrent.ConcurrentException because the object under test may throw it.
79       */
80      @Test
81      void testGet() throws ConcurrentException {
82          assertNotNull(createInitializer().get(), "No managed object");
83      }
84  
85      /**
86       * Tests whether get() can be invoked from multiple threads concurrently. Always the same object should be returned.
87       *
88       * @throws org.apache.commons.lang3.concurrent.ConcurrentException because the object under test may throw it.
89       * @throws InterruptedException                                    because the threading API my throw it.
90       */
91      @Test
92      void testGetConcurrent() throws ConcurrentException, InterruptedException {
93          final ConcurrentInitializer<T> initializer = createInitializer();
94          final int threadCount = 20;
95          final CountDownLatch startLatch = new CountDownLatch(1);
96          final GetThread[] threads = new GetThread[threadCount];
97          for (int i = 0; i < threadCount; i++) {
98              threads[i] = new GetThread(startLatch, initializer);
99              threads[i].start();
100         }
101 
102         // fire all threads and wait until they are ready
103         startLatch.countDown();
104         for (final Thread t : threads) {
105             t.join();
106         }
107 
108         // check results
109         final Object managedObject = initializer.get();
110         for (final GetThread t : threads) {
111             assertEquals(managedObject, t.object, "Wrong object");
112         }
113     }
114 
115     /**
116      * Tests whether sequential get() invocations always return the same instance.
117      *
118      * @throws org.apache.commons.lang3.concurrent.ConcurrentException because the object under test may throw it.
119      */
120     @Test
121     void testGetMultipleTimes() throws ConcurrentException {
122         final ConcurrentInitializer<T> initializer = createInitializer();
123         final Object obj = initializer.get();
124         for (int i = 0; i < 10; i++) {
125             assertEquals(obj, initializer.get(), "Got different object at " + i);
126         }
127     }
128 
129     /**
130      * Tests a simple invocation of the isInitialized() method.
131      *
132      * @throws Throwable on test failure.
133      */
134     @Test
135     void testisInitialized() throws Throwable {
136         final ConcurrentInitializer<T> initializer = createInitializer();
137         if (initializer instanceof AbstractConcurrentInitializer) {
138             @SuppressWarnings("unchecked")
139             final AbstractConcurrentInitializer<T, Exception> castedInitializer = (AbstractConcurrentInitializer<T, Exception>) initializer;
140             assertFalse(castedInitializer.isInitialized(), "was initialized before get()");
141             assertNotNull(castedInitializer.get(), "No managed object");
142             assertTrue(castedInitializer.isInitialized(), "was not initialized after get()");
143         }
144     }
145 }