From 1994a8ed62ee041c46421d782b40cbdb42c32dd8 Mon Sep 17 00:00:00 2001 From: Tram Bui Date: Wed, 18 Mar 2026 14:09:39 -0700 Subject: [PATCH 1/2] add in unit tests for projected with the JXR projected testing library --- gradle/libs.versions.toml | 12 ++++ samples/gemini-live-todo/build.gradle.kts | 16 +++++ .../geminilivetodo/ProjectedContextTests.kt | 71 +++++++++++++++++++ 3 files changed, 99 insertions(+) create mode 100644 samples/gemini-live-todo/src/test/java/com/android/ai/samples/geminilivetodo/ProjectedContextTests.kt diff --git a/gradle/libs.versions.toml b/gradle/libs.versions.toml index ca4454d0..e09585bd 100644 --- a/gradle/libs.versions.toml +++ b/gradle/libs.versions.toml @@ -37,6 +37,12 @@ material3WindowSizeClass = "1.3.2" richtext = "1.0.0-alpha02" glimmer = "1.0.0-alpha08" projected = "1.0.0-alpha05" +coreKtxVersion = "1.7.0" +truth = "1.4.4" +robolectric = "4.16.1" +mockito = "4.11.0" +mockitoKotlin = "4.1.0" +runner = "1.7.0" [libraries] @@ -89,6 +95,12 @@ androidx-lifecycle-viewmodel-android = { group = "androidx.lifecycle", name = "l androidx-exifinterface = { group = "androidx.exifinterface", name = "exifinterface", version.ref = "exifinterface" } androidx-xr-glimmer = { group = "androidx.xr.glimmer", name = "glimmer", version.ref = "glimmer" } androidx-xr-projected = { group = "androidx.xr.projected", name = "projected", version.ref = "projected" } +androidx-projected-testing = { group = "androidx.xr.projected", name = "projected-testing", version.ref = "projected" } +core-ktx = { group = "androidx.test", name = "core-ktx", version.ref = "coreKtxVersion" } +truth = { group = "com.google.truth", name = "truth", version.ref = "truth" } +robolectric = { group = "org.robolectric", name = "robolectric", version.ref = "robolectric" } +mockito-kotlin = { group = "org.mockito.kotlin", name = "mockito-kotlin", version.ref = "mockitoKotlin" } +androidx-runner = { group = "androidx.test", name = "runner", version.ref = "runner" } [plugins] android-application = { id = "com.android.application", version.ref = "agp" } diff --git a/samples/gemini-live-todo/build.gradle.kts b/samples/gemini-live-todo/build.gradle.kts index 05ccb02d..a9194a9c 100644 --- a/samples/gemini-live-todo/build.gradle.kts +++ b/samples/gemini-live-todo/build.gradle.kts @@ -48,6 +48,11 @@ android { kotlinOptions { jvmTarget = "17" } + testOptions { + unitTests { + isIncludeAndroidResources = true + } + } } dependencies { @@ -64,12 +69,23 @@ dependencies { implementation(libs.androidx.material3) implementation(libs.hilt.android) implementation(libs.ui.tooling.preview) + implementation(libs.core.ktx) ksp(libs.hilt.compiler) implementation(project(":ui-component")) implementation(libs.hilt.navigation.compose) implementation(libs.androidx.activity.compose) implementation(libs.kotlinx.serialization.json) + implementation(libs.androidx.runner) + + testImplementation(libs.junit) + testImplementation(libs.androidx.junit) + testImplementation(libs.androidx.projected.testing) + testImplementation(libs.truth) + testImplementation(libs.robolectric) + testImplementation(libs.core.ktx) + testImplementation(libs.mockito.kotlin) + androidTestImplementation(libs.androidx.junit) androidTestImplementation(libs.androidx.espresso.core) } diff --git a/samples/gemini-live-todo/src/test/java/com/android/ai/samples/geminilivetodo/ProjectedContextTests.kt b/samples/gemini-live-todo/src/test/java/com/android/ai/samples/geminilivetodo/ProjectedContextTests.kt new file mode 100644 index 00000000..74b31759 --- /dev/null +++ b/samples/gemini-live-todo/src/test/java/com/android/ai/samples/geminilivetodo/ProjectedContextTests.kt @@ -0,0 +1,71 @@ +package com.android.ai.samples.geminilivetodo + +import android.content.Context +import android.os.Build +import androidx.test.core.app.ApplicationProvider +import androidx.test.ext.junit.runners.AndroidJUnit4 +import androidx.xr.projected.ProjectedContext +import androidx.xr.projected.ProjectedDeviceController +import androidx.xr.projected.ProjectedDeviceController.Capability.Companion.CAPABILITY_VISUAL_UI +import androidx.xr.projected.experimental.ExperimentalProjectedApi +import androidx.xr.projected.testing.ProjectedTestRule +import com.google.common.truth.Truth.assertThat +import kotlinx.coroutines.runBlocking +import org.junit.Assert.assertThrows +import org.junit.Rule +import org.junit.Test +import org.junit.runner.RunWith +import org.robolectric.annotation.Config +import kotlin.intArrayOf + +@OptIn(ExperimentalProjectedApi::class) +@Config(sdk = [Build.VERSION_CODES.BAKLAVA]) +@RunWith(AndroidJUnit4::class) +class ProjectedContextTests { + @get:Rule + val projectedTestRule = ProjectedTestRule() + + private val context: Context + get() = ApplicationProvider.getApplicationContext() + + private lateinit var projectedDeviceController: ProjectedDeviceController + + + @Test + fun app_initializesProjectedContext_whenDeviceIsConnected() { + val projectedContext = ProjectedContext.createProjectedDeviceContext(context) + assertThat(projectedContext).isNotNull() + } + + @Test + fun app_throwsException_whenDeviceDisconnected() { + projectedTestRule.isDeviceConnected = false + + assertThrows(IllegalStateException::class.java) { + ProjectedContext.createProjectedDeviceContext(context) + } + } + + @Test + fun capabilities_includesVisualUiByDefault_returnsCapabilityVisualUi() { + projectedTestRule.launchTestProjectedDeviceActivity { activity -> + runBlocking { + projectedDeviceController = ProjectedDeviceController.create(activity) + } + + assertThat(projectedDeviceController.capabilities).contains(CAPABILITY_VISUAL_UI) + } + } + + @Test + fun capabilities_emptyCapabilities_doesNotReturnCapabilityVisualUi() { + projectedTestRule.launchTestProjectedDeviceActivity { activity -> + projectedTestRule.capabilities = setOf() + runBlocking { + projectedDeviceController = ProjectedDeviceController.create(activity) + } + + assertThat(projectedDeviceController.capabilities).doesNotContain(CAPABILITY_VISUAL_UI) + } + } +} From 4904da01606298149201e683664da0f2160c7609 Mon Sep 17 00:00:00 2001 From: Tram Bui Date: Wed, 18 Mar 2026 14:31:12 -0700 Subject: [PATCH 2/2] fix gemini code reviews --- gradle/libs.versions.toml | 5 ++--- samples/gemini-live-todo/build.gradle.kts | 2 -- .../samples/geminilivetodo/ProjectedContextTests.kt | 13 ++++++------- 3 files changed, 8 insertions(+), 12 deletions(-) diff --git a/gradle/libs.versions.toml b/gradle/libs.versions.toml index e09585bd..36b5a38a 100644 --- a/gradle/libs.versions.toml +++ b/gradle/libs.versions.toml @@ -37,12 +37,11 @@ material3WindowSizeClass = "1.3.2" richtext = "1.0.0-alpha02" glimmer = "1.0.0-alpha08" projected = "1.0.0-alpha05" -coreKtxVersion = "1.7.0" +coreKtxVersion = "1.6.1" truth = "1.4.4" robolectric = "4.16.1" -mockito = "4.11.0" mockitoKotlin = "4.1.0" -runner = "1.7.0" +runner = "1.6.1" [libraries] diff --git a/samples/gemini-live-todo/build.gradle.kts b/samples/gemini-live-todo/build.gradle.kts index a9194a9c..484e9f86 100644 --- a/samples/gemini-live-todo/build.gradle.kts +++ b/samples/gemini-live-todo/build.gradle.kts @@ -69,13 +69,11 @@ dependencies { implementation(libs.androidx.material3) implementation(libs.hilt.android) implementation(libs.ui.tooling.preview) - implementation(libs.core.ktx) ksp(libs.hilt.compiler) implementation(project(":ui-component")) implementation(libs.hilt.navigation.compose) implementation(libs.androidx.activity.compose) implementation(libs.kotlinx.serialization.json) - implementation(libs.androidx.runner) testImplementation(libs.junit) diff --git a/samples/gemini-live-todo/src/test/java/com/android/ai/samples/geminilivetodo/ProjectedContextTests.kt b/samples/gemini-live-todo/src/test/java/com/android/ai/samples/geminilivetodo/ProjectedContextTests.kt index 74b31759..aaa22841 100644 --- a/samples/gemini-live-todo/src/test/java/com/android/ai/samples/geminilivetodo/ProjectedContextTests.kt +++ b/samples/gemini-live-todo/src/test/java/com/android/ai/samples/geminilivetodo/ProjectedContextTests.kt @@ -28,9 +28,6 @@ class ProjectedContextTests { private val context: Context get() = ApplicationProvider.getApplicationContext() - private lateinit var projectedDeviceController: ProjectedDeviceController - - @Test fun app_initializesProjectedContext_whenDeviceIsConnected() { val projectedContext = ProjectedContext.createProjectedDeviceContext(context) @@ -46,11 +43,12 @@ class ProjectedContextTests { } } + @Test fun capabilities_includesVisualUiByDefault_returnsCapabilityVisualUi() { projectedTestRule.launchTestProjectedDeviceActivity { activity -> - runBlocking { - projectedDeviceController = ProjectedDeviceController.create(activity) + val projectedDeviceController = runBlocking { + ProjectedDeviceController.create(activity) } assertThat(projectedDeviceController.capabilities).contains(CAPABILITY_VISUAL_UI) @@ -61,8 +59,9 @@ class ProjectedContextTests { fun capabilities_emptyCapabilities_doesNotReturnCapabilityVisualUi() { projectedTestRule.launchTestProjectedDeviceActivity { activity -> projectedTestRule.capabilities = setOf() - runBlocking { - projectedDeviceController = ProjectedDeviceController.create(activity) + + val projectedDeviceController = runBlocking { + ProjectedDeviceController.create(activity) } assertThat(projectedDeviceController.capabilities).doesNotContain(CAPABILITY_VISUAL_UI)