| package test.collections |
| |
| import test.collections.behaviors.listBehavior |
| import test.collections.behaviors.mapBehavior |
| import test.collections.behaviors.setBehavior |
| import kotlin.test.* |
| |
| class ContainerBuilderTest { |
| private fun <E> mutableCollectionOperations(present: E, absent: E) = listOf<Pair<String, MutableCollection<E>.() -> Unit>>( |
| "add(present)" to { add(present) }, |
| "add(absent)" to { add(absent) }, |
| |
| "addAll(listOf(present))" to { addAll(listOf(present)) }, |
| "addAll(listOf(absent))" to { addAll(listOf(absent)) }, |
| "addAll(emptyList())" to { addAll(emptyList()) }, |
| |
| "remove(present)" to { remove(present) }, |
| "remove(absent)" to { remove(absent) }, |
| |
| "removeAll(listOf(present))" to { removeAll(listOf(present)) }, |
| "removeAll(emptyList())" to { removeAll(emptyList()) }, |
| "removeAll(this.toList())" to { removeAll(this.toList()) }, |
| |
| "retainAll(listOf(present))" to { retainAll(listOf(present)) }, |
| "retainAll(emptyList())" to { retainAll(emptyList()) }, |
| "retainAll(this.toList())" to { retainAll(this.toList()) }, |
| |
| "clear()" to { clear() }, |
| |
| "iterator().apply { next() }.remove()" to { iterator().apply { next() }.remove() } |
| ) |
| |
| private fun <E> mutableListOperations(present: E, absent: E) = mutableCollectionOperations(present, absent) + listOf<Pair<String, MutableList<E>.() -> Unit>>( |
| "add(0, present)" to { add(0, present) }, |
| "addAll(0, listOf(present))" to { addAll(0, listOf(present)) }, |
| "addAll(0, emptyList())" to { addAll(0, emptyList()) }, |
| "removeAt(0)" to { removeAt(0) }, |
| "set(0, present)" to { set(0, present) }, |
| "listIterator().apply { next() }.remove()" to { listIterator().apply { next() }.remove() }, |
| "listIterator(0).apply { next() }.remove()" to { listIterator(0).apply { next() }.remove() }, |
| "listIterator().apply { next() }.set(present)" to { listIterator().apply { next() }.set(present) }, |
| "listIterator().add(present)" to { listIterator().add(present) } |
| ) |
| |
| private fun <E> mutableSetOperations(present: E, absent: E) = mutableCollectionOperations(present, absent) + listOf<Pair<String, MutableSet<E>.() -> Unit>>( |
| // check java.util.AbstractSet.removeAll optimisation |
| "removeAll(List(this.size) { absent })" to { removeAll(List(this.size) { absent }) } |
| ) |
| |
| private fun <K, V> mutableMapOperations(k: K, v: V) = listOf<Pair<String, MutableMap<K, V>.() -> Unit>>( |
| "put(k, v)" to { put(k, v) }, |
| "remove(k)" to { remove(k) }, |
| "putAll(mapOf(k to v))" to { putAll(mapOf(k to v)) }, |
| "putAll(emptyMap())" to { putAll(emptyMap()) }, |
| "clear()" to { clear() }, |
| "entries.first().setValue(v)" to { entries.first().setValue(v) }, |
| "entries.iterator().next().setValue(v)" to { entries.iterator().next().setValue(v) } |
| ) |
| |
| @Test |
| fun buildList() { |
| val x = buildList { |
| add('b') |
| add('c') |
| } |
| |
| val subList: MutableList<Char> |
| |
| val y = buildList<Char>(4) { |
| add('a') |
| addAll(x) |
| add('d') |
| |
| subList = subList(0, 4) |
| } |
| |
| compare(listOf('a', 'b', 'c', 'd'), y) { listBehavior() } |
| compare(listOf('a', 'b', 'c', 'd'), y.subList(0, 4)) { listBehavior() } |
| compare(listOf('b', 'c'), y.subList(1, 4).subList(0, 2)) { listBehavior() } |
| |
| assertEquals(listOf(1), buildList(0) { add(1) }) |
| assertFailsWith<IllegalArgumentException> { |
| buildList(-1) { add(0) } |
| } |
| |
| assertTrue(y is MutableList<Char>) |
| for ((fName, operation) in mutableListOperations('b', 'x')) { |
| assertFailsWith<UnsupportedOperationException>("y.$fName") { y.operation() } |
| assertFailsWith<UnsupportedOperationException>("y.subList(1, 3).$fName") { y.subList(1, 3).operation() } |
| assertFailsWith<UnsupportedOperationException>("subList.$fName") { subList.operation() } |
| } |
| } |
| |
| @Test |
| fun listBuilderSubList() { |
| buildList<Char> { |
| addAll(listOf('a', 'b', 'c', 'd', 'e')) |
| |
| val subList = subList(1, 4) |
| compare(listOf('a', 'b', 'c', 'd', 'e'), this) { listBehavior() } |
| compare(listOf('b', 'c', 'd'), subList) { listBehavior() } |
| |
| set(2, '1') |
| compare(listOf('a', 'b', '1', 'd', 'e'), this) { listBehavior() } |
| compare(listOf('b', '1', 'd'), subList) { listBehavior() } |
| |
| subList[2] = '2' |
| compare(listOf('a', 'b', '1', '2', 'e'), this) { listBehavior() } |
| compare(listOf('b', '1', '2'), subList) { listBehavior() } |
| |
| subList.add('3') |
| compare(listOf('a', 'b', '1', '2', '3', 'e'), this) { listBehavior() } |
| compare(listOf('b', '1', '2', '3'), subList) { listBehavior() } |
| |
| val subSubList = subList.subList(2, 4) |
| // buffer reallocation should happen |
| repeat(20) { subSubList.add('x') } |
| repeat(20) { subSubList.add(subSubList.size - 2 * it, 'y') } |
| |
| val addedChars = "xy".repeat(20) |
| compare("ab123${addedChars}e".toList(), this) { listBehavior() } |
| compare("b123$addedChars".toList(), subList) { listBehavior() } |
| compare("23$addedChars".toList(), subSubList) { listBehavior() } |
| } |
| } |
| |
| @Test |
| fun buildSet() { |
| val x = buildSet { |
| add('b') |
| add('c') |
| } |
| |
| val y = buildSet(4) { |
| add('c') |
| addAll(x) |
| add('d') |
| } |
| |
| compare(setOf('c', 'b', 'd'), y) { setBehavior() } |
| |
| assertEquals(setOf(1), buildSet(0) { add(1) }) |
| assertFailsWith<IllegalArgumentException> { |
| buildSet(-1) { add(0) } |
| } |
| |
| assertTrue(y is MutableSet<Char>) |
| for ((fName, operation) in mutableSetOperations('b', 'x')) { |
| assertFailsWith<UnsupportedOperationException>("y.$fName") { y.operation() } |
| } |
| } |
| |
| @Test |
| fun buildMap() { |
| val x = buildMap<Char, Int> { |
| put('b', 2) |
| put('c', 3) |
| } |
| |
| val y = buildMap<Char, Int>(4) { |
| put('a', 1) |
| put('c', 0) |
| putAll(x) |
| put('d', 4) |
| } |
| |
| compare(mapOf('a' to 1, 'c' to 3, 'b' to 2, 'd' to 4), y) { mapBehavior() } |
| |
| assertEquals(mapOf("a" to 1), buildMap<String, Int>(0) { put("a", 1) }) |
| assertFailsWith<IllegalArgumentException> { |
| buildMap<String, Int>(-1) { put("x", 1) } |
| } |
| |
| assertTrue(y is MutableMap<Char, Int>) |
| for ((fName, operation) in mutableMapOperations('a', 1) + mutableMapOperations('x', 10)) { |
| assertFailsWith<UnsupportedOperationException>("y.$fName") { y.operation() } |
| } |
| for ((fName, operation) in mutableSetOperations('a', 'x')) { |
| assertFailsWith<UnsupportedOperationException>("y.keys.$fName") { y.keys.operation() } |
| } |
| for ((fName, operation) in mutableCollectionOperations(1, 10)) { |
| assertFailsWith<UnsupportedOperationException>("y.values.$fName") { y.values.operation() } |
| } |
| val presentEntry = y.entries.first() |
| val absentEntry: MutableMap.MutableEntry<Char, Int> = object : MutableMap.MutableEntry<Char, Int> { |
| override val key: Char get() = 'x' |
| override val value: Int get() = 10 |
| override fun setValue(newValue: Int): Int = fail("Unreachable") |
| } |
| for ((fName, operation) in mutableSetOperations(presentEntry, absentEntry)) { |
| assertFailsWith<UnsupportedOperationException>("y.entries.$fName") { y.entries.operation() } |
| } |
| } |
| } |