Skip to content

Commit e4f2883

Browse files
authored
fix: Resolve WearOS NoClassDefFoundError and add regression tests (#2349)
* fix: Resolve WearOS crash and add regression tests Fixes NoClassDefFoundError by adding org.apache.http.legacy. Adds AppLaunchTest regression test. Updates verify_all.sh with --connected flag. * docs: Document verify_all.sh usage and flags Adds --help, --connected-wear, and --connected-mobile flags to verify_all.sh. Updates README.md with usage instructions. * fix: Add missing copyright header to AppLaunchTest.kt
1 parent 7a4b5df commit e4f2883

File tree

6 files changed

+173
-2
lines changed

6 files changed

+173
-2
lines changed

ApiDemos/project/java-app/build.gradle.kts

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -81,6 +81,7 @@ dependencies {
8181
androidTestImplementation(libs.junit)
8282
androidTestImplementation(libs.espresso.core)
8383
androidTestImplementation(libs.truth)
84+
androidTestImplementation(libs.ext.junit)
8485
}
8586

8687
secrets {

README.md

Lines changed: 10 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -51,6 +51,16 @@ To verify that all samples build and pass tests, run:
5151
./scripts/verify_all.sh
5252
```
5353

54+
To run instrumentation tests on a connected device or emulator, use:
55+
56+
```bash
57+
# For Wear OS devices/emulators
58+
./scripts/verify_all.sh --connected-wear
59+
60+
# For Mobile (Handheld) devices/emulators
61+
./scripts/verify_all.sh --connected-mobile
62+
```
63+
5464
Alternatively, use the `gradlew build` command to build the project directly or download an APK
5565
under [releases](https://github.com/googlemaps/android-samples/releases).
5666

WearOS/Wearable/build.gradle.kts

Lines changed: 13 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -29,6 +29,7 @@ android {
2929
targetSdk = libs.versions.targetSdk.get().toInt()
3030
versionCode = 1
3131
versionName = libs.versions.versionName.get()
32+
testInstrumentationRunner = "androidx.test.runner.AndroidJUnitRunner"
3233
}
3334

3435
buildTypes {
@@ -77,13 +78,25 @@ dependencies {
7778

7879
// This dependency is necessary for ambient mode
7980
implementation(libs.wear)
81+
82+
// Android Test Dependencies
83+
androidTestImplementation(libs.ext.junit)
84+
androidTestImplementation(libs.espresso.core)
85+
androidTestImplementation(libs.uiautomator)
86+
androidTestImplementation(libs.truth)
87+
androidTestImplementation(libs.junit)
8088

8189
// If your project does not use a version catalog, you can use the following dependencies instead:
8290
//
8391
// compileOnly("com.google.android.wearable:wearable:2.9.0")
8492
// implementation("com.google.android.support:wearable:2.9.0")
8593
// implementation("com.google.android.gms:play-services-maps:20.0.0")
8694
// implementation("androidx.wear:wear:1.3.0")
95+
// androidTestImplementation("androidx.test.ext:junit:1.1.5")
96+
// androidTestImplementation("androidx.test.espresso:espresso-core:3.5.1")
97+
// androidTestImplementation("androidx.test.uiautomator:uiautomator:2.3.0")
98+
// androidTestImplementation("com.google.truth:truth:1.4.2")
99+
// androidTestImplementation("junit:junit:4.13.2")
87100
}
88101
// [END maps_wear_os_dependencies]
89102

Lines changed: 79 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,79 @@
1+
/*
2+
* Copyright 2026 Google LLC
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 com.example.wearosmap
18+
19+
import androidx.test.core.app.ActivityScenario
20+
import androidx.test.ext.junit.runners.AndroidJUnit4
21+
import androidx.test.filters.LargeTest
22+
import androidx.test.platform.app.InstrumentationRegistry
23+
import androidx.test.uiautomator.UiDevice
24+
import androidx.test.uiautomator.UiSelector
25+
import com.example.wearosmap.kt.MainActivity
26+
import com.google.common.truth.Truth.assertThat
27+
import org.junit.Test
28+
import org.junit.runner.RunWith
29+
30+
@RunWith(AndroidJUnit4::class)
31+
@LargeTest
32+
class AppLaunchTest {
33+
34+
@Test
35+
fun appLaunchesSuccessfully() {
36+
// Launch the activity
37+
ActivityScenario.launch(MainActivity::class.java).use { scenario ->
38+
scenario.onActivity { activity ->
39+
assertThat(activity).isNotNull()
40+
}
41+
}
42+
}
43+
44+
@Test
45+
fun mapContainerIsDisplayed() {
46+
// Launch the activity
47+
ActivityScenario.launch(MainActivity::class.java).use {
48+
val device = UiDevice.getInstance(InstrumentationRegistry.getInstrumentation())
49+
50+
// Wait for the map container to appear
51+
val mapContainer = device.findObject(UiSelector().resourceId("com.example.wearos:id/map_container"))
52+
assertThat(mapContainer.waitForExists(5000)).isTrue()
53+
}
54+
}
55+
56+
@Test
57+
fun markerTitleIsDisplayed() {
58+
// Launch the activity
59+
ActivityScenario.launch(MainActivity::class.java).use {
60+
val device = UiDevice.getInstance(InstrumentationRegistry.getInstrumentation())
61+
62+
// Wait for the marker title "Sydney Opera House" to appear
63+
// Note: This relies on the Google Map rendering the marker and it being visible/accessible.
64+
// In a real device/emulator, this might need more robust waiting logic or accessibility settings.
65+
val markerTitle = device.findObject(UiSelector().descriptionContains("Sydney Opera House"))
66+
67+
// Allow some time for map loading and rendering
68+
if (!markerTitle.exists()) {
69+
markerTitle.waitForExists(10000)
70+
}
71+
72+
// Verify if found (Bonus check, might flake on some emulators if map doesn't render)
73+
// We use a softer check here or assertion if we are confident.
74+
// For now, let's assert it exists if we want to be strict, or just log if we want to be lenient.
75+
// Given the requirement "Verify Map and Marker presence (Bonus)", we'll try to assert.
76+
assertThat(markerTitle.exists()).isTrue()
77+
}
78+
}
79+
}

WearOS/Wearable/src/main/AndroidManifest.xml

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -32,6 +32,9 @@
3232
android:name="com.google.android.wearable.standalone"
3333
android:value="true" />
3434

35+
<!-- Required for Maps SDK on Wear OS devices -->
36+
<uses-library android:name="org.apache.http.legacy" android:required="false" />
37+
3538
<!-- Reference the wearable shared library required to support ambient mode. -->
3639
<uses-library android:name="com.google.android.wearable" android:required="false" />
3740

scripts/verify_all.sh

Lines changed: 67 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -52,6 +52,50 @@ MODULES=(
5252
":tutorials:kotlin:Polygons"
5353
)
5454

55+
# Parse arguments
56+
RUN_CONNECTED_WEAR=false
57+
RUN_CONNECTED_MOBILE=false
58+
59+
print_usage() {
60+
echo "Usage: $0 [OPTIONS]"
61+
echo ""
62+
echo "Verifies all Android modules in the project by running assemble, test, and lint."
63+
echo ""
64+
echo "Options:"
65+
echo " -h, --help Show this help message and exit"
66+
echo " --connected-wear Run connected instrumentation tests for Wear OS modules"
67+
echo " --connected-mobile Run connected instrumentation tests for Mobile (Handheld) modules"
68+
echo " --connected Run ALL connected tests (Wear OS + Mobile) - Warning: Requires multiple emulators"
69+
echo ""
70+
}
71+
72+
for arg in "$@"; do
73+
case $arg in
74+
-h|--help)
75+
print_usage
76+
exit 0
77+
;;
78+
--connected-wear)
79+
RUN_CONNECTED_WEAR=true
80+
echo "Running with Connected Wear OS Tests..."
81+
;;
82+
--connected-mobile)
83+
RUN_CONNECTED_MOBILE=true
84+
echo "Running with Connected Mobile Tests..."
85+
;;
86+
--connected)
87+
RUN_CONNECTED_WEAR=true
88+
RUN_CONNECTED_MOBILE=true
89+
echo "Warning: Running ALL connected tests. This requires multiple simultaneous emulators (Wear + Handheld) or may fail."
90+
;;
91+
*)
92+
echo "Unknown option: $arg"
93+
print_usage
94+
exit 1
95+
;;
96+
esac
97+
done
98+
5599
# Function to run verification for a module
56100
verify_module() {
57101
local module=$1
@@ -60,15 +104,36 @@ verify_module() {
60104
local assembleTask=":assembleDebug"
61105
local testTask=":testDebugUnitTest"
62106
local lintTask=":lintDebug"
107+
local connectedTask=""
63108

64109
if [[ "$module" == ":snippets:app" ]]; then
65110
assembleTask=":assembleGmsDebug"
66111
testTask=":testGmsDebugUnitTest"
67112
lintTask=":lintGmsDebug"
68113
fi
69114

70-
# Run assemble, lint, and test
71-
if ./gradlew "$module$assembleTask" "$module$testTask" "$module$lintTask"; then
115+
# Define connected test task if enabled
116+
if [ "$RUN_CONNECTED_WEAR" = true ] && [[ "$module" == ":WearOS:Wearable" ]]; then
117+
connectedTask=":connectedDebugAndroidTest"
118+
elif [ "$RUN_CONNECTED_MOBILE" = true ] && [[ "$module" != ":WearOS:Wearable" ]]; then
119+
if [[ "$module" == ":snippets:app" ]]; then
120+
connectedTask=":connectedGmsDebugAndroidTest"
121+
else
122+
connectedTask=":connectedDebugAndroidTest"
123+
fi
124+
fi
125+
# Note: If both flags are set, both types run (for their respective modules).
126+
127+
# Build command
128+
local cmd="./gradlew $module$assembleTask $module$testTask $module$lintTask"
129+
130+
if [ -n "$connectedTask" ]; then
131+
cmd="$cmd $module$connectedTask"
132+
fi
133+
134+
# Run assemble, lint, and test (and connected if requested)
135+
echo "Running: $cmd"
136+
if $cmd; then
72137
echo -e "${GREEN}SUCCESS: $module verified.${NC}"
73138
else
74139
echo -e "${RED}FAILURE: $module failed verification.${NC}"

0 commit comments

Comments
 (0)