Merge branch 'main' into tm/metrics-one-folder

Change-Id: I00231b47daafbeb06c3435281a5c9864b46c8f54
pull/754/head
Tomáš Mlynarič 2 years ago
commit 5b8453b8d9

@ -100,7 +100,7 @@ jobs:
disable-animations: true disable-animations: true
disk-size: 6000M disk-size: 6000M
heap-size: 600M heap-size: 600M
script: ./gradlew connectedDemoDebugAndroidTest -x :benchmark:connectedDemoBenchmarkAndroidTest --daemon script: ./gradlew connectedDemoDebugAndroidTest --daemon
- name: Upload test reports - name: Upload test reports
if: always() if: always()

@ -0,0 +1,44 @@
/*
* Copyright 2023 The Android Open Source Project
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* https://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package com.google.samples.apps.nowinandroid
import android.Manifest.permission
import android.os.Build.VERSION.SDK_INT
import android.os.Build.VERSION_CODES.TIRAMISU
import androidx.benchmark.macro.MacrobenchmarkScope
/**
* Because the app under test is different from the one running the instrumentation test,
* the permission has to be granted manually by either:
*
* - tapping the Allow button
* ```kotlin
* val obj = By.text("Allow")
* val dialog = device.wait(Until.findObject(obj), TIMEOUT)
* dialog?.let {
* it.click()
* device.wait(Until.gone(obj), 5_000)
* }
* ```
* - or (preferred) executing the grant command on the target package.
*/
fun MacrobenchmarkScope.allowNotifications() {
if (SDK_INT >= TIRAMISU) {
val command = "pm grant $packageName ${permission.POST_NOTIFICATIONS}"
device.executeShellCommand(command)
}
}

@ -22,6 +22,7 @@ import androidx.benchmark.macro.StartupMode
import androidx.benchmark.macro.junit4.MacrobenchmarkRule import androidx.benchmark.macro.junit4.MacrobenchmarkRule
import androidx.test.internal.runner.junit4.AndroidJUnit4ClassRunner import androidx.test.internal.runner.junit4.AndroidJUnit4ClassRunner
import com.google.samples.apps.nowinandroid.PACKAGE_NAME import com.google.samples.apps.nowinandroid.PACKAGE_NAME
import com.google.samples.apps.nowinandroid.allowNotifications
import org.junit.Rule import org.junit.Rule
import org.junit.Test import org.junit.Test
import org.junit.runner.RunWith import org.junit.runner.RunWith
@ -47,6 +48,7 @@ class ScrollForYouFeedBenchmark {
// Start the app // Start the app
pressHome() pressHome()
startActivityAndWait() startActivityAndWait()
allowNotifications()
}, },
) { ) {
forYouWaitForContent() forYouWaitForContent()

@ -23,6 +23,7 @@ import androidx.benchmark.macro.junit4.MacrobenchmarkRule
import androidx.test.ext.junit.runners.AndroidJUnit4 import androidx.test.ext.junit.runners.AndroidJUnit4
import androidx.test.uiautomator.By import androidx.test.uiautomator.By
import com.google.samples.apps.nowinandroid.PACKAGE_NAME import com.google.samples.apps.nowinandroid.PACKAGE_NAME
import com.google.samples.apps.nowinandroid.allowNotifications
import org.junit.Rule import org.junit.Rule
import org.junit.Test import org.junit.Test
import org.junit.runner.RunWith import org.junit.runner.RunWith
@ -47,7 +48,7 @@ class TopicsScreenRecompositionBenchmark {
// Start the app // Start the app
pressHome() pressHome()
startActivityAndWait() startActivityAndWait()
allowNotifications()
// Navigate to interests screen // Navigate to interests screen
device.findObject(By.text("Interests")).click() device.findObject(By.text("Interests")).click()
device.waitForIdle() device.waitForIdle()

@ -17,7 +17,7 @@
package com.google.samples.apps.nowinandroid.core.network.di package com.google.samples.apps.nowinandroid.core.network.di
import com.google.samples.apps.nowinandroid.core.network.Dispatcher import com.google.samples.apps.nowinandroid.core.network.Dispatcher
import com.google.samples.apps.nowinandroid.core.network.NiaDispatchers.IO import com.google.samples.apps.nowinandroid.core.network.NiaDispatchers.Default
import dagger.Module import dagger.Module
import dagger.Provides import dagger.Provides
import dagger.hilt.InstallIn import dagger.hilt.InstallIn
@ -25,15 +25,20 @@ import dagger.hilt.components.SingletonComponent
import kotlinx.coroutines.CoroutineDispatcher import kotlinx.coroutines.CoroutineDispatcher
import kotlinx.coroutines.CoroutineScope import kotlinx.coroutines.CoroutineScope
import kotlinx.coroutines.SupervisorJob import kotlinx.coroutines.SupervisorJob
import javax.inject.Qualifier
import javax.inject.Singleton import javax.inject.Singleton
@Retention(AnnotationRetention.RUNTIME)
@Qualifier
annotation class ApplicationScope
@Module @Module
@InstallIn(SingletonComponent::class) @InstallIn(SingletonComponent::class)
object CoroutineScopesModule { object CoroutineScopesModule {
@Provides @Provides
@Singleton @Singleton
@Dispatcher(IO) @ApplicationScope
fun providesIOCoroutineScope( fun providesCoroutineScope(
@Dispatcher(IO) ioDispatcher: CoroutineDispatcher, @Dispatcher(Default) dispatcher: CoroutineDispatcher,
): CoroutineScope = CoroutineScope(SupervisorJob() + ioDispatcher) ): CoroutineScope = CoroutineScope(SupervisorJob() + dispatcher)
} }

@ -25,4 +25,5 @@ android {
dependencies { dependencies {
api(project(":core:data")) api(project(":core:data"))
implementation(project(":core:testing")) implementation(project(":core:testing"))
implementation(project(":core:common"))
} }

@ -20,6 +20,7 @@ import com.google.samples.apps.nowinandroid.core.database.dao.NewsResourceDao
import com.google.samples.apps.nowinandroid.core.database.dao.NewsResourceFtsDao import com.google.samples.apps.nowinandroid.core.database.dao.NewsResourceFtsDao
import com.google.samples.apps.nowinandroid.core.database.dao.TopicDao import com.google.samples.apps.nowinandroid.core.database.dao.TopicDao
import com.google.samples.apps.nowinandroid.core.database.dao.TopicFtsDao import com.google.samples.apps.nowinandroid.core.database.dao.TopicFtsDao
import com.google.samples.apps.nowinandroid.core.database.model.PopulatedNewsResource
import com.google.samples.apps.nowinandroid.core.database.model.asExternalModel import com.google.samples.apps.nowinandroid.core.database.model.asExternalModel
import com.google.samples.apps.nowinandroid.core.database.model.asFtsEntity import com.google.samples.apps.nowinandroid.core.database.model.asFtsEntity
import com.google.samples.apps.nowinandroid.core.model.data.SearchResult import com.google.samples.apps.nowinandroid.core.model.data.SearchResult
@ -29,6 +30,7 @@ import kotlinx.coroutines.CoroutineDispatcher
import kotlinx.coroutines.flow.Flow import kotlinx.coroutines.flow.Flow
import kotlinx.coroutines.flow.combine import kotlinx.coroutines.flow.combine
import kotlinx.coroutines.flow.distinctUntilChanged import kotlinx.coroutines.flow.distinctUntilChanged
import kotlinx.coroutines.flow.first
import kotlinx.coroutines.flow.flatMapLatest import kotlinx.coroutines.flow.flatMapLatest
import kotlinx.coroutines.flow.mapLatest import kotlinx.coroutines.flow.mapLatest
import kotlinx.coroutines.withContext import kotlinx.coroutines.withContext
@ -45,7 +47,12 @@ class DefaultSearchContentsRepository @Inject constructor(
override suspend fun populateFtsData() { override suspend fun populateFtsData() {
withContext(ioDispatcher) { withContext(ioDispatcher) {
newsResourceFtsDao.insertAll( newsResourceFtsDao.insertAll(
newsResourceDao.getOneOffNewsResources().map { it.asFtsEntity() }, newsResourceDao.getNewsResources(
useFilterTopicIds = false,
useFilterNewsIds = false,
)
.first()
.map(PopulatedNewsResource::asFtsEntity),
) )
topicFtsDao.insertAll(topicDao.getOneOffTopicEntities().map { it.asFtsEntity() }) topicFtsDao.insertAll(topicDao.getOneOffTopicEntities().map { it.asFtsEntity() })
} }

@ -67,8 +67,6 @@ class TestNewsResourceDao : NewsResourceDao {
result result
} }
override suspend fun getOneOffNewsResources(): List<PopulatedNewsResource> = emptyList()
override suspend fun insertOrIgnoreNewsResources( override suspend fun insertOrIgnoreNewsResources(
entities: List<NewsResourceEntity>, entities: List<NewsResourceEntity>,
): List<Long> { ): List<Long> {

@ -65,10 +65,6 @@ interface NewsResourceDao {
filterNewsIds: Set<String> = emptySet(), filterNewsIds: Set<String> = emptySet(),
): Flow<List<PopulatedNewsResource>> ): Flow<List<PopulatedNewsResource>>
@Transaction
@Query(value = "SELECT * FROM news_resources ORDER BY publish_date DESC")
suspend fun getOneOffNewsResources(): List<PopulatedNewsResource>
/** /**
* Inserts [entities] into the db if they don't exist, and ignores those that do * Inserts [entities] into the db if they don't exist, and ignores those that do
*/ */

@ -26,6 +26,7 @@ dependencies {
api(project(":core:datastore")) api(project(":core:datastore"))
api(libs.androidx.dataStore.core) api(libs.androidx.dataStore.core)
implementation(libs.protobuf.kotlin.lite)
implementation(project(":core:common")) implementation(project(":core:common"))
implementation(project(":core:testing")) implementation(project(":core:testing"))
} }

@ -21,8 +21,7 @@ import androidx.datastore.core.DataStoreFactory
import com.google.samples.apps.nowinandroid.core.datastore.UserPreferences import com.google.samples.apps.nowinandroid.core.datastore.UserPreferences
import com.google.samples.apps.nowinandroid.core.datastore.UserPreferencesSerializer import com.google.samples.apps.nowinandroid.core.datastore.UserPreferencesSerializer
import com.google.samples.apps.nowinandroid.core.datastore.di.DataStoreModule import com.google.samples.apps.nowinandroid.core.datastore.di.DataStoreModule
import com.google.samples.apps.nowinandroid.core.network.Dispatcher import com.google.samples.apps.nowinandroid.core.network.di.ApplicationScope
import com.google.samples.apps.nowinandroid.core.network.NiaDispatchers.IO
import dagger.Module import dagger.Module
import dagger.Provides import dagger.Provides
import dagger.hilt.components.SingletonComponent import dagger.hilt.components.SingletonComponent
@ -41,12 +40,12 @@ object TestDataStoreModule {
@Provides @Provides
@Singleton @Singleton
fun providesUserPreferencesDataStore( fun providesUserPreferencesDataStore(
@Dispatcher(IO) ioScope: CoroutineScope, @ApplicationScope scope: CoroutineScope,
userPreferencesSerializer: UserPreferencesSerializer, userPreferencesSerializer: UserPreferencesSerializer,
tmpFolder: TemporaryFolder, tmpFolder: TemporaryFolder,
): DataStore<UserPreferences> = ): DataStore<UserPreferences> =
tmpFolder.testUserPreferencesDataStore( tmpFolder.testUserPreferencesDataStore(
coroutineScope = ioScope, coroutineScope = scope,
userPreferencesSerializer = userPreferencesSerializer, userPreferencesSerializer = userPreferencesSerializer,
) )
} }

@ -25,11 +25,13 @@ import com.google.samples.apps.nowinandroid.core.datastore.UserPreferences
import com.google.samples.apps.nowinandroid.core.datastore.UserPreferencesSerializer import com.google.samples.apps.nowinandroid.core.datastore.UserPreferencesSerializer
import com.google.samples.apps.nowinandroid.core.network.Dispatcher import com.google.samples.apps.nowinandroid.core.network.Dispatcher
import com.google.samples.apps.nowinandroid.core.network.NiaDispatchers.IO import com.google.samples.apps.nowinandroid.core.network.NiaDispatchers.IO
import com.google.samples.apps.nowinandroid.core.network.di.ApplicationScope
import dagger.Module import dagger.Module
import dagger.Provides import dagger.Provides
import dagger.hilt.InstallIn import dagger.hilt.InstallIn
import dagger.hilt.android.qualifiers.ApplicationContext import dagger.hilt.android.qualifiers.ApplicationContext
import dagger.hilt.components.SingletonComponent import dagger.hilt.components.SingletonComponent
import kotlinx.coroutines.CoroutineDispatcher
import kotlinx.coroutines.CoroutineScope import kotlinx.coroutines.CoroutineScope
import javax.inject.Singleton import javax.inject.Singleton
@ -41,12 +43,13 @@ object DataStoreModule {
@Singleton @Singleton
fun providesUserPreferencesDataStore( fun providesUserPreferencesDataStore(
@ApplicationContext context: Context, @ApplicationContext context: Context,
@Dispatcher(IO) ioScope: CoroutineScope, @Dispatcher(IO) ioDispatcher: CoroutineDispatcher,
@ApplicationScope scope: CoroutineScope,
userPreferencesSerializer: UserPreferencesSerializer, userPreferencesSerializer: UserPreferencesSerializer,
): DataStore<UserPreferences> = ): DataStore<UserPreferences> =
DataStoreFactory.create( DataStoreFactory.create(
serializer = userPreferencesSerializer, serializer = userPreferencesSerializer,
scope = ioScope, scope = CoroutineScope(scope.coroutineContext + ioDispatcher),
migrations = listOf( migrations = listOf(
IntToStringIdsMigration, IntToStringIdsMigration,
), ),

@ -17,6 +17,7 @@
package com.google.samples.apps.nowinandroid.core.testing.di package com.google.samples.apps.nowinandroid.core.testing.di
import com.google.samples.apps.nowinandroid.core.network.Dispatcher import com.google.samples.apps.nowinandroid.core.network.Dispatcher
import com.google.samples.apps.nowinandroid.core.network.NiaDispatchers.Default
import com.google.samples.apps.nowinandroid.core.network.NiaDispatchers.IO import com.google.samples.apps.nowinandroid.core.network.NiaDispatchers.IO
import com.google.samples.apps.nowinandroid.core.network.di.DispatchersModule import com.google.samples.apps.nowinandroid.core.network.di.DispatchersModule
import dagger.Module import dagger.Module
@ -35,4 +36,10 @@ object TestDispatchersModule {
@Provides @Provides
@Dispatcher(IO) @Dispatcher(IO)
fun providesIODispatcher(testDispatcher: TestDispatcher): CoroutineDispatcher = testDispatcher fun providesIODispatcher(testDispatcher: TestDispatcher): CoroutineDispatcher = testDispatcher
@Provides
@Dispatcher(Default)
fun providesDefaultDispatcher(
testDispatcher: TestDispatcher,
): CoroutineDispatcher = testDispatcher
} }

Loading…
Cancel
Save