/* * Copyright 2010-2020 JetBrains s.r.o. and Kotlin Programming Language contributors. * Use of this source code is governed by the Apache 2.0 license that can be found in the license/LICENSE.txt file. */ package test.collections import kotlin.test.* class IndexOverflowJVMTest { companion object { fun repeatCounted(value: T, count: Long = Int.MAX_VALUE + 1L): Sequence = Sequence { object : Iterator { var counter = count override fun hasNext(): Boolean = counter > 0 override fun next(): T = value.also { counter-- } } } val maxIndexSequence = repeatCounted("k", (Int.MAX_VALUE + 1L) + 1L) // here the last index is one greater than Int.MAX_VALUE val maxIndexIterable = maxIndexSequence.asIterable() val longCountSequence = Sequence { object : Iterator { var counter = 0L override fun hasNext(): Boolean = true override fun next(): Long = counter++ } } fun assertIndexOverflow(f: () -> Unit) { val ex = assertFailsWith(block = f) assertTrue(ex.message!!.contains("index", ignoreCase = true)) } fun assertCountOverflow(f: () -> Unit) { val ex = assertFailsWith(block = f) assertTrue(ex.message!!.contains("count", ignoreCase = true)) } fun checkIndexPositive(index: Int) { if (index < 0) fail("Encountered negative index") } } @Test fun indexOfOverflowSequence() { assertIndexOverflow { maxIndexSequence.indexOf("j") } assertIndexOverflow { maxIndexSequence.lastIndexOf("k") } assertIndexOverflow { maxIndexSequence.indexOfFirst { false } } assertIndexOverflow { maxIndexSequence.indexOfLast { false } } } @Test fun indexOfOverflowIterable() { assertIndexOverflow { maxIndexIterable.indexOf("j") } assertIndexOverflow { maxIndexIterable.lastIndexOf("k") } assertIndexOverflow { maxIndexIterable.indexOfFirst { false } } assertIndexOverflow { maxIndexIterable.indexOfLast { false } } } @Test fun forEachIndexedOverflow() { assertIndexOverflow { maxIndexSequence.forEachIndexed { index, _ -> checkIndexPositive(index) } } assertIndexOverflow { maxIndexIterable.forEachIndexed { index, _ -> checkIndexPositive(index) } } } @Test fun withIndexOverflow() { assertIndexOverflow { maxIndexSequence.withIndex().forEach { (index, _) -> checkIndexPositive(index) } } assertIndexOverflow { maxIndexIterable.withIndex().forEach { (index, _) -> checkIndexPositive(index) } } } @Test fun countOverflow() { assertCountOverflow { repeatCounted("k").count() } assertCountOverflow { repeatCounted("k").count { true } } assertCountOverflow { repeatCounted("k").asIterable().count() } assertCountOverflow { repeatCounted("k").asIterable().count { true } } } @Test fun averageCountOverflow() { assertCountOverflow { repeatCounted(1.0).average() } assertCountOverflow { repeatCounted(1L).asIterable().average() } } private class CountingCollection : AbstractMutableCollection() { private var _size = 0 override fun add(element: T): Boolean { if (_size < 0) error("Collection is too long") _size++ return true } override val size: Int get() = _size override fun iterator(): MutableIterator = error("not implemented") } @Test fun mapIndexedOverflow() { assertIndexOverflow { maxIndexSequence.mapIndexed { index, _ -> checkIndexPositive(index) }.forEach { } } assertIndexOverflow { maxIndexSequence.mapIndexedTo(CountingCollection()) { index, _ -> checkIndexPositive(index) } } assertIndexOverflow { maxIndexIterable.mapIndexedTo(CountingCollection()) { index, _ -> checkIndexPositive(index) } } } @Test fun mapNotNullIndexedOverflow() { assertIndexOverflow { maxIndexSequence.mapIndexedNotNull { index, _ -> checkIndexPositive(index) }.forEach { } } assertIndexOverflow { maxIndexSequence.mapIndexedNotNullTo(CountingCollection()) { index, _ -> checkIndexPositive(index) } } assertIndexOverflow { maxIndexIterable.mapIndexedNotNullTo(CountingCollection()) { index, _ -> checkIndexPositive(index) } } } @Test fun filterIndexedOverflow() { assertIndexOverflow { maxIndexSequence.filterIndexed { index, _ -> checkIndexPositive(index); true }.forEach { } } assertIndexOverflow { maxIndexSequence.filterIndexedTo(CountingCollection()) { index, _ -> checkIndexPositive(index); true } } assertIndexOverflow { maxIndexIterable.filterIndexedTo(CountingCollection()) { index, _ -> checkIndexPositive(index); true } } } @Test fun foldIndexedOverflow() { assertIndexOverflow { maxIndexSequence.foldIndexed("") { index, _, s -> checkIndexPositive(index); s } } assertIndexOverflow { maxIndexIterable.foldIndexed("") { index, _, s -> checkIndexPositive(index); s } } } @Test fun reduceIndexedOverflow() { assertIndexOverflow { maxIndexSequence.reduceIndexed { index, _, s -> checkIndexPositive(index); s } } assertIndexOverflow { maxIndexIterable.reduceIndexed { index, _, s -> checkIndexPositive(index); s } } } @Test fun scanIndexedOverflow() { assertIndexOverflow { maxIndexSequence.scanIndexed("") { index, _, s -> checkIndexPositive(index); s }.forEach { } } } @Test fun runningReduceIndexedOverflow() { assertIndexOverflow { maxIndexSequence.runningReduceIndexed { index, _, s -> checkIndexPositive(index); s }.forEach { } } } @Test fun dropTwiceMaxValue() { val halfMax = (1 shl 30) + 1 val dropOnce = longCountSequence.drop(halfMax) val dropTwice = dropOnce.drop(halfMax) val expectedEnd = halfMax.toLong() * 2 assertEquals(expectedEnd, dropTwice.first()) val dropTake = dropOnce.take(halfMax + 1) assertEquals(expectedEnd, dropTake.last()) } @Test fun dropMaxValue() { val range = 0L..Int.MAX_VALUE + 1L assertEquals(listOf(Int.MAX_VALUE.toLong(), Int.MAX_VALUE + 1L), range.drop(Int.MAX_VALUE)) } }