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.scenecore
18 
19 import androidx.xr.runtime.internal.ActivityPose.HitTestFilter as RtHitTestFilter
20 import androidx.xr.runtime.internal.ActivitySpace as RtActivitySpace
21 import androidx.xr.runtime.internal.AnchorPlacement as RtAnchorPlacement
22 import androidx.xr.runtime.internal.Dimensions as RuntimeDimensions
23 import androidx.xr.runtime.internal.Entity as RuntimeEntity
24 import androidx.xr.runtime.internal.HitTestResult as RuntimeHitTestResult
25 import androidx.xr.runtime.internal.InputEvent as RuntimeInputEvent
26 import androidx.xr.runtime.internal.InputEvent.Companion.HitInfo as RuntimeHitInfo
27 import androidx.xr.runtime.internal.JxrPlatformAdapter
28 import androidx.xr.runtime.internal.MoveEvent as RuntimeMoveEvent
29 import androidx.xr.runtime.internal.PixelDimensions as RuntimePixelDimensions
30 import androidx.xr.runtime.internal.PlaneSemantic as RtPlaneSemantic
31 import androidx.xr.runtime.internal.PlaneType as RtPlaneType
32 import androidx.xr.runtime.internal.ResizeEvent as RuntimeResizeEvent
33 import androidx.xr.runtime.internal.SpatialCapabilities as RuntimeSpatialCapabilities
34 import androidx.xr.runtime.internal.SpatialVisibility as RuntimeSpatialVisibility
35 import androidx.xr.runtime.internal.TextureSampler as RuntimeTextureSampler
36 import androidx.xr.runtime.math.Matrix4
37 import androidx.xr.runtime.math.Pose
38 import androidx.xr.runtime.math.Quaternion
39 import androidx.xr.runtime.math.Ray
40 import androidx.xr.runtime.math.Vector3
41 import androidx.xr.scenecore.ActivityPose.HitTestFilter
42 import com.google.common.truth.Truth.assertThat
43 import kotlin.test.assertFailsWith
44 import org.junit.Test
45 import org.mockito.kotlin.mock
46 import org.mockito.kotlin.whenever
47 
48 class UtilsTest {
49     @Test
verifyDefaultPoseIsIdentitynull50     fun verifyDefaultPoseIsIdentity() {
51         val rtPose = Pose.Identity
52 
53         assertThat(rtPose.translation.x).isZero()
54         assertThat(rtPose.translation.y).isZero()
55         assertThat(rtPose.translation.z).isZero()
56         assertThat(rtPose.rotation.x).isZero()
57         assertThat(rtPose.rotation.y).isZero()
58         assertThat(rtPose.rotation.z).isZero()
59         assertThat(rtPose.rotation.w).isEqualTo(1f)
60     }
61 
62     @Test
verifyPoseToRtPoseConversionnull63     fun verifyPoseToRtPoseConversion() {
64         val rtPose = Pose(Vector3(1f, 2f, 3f), Quaternion(1f, 2f, 3f, 4f).toNormalized())
65 
66         assertThat(rtPose.translation.x).isEqualTo(1f)
67         assertThat(rtPose.translation.y).isEqualTo(2f)
68         assertThat(rtPose.translation.z).isEqualTo(3f)
69 
70         // The quaternion is always normalized, so the retrieved values differ from the original
71         // ones.
72         assertThat(rtPose.rotation.x).isWithin(1e-5f).of(0.18257418f)
73         assertThat(rtPose.rotation.y).isWithin(1e-5f).of(0.36514837f)
74         assertThat(rtPose.rotation.z).isWithin(1e-5f).of(0.5477225f)
75         assertThat(rtPose.rotation.w).isWithin(1e-5f).of(0.73029673f)
76     }
77 
78     @Test
verifyRtPoseToPoseConversionnull79     fun verifyRtPoseToPoseConversion() {
80         val pose = Pose(Vector3(1f, 2f, 3f), Quaternion(1f, 2f, 3f, 4f).toNormalized())
81 
82         assertThat(pose.translation.x).isEqualTo(1f)
83         assertThat(pose.translation.y).isEqualTo(2f)
84         assertThat(pose.translation.z).isEqualTo(3f)
85 
86         // The quaternion is always normalized, so the retrieved values differ from the original
87         // ones.
88         assertThat(pose.rotation.x).isWithin(1e-5f).of(0.18257418f)
89         assertThat(pose.rotation.y).isWithin(1e-5f).of(0.36514837f)
90         assertThat(pose.rotation.z).isWithin(1e-5f).of(0.5477225f)
91         assertThat(pose.rotation.w).isWithin(1e-5f).of(0.73029673f)
92     }
93 
94     @Test
verifyRtVector3toVector3null95     fun verifyRtVector3toVector3() {
96         val vector3: Vector3 = Vector3(1f, 2f, 3f)
97         assertThat(vector3.x).isEqualTo(1f)
98         assertThat(vector3.y).isEqualTo(2f)
99         assertThat(vector3.z).isEqualTo(3f)
100     }
101 
102     @Test
verifyVector3toRtVector3null103     fun verifyVector3toRtVector3() {
104         val rtVector3: Vector3 = Vector3(1f, 2f, 3f)
105         assertThat(rtVector3.x).isEqualTo(1f)
106         assertThat(rtVector3.y).isEqualTo(2f)
107         assertThat(rtVector3.z).isEqualTo(3f)
108     }
109 
110     @Test
verifyRtMoveEventToMoveEventnull111     fun verifyRtMoveEventToMoveEvent() {
112         val vector0 = Vector3(0f, 0f, 0f)
113         val vector1 = Vector3(1f, 1f, 1f)
114         val vector2 = Vector3(2f, 2f, 2f)
115 
116         val initialInputRay = Ray(vector0, vector1)
117         val currentInputRay = Ray(vector1, vector2)
118         val entityManager = EntityManager()
119         val activitySpace = mock<RtActivitySpace>()
120         entityManager.setEntityForRtEntity(activitySpace, mock<Entity>())
121         val moveEvent =
122             RuntimeMoveEvent(
123                     RuntimeMoveEvent.MOVE_STATE_ONGOING,
124                     initialInputRay,
125                     currentInputRay,
126                     Pose(),
127                     Pose(vector1, Quaternion.Identity),
128                     vector1,
129                     vector1,
130                     activitySpace,
131                     null,
132                     null,
133                 )
134                 .toMoveEvent(entityManager)
135 
136         assertThat(moveEvent.moveState).isEqualTo(MoveEvent.MOVE_STATE_ONGOING)
137 
138         assertThat(moveEvent.initialInputRay.origin.x).isEqualTo(0f)
139         assertThat(moveEvent.initialInputRay.origin.y).isEqualTo(0f)
140         assertThat(moveEvent.initialInputRay.origin.z).isEqualTo(0f)
141         assertThat(moveEvent.initialInputRay.direction.x).isEqualTo(1f)
142         assertThat(moveEvent.initialInputRay.direction.y).isEqualTo(1f)
143         assertThat(moveEvent.initialInputRay.direction.z).isEqualTo(1f)
144 
145         assertThat(moveEvent.currentInputRay.origin.x).isEqualTo(1f)
146         assertThat(moveEvent.currentInputRay.origin.y).isEqualTo(1f)
147         assertThat(moveEvent.currentInputRay.origin.z).isEqualTo(1f)
148         assertThat(moveEvent.currentInputRay.direction.x).isEqualTo(2f)
149         assertThat(moveEvent.currentInputRay.direction.y).isEqualTo(2f)
150         assertThat(moveEvent.currentInputRay.direction.z).isEqualTo(2f)
151 
152         assertThat(moveEvent.previousPose.translation.x).isEqualTo(0f)
153         assertThat(moveEvent.previousPose.translation.y).isEqualTo(0f)
154         assertThat(moveEvent.previousPose.translation.z).isEqualTo(0f)
155         assertThat(moveEvent.previousPose.rotation.x).isEqualTo(0f)
156         assertThat(moveEvent.previousPose.rotation.y).isEqualTo(0f)
157         assertThat(moveEvent.previousPose.rotation.z).isEqualTo(0f)
158         assertThat(moveEvent.previousPose.rotation.w).isEqualTo(1f)
159 
160         assertThat(moveEvent.currentPose.translation.x).isEqualTo(1f)
161         assertThat(moveEvent.currentPose.translation.y).isEqualTo(1f)
162         assertThat(moveEvent.currentPose.translation.z).isEqualTo(1f)
163         assertThat(moveEvent.currentPose.rotation.x).isEqualTo(0f)
164         assertThat(moveEvent.currentPose.rotation.y).isEqualTo(0f)
165         assertThat(moveEvent.currentPose.rotation.z).isEqualTo(0f)
166         assertThat(moveEvent.currentPose.rotation.w).isEqualTo(1f)
167 
168         assertThat(moveEvent.previousScale).isEqualTo(1f)
169 
170         assertThat(moveEvent.currentScale).isEqualTo(1f)
171     }
172 
173     @Test
verifyRtInputEventToInputEventConversionnull174     fun verifyRtInputEventToInputEventConversion() {
175         val entityManager = EntityManager()
176         val activitySpace = mock<RtActivitySpace>()
177         entityManager.setEntityForRtEntity(activitySpace, mock<Entity>())
178         val inputEvent =
179             RuntimeInputEvent(
180                     RuntimeInputEvent.SOURCE_HANDS,
181                     RuntimeInputEvent.POINTER_TYPE_LEFT,
182                     123456789,
183                     Vector3(1f, 2f, 3f),
184                     Vector3(4f, 5f, 6f),
185                     RuntimeInputEvent.ACTION_DOWN,
186                     null,
187                     null,
188                 )
189                 .toInputEvent(entityManager)
190         assertThat(inputEvent.source).isEqualTo(InputEvent.SOURCE_HANDS)
191         assertThat(inputEvent.pointerType).isEqualTo(InputEvent.POINTER_TYPE_LEFT)
192         assertThat(inputEvent.timestamp).isEqualTo(123456789)
193         assertThat(inputEvent.origin.x).isEqualTo(1f)
194         assertThat(inputEvent.origin.y).isEqualTo(2f)
195         assertThat(inputEvent.origin.z).isEqualTo(3f)
196         assertThat(inputEvent.direction.x).isEqualTo(4f)
197         assertThat(inputEvent.direction.y).isEqualTo(5f)
198         assertThat(inputEvent.direction.z).isEqualTo(6f)
199         assertThat(inputEvent.action).isEqualTo(InputEvent.ACTION_DOWN)
200     }
201 
202     @Test
verifyRtHitInfoToHitInfoConversionnull203     fun verifyRtHitInfoToHitInfoConversion() {
204         val entityManager = EntityManager()
205         val rtMockEntity = mock<RuntimeEntity>()
206         val mockEntity = mock<Entity>()
207         entityManager.setEntityForRtEntity(rtMockEntity, mockEntity)
208         val hitPosition = Vector3(1f, 2f, 3f)
209         val transform = Matrix4.Identity
210         val hitInfo = RuntimeHitInfo(rtMockEntity, hitPosition, transform).toHitInfo(entityManager)
211 
212         assertThat(hitInfo).isNotNull()
213         assertThat(hitInfo!!.inputEntity).isEqualTo(mockEntity)
214         assertThat(hitInfo.hitPosition).isEqualTo(hitPosition)
215         assertThat(hitInfo.transform).isEqualTo(transform)
216     }
217 
218     @Test
verifyRtHitInfoToHitInfoConversionWhenEntityNotFoundnull219     fun verifyRtHitInfoToHitInfoConversionWhenEntityNotFound() {
220         val entityManager = EntityManager()
221         val rtMockEntity = mock<RuntimeEntity>()
222         val hitPosition = Vector3(1f, 2f, 3f)
223         val transform = Matrix4.Identity
224         val hitInfo = RuntimeHitInfo(rtMockEntity, hitPosition, transform).toHitInfo(entityManager)
225 
226         // EntityManager does not have the entity for the given RuntimeEntity, so the hit info is
227         // null.
228         assertThat(hitInfo).isNull()
229     }
230 
231     @Test
verifyRtDimensionsToDimensionsnull232     fun verifyRtDimensionsToDimensions() {
233         val dimensions: Dimensions = RuntimeDimensions(2f, 4f, 6f).toDimensions()
234         assertThat(dimensions.width).isEqualTo(2f)
235         assertThat(dimensions.height).isEqualTo(4f)
236         assertThat(dimensions.depth).isEqualTo(6f)
237     }
238 
239     @Test
verifyRtPixelDimensionsToPixelDimensionsnull240     fun verifyRtPixelDimensionsToPixelDimensions() {
241         val pixelDimensions: PixelDimensions = RuntimePixelDimensions(14, 15).toPixelDimensions()
242         assertThat(pixelDimensions.width).isEqualTo(14)
243         assertThat(pixelDimensions.height).isEqualTo(15)
244     }
245 
246     @Test
verifyPixelDimensionsToRtPixelDimensionsnull247     fun verifyPixelDimensionsToRtPixelDimensions() {
248         val pixelDimensions: PixelDimensions = RuntimePixelDimensions(17, 18).toPixelDimensions()
249         assertThat(pixelDimensions.width).isEqualTo(17)
250         assertThat(pixelDimensions.height).isEqualTo(18)
251     }
252 
253     @Test
verifyRuntimeResizeEventToResizeEventnull254     fun verifyRuntimeResizeEventToResizeEvent() {
255         val resizeEvent: ResizeEvent =
256             RuntimeResizeEvent(RuntimeResizeEvent.RESIZE_STATE_START, RuntimeDimensions(1f, 3f, 5f))
257                 .toResizeEvent()
258         assertThat(resizeEvent.resizeState).isEqualTo(ResizeEvent.RESIZE_STATE_START)
259         assertThat(resizeEvent.newSize.width).isEqualTo(1f)
260         assertThat(resizeEvent.newSize.height).isEqualTo(3f)
261         assertThat(resizeEvent.newSize.depth).isEqualTo(5f)
262     }
263 
264     @Test
runtimeSpatialCapabilitiesToSpatialCapabilities_noCapabilitiesnull265     fun runtimeSpatialCapabilitiesToSpatialCapabilities_noCapabilities() {
266         val caps = RuntimeSpatialCapabilities(0).toSpatialCapabilities()
267         assertThat(caps.hasCapability(SpatialCapabilities.SPATIAL_CAPABILITY_UI)).isFalse()
268         assertThat(caps.hasCapability(SpatialCapabilities.SPATIAL_CAPABILITY_3D_CONTENT)).isFalse()
269         assertThat(caps.hasCapability(SpatialCapabilities.SPATIAL_CAPABILITY_PASSTHROUGH_CONTROL))
270             .isFalse()
271         assertThat(caps.hasCapability(SpatialCapabilities.SPATIAL_CAPABILITY_APP_ENVIRONMENT))
272             .isFalse()
273         assertThat(caps.hasCapability(SpatialCapabilities.SPATIAL_CAPABILITY_SPATIAL_AUDIO))
274             .isFalse()
275         assertThat(caps.hasCapability(SpatialCapabilities.SPATIAL_CAPABILITY_EMBED_ACTIVITY))
276             .isFalse()
277     }
278 
279     @Test
runtimeSpatialCapabilitiesToSpatialCapabilities_singleCapabilitynull280     fun runtimeSpatialCapabilitiesToSpatialCapabilities_singleCapability() {
281         var caps =
282             RuntimeSpatialCapabilities(RuntimeSpatialCapabilities.SPATIAL_CAPABILITY_UI)
283                 .toSpatialCapabilities()
284         assertThat(caps.hasCapability(SpatialCapabilities.SPATIAL_CAPABILITY_UI)).isTrue()
285         assertThat(caps.hasCapability(SpatialCapabilities.SPATIAL_CAPABILITY_3D_CONTENT)).isFalse()
286         assertThat(caps.hasCapability(SpatialCapabilities.SPATIAL_CAPABILITY_PASSTHROUGH_CONTROL))
287             .isFalse()
288         assertThat(caps.hasCapability(SpatialCapabilities.SPATIAL_CAPABILITY_APP_ENVIRONMENT))
289             .isFalse()
290         assertThat(caps.hasCapability(SpatialCapabilities.SPATIAL_CAPABILITY_SPATIAL_AUDIO))
291             .isFalse()
292         assertThat(caps.hasCapability(SpatialCapabilities.SPATIAL_CAPABILITY_EMBED_ACTIVITY))
293             .isFalse()
294 
295         caps =
296             RuntimeSpatialCapabilities(RuntimeSpatialCapabilities.SPATIAL_CAPABILITY_SPATIAL_AUDIO)
297                 .toSpatialCapabilities()
298         assertThat(caps.hasCapability(SpatialCapabilities.SPATIAL_CAPABILITY_UI)).isFalse()
299         assertThat(caps.hasCapability(SpatialCapabilities.SPATIAL_CAPABILITY_3D_CONTENT)).isFalse()
300         assertThat(caps.hasCapability(SpatialCapabilities.SPATIAL_CAPABILITY_PASSTHROUGH_CONTROL))
301             .isFalse()
302         assertThat(caps.hasCapability(SpatialCapabilities.SPATIAL_CAPABILITY_APP_ENVIRONMENT))
303             .isFalse()
304         assertThat(caps.hasCapability(SpatialCapabilities.SPATIAL_CAPABILITY_SPATIAL_AUDIO))
305             .isTrue()
306         assertThat(caps.hasCapability(SpatialCapabilities.SPATIAL_CAPABILITY_EMBED_ACTIVITY))
307             .isFalse()
308     }
309 
310     @Test
runtimeSpatialCapabilitiesToSpatialCapabilities_allCapabilitiesnull311     fun runtimeSpatialCapabilitiesToSpatialCapabilities_allCapabilities() {
312         val caps =
313             RuntimeSpatialCapabilities(
314                     RuntimeSpatialCapabilities.SPATIAL_CAPABILITY_UI or
315                         RuntimeSpatialCapabilities.SPATIAL_CAPABILITY_3D_CONTENT or
316                         RuntimeSpatialCapabilities.SPATIAL_CAPABILITY_PASSTHROUGH_CONTROL or
317                         RuntimeSpatialCapabilities.SPATIAL_CAPABILITY_APP_ENVIRONMENT or
318                         RuntimeSpatialCapabilities.SPATIAL_CAPABILITY_SPATIAL_AUDIO or
319                         RuntimeSpatialCapabilities.SPATIAL_CAPABILITY_EMBED_ACTIVITY
320                 )
321                 .toSpatialCapabilities()
322         assertThat(caps.hasCapability(SpatialCapabilities.SPATIAL_CAPABILITY_UI)).isTrue()
323         assertThat(caps.hasCapability(SpatialCapabilities.SPATIAL_CAPABILITY_3D_CONTENT)).isTrue()
324         assertThat(caps.hasCapability(SpatialCapabilities.SPATIAL_CAPABILITY_PASSTHROUGH_CONTROL))
325             .isTrue()
326         assertThat(caps.hasCapability(SpatialCapabilities.SPATIAL_CAPABILITY_APP_ENVIRONMENT))
327             .isTrue()
328         assertThat(caps.hasCapability(SpatialCapabilities.SPATIAL_CAPABILITY_SPATIAL_AUDIO))
329             .isTrue()
330         assertThat(caps.hasCapability(SpatialCapabilities.SPATIAL_CAPABILITY_EMBED_ACTIVITY))
331             .isTrue()
332     }
333 
334     @Test
runtimeSpatialCapabilitiesToSpatialCapabilities_mixedCapabilitiesnull335     fun runtimeSpatialCapabilitiesToSpatialCapabilities_mixedCapabilities() {
336         var caps =
337             RuntimeSpatialCapabilities(
338                     RuntimeSpatialCapabilities.SPATIAL_CAPABILITY_3D_CONTENT or
339                         RuntimeSpatialCapabilities.SPATIAL_CAPABILITY_SPATIAL_AUDIO
340                 )
341                 .toSpatialCapabilities()
342         assertThat(caps.hasCapability(SpatialCapabilities.SPATIAL_CAPABILITY_UI)).isFalse()
343         assertThat(caps.hasCapability(SpatialCapabilities.SPATIAL_CAPABILITY_3D_CONTENT)).isTrue()
344         assertThat(caps.hasCapability(SpatialCapabilities.SPATIAL_CAPABILITY_PASSTHROUGH_CONTROL))
345             .isFalse()
346         assertThat(caps.hasCapability(SpatialCapabilities.SPATIAL_CAPABILITY_APP_ENVIRONMENT))
347             .isFalse()
348         assertThat(caps.hasCapability(SpatialCapabilities.SPATIAL_CAPABILITY_SPATIAL_AUDIO))
349             .isTrue()
350         assertThat(caps.hasCapability(SpatialCapabilities.SPATIAL_CAPABILITY_EMBED_ACTIVITY))
351             .isFalse()
352 
353         caps =
354             RuntimeSpatialCapabilities(
355                     RuntimeSpatialCapabilities.SPATIAL_CAPABILITY_UI or
356                         RuntimeSpatialCapabilities.SPATIAL_CAPABILITY_PASSTHROUGH_CONTROL or
357                         RuntimeSpatialCapabilities.SPATIAL_CAPABILITY_APP_ENVIRONMENT or
358                         RuntimeSpatialCapabilities.SPATIAL_CAPABILITY_EMBED_ACTIVITY
359                 )
360                 .toSpatialCapabilities()
361         assertThat(caps.hasCapability(SpatialCapabilities.SPATIAL_CAPABILITY_UI)).isTrue()
362         assertThat(caps.hasCapability(SpatialCapabilities.SPATIAL_CAPABILITY_3D_CONTENT)).isFalse()
363         assertThat(caps.hasCapability(SpatialCapabilities.SPATIAL_CAPABILITY_PASSTHROUGH_CONTROL))
364             .isTrue()
365         assertThat(caps.hasCapability(SpatialCapabilities.SPATIAL_CAPABILITY_APP_ENVIRONMENT))
366             .isTrue()
367         assertThat(caps.hasCapability(SpatialCapabilities.SPATIAL_CAPABILITY_SPATIAL_AUDIO))
368             .isFalse()
369         assertThat(caps.hasCapability(SpatialCapabilities.SPATIAL_CAPABILITY_EMBED_ACTIVITY))
370             .isTrue()
371     }
372 
373     @Test
RtSpatialVisibilityToSpatialVisibility_convertsCorrectlynull374     fun RtSpatialVisibilityToSpatialVisibility_convertsCorrectly() {
375         assertThat(
376                 listOf(
377                         RuntimeSpatialVisibility(RuntimeSpatialVisibility.UNKNOWN),
378                         RuntimeSpatialVisibility(RuntimeSpatialVisibility.OUTSIDE_FOV),
379                         RuntimeSpatialVisibility(RuntimeSpatialVisibility.PARTIALLY_WITHIN_FOV),
380                         RuntimeSpatialVisibility(RuntimeSpatialVisibility.WITHIN_FOV),
381                     )
382                     .map { it.toSpatialVisibility() }
383             )
384             .containsExactly(
385                 SpatialVisibility(SpatialVisibility.UNKNOWN),
386                 SpatialVisibility(SpatialVisibility.OUTSIDE_FOV),
387                 SpatialVisibility(SpatialVisibility.PARTIALLY_WITHIN_FOV),
388                 SpatialVisibility(SpatialVisibility.WITHIN_FOV),
389             )
390             .inOrder()
391     }
392 
393     @Test
intToSpatialVisibility_convertsCorrectlynull394     fun intToSpatialVisibility_convertsCorrectly() {
395         assertThat(
396                 listOf(
397                         RuntimeSpatialVisibility.UNKNOWN,
398                         RuntimeSpatialVisibility.OUTSIDE_FOV,
399                         RuntimeSpatialVisibility.PARTIALLY_WITHIN_FOV,
400                         RuntimeSpatialVisibility.WITHIN_FOV,
401                     )
402                     .map { it.toSpatialVisibilityValue() }
403             )
404             .containsExactly(
405                 SpatialVisibility.UNKNOWN,
406                 SpatialVisibility.OUTSIDE_FOV,
407                 SpatialVisibility.PARTIALLY_WITHIN_FOV,
408                 SpatialVisibility.WITHIN_FOV,
409             )
410             .inOrder()
411     }
412 
413     @Test
intToSpatialVisibility_invalidValue_throwsErrornull414     fun intToSpatialVisibility_invalidValue_throwsError() {
415         assertFailsWith<IllegalStateException> { 100.toSpatialVisibilityValue() }
416     }
417 
418     @Test
intToMoveState_convertsCorrectlynull419     fun intToMoveState_convertsCorrectly() {
420         assertThat(
421                 listOf(
422                         RuntimeMoveEvent.MOVE_STATE_START,
423                         RuntimeMoveEvent.MOVE_STATE_ONGOING,
424                         RuntimeMoveEvent.MOVE_STATE_END,
425                     )
426                     .map { it.toMoveState() }
427             )
428             .containsExactly(
429                 MoveEvent.MOVE_STATE_START,
430                 MoveEvent.MOVE_STATE_ONGOING,
431                 MoveEvent.MOVE_STATE_END,
432             )
433             .inOrder()
434     }
435 
436     @Test
intToMoveState_invalidValue_throwsErrornull437     fun intToMoveState_invalidValue_throwsError() {
438         assertFailsWith<IllegalStateException> { 100.toMoveState() }
439     }
440 
441     @Test
intToResizeState_convertsCorrectlynull442     fun intToResizeState_convertsCorrectly() {
443         assertThat(
444                 listOf(
445                         RuntimeResizeEvent.RESIZE_STATE_UNKNOWN,
446                         RuntimeResizeEvent.RESIZE_STATE_START,
447                         RuntimeResizeEvent.RESIZE_STATE_ONGOING,
448                         RuntimeResizeEvent.RESIZE_STATE_END,
449                     )
450                     .map { it.toResizeState() }
451             )
452             .containsExactly(
453                 ResizeEvent.RESIZE_STATE_UNKNOWN,
454                 ResizeEvent.RESIZE_STATE_START,
455                 ResizeEvent.RESIZE_STATE_ONGOING,
456                 ResizeEvent.RESIZE_STATE_END,
457             )
458             .inOrder()
459     }
460 
461     @Test
intToResizeState_invalidValue_throwsErrornull462     fun intToResizeState_invalidValue_throwsError() {
463         assertFailsWith<IllegalStateException> { 100.toResizeState() }
464     }
465 
466     @Test
intToInputEventSource_convertsCorrectlynull467     fun intToInputEventSource_convertsCorrectly() {
468         assertThat(
469                 listOf(
470                         RuntimeInputEvent.SOURCE_UNKNOWN,
471                         RuntimeInputEvent.SOURCE_HEAD,
472                         RuntimeInputEvent.SOURCE_CONTROLLER,
473                         RuntimeInputEvent.SOURCE_HANDS,
474                         RuntimeInputEvent.SOURCE_MOUSE,
475                         RuntimeInputEvent.SOURCE_GAZE_AND_GESTURE,
476                     )
477                     .map { it.toInputEventSource() }
478             )
479             .containsExactly(
480                 InputEvent.SOURCE_UNKNOWN,
481                 InputEvent.SOURCE_HEAD,
482                 InputEvent.SOURCE_CONTROLLER,
483                 InputEvent.SOURCE_HANDS,
484                 InputEvent.SOURCE_MOUSE,
485                 InputEvent.SOURCE_GAZE_AND_GESTURE,
486             )
487             .inOrder()
488     }
489 
490     @Test
intToInputEventSource_invalidValue_throwsErrornull491     fun intToInputEventSource_invalidValue_throwsError() {
492         assertFailsWith<IllegalStateException> { 100.toInputEventSource() }
493     }
494 
495     @Test
intToInputEventPointerType_convertsCorrectlynull496     fun intToInputEventPointerType_convertsCorrectly() {
497         assertThat(
498                 listOf(
499                         RuntimeInputEvent.POINTER_TYPE_DEFAULT,
500                         RuntimeInputEvent.POINTER_TYPE_LEFT,
501                         RuntimeInputEvent.POINTER_TYPE_RIGHT,
502                     )
503                     .map { it.toInputEventPointerType() }
504             )
505             .containsExactly(
506                 InputEvent.POINTER_TYPE_DEFAULT,
507                 InputEvent.POINTER_TYPE_LEFT,
508                 InputEvent.POINTER_TYPE_RIGHT,
509             )
510             .inOrder()
511     }
512 
513     @Test
intToInputEventPointerType_invalidValue_throwsErrornull514     fun intToInputEventPointerType_invalidValue_throwsError() {
515         assertFailsWith<IllegalStateException> { 100.toInputEventPointerType() }
516     }
517 
518     @Test
intToSpatialCapability_convertsCorrectlynull519     fun intToSpatialCapability_convertsCorrectly() {
520         assertThat(
521                 listOf(
522                         RuntimeSpatialCapabilities.SPATIAL_CAPABILITY_UI,
523                         RuntimeSpatialCapabilities.SPATIAL_CAPABILITY_3D_CONTENT,
524                         RuntimeSpatialCapabilities.SPATIAL_CAPABILITY_PASSTHROUGH_CONTROL,
525                         RuntimeSpatialCapabilities.SPATIAL_CAPABILITY_APP_ENVIRONMENT,
526                         RuntimeSpatialCapabilities.SPATIAL_CAPABILITY_SPATIAL_AUDIO,
527                         RuntimeSpatialCapabilities.SPATIAL_CAPABILITY_EMBED_ACTIVITY,
528                     )
529                     .map { it.toSpatialCapability() }
530             )
531             .containsExactly(
532                 SpatialCapabilities.SPATIAL_CAPABILITY_UI,
533                 SpatialCapabilities.SPATIAL_CAPABILITY_3D_CONTENT,
534                 SpatialCapabilities.SPATIAL_CAPABILITY_PASSTHROUGH_CONTROL,
535                 SpatialCapabilities.SPATIAL_CAPABILITY_APP_ENVIRONMENT,
536                 SpatialCapabilities.SPATIAL_CAPABILITY_SPATIAL_AUDIO,
537                 SpatialCapabilities.SPATIAL_CAPABILITY_EMBED_ACTIVITY,
538             )
539             .inOrder()
540     }
541 
542     @Test
intToInputEventAction_convertsCorrectlynull543     fun intToInputEventAction_convertsCorrectly() {
544         assertThat(
545                 listOf(
546                         RuntimeInputEvent.ACTION_DOWN,
547                         RuntimeInputEvent.ACTION_UP,
548                         RuntimeInputEvent.ACTION_MOVE,
549                         RuntimeInputEvent.ACTION_CANCEL,
550                         RuntimeInputEvent.ACTION_HOVER_MOVE,
551                         RuntimeInputEvent.ACTION_HOVER_ENTER,
552                         RuntimeInputEvent.ACTION_HOVER_EXIT,
553                     )
554                     .map { it.toInputEventAction() }
555             )
556             .containsExactly(
557                 InputEvent.ACTION_DOWN,
558                 InputEvent.ACTION_UP,
559                 InputEvent.ACTION_MOVE,
560                 InputEvent.ACTION_CANCEL,
561                 InputEvent.ACTION_HOVER_MOVE,
562                 InputEvent.ACTION_HOVER_ENTER,
563                 InputEvent.ACTION_HOVER_EXIT,
564             )
565             .inOrder()
566     }
567 
568     @Test
intToInputEventAction_invalidValue_throwsErrornull569     fun intToInputEventAction_invalidValue_throwsError() {
570         assertFailsWith<IllegalStateException> { 100.toInputEventAction() }
571     }
572 
573     @Test
anchorPlacementToRuntimeAnchorPlacement_setsCorrectlynull574     fun anchorPlacementToRuntimeAnchorPlacement_setsCorrectly() {
575         val mockRuntime = mock<JxrPlatformAdapter>()
576         val mockAnchorPlacement1 = mock<RtAnchorPlacement>()
577         val mockAnchorPlacement2 = mock<RtAnchorPlacement>()
578         whenever(
579                 mockRuntime.createAnchorPlacementForPlanes(
580                     setOf(RtPlaneType.HORIZONTAL),
581                     setOf(RtPlaneSemantic.ANY),
582                 )
583             )
584             .thenReturn(mockAnchorPlacement1)
585         whenever(
586                 mockRuntime.createAnchorPlacementForPlanes(
587                     setOf(RtPlaneType.ANY),
588                     setOf(RtPlaneSemantic.WALL, RtPlaneSemantic.FLOOR),
589                 )
590             )
591             .thenReturn(mockAnchorPlacement2)
592 
593         val anchorPlacement1 =
594             AnchorPlacement.createForPlanes(planeTypeFilter = setOf(PlaneType.HORIZONTAL))
595         val anchorPlacement2 =
596             AnchorPlacement.createForPlanes(
597                 planeSemanticFilter = setOf(PlaneSemantic.WALL, PlaneSemantic.FLOOR)
598             )
599 
600         val rtPlacementSet =
601             setOf(anchorPlacement1, anchorPlacement2).toRtAnchorPlacement(mockRuntime)
602 
603         assertThat(rtPlacementSet.size).isEqualTo(2)
604         assertThat(rtPlacementSet).containsExactly(mockAnchorPlacement1, mockAnchorPlacement2)
605     }
606 
607     @Test
anchorPlacementToRuntimeAnchotPlacementEmptySet_returnsEmptySetnull608     fun anchorPlacementToRuntimeAnchotPlacementEmptySet_returnsEmptySet() {
609         val mockRuntime = mock<JxrPlatformAdapter>()
610 
611         val rtPlacementSet = emptySet<AnchorPlacement>().toRtAnchorPlacement(mockRuntime)
612 
613         assertThat(rtPlacementSet).isEmpty()
614     }
615 
616     @Test
intToTextureSampler_convertsCorrectlynull617     fun intToTextureSampler_convertsCorrectly() {
618         val sampler: TextureSampler =
619             TextureSampler(
620                 TextureSampler.MinFilter.NEAREST,
621                 TextureSampler.MagFilter.LINEAR,
622                 TextureSampler.WrapMode.CLAMP_TO_EDGE,
623                 TextureSampler.WrapMode.REPEAT,
624                 TextureSampler.WrapMode.MIRRORED_REPEAT,
625                 TextureSampler.CompareMode.NONE,
626                 TextureSampler.CompareFunc.LE,
627                 2,
628             )
629 
630         val rtSampler: RuntimeTextureSampler = sampler.toRtTextureSampler()
631 
632         assertThat(rtSampler.wrapModeS).isEqualTo(RuntimeTextureSampler.CLAMP_TO_EDGE)
633         assertThat(rtSampler.wrapModeT).isEqualTo(RuntimeTextureSampler.REPEAT)
634         assertThat(rtSampler.wrapModeR).isEqualTo(RuntimeTextureSampler.MIRRORED_REPEAT)
635         assertThat(rtSampler.minFilter).isEqualTo(RuntimeTextureSampler.NEAREST)
636         assertThat(rtSampler.magFilter).isEqualTo(RuntimeTextureSampler.MAG_LINEAR)
637         assertThat(rtSampler.compareMode).isEqualTo(RuntimeTextureSampler.NONE)
638         assertThat(rtSampler.compareFunc).isEqualTo(RuntimeTextureSampler.LE)
639         assertThat(rtSampler.anisotropyLog2).isEqualTo(2)
640     }
641 
642     @Test
runtimeHitTestResultToHitTestResult_convertsCorrectlynull643     fun runtimeHitTestResultToHitTestResult_convertsCorrectly() {
644         val hitPosition = Vector3(1f, 2f, 3f)
645         val surfaceNormal = Vector3(4f, 5f, 6f)
646         val surfaceType = RuntimeHitTestResult.HitTestSurfaceType.HIT_TEST_RESULT_SURFACE_TYPE_PLANE
647         val distance = 7f
648         val rtHitTestResult =
649             RuntimeHitTestResult(hitPosition, surfaceNormal, surfaceType, distance)
650         val hitTestResult = rtHitTestResult.toHitTestResult()
651         assertThat(hitTestResult.hitPosition).isEqualTo(hitPosition)
652         assertThat(hitTestResult.surfaceNormal).isEqualTo(surfaceNormal)
653         assertThat(hitTestResult.surfaceType).isEqualTo(HitTestResult.SurfaceType.PLANE)
654         assertThat(hitTestResult.distance).isEqualTo(distance)
655     }
656 
657     @Test
runtimeHitTestResultWithNullToHitTestResult_convertsCorrectlynull658     fun runtimeHitTestResultWithNullToHitTestResult_convertsCorrectly() {
659         val hitPosition = null
660         val surfaceNormal = null
661         val surfaceType =
662             RuntimeHitTestResult.HitTestSurfaceType.HIT_TEST_RESULT_SURFACE_TYPE_UNKNOWN
663         val distance = Float.POSITIVE_INFINITY
664         val rtHitTestResult =
665             RuntimeHitTestResult(hitPosition, surfaceNormal, surfaceType, distance)
666 
667         val hitTestResult = rtHitTestResult.toHitTestResult()
668 
669         assertThat(hitTestResult.hitPosition).isEqualTo(hitPosition)
670         assertThat(hitTestResult.surfaceNormal).isEqualTo(surfaceNormal)
671         assertThat(hitTestResult.surfaceType).isEqualTo(HitTestResult.SurfaceType.UNKNOWN)
672         assertThat(hitTestResult.distance).isEqualTo(distance)
673     }
674 
675     @Test
hitTestFilterToRuntimeHitTestFilter_convertsCorrectlynull676     fun hitTestFilterToRuntimeHitTestFilter_convertsCorrectly() {
677         assertThat(
678                 listOf(
679                         0,
680                         HitTestFilter.SELF_SCENE,
681                         HitTestFilter.OTHER_SCENES,
682                         (HitTestFilter.SELF_SCENE or HitTestFilter.OTHER_SCENES),
683                     )
684                     .map { it.toRtHitTestFilter() }
685             )
686             .containsExactly(
687                 0,
688                 RtHitTestFilter.SELF_SCENE,
689                 RtHitTestFilter.OTHER_SCENES,
690                 RtHitTestFilter.SELF_SCENE or RtHitTestFilter.OTHER_SCENES,
691             )
692     }
693 
694     @Test
runtimeHitTestSurfaceTypeToHitTestSurfaceType_convertsCorrectlynull695     fun runtimeHitTestSurfaceTypeToHitTestSurfaceType_convertsCorrectly() {
696         assertThat(
697                 listOf(
698                         RuntimeHitTestResult.HitTestSurfaceType
699                             .HIT_TEST_RESULT_SURFACE_TYPE_UNKNOWN,
700                         RuntimeHitTestResult.HitTestSurfaceType.HIT_TEST_RESULT_SURFACE_TYPE_PLANE,
701                         RuntimeHitTestResult.HitTestSurfaceType.HIT_TEST_RESULT_SURFACE_TYPE_OBJECT,
702                     )
703                     .map { it.toHitTestSurfaceType() }
704             )
705             .containsExactly(
706                 HitTestResult.SurfaceType.UNKNOWN,
707                 HitTestResult.SurfaceType.PLANE,
708                 HitTestResult.SurfaceType.OBJECT,
709             )
710     }
711 }
712