From 305240215b46d1fa6a700fc1f7ba175d92bf0684 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Wojtek=20Kalici=C5=84ski?= <146713236+wkal-pubnub@users.noreply.github.com> Date: Sat, 30 Nov 2024 12:41:06 +0100 Subject: [PATCH] Remove kotlinx.datetime dependency (#123) * Remove kotlinx.datetime dependency * Add 1s delays in tests before history and message counts * Add verbose test logging --- .github/workflows/run-tests.yml | 2 +- build.gradle.kts | 2 +- gradle/libs.versions.toml | 10 +--- .../api/pubnub-3p-diff-match-patch.api | 2 +- pubnub-3p-diff-match-patch/build.gradle.kts | 5 -- .../fraser/neil/plaintext/DiffMatchPatch.kt | 50 +++++++++++-------- .../commonTest/kotlin/DiffMatchPatchTest.kt | 18 ++++--- pubnub-chat-impl/build.gradle.kts | 1 - .../com/pubnub/chat/internal/ChatImpl.kt | 4 +- .../com/pubnub/chat/internal/UserImpl.kt | 4 +- .../chat/internal/channel/BaseChannel.kt | 4 +- .../chat/internal/channel/ChannelImpl.kt | 2 +- .../internal/channel/ThreadChannelImpl.kt | 2 +- .../integration/ChannelIntegrationTest.kt | 32 +++++++----- .../pubnub/integration/ChatIntegrationTest.kt | 24 +++++---- .../integration/MessageIntegrationTest.kt | 24 +++++---- .../kotlin/com/pubnub/internal/testutils.kt | 8 +++ .../kotlin/com/pubnub/kmp/ChannelTest.kt | 4 +- .../com/pubnub/kmp/ThreadChannelTest.kt | 2 +- .../kmp/utils/ExponentialRateLimiterTest.kt | 4 +- pubnub-kotlin | 2 +- 21 files changed, 112 insertions(+), 94 deletions(-) create mode 100644 pubnub-chat-impl/src/commonTest/kotlin/com/pubnub/internal/testutils.kt diff --git a/.github/workflows/run-tests.yml b/.github/workflows/run-tests.yml index 82c10dc3..efdbee03 100644 --- a/.github/workflows/run-tests.yml +++ b/.github/workflows/run-tests.yml @@ -37,7 +37,7 @@ jobs: ${{ runner.os }}-gradle- - name: Build and run tests run: | - ./gradlew check + ./gradlew check -i env: SDK_PUB_KEY: ${{ secrets.SDK_PUB_KEY }} SDK_SUB_KEY: ${{ secrets.SDK_SUB_KEY }} diff --git a/build.gradle.kts b/build.gradle.kts index a3b101c2..d7ae09fe 100644 --- a/build.gradle.kts +++ b/build.gradle.kts @@ -48,7 +48,7 @@ kotlin { val commonTest by getting { dependencies { implementation(kotlin("test")) - implementation("com.pubnub:pubnub-kotlin-test") + implementation(libs.pubnub.kotlin.test) } } } diff --git a/gradle/libs.versions.toml b/gradle/libs.versions.toml index c358654d..f2d5e486 100644 --- a/gradle/libs.versions.toml +++ b/gradle/libs.versions.toml @@ -4,24 +4,16 @@ kotlin = "2.0.21" vanniktech = "0.29.0" ktlint = "12.1.0" dokka = "1.9.20" -kotlinx_datetime = "0.6.0" -kotlinx_coroutines = "1.8.1" kotlinx_serialization = "1.7.1" -pubnub = "10.1.0" +pubnub = "10.2.0" [libraries] pubnub-kotlin-api = { module = "com.pubnub:pubnub-kotlin-api", version.ref = "pubnub" } -pubnub-kotlin-impl = { module = "com.pubnub:pubnub-kotlin-impl", version.ref = "pubnub" } pubnub-kotlin = { module = "com.pubnub:pubnub-kotlin", version.ref = "pubnub" } pubnub-kotlin-test = { module = "com.pubnub:pubnub-kotlin-test", version.ref = "pubnub" } -#slf4j = { module = "org.slf4j:slf4j-api", version = "1.7.36" } -#cbor = { module = "co.nstant.in:cbor", version = "0.9" } -#jetbrains-annotations = { module = "org.jetbrains:annotations", version = "24.1.0" } kotlinx-atomicfu = { module = "org.jetbrains.kotlinx:atomicfu", version = "0.25.0" } kotlinx-serialization-core = { module = "org.jetbrains.kotlinx:kotlinx-serialization-core", version.ref = "kotlinx_serialization" } -kotlinx-datetime = { module = "org.jetbrains.kotlinx:kotlinx-datetime", version.ref = "kotlinx_datetime"} touchlab-kermit = { module = "co.touchlab:kermit", version = "2.0.4" } -diff-match-patch = { module = "org.bitbucket.cowwoc:diff-match-patch", version = "1.2" } ## tests #logback-classic = { module = "ch.qos.logback:logback-classic", version.ref = "logback" } diff --git a/pubnub-3p-diff-match-patch/api/pubnub-3p-diff-match-patch.api b/pubnub-3p-diff-match-patch/api/pubnub-3p-diff-match-patch.api index 499424a3..047dfa28 100644 --- a/pubnub-3p-diff-match-patch/api/pubnub-3p-diff-match-patch.api +++ b/pubnub-3p-diff-match-patch/api/pubnub-3p-diff-match-patch.api @@ -3,7 +3,7 @@ public final class name/fraser/neil/plaintext/DiffMatchPatch { public fun ()V public fun (FSFIFS)V public synthetic fun (FSFIFSILkotlin/jvm/internal/DefaultConstructorMarker;)V - public final fun diff_bisect (Ljava/lang/String;Ljava/lang/String;J)Ljava/util/List; + public final fun diff_bisect-UpJHN0k (Ljava/lang/String;Ljava/lang/String;J)Ljava/util/List; public final fun diff_charsToLines (Ljava/util/List;Ljava/util/List;)V public final fun diff_cleanupEfficiency (Ljava/util/List;)V public final fun diff_cleanupMerge (Ljava/util/List;)V diff --git a/pubnub-3p-diff-match-patch/build.gradle.kts b/pubnub-3p-diff-match-patch/build.gradle.kts index 7ec75968..115f0d1d 100644 --- a/pubnub-3p-diff-match-patch/build.gradle.kts +++ b/pubnub-3p-diff-match-patch/build.gradle.kts @@ -12,11 +12,6 @@ mavenPublishing { kotlin { sourceSets { - commonMain { - dependencies { - implementation("org.jetbrains.kotlinx:kotlinx-datetime:0.6.1") - } - } commonTest { dependencies { implementation(kotlin("test")) diff --git a/pubnub-3p-diff-match-patch/src/commonMain/kotlin/name/fraser/neil/plaintext/DiffMatchPatch.kt b/pubnub-3p-diff-match-patch/src/commonMain/kotlin/name/fraser/neil/plaintext/DiffMatchPatch.kt index d8faa075..a6c4474c 100644 --- a/pubnub-3p-diff-match-patch/src/commonMain/kotlin/name/fraser/neil/plaintext/DiffMatchPatch.kt +++ b/pubnub-3p-diff-match-patch/src/commonMain/kotlin/name/fraser/neil/plaintext/DiffMatchPatch.kt @@ -17,10 +17,12 @@ */ package name.fraser.neil.plaintext -import kotlinx.datetime.Clock import kotlin.math.abs import kotlin.math.max import kotlin.math.min +import kotlin.time.Duration +import kotlin.time.Duration.Companion.milliseconds +import kotlin.time.TimeSource /* * Functions for diff, match and patch. @@ -123,11 +125,11 @@ class DiffMatchPatch( checklines: Boolean = true, ): MutableList { // was LinkedList // Set a deadline by which time the diff must be complete. - val deadline: Long + val deadline: TimeSource.Monotonic.ValueTimeMark if (diffTimeout <= 0) { - deadline = Long.MAX_VALUE + deadline = TimeSource.Monotonic.markNow() + Duration.INFINITE } else { - deadline = Clock.System.now().toEpochMilliseconds() + (diffTimeout * 1000).toLong() + deadline = TimeSource.Monotonic.markNow() + (diffTimeout * 1000).toLong().milliseconds } return diff_main(text1, text2, checklines, deadline) } @@ -140,13 +142,15 @@ class DiffMatchPatch( * @param checklines Speedup flag. If false, then don't run a * line-level diff first to identify the changed areas. * If true, then run a faster slightly less optimal diff. - * @param deadline Time when the diff should be complete by. Used + * @param deadline When the computation started. Used * internally for recursive calls. Users should set DiffTimeout instead. * @return Linked List of Diff objects. */ private fun diff_main( - text1: String, text2: String, - checklines: Boolean, deadline: Long, + text1: String, + text2: String, + checklines: Boolean, + deadline: TimeSource.Monotonic.ValueTimeMark, ): MutableList { // was LinkedList // Check for null inputs. var text1 = text1 @@ -197,12 +201,14 @@ class DiffMatchPatch( * @param checklines Speedup flag. If false, then don't run a * line-level diff first to identify the changed areas. * If true, then run a faster slightly less optimal diff. - * @param deadline Time when the diff should be complete by. + * @param deadline When the computation started. * @return Linked List of Diff objects. */ private fun diff_compute( - text1: String, text2: String, - checklines: Boolean, deadline: Long, + text1: String, + text2: String, + checklines: Boolean, + deadline: TimeSource.Monotonic.ValueTimeMark, ): MutableList { // was LinkedList var diffs = mutableListOf() // was LinkedList @@ -274,12 +280,13 @@ class DiffMatchPatch( * This speedup can produce non-minimal diffs. * @param text1 Old string to be diffed. * @param text2 New string to be diffed. - * @param deadline Time when the diff should be complete by. + * @param deadline When the computation started. * @return Linked List of Diff objects. */ private fun diff_lineMode( - text1: String, text2: String, - deadline: Long, + text1: String, + text2: String, + deadline: TimeSource.Monotonic.ValueTimeMark, ): MutableList { // was LinkedList // Scan the text on a line-by-line basis first. var text1 = text1 @@ -353,12 +360,13 @@ class DiffMatchPatch( * See Myers 1986 paper: An O(ND) Difference Algorithm and Its Variations. * @param text1 Old string to be diffed. * @param text2 New string to be diffed. - * @param deadline Time at which to bail if not yet complete. + * @param deadline When the computation started. * @return LinkedList of Diff objects. */ fun diff_bisect( - text1: String, text2: String, - deadline: Long, + text1: String, + text2: String, + deadline: TimeSource.Monotonic.ValueTimeMark, ): MutableList { // was LinkedList // Cache the text lengths to prevent multiple calls. val text1_length = text1.length @@ -386,7 +394,7 @@ class DiffMatchPatch( var k2end = 0 for (d in 0 until max_d) { // Bail out if deadline is reached. - if (Clock.System.now().toEpochMilliseconds() > deadline) { + if (deadline.hasPassedNow()) { break } @@ -478,12 +486,14 @@ class DiffMatchPatch( * @param text2 New string to be diffed. * @param x Index of split point in text1. * @param y Index of split point in text2. - * @param deadline Time at which to bail if not yet complete. + * @param deadline When the computation started. * @return LinkedList of Diff objects. */ private fun diff_bisectSplit( - text1: String, text2: String, - x: Int, y: Int, deadline: Long, + text1: String, + text2: String, + x: Int, y: Int, + deadline: TimeSource.Monotonic.ValueTimeMark, ): MutableList { // was LinkedList val text1a: String = text1.substring(0, x) val text2a: String = text2.substring(0, y) diff --git a/pubnub-3p-diff-match-patch/src/commonTest/kotlin/DiffMatchPatchTest.kt b/pubnub-3p-diff-match-patch/src/commonTest/kotlin/DiffMatchPatchTest.kt index fcc8a9cb..31e1133f 100644 --- a/pubnub-3p-diff-match-patch/src/commonTest/kotlin/DiffMatchPatchTest.kt +++ b/pubnub-3p-diff-match-patch/src/commonTest/kotlin/DiffMatchPatchTest.kt @@ -17,9 +17,11 @@ */ package name.fraser.neil.plaintext -import kotlinx.datetime.Clock import name.fraser.neil.plaintext.DiffMatchPatch.LinesToCharsResult import kotlin.test.Test +import kotlin.time.Duration +import kotlin.time.TimeSource +import kotlin.time.measureTime class DiffMatchPatchTest { private val dmp: DiffMatchPatch = DiffMatchPatch() @@ -851,11 +853,11 @@ class DiffMatchPatchTest { EQUAL, "a" ), DiffMatchPatch.Diff(DELETE, "t"), DiffMatchPatch.Diff(INSERT, "p") ) - assertEquals("diff_bisect: Normal.", diffs, dmp.diff_bisect(a, b, Long.MAX_VALUE)) + assertEquals("diff_bisect: Normal.", diffs, dmp.diff_bisect(a, b, TimeSource.Monotonic.markNow() + Duration.INFINITE)) // Timeout. diffs = diffList(DiffMatchPatch.Diff(DELETE, "cat"), DiffMatchPatch.Diff(INSERT, "map")) - assertEquals("diff_bisect: Timeout.", diffs, dmp.diff_bisect(a, b, 0)) + assertEquals("diff_bisect: Timeout.", diffs, dmp.diff_bisect(a, b, TimeSource.Monotonic.markNow() - Duration.INFINITE)) } @Test @@ -979,15 +981,15 @@ class DiffMatchPatchTest { a += a b += b } - val startTime = Clock.System.now().toEpochMilliseconds() - dmp.diff_main(a, b) - val endTime = Clock.System.now().toEpochMilliseconds() + val time = measureTime { + dmp.diff_main(a, b) + } // Test that we took at least the timeout period. - assertTrue("diff_main: Timeout min.", dmp.diffTimeout * 1000 <= endTime - startTime) + assertTrue("diff_main: Timeout min.", dmp.diffTimeout * 1000 <= time.inWholeMilliseconds) // Test that we didn't take forever (be forgiving). // Theoretically this test could fail very occasionally if the // OS task swaps or locks up for a second at the wrong moment. - assertTrue("diff_main: Timeout max.", dmp.diffTimeout * 1000 * 2 > endTime - startTime) + assertTrue("diff_main: Timeout max.", dmp.diffTimeout * 1000 * 2 > time.inWholeMilliseconds) dmp.diffTimeout = 0f // Test the linemode speedup. diff --git a/pubnub-chat-impl/build.gradle.kts b/pubnub-chat-impl/build.gradle.kts index 1a9e32c1..7df05c0e 100644 --- a/pubnub-chat-impl/build.gradle.kts +++ b/pubnub-chat-impl/build.gradle.kts @@ -42,7 +42,6 @@ kotlin { implementation(project(":pubnub-chat-api")) implementation(project(":pubnub-3p-diff-match-patch")) implementation(libs.pubnub.kotlin.api) - implementation(libs.kotlinx.datetime) implementation(libs.kotlinx.serialization.core) implementation(libs.kotlinx.atomicfu) implementation(libs.touchlab.kermit) diff --git a/pubnub-chat-impl/src/commonMain/kotlin/com/pubnub/chat/internal/ChatImpl.kt b/pubnub-chat-impl/src/commonMain/kotlin/com/pubnub/chat/internal/ChatImpl.kt index fe0b0843..28cafa06 100644 --- a/pubnub-chat-impl/src/commonMain/kotlin/com/pubnub/chat/internal/ChatImpl.kt +++ b/pubnub-chat-impl/src/commonMain/kotlin/com/pubnub/chat/internal/ChatImpl.kt @@ -31,6 +31,8 @@ import com.pubnub.api.models.consumer.pubsub.PNEvent import com.pubnub.api.models.consumer.push.PNPushAddChannelResult import com.pubnub.api.models.consumer.push.PNPushListProvisionsResult import com.pubnub.api.models.consumer.push.PNPushRemoveChannelResult +import com.pubnub.api.utils.Clock +import com.pubnub.api.utils.Instant import com.pubnub.api.v2.callbacks.Result import com.pubnub.chat.Channel import com.pubnub.chat.Chat @@ -109,8 +111,6 @@ import com.pubnub.kmp.createEventListener import com.pubnub.kmp.then import com.pubnub.kmp.thenAsync import encodeForSending -import kotlinx.datetime.Clock -import kotlinx.datetime.Instant import kotlin.reflect.KClass import kotlin.time.Duration.Companion.seconds diff --git a/pubnub-chat-impl/src/commonMain/kotlin/com/pubnub/chat/internal/UserImpl.kt b/pubnub-chat-impl/src/commonMain/kotlin/com/pubnub/chat/internal/UserImpl.kt index 97f090ca..75d5ec8d 100644 --- a/pubnub-chat-impl/src/commonMain/kotlin/com/pubnub/chat/internal/UserImpl.kt +++ b/pubnub-chat-impl/src/commonMain/kotlin/com/pubnub/chat/internal/UserImpl.kt @@ -11,6 +11,8 @@ import com.pubnub.api.models.consumer.objects.membership.PNChannelMembershipArra import com.pubnub.api.models.consumer.objects.uuid.PNUUIDMetadata import com.pubnub.api.models.consumer.pubsub.objects.PNDeleteUUIDMetadataEventMessage import com.pubnub.api.models.consumer.pubsub.objects.PNSetUUIDMetadataEventMessage +import com.pubnub.api.utils.Clock +import com.pubnub.api.utils.Instant import com.pubnub.api.utils.PatchValue import com.pubnub.api.v2.callbacks.Result import com.pubnub.chat.Channel @@ -31,8 +33,6 @@ import com.pubnub.kmp.asFuture import com.pubnub.kmp.catch import com.pubnub.kmp.createEventListener import com.pubnub.kmp.then -import kotlinx.datetime.Clock -import kotlinx.datetime.Instant import tryLong data class UserImpl( diff --git a/pubnub-chat-impl/src/commonMain/kotlin/com/pubnub/chat/internal/channel/BaseChannel.kt b/pubnub-chat-impl/src/commonMain/kotlin/com/pubnub/chat/internal/channel/BaseChannel.kt index 03a61b9c..fd1d4e73 100644 --- a/pubnub-chat-impl/src/commonMain/kotlin/com/pubnub/chat/internal/channel/BaseChannel.kt +++ b/pubnub-chat-impl/src/commonMain/kotlin/com/pubnub/chat/internal/channel/BaseChannel.kt @@ -24,6 +24,8 @@ import com.pubnub.api.models.consumer.pubsub.objects.PNDeleteChannelMetadataEven import com.pubnub.api.models.consumer.pubsub.objects.PNObjectEventResult import com.pubnub.api.models.consumer.pubsub.objects.PNSetChannelMetadataEventMessage import com.pubnub.api.models.consumer.push.payload.PushPayloadHelper +import com.pubnub.api.utils.Clock +import com.pubnub.api.utils.Instant import com.pubnub.api.utils.PatchValue import com.pubnub.api.v2.callbacks.Result import com.pubnub.api.v2.subscriptions.SubscriptionOptions @@ -95,8 +97,6 @@ import encodeForSending import kotlinx.atomicfu.atomic import kotlinx.atomicfu.locks.reentrantLock import kotlinx.atomicfu.locks.withLock -import kotlinx.datetime.Clock -import kotlinx.datetime.Instant import tryLong import kotlin.time.Duration import kotlin.time.Duration.Companion.milliseconds diff --git a/pubnub-chat-impl/src/commonMain/kotlin/com/pubnub/chat/internal/channel/ChannelImpl.kt b/pubnub-chat-impl/src/commonMain/kotlin/com/pubnub/chat/internal/channel/ChannelImpl.kt index 79069ff8..4b8ec25d 100644 --- a/pubnub-chat-impl/src/commonMain/kotlin/com/pubnub/chat/internal/channel/ChannelImpl.kt +++ b/pubnub-chat-impl/src/commonMain/kotlin/com/pubnub/chat/internal/channel/ChannelImpl.kt @@ -1,13 +1,13 @@ package com.pubnub.chat.internal.channel import com.pubnub.api.models.consumer.objects.channel.PNChannelMetadata +import com.pubnub.api.utils.Clock import com.pubnub.chat.Channel import com.pubnub.chat.Message import com.pubnub.chat.internal.ChatInternal import com.pubnub.chat.internal.DELETED import com.pubnub.chat.internal.message.MessageImpl import com.pubnub.chat.types.ChannelType -import kotlinx.datetime.Clock data class ChannelImpl( override val chat: ChatInternal, diff --git a/pubnub-chat-impl/src/commonMain/kotlin/com/pubnub/chat/internal/channel/ThreadChannelImpl.kt b/pubnub-chat-impl/src/commonMain/kotlin/com/pubnub/chat/internal/channel/ThreadChannelImpl.kt index 82916c3c..87a87198 100644 --- a/pubnub-chat-impl/src/commonMain/kotlin/com/pubnub/chat/internal/channel/ThreadChannelImpl.kt +++ b/pubnub-chat-impl/src/commonMain/kotlin/com/pubnub/chat/internal/channel/ThreadChannelImpl.kt @@ -4,6 +4,7 @@ import co.touchlab.kermit.Logger import com.pubnub.api.models.consumer.PNPublishResult import com.pubnub.api.models.consumer.message_actions.PNMessageAction import com.pubnub.api.models.consumer.objects.channel.PNChannelMetadata +import com.pubnub.api.utils.Clock import com.pubnub.chat.Channel import com.pubnub.chat.Message import com.pubnub.chat.ThreadChannel @@ -25,7 +26,6 @@ import com.pubnub.kmp.asFuture import com.pubnub.kmp.awaitAll import com.pubnub.kmp.then import com.pubnub.kmp.thenAsync -import kotlinx.datetime.Clock data class ThreadChannelImpl( override val parentMessage: Message, diff --git a/pubnub-chat-impl/src/commonTest/kotlin/com/pubnub/integration/ChannelIntegrationTest.kt b/pubnub-chat-impl/src/commonTest/kotlin/com/pubnub/integration/ChannelIntegrationTest.kt index 7ebe6031..68c172ec 100644 --- a/pubnub-chat-impl/src/commonTest/kotlin/com/pubnub/integration/ChannelIntegrationTest.kt +++ b/pubnub-chat-impl/src/commonTest/kotlin/com/pubnub/integration/ChannelIntegrationTest.kt @@ -2,6 +2,8 @@ package com.pubnub.integration import com.pubnub.api.models.consumer.objects.PNMemberKey import com.pubnub.api.models.consumer.objects.PNSortKey +import com.pubnub.api.utils.Clock +import com.pubnub.api.utils.Instant import com.pubnub.chat.Channel import com.pubnub.chat.Event import com.pubnub.chat.MentionTarget @@ -24,11 +26,10 @@ import com.pubnub.kmp.createCustomObject import com.pubnub.test.await import com.pubnub.test.randomString import com.pubnub.test.test +import delayForHistory import kotlinx.atomicfu.atomic import kotlinx.coroutines.CompletableDeferred import kotlinx.coroutines.test.runTest -import kotlinx.datetime.Clock -import kotlinx.datetime.Instant import kotlin.test.Ignore import kotlin.test.Test import kotlin.test.assertContains @@ -44,10 +45,12 @@ class ChannelIntegrationTest : BaseChatIntegrationTest() { @Test fun getPinnedMessage() = runTest { val timetoken = channel01.sendText("Text text text").await() + delayForHistory() val message = channel01.getMessage(timetoken.timetoken).await()!! val updatedChannel = channel01.pinMessage(message).await() assertEquals(timetoken.timetoken.toString(), updatedChannel.custom?.get(PINNED_MESSAGE_TIMETOKEN)) + delayForHistory() val pinnedMessage = updatedChannel.getPinnedMessage().await() assertNotNull(pinnedMessage) @@ -102,15 +105,17 @@ class ChannelIntegrationTest : BaseChatIntegrationTest() { pubnub.test(backgroundScope, checkAllEvents = false) { var closeable: AutoCloseable? = null pubnub.awaitSubscribe(listOf(channel01.id)) { - channel01.streamPresence { - if (it.isNotEmpty()) { + closeable = channel01.streamPresence { + if (someUser02.id in it) { completable.complete(it) } } - closeable = channel01.connect {} } - assertEquals(setOf(someUser.id), completable.await()) + + val closeable2 = channel01Chat02.connect {} + completable.await() closeable?.close() + closeable2.close() } } @@ -138,13 +143,13 @@ class ChannelIntegrationTest : BaseChatIntegrationTest() { @Test fun join_updates_lastReadMessageTimetoken() = runTest { - val then = Instant.fromEpochSeconds(chat.pubNub.time().await().timetoken / 10000000) + val then = Instant.fromEpochSeconds(chat.pubNub.time().await().timetoken / 10000000, 0) val channel = chat.createChannel(randomString()).await() val lastReadMessage: Long = channel.join().await().membership.lastReadMessageTimetoken ?: 0 assertTrue(lastReadMessage > 0) - assertContains(then..Clock.System.now(), Instant.fromEpochSeconds(lastReadMessage / 10000000)) + assertContains(then..Clock.System.now(), Instant.fromEpochSeconds(lastReadMessage / 10000000, 0)) } @Test @@ -385,6 +390,7 @@ class ChannelIntegrationTest : BaseChatIntegrationTest() { val channel = chat.createDirectConversation(user2).await().channel channel.sendText("text1").await().timetoken + delayForHistory() chat.markAllMessagesAsRead().await() val tt = channel.sendText("text2").await().timetoken @@ -486,8 +492,7 @@ class ChannelIntegrationTest : BaseChatIntegrationTest() { val messageText = "some text" val tt = channel01.sendText(text = messageText, ttl = 60).await().timetoken - delayInMillis(1000) - + delayForHistory() val message = channel01.getMessage(tt).await() assertEquals(messageText, message?.text) assertEquals(tt, message?.timetoken) @@ -514,8 +519,7 @@ class ChannelIntegrationTest : BaseChatIntegrationTest() { referencedChannels = referencedChannels ).await().timetoken - delayInMillis(1500) - + delayForHistory() val message = channel01.getMessage(tt).await() val actualMentionedUsers: Map? = message?.mentionedUsers val actualMentionPosition = actualMentionedUsers?.keys?.first() @@ -602,7 +606,7 @@ class ChannelIntegrationTest : BaseChatIntegrationTest() { fun can_getMessageReportsHistory() = runTest { val pnPublishResult = channel01.sendText(text = "message1").await() val timetoken = pnPublishResult.timetoken - delayInMillis(250) + delayForHistory() val message = channel01.getMessage(timetoken).await()!! // report messages @@ -612,6 +616,7 @@ class ChannelIntegrationTest : BaseChatIntegrationTest() { message.report(reason02).await() // getMessageReport + delayForHistory() val eventsHistoryResult: GetEventsHistoryResult = channel01.getMessageReportsHistory().await() assertEquals(2, eventsHistoryResult.events.size) @@ -638,6 +643,7 @@ class ChannelIntegrationTest : BaseChatIntegrationTest() { val messageText = "message1" val pnPublishResult = channel01.sendText(text = messageText).await() val timetoken = pnPublishResult.timetoken + delayForHistory() val message = channel01.getMessage(timetoken).await()!! val assertionErrorInCallback = CompletableDeferred() diff --git a/pubnub-chat-impl/src/commonTest/kotlin/com/pubnub/integration/ChatIntegrationTest.kt b/pubnub-chat-impl/src/commonTest/kotlin/com/pubnub/integration/ChatIntegrationTest.kt index a46bbce8..cabab6b9 100644 --- a/pubnub-chat-impl/src/commonTest/kotlin/com/pubnub/integration/ChatIntegrationTest.kt +++ b/pubnub-chat-impl/src/commonTest/kotlin/com/pubnub/integration/ChatIntegrationTest.kt @@ -40,6 +40,7 @@ import com.pubnub.kmp.createPubNub import com.pubnub.test.await import com.pubnub.test.randomString import com.pubnub.test.test +import delayForHistory import kotlinx.coroutines.CompletableDeferred import kotlinx.coroutines.test.runTest import tryLong @@ -234,6 +235,7 @@ class ChatIntegrationTest : BaseChatIntegrationTest() { } // then + delayForHistory() val markAllMessageAsReadResponse: MarkAllMessageAsReadResponse = chat.markAllMessagesAsRead().await() // verify response contains updated "lastReadMessageTimetoken" @@ -276,11 +278,10 @@ class ChatIntegrationTest : BaseChatIntegrationTest() { // send message channel01.sendText("message01In$channelId01").await() - delayInMillis(150) // history calls have around 130ms of cache time - // join (implicitly setLastReadMessageTimetoken) val joinResult: JoinResult = channel01.join { }.await() val membership = joinResult.membership + delayForHistory() val unreadMessageCount: Long? = membership.getUnreadMessagesCount().await() assertEquals(0, unreadMessageCount) @@ -292,10 +293,11 @@ class ChatIntegrationTest : BaseChatIntegrationTest() { assertEquals(1L, unreadMessageCount02) // markAllMessagesAsRead + delayForHistory() val markAllMessageAsReadResponse: MarkAllMessageAsReadResponse = chat.markAllMessagesAsRead().await() val membershipWithUpgradeLastReadMessageTimetoken = markAllMessageAsReadResponse.memberships.first() - delayInMillis(1500) + delayForHistory() val unreadMessageCount03: Long? = membershipWithUpgradeLastReadMessageTimetoken.getUnreadMessagesCount().await() assertEquals(0, unreadMessageCount03) @@ -316,7 +318,7 @@ class ChatIntegrationTest : BaseChatIntegrationTest() { // send message channel01.sendText("message01In$channelId01").await() channel02.sendText("message01In$channelId02").await() - delayInMillis(1500) // history calls have around 130ms of cache time + delayForHistory() // read message count var unreadMessagesCounts = chat.getUnreadMessagesCounts().await() @@ -330,10 +332,10 @@ class ChatIntegrationTest : BaseChatIntegrationTest() { assertEquals(1, unreadMessagesCountsForChannel02) // markUnread + delayForHistory() chat.markAllMessagesAsRead().await() - delayInMillis(6000) // history calls have around 130ms of cache time - // todo not sure why 5s is needed here but without it test doesn't pass in most cases. What can take so long? markAllMessagesAsRead method sets Membership. Does it take so long to propagate? + delayForHistory() // read message count unreadMessagesCounts = chat.getUnreadMessagesCounts().await() unreadMessagesCountsForChannel01 = @@ -452,6 +454,7 @@ class ChatIntegrationTest : BaseChatIntegrationTest() { channel01.sendText("message03In$channelId01").await() // when + delayForHistory() val eventsForUser: GetEventsHistoryResult = chat.getEventsHistory(channelId = userId, count = count).await() val messageEvents = chat.getEventsHistory(channelId = channelId01, count = count).await() @@ -479,7 +482,7 @@ class ChatIntegrationTest : BaseChatIntegrationTest() { // send messages with user mentions channel01.sendText(text = message, mentionedUsers = messageMentionedUsers).await() - delayInMillis(1000) + delayForHistory() // when val currentUserMentionsResult: GetCurrentUserMentionsResult = chat.getCurrentUserMentions().await() @@ -506,7 +509,7 @@ class ChatIntegrationTest : BaseChatIntegrationTest() { // send messages with user mentions threadChannel.sendText(text = message, mentionedUsers = messageMentionedUsers).await() - delayInMillis(1500) + delayForHistory() // when val currentUserMentionsResult: GetCurrentUserMentionsResult = chat.getCurrentUserMentions().await() @@ -540,7 +543,7 @@ class ChatIntegrationTest : BaseChatIntegrationTest() { assertFalse(channel.getMembers().await().members.any { it.user.id == chat.currentUser.id }) unsubscribe?.close() } - delayInMillis(1000) + delayForHistory() val eventFromHistory = chat.getEventsHistory(channel.id, tt + 1, tt).await().events.first() require(eventFromHistory.payload is EventContent.Custom) assertEquals(mapOf("abc" to "def"), (eventFromHistory.payload as EventContent.Custom).data) @@ -591,8 +594,7 @@ class ChatIntegrationTest : BaseChatIntegrationTest() { } } - delayInMillis(3000) // todo consider refactor or creating group of long running test - + delayForHistory() val eventFromHistory = chat.getEventsHistory(channelId, tt + 1, tt).await().events.first() val payload: EventContent.Custom = eventFromHistory.payload as EventContent.Custom val customEventData = payload.data diff --git a/pubnub-chat-impl/src/commonTest/kotlin/com/pubnub/integration/MessageIntegrationTest.kt b/pubnub-chat-impl/src/commonTest/kotlin/com/pubnub/integration/MessageIntegrationTest.kt index a044177d..bec5efd2 100644 --- a/pubnub-chat-impl/src/commonTest/kotlin/com/pubnub/integration/MessageIntegrationTest.kt +++ b/pubnub-chat-impl/src/commonTest/kotlin/com/pubnub/integration/MessageIntegrationTest.kt @@ -20,12 +20,14 @@ import com.pubnub.kmp.createCustomObject import com.pubnub.test.await import com.pubnub.test.randomString import com.pubnub.test.test +import delayForHistory import kotlinx.coroutines.CompletableDeferred import kotlinx.coroutines.test.runTest import kotlin.test.Test import kotlin.test.assertEquals import kotlin.test.assertFalse import kotlin.test.assertTrue +import kotlin.time.Duration.Companion.minutes class MessageIntegrationTest : BaseChatIntegrationTest() { @Test @@ -40,7 +42,7 @@ class MessageIntegrationTest : BaseChatIntegrationTest() { ) ).await() - delayInMillis(250) + delayForHistory() val message: Message = channel01.getMessage(tt.timetoken).await()!! assertEquals(1, message.files.size) val file = message.files.first() @@ -56,7 +58,7 @@ class MessageIntegrationTest : BaseChatIntegrationTest() { val messageText = "messageText_${randomString()}" val publishResult = channel01.sendText(text = messageText).await() val publishTimetoken = publishResult.timetoken - delayInMillis(200) + delayForHistory() val message: Message = channel01.getMessage(publishTimetoken).await()!! val deletedMessage = message.delete(soft = true).await()!! val restoredMessage = deletedMessage.restore().await() @@ -69,11 +71,12 @@ class MessageIntegrationTest : BaseChatIntegrationTest() { val reactionValue = "wow" val pnPublishResult = channel01.sendText(text = messageText).await() val publishTimetoken = pnPublishResult.timetoken + delayForHistory() val message: Message = channel01.getMessage(publishTimetoken).await()!! val threadChannel: ThreadChannel = message.createThread().await() // we need to call sendText because addMessageAction is called in sendText that stores details about thread threadChannel.sendText("message in thread_${randomString()}").await() - + delayForHistory() val history: HistoryResponse = threadChannel.getHistory().await() val messageWithThread = channel01.getMessage(publishTimetoken).await() @@ -96,26 +99,25 @@ class MessageIntegrationTest : BaseChatIntegrationTest() { } @Test - fun createMessageWithThreadThenDeleteThread() = runTest { + fun createMessageWithThreadThenDeleteThread() = runTest(timeout = 100.minutes) { // create message with thread val messageText = "messageText_${randomString()}" val pnPublishResult = channel01.sendText(text = messageText).await() val publishTimetoken = pnPublishResult.timetoken - delayInMillis(300) + delayForHistory() val message: Message = channel01.getMessage(publishTimetoken).await()!! val threadChannel: ThreadChannel = message.createThread().await() // we need to call sendText because addMessageAction is called in sendText that stores details about thread threadChannel.sendText("message in thread_${randomString()}").await() - delayInMillis(1500) + delayForHistory() // we need to call getMessage to get message with indication that it hasThread val messageWithThread: Message = channel01.getMessage(publishTimetoken).await()!! assertTrue(messageWithThread.hasThread) messageWithThread.removeThread().await() - delayInMillis(300) - + delayForHistory() // we need to call getMessage to get message with indication that it has no Thread val messageWithNoThread: Message = channel01.getMessage(publishTimetoken).await()!! @@ -135,7 +137,7 @@ class MessageIntegrationTest : BaseChatIntegrationTest() { val tt1 = channel01.sendText("message1").await() val tt2 = channel01.sendText("message2").await() - delayInMillis(1000) + delayForHistory() val message1 = channel01.getMessage(tt1.timetoken).await()!! val message2 = channel01.getMessage(tt2.timetoken).await()!! @@ -202,13 +204,14 @@ class MessageIntegrationTest : BaseChatIntegrationTest() { val messageText = "messageText_${randomString()}" val pnPublishResult = channel01.sendText(text = messageText).await() val publishTimetoken = pnPublishResult.timetoken + delayForHistory() val message: Message = channel01.getMessage(publishTimetoken).await()!! val messageWithReaction = message.toggleReaction(reactionValue).await() assertTrue(messageWithReaction.hasUserReaction(reactionValue)) - delayInMillis(1000) + delayForHistory() val messageWithReactionFromHistory: Message = channel01.getHistory(publishTimetoken + 1, publishTimetoken).await().messages.first() assertTrue(messageWithReactionFromHistory.hasUserReaction(reactionValue)) @@ -242,6 +245,7 @@ class MessageIntegrationTest : BaseChatIntegrationTest() { ) } // when + delayForHistory() val message: Message = channel01.getMessage(timetoken).await()!! message.report(reason).await() diff --git a/pubnub-chat-impl/src/commonTest/kotlin/com/pubnub/internal/testutils.kt b/pubnub-chat-impl/src/commonTest/kotlin/com/pubnub/internal/testutils.kt new file mode 100644 index 00000000..0dbaeb32 --- /dev/null +++ b/pubnub-chat-impl/src/commonTest/kotlin/com/pubnub/internal/testutils.kt @@ -0,0 +1,8 @@ +import kotlinx.coroutines.Dispatchers +import kotlinx.coroutines.delay +import kotlinx.coroutines.withContext +import kotlin.time.Duration.Companion.seconds + +suspend fun delayForHistory() = withContext(Dispatchers.Default) { + delay(1.seconds) +} diff --git a/pubnub-chat-impl/src/commonTest/kotlin/com/pubnub/kmp/ChannelTest.kt b/pubnub-chat-impl/src/commonTest/kotlin/com/pubnub/kmp/ChannelTest.kt index 57384916..54cdd5f1 100644 --- a/pubnub-chat-impl/src/commonTest/kotlin/com/pubnub/kmp/ChannelTest.kt +++ b/pubnub-chat-impl/src/commonTest/kotlin/com/pubnub/kmp/ChannelTest.kt @@ -18,6 +18,8 @@ import com.pubnub.api.models.consumer.objects.PNSortKey import com.pubnub.api.models.consumer.objects.channel.PNChannelMetadata import com.pubnub.api.models.consumer.objects.channel.PNChannelMetadataResult import com.pubnub.api.models.consumer.objects.member.PNUUIDDetailsLevel +import com.pubnub.api.utils.Clock +import com.pubnub.api.utils.Instant import com.pubnub.api.utils.PatchValue import com.pubnub.api.v2.callbacks.Consumer import com.pubnub.api.v2.callbacks.Result @@ -58,8 +60,6 @@ import dev.mokkery.mock import dev.mokkery.verify import dev.mokkery.verify.VerifyMode.Companion.exactly import kotlinx.coroutines.test.runTest -import kotlinx.datetime.Clock -import kotlinx.datetime.Instant import kotlin.test.BeforeTest import kotlin.test.Test import kotlin.test.assertEquals diff --git a/pubnub-chat-impl/src/commonTest/kotlin/com/pubnub/kmp/ThreadChannelTest.kt b/pubnub-chat-impl/src/commonTest/kotlin/com/pubnub/kmp/ThreadChannelTest.kt index 9755a308..ad29eb3f 100644 --- a/pubnub-chat-impl/src/commonTest/kotlin/com/pubnub/kmp/ThreadChannelTest.kt +++ b/pubnub-chat-impl/src/commonTest/kotlin/com/pubnub/kmp/ThreadChannelTest.kt @@ -1,5 +1,6 @@ package com.pubnub.kmp +import com.pubnub.api.utils.Clock import com.pubnub.api.v2.callbacks.Result import com.pubnub.chat.Channel import com.pubnub.chat.Message @@ -12,7 +13,6 @@ import dev.mokkery.MockMode import dev.mokkery.answering.returns import dev.mokkery.every import dev.mokkery.mock -import kotlinx.datetime.Clock import kotlin.test.BeforeTest import kotlin.test.Test import kotlin.test.assertEquals diff --git a/pubnub-chat-impl/src/commonTest/kotlin/com/pubnub/kmp/utils/ExponentialRateLimiterTest.kt b/pubnub-chat-impl/src/commonTest/kotlin/com/pubnub/kmp/utils/ExponentialRateLimiterTest.kt index 2aceec0e..01581d1d 100644 --- a/pubnub-chat-impl/src/commonTest/kotlin/com/pubnub/kmp/utils/ExponentialRateLimiterTest.kt +++ b/pubnub-chat-impl/src/commonTest/kotlin/com/pubnub/kmp/utils/ExponentialRateLimiterTest.kt @@ -1,5 +1,7 @@ package com.pubnub.kmp.utils +import com.pubnub.api.utils.Clock +import com.pubnub.api.utils.Instant import com.pubnub.chat.internal.timer.createTimerManager import com.pubnub.chat.internal.utils.ExponentialRateLimiter import com.pubnub.kmp.PNFuture @@ -10,8 +12,6 @@ import kotlinx.coroutines.Dispatchers import kotlinx.coroutines.delay import kotlinx.coroutines.test.runTest import kotlinx.coroutines.withContext -import kotlinx.datetime.Clock -import kotlinx.datetime.Instant import kotlin.test.Test import kotlin.test.assertContains import kotlin.time.Duration diff --git a/pubnub-kotlin b/pubnub-kotlin index 50a9202c..e117611c 160000 --- a/pubnub-kotlin +++ b/pubnub-kotlin @@ -1 +1 @@ -Subproject commit 50a9202c630875c40317d6ead2a42e54b2d06328 +Subproject commit e117611cb3c2fb65cc013736fafa3faa48e5cc6f