• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
1 /*
2  * Copyright (C) 2015 The Guava Authors
3  *
4  * Licensed under the Apache License, Version 2.0 (the "License");
5  * you may not use this file except in compliance with the License.
6  * You may obtain a copy of the License at
7  *
8  * http://www.apache.org/licenses/LICENSE-2.0
9  *
10  * Unless required by applicable law or agreed to in writing, software
11  * distributed under the License is distributed on an "AS IS" BASIS,
12  * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13  * See the License for the specific language governing permissions and
14  * limitations under the License.
15  */
16 
17 package com.google.common.collect.testing;
18 
19 import static com.google.common.base.Preconditions.checkNotNull;
20 import static com.google.common.collect.testing.Helpers.assertEqualIgnoringOrder;
21 import static com.google.common.collect.testing.Helpers.assertEqualInOrder;
22 import static com.google.common.collect.testing.Platform.format;
23 import static java.util.Comparator.naturalOrder;
24 import static junit.framework.Assert.assertEquals;
25 import static junit.framework.Assert.assertFalse;
26 import static junit.framework.Assert.assertTrue;
27 import static junit.framework.Assert.fail;
28 
29 import com.google.common.annotations.GwtCompatible;
30 import com.google.common.collect.ImmutableSet;
31 import com.google.common.collect.Ordering;
32 import com.google.common.primitives.Ints;
33 import java.util.ArrayList;
34 import java.util.Arrays;
35 import java.util.Comparator;
36 import java.util.EnumSet;
37 import java.util.List;
38 import java.util.Spliterator;
39 import java.util.function.Consumer;
40 import java.util.function.Function;
41 import java.util.function.Supplier;
42 import org.checkerframework.checker.nullness.qual.Nullable;
43 
44 /** Tester for {@code Spliterator} implementations. */
45 @GwtCompatible
46 public final class SpliteratorTester<E> {
47   /** Return type from "contains the following elements" assertions. */
48   public interface Ordered {
49     /**
50      * Attests that the expected values must not just be present but must be present in the order
51      * they were given.
52      */
inOrder()53     void inOrder();
54   }
55 
56   private abstract static class GeneralSpliterator<E> {
57     final Spliterator<E> spliterator;
58 
GeneralSpliterator(Spliterator<E> spliterator)59     GeneralSpliterator(Spliterator<E> spliterator) {
60       this.spliterator = checkNotNull(spliterator);
61     }
62 
forEachRemaining(Consumer<? super E> action)63     abstract void forEachRemaining(Consumer<? super E> action);
64 
tryAdvance(Consumer<? super E> action)65     abstract boolean tryAdvance(Consumer<? super E> action);
66 
trySplit()67     abstract GeneralSpliterator<E> trySplit();
68 
characteristics()69     final int characteristics() {
70       return spliterator.characteristics();
71     }
72 
estimateSize()73     final long estimateSize() {
74       return spliterator.estimateSize();
75     }
76 
getComparator()77     final Comparator<? super E> getComparator() {
78       return spliterator.getComparator();
79     }
80 
getExactSizeIfKnown()81     final long getExactSizeIfKnown() {
82       return spliterator.getExactSizeIfKnown();
83     }
84 
hasCharacteristics(int characteristics)85     final boolean hasCharacteristics(int characteristics) {
86       return spliterator.hasCharacteristics(characteristics);
87     }
88   }
89 
90   private static final class GeneralSpliteratorOfObject<E> extends GeneralSpliterator<E> {
GeneralSpliteratorOfObject(Spliterator<E> spliterator)91     GeneralSpliteratorOfObject(Spliterator<E> spliterator) {
92       super(spliterator);
93     }
94 
95     @Override
forEachRemaining(Consumer<? super E> action)96     void forEachRemaining(Consumer<? super E> action) {
97       spliterator.forEachRemaining(action);
98     }
99 
100     @Override
tryAdvance(Consumer<? super E> action)101     boolean tryAdvance(Consumer<? super E> action) {
102       return spliterator.tryAdvance(action);
103     }
104 
105     @Override
trySplit()106     GeneralSpliterator<E> trySplit() {
107       Spliterator<E> split = spliterator.trySplit();
108       return split == null ? null : new GeneralSpliteratorOfObject<>(split);
109     }
110   }
111 
112   /*
113    * The AndroidJdkLibsChecker violation is informing us that this method isn't usable under
114    * Desugar. But we want to include it here for Nougat+ users -- and, mainly, for non-Android
115    * users. Fortunately, anyone who tries to use it under Desugar will presumably already see errors
116    * from creating the Spliterator.OfInt in the first place. So it's probably OK for us to suppress
117    * this particular violation.
118    */
119   @SuppressWarnings("AndroidJdkLibsChecker")
120   private static final class GeneralSpliteratorOfPrimitive<E, C> extends GeneralSpliterator<E> {
121     final Spliterator.OfPrimitive<E, C, ?> spliterator;
122     final Function<Consumer<? super E>, C> consumerizer;
123 
GeneralSpliteratorOfPrimitive( Spliterator.OfPrimitive<E, C, ?> spliterator, Function<Consumer<? super E>, C> consumerizer)124     GeneralSpliteratorOfPrimitive(
125         Spliterator.OfPrimitive<E, C, ?> spliterator,
126         Function<Consumer<? super E>, C> consumerizer) {
127       super(spliterator);
128       this.spliterator = spliterator;
129       this.consumerizer = consumerizer;
130     }
131 
132     @Override
forEachRemaining(Consumer<? super E> action)133     void forEachRemaining(Consumer<? super E> action) {
134       spliterator.forEachRemaining(consumerizer.apply(action));
135     }
136 
137     @Override
tryAdvance(Consumer<? super E> action)138     boolean tryAdvance(Consumer<? super E> action) {
139       return spliterator.tryAdvance(consumerizer.apply(action));
140     }
141 
142     @Override
trySplit()143     GeneralSpliterator<E> trySplit() {
144       Spliterator.OfPrimitive<E, C, ?> split = spliterator.trySplit();
145       return split == null ? null : new GeneralSpliteratorOfPrimitive<>(split, consumerizer);
146     }
147   }
148 
149   /**
150    * Different ways of decomposing a Spliterator, all of which must produce the same elements (up to
151    * ordering, if Spliterator.ORDERED is not present).
152    */
153   enum SpliteratorDecompositionStrategy {
154     NO_SPLIT_FOR_EACH_REMAINING {
155       @Override
forEach(GeneralSpliterator<E> spliterator, Consumer<? super E> consumer)156       <E> void forEach(GeneralSpliterator<E> spliterator, Consumer<? super E> consumer) {
157         spliterator.forEachRemaining(consumer);
158       }
159     },
160     NO_SPLIT_TRY_ADVANCE {
161       @Override
forEach(GeneralSpliterator<E> spliterator, Consumer<? super E> consumer)162       <E> void forEach(GeneralSpliterator<E> spliterator, Consumer<? super E> consumer) {
163         while (spliterator.tryAdvance(consumer)) {
164           // do nothing
165         }
166       }
167     },
168     MAXIMUM_SPLIT {
169       @Override
forEach(GeneralSpliterator<E> spliterator, Consumer<? super E> consumer)170       <E> void forEach(GeneralSpliterator<E> spliterator, Consumer<? super E> consumer) {
171         for (GeneralSpliterator<E> prefix = trySplitTestingSize(spliterator);
172             prefix != null;
173             prefix = trySplitTestingSize(spliterator)) {
174           forEach(prefix, consumer);
175         }
176         long size = spliterator.getExactSizeIfKnown();
177         long[] counter = {0};
178         spliterator.forEachRemaining(
179             e -> {
180               consumer.accept(e);
181               counter[0]++;
182             });
183         if (size >= 0) {
184           assertEquals(size, counter[0]);
185         }
186       }
187     },
188     ALTERNATE_ADVANCE_AND_SPLIT {
189       @Override
forEach(GeneralSpliterator<E> spliterator, Consumer<? super E> consumer)190       <E> void forEach(GeneralSpliterator<E> spliterator, Consumer<? super E> consumer) {
191         while (spliterator.tryAdvance(consumer)) {
192           GeneralSpliterator<E> prefix = trySplitTestingSize(spliterator);
193           if (prefix != null) {
194             forEach(prefix, consumer);
195           }
196         }
197       }
198     };
199 
forEach(GeneralSpliterator<E> spliterator, Consumer<? super E> consumer)200     abstract <E> void forEach(GeneralSpliterator<E> spliterator, Consumer<? super E> consumer);
201   }
202 
trySplitTestingSize( GeneralSpliterator<E> spliterator)203   private static <E> @Nullable GeneralSpliterator<E> trySplitTestingSize(
204       GeneralSpliterator<E> spliterator) {
205     boolean subsized = spliterator.hasCharacteristics(Spliterator.SUBSIZED);
206     long originalSize = spliterator.estimateSize();
207     GeneralSpliterator<E> trySplit = spliterator.trySplit();
208     if (spliterator.estimateSize() > originalSize) {
209       fail(
210           format(
211               "estimated size of spliterator after trySplit (%s) is larger than original size (%s)",
212               spliterator.estimateSize(), originalSize));
213     }
214     if (trySplit != null) {
215       if (trySplit.estimateSize() > originalSize) {
216         fail(
217             format(
218                 "estimated size of trySplit result (%s) is larger than original size (%s)",
219                 trySplit.estimateSize(), originalSize));
220       }
221     }
222     if (subsized) {
223       if (trySplit != null) {
224         assertEquals(
225             "sum of estimated sizes of trySplit and original spliterator after trySplit",
226             originalSize,
227             trySplit.estimateSize() + spliterator.estimateSize());
228       } else {
229         assertEquals(
230             "estimated size of spliterator after failed trySplit",
231             originalSize,
232             spliterator.estimateSize());
233       }
234     }
235     return trySplit;
236   }
237 
of(Supplier<Spliterator<E>> spliteratorSupplier)238   public static <E> SpliteratorTester<E> of(Supplier<Spliterator<E>> spliteratorSupplier) {
239     return new SpliteratorTester<>(
240         ImmutableSet.of(() -> new GeneralSpliteratorOfObject<>(spliteratorSupplier.get())));
241   }
242 
243   /** @since 28.1 */
244   @SuppressWarnings("AndroidJdkLibsChecker") // see comment on GeneralSpliteratorOfPrimitive
ofInt(Supplier<Spliterator.OfInt> spliteratorSupplier)245   public static SpliteratorTester<Integer> ofInt(Supplier<Spliterator.OfInt> spliteratorSupplier) {
246     return new SpliteratorTester<>(
247         ImmutableSet.of(
248             () -> new GeneralSpliteratorOfObject<>(spliteratorSupplier.get()),
249             () -> new GeneralSpliteratorOfPrimitive<>(spliteratorSupplier.get(), c -> c::accept)));
250   }
251 
252   /** @since 28.1 */
253   @SuppressWarnings("AndroidJdkLibsChecker") // see comment on GeneralSpliteratorOfPrimitive
ofLong(Supplier<Spliterator.OfLong> spliteratorSupplier)254   public static SpliteratorTester<Long> ofLong(Supplier<Spliterator.OfLong> spliteratorSupplier) {
255     return new SpliteratorTester<>(
256         ImmutableSet.of(
257             () -> new GeneralSpliteratorOfObject<>(spliteratorSupplier.get()),
258             () -> new GeneralSpliteratorOfPrimitive<>(spliteratorSupplier.get(), c -> c::accept)));
259   }
260 
261   /** @since 28.1 */
262   @SuppressWarnings("AndroidJdkLibsChecker") // see comment on GeneralSpliteratorOfPrimitive
ofDouble( Supplier<Spliterator.OfDouble> spliteratorSupplier)263   public static SpliteratorTester<Double> ofDouble(
264       Supplier<Spliterator.OfDouble> spliteratorSupplier) {
265     return new SpliteratorTester<>(
266         ImmutableSet.of(
267             () -> new GeneralSpliteratorOfObject<>(spliteratorSupplier.get()),
268             () -> new GeneralSpliteratorOfPrimitive<>(spliteratorSupplier.get(), c -> c::accept)));
269   }
270 
271   private final ImmutableSet<Supplier<GeneralSpliterator<E>>> spliteratorSuppliers;
272 
SpliteratorTester(ImmutableSet<Supplier<GeneralSpliterator<E>>> spliteratorSuppliers)273   private SpliteratorTester(ImmutableSet<Supplier<GeneralSpliterator<E>>> spliteratorSuppliers) {
274     this.spliteratorSuppliers = checkNotNull(spliteratorSuppliers);
275   }
276 
277   @SafeVarargs
expect(Object... elements)278   public final Ordered expect(Object... elements) {
279     return expect(Arrays.asList(elements));
280   }
281 
expect(Iterable<?> elements)282   public final Ordered expect(Iterable<?> elements) {
283     List<List<E>> resultsForAllStrategies = new ArrayList<>();
284     for (Supplier<GeneralSpliterator<E>> spliteratorSupplier : spliteratorSuppliers) {
285       GeneralSpliterator<E> spliterator = spliteratorSupplier.get();
286       int characteristics = spliterator.characteristics();
287       long estimatedSize = spliterator.estimateSize();
288       for (SpliteratorDecompositionStrategy strategy :
289           EnumSet.allOf(SpliteratorDecompositionStrategy.class)) {
290         List<E> resultsForStrategy = new ArrayList<>();
291         strategy.forEach(spliteratorSupplier.get(), resultsForStrategy::add);
292 
293         // TODO(cpovirk): better failure messages
294         if ((characteristics & Spliterator.NONNULL) != 0) {
295           assertFalse(resultsForStrategy.contains(null));
296         }
297         if ((characteristics & Spliterator.SORTED) != 0) {
298           Comparator<? super E> comparator = spliterator.getComparator();
299           if (comparator == null) {
300             comparator = (Comparator) naturalOrder();
301           }
302           assertTrue(Ordering.from(comparator).isOrdered(resultsForStrategy));
303         }
304         if ((characteristics & Spliterator.SIZED) != 0) {
305           assertEquals(Ints.checkedCast(estimatedSize), resultsForStrategy.size());
306         }
307 
308         assertEqualIgnoringOrder(elements, resultsForStrategy);
309         resultsForAllStrategies.add(resultsForStrategy);
310       }
311     }
312     return new Ordered() {
313       @Override
314       public void inOrder() {
315         for (List<E> resultsForStrategy : resultsForAllStrategies) {
316           assertEqualInOrder(elements, resultsForStrategy);
317         }
318       }
319     };
320   }
321 }
322