Skip to content

Commit 9b6540e

Browse files
authored
Wenxi/suspendable analytics (#50)
* init changes to make sdk suspendable * get ride of singleton dispatchers * fix configuration issue * fix unit test errors * fix android errors * fix android unit tests errors * fix destination unit tests errors * remove weak reference from SegmentDestination.kt * update settings to use withContext * refactor dispatcher to be injectable * fix core unit tests * fix android unit tests * fix destination unit tests * separate base data for accuracy * fix unit test * add blocking api warning * fix JavaAnalytics.kt * refactor annotation class to a separate file * update sovran version * add comments to constructors
1 parent acebd83 commit 9b6540e

File tree

32 files changed

+419
-283
lines changed

32 files changed

+419
-283
lines changed

android/build.gradle

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -46,7 +46,7 @@ android {
4646
dependencies {
4747
// MAIN DEPS
4848
api project(':core')
49-
api 'com.github.segmentio:sovran-kotlin:0.1.0'
49+
api 'com.github.segmentio:sovran-kotlin:1.1.0'
5050
api "org.jetbrains.kotlinx:kotlinx-serialization-json:1.0.1"
5151
implementation 'org.jetbrains.kotlinx:kotlinx-coroutines-core:1.3.9'
5252
implementation 'org.jetbrains.kotlinx:kotlinx-coroutines-android:1.3.9'

android/src/main/java/com/segment/analytics/kotlin/android/Storage.kt

Lines changed: 5 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -29,18 +29,20 @@ class AndroidStorage(
2929
internal val eventsFile =
3030
EventsFileManager(storageDirectory, writeKey, AndroidKVS(sharedPreferences))
3131

32-
override fun subscribeToStore() {
32+
override suspend fun subscribeToStore() {
3333
store.subscribe(
3434
this,
3535
UserInfo::class,
3636
initialState = true,
37-
handler = ::userInfoUpdate
37+
handler = ::userInfoUpdate,
38+
queue = ioDispatcher
3839
)
3940
store.subscribe(
4041
this,
4142
System::class,
4243
initialState = true,
43-
handler = ::systemUpdate
44+
handler = ::systemUpdate,
45+
queue = ioDispatcher
4446
)
4547
}
4648

android/src/main/java/com/segment/analytics/kotlin/android/plugins/AndroidLifecyclePlugin.kt

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -73,7 +73,7 @@ class AndroidLifecyclePlugin() : Application.ActivityLifecycleCallbacks, Default
7373
}
7474

7575
private fun runOnAnalyticsThread(block: () -> Unit) = with(analytics) {
76-
analyticsScope.launch(processingDispatcher) {
76+
analyticsScope.launch(analyticsDispatcher) {
7777
block()
7878
}
7979
}

android/src/test/java/com/segment/analytics/kotlin/android/AndroidLifecyclePluginTests.kt

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -12,6 +12,7 @@ import com.segment.analytics.kotlin.android.utils.TestRunPlugin
1212
import com.segment.analytics.kotlin.core.platform.Plugin
1313
import com.segment.analytics.kotlin.android.plugins.AndroidLifecycle
1414
import com.segment.analytics.kotlin.android.plugins.AndroidLifecyclePlugin
15+
import com.segment.analytics.kotlin.android.utils.mockHTTPClient
1516
import io.mockk.*
1617
import kotlinx.serialization.json.buildJsonObject
1718
import kotlinx.serialization.json.put
@@ -47,6 +48,8 @@ class AndroidLifecyclePluginTests {
4748
every { Instant.now() } returns Date(0).toInstant()
4849
mockkStatic(UUID::class)
4950
every { UUID.randomUUID().toString() } returns "qwerty-qwerty-123"
51+
52+
mockHTTPClient()
5053
}
5154

5255
@Before

android/src/test/java/com/segment/analytics/kotlin/android/StorageTests.kt

Lines changed: 4 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -7,6 +7,7 @@ import com.segment.analytics.kotlin.android.utils.MemorySharedPreferences
77
import com.segment.analytics.kotlin.android.utils.clearPersistentStorage
88
import com.segment.analytics.kotlin.android.utils.mockAnalytics
99
import com.segment.analytics.kotlin.android.utils.mockContext
10+
import kotlinx.coroutines.runBlocking
1011
import kotlinx.coroutines.test.TestCoroutineDispatcher
1112
import kotlinx.coroutines.test.runBlockingTest
1213
import kotlinx.serialization.decodeFromString
@@ -33,7 +34,7 @@ class StorageTests {
3334
private var mockContext: Context = mockContext()
3435

3536
@BeforeEach
36-
fun setup() {
37+
fun setup() = runBlocking {
3738
clearPersistentStorage()
3839
store.provide(
3940
UserInfo(
@@ -62,7 +63,7 @@ class StorageTests {
6263

6364

6465
@Test
65-
fun `userInfo update calls write`() = runBlockingTest {
66+
fun `userInfo update calls write`() = runBlocking {
6667
val action = object : Action<UserInfo> {
6768
override fun reduce(state: UserInfo): UserInfo {
6869
return UserInfo(
@@ -83,7 +84,7 @@ class StorageTests {
8384
}
8485

8586
@Test
86-
fun `system update calls write for settings`() = runBlockingTest {
87+
fun `system update calls write for settings`() = runBlocking {
8788
val action = object : Action<System> {
8889
override fun reduce(state: System): System {
8990
return System(

android/src/test/java/com/segment/analytics/kotlin/android/utils/Mocks.kt

Lines changed: 38 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -3,17 +3,33 @@ package com.segment.analytics.kotlin.android.utils
33
import android.content.Context
44
import android.content.pm.PackageInfo
55
import android.content.pm.PackageManager
6-
import com.segment.analytics.kotlin.android.utils.MemorySharedPreferences
76
import com.segment.analytics.kotlin.core.Analytics
7+
import com.segment.analytics.kotlin.core.Connection
8+
import com.segment.analytics.kotlin.core.HTTPClient
89
import io.mockk.every
910
import io.mockk.mockk
11+
import io.mockk.mockkConstructor
12+
import io.mockk.spyk
13+
import kotlinx.coroutines.CoroutineDispatcher
14+
import kotlinx.coroutines.CoroutineScope
15+
import kotlinx.coroutines.test.TestCoroutineDispatcher
16+
import kotlinx.coroutines.test.TestCoroutineScope
1017
import sovran.kotlin.Store
18+
import java.io.ByteArrayInputStream
1119
import java.io.File
20+
import java.net.HttpURLConnection
21+
import kotlin.coroutines.CoroutineContext
1222

1323
fun mockAnalytics(): Analytics {
1424
val mock = mockk<Analytics>(relaxed = true)
15-
val mockStore = Store()
25+
val scope = TestCoroutineScope()
26+
val dispatcher = TestCoroutineDispatcher()
27+
val mockStore = spyStore(scope, dispatcher)
1628
every { mock.store } returns mockStore
29+
every { mock.analyticsScope } returns scope
30+
every { mock.fileIODispatcher } returns dispatcher
31+
every { mock.networkIODispatcher } returns dispatcher
32+
every { mock.analyticsDispatcher } returns dispatcher
1733
return mock
1834
}
1935

@@ -37,3 +53,23 @@ fun mockContext(): Context {
3753
fun clearPersistentStorage() {
3854
File("/tmp/analytics-android-test/").deleteRecursively()
3955
}
56+
57+
fun spyStore(scope: CoroutineScope, dispatcher: CoroutineDispatcher): Store {
58+
val store = spyk(Store())
59+
every { store getProperty "sovranScope" } propertyType CoroutineScope::class returns scope
60+
every { store getProperty "syncQueue" } propertyType CoroutineContext::class returns dispatcher
61+
every { store getProperty "updateQueue" } propertyType CoroutineContext::class returns dispatcher
62+
return store
63+
}
64+
65+
fun mockHTTPClient() {
66+
mockkConstructor(HTTPClient::class)
67+
val settingsStream = ByteArrayInputStream(
68+
"""
69+
{"integrations":{"Segment.io":{"apiKey":"1vNgUqwJeCHmqgI9S1sOm9UHCyfYqbaQ"}},"plan":{},"edgeFunction":{}}
70+
""".trimIndent().toByteArray()
71+
)
72+
val httpConnection: HttpURLConnection = mockk()
73+
val connection = object : Connection(httpConnection, settingsStream, null) {}
74+
every { anyConstructed<HTTPClient>().settings("cdn-settings.segment.com/v1") } returns connection
75+
}

core/build.gradle

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -16,7 +16,7 @@ test {
1616
dependencies {
1717
implementation "org.jetbrains.kotlin:kotlin-stdlib:$kotlin_version"
1818
// MAIN DEPS
19-
api 'com.github.segmentio:sovran-kotlin:1.0.0'
19+
api 'com.github.segmentio:sovran-kotlin:1.1.0'
2020
api "org.jetbrains.kotlinx:kotlinx-serialization-json:1.0.1"
2121
implementation 'org.jetbrains.kotlinx:kotlinx-coroutines-core:1.3.9'
2222

0 commit comments

Comments
 (0)