diff --git a/ApiDemos/project/java-app/build.gradle.kts b/ApiDemos/project/java-app/build.gradle.kts index 9187a69e4..9c1e72613 100644 --- a/ApiDemos/project/java-app/build.gradle.kts +++ b/ApiDemos/project/java-app/build.gradle.kts @@ -81,6 +81,7 @@ dependencies { androidTestImplementation(libs.junit) androidTestImplementation(libs.espresso.core) androidTestImplementation(libs.truth) + androidTestImplementation(libs.ext.junit) } secrets { diff --git a/README.md b/README.md index 72d660fca..732f33b08 100644 --- a/README.md +++ b/README.md @@ -51,6 +51,16 @@ To verify that all samples build and pass tests, run: ./scripts/verify_all.sh ``` +To run instrumentation tests on a connected device or emulator, use: + +```bash +# For Wear OS devices/emulators +./scripts/verify_all.sh --connected-wear + +# For Mobile (Handheld) devices/emulators +./scripts/verify_all.sh --connected-mobile +``` + Alternatively, use the `gradlew build` command to build the project directly or download an APK under [releases](https://github.com/googlemaps/android-samples/releases). diff --git a/WearOS/Wearable/build.gradle.kts b/WearOS/Wearable/build.gradle.kts index 229d6bcb8..eac685661 100644 --- a/WearOS/Wearable/build.gradle.kts +++ b/WearOS/Wearable/build.gradle.kts @@ -29,6 +29,7 @@ android { targetSdk = libs.versions.targetSdk.get().toInt() versionCode = 1 versionName = libs.versions.versionName.get() + testInstrumentationRunner = "androidx.test.runner.AndroidJUnitRunner" } buildTypes { @@ -77,6 +78,13 @@ dependencies { // This dependency is necessary for ambient mode implementation(libs.wear) + + // Android Test Dependencies + androidTestImplementation(libs.ext.junit) + androidTestImplementation(libs.espresso.core) + androidTestImplementation(libs.uiautomator) + androidTestImplementation(libs.truth) + androidTestImplementation(libs.junit) // If your project does not use a version catalog, you can use the following dependencies instead: // @@ -84,6 +92,11 @@ dependencies { // implementation("com.google.android.support:wearable:2.9.0") // implementation("com.google.android.gms:play-services-maps:20.0.0") // implementation("androidx.wear:wear:1.3.0") + // androidTestImplementation("androidx.test.ext:junit:1.1.5") + // androidTestImplementation("androidx.test.espresso:espresso-core:3.5.1") + // androidTestImplementation("androidx.test.uiautomator:uiautomator:2.3.0") + // androidTestImplementation("com.google.truth:truth:1.4.2") + // androidTestImplementation("junit:junit:4.13.2") } // [END maps_wear_os_dependencies] diff --git a/WearOS/Wearable/src/androidTest/java/com/example/wearosmap/AppLaunchTest.kt b/WearOS/Wearable/src/androidTest/java/com/example/wearosmap/AppLaunchTest.kt new file mode 100644 index 000000000..1e03b7d72 --- /dev/null +++ b/WearOS/Wearable/src/androidTest/java/com/example/wearosmap/AppLaunchTest.kt @@ -0,0 +1,79 @@ +/* + * Copyright 2026 Google LLC + * + * 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.example.wearosmap + +import androidx.test.core.app.ActivityScenario +import androidx.test.ext.junit.runners.AndroidJUnit4 +import androidx.test.filters.LargeTest +import androidx.test.platform.app.InstrumentationRegistry +import androidx.test.uiautomator.UiDevice +import androidx.test.uiautomator.UiSelector +import com.example.wearosmap.kt.MainActivity +import com.google.common.truth.Truth.assertThat +import org.junit.Test +import org.junit.runner.RunWith + +@RunWith(AndroidJUnit4::class) +@LargeTest +class AppLaunchTest { + + @Test + fun appLaunchesSuccessfully() { + // Launch the activity + ActivityScenario.launch(MainActivity::class.java).use { scenario -> + scenario.onActivity { activity -> + assertThat(activity).isNotNull() + } + } + } + + @Test + fun mapContainerIsDisplayed() { + // Launch the activity + ActivityScenario.launch(MainActivity::class.java).use { + val device = UiDevice.getInstance(InstrumentationRegistry.getInstrumentation()) + + // Wait for the map container to appear + val mapContainer = device.findObject(UiSelector().resourceId("com.example.wearos:id/map_container")) + assertThat(mapContainer.waitForExists(5000)).isTrue() + } + } + + @Test + fun markerTitleIsDisplayed() { + // Launch the activity + ActivityScenario.launch(MainActivity::class.java).use { + val device = UiDevice.getInstance(InstrumentationRegistry.getInstrumentation()) + + // Wait for the marker title "Sydney Opera House" to appear + // Note: This relies on the Google Map rendering the marker and it being visible/accessible. + // In a real device/emulator, this might need more robust waiting logic or accessibility settings. + val markerTitle = device.findObject(UiSelector().descriptionContains("Sydney Opera House")) + + // Allow some time for map loading and rendering + if (!markerTitle.exists()) { + markerTitle.waitForExists(10000) + } + + // Verify if found (Bonus check, might flake on some emulators if map doesn't render) + // We use a softer check here or assertion if we are confident. + // For now, let's assert it exists if we want to be strict, or just log if we want to be lenient. + // Given the requirement "Verify Map and Marker presence (Bonus)", we'll try to assert. + assertThat(markerTitle.exists()).isTrue() + } + } +} diff --git a/WearOS/Wearable/src/main/AndroidManifest.xml b/WearOS/Wearable/src/main/AndroidManifest.xml index b49353796..d69f72d9a 100644 --- a/WearOS/Wearable/src/main/AndroidManifest.xml +++ b/WearOS/Wearable/src/main/AndroidManifest.xml @@ -32,6 +32,9 @@ android:name="com.google.android.wearable.standalone" android:value="true" /> + + + diff --git a/scripts/verify_all.sh b/scripts/verify_all.sh index dab8c3df2..7f47067ee 100755 --- a/scripts/verify_all.sh +++ b/scripts/verify_all.sh @@ -52,6 +52,50 @@ MODULES=( ":tutorials:kotlin:Polygons" ) +# Parse arguments +RUN_CONNECTED_WEAR=false +RUN_CONNECTED_MOBILE=false + +print_usage() { + echo "Usage: $0 [OPTIONS]" + echo "" + echo "Verifies all Android modules in the project by running assemble, test, and lint." + echo "" + echo "Options:" + echo " -h, --help Show this help message and exit" + echo " --connected-wear Run connected instrumentation tests for Wear OS modules" + echo " --connected-mobile Run connected instrumentation tests for Mobile (Handheld) modules" + echo " --connected Run ALL connected tests (Wear OS + Mobile) - Warning: Requires multiple emulators" + echo "" +} + +for arg in "$@"; do + case $arg in + -h|--help) + print_usage + exit 0 + ;; + --connected-wear) + RUN_CONNECTED_WEAR=true + echo "Running with Connected Wear OS Tests..." + ;; + --connected-mobile) + RUN_CONNECTED_MOBILE=true + echo "Running with Connected Mobile Tests..." + ;; + --connected) + RUN_CONNECTED_WEAR=true + RUN_CONNECTED_MOBILE=true + echo "Warning: Running ALL connected tests. This requires multiple simultaneous emulators (Wear + Handheld) or may fail." + ;; + *) + echo "Unknown option: $arg" + print_usage + exit 1 + ;; + esac +done + # Function to run verification for a module verify_module() { local module=$1 @@ -60,6 +104,7 @@ verify_module() { local assembleTask=":assembleDebug" local testTask=":testDebugUnitTest" local lintTask=":lintDebug" + local connectedTask="" if [[ "$module" == ":snippets:app" ]]; then assembleTask=":assembleGmsDebug" @@ -67,8 +112,28 @@ verify_module() { lintTask=":lintGmsDebug" fi - # Run assemble, lint, and test - if ./gradlew "$module$assembleTask" "$module$testTask" "$module$lintTask"; then + # Define connected test task if enabled + if [ "$RUN_CONNECTED_WEAR" = true ] && [[ "$module" == ":WearOS:Wearable" ]]; then + connectedTask=":connectedDebugAndroidTest" + elif [ "$RUN_CONNECTED_MOBILE" = true ] && [[ "$module" != ":WearOS:Wearable" ]]; then + if [[ "$module" == ":snippets:app" ]]; then + connectedTask=":connectedGmsDebugAndroidTest" + else + connectedTask=":connectedDebugAndroidTest" + fi + fi + # Note: If both flags are set, both types run (for their respective modules). + + # Build command + local cmd="./gradlew $module$assembleTask $module$testTask $module$lintTask" + + if [ -n "$connectedTask" ]; then + cmd="$cmd $module$connectedTask" + fi + + # Run assemble, lint, and test (and connected if requested) + echo "Running: $cmd" + if $cmd; then echo -e "${GREEN}SUCCESS: $module verified.${NC}" else echo -e "${RED}FAILURE: $module failed verification.${NC}"