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.assertNotEquals;
23  import static org.junit.jupiter.api.Assertions.assertNotSame;
24  import static org.junit.jupiter.api.Assertions.assertSame;
25  import static org.junit.jupiter.api.Assertions.assertThrows;
26  import static org.junit.jupiter.api.Assertions.assertTrue;
27  
28  import java.util.ArrayList;
29  import java.util.Arrays;
30  import java.util.Deque;
31  import java.util.LinkedList;
32  import java.util.List;
33  import java.util.NoSuchElementException;
34  import java.util.function.Consumer;
35  import java.util.function.Predicate;
36  
37  import org.junit.jupiter.api.Test;
38  import org.junit.jupiter.params.ParameterizedTest;
39  import org.junit.jupiter.params.provider.ValueSource;
40  
41  public class LayerManagerTest {
42  
43      private final Shape shape = Shape.fromKM(17, 72);
44  
45      @ParameterizedTest
46      @ValueSource(ints = {4, 10, 2, 1})
47      public void testAdvanceOnCount(final int breakAt) {
48          final Predicate<LayerManager<SimpleBloomFilter>> underTest = LayerManager.ExtendCheck.advanceOnCount(breakAt);
49          final LayerManager<SimpleBloomFilter> layerManager = testingBuilder().get();
50          for (int i = 0; i < breakAt - 1; i++) {
51              assertFalse(underTest.test(layerManager), "at " + i);
52              layerManager.getTarget().merge(TestingHashers.FROM1);
53          }
54          assertTrue(underTest.test(layerManager));
55      }
56  
57      @Test
58      public void testAdvanceOnCountInvalidArguments() {
59          assertThrows(IllegalArgumentException.class, () -> LayerManager.ExtendCheck.advanceOnCount(0));
60          assertThrows(IllegalArgumentException.class, () -> LayerManager.ExtendCheck.advanceOnCount(-1));
61      }
62  
63      @Test
64      public void testAdvanceOnPopulated() {
65          final Predicate<LayerManager<SimpleBloomFilter>> underTest = LayerManager.ExtendCheck.advanceOnPopulated();
66          final LayerManager<SimpleBloomFilter> layerManager = testingBuilder().get();
67          assertFalse(underTest.test(layerManager));
68          layerManager.getTarget().merge(TestingHashers.FROM1);
69          assertTrue(underTest.test(layerManager));
70      }
71  
72      @Test
73      public void testAdvanceOnSaturation() {
74          final double maxN = shape.estimateMaxN();
75          int hashStart = 0;
76          final Predicate<LayerManager<SimpleBloomFilter>> underTest = LayerManager.ExtendCheck.advanceOnSaturation(maxN);
77          final LayerManager<SimpleBloomFilter> layerManager = testingBuilder().get();
78          while (layerManager.getTarget().getShape().estimateN(layerManager.getTarget().cardinality()) < maxN) {
79              assertFalse(underTest.test(layerManager));
80              layerManager.getTarget().merge(new IncrementingHasher(hashStart, shape.getNumberOfHashFunctions()));
81              hashStart += shape.getNumberOfHashFunctions();
82          }
83          assertTrue(underTest.test(layerManager));
84          assertThrows(IllegalArgumentException.class, () -> LayerManager.ExtendCheck.advanceOnSaturation(0));
85          assertThrows(IllegalArgumentException.class, () -> LayerManager.ExtendCheck.advanceOnSaturation(-1));
86      }
87  
88      @Test
89      public void testBuilder() {
90          final LayerManager.Builder<SimpleBloomFilter> underTest = LayerManager.builder();
91          NullPointerException npe = assertThrows(NullPointerException.class, underTest::get);
92          assertTrue(npe.getMessage().contains("filterSupplier"));
93          underTest.setSupplier(() -> null).setCleanup(null);
94          npe = assertThrows(NullPointerException.class, underTest::get);
95          assertTrue(npe.getMessage().contains("filterCleanup"));
96          underTest.setCleanup(x -> {
97          }).setExtendCheck(null);
98          npe = assertThrows(NullPointerException.class, underTest::get);
99          assertTrue(npe.getMessage().contains("extendCheck"));
100 
101         npe = assertThrows(NullPointerException.class, () -> LayerManager.builder().setSupplier(() -> null).get());
102         assertTrue(npe.getMessage().contains("filterSupplier.get() returned null."));
103 
104     }
105 
106     @Test
107     public void testClear() {
108         final LayerManager<SimpleBloomFilter> underTest = LayerManager.<SimpleBloomFilter>builder().setSupplier(() -> new SimpleBloomFilter(shape)).get();
109         underTest.getTarget().merge(TestingHashers.randomHasher());
110         underTest.next();
111         underTest.getTarget().merge(TestingHashers.randomHasher());
112         underTest.next();
113         underTest.getTarget().merge(TestingHashers.randomHasher());
114         assertEquals(3, underTest.getDepth());
115         underTest.clear();
116         assertEquals(1, underTest.getDepth());
117         assertEquals(0, underTest.getTarget().cardinality());
118     }
119 
120     @Test
121     public void testCopy() {
122         final LayerManager<SimpleBloomFilter> underTest = LayerManager.<SimpleBloomFilter>builder().setSupplier(() -> new SimpleBloomFilter(shape)).get();
123         underTest.getTarget().merge(TestingHashers.randomHasher());
124         underTest.next();
125         underTest.getTarget().merge(TestingHashers.randomHasher());
126         underTest.next();
127         underTest.getTarget().merge(TestingHashers.randomHasher());
128         assertEquals(3, underTest.getDepth());
129 
130         final LayerManager<SimpleBloomFilter> copy = underTest.copy();
131         assertNotSame(underTest, copy);
132         // object equals not implemented
133         assertNotEquals(underTest, copy);
134 
135         assertEquals(underTest.getDepth(), copy.getDepth());
136         assertTrue(
137                 underTest.processBloomFilterPair(copy, (x, y) -> Arrays.equals(x.asBitMapArray(), y.asBitMapArray())));
138     }
139 
140     @Test
141     public void testForEachBloomFilter() {
142         final LayerManager<SimpleBloomFilter> underTest = LayerManager.<SimpleBloomFilter>builder().setSupplier(() -> new SimpleBloomFilter(shape))
143                 .setExtendCheck(LayerManager.ExtendCheck.advanceOnPopulated()).get();
144 
145         final List<SimpleBloomFilter> lst = new ArrayList<>();
146         for (int i = 0; i < 10; i++) {
147             final SimpleBloomFilter bf = new SimpleBloomFilter(shape);
148             bf.merge(TestingHashers.randomHasher());
149             lst.add(bf);
150             underTest.getTarget().merge(bf);
151         }
152         final List<BloomFilter> lst2 = new ArrayList<>();
153         underTest.processBloomFilters(lst2::add);
154         assertEquals(10, lst.size());
155         assertEquals(10, lst2.size());
156         for (int i = 0; i < lst.size(); i++) {
157             assertArrayEquals(lst.get(i).asBitMapArray(), lst2.get(i).asBitMapArray());
158         }
159     }
160 
161     @Test
162     public void testGet() {
163         final SimpleBloomFilter f = new SimpleBloomFilter(shape);
164         final LayerManager<SimpleBloomFilter> underTest = LayerManager.<SimpleBloomFilter>builder().setSupplier(() -> f).get();
165         assertEquals(1, underTest.getDepth());
166         assertSame(f, underTest.get(0));
167         assertThrows(NoSuchElementException.class, () -> underTest.get(-1));
168         assertThrows(NoSuchElementException.class, () -> underTest.get(1));
169     }
170 
171     private LayerManager.Builder<SimpleBloomFilter> testingBuilder() {
172         return LayerManager.<SimpleBloomFilter>builder().setSupplier(() -> new SimpleBloomFilter(shape));
173     }
174 
175     @Test
176     public void testNeverAdvance() {
177         final Predicate<LayerManager<SimpleBloomFilter>> underTest = LayerManager.ExtendCheck.neverAdvance();
178         final LayerManager<SimpleBloomFilter> layerManager = testingBuilder().get();
179         assertFalse(underTest.test(layerManager));
180         for (int i = 0; i < 10; i++) {
181             layerManager.getTarget().merge(TestingHashers.randomHasher());
182             assertFalse(underTest.test(layerManager));
183         }
184     }
185 
186     @Test
187     public void testNextAndGetDepth() {
188         final LayerManager<SimpleBloomFilter> underTest = LayerManager.<SimpleBloomFilter>builder().setSupplier(() -> new SimpleBloomFilter(shape)).get();
189         assertEquals(1, underTest.getDepth());
190         underTest.getTarget().merge(TestingHashers.randomHasher());
191         assertEquals(1, underTest.getDepth());
192         underTest.next();
193         assertEquals(2, underTest.getDepth());
194     }
195 
196     @Test
197     public void testNoCleanup() {
198         final Consumer<Deque<SimpleBloomFilter>> underTest = LayerManager.Cleanup.noCleanup();
199         final Deque<SimpleBloomFilter> list = new LinkedList<>();
200         for (int i = 0; i < 20; i++) {
201             assertEquals(i, list.size());
202             list.add(new SimpleBloomFilter(shape));
203             underTest.accept(list);
204         }
205     }
206 
207     @ParameterizedTest
208     @ValueSource(ints = {5, 100, 2, 1})
209     public void testOnMaxSize(final int maxSize) {
210         final Consumer<Deque<SimpleBloomFilter>> underTest = LayerManager.Cleanup.onMaxSize(maxSize);
211         final LinkedList<SimpleBloomFilter> list = new LinkedList<>();
212         for (int i = 0; i < maxSize; i++) {
213             assertEquals(i, list.size());
214             list.add(new SimpleBloomFilter(shape));
215             underTest.accept(list);
216         }
217         assertEquals(maxSize, list.size());
218 
219         for (int i = 0; i < maxSize; i++) {
220             list.add(new SimpleBloomFilter(shape));
221             underTest.accept(list);
222             assertEquals(maxSize, list.size());
223         }
224     }
225 
226     @Test
227     public void testOnMaxSizeIllegalValues() {
228         assertThrows(IllegalArgumentException.class, () -> LayerManager.Cleanup.onMaxSize(0));
229         assertThrows(IllegalArgumentException.class, () -> LayerManager.Cleanup.onMaxSize(-1));
230     }
231 
232     @Test
233     public void testRemoveEmptyTarget() {
234         final Consumer<Deque<SimpleBloomFilter>> underTest = LayerManager.Cleanup.removeEmptyTarget();
235         final LinkedList<SimpleBloomFilter> list = new LinkedList<>();
236 
237         // removes an empty filter
238         final SimpleBloomFilter bf = new SimpleBloomFilter(shape);
239         list.add(bf);
240         assertEquals(bf, list.get(0));
241         underTest.accept(list);
242         assertTrue(list.isEmpty());
243 
244         // does not remove a populated filter.
245         bf.merge(IndexExtractor.fromIndexArray(1));
246         list.add(bf);
247         assertEquals(bf, list.get(0));
248         underTest.accept(list);
249         assertEquals(bf, list.get(0));
250 
251         // does not remove an empty filter followed by a populated filter.
252         list.clear();
253         list.add(new SimpleBloomFilter(shape));
254         list.add(bf);
255         assertEquals(2, list.size());
256         underTest.accept(list);
257         assertEquals(2, list.size());
258 
259         // does not remove multiple empty filters at the end of the list, just the last
260         // one.
261         list.clear();
262         list.add(bf);
263         list.add(new SimpleBloomFilter(shape));
264         list.add(new SimpleBloomFilter(shape));
265         assertEquals(3, list.size());
266         underTest.accept(list);
267         assertEquals(2, list.size());
268         assertEquals(bf, list.get(0));
269     }
270 
271     @Test
272     public void testTarget() {
273         final boolean[] extendCheckCalled = { false };
274         final boolean[] cleanupCalled = { false };
275         final int[] supplierCount = { 0 };
276         final LayerManager<SimpleBloomFilter> underTest = LayerManager.<SimpleBloomFilter>builder().setSupplier(() -> {
277             supplierCount[0]++;
278             return new SimpleBloomFilter(shape);
279         }).setExtendCheck(lm -> {
280             extendCheckCalled[0] = true;
281             return true;
282         }).setCleanup(ll -> {
283             cleanupCalled[0] = true;
284         }).get();
285         assertFalse(extendCheckCalled[0]);
286         assertFalse(cleanupCalled[0]);
287         assertEquals(1, supplierCount[0]);
288         underTest.getTarget();
289         assertTrue(extendCheckCalled[0]);
290         assertTrue(cleanupCalled[0]);
291         assertEquals(2, supplierCount[0]);
292     }
293 
294 }