-
Notifications
You must be signed in to change notification settings - Fork 565
Description
Summary
Add support for launching Android apps via adb shell am instrument instead of adb shell am start when running device tests. This enables proper wait-for-completion semantics for test execution scenarios.
Background
The dotnet test for mobile platforms spec (dotnet/maui#33117) defines how dotnet test will work with device test projects. A working POC (rmarinho/testfx#2) demonstrates two execution modes for Android:
- Activity Mode (current): Uses
adb shell am startto launch MainActivity - Instrumentation Mode (proposed): Uses
adb shell am instrument -wfor reliable test execution
The Instrumentation Mode provides:
- Proper wait-for-completion via
-wflag - Reliable exit code propagation via
Instrumentation.Finish() - Standard Android test execution pattern
Proposed Changes
Modify _AndroidComputeRunArguments target in [Microsoft.Android.Sdk.Application.targets](https://github.com/dotnet/android/blob/main/src/Xamarin.Android.Build. Tasks/Microsoft.Android.Sdk/targets/Microsoft.Android. Sdk.Application.targets#L52-L79) to support a new UseInstrumentation property.
Current Implementation
<!-- When WaitForExit is false, use direct adb command (no logcat streaming) -->
<PropertyGroup Condition=" '$(WaitForExit)' == 'false' ">
<RunCommand>$(_AdbToolPath)</RunCommand>
<RunArguments>$(AdbTarget) shell am start -S -n "$(_AndroidPackage)/$(AndroidLaunchActivity)"</RunArguments>
</PropertyGroup>Proposed Enhancement
<!-- New: When UseInstrumentation is true, launch via adb instrument -->
<PropertyGroup Condition=" '$(UseInstrumentation)' == 'true' ">
<_AndroidRunPath Condition=" '$(_AndroidRunPath)' == '' ">$(MSBuildThisFileDirectory)..\tools\Microsoft.Android.Run.dll</_AndroidRunPath>
<RunCommand>dotnet</RunCommand>
<RunArguments>exec "$(_AndroidRunPath)" --adb "$(_AdbToolPath)" --package "$(_AndroidPackage)" --instrument "$(AndroidInstrumentationName)" --logcat-args "$(_AndroidRunLogcatArgs)" $(_AndroidRunExtraArgs)</RunArguments>
</PropertyGroup>
<!-- Alternative: Direct adb instrument without Microsoft. Android.Run wrapper -->
<PropertyGroup Condition=" '$(UseInstrumentation)' == 'true' AND '$(WaitForExit)' == 'false' ">
<RunCommand>$(_AdbToolPath)</RunCommand>
<RunArguments>$(AdbTarget) shell am instrument -w "$(_AndroidPackage)/$(AndroidInstrumentationName)"</RunArguments>
</PropertyGroup>New MSBuild Properties
| Property | Description | Default |
|---|---|---|
$(UseInstrumentation) |
When true, use adb instrument instead of adb am start |
false |
$(AndroidInstrumentationName) |
Full instrumentation class name (e.g., myapp.TestInstrumentation) |
(none, required when UseInstrumentation=true, if not provided try to find the first on the manifest or using the Instrumentation attribute ) |
Usage
# Current (Activity Mode)
dotnet run --project MyTests.csproj -f net10.0-android --device emulator-5554
# Proposed (Instrumentation Mode)
dotnet run --project MyTests.csproj -f net10.0-android --device emulator-5554 -p:UseInstrumentation=true -p:AndroidInstrumentationName=myapp.TestInstrumentationTest Project Requirements
Projects using Instrumentation Mode need:
1. TestInstrumentation class:
[Instrumentation(Name = "myapp.TestInstrumentation")]
public class TestInstrumentation : Instrumentation
{
public override void OnCreate(Bundle? arguments)
{
base.OnCreate(arguments);
Start();
}
public override async void OnStart()
{
base.OnStart();
int exitCode = 1;
Bundle results = new Bundle();
try
{
// Run tests via Microsoft. Testing.Platform
exitCode = await MicrosoftTestingPlatformEntryPoint.Main(args);
results. PutString("status", exitCode == 0 ? "SUCCESS" : "FAILURE");
}
finally
{
// Signal completion - adb instrument -w waits for this
Finish(exitCode == 0 ? Result.Ok : Result. Canceled, results);
}
}
}2. AndroidManifest.xml registration:
<instrumentation
android:name="myapp.TestInstrumentation"
android:targetPackage="com.mycompany.mytestapp"
android: label="Test Instrumentation" />Why Instrumentation Mode?
| Aspect | Activity Mode | Instrumentation Mode |
|---|---|---|
| Completion detection | App termination (unreliable) | Finish() call (reliable) |
| Exit code | Java.Lang.JavaSystem.Exit() |
Finish(Result, Bundle) |
| Wait semantics | Polling/timeout | -w flag blocks until done |
| Long-running tests | May timeout | Waits indefinitely |
| Standard pattern | Custom | Android test convention |
Related
- Spec: dotnet/maui#33117 -
dotnet testfor Mobile Platforms - POC: rmarinho/testfx#2 - Working implementation with both modes
- Related issue: dotnet/android#9496