From 56672c2564ed602927c961d1826295b1072955e4 Mon Sep 17 00:00:00 2001 From: Ilya Gorbunov Date: Wed, 16 Jan 2019 02:17:34 +0300 Subject: [PATCH] Ensure stable sorting in MutableList.sort/sortWith MutableList.sortWith now works correctly in JS_IR backend too #KT-12473 --- .../function/sortListOfStrings.kt | 1 - .../box/sam/constructors/comparator.kt | 1 - .../sam/constructors/nonLiteralComparator.kt | 1 - libraries/stdlib/js/src/kotlin/collections.kt | 3 +- .../stdlib/test/collections/ArraysTest.kt | 16 ++-------- .../stdlib/test/collections/CollectionTest.kt | 32 ++++++++++++++++++- .../stdlib/test/collections/IterableTests.kt | 14 ++++++++ 7 files changed, 49 insertions(+), 19 deletions(-) diff --git a/compiler/testData/codegen/box/callableReference/function/sortListOfStrings.kt b/compiler/testData/codegen/box/callableReference/function/sortListOfStrings.kt index 008ce18dd03..f37b6b89a98 100644 --- a/compiler/testData/codegen/box/callableReference/function/sortListOfStrings.kt +++ b/compiler/testData/codegen/box/callableReference/function/sortListOfStrings.kt @@ -1,5 +1,4 @@ // IGNORE_BACKEND: JVM_IR -// IGNORE_BACKEND: JS_IR // WITH_RUNTIME fun sort(list: MutableList, comparator: (String, String) -> Int) { diff --git a/compiler/testData/codegen/box/sam/constructors/comparator.kt b/compiler/testData/codegen/box/sam/constructors/comparator.kt index 5885a026a1c..56b24616f79 100644 --- a/compiler/testData/codegen/box/sam/constructors/comparator.kt +++ b/compiler/testData/codegen/box/sam/constructors/comparator.kt @@ -1,5 +1,4 @@ // IGNORE_BACKEND: JVM_IR -// IGNORE_BACKEND: JS_IR // WITH_RUNTIME fun box(): String { diff --git a/compiler/testData/codegen/box/sam/constructors/nonLiteralComparator.kt b/compiler/testData/codegen/box/sam/constructors/nonLiteralComparator.kt index c415e666343..0bef462b2a9 100644 --- a/compiler/testData/codegen/box/sam/constructors/nonLiteralComparator.kt +++ b/compiler/testData/codegen/box/sam/constructors/nonLiteralComparator.kt @@ -1,5 +1,4 @@ // IGNORE_BACKEND: JVM_IR -// IGNORE_BACKEND: JS_IR // WITH_RUNTIME fun box(): String { diff --git a/libraries/stdlib/js/src/kotlin/collections.kt b/libraries/stdlib/js/src/kotlin/collections.kt index a54948cec2f..6abd57149f3 100644 --- a/libraries/stdlib/js/src/kotlin/collections.kt +++ b/libraries/stdlib/js/src/kotlin/collections.kt @@ -109,8 +109,7 @@ private fun collectionsSort(list: MutableList, comparator: Comparator Sortable(keyRange.random(), index) } - array.sortedBy { it.key }.iterator().assertStableSorted() - array.sortedByDescending { it.key }.iterator().assertStableSorted(descending = true) + array.sortedBy { it.key }.assertStableSorted() + array.sortedByDescending { it.key }.assertStableSorted(descending = true) array.sortBy { it.key } array.assertStableSorted() @@ -1488,20 +1488,10 @@ class ArraysTest { } } -private data class Sortable>(val key: K, val index: Int) : Comparable> { - override fun compareTo(other: Sortable): Int = this.key.compareTo(other.key) -} -private fun > Array>.assertStableSorted(descending: Boolean = false) = +fun > Array>.assertStableSorted(descending: Boolean = false) = iterator().assertStableSorted(descending = descending) -private fun > Iterator>.assertStableSorted(descending: Boolean = false) { - assertSorted { a, b -> - val relation = a.key.compareTo(b.key) - (if (descending) relation > 0 else relation < 0) || relation == 0 && a.index < b.index - } -} - private class ArraySortedChecker(val array: A, val comparator: Comparator) { public fun checkSorted(sorted: A.() -> R, sortedDescending: A.() -> R, iterator: R.() -> Iterator) { array.sorted().iterator().assertSorted { a, b -> comparator.compare(a, b) <= 0 } diff --git a/libraries/stdlib/test/collections/CollectionTest.kt b/libraries/stdlib/test/collections/CollectionTest.kt index 22fcbbf7be0..d5711fbc80f 100644 --- a/libraries/stdlib/test/collections/CollectionTest.kt +++ b/libraries/stdlib/test/collections/CollectionTest.kt @@ -9,7 +9,6 @@ import test.assertStaticAndRuntimeTypeIs import kotlin.test.* import test.collections.behaviors.* import test.comparisons.STRING_CASE_INSENSITIVE_ORDER -import kotlin.math.sin import kotlin.random.Random class CollectionTest { @@ -829,6 +828,21 @@ class CollectionTest { assertEquals(listOf("aa" to 20, "aa" to 3, "ab" to 3), data) } + @Test fun sortStable() { + val keyRange = 'A'..'D' + for (size in listOf(10, 100, 2000)) { + val list = MutableList(size) { index -> Sortable(keyRange.random(), index) } + + list.sorted().assertStableSorted() + list.sortedDescending().assertStableSorted(descending = true) + + list.sort() + list.assertStableSorted() + list.sortDescending() + list.assertStableSorted(descending = true) + } + } + @Test fun sortedBy() { assertEquals(listOf("two" to 3, "three" to 20), listOf("three" to 20, "two" to 3).sortedBy { it.second }) assertEquals(listOf("three" to 20, "two" to 3), listOf("three" to 20, "two" to 3).sortedBy { it.first }) @@ -862,6 +876,22 @@ class CollectionTest { expect(listOf("BAD", "dad", "cat")) { data.sortedWith(comparator.reversed().reversed()) } } + @Test fun sortByStable() { + val keyRange = 'A'..'D' + for (size in listOf(10, 100, 2000)) { + val list = MutableList(size) { index -> Sortable(keyRange.random(), index) } + + list.sortedBy { it.key }.assertStableSorted() + list.sortedByDescending { it.key }.assertStableSorted(descending = true) + + list.sortBy { it.key } + list.assertStableSorted() + + list.sortByDescending { it.key } + list.assertStableSorted(descending = true) + } + } + @Test fun decomposeFirst() { val (first) = listOf(1, 2) assertEquals(first, 1) diff --git a/libraries/stdlib/test/collections/IterableTests.kt b/libraries/stdlib/test/collections/IterableTests.kt index 274904b60b9..d090dff6c17 100644 --- a/libraries/stdlib/test/collections/IterableTests.kt +++ b/libraries/stdlib/test/collections/IterableTests.kt @@ -508,3 +508,17 @@ fun Iterator.assertSorted(isInOrder: (T, T) -> Boolean) { return } +data class Sortable>(val key: K, val index: Int) : Comparable> { + override fun compareTo(other: Sortable): Int = this.key.compareTo(other.key) +} + + +fun > Iterator>.assertStableSorted(descending: Boolean = false) { + assertSorted { a, b -> + val relation = a.key.compareTo(b.key) + (if (descending) relation > 0 else relation < 0) || relation == 0 && a.index < b.index + } +} + +fun > Iterable>.assertStableSorted(descending: Boolean = false) = + iterator().assertStableSorted(descending = descending)