fix: 首次提交
This commit is contained in:
40
SettingsLib/Spa/testutils/Android.bp
Normal file
40
SettingsLib/Spa/testutils/Android.bp
Normal file
@@ -0,0 +1,40 @@
|
||||
//
|
||||
// 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 {
|
||||
default_applicable_licenses: ["frameworks_base_license"],
|
||||
}
|
||||
|
||||
android_library {
|
||||
name: "SpaLibTestUtils",
|
||||
|
||||
srcs: ["src/**/*.kt"],
|
||||
use_resource_processor: true,
|
||||
static_libs: [
|
||||
"SpaLib",
|
||||
"androidx.arch.core_core-testing",
|
||||
"androidx.compose.runtime_runtime",
|
||||
"androidx.compose.ui_ui-test-junit4",
|
||||
"androidx.compose.ui_ui-test-manifest",
|
||||
"androidx.lifecycle_lifecycle-runtime-testing",
|
||||
"mockito-kotlin2",
|
||||
"truth",
|
||||
],
|
||||
kotlincflags: [
|
||||
"-Xjvm-default=all",
|
||||
],
|
||||
min_sdk_version: "31",
|
||||
}
|
||||
20
SettingsLib/Spa/testutils/AndroidManifest.xml
Normal file
20
SettingsLib/Spa/testutils/AndroidManifest.xml
Normal file
@@ -0,0 +1,20 @@
|
||||
<?xml version="1.0" encoding="utf-8"?><!--
|
||||
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.
|
||||
-->
|
||||
|
||||
<manifest xmlns:android="http://schemas.android.com/apk/res/android"
|
||||
package="com.android.settingslib.spa.testutils">
|
||||
<uses-sdk android:minSdkVersion="21"/>
|
||||
</manifest>
|
||||
52
SettingsLib/Spa/testutils/build.gradle.kts
Normal file
52
SettingsLib/Spa/testutils/build.gradle.kts
Normal file
@@ -0,0 +1,52 @@
|
||||
/*
|
||||
* Copyright 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.
|
||||
*/
|
||||
|
||||
plugins {
|
||||
alias(libs.plugins.android.library)
|
||||
alias(libs.plugins.kotlin.android)
|
||||
}
|
||||
|
||||
val jetpackComposeVersion: String? by extra
|
||||
|
||||
android {
|
||||
namespace = "com.android.settingslib.spa.testutils"
|
||||
|
||||
sourceSets {
|
||||
sourceSets.getByName("main") {
|
||||
kotlin.setSrcDirs(listOf("src"))
|
||||
manifest.srcFile("AndroidManifest.xml")
|
||||
}
|
||||
}
|
||||
buildFeatures {
|
||||
compose = true
|
||||
}
|
||||
}
|
||||
|
||||
dependencies {
|
||||
api(project(":spa"))
|
||||
|
||||
api("androidx.arch.core:core-testing:2.2.0-alpha01")
|
||||
api("androidx.compose.ui:ui-test-junit4:$jetpackComposeVersion")
|
||||
api("androidx.lifecycle:lifecycle-runtime-testing")
|
||||
api("org.mockito.kotlin:mockito-kotlin:2.2.11")
|
||||
api("org.mockito:mockito-core") {
|
||||
version {
|
||||
strictly("2.28.2")
|
||||
}
|
||||
}
|
||||
api(libs.truth)
|
||||
debugApi("androidx.compose.ui:ui-test-manifest:$jetpackComposeVersion")
|
||||
}
|
||||
@@ -0,0 +1,92 @@
|
||||
/*
|
||||
* 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.testutils
|
||||
|
||||
import androidx.compose.foundation.layout.Box
|
||||
import androidx.compose.foundation.layout.sizeIn
|
||||
import androidx.compose.material3.Surface
|
||||
import androidx.compose.runtime.Composable
|
||||
import androidx.compose.ui.Modifier
|
||||
import androidx.compose.ui.platform.testTag
|
||||
import androidx.compose.ui.test.ComposeTimeoutException
|
||||
import androidx.compose.ui.test.SemanticsMatcher
|
||||
import androidx.compose.ui.test.SemanticsNodeInteraction
|
||||
import androidx.compose.ui.test.getUnclippedBoundsInRoot
|
||||
import androidx.compose.ui.test.hasAnyAncestor
|
||||
import androidx.compose.ui.test.hasText
|
||||
import androidx.compose.ui.test.isDialog
|
||||
import androidx.compose.ui.test.junit4.ComposeContentTestRule
|
||||
import androidx.compose.ui.test.junit4.ComposeTestRule
|
||||
import androidx.compose.ui.test.onNodeWithTag
|
||||
import androidx.compose.ui.test.onRoot
|
||||
import androidx.compose.ui.unit.Dp
|
||||
import androidx.compose.ui.unit.dp
|
||||
import androidx.compose.ui.unit.height
|
||||
import androidx.compose.ui.unit.width
|
||||
import com.android.settingslib.spa.framework.theme.SettingsTheme
|
||||
|
||||
/** Blocks until the found a semantics node that match the given condition. */
|
||||
fun ComposeContentTestRule.waitUntilExists(matcher: SemanticsMatcher) = waitUntil {
|
||||
onAllNodes(matcher).fetchSemanticsNodes().isNotEmpty()
|
||||
}
|
||||
|
||||
/** Blocks until the timeout is reached. */
|
||||
fun ComposeContentTestRule.delay(timeoutMillis: Long = 1_000) = try {
|
||||
waitUntil(timeoutMillis) { false }
|
||||
} catch (_: ComposeTimeoutException) {
|
||||
// Expected
|
||||
}
|
||||
|
||||
/** Finds a text node that within dialog. */
|
||||
fun ComposeContentTestRule.onDialogText(text: String): SemanticsNodeInteraction =
|
||||
onNode(hasAnyAncestor(isDialog()) and hasText(text))
|
||||
|
||||
fun ComposeTestRule.rootWidth(): Dp = onRoot().getUnclippedBoundsInRoot().width
|
||||
|
||||
fun ComposeTestRule.rootHeight(): Dp = onRoot().getUnclippedBoundsInRoot().height
|
||||
|
||||
/**
|
||||
* Constant to emulate very big but finite constraints
|
||||
*/
|
||||
private val sizeAssertionMaxSize = 5000.dp
|
||||
|
||||
private const val SIZE_ASSERTION_TAG = "containerForSizeAssertion"
|
||||
|
||||
fun ComposeContentTestRule.setContentForSizeAssertions(
|
||||
parentMaxWidth: Dp = sizeAssertionMaxSize,
|
||||
parentMaxHeight: Dp = sizeAssertionMaxSize,
|
||||
// TODO : figure out better way to make it flexible
|
||||
content: @Composable () -> Unit
|
||||
): SemanticsNodeInteraction {
|
||||
setContent {
|
||||
SettingsTheme {
|
||||
Surface {
|
||||
Box {
|
||||
Box(
|
||||
Modifier
|
||||
.sizeIn(maxWidth = parentMaxWidth, maxHeight = parentMaxHeight)
|
||||
.testTag(SIZE_ASSERTION_TAG)
|
||||
) {
|
||||
content()
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return onNodeWithTag(SIZE_ASSERTION_TAG)
|
||||
}
|
||||
@@ -0,0 +1,42 @@
|
||||
/*
|
||||
* 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.testutils
|
||||
|
||||
import androidx.compose.runtime.Composable
|
||||
import androidx.compose.runtime.CompositionLocalProvider
|
||||
import com.android.settingslib.spa.framework.compose.LocalNavController
|
||||
import com.android.settingslib.spa.framework.compose.NavControllerWrapper
|
||||
|
||||
class FakeNavControllerWrapper : NavControllerWrapper {
|
||||
var navigateCalledWith: String? = null
|
||||
var navigateBackIsCalled = false
|
||||
|
||||
override fun navigate(route: String, popUpCurrent: Boolean) {
|
||||
navigateCalledWith = route
|
||||
}
|
||||
|
||||
override fun navigateBack() {
|
||||
navigateBackIsCalled = true
|
||||
}
|
||||
|
||||
@Composable
|
||||
fun Wrapper(content: @Composable () -> Unit) {
|
||||
CompositionLocalProvider(LocalNavController provides this) {
|
||||
content()
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,34 @@
|
||||
/*
|
||||
* 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.testutils
|
||||
|
||||
import kotlinx.coroutines.flow.Flow
|
||||
import kotlinx.coroutines.flow.first
|
||||
import kotlinx.coroutines.flow.toList
|
||||
import kotlinx.coroutines.withTimeoutOrNull
|
||||
|
||||
suspend fun <T> Flow<T>.firstWithTimeoutOrNull(timeMillis: Long = 500): T? =
|
||||
withTimeoutOrNull(timeMillis) {
|
||||
first()
|
||||
}
|
||||
|
||||
suspend fun <T> Flow<T>.toListWithTimeout(timeMillis: Long = 500): List<T> {
|
||||
val list = mutableListOf<T>()
|
||||
return withTimeoutOrNull(timeMillis) {
|
||||
toList(list)
|
||||
} ?: list
|
||||
}
|
||||
@@ -0,0 +1,42 @@
|
||||
/*
|
||||
* Copyright (C) 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
|
||||
*
|
||||
* 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.testutils
|
||||
|
||||
import androidx.compose.ui.graphics.Color
|
||||
import androidx.compose.ui.graphics.ImageBitmap
|
||||
import androidx.compose.ui.graphics.toPixelMap
|
||||
|
||||
/**
|
||||
* Asserts that the expected color is present in this bitmap.
|
||||
*
|
||||
* @throws AssertionError if the expected color is not present.
|
||||
*/
|
||||
fun ImageBitmap.assertContainsColor(expectedColor: Color) {
|
||||
assert(containsColor(expectedColor)) {
|
||||
"The given color $expectedColor was not found in the bitmap."
|
||||
}
|
||||
}
|
||||
|
||||
private fun ImageBitmap.containsColor(expectedColor: Color): Boolean {
|
||||
val pixels = toPixelMap()
|
||||
for (x in 0 until width) {
|
||||
for (y in 0 until height) {
|
||||
if (pixels[x, y] == expectedColor) return true
|
||||
}
|
||||
}
|
||||
return false
|
||||
}
|
||||
@@ -0,0 +1,26 @@
|
||||
/*
|
||||
* Copyright (C) 2024 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.testutils
|
||||
|
||||
import androidx.compose.ui.semantics.Role
|
||||
import androidx.compose.ui.semantics.SemanticsProperties
|
||||
import androidx.compose.ui.semantics.getOrNull
|
||||
import androidx.compose.ui.test.SemanticsMatcher
|
||||
|
||||
fun hasRole(role: Role) = SemanticsMatcher("${SemanticsProperties.Role.name} has $role") {
|
||||
it.config.getOrNull(SemanticsProperties.Role) == role
|
||||
}
|
||||
@@ -0,0 +1,33 @@
|
||||
/*
|
||||
* 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.testutils
|
||||
|
||||
import java.util.concurrent.TimeoutException
|
||||
|
||||
/**
|
||||
* Blocks until the given condition is satisfied.
|
||||
*/
|
||||
fun waitUntil(timeoutMillis: Long = 1000, condition: () -> Boolean) {
|
||||
val startTime = System.currentTimeMillis()
|
||||
while (!condition()) {
|
||||
// Let Android run measure, draw and in general any other async operations.
|
||||
Thread.sleep(10)
|
||||
if (System.currentTimeMillis() - startTime > timeoutMillis) {
|
||||
throw TimeoutException("Condition still not satisfied after $timeoutMillis ms")
|
||||
}
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user