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.collections4.bloomfilter;
18  
19  import static org.junit.jupiter.api.Assertions.assertArrayEquals;
20  import static org.junit.jupiter.api.Assertions.assertEquals;
21  import static org.junit.jupiter.api.Assertions.assertFalse;
22  import static org.junit.jupiter.api.Assertions.assertSame;
23  import static org.junit.jupiter.api.Assertions.assertTrue;
24  
25  import java.util.Arrays;
26  import java.util.BitSet;
27  import java.util.function.IntPredicate;
28  
29  import org.junit.jupiter.api.Test;
30  
31  /**
32   * Test for IndexProducer.
33   */
34  public abstract class AbstractIndexProducerTest {
35  
36      /**
37       * An expandable list of int values.
38       */
39      protected static class IntList {
40          private int size;
41          private int[] data = {0};
42  
43          /**
44           * Adds the value to the list.
45           *
46           * @param value the value
47           * @return true if the list was modified
48           */
49          boolean add(final int value) {
50              if (size == data.length) {
51                  data = Arrays.copyOf(data, size << 1);
52              }
53              data[size++] = value;
54              return true;
55          }
56  
57          /**
58           * Convert to an array.
59           *
60           * @return the array
61           */
62          int[] toArray() {
63              return Arrays.copyOf(data, size);
64          }
65      }
66      private static final IntPredicate TRUE_PREDICATE = i -> true;
67  
68      private static final IntPredicate FALSE_PREDICATE = i -> false;
69      /** Flag to indicate the indices are ordered, e.g. from {@link IndexProducer#forEachIndex(IntPredicate)}. */
70      protected static final int ORDERED = 0x1;
71  
72      /** Flag to indicate the indices are distinct, e.g. from {@link IndexProducer#forEachIndex(IntPredicate)}. */
73      protected static final int DISTINCT = 0x2;
74  
75      /**
76       * Creates an producer without data.
77       * @return a producer that has no data.
78       */
79      protected abstract IndexProducer createEmptyProducer();
80  
81      /**
82       * Creates a producer with some data.
83       * @return a producer with some data
84       */
85      protected abstract IndexProducer createProducer();
86  
87      /**
88       * Gets the behavior of the {@link IndexProducer#asIndexArray()} method.
89       * @return the behavior.
90       * @see #ORDERED
91       * @see #DISTINCT
92       */
93      protected abstract int getAsIndexArrayBehaviour();
94  
95      /**
96       * Creates an array of expected indices.
97       * The expected indices are dependent upon the producer created in the {@code createProducer()} method.
98       * @return an array of expected indices.
99       */
100     protected abstract int[] getExpectedIndices();
101 
102     /**
103      * Gets the behavior of the {@link IndexProducer#forEachIndex(IntPredicate)} method.
104      * By default returns the value of {@code getAsIndexArrayBehaviour()} method.
105      * @return the behavior.
106      * @see #ORDERED
107      * @see #DISTINCT
108      */
109     protected int getForEachIndexBehaviour() {
110         return getAsIndexArrayBehaviour();
111     }
112 
113     /**
114      * Test to ensure that all expected values are generated at least once.
115      */
116     @Test
117     public final void testAsIndexArrayValues() {
118         final BitSet bs = new BitSet();
119         Arrays.stream(createProducer().asIndexArray()).forEach(bs::set);
120         for (final int i : getExpectedIndices()) {
121             assertTrue(bs.get(i), () -> "Missing " + i);
122         }
123     }
124 
125     /**
126      * Tests the behavior of {@code IndexProducer.asIndexArray()}.
127      * The expected behavior is defined by the {@code getBehaviour()} method.
128      * The index array may be Ordered, Distinct or both.
129      * If the index array is not distinct then all elements returned by the {@code getExpectedIndices()}
130      * method, including duplicates, are expected to be returned by the {@code asIndexArray()} method.
131      */
132     @Test
133     public final void testBehaviourAsIndexArray() {
134         final int flags = getAsIndexArrayBehaviour();
135         final int[] actual = createProducer().asIndexArray();
136         if ((flags & ORDERED) != 0) {
137             final int[] expected = Arrays.stream(actual).sorted().toArray();
138             assertArrayEquals(expected, actual);
139         }
140         if ((flags & DISTINCT) != 0) {
141             final long count = Arrays.stream(actual).distinct().count();
142             assertEquals(count, actual.length);
143         } else {
144             // if the array is not distinct all expected elements must be generated
145             // This is modified so use a copy
146             final int[] expected = getExpectedIndices().clone();
147             Arrays.sort(expected);
148             Arrays.sort(actual);
149             assertArrayEquals(expected, actual);
150         }
151     }
152 
153     /**
154      * Tests the behavior of {@code IndexProducer.forEachIndex()}.
155      * The expected behavior is defined by the {@code getBehaviour()} method.
156      * The order is assumed to follow the order produced by {@code IndexProducer.asIndexArray()}.
157      */
158     @Test
159     public final void testBehaviourForEachIndex() {
160         final int flags = getForEachIndexBehaviour();
161         final IntList list = new IntList();
162         createProducer().forEachIndex(list::add);
163         final int[] actual = list.toArray();
164         if ((flags & ORDERED) != 0) {
165             final int[] expected = Arrays.stream(actual).sorted().toArray();
166             assertArrayEquals(expected, actual);
167         }
168         if ((flags & DISTINCT) != 0) {
169             final long count = Arrays.stream(actual).distinct().count();
170             assertEquals(count, actual.length);
171         } else {
172             // if forEach is not distinct all expected elements must be generated
173             final int[] expected = getExpectedIndices().clone();
174             Arrays.sort(expected);
175             Arrays.sort(actual);
176             assertArrayEquals(expected, actual);
177         }
178     }
179 
180     /**
181      * Test the distinct indices output from the producer are consistent.
182      */
183     @Test
184     public final void testConsistency() {
185         final IndexProducer producer = createProducer();
186         final BitSet bs1 = new BitSet();
187         final BitSet bs2 = new BitSet();
188         Arrays.stream(producer.asIndexArray()).forEach(bs1::set);
189         producer.forEachIndex(i -> {
190             bs2.set(i);
191             return true;
192         });
193         assertEquals(bs1, bs2);
194     }
195 
196     @Test
197     public final void testEmptyProducer() {
198         final IndexProducer empty = createEmptyProducer();
199         final int[] ary = empty.asIndexArray();
200         assertEquals(0, ary.length);
201         assertTrue(empty.forEachIndex(i -> {
202             throw new AssertionError("forEach predictate should not be called");
203         }));
204     }
205 
206     /**
207      * Test to ensure that for each index returns each expected index at least once.
208      */
209     @Test
210     public final void testForEachIndex() {
211         final BitSet bs1 = new BitSet();
212         final BitSet bs2 = new BitSet();
213         Arrays.stream(getExpectedIndices()).forEach(bs1::set);
214         createProducer().forEachIndex(i -> {
215             bs2.set(i);
216             return true;
217         });
218         assertEquals(bs1, bs2);
219     }
220 
221     @Test
222     public void testForEachIndexEarlyExit() {
223         final int[] passes = new int[1];
224         assertFalse(createProducer().forEachIndex(i -> {
225             passes[0]++;
226             return false;
227         }));
228         assertEquals(1, passes[0]);
229 
230         passes[0] = 0;
231         assertTrue(createEmptyProducer().forEachIndex(i -> {
232             passes[0]++;
233             return false;
234         }));
235         assertEquals(0, passes[0]);
236     }
237 
238     @Test
239     public final void testForEachIndexPredicates() {
240         final IndexProducer populated = createProducer();
241         final IndexProducer empty = createEmptyProducer();
242 
243         assertFalse(populated.forEachIndex(FALSE_PREDICATE), "non-empty should be false");
244         assertTrue(empty.forEachIndex(FALSE_PREDICATE), "empty should be true");
245 
246         assertTrue(populated.forEachIndex(TRUE_PREDICATE), "non-empty should be true");
247         assertTrue(empty.forEachIndex(TRUE_PREDICATE), "empty should be true");
248     }
249 
250     @Test
251     public void testUniqueReturnsSelf() {
252         final IndexProducer expected = createProducer().uniqueIndices();
253         assertSame(expected, expected.uniqueIndices());
254     }
255 }