1 /*
2 * Copyright 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 androidx.xr.compose.testing
18
19 import android.app.Activity
20 import android.view.Display
21 import androidx.activity.ComponentActivity
22 import androidx.annotation.NonNull
23 import androidx.annotation.RestrictTo
24 import androidx.compose.runtime.Composable
25 import androidx.compose.ui.test.junit4.AndroidComposeTestRule
26 import androidx.xr.compose.platform.SceneManager
27 import androidx.xr.compose.spatial.Subspace
28 import androidx.xr.compose.subspace.SubspaceComposable
29 import androidx.xr.runtime.Session
30 import androidx.xr.runtime.internal.JxrPlatformAdapter
31 import androidx.xr.runtime.testing.FakeRuntimeFactory
32 import androidx.xr.scenecore.impl.JxrPlatformAdapterAxr
33 import androidx.xr.scenecore.impl.extensions.XrExtensionsProvider
34 import androidx.xr.scenecore.impl.perception.PerceptionLibrary
35 import androidx.xr.scenecore.testing.FakeImpressApi
36 import androidx.xr.scenecore.testing.FakeScheduledExecutorService
37 import com.android.extensions.xr.XrExtensions
38 import com.google.androidxr.splitengine.SplitEngineSubspaceManager
39 import com.google.ar.imp.view.splitengine.ImpSplitEngineRenderer
40 import org.mockito.Mockito.mock
41 import org.robolectric.shadows.ShadowDisplay
42
43 /** Custom test class that should be used for testing [SubspaceComposable] content. */
44 @Suppress("ForbiddenSuperClass")
45 @RestrictTo(RestrictTo.Scope.LIBRARY_GROUP_PREFIX)
46 public class SubspaceTestingActivity : ComponentActivity() {
47 public val extensions: XrExtensions = XrExtensionsProvider.getXrExtensions()!!
48 @Suppress("MutableBareField") public lateinit var session: Session
49
50 /** Throws an exception by default under test; return Robolectric Display impl instead. */
getDisplaynull51 @NonNull override fun getDisplay(): Display = ShadowDisplay.getDefaultDisplay()
52
53 override fun onStart() {
54 SceneManager.start()
55 super.onStart()
56 }
57
onDestroynull58 override fun onDestroy() {
59 SceneManager.stop()
60 super.onDestroy()
61 }
62 }
63
64 /** Analog to [AndroidComposeTestRule.setContent] for testing [SubspaceComposable] content. */
65 @RestrictTo(RestrictTo.Scope.LIBRARY_GROUP_PREFIX)
66 public fun AndroidComposeTestRule<*, SubspaceTestingActivity>.setSubspaceContent(
67 content: @Composable @SubspaceComposable () -> Unit
68 ) {
<lambda>null69 setContent { TestSetup { Subspace { content() } } }
70 }
71
72 /** Analog to [AndroidComposeTestRule.setContent] for testing [SubspaceComposable] content. */
73 @RestrictTo(RestrictTo.Scope.LIBRARY_GROUP_PREFIX)
74 public fun AndroidComposeTestRule<*, SubspaceTestingActivity>.setSubspaceContent(
75 uiContent: @Composable () -> Unit,
76 content: @Composable @SubspaceComposable () -> Unit,
77 ) {
<lambda>null78 setContent {
79 TestSetup {
80 uiContent()
81 Subspace { content() }
82 }
83 }
84 }
85
86 /** Subspace version of onNode in Compose. */
87 @RestrictTo(RestrictTo.Scope.LIBRARY_GROUP_PREFIX)
onSubspaceNodenull88 public fun AndroidComposeTestRule<*, SubspaceTestingActivity>.onSubspaceNode(
89 matcher: SubspaceSemanticsMatcher
90 ): SubspaceSemanticsNodeInteraction =
91 SubspaceSemanticsNodeInteraction(SubspaceTestContext(this), matcher)
92
93 /** Subspace version of onAllNodes in Compose. */
94 @RestrictTo(RestrictTo.Scope.LIBRARY_GROUP_PREFIX)
95 public fun AndroidComposeTestRule<*, SubspaceTestingActivity>.onAllSubspaceNodes(
96 matcher: SubspaceSemanticsMatcher
97 ): SubspaceSemanticsNodeInteractionCollection =
98 SubspaceSemanticsNodeInteractionCollection(SubspaceTestContext(this), matcher)
99
100 /** Subspace version of onNodeWithTag in Compose. */
101 @RestrictTo(RestrictTo.Scope.LIBRARY_GROUP_PREFIX)
102 public fun AndroidComposeTestRule<*, SubspaceTestingActivity>.onSubspaceNodeWithTag(
103 testTag: String
104 ): SubspaceSemanticsNodeInteraction = onSubspaceNode(hasTestTag(testTag))
105
106 /** Subspace version of onAllNodesWithTag in Compose. */
107 @RestrictTo(RestrictTo.Scope.LIBRARY_GROUP_PREFIX)
108 public fun AndroidComposeTestRule<*, SubspaceTestingActivity>.onAllSubspaceNodesWithTag(
109 testTag: String
110 ): SubspaceSemanticsNodeInteractionCollection = onAllSubspaceNodes(hasTestTag(testTag))
111
112 /**
113 * Create a fake [Session] for testing.
114 *
115 * A convenience method that creates a fake [Session] for testing. If runtime is not provided, a
116 * fake [JxrPlatformAdapter] will be created by default.
117 *
118 * @param activity The [SubspaceTestingActivity] to use for the [Session].
119 * @param runtime The [JxrPlatformAdapter] to use for the [Session].
120 */
121 @RestrictTo(RestrictTo.Scope.LIBRARY_GROUP_PREFIX)
122 public fun createFakeSession(
123 activity: SubspaceTestingActivity,
124 runtime: JxrPlatformAdapter = createFakeRuntime(activity),
125 ): Session = Session(activity, FakeRuntimeFactory().createRuntime(activity), runtime)
126
127 /**
128 * Create a fake [Session] for testing using configs from [TestJxrPlatformAdapter].
129 *
130 * A convenience method that creates a fake [Session] for testing. If runtime is not provided, a
131 * fake [JxrPlatformAdapter] using [TestJxrPlatformAdapter] will be created by default.
132 *
133 * @param activity The [Activity] to use for the [Session].
134 * @param runtime The [JxrPlatformAdapter] to use for the [Session].
135 */
136 internal fun createFakeSessionWithTestConfigs(
137 activity: Activity,
138 runtime: JxrPlatformAdapter = createFakeRuntime(activity),
139 ): Session = Session(activity, FakeRuntimeFactory().createRuntime(activity), runtime)
140
141 /**
142 * Create a fake [JxrPlatformAdapter] for testing.
143 *
144 * A convenience method that creates a fake [JxrPlatformAdapter] for testing.
145 *
146 * @param activity The [Activity] to use for the [JxrPlatformAdapter].
147 */
148 @RestrictTo(RestrictTo.Scope.LIBRARY_GROUP_PREFIX)
149 public fun createFakeRuntime(activity: Activity): JxrPlatformAdapter =
150 // FakeJxrPlatformAdapterFactory().createPlatformAdapter(activity)
151 JxrPlatformAdapterAxr.create(
152 /* activity = */ activity,
153 /* executor = */ FakeScheduledExecutorService(),
154 /* extensions = */ (activity as SubspaceTestingActivity).extensions,
155 /* impressApi = */ FakeImpressApi(),
156 /* perceptionLibrary = */ PerceptionLibrary(),
157 /* splitEngineSubspaceManager = */ mock(SplitEngineSubspaceManager::class.java),
158 /* splitEngineRenderer = */ mock(ImpSplitEngineRenderer::class.java),
159 )
160