1 /* 2 * Copyright (C) 2016 The Android Open Source Project 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 libcore.java.util; 18 19 20 import java.util.ArrayList; 21 import java.util.Collection; 22 import java.util.Collections; 23 import java.util.Comparator; 24 import java.util.HashSet; 25 import java.util.Iterator; 26 import java.util.List; 27 import java.util.List; 28 import java.util.Locale; 29 import java.util.Spliterator; 30 import java.util.function.Consumer; 31 32 import static java.util.Spliterator.ORDERED; 33 import static java.util.Spliterator.SIZED; 34 import static java.util.Spliterator.SUBSIZED; 35 import static junit.framework.Assert.assertEquals; 36 import static junit.framework.Assert.assertFalse; 37 import static junit.framework.Assert.assertNotNull; 38 import static junit.framework.Assert.assertNull; 39 import static junit.framework.Assert.assertTrue; 40 import static junit.framework.Assert.fail; 41 42 public class SpliteratorTester { runBasicIterationTests(Spliterator<T> spliterator, List<T> expectedElements)43 public static <T> void runBasicIterationTests(Spliterator<T> spliterator, 44 List<T> expectedElements) { 45 List<T> recorder = new ArrayList<T>(expectedElements.size()); 46 Consumer<T> consumer = (T value) -> recorder.add(value); 47 48 // tryAdvance. 49 boolean didAdvance = spliterator.tryAdvance(consumer); 50 assertEquals(!expectedElements.isEmpty(), didAdvance); 51 52 // forEachRemaining. 53 spliterator.forEachRemaining(consumer); 54 assertEquals(expectedElements, recorder); 55 56 // There should be no more elements remaining in this spliterator. 57 assertFalse(spliterator.tryAdvance(consumer)); 58 spliterator.forEachRemaining((T) -> fail()); 59 } 60 runBasicIterationTests_unordered(Spliterator<T> spliterator, List<T> expectedElements, Comparator<T> comparator)61 public static <T> void runBasicIterationTests_unordered(Spliterator<T> spliterator, 62 List<T> expectedElements, Comparator<T> comparator) { 63 ArrayList<T> recorder = new ArrayList<T>(expectedElements.size()); 64 Consumer<T> consumer = (T value) -> recorder.add(value); 65 66 // tryAdvance. 67 if (expectedElements.isEmpty()) { 68 assertFalse(spliterator.tryAdvance(consumer)); 69 } else { 70 assertTrue(spliterator.tryAdvance(consumer)); 71 assertTrue(expectedElements.contains(recorder.get(0))); 72 } 73 74 // forEachRemaining. 75 spliterator.forEachRemaining(consumer); 76 Collections.sort(expectedElements, comparator); 77 Collections.sort(recorder, comparator); 78 assertEquals(expectedElements, recorder); 79 80 // There should be no more elements remaining in this spliterator. 81 assertFalse(spliterator.tryAdvance(consumer)); 82 spliterator.forEachRemaining((T) -> fail()); 83 } 84 recordAndAssertBasicIteration( Spliterator<T> spliterator, ArrayList<T> recorder)85 private static <T> void recordAndAssertBasicIteration( 86 Spliterator<T> spliterator, ArrayList<T> recorder) { 87 spliterator.tryAdvance(value -> recorder.add(value)); 88 spliterator.forEachRemaining(value -> recorder.add(value)); 89 90 // There shouldn't be any elements left in the spliterator. 91 assertFalse(spliterator.tryAdvance(value -> recorder.add(value))); 92 spliterator.tryAdvance(value -> fail()); 93 94 // And all subsequent splits should fail. 95 assertNull(spliterator.trySplit()); 96 } 97 testSpliteratorNPE(Spliterator<?> spliterator)98 public static void testSpliteratorNPE(Spliterator<?> spliterator) { 99 try { 100 spliterator.tryAdvance(null); 101 fail(); 102 } catch (NullPointerException expected) { 103 } 104 105 try { 106 spliterator.forEachRemaining(null); 107 fail(); 108 } catch (NullPointerException expected) { 109 } 110 } 111 runBasicSplitTests( Iterable<T> spliterable, List<T> expectedElements)112 public static <T extends Comparable<T>> void runBasicSplitTests( 113 Iterable<T> spliterable, List<T> expectedElements) { 114 runBasicSplitTests(spliterable, expectedElements, T::compareTo); 115 } 116 runBasicSplitTests(Spliterator<T> spliterator, List<T> expectedElements, Comparator<T> comparator)117 public static <T> void runBasicSplitTests(Spliterator<T> spliterator, 118 List<T> expectedElements, Comparator<T> comparator) { 119 boolean empty = expectedElements.isEmpty(); 120 ArrayList<T> recorder = new ArrayList<>(); 121 122 // Advance the original spliterator by one element. 123 boolean didAdvance = spliterator.tryAdvance(value -> recorder.add(value)); 124 assertEquals(!empty, didAdvance); 125 126 // Try splitting it. 127 Spliterator<T> split1 = spliterator.trySplit(); 128 // trySplit() may always return null, but is only required to when empty 129 if (empty) { 130 assertNull(split1); 131 } else if (split1 != null) { 132 // Try to split the resulting split. 133 Spliterator<T> split1_1 = split1.trySplit(); 134 Spliterator<T> split1_2 = split1.trySplit(); 135 if (split1_1 != null) { 136 recordAndAssertBasicIteration(split1_1, recorder); 137 } 138 if (split1_2 != null) { 139 recordAndAssertBasicIteration(split1_2, recorder); 140 } 141 142 // Iterate over the remainder of split1. 143 recordAndAssertBasicIteration(split1, recorder); 144 } 145 // Try to split the original iterator again. 146 Spliterator<T> split2 = spliterator.trySplit(); 147 if (split2 != null) { 148 recordAndAssertBasicIteration(split2, recorder); 149 } 150 151 // Record all remaining elements of the original spliterator. 152 recordAndAssertBasicIteration(spliterator, recorder); 153 154 Collections.sort(expectedElements, comparator); 155 Collections.sort(recorder, comparator); 156 assertEquals(expectedElements, recorder); 157 } 158 assertSupportsTrySplit(Iterable spliterable)159 public static <T> void assertSupportsTrySplit(Iterable spliterable) { 160 assertNotNull(spliterable.spliterator().trySplit()); 161 // only non-empty Iterables may return a non-null value from trySplit() 162 assertTrue("Expected nonempty iterable, got " + spliterable, 163 spliterable.iterator().hasNext()); 164 } 165 166 /** 167 * Note that the contract of trySplit() is generally quite weak (as it must be). There 168 * are no demands about when the spliterator can or cannot split itself. In general, this 169 * test is quite loose. All it does is exercise the basic methods on the splits (if any) 170 * and confirms that the union of all elements in the split is the collection that was 171 * iterated over. 172 */ runBasicSplitTests(Iterable<T> spliterable, List<T> expectedElements, Comparator<T> comparator)173 public static <T> void runBasicSplitTests(Iterable<T> spliterable, 174 List<T> expectedElements, Comparator<T> comparator) { 175 runBasicSplitTests(spliterable.spliterator(), expectedElements, comparator); 176 } 177 toList(Iterator<T> iterator)178 private static<T> List<T> toList(Iterator<T> iterator) { 179 List<T> result = new ArrayList<>(); 180 while (iterator.hasNext()) { 181 result.add(iterator.next()); 182 } 183 return result; 184 } 185 toList(Spliterator<T> spliterator)186 private static<T> List<T> toList(Spliterator<T> spliterator) { 187 List<T> result = new ArrayList<>(); 188 spliterator.forEachRemaining(value -> result.add(value)); 189 return result; 190 } 191 runOrderedTests(Iterable<T> spliterable)192 public static <T> void runOrderedTests(Iterable<T> spliterable) { 193 List<T> elements = toList(spliterable.spliterator()); 194 assertEquals("Ordering should be consistent", elements, toList(spliterable.spliterator())); 195 196 // NOTE: This would fail for some Collections because of b/34757089: 197 // assertTrue(spliterable.spliterator().hasCharacteristics(ORDERED)); 198 199 if (spliterable instanceof Collection) { 200 assertEquals("ORDERED Spliterator must be consistent with Iterator: " 201 + spliterable.getClass(), elements, toList(spliterable.iterator())); 202 } 203 204 boolean isEmpty = !spliterable.iterator().hasNext(); 205 206 Spliterator<T> sa = spliterable.spliterator(); 207 Spliterator<T> sb = spliterable.spliterator(); 208 Spliterator<T> saSplit = sa.trySplit(); 209 Spliterator<T> sbSplit = sb.trySplit(); 210 // trySplit() may always return null, but is only required to when empty 211 if (isEmpty) { 212 assertNull(saSplit); 213 assertNull(sbSplit); 214 } else { 215 // A non-empty Iterable may still return null from trySplit(); 216 // if it does, then the un-split parent spliterators (sa, sb) must 217 // each still contain all of the elements. Regardless of whether 218 // the split was successful, sa and sb must behave the consistently 219 // with each other since they came from the same Iterable. 220 if (saSplit != null) { 221 assertEquals(toList(saSplit), toList(sbSplit)); 222 assertEquals(toList(sa), toList(sb)); 223 } else { 224 assertEquals(elements, toList(sa)); 225 assertEquals(elements, toList(sb)); 226 } 227 } 228 } 229 230 /** 231 * Checks that the specified SIZED Spliterator reports containing the 232 * specified number of elements. 233 */ runSizedTests(Spliterator<T> spliterator, int expectedSize)234 public static <T> void runSizedTests(Spliterator<T> spliterator, int expectedSize) { 235 assertHasCharacteristics(SIZED, spliterator); 236 assertEquals(expectedSize, spliterator.estimateSize()); 237 assertEquals(expectedSize, spliterator.getExactSizeIfKnown()); 238 } 239 runSizedTests(Iterable<T> spliterable, int expectedSize)240 public static <T> void runSizedTests(Iterable<T> spliterable, int expectedSize) { 241 runSizedTests(spliterable.spliterator(), expectedSize); 242 } 243 244 /** 245 * Checks that the specified Spliterator and its {@link Spliterator#trySplit() 246 * children} are SIZED and SUBSIZED and report containing the specified number 247 * of elements. 248 */ runSubSizedTests(Spliterator<T> spliterator, int expectedSize)249 public static <T> void runSubSizedTests(Spliterator<T> spliterator, int expectedSize) { 250 assertHasCharacteristics(SIZED | SUBSIZED, spliterator); 251 assertEquals(expectedSize, spliterator.estimateSize()); 252 assertEquals(expectedSize, spliterator.getExactSizeIfKnown()); 253 254 Spliterator<T> child = spliterator.trySplit(); 255 assertHasCharacteristics(SIZED | SUBSIZED, spliterator); 256 if (expectedSize == 0) { 257 assertNull(child); 258 assertEquals(expectedSize, spliterator.estimateSize()); 259 assertEquals(expectedSize, spliterator.getExactSizeIfKnown()); 260 } else { 261 assertHasCharacteristics(SIZED | SUBSIZED, child); 262 assertEquals(expectedSize, spliterator.estimateSize() + child.estimateSize()); 263 assertEquals(expectedSize, 264 spliterator.getExactSizeIfKnown() + child.getExactSizeIfKnown()); 265 } 266 } 267 runSubSizedTests(Iterable<T> spliterable, int expectedSize)268 public static <T> void runSubSizedTests(Iterable<T> spliterable, int expectedSize) { 269 runSubSizedTests(spliterable.spliterator(), expectedSize); 270 } 271 runDistinctTests(Iterable<T> spliterable)272 public static <T> void runDistinctTests(Iterable<T> spliterable) { 273 HashSet<T> distinct = new HashSet<>(); 274 ArrayList<T> allElements = new ArrayList<>(); 275 276 Spliterator<T> spliterator = spliterable.spliterator(); 277 Spliterator<T> split1 = spliterator.trySplit(); 278 279 // First test that iterating via the spliterator using forEachRemaining 280 // yields distinct elements. 281 spliterator.forEachRemaining(value -> { distinct.add(value); allElements.add(value); }); 282 // trySplit() may return null, even when non-empty 283 if (split1 != null) { 284 split1.forEachRemaining(value -> { distinct.add(value); allElements.add(value); }); 285 } 286 assertEquals(distinct.size(), allElements.size()); 287 288 distinct.clear(); 289 allElements.clear(); 290 spliterator = spliterable.spliterator(); 291 split1 = spliterator.trySplit(); 292 293 // Then test whether using tryAdvance yields the same results. 294 while (spliterator.tryAdvance(value -> { distinct.add(value); allElements.add(value); })) { 295 } 296 297 // trySplit() may return null, even when non-empty 298 if (split1 != null) { 299 while (split1.tryAdvance(value -> { distinct.add(value); allElements.add(value); })) { 300 } 301 } 302 303 assertEquals(distinct.size(), allElements.size()); 304 } 305 runSortedTests(Iterable<T> spliterable, Comparator<T> comparator)306 public static <T> void runSortedTests(Iterable<T> spliterable, Comparator<T> comparator) { 307 Spliterator<T> spliterator = spliterable.spliterator(); 308 Spliterator<T> split1 = spliterator.trySplit(); 309 310 ArrayList<T> elements = new ArrayList<>(); 311 spliterator.forEachRemaining(value -> elements.add(value)); 312 313 ArrayList<T> sortedElements = new ArrayList<>(elements); 314 Collections.sort(sortedElements, comparator); 315 assertEquals(elements, sortedElements); 316 317 elements.clear(); 318 319 split1.forEachRemaining(value -> elements.add(value)); 320 sortedElements = new ArrayList<>(elements); 321 Collections.sort(sortedElements, comparator); 322 assertEquals(elements, sortedElements); 323 } 324 runSortedTests(Iterable<T> spliterable)325 public static <T extends Comparable<T>> void runSortedTests(Iterable<T> spliterable) { 326 runSortedTests(spliterable, T::compareTo); 327 } 328 assertHasCharacteristics(int expectedCharacteristics, Spliterator<?> spliterator)329 public static void assertHasCharacteristics(int expectedCharacteristics, 330 Spliterator<?> spliterator) { 331 int actualCharacteristics = spliterator.characteristics(); 332 String msg = String.format(Locale.US, 333 "Expected expectedCharacteristics containing 0x%x, got 0x%x", 334 expectedCharacteristics, actualCharacteristics); 335 assertTrue(msg, spliterator.hasCharacteristics(expectedCharacteristics)); 336 } 337 } 338