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.math4.legacy.analysis.integration.gauss;
18  
19  import java.util.List;
20  import java.util.ArrayList;
21  import java.util.concurrent.ThreadPoolExecutor;
22  import java.util.concurrent.ArrayBlockingQueue;
23  import java.util.concurrent.TimeUnit;
24  import java.util.concurrent.Callable;
25  import java.util.concurrent.Future;
26  import java.util.concurrent.ExecutionException;
27  import java.util.concurrent.atomic.AtomicInteger;
28  
29  import org.apache.commons.math4.legacy.core.Pair;
30  import org.junit.Test;
31  import org.junit.Assert;
32  
33  /**
34   * Test for {@link BaseRuleFactory}.
35   *
36   */
37  public class BaseRuleFactoryTest {
38      /**
39       * Tests that a given rule rule will be computed and added once to the cache
40       * whatever the number of times this rule is called concurrently.
41       */
42      @Test
43          public void testConcurrentCreation() throws InterruptedException,
44                                                      ExecutionException {
45          // Number of times the same rule will be called.
46          final int numTasks = 20;
47  
48          final ThreadPoolExecutor exec
49              = new ThreadPoolExecutor(3, numTasks, 1, TimeUnit.SECONDS,
50                                       new ArrayBlockingQueue<>(2));
51  
52          final List<Future<Pair<double[], double[]>>> results
53              = new ArrayList<>();
54          for (int i = 0; i < numTasks; i++) {
55              results.add(exec.submit(new RuleBuilder()));
56          }
57  
58          // Ensure that all computations have completed.
59          for (Future<Pair<double[], double[]>> f : results) {
60              f.get();
61          }
62          exec.shutdown();
63  
64          // Assertion would fail if "getRuleInternal" were not "synchronized".
65          final int n = RuleBuilder.getNumberOfCalls();
66          Assert.assertEquals("Rule computation was called " + n + " times", 1, n);
67      }
68  }
69  
70  class RuleBuilder implements Callable<Pair<double[], double[]>> {
71      private static final DummyRuleFactory factory = new DummyRuleFactory();
72  
73      @Override
74      public Pair<double[], double[]> call() {
75          final int dummy = 2; // Always request the same rule.
76          return factory.getRule(dummy);
77      }
78  
79      public static int getNumberOfCalls() {
80          return factory.getNumberOfCalls();
81      }
82  }
83  
84  class DummyRuleFactory extends BaseRuleFactory<Double> {
85      /** Rule computations counter. */
86      private static AtomicInteger nCalls = new AtomicInteger();
87  
88      @Override
89      protected Pair<Double[], Double[]> computeRule(int order) {
90          // Tracks whether this computation has been called more than once.
91          nCalls.getAndIncrement();
92  
93          try {
94              // Sleep to simulate computation time.
95              Thread.sleep(20);
96          } catch (InterruptedException e) {
97              Assert.fail("Unexpected interruption");
98          }
99  
100          // Dummy rule (but contents must exist).
101         final Double[] p = new Double[order];
102         final Double[] w = new Double[order];
103         for (int i = 0; i < order; i++) {
104             p[i] = Double.valueOf(i);
105             w[i] = Double.valueOf(i);
106         }
107         return new Pair<>(p, w);
108     }
109 
110     public int getNumberOfCalls() {
111         return nCalls.get();
112     }
113 }