guava
344 строки · 11.9 Кб
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
17package com.google.common.collect.testing;
18
19import static com.google.common.base.Preconditions.checkNotNull;
20import static com.google.common.collect.testing.Helpers.assertEqualIgnoringOrder;
21import static com.google.common.collect.testing.Helpers.assertEqualInOrder;
22import static com.google.common.collect.testing.Platform.format;
23import static java.util.Arrays.asList;
24import static java.util.Collections.unmodifiableSet;
25import static junit.framework.Assert.assertEquals;
26import static junit.framework.Assert.assertFalse;
27import static junit.framework.Assert.assertTrue;
28import static junit.framework.Assert.fail;
29
30import com.google.common.annotations.GwtCompatible;
31import com.google.common.collect.ImmutableSet;
32import com.google.common.collect.Ordering;
33import com.google.common.primitives.Ints;
34import com.google.errorprone.annotations.CanIgnoreReturnValue;
35import java.util.ArrayList;
36import java.util.Arrays;
37import java.util.Comparator;
38import java.util.LinkedHashSet;
39import java.util.List;
40import java.util.Set;
41import java.util.Spliterator;
42import java.util.Spliterator.OfPrimitive;
43import java.util.function.Consumer;
44import java.util.function.Function;
45import java.util.function.Supplier;
46import org.checkerframework.checker.nullness.qual.Nullable;
47
48/**
49* Tester for {@code Spliterator} implementations.
50*
51* @since 21.0
52*/
53@GwtCompatible
54@ElementTypesAreNonnullByDefault
55public final class SpliteratorTester<E extends @Nullable Object> {
56/** Return type from "contains the following elements" assertions. */
57public interface Ordered {
58/**
59* Attests that the expected values must not just be present but must be present in the order
60* they were given.
61*/
62void inOrder();
63}
64
65private abstract static class GeneralSpliterator<E extends @Nullable Object> {
66final Spliterator<E> spliterator;
67
68GeneralSpliterator(Spliterator<E> spliterator) {
69this.spliterator = checkNotNull(spliterator);
70}
71
72abstract void forEachRemaining(Consumer<? super E> action);
73
74abstract boolean tryAdvance(Consumer<? super E> action);
75
76abstract @Nullable GeneralSpliterator<E> trySplit();
77
78final int characteristics() {
79return spliterator.characteristics();
80}
81
82final long estimateSize() {
83return spliterator.estimateSize();
84}
85
86final Comparator<? super E> getComparator() {
87return spliterator.getComparator();
88}
89
90final long getExactSizeIfKnown() {
91return spliterator.getExactSizeIfKnown();
92}
93
94final boolean hasCharacteristics(int characteristics) {
95return spliterator.hasCharacteristics(characteristics);
96}
97}
98
99private static final class GeneralSpliteratorOfObject<E extends @Nullable Object>
100extends GeneralSpliterator<E> {
101GeneralSpliteratorOfObject(Spliterator<E> spliterator) {
102super(spliterator);
103}
104
105@Override
106void forEachRemaining(Consumer<? super E> action) {
107spliterator.forEachRemaining(action);
108}
109
110@Override
111boolean tryAdvance(Consumer<? super E> action) {
112return spliterator.tryAdvance(action);
113}
114
115@Override
116@Nullable GeneralSpliterator<E> trySplit() {
117Spliterator<E> split = spliterator.trySplit();
118return split == null ? null : new GeneralSpliteratorOfObject<>(split);
119}
120}
121
122private static final class GeneralSpliteratorOfPrimitive<
123E extends @Nullable Object, C, S extends Spliterator.OfPrimitive<E, C, S>>
124extends GeneralSpliterator<E> {
125final OfPrimitive<E, C, S> spliteratorOfPrimitive;
126final Function<Consumer<? super E>, C> consumerizer;
127
128GeneralSpliteratorOfPrimitive(
129Spliterator.OfPrimitive<E, C, S> spliterator,
130Function<Consumer<? super E>, C> consumerizer) {
131super(spliterator);
132this.spliteratorOfPrimitive = spliterator;
133this.consumerizer = consumerizer;
134}
135
136@Override
137void forEachRemaining(Consumer<? super E> action) {
138spliteratorOfPrimitive.forEachRemaining(consumerizer.apply(action));
139}
140
141@Override
142boolean tryAdvance(Consumer<? super E> action) {
143return spliteratorOfPrimitive.tryAdvance(consumerizer.apply(action));
144}
145
146@Override
147@Nullable GeneralSpliterator<E> trySplit() {
148Spliterator.OfPrimitive<E, C, ?> split = spliteratorOfPrimitive.trySplit();
149return split == null ? null : new GeneralSpliteratorOfPrimitive<>(split, consumerizer);
150}
151}
152
153/**
154* Different ways of decomposing a Spliterator, all of which must produce the same elements (up to
155* ordering, if Spliterator.ORDERED is not present).
156*/
157enum SpliteratorDecompositionStrategy {
158NO_SPLIT_FOR_EACH_REMAINING {
159@Override
160<E extends @Nullable Object> void forEach(
161GeneralSpliterator<E> spliterator, Consumer<? super E> consumer) {
162spliterator.forEachRemaining(consumer);
163}
164},
165NO_SPLIT_TRY_ADVANCE {
166@Override
167<E extends @Nullable Object> void forEach(
168GeneralSpliterator<E> spliterator, Consumer<? super E> consumer) {
169while (spliterator.tryAdvance(consumer)) {
170// do nothing
171}
172}
173},
174MAXIMUM_SPLIT {
175@Override
176<E extends @Nullable Object> void forEach(
177GeneralSpliterator<E> spliterator, Consumer<? super E> consumer) {
178for (GeneralSpliterator<E> prefix = trySplitTestingSize(spliterator);
179prefix != null;
180prefix = trySplitTestingSize(spliterator)) {
181forEach(prefix, consumer);
182}
183long size = spliterator.getExactSizeIfKnown();
184long[] counter = {0};
185spliterator.forEachRemaining(
186e -> {
187consumer.accept(e);
188counter[0]++;
189});
190if (size >= 0) {
191assertEquals(size, counter[0]);
192}
193}
194},
195ALTERNATE_ADVANCE_AND_SPLIT {
196@Override
197<E extends @Nullable Object> void forEach(
198GeneralSpliterator<E> spliterator, Consumer<? super E> consumer) {
199while (spliterator.tryAdvance(consumer)) {
200GeneralSpliterator<E> prefix = trySplitTestingSize(spliterator);
201if (prefix != null) {
202forEach(prefix, consumer);
203}
204}
205}
206};
207
208abstract <E extends @Nullable Object> void forEach(
209GeneralSpliterator<E> spliterator, Consumer<? super E> consumer);
210
211static final Set<SpliteratorDecompositionStrategy> ALL_STRATEGIES =
212unmodifiableSet(new LinkedHashSet<>(asList(values())));
213}
214
215private static <E extends @Nullable Object> @Nullable GeneralSpliterator<E> trySplitTestingSize(
216GeneralSpliterator<E> spliterator) {
217boolean subsized = spliterator.hasCharacteristics(Spliterator.SUBSIZED);
218long originalSize = spliterator.estimateSize();
219GeneralSpliterator<E> trySplit = spliterator.trySplit();
220if (spliterator.estimateSize() > originalSize) {
221fail(
222format(
223"estimated size of spliterator after trySplit (%s) is larger than original size (%s)",
224spliterator.estimateSize(), originalSize));
225}
226if (trySplit != null) {
227if (trySplit.estimateSize() > originalSize) {
228fail(
229format(
230"estimated size of trySplit result (%s) is larger than original size (%s)",
231trySplit.estimateSize(), originalSize));
232}
233}
234if (subsized) {
235if (trySplit != null) {
236assertEquals(
237"sum of estimated sizes of trySplit and original spliterator after trySplit",
238originalSize,
239trySplit.estimateSize() + spliterator.estimateSize());
240} else {
241assertEquals(
242"estimated size of spliterator after failed trySplit",
243originalSize,
244spliterator.estimateSize());
245}
246}
247return trySplit;
248}
249
250public static <E extends @Nullable Object> SpliteratorTester<E> of(
251Supplier<Spliterator<E>> spliteratorSupplier) {
252return new SpliteratorTester<>(
253ImmutableSet.of(() -> new GeneralSpliteratorOfObject<>(spliteratorSupplier.get())));
254}
255
256/**
257* @since 28.1
258*/
259public static SpliteratorTester<Integer> ofInt(Supplier<Spliterator.OfInt> spliteratorSupplier) {
260return new SpliteratorTester<>(
261ImmutableSet.of(
262() -> new GeneralSpliteratorOfObject<>(spliteratorSupplier.get()),
263() -> new GeneralSpliteratorOfPrimitive<>(spliteratorSupplier.get(), c -> c::accept)));
264}
265
266/**
267* @since 28.1
268*/
269public static SpliteratorTester<Long> ofLong(Supplier<Spliterator.OfLong> spliteratorSupplier) {
270return new SpliteratorTester<>(
271ImmutableSet.of(
272() -> new GeneralSpliteratorOfObject<>(spliteratorSupplier.get()),
273() -> new GeneralSpliteratorOfPrimitive<>(spliteratorSupplier.get(), c -> c::accept)));
274}
275
276/**
277* @since 28.1
278*/
279public static SpliteratorTester<Double> ofDouble(
280Supplier<Spliterator.OfDouble> spliteratorSupplier) {
281return new SpliteratorTester<>(
282ImmutableSet.of(
283() -> new GeneralSpliteratorOfObject<>(spliteratorSupplier.get()),
284() -> new GeneralSpliteratorOfPrimitive<>(spliteratorSupplier.get(), c -> c::accept)));
285}
286
287private final ImmutableSet<Supplier<GeneralSpliterator<E>>> spliteratorSuppliers;
288
289private SpliteratorTester(ImmutableSet<Supplier<GeneralSpliterator<E>>> spliteratorSuppliers) {
290this.spliteratorSuppliers = checkNotNull(spliteratorSuppliers);
291}
292
293@SafeVarargs
294@CanIgnoreReturnValue
295public final Ordered expect(Object... elements) {
296return expect(Arrays.asList(elements));
297}
298
299@CanIgnoreReturnValue
300public final Ordered expect(Iterable<?> elements) {
301List<List<E>> resultsForAllStrategies = new ArrayList<>();
302for (Supplier<GeneralSpliterator<E>> spliteratorSupplier : spliteratorSuppliers) {
303GeneralSpliterator<E> spliterator = spliteratorSupplier.get();
304int characteristics = spliterator.characteristics();
305long estimatedSize = spliterator.estimateSize();
306for (SpliteratorDecompositionStrategy strategy :
307SpliteratorDecompositionStrategy.ALL_STRATEGIES) {
308List<E> resultsForStrategy = new ArrayList<>();
309strategy.forEach(spliteratorSupplier.get(), resultsForStrategy::add);
310
311// TODO(cpovirk): better failure messages
312if ((characteristics & Spliterator.NONNULL) != 0) {
313assertFalse(resultsForStrategy.contains(null));
314}
315if ((characteristics & Spliterator.SORTED) != 0) {
316Comparator<? super E> comparator = spliterator.getComparator();
317if (comparator == null) {
318// A sorted spliterator with no comparator is already using natural order.
319// (We could probably find a way to avoid rawtypes here if we wanted.)
320@SuppressWarnings({"unchecked", "rawtypes"})
321Comparator<? super E> naturalOrder =
322(Comparator<? super E>) Comparator.<Comparable>naturalOrder();
323comparator = naturalOrder;
324}
325assertTrue(Ordering.from(comparator).isOrdered(resultsForStrategy));
326}
327if ((characteristics & Spliterator.SIZED) != 0) {
328assertEquals(Ints.checkedCast(estimatedSize), resultsForStrategy.size());
329}
330
331assertEqualIgnoringOrder(elements, resultsForStrategy);
332resultsForAllStrategies.add(resultsForStrategy);
333}
334}
335return new Ordered() {
336@Override
337public void inOrder() {
338for (List<E> resultsForStrategy : resultsForAllStrategies) {
339assertEqualInOrder(elements, resultsForStrategy);
340}
341}
342};
343}
344}
345