diff --git a/platform/platform-tests/testSrc/com/intellij/util/containers/ContainerUtilTest.java b/platform/platform-tests/testSrc/com/intellij/util/containers/ContainerUtilTest.java index feb2ae1e19e8..02cf2631ab73 100644 --- a/platform/platform-tests/testSrc/com/intellij/util/containers/ContainerUtilTest.java +++ b/platform/platform-tests/testSrc/com/intellij/util/containers/ContainerUtilTest.java @@ -3,16 +3,12 @@ package com.intellij.util.containers; import com.intellij.openapi.diagnostic.Logger; import com.intellij.openapi.util.*; -import com.intellij.testFramework.UsefulTestCase; import com.intellij.tools.ide.metrics.benchmark.Benchmark; import com.intellij.util.ArrayUtil; -import com.intellij.util.ArrayUtilRt; import com.intellij.util.ReflectionUtil; import com.intellij.util.concurrency.AppExecutorUtil; -import junit.framework.TestCase; -import one.util.streamex.IntStreamEx; -import org.jetbrains.annotations.NotNull; -import org.junit.Assert; +import org.assertj.core.api.Assertions; +import org.junit.Test; import org.junit.function.ThrowingRunnable; import java.util.*; @@ -24,14 +20,20 @@ import java.util.concurrent.Future; import java.util.concurrent.atomic.AtomicBoolean; import java.util.stream.IntStream; -public class ContainerUtilTest extends TestCase { +import static org.junit.Assert.*; + +@SuppressWarnings("SSBasedInspection") +public class ContainerUtilTest { private static final Logger LOG = Logger.getInstance(ContainerUtilTest.class); + + @Test public void testFindInstanceWorks() { Iterator iterator = Arrays.asList(1, new ArrayList<>(), "1").iterator(); String string = ContainerUtil.findInstance(iterator, String.class); assertEquals("1", string); } + @Test public void testConcatTwoListsMustSupportListContracts() { Iterable concat = ContainerUtil.concat(Collections.emptySet(), Collections.emptySet()); assertFalse(concat.iterator().hasNext()); @@ -54,6 +56,7 @@ public class ContainerUtilTest extends TestCase { assertFalse(iterator.hasNext()); } + @Test public void testConcatMultipleListsWorks() { List l = ContainerUtil.concat(Arrays.asList(1, 2), Collections.emptyList(), Arrays.asList(3, 4)); assertEquals(4, l.size()); @@ -79,7 +82,8 @@ public class ContainerUtilTest extends TestCase { } } - public void testConcatedListsAfterModificationMustThrowCME() { + @Test + public void testConcatenatedListsAfterModificationMustThrowCME() { List a1 = new ArrayList<>(Arrays.asList(0, 1)); List l = ContainerUtil.concat(a1, Arrays.asList(2, 3), ContainerUtil.emptyList()); assertEquals(4, l.size()); @@ -116,10 +120,11 @@ public class ContainerUtilTest extends TestCase { }); } - private static List createSequentialList(int size) { + private static List createSequentialList(@SuppressWarnings("SameParameterValue") int size) { return ContainerUtil.createLockFreeCopyOnWriteList(IntStream.range(0, size).boxed().toList()); } + @Test public void testConcatenatedDynamicListsAreIterableEvenWhenTheyAreChangingDuringIteration() throws Exception { List list1 = createSequentialList(32); List list2 = createSequentialList(32); @@ -138,6 +143,7 @@ public class ContainerUtilTest extends TestCase { // must work on streams (even parallel), too. count += concat.parallelStream().count(); } + System.out.println("count: " + count); } finally { stop.set(true); // finally stop even in case of an error @@ -146,6 +152,7 @@ public class ContainerUtilTest extends TestCase { } } + @Test public void testIterateWithCondition() { Condition cond = integer -> integer > 2; @@ -160,6 +167,7 @@ public class ContainerUtilTest extends TestCase { assertEquals(Arrays.asList(expected), actual); } + @Test public void testIteratingBackward() { List ss = new ArrayList<>(); ss.add("a"); @@ -175,13 +183,15 @@ public class ContainerUtilTest extends TestCase { log.append(s); } + //noinspection SpellCheckingInspection assertEquals("abccba", log.toString()); } + @Test public void testLockFreeSingleThreadPerformance() { - final List stock = new CopyOnWriteArrayList<>(); + List stock = new CopyOnWriteArrayList<>(); measure(stock); - final List my = ContainerUtil.createLockFreeCopyOnWriteList(); + List my = ContainerUtil.createLockFreeCopyOnWriteList(); measure(my); measure(stock); measure(my); // warm up @@ -208,12 +218,13 @@ public class ContainerUtilTest extends TestCase { return finish - start; } + @Test public void testLockFreeCOWDoesNotCreateEmptyArrays() { List my = ContainerUtil.createLockFreeCopyOnWriteList(); for (int i = 0; i < 2; i++) { Object[] array = ReflectionUtil.getField(my.getClass(), my, Object[].class, "array"); - assertSame(ArrayUtilRt.EMPTY_OBJECT_ARRAY, array); + assertSame(ArrayUtil.EMPTY_OBJECT_ARRAY, array); assertReallyEmpty(my); my.add(this); my.remove(this); @@ -228,10 +239,11 @@ public class ContainerUtilTest extends TestCase { } } + @Test public void testCOWListPerformanceAdd() { List list = ContainerUtil.createLockFreeCopyOnWriteList(); int count = 15000; - List ints = IntStreamEx.range(0, count).boxed().toList(); + List ints = IntStream.range(0, count).boxed().toList(); Benchmark.newBenchmark("COWList add", () -> { for (int it = 0; it < 10; it++) { list.clear(); @@ -249,12 +261,13 @@ public class ContainerUtilTest extends TestCase { assertEquals(0, my.size()); Object[] objects = my.toArray(); - assertSame(ArrayUtilRt.EMPTY_OBJECT_ARRAY, objects); + assertSame(ArrayUtil.EMPTY_OBJECT_ARRAY, objects); Iterator iterator = my.iterator(); assertSame(Collections.emptyIterator(), iterator); } + @Test public void testIdenticalItemsInLockFreeCOW() { List list = ContainerUtil.createLockFreeCopyOnWriteList(Arrays.asList("a", "b")); list.add("a"); @@ -265,6 +278,7 @@ public class ContainerUtilTest extends TestCase { assertEquals(1, list.size()); } + @Test public void testLockFreeCOWIteratorRemove() { List seq = Arrays.asList("0", "1", "2", "3", "4"); List my = ContainerUtil.createLockFreeCopyOnWriteList(seq); @@ -302,6 +316,7 @@ public class ContainerUtilTest extends TestCase { } } + @Test public void testLockFreeCOWReplaceAll_Stress() { int N = 500 * ForkJoinPool.getCommonPoolParallelism(); List list = ContainerUtil.createLockFreeCopyOnWriteList(IntStream.range(0, N).mapToObj(__->0).toList()); @@ -309,6 +324,7 @@ public class ContainerUtilTest extends TestCase { assertEquals(N*N, list.stream().mapToInt(i -> i).sum()); } + @Test public void testLockFreeListStreamMustNotCMEOnParallelModifications() throws Exception { List list = ContainerUtil.createLockFreeCopyOnWriteList(); Future future = AppExecutorUtil.getAppExecutorService().submit( @@ -325,6 +341,7 @@ public class ContainerUtilTest extends TestCase { assertReallyEmpty(list); } + @Test public void testImmutableListEquals() { String value = "stringValue"; List expected = Collections.singletonList(value); @@ -332,6 +349,7 @@ public class ContainerUtilTest extends TestCase { assertEquals(expected, actual); } + @Test public void testMergeSortedLists() { List target = new ArrayList<>(Arrays.asList( range(0, 0), @@ -388,6 +406,7 @@ public class ContainerUtilTest extends TestCase { return ContainerUtil.mergeSortedLists(list1, list2, Segment.BY_START_OFFSET_THEN_END_OFFSET, true); } + @Test public void testMergeSortedArrays() { List list1 = Collections.singletonList(0); List list2 = Collections.singletonList(4); @@ -396,36 +415,33 @@ public class ContainerUtilTest extends TestCase { m = ContainerUtil.mergeSortedLists(list2, list1, Comparator.naturalOrder(), true); assertEquals(Arrays.asList(0, 4), m); } + @Test public void testWhenMergeSortedArraysButTheyAreActuallyUnsortedTheExceptionMustBeThrown() { List list1 = Arrays.asList(5, 4); List list2 = Arrays.asList(4, 5); - Assert.assertThrows(IllegalArgumentException.class, ()->ContainerUtil.mergeSortedLists(list1, list2, Comparator.naturalOrder(), true)); - Assert.assertThrows(IllegalArgumentException.class, ()->ContainerUtil.mergeSortedLists(list2, list1, Comparator.naturalOrder(), true)); - Assert.assertThrows(IllegalArgumentException.class, ()->ContainerUtil.mergeSortedLists(list1, List.of(), Comparator.naturalOrder(), true)); - Assert.assertThrows(IllegalArgumentException.class, ()->ContainerUtil.mergeSortedLists(List.of(), list1, Comparator.naturalOrder(), true)); + assertThrows(IllegalArgumentException.class, ()->ContainerUtil.mergeSortedLists(list1, list2, Comparator.naturalOrder(), true)); + assertThrows(IllegalArgumentException.class, ()->ContainerUtil.mergeSortedLists(list2, list1, Comparator.naturalOrder(), true)); + assertThrows(IllegalArgumentException.class, ()->ContainerUtil.mergeSortedLists(list1, List.of(), Comparator.naturalOrder(), true)); + assertThrows(IllegalArgumentException.class, ()->ContainerUtil.mergeSortedLists(List.of(), list1, Comparator.naturalOrder(), true)); } + @Test public void testMergeSortedArrays2() { int[] a1 = {0, 4}; int[] a2 = {4}; int[] m = ArrayUtil.mergeSortedArrays(a1, a2, true); - Assert.assertArrayEquals(new int[]{0, 4}, m); + assertArrayEquals(new int[]{0, 4}, m); m = ArrayUtil.mergeSortedArrays(a2, a1, true); - Assert.assertArrayEquals(new int[]{0, 4}, m); + assertArrayEquals(new int[]{0, 4}, m); } - public void testImmutableListSubList() { - List list = ContainerUtil.immutableList(0, 1, 2, 3, 4); - List subList = list.subList(1, 4); - UsefulTestCase.assertOrderedEquals(subList, 1, 2, 3); - List subSubList = subList.subList(1, 2); - UsefulTestCase.assertOrderedEquals(subSubList, 2); - assertEquals(new ArrayList<>(subSubList), subSubList); - } + @Test public void testFlatMap() { List list = ContainerUtil.flatMap(List.of(0, 1), i->List.of(i,i)); assertEquals(List.of(0,0,1,1), list); } + + @Test public void testCOWRemoveIf() { { List list = ContainerUtil.createLockFreeCopyOnWriteList(Arrays.asList("a", "b")); @@ -436,12 +452,12 @@ public class ContainerUtilTest extends TestCase { { List list = ContainerUtil.createLockFreeCopyOnWriteList(Arrays.asList("a", "bb")); assertTrue(list.removeIf(e -> e.length() == 1)); - assertEquals("bb", UsefulTestCase.assertOneElement(list)); + Assertions.assertThat(list).containsExactly("bb"); } { List list = ContainerUtil.createLockFreeCopyOnWriteList(Arrays.asList("aa", "b")); assertTrue(list.removeIf(e -> e.length() == 1)); - assertEquals("aa", UsefulTestCase.assertOneElement(list)); + Assertions.assertThat(list).containsExactly("aa"); } { List list = ContainerUtil.createLockFreeCopyOnWriteList(Arrays.asList("aa", "bb")); @@ -450,6 +466,7 @@ public class ContainerUtilTest extends TestCase { } } + @Test public void testAggregateFunctionsReturnReallyUnmodifiableCollections() { ContainerUtil.Options.RETURN_REALLY_UNMODIFIABLE_COLLECTION_FROM_METHODS_MARKED_UNMODIFIABLE = true; // in case the test was started without ApplicationImpl init assertUnmodifiable(ContainerUtil.append(new ArrayList<>(Arrays.asList(1, 2)), 3)); @@ -475,14 +492,9 @@ public class ContainerUtilTest extends TestCase { assertUnmodifiable(ContainerUtil.filterIsInstance(new String[]{"a","b"}, String.class)); assertUnmodifiable(ContainerUtil.filterIsInstance(new ArrayList<>(Arrays.asList("a", "b")), String.class)); assertUnmodifiable(ContainerUtil.flatMap(List.of(1, 2, 3 ), t->List.of(t, t))); + //noinspection unchecked assertUnmodifiable(ContainerUtil.flatten(new Collection[]{new ArrayList<>(Arrays.asList("a", "b")), new ArrayList<>(Arrays.asList("a", "b"))})); assertUnmodifiable(ContainerUtil.flatten(List.of(new ArrayList<>(Arrays.asList("a", "b")), new ArrayList<>(Arrays.asList("a", "b"))))); - assertUnmodifiable(ContainerUtil.immutableSet("a", "b")); - assertUnmodifiable(ContainerUtil.immutableList("a", "b")); - assertUnmodifiable(ContainerUtil.immutableList()); - assertUnmodifiable(ContainerUtil.immutableList(1)); - assertUnmodifiable(ContainerUtil.immutableList(new ArrayList<>(Arrays.asList("a", "b")))); - assertUnmodifiable(ContainerUtil.immutableMapBuilder().put(1,1).build()); assertUnmodifiable(ContainerUtil.intersection(new ArrayList<>(Arrays.asList("a", "b")), new ArrayList<>(Arrays.asList("a", "b")))); assertUnmodifiable(ContainerUtil.intersection(new HashMap<>(Map.of("a", "b")), new HashMap<>(Map.of("a", "b")))); assertUnmodifiable(ContainerUtil.mergeSortedLists(new ArrayList<>(Arrays.asList("a", "b")), new ArrayList<>(Arrays.asList("a", "b")), String::compareTo, true)); @@ -491,7 +503,6 @@ public class ContainerUtilTest extends TestCase { assertUnmodifiable(ContainerUtil.map(new ArrayList<>(Arrays.asList("a", "b")).iterator(), s->!s.isEmpty())); assertUnmodifiable(ContainerUtil.map(new String[]{"a","b"}, s->!s.isEmpty())); assertUnmodifiable(ContainerUtil.map2LinkedSet(new ArrayList<>(Arrays.asList("a", "b")), t->t)); - assertUnmodifiable(ContainerUtil.map2List(new ArrayList<>(Arrays.asList("a", "b")), t->t)); assertUnmodifiable(ContainerUtil.map2Map(List.of("a", "b"), s-> Pair.create(s, s))); assertUnmodifiable(ContainerUtil.map2Map(List.of(Pair.create("a", "b"), Pair.create("c", "e")))); assertUnmodifiable(ContainerUtil.map2Map(new String[]{"a", "b"}, s-> Pair.create(s, s))); @@ -505,7 +516,6 @@ public class ContainerUtilTest extends TestCase { assertUnmodifiable(ContainerUtil.mapNotNull(new String[]{"a","b"}, s->!s.isEmpty())); assertUnmodifiable(ContainerUtil.newMapFromKeys(Arrays.asList("a", "b").iterator(), k->k)); assertUnmodifiable(ContainerUtil.newMapFromValues(Arrays.asList("a", "b").iterator(), k->k)); - assertUnmodifiable(ContainerUtil.newUnmodifiableList(new ArrayList<>(Arrays.asList("a", "b")))); assertUnmodifiable(ContainerUtil.notNullize(new HashSet<>(Arrays.asList("a", "b")))); assertUnmodifiable(ContainerUtil.notNullize(new ArrayList<>(Arrays.asList("a", "b")))); assertUnmodifiable(ContainerUtil.notNullize(new HashMap<>(Map.of("a", "b")))); @@ -527,7 +537,7 @@ public class ContainerUtilTest extends TestCase { assertUnmodifiable(ContainerUtil.unmodifiableOrEmptyMap(new HashMap<>(Map.of("a", "b")))); } - private static void assertUnmodifiable(@NotNull Map map) { + private static void assertUnmodifiable(Map map) { assertFalse(map.isEmpty()); assertThrowsUOE(map, () -> map.clear()); assertThrowsUOE(map, ()->map.put(null, null)); @@ -562,18 +572,19 @@ public class ContainerUtilTest extends TestCase { assertThrowsUOE(map, ()-> map.entrySet().clear()); } - private static void assertThrowsUOE(@NotNull Collection collection, @NotNull ThrowingRunnable runnable) { + private static void assertThrowsUOE(Collection collection, ThrowingRunnable runnable) { int sizeBefore = collection.size(); - Assert.assertThrows(UnsupportedOperationException.class, runnable); - assertEquals(sizeBefore, collection.size()); - } - private static void assertThrowsUOE(@NotNull Map collection, @NotNull ThrowingRunnable runnable) { - int sizeBefore = collection.size(); - Assert.assertThrows(UnsupportedOperationException.class, runnable); + assertThrows(UnsupportedOperationException.class, runnable); assertEquals(sizeBefore, collection.size()); } - private static void assertUnmodifiable(@NotNull Collection collection) { + private static void assertThrowsUOE(Map collection, ThrowingRunnable runnable) { + int sizeBefore = collection.size(); + assertThrows(UnsupportedOperationException.class, runnable); + assertEquals(sizeBefore, collection.size()); + } + + private static void assertUnmodifiable(Collection collection) { //noinspection SizeReplaceableByIsEmpty assertEquals(collection.size() ==0, collection.isEmpty()); assertThrowsUOE(collection, ()->collection.add(null)); @@ -612,4 +623,4 @@ public class ContainerUtilTest extends TestCase { assertThrowsUOE(collection, ()->list.set(0, null)); } } -} \ No newline at end of file +} diff --git a/platform/util/src/com/intellij/util/containers/ContainerUtil.java b/platform/util/src/com/intellij/util/containers/ContainerUtil.java index 046e23a5607f..a1f305675970 100644 --- a/platform/util/src/com/intellij/util/containers/ContainerUtil.java +++ b/platform/util/src/com/intellij/util/containers/ContainerUtil.java @@ -25,6 +25,7 @@ public final class ContainerUtil { @ApiStatus.Internal public static final class Options { @ApiStatus.Internal + @SuppressWarnings("StaticNonFinalField") public static boolean RETURN_REALLY_UNMODIFIABLE_COLLECTION_FROM_METHODS_MARKED_UNMODIFIABLE; } private static final int INSERTION_SORT_THRESHOLD = 10; @@ -36,7 +37,7 @@ public final class ContainerUtil { } /** - * @deprecated Use {@link java.util.HashMap#HashMap()} + * @deprecated Use {@link HashMap#HashMap()} */ @Contract(pure = true) @Deprecated @@ -547,7 +548,6 @@ public final class ContainerUtil { } } - @Deprecated private static final @Unmodifiable class ImmutableListBackedByList extends ImmutableList { private final List myStore; @@ -571,7 +571,6 @@ public final class ContainerUtil { } } - @Deprecated private static final @Unmodifiable class ImmutableListBackedByArray extends ImmutableList { private final E[] myStore; @@ -838,7 +837,6 @@ public final class ContainerUtil { * @return true if all {@link Processor#process(Object)} returned true; false otherwise */ public static boolean process(@NotNull List list, @NotNull Processor processor) { - //noinspection ForLoopReplaceableByForEach for (int i = 0, size = list.size(); i < size; i++) { T t = list.get(i); if (!processor.process(t)) { @@ -1129,12 +1127,7 @@ public final class ContainerUtil { } public static boolean all(@NotNull Collection collection, @NotNull Condition condition) { - for (T t : collection) { - if (!condition.value(t)) { - return false; - } - } - return true; + return and(collection, condition); } @Contract(mutates = "param1") @@ -1479,7 +1472,7 @@ public final class ContainerUtil { public static @NotNull Iterable concat(@NotNull Iterable it1, @NotNull Iterable it2) { return new Iterable() { @Override - public void forEach(java.util.function.Consumer action) { + public void forEach(Consumer action) { it1.forEach(action); it2.forEach(action); } @@ -2451,7 +2444,6 @@ public final class ContainerUtil { List<@NotNull T> result = null; for (int i = 0; i < list.size(); i++) { T t = list.get(i); - //noinspection ConstantValue if (t == null) { throw new IllegalArgumentException("get(" + i + ") = null"); } @@ -2675,7 +2667,7 @@ public final class ContainerUtil { * - less memory * - slower modification in highly contented case (which is the kind of situation you shouldn't use COWAL anyway)
* - * N.B. Avoid using {@code list.toArray(new T[list.size()])} on this list because it is inherently racey and + * N.B. Avoid using {@code list.toArray(new T[list.size()])} on this list because it is inherently race-prone and * therefore can return an array with null elements at the end. */ @Contract(value = " -> new", pure = true) @@ -3053,7 +3045,7 @@ public final class ContainerUtil { } /** - * @deprecated use {@link java.util.WeakHashMap} instead + * @deprecated use {@link WeakHashMap} instead */ @Contract(value = " -> new", pure = true) @Deprecated