xref: /aosp_15_r20/platform_testing/libraries/motion/compose/src/platform/test/motion/compose/MotionTestValues.kt (revision dd0948b35e70be4c0246aabd6c72554a5eb8b22a)
1 /*
2  * Copyright (C) 2024 The Android Open Source Project
3  *
4  * Licensed under the Apache License, Version 2.0 (the "License");
5  * you may not use this file except in compliance with the License.
6  * You may obtain a copy of the License at
7  *
8  *      http://www.apache.org/licenses/LICENSE-2.0
9  *
10  * Unless required by applicable law or agreed to in writing, software
11  * distributed under the License is distributed on an "AS IS" BASIS,
12  * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13  * See the License for the specific language governing permissions and
14  * limitations under the License.
15  */
16 
17 package platform.test.motion.compose
18 
19 import androidx.compose.ui.semantics.SemanticsNode
20 import androidx.compose.ui.test.SemanticsMatcher
21 import androidx.compose.ui.test.SemanticsNodeInteractionsProvider
22 import platform.test.motion.compose.values.MotionTestValueKey
23 import platform.test.motion.golden.DataPointType
24 import platform.test.motion.golden.FeatureCapture
25 import platform.test.motion.golden.TimeSeriesCaptureScope
26 
27 /** Matches nodes that have the [motionTestValueKey] set. */
hasMotionTestValuenull28 fun <T> hasMotionTestValue(motionTestValueKey: MotionTestValueKey<T>): SemanticsMatcher =
29     SemanticsMatcher.keyIsDefined(motionTestValueKey.semanticsPropertyKey)
30 
31 /**
32  * Looks up a node matching [matcher], and returns the associated [motionTestValueKey].
33  *
34  * [AssertionError] is thrown if zero or more than one matching node is found.
35  * [IllegalStateException] is thrown if the node does not have a [motionTestValueKey] exported.
36  */
37 fun <T> SemanticsNodeInteractionsProvider.motionTestValueOfNode(
38     motionTestValueKey: MotionTestValueKey<T>,
39     matcher: SemanticsMatcher = hasMotionTestValue(motionTestValueKey),
40     useUnmergedTree: Boolean = false,
41 ): T = onNode(matcher, useUnmergedTree).fetchSemanticsNode().get(motionTestValueKey)
42 
43 /**
44  * Records a feature by sampling the value associated to [motionTestValueKey], on a single node
45  * matching [matcher].
46  *
47  * Records `DataPoint.notFound()` if 0 or 2+ matching node are found. [IllegalStateException] is
48  * thrown if the node does not have a [motionTestValueKey] exported.
49  */
50 fun <T : Any> TimeSeriesCaptureScope<SemanticsNodeInteractionsProvider>.feature(
51     motionTestValueKey: MotionTestValueKey<T>,
52     dataPointType: DataPointType<T>,
53     matcher: SemanticsMatcher = hasMotionTestValue(motionTestValueKey),
54     name: String = motionTestValueKey.semanticsPropertyKey.name,
55 ) {
56     feature(
57         matcher,
58         FeatureCapture(name) { dataPointType.makeDataPoint(it.get(motionTestValueKey)) },
59     )
60 }
61 
62 /**
63  * Captures the feature using [capture], where [capture]'s input is the value associated to
64  * [motionTestValueKey], read from a single node matching [matcher].
65  *
66  * Records `DataPoint.notFound()` if 0 or 2+ matching node are found. [IllegalStateException] is
67  * thrown if the node does not have a [motionTestValueKey] exported.
68  */
featurenull69 fun <T> TimeSeriesCaptureScope<SemanticsNodeInteractionsProvider>.feature(
70     motionTestValueKey: MotionTestValueKey<T>,
71     capture: FeatureCapture<T, *>,
72     matcher: SemanticsMatcher = hasMotionTestValue(motionTestValueKey),
73     name: String = "${motionTestValueKey.semanticsPropertyKey.name}_${capture.name}",
74 ) {
75     feature(matcher, FeatureCapture(name) { capture.capture(it.get(motionTestValueKey)) })
76 }
77 
getnull78 private fun <T> SemanticsNode.get(motionTestValueKey: MotionTestValueKey<T>) =
79     config[motionTestValueKey.semanticsPropertyKey]
80