Movatterモバイル変換


[0]ホーム

URL:


Skip to content

Navigation Menu

Sign in
Appearance settings

Search code, repositories, users, issues, pull requests...

Provide feedback

We read every piece of feedback, and take your input very seriously.

Saved searches

Use saved searches to filter your results more quickly

Sign up
Appearance settings

Commit375b8ca

Browse files
authored
feat: add query profiling and cached paginated list [WPB-14826] (#3726)
1 parenta346709 commit375b8ca

File tree

6 files changed

+258
-3
lines changed

6 files changed

+258
-3
lines changed

‎app/src/main/kotlin/com/wire/android/WireApplication.kt

Lines changed: 8 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -29,6 +29,7 @@ import co.touchlab.kermit.platformLogWriter
2929
importcom.wire.android.analytics.ObserveCurrentSessionAnalyticsUseCase
3030
importcom.wire.android.datastore.GlobalDataStore
3131
importcom.wire.android.datastore.UserDataStoreProvider
32+
importcom.wire.android.debug.DatabaseProfilingManager
3233
importcom.wire.android.di.ApplicationScope
3334
importcom.wire.android.di.KaliumCoreLogic
3435
importcom.wire.android.feature.analytics.AnonymousAnalyticsManagerImpl
@@ -89,6 +90,9 @@ class WireApplication : BaseApp() {
8990
@Inject
9091
lateinitvar currentScreenManager:CurrentScreenManager
9192

93+
@Inject
94+
lateinitvar databaseProfilingManager:DatabaseProfilingManager
95+
9296
overrideval workManagerConfiguration:Configuration
9397
get()=Configuration.Builder()
9498
.setWorkerFactory(wireWorkerFactory.get())
@@ -183,6 +187,10 @@ class WireApplication : BaseApp() {
183187
logDeviceInformation()
184188
// 5. Verify if we can initialize Anonymous Analytics
185189
initializeAnonymousAnalytics()
190+
// 6. Observe and update profiling when needed
191+
globalAppScope.launch {
192+
databaseProfilingManager.observeAndUpdateProfiling()
193+
}
186194
}
187195

188196
privatefuninitializeAnonymousAnalytics() {
Lines changed: 56 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,56 @@
1+
/*
2+
* Wire
3+
* Copyright (C) 2024 Wire Swiss GmbH
4+
*
5+
* This program is free software: you can redistribute it and/or modify
6+
* it under the terms of the GNU General Public License as published by
7+
* the Free Software Foundation, either version 3 of the License, or
8+
* (at your option) any later version.
9+
*
10+
* This program is distributed in the hope that it will be useful,
11+
* but WITHOUT ANY WARRANTY; without even the implied warranty of
12+
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
13+
* GNU General Public License for more details.
14+
*
15+
* You should have received a copy of the GNU General Public License
16+
* along with this program. If not, see http://www.gnu.org/licenses/.
17+
*/
18+
packagecom.wire.android.debug
19+
20+
importcom.wire.android.datastore.GlobalDataStore
21+
importcom.wire.android.di.KaliumCoreLogic
22+
importcom.wire.kalium.logic.CoreLogic
23+
importcom.wire.kalium.logic.data.user.UserId
24+
importcom.wire.kalium.logic.functional.mapToRightOr
25+
importkotlinx.coroutines.flow.distinctUntilChanged
26+
importkotlinx.coroutines.flow.filter
27+
importkotlinx.coroutines.flow.flatMapLatest
28+
importkotlinx.coroutines.flow.map
29+
importkotlinx.coroutines.flow.scan
30+
importjavax.inject.Inject
31+
importjavax.inject.Singleton
32+
33+
@Singleton
34+
classDatabaseProfilingManager @Inject constructor(
35+
@KaliumCoreLogicprivatevalcoreLogic:CoreLogic,
36+
privatevalglobalDataStore:GlobalDataStore,
37+
) {
38+
39+
suspendfunobserveAndUpdateProfiling() {
40+
globalDataStore.isLoggingEnabled()
41+
.flatMapLatest { isLoggingEnabled->
42+
coreLogic.getGlobalScope().sessionRepository.allValidSessionsFlow()
43+
.mapToRightOr(emptyList())
44+
.map { it.map { it.userId } }
45+
.scan(emptyList<UserId>()) { previousList, currentList-> currentList- previousList.toSet() }
46+
.map { userIds-> isLoggingEnabled to userIds }
47+
}
48+
.filter { (_, userIds)-> userIds.isNotEmpty() }
49+
.distinctUntilChanged()
50+
.collect { (isLoggingEnabled, userIds)->
51+
userIds.forEach { userId->
52+
coreLogic.getSessionScope(userId).debug.changeProfiling(isLoggingEnabled)
53+
}
54+
}
55+
}
56+
}

‎app/src/main/kotlin/com/wire/android/ui/home/conversationslist/ConversationListViewModel.kt

Lines changed: 4 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -24,6 +24,7 @@ import androidx.compose.runtime.setValue
2424
importandroidx.lifecycle.ViewModel
2525
importandroidx.lifecycle.viewModelScope
2626
importandroidx.paging.PagingData
27+
importandroidx.paging.cachedIn
2728
importandroidx.paging.insertSeparators
2829
importandroidx.paging.map
2930
importcom.wire.android.BuildConfig
@@ -211,10 +212,11 @@ class ConversationListViewModelImpl @AssistedInject constructor(
211212
}
212213
}
213214
.flowOn(dispatcher.io())
215+
.cachedIn(viewModelScope)
214216

215217
privatevar notPaginatedConversationListState by mutableStateOf(ConversationListState.NotPaginated())
216-
overrideval conversationListState:ConversationListState
217-
get()=if (usePagination) {
218+
overrideval conversationListState:ConversationListState=
219+
if (usePagination) {
218220
ConversationListState.Paginated(
219221
conversations= conversationsPaginatedFlow,
220222
domain= currentAccount.domain
Lines changed: 187 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,187 @@
1+
/*
2+
* Wire
3+
* Copyright (C) 2024 Wire Swiss GmbH
4+
*
5+
* This program is free software: you can redistribute it and/or modify
6+
* it under the terms of the GNU General Public License as published by
7+
* the Free Software Foundation, either version 3 of the License, or
8+
* (at your option) any later version.
9+
*
10+
* This program is distributed in the hope that it will be useful,
11+
* but WITHOUT ANY WARRANTY; without even the implied warranty of
12+
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
13+
* GNU General Public License for more details.
14+
*
15+
* You should have received a copy of the GNU General Public License
16+
* along with this program. If not, see http://www.gnu.org/licenses/.
17+
*/
18+
packagecom.wire.android.debug
19+
20+
importcom.wire.android.config.CoroutineTestExtension
21+
importcom.wire.android.datastore.GlobalDataStore
22+
importcom.wire.kalium.logic.CoreLogic
23+
importcom.wire.kalium.logic.StorageFailure
24+
importcom.wire.kalium.logic.data.auth.AccountInfo
25+
importcom.wire.kalium.logic.data.user.UserId
26+
importcom.wire.kalium.logic.functional.Either
27+
importio.mockk.MockKAnnotations
28+
importio.mockk.coEvery
29+
importio.mockk.impl.annotations.MockK
30+
importio.mockk.mockk
31+
importkotlinx.collections.immutable.PersistentMap
32+
importkotlinx.collections.immutable.persistentMapOf
33+
importkotlinx.coroutines.flow.Flow
34+
importkotlinx.coroutines.flow.MutableStateFlow
35+
importkotlinx.coroutines.flow.flowOf
36+
importkotlinx.coroutines.launch
37+
importkotlinx.coroutines.test.advanceUntilIdle
38+
importkotlinx.coroutines.test.runTest
39+
importorg.amshove.kluent.internal.assertEquals
40+
importorg.junit.jupiter.api.Test
41+
importorg.junit.jupiter.api.extension.ExtendWith
42+
43+
@ExtendWith(CoroutineTestExtension::class)
44+
classDatabaseProfilingManagerTest {
45+
46+
@Test
47+
fun`given valid session and logging enabled, when observing, then profiling should be enabled`()=
48+
runTest {
49+
// given
50+
val account=AccountInfo.Valid(UserId("user","domain"))
51+
val (arrangement, databaseProfilingManager)=Arrangement()
52+
.withAllValidSessions(flowOf(Either.Right(listOf(account))))
53+
.withIsLoggingEnabled(flowOf(true))
54+
.arrange()
55+
56+
// when
57+
val job= launch {
58+
databaseProfilingManager.observeAndUpdateProfiling()
59+
}
60+
advanceUntilIdle()
61+
// then
62+
assertEquals(true, arrangement.profilingValues[account.userId])
63+
job.cancel()
64+
}
65+
66+
@Test
67+
fun`given valid session and logging disabled, when observing, then profiling is disabled`()=
68+
runTest {
69+
// given
70+
val account=AccountInfo.Valid(UserId("user","domain"))
71+
val (arrangement, databaseProfilingManager)=Arrangement()
72+
.withAllValidSessions(flowOf(Either.Right(listOf(account))))
73+
.withIsLoggingEnabled(flowOf(false))
74+
.arrange()
75+
// when
76+
val job= launch {
77+
databaseProfilingManager.observeAndUpdateProfiling()
78+
}
79+
advanceUntilIdle()
80+
// then
81+
assertEquals(false, arrangement.profilingValues[account.userId])
82+
job.cancel()
83+
}
84+
85+
@Test
86+
fun`given valid session, when observing and logging changes from disabled to enabled, then profiling is enabled`()=
87+
runTest {
88+
// given
89+
val account=AccountInfo.Valid(UserId("user","domain"))
90+
val (arrangement, databaseProfilingManager)=Arrangement()
91+
.withAllValidSessions(flowOf(Either.Right(listOf(account))))
92+
.withIsLoggingEnabled(flowOf(false))
93+
.arrange()
94+
// when
95+
val job= launch {
96+
databaseProfilingManager.observeAndUpdateProfiling()
97+
}
98+
arrangement.withIsLoggingEnabled(flowOf(true))
99+
advanceUntilIdle()
100+
// then
101+
assertEquals(true, arrangement.profilingValues[account.userId])
102+
job.cancel()
103+
}
104+
105+
@Test
106+
fun`given two valid sessions, when observing and logging changes from disabled to enabled, then profiling is enabled for both`()=
107+
runTest {
108+
// given
109+
val account1=AccountInfo.Valid(UserId("user1","domain"))
110+
val account2=AccountInfo.Valid(UserId("user2","domain"))
111+
val (arrangement, databaseProfilingManager)=Arrangement()
112+
.withAllValidSessions(flowOf(Either.Right(listOf(account1, account2))))
113+
.withIsLoggingEnabled(flowOf(false))
114+
.arrange()
115+
// when
116+
val job= launch {
117+
databaseProfilingManager.observeAndUpdateProfiling()
118+
}
119+
arrangement.withIsLoggingEnabled(flowOf(true))
120+
advanceUntilIdle()
121+
// then
122+
assertEquals(true, arrangement.profilingValues[account1.userId])
123+
assertEquals(true, arrangement.profilingValues[account2.userId])
124+
job.cancel()
125+
}
126+
127+
@Test
128+
fun`given valid session and logging enabled, when observing and new session appears, then profiling is enabled for both`()=
129+
runTest {
130+
// given
131+
val account1=AccountInfo.Valid(UserId("user1","domain"))
132+
val account2=AccountInfo.Valid(UserId("user2","domain"))
133+
val validSessionsFlow=MutableStateFlow(Either.Right(listOf(account1)))
134+
val (arrangement, databaseProfilingManager)=Arrangement()
135+
.withAllValidSessions(validSessionsFlow)
136+
.withIsLoggingEnabled(flowOf(true))
137+
.arrange()
138+
// when
139+
val job= launch {
140+
databaseProfilingManager.observeAndUpdateProfiling()
141+
}
142+
validSessionsFlow.value=Either.Right(listOf(account1, account2))
143+
advanceUntilIdle()
144+
// then
145+
assertEquals(true, arrangement.profilingValues[account1.userId])
146+
assertEquals(true, arrangement.profilingValues[account2.userId])
147+
job.cancel()
148+
}
149+
150+
privateclassArrangement {
151+
152+
@MockK
153+
lateinitvar coreLogic:CoreLogic
154+
155+
@MockK
156+
privatelateinitvar globalDataStore:GlobalDataStore
157+
158+
var profilingValues:PersistentMap<UserId,Boolean>= persistentMapOf()
159+
private set
160+
161+
init {
162+
MockKAnnotations.init(this, relaxed=true, relaxUnitFun=true)
163+
coEvery { coreLogic.getSessionScope(any()).debug.changeProfiling(any()) } answers {
164+
profilingValues= profilingValues.put(firstArg(), secondArg())
165+
}
166+
coEvery { coreLogic.getSessionScope(any()) } answers {
167+
val userId= firstArg<UserId>()
168+
mockk {
169+
coEvery { debug.changeProfiling(any()) } answers {
170+
val profilingValue= firstArg<Boolean>()
171+
profilingValues= profilingValues.put(userId, profilingValue)
172+
}
173+
}
174+
}
175+
}
176+
177+
funwithIsLoggingEnabled(isLoggingEnabledFlow:Flow<Boolean>)=apply {
178+
coEvery { globalDataStore.isLoggingEnabled() } returns isLoggingEnabledFlow
179+
}
180+
181+
funwithAllValidSessions(allValidSessionsFlow:Flow<Either<StorageFailure,List<AccountInfo>>>)=apply {
182+
coEvery { coreLogic.getGlobalScope().sessionRepository.allValidSessionsFlow() } returns allValidSessionsFlow
183+
}
184+
185+
funarrange()=this toDatabaseProfilingManager(coreLogic, globalDataStore)
186+
}
187+
}

‎app/src/test/kotlin/com/wire/android/ui/CallActivityViewModelTest.kt

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -117,6 +117,7 @@ class CallActivityViewModelTest {
117117
.arrange()
118118

119119
viewModel.switchAccountIfNeeded(userId, arrangement.switchAccountActions)
120+
advanceUntilIdle()
120121

121122
coVerify(inverse=true) { arrangement.accountSwitch(any()) }
122123
}
@@ -132,6 +133,7 @@ class CallActivityViewModelTest {
132133
.arrange()
133134

134135
viewModel.switchAccountIfNeeded(UserId("anotherUser","domain"), arrangement.switchAccountActions)
136+
advanceUntilIdle()
135137

136138
coVerify(exactly=if (switchedToAnotherAccountCalled)1else0) {
137139
arrangement.switchAccountActions.switchedToAnotherAccount()

‎kalium

Submodulekalium updated9 files

0 commit comments

Comments
 (0)

[8]ページ先頭

©2009-2025 Movatter.jp