diff --git a/assertk/src/commonMain/kotlin/assertk/assertions/iterable.kt b/assertk/src/commonMain/kotlin/assertk/assertions/iterable.kt index 9fd4f510..dda585c6 100644 --- a/assertk/src/commonMain/kotlin/assertk/assertions/iterable.kt +++ b/assertk/src/commonMain/kotlin/assertk/assertions/iterable.kt @@ -116,6 +116,27 @@ fun Assert>.containsExactlyInAnyOrder(vararg elements: Any?) = given }.toString()) } +inline fun Assert>.containsExactlyInAnyOrder(expected: Iterable) = given { actual -> + val actualMap = actual.groupingBy { it }.eachCount() + val expectedMap = expected.groupingBy { it }.eachCount() + + if (actualMap == expectedMap) { + return + } + + val notInActual = expectedMap.filter { (key, value) -> (actualMap[key] ?: 0) < value }.map { it.key } + val notInExpected = actualMap.filter { (key, value) -> (expectedMap[key] ?: 0) < value }.map { it.key } + + expected(StringBuilder("to contain exactly in any order:${show(expected)} but was:${show(actual)}").apply { + if (notInActual.isNotEmpty()) { + append("\n elements not found:${show(notInActual)}") + } + if (notInExpected.isNotEmpty()) { + append("\n extra elements found:${show(notInExpected)}") + } + }.toString()) +} + internal fun MutableList<*>.removeFirst(value: Any?) { val index = indexOf(value) if (index > -1) removeAt(index) diff --git a/assertk/src/commonTest/kotlin/test/assertk/assertions/IterableTest.kt b/assertk/src/commonTest/kotlin/test/assertk/assertions/IterableTest.kt index 390a3774..76d30796 100644 --- a/assertk/src/commonTest/kotlin/test/assertk/assertions/IterableTest.kt +++ b/assertk/src/commonTest/kotlin/test/assertk/assertions/IterableTest.kt @@ -206,6 +206,72 @@ class IterableTest { error.message ) } + + @Test fun containsExactlyInAnyOrder_only_elements_of_another_iterable_passes() { + assertThat(iterableOf(1, 2)).containsExactlyInAnyOrder(iterableOf(2, 1)) + } + + @Test fun containsExactlyInAnyOrder_only_elements_of_another_iterable_passes2() { + assertThat(iterableOf(1, 2, 1)).containsExactlyInAnyOrder(iterableOf(2, 1, 1)) + } + + @Test fun containsExactlyInAnyOrder_duplicate_elements_of_another_iterable_fails() { + val error = assertFailsWith { + assertThat(iterableOf(1, 2, 2)).containsExactlyInAnyOrder(iterableOf(2, 1)) + } + assertEquals( + """expected to contain exactly in any order:<[2, 1]> but was:<[1, 2, 2]> + | extra elements found:<[2]> + """.trimMargin(), error.message + ) + } + + @Test fun containsExactlyInAnyOrder_of_another_iterable_duplicate_elements_fails2() { + val error = assertFailsWith { + assertThat(iterableOf(1, 2)).containsExactlyInAnyOrder(iterableOf(2, 2, 1)) + } + assertEquals( + """expected to contain exactly in any order:<[2, 2, 1]> but was:<[1, 2]> + | elements not found:<[2]> + """.trimMargin(), error.message + ) + } + + @Test fun containsExactlyInAnyOrder__of_another_iterable_more_elements_fails() { + val error = assertFailsWith { + assertThat(iterableOf(1, 2, 3)).containsExactlyInAnyOrder(iterableOf(2, 1)) + } + assertEquals( + """expected to contain exactly in any order:<[2, 1]> but was:<[1, 2, 3]> + | extra elements found:<[3]> + """.trimMargin(), error.message + ) + } + + @Test fun containsExactlyInAnyOrder_of_another_iterable_less_elements_fails() { + val error = assertFailsWith { + assertThat(iterableOf(1, 2, 3)).containsExactlyInAnyOrder(iterableOf(2, 1, 3, 4)) + } + assertEquals( + """expected to contain exactly in any order:<[2, 1, 3, 4]> but was:<[1, 2, 3]> + | elements not found:<[4]> + """.trimMargin(), + error.message + ) + } + + @Test fun containsExactlyInAnyOrder_of_another_iterable_different_elements_fails() { + val error = assertFailsWith { + assertThat(iterableOf(1)).containsExactlyInAnyOrder(iterableOf(2)) + } + assertEquals( + """expected to contain exactly in any order:<[2]> but was:<[1]> + | elements not found:<[2]> + | extra elements found:<[1]> + """.trimMargin(), + error.message + ) + } //endregion //region each