Donate to e Foundation | Murena handsets with /e/OS | Own a part of Murena! Learn more

Commit 5387ee15 authored by Zekan Qian's avatar Zekan Qian
Browse files

Add tests of BrowseActivity

Bug: 256582545
Test: unit-test & local build gallery
Change-Id: If2c258b307250ac0e77fb7e0978eeef6fecac3bc
parent c9d48ffd
Loading
Loading
Loading
Loading
+77 −64
Original line number Diff line number Diff line
@@ -19,6 +19,7 @@ package com.android.settingslib.spa.framework
import android.os.Bundle
import androidx.activity.ComponentActivity
import androidx.activity.compose.setContent
import androidx.annotation.VisibleForTesting
import androidx.compose.runtime.Composable
import androidx.compose.runtime.CompositionLocalProvider
import androidx.compose.runtime.DisposableEffect
@@ -37,6 +38,7 @@ import androidx.navigation.compose.rememberNavController
import com.android.settingslib.spa.R
import com.android.settingslib.spa.framework.common.LogCategory
import com.android.settingslib.spa.framework.common.SettingsPage
import com.android.settingslib.spa.framework.common.SettingsPageProvider
import com.android.settingslib.spa.framework.common.SpaEnvironmentFactory
import com.android.settingslib.spa.framework.common.createSettingsPage
import com.android.settingslib.spa.framework.compose.LocalNavController
@@ -74,48 +76,48 @@ open class BrowseActivity : ComponentActivity() {

        setContent {
            SettingsTheme {
                MainContent()
                val sppRepository by spaEnvironment.pageProviderRepository
                BrowseContent(
                    allProviders = sppRepository.getAllProviders(),
                    initialDestination = intent?.getStringExtra(KEY_DESTINATION)
                        ?: sppRepository.getDefaultStartPage(),
                    initialEntryId = intent?.getStringExtra(KEY_HIGHLIGHT_ENTRY)
                )
            }
        }
    }

    companion object {
        const val KEY_DESTINATION = "spaActivityDestination"
        const val KEY_HIGHLIGHT_ENTRY = "highlightEntry"
    }
}

@VisibleForTesting
@Composable
    private fun MainContent() {
        val sppRepository by spaEnvironment.pageProviderRepository
fun BrowseContent(
    allProviders: Collection<SettingsPageProvider>,
    initialDestination: String,
    initialEntryId: String?
) {
    val navController = rememberNavController()
        val nullPage = SettingsPage.createNull()
    CompositionLocalProvider(navController.localNavController()) {
            NavHost(
                navController = navController,
                startDestination = nullPage.sppName,
            ) {
                composable(nullPage.sppName) {}
                for (spp in sppRepository.getAllProviders()) {
                    composable(
                        route = spp.name + spp.parameter.navRoute(),
                        arguments = spp.parameter,
                    ) { navBackStackEntry ->
                        PageLogger(remember(navBackStackEntry.arguments) {
                            spp.createSettingsPage(arguments = navBackStackEntry.arguments)
                        })

                        spp.Page(navBackStackEntry.arguments)
                    }
                }
            }
            InitialDestinationNavigator()
        val controller = LocalNavController.current as NavControllerWrapperImpl
        controller.NavContent(allProviders)
        controller.InitialDestination(initialDestination, initialEntryId)
    }
}

@Composable
    private fun PageLogger(settingsPage: SettingsPage) {
private fun SettingsPageProvider.PageEvents(arguments: Bundle? = null) {
    val page = remember(arguments) { createSettingsPage(arguments) }
    val lifecycleOwner = LocalLifecycleOwner.current
    DisposableEffect(lifecycleOwner) {
        val observer = LifecycleEventObserver { _, event ->
            if (event == Lifecycle.Event.ON_START) {
                    settingsPage.enterPage()
                page.enterPage()
            } else if (event == Lifecycle.Event.ON_STOP) {
                    settingsPage.leavePage()
                page.leavePage()
            }
        }

@@ -130,19 +132,37 @@ open class BrowseActivity : ComponentActivity() {
}

@Composable
    private fun InitialDestinationNavigator() {
        val sppRepository by spaEnvironment.pageProviderRepository
private fun NavControllerWrapperImpl.NavContent(allProvider: Collection<SettingsPageProvider>) {
    val nullPage = SettingsPage.createNull()
    NavHost(
        navController = navController,
        startDestination = nullPage.sppName,
    ) {
        composable(nullPage.sppName) {}
        for (spp in allProvider) {
            composable(
                route = spp.name + spp.parameter.navRoute(),
                arguments = spp.parameter,
            ) { navBackStackEntry ->
                spp.PageEvents(navBackStackEntry.arguments)
                spp.Page(navBackStackEntry.arguments)
            }
        }
    }
}

@Composable
private fun NavControllerWrapperImpl.InitialDestination(
    destination: String,
    highlightEntryId: String?
) {
    val destinationNavigated = rememberSaveable { mutableStateOf(false) }
    if (destinationNavigated.value) return
    destinationNavigated.value = true
        val controller = LocalNavController.current as NavControllerWrapperImpl

    if (destination.isEmpty()) return
    LaunchedEffect(Unit) {
            val destination =
                intent?.getStringExtra(KEY_DESTINATION) ?: sppRepository.getDefaultStartPage()
            val highlightEntryId = intent?.getStringExtra(KEY_HIGHLIGHT_ENTRY)
            if (destination.isNotEmpty()) {
                controller.highlightId = highlightEntryId
                val navController = controller.navController
        highlightId = highlightEntryId
        navController.navigate(destination) {
            popUpTo(navController.graph.findStartDestination().id) {
                inclusive = true
@@ -150,10 +170,3 @@ open class BrowseActivity : ComponentActivity() {
        }
    }
}
    }

    companion object {
        const val KEY_DESTINATION = "spaActivityDestination"
        const val KEY_HIGHLIGHT_ENTRY = "highlightEntry"
    }
}
+9 −0
Original line number Diff line number Diff line
@@ -156,6 +156,15 @@ data class SettingsPage(
    }
}

fun SettingsPageProvider.createSettingsPage(arguments: Bundle? = null): SettingsPage {
    return SettingsPage.create(
        name = name,
        displayName = displayName,
        parameter = parameter,
        arguments = arguments
    )
}

fun String.toHashId(): String {
    return this.hashCode().toUInt().toString(36)
}
+0 −9
Original line number Diff line number Diff line
@@ -51,12 +51,3 @@ interface SettingsPageProvider {
        }
    }
}

fun SettingsPageProvider.createSettingsPage(arguments: Bundle? = null): SettingsPage {
    return SettingsPage.create(
        name = name,
        displayName = displayName,
        parameter = parameter,
        arguments = arguments
    )
}
+89 −0
Original line number Diff line number Diff line
/*
 * Copyright (C) 2022 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
 *
 *      http://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.android.settingslib.spa.framework

import android.content.Context
import androidx.compose.ui.test.assertIsDisplayed
import androidx.compose.ui.test.junit4.createComposeRule
import androidx.compose.ui.test.onAllNodesWithText
import androidx.compose.ui.test.onNodeWithText
import androidx.compose.ui.test.performClick
import androidx.test.core.app.ApplicationProvider
import androidx.test.ext.junit.runners.AndroidJUnit4
import com.android.settingslib.spa.framework.common.LogCategory
import com.android.settingslib.spa.framework.common.LogEvent
import com.android.settingslib.spa.framework.common.SpaEnvironmentFactory
import com.android.settingslib.spa.framework.common.createSettingsPage
import com.android.settingslib.spa.tests.testutils.SpaEnvironmentForTest
import com.android.settingslib.spa.tests.testutils.SpaLoggerForTest
import com.android.settingslib.spa.testutils.waitUntil
import com.google.common.truth.Truth
import org.junit.Rule
import org.junit.Test
import org.junit.runner.RunWith

const val WAIT_UNTIL_TIMEOUT = 1000L

@RunWith(AndroidJUnit4::class)
class BrowseActivityTest {
    @get:Rule
    val composeTestRule = createComposeRule()

    private val context: Context = ApplicationProvider.getApplicationContext()
    private val spaLogger = SpaLoggerForTest()
    private val spaEnvironment = SpaEnvironmentForTest(context, logger = spaLogger)

    @Test
    fun testBrowsePage() {
        spaLogger.reset()
        SpaEnvironmentFactory.reset(spaEnvironment)

        val sppRepository by spaEnvironment.pageProviderRepository
        val sppHome = sppRepository.getProviderOrNull("SppHome")!!
        val pageHome = sppHome.createSettingsPage()
        val sppLayer1 = sppRepository.getProviderOrNull("SppLayer1")!!
        val pageLayer1 = sppLayer1.createSettingsPage()

        composeTestRule.setContent {
            BrowseContent(
                allProviders = listOf(sppHome, sppLayer1),
                initialDestination = pageHome.buildRoute(),
                initialEntryId = null
            )
        }

        composeTestRule.onNodeWithText(sppHome.getTitle(null)).assertIsDisplayed()
        spaLogger.verifyPageEvent(pageHome.id, 1, 0)
        spaLogger.verifyPageEvent(pageLayer1.id, 0, 0)

        // click to layer1 page
        composeTestRule.onNodeWithText("SppHome to Layer1").assertIsDisplayed().performClick()
        waitUntil(WAIT_UNTIL_TIMEOUT) {
            composeTestRule.onAllNodesWithText(sppLayer1.getTitle(null))
                .fetchSemanticsNodes().size == 1
        }
        spaLogger.verifyPageEvent(pageHome.id, 1, 1)
        spaLogger.verifyPageEvent(pageLayer1.id, 1, 0)
    }
}

private fun SpaLoggerForTest.verifyPageEvent(id: String, entryCount: Int, leaveCount: Int) {
    Truth.assertThat(getEventCount(id, LogEvent.PAGE_ENTER, LogCategory.FRAMEWORK))
        .isEqualTo(entryCount)
    Truth.assertThat(getEventCount(id, LogEvent.PAGE_LEAVE, LogCategory.FRAMEWORK))
        .isEqualTo(leaveCount)
}
+7 −0
Original line number Diff line number Diff line
@@ -35,6 +35,7 @@ import com.android.settingslib.spa.framework.common.SettingsPageProviderReposito
import com.android.settingslib.spa.framework.common.SpaEnvironment
import com.android.settingslib.spa.framework.common.SpaLogger
import com.android.settingslib.spa.framework.common.createSettingsPage
import com.android.settingslib.spa.widget.preference.SimplePreferenceMacro

class SpaLoggerForTest : SpaLogger {
    data class MsgCountKey(val msg: String, val category: LogCategory)
@@ -98,6 +99,12 @@ object SppLayer1 : SettingsPageProvider {

    fun buildInject(): SettingsEntryBuilder {
        return SettingsEntryBuilder.createInject(this.createSettingsPage())
            .setMacro {
                SimplePreferenceMacro(
                    title = "SppHome to Layer1",
                    clickRoute = name
                )
            }
    }

    override fun buildEntry(arguments: Bundle?): List<SettingsEntry> {