diff --git a/kotlinx-coroutines-core/common/test/channels/BasicOperationsTest.kt b/kotlinx-coroutines-core/common/test/channels/BasicOperationsTest.kt index fb9e0d9cf1..03c5f7457f 100644 --- a/kotlinx-coroutines-core/common/test/channels/BasicOperationsTest.kt +++ b/kotlinx-coroutines-core/common/test/channels/BasicOperationsTest.kt @@ -8,31 +8,31 @@ class BasicOperationsTest : TestBase() { @Test fun testSimpleSendReceive() = runTest { // Parametrized common test :( - TestChannelKind.values().forEach { kind -> testSendReceive(kind, 20) } + TestChannelKind.entries.forEach { kind -> testSendReceive(kind, 20) } } @Test fun testTrySendToFullChannel() = runTest { - TestChannelKind.values().forEach { kind -> testTrySendToFullChannel(kind) } + TestChannelKind.entries.forEach { kind -> testTrySendToFullChannel(kind) } } @Test fun testTrySendAfterClose() = runTest { - TestChannelKind.values().forEach { kind -> testTrySendAfterClose(kind) } + TestChannelKind.entries.forEach { kind -> testTrySendAfterClose(kind) } } @Test fun testSendAfterClose() = runTest { - TestChannelKind.values().forEach { kind -> testSendAfterClose(kind) } + TestChannelKind.entries.forEach { kind -> testSendAfterClose(kind) } } @Test fun testReceiveCatching() = runTest { - TestChannelKind.values().forEach { kind -> testReceiveCatching(kind) } + TestChannelKind.entries.forEach { kind -> testReceiveCatching(kind) } } @Test - fun testInvokeOnClose() = TestChannelKind.values().forEach { kind -> + fun testInvokeOnClose() = TestChannelKind.entries.forEach { kind -> reset() val channel = kind.create() channel.invokeOnClose { @@ -48,7 +48,7 @@ class BasicOperationsTest : TestBase() { } @Test - fun testInvokeOnClosed() = TestChannelKind.values().forEach { kind -> + fun testInvokeOnClosed() = TestChannelKind.entries.forEach { kind -> reset() expect(1) val channel = kind.create() @@ -59,7 +59,7 @@ class BasicOperationsTest : TestBase() { } @Test - fun testMultipleInvokeOnClose() = TestChannelKind.values().forEach { kind -> + fun testMultipleInvokeOnClose() = TestChannelKind.entries.forEach { kind -> reset() val channel = kind.create() channel.invokeOnClose { expect(3) } @@ -71,14 +71,16 @@ class BasicOperationsTest : TestBase() { } @Test - fun testIterator() = runTest { - TestChannelKind.values().forEach { kind -> + fun testIteratorHasNextMustPrecedeNext() = runTest { + TestChannelKind.entries.forEach { kind -> val channel = kind.create() val iterator = channel.iterator() assertFailsWith { iterator.next() } channel.close() assertFailsWith { iterator.next() } assertFalse(iterator.hasNext()) + assertFailsWith { iterator.next() } + assertFailsWith { iterator.next() } } } @@ -109,7 +111,7 @@ class BasicOperationsTest : TestBase() { try { expect(2) channel.close() - } catch (e: TestException) { + } catch (_: TestException) { expect(4) } } diff --git a/kotlinx-coroutines-core/common/test/channels/ClosedChannelTest.kt b/kotlinx-coroutines-core/common/test/channels/ClosedChannelTest.kt new file mode 100644 index 0000000000..e47226fdb8 --- /dev/null +++ b/kotlinx-coroutines-core/common/test/channels/ClosedChannelTest.kt @@ -0,0 +1,196 @@ +package kotlinx.coroutines.channels + +import kotlinx.coroutines.testing.* +import kotlin.test.* + +class ClosedChannelTest : TestBase() { + /** + * Iterator. + */ + + @Test + fun testIteratorClosedHasNextFalse() = runTest { + TestChannelKind.entries.forEach { kind -> + val channel = kind.create() + val iterator = channel.iterator() + channel.close() + assertFalse(iterator.hasNext()) + } + } + + @Test + fun testIteratorClosedWithExceptionHasNextThrows() = runTest { + TestChannelKind.entries.forEach { kind -> + val channel = kind.create() + val iterator = channel.iterator() + channel.close(TestException()) + assertFailsWith { (iterator.hasNext()) } + } + } + + @Test + fun testClosedToListOk() = runTest { + TestChannelKind.entries.forEach { kind -> + val channel = kind.create() + channel.close() + channel.toList() + } + } + + @Test + fun testClosedWithExceptionToListThrows() = runTest { + TestChannelKind.entries.forEach { kind -> + val channel = kind.create() + channel.close(TestException()) + assertFailsWith { channel.toList() } + } + } + + @Test + fun testClosedConsumeEachOk() = runTest { + TestChannelKind.entries.forEach { kind -> + val channel = kind.create() + channel.close() + channel.consumeEach { } + } + } + + @Test + fun testClosedWithExceptionConsumeEachThrows() = runTest { + TestChannelKind.entries.forEach { kind -> + val channel = kind.create() + channel.close(TestException()) + assertFailsWith { channel.consumeEach { } } + } + } + + /** + * toList() alternative when a channel might close with an exception. + */ + + @Test + fun testToListCatching() = runTest { + TestChannelKind.entries.forEach { kind -> + val channel = kind.create() + channel.close(TestException()) + val lst = mutableListOf() + while (true) { + channel.receiveCatching().onClosed { // suspends + assertTrue(it is TestException) + break + }.getOrThrow().apply(lst::add) + } + assertTrue(lst.isEmpty()) + } + } + + @Test + fun testTryToListCatching() = runTest { + TestChannelKind.entries.forEach { kind -> + val channel = kind.create() + channel.close(TestException()) + val lst = mutableListOf() + while (true) { + channel.tryReceive().onClosed { // does not suspend + assertTrue(it is TestException) + break + }.onFailure { + // empty, but not yet closed + break + }.getOrThrow().apply(lst::add) + } + assertTrue(lst.isEmpty()) + } + } + + /** + * Properties. + * + * Properties stay the same regardless of whether the channel was closed with or without exception. + */ + + @Test + fun testClosedIsClosedForReceive() = runTest { + TestChannelKind.entries.forEach { kind -> + val channel = kind.create() + assertFalse(channel.isClosedForReceive) + channel.close() + assertTrue(channel.isClosedForReceive) + } + } + + @Test + fun testClosedWithExceptionIsClosedForReceive() = runTest { + TestChannelKind.entries.forEach { kind -> + val channel = kind.create() + assertFalse(channel.isClosedForReceive) + channel.close(TestException()) + assertTrue(channel.isClosedForReceive) + } + } + + @Test + fun testIsEmpty() = runTest { + val channel = Channel() + assertTrue(channel.isEmpty) + assertFalse(channel.isClosedForReceive) + } + + @Test + fun testClosedIsEmptyFalse() = runTest { + TestChannelKind.entries.forEach { kind -> + val channel = kind.create() + assertTrue(channel.isEmpty) + assertFalse(channel.isClosedForReceive) + channel.close() + // NB! Not obvious. + assertFalse(channel.isEmpty) + assertTrue(channel.isClosedForReceive) + } + } + + @Test + fun testClosedWithExceptionIsEmptyFalse() = runTest { + TestChannelKind.entries.forEach { kind -> + val channel = kind.create() + assertTrue(channel.isEmpty) + assertFalse(channel.isClosedForReceive) + channel.close(TestException()) + assertFalse(channel.isEmpty) + assertTrue(channel.isClosedForReceive) + } + } + + @Test + fun testSendClosedReceiveIsEmptyFalse() = runTest { + val channel = Channel(1) + assertTrue(channel.isEmpty) + assertFalse(channel.isClosedForReceive) + channel.send(1) + assertFalse(channel.isEmpty) + assertFalse(channel.isClosedForReceive) + channel.close() + assertFalse(channel.isEmpty) + assertFalse(channel.isClosedForReceive) + channel.receive() + assertFalse(channel.isEmpty) + assertTrue(channel.isClosedForReceive) + } + + @Test + fun testSendClosedWithExceptionReceiveIsEmptyFalse() = runTest { + val channel = Channel(1) + assertTrue(channel.isEmpty) + assertFalse(channel.isClosedForReceive) + channel.send(1) + assertFalse(channel.isEmpty) + assertFalse(channel.isClosedForReceive) + channel.close(TestException()) + assertFalse(channel.isEmpty) + assertFalse(channel.isClosedForReceive) + channel.receive() + assertFalse(channel.isEmpty) + assertTrue(channel.isClosedForReceive) + } + +}