diff --git a/external/xamarin-android-tools b/external/xamarin-android-tools
index 40b30131791..d8ee2d591af 160000
--- a/external/xamarin-android-tools
+++ b/external/xamarin-android-tools
@@ -1 +1 @@
-Subproject commit 40b30131791e7e996e20d461f8d3694b273f6985
+Subproject commit d8ee2d591afb4175c5ebf75e8ca261ed9c6685ff
diff --git a/src/Xamarin.Android.Build.Tasks/Tasks/BootAndroidEmulator.cs b/src/Xamarin.Android.Build.Tasks/Tasks/BootAndroidEmulator.cs
index bcfa0d656de..62ca7fc79a1 100644
--- a/src/Xamarin.Android.Build.Tasks/Tasks/BootAndroidEmulator.cs
+++ b/src/Xamarin.Android.Build.Tasks/Tasks/BootAndroidEmulator.cs
@@ -1,10 +1,8 @@
#nullable enable
using System;
-using System.Collections.Generic;
using System.Diagnostics;
using System.IO;
-using System.Threading;
using Microsoft.Android.Build.Tasks;
using Microsoft.Build.Framework;
using Xamarin.Android.Tools;
@@ -24,11 +22,13 @@ namespace Xamarin.Android.Tasks;
/// the emulator and waits for it to become fully ready.
///
/// On success, outputs the resolved ADB serial and AdbTarget for use by subsequent tasks.
+///
+/// Boot logic is delegated to and
+/// in Xamarin.Android.Tools.AndroidSdk.
///
public class BootAndroidEmulator : AndroidTask
{
const int DefaultBootTimeoutSeconds = 300;
- const int PollIntervalMilliseconds = 500;
public override string TaskPrefix => "BAE";
@@ -95,71 +95,63 @@ public class BootAndroidEmulator : AndroidTask
public override bool RunTask ()
{
var adbPath = ResolveAdbPath ();
+ var emulatorPath = ResolveEmulatorPath ();
+ var logger = this.CreateTaskLogger ();
- // Check if DeviceId is already a known online ADB serial
- if (IsOnlineAdbDevice (adbPath, Device)) {
- Log.LogMessage (MessageImportance.Normal, $"Device '{Device}' is already online.");
- ResolvedDevice = Device;
- AdbTarget = $"-s {Device}";
- return true;
- }
-
- // DeviceId is not an online serial — treat it as an AVD name and boot it
- Log.LogMessage (MessageImportance.Normal, $"Device '{Device}' is not an online ADB device. Treating as AVD name.");
+ var options = new EmulatorBootOptions {
+ BootTimeout = TimeSpan.FromSeconds (BootTimeoutSeconds),
+ AdditionalArgs = ParseExtraArguments (EmulatorExtraArguments),
+ };
- var emulatorPath = ResolveEmulatorPath ();
- var avdName = Device;
+ var result = ExecuteBoot (adbPath, emulatorPath, logger, Device, options);
- // Check if this AVD is already running (but perhaps still booting)
- var existingSerial = FindRunningEmulatorForAvd (adbPath, avdName);
- if (existingSerial != null) {
- Log.LogMessage (MessageImportance.High, $"Emulator '{avdName}' is already running as '{existingSerial}'");
- ResolvedDevice = existingSerial;
- AdbTarget = $"-s {existingSerial}";
- return WaitForFullBoot (adbPath, avdName, existingSerial);
+ if (result.Success) {
+ ResolvedDevice = result.Serial;
+ AdbTarget = $"-s {result.Serial}";
+ Log.LogMessage (MessageImportance.High, $"Emulator '{Device}' ({result.Serial}) is fully booted and ready.");
+ return true;
}
- // Launch the emulator process in the background
- Log.LogMessage (MessageImportance.High, $"Booting emulator '{avdName}'...");
- using var emulatorProcess = LaunchEmulatorProcess (emulatorPath, avdName);
- if (emulatorProcess == null) {
- return false;
+ // Map the error message to the appropriate MSBuild error code
+ var errorMessage = result.ErrorMessage ?? "Unknown error";
+
+ if (errorMessage.Contains ("Failed to launch")) {
+ Log.LogCodedError ("XA0143", Properties.Resources.XA0143, Device, errorMessage);
+ } else if (errorMessage.Contains ("Timed out")) {
+ Log.LogCodedError ("XA0145", Properties.Resources.XA0145, Device, BootTimeoutSeconds);
+ } else {
+ Log.LogCodedError ("XA0145", Properties.Resources.XA0145, Device, BootTimeoutSeconds);
}
- try {
- var timeout = TimeSpan.FromSeconds (BootTimeoutSeconds);
- var stopwatch = Stopwatch.StartNew ();
+ return false;
+ }
- // Phase 1: Wait for the emulator to appear in 'adb devices' as online
- Log.LogMessage (MessageImportance.Normal, "Waiting for emulator to appear in adb devices...");
- var serial = WaitForEmulatorOnline (adbPath, avdName, emulatorProcess, stopwatch, timeout);
- if (serial == null) {
- if (emulatorProcess.HasExited) {
- Log.LogCodedError ("XA0144", Properties.Resources.XA0144, avdName, emulatorProcess.ExitCode);
- } else {
- Log.LogCodedError ("XA0145", Properties.Resources.XA0145, avdName, BootTimeoutSeconds);
- }
- return false;
- }
+ ///
+ /// Executes the full boot flow via .
+ /// Virtual so tests can return canned results without launching real processes.
+ ///
+ protected virtual EmulatorBootResult ExecuteBoot (
+ string adbPath,
+ string emulatorPath,
+ Action logger,
+ string device,
+ EmulatorBootOptions options)
+ {
+ var adbRunner = new AdbRunner (adbPath, logger: logger);
+ var emulatorRunner = new EmulatorRunner (emulatorPath, logger: logger);
+ return emulatorRunner.BootEmulatorAsync (device, adbRunner, options)
+ .GetAwaiter ().GetResult ();
+ }
- ResolvedDevice = serial;
- AdbTarget = $"-s {serial}";
- Log.LogMessage (MessageImportance.Normal, $"Emulator appeared as '{serial}'");
+ ///
+ /// Parses space-separated extra arguments into an array suitable for .
+ ///
+ static string[]? ParseExtraArguments (string? extraArgs)
+ {
+ if (extraArgs.IsNullOrEmpty ())
+ return null;
- // Phase 2: Wait for the device to fully boot
- return WaitForFullBoot (adbPath, avdName, serial);
- } finally {
- // Stop async reads and unsubscribe events; using var handles Dispose
- try {
- emulatorProcess.CancelOutputRead ();
- emulatorProcess.CancelErrorRead ();
- } catch (InvalidOperationException e) {
- // Async reads may not have been started or process already exited
- Log.LogDebugMessage ($"Failed to cancel async reads: {e}");
- }
- emulatorProcess.OutputDataReceived -= EmulatorOutputDataReceived;
- emulatorProcess.ErrorDataReceived -= EmulatorErrorDataReceived;
- }
+ return extraArgs.Split ([' '], StringSplitOptions.RemoveEmptyEntries);
}
///
@@ -193,262 +185,4 @@ string ResolveEmulatorPath ()
return dir.IsNullOrEmpty () ? exe : Path.Combine (dir, exe);
}
-
- ///
- /// Checks whether the given deviceId is currently listed as an online device in 'adb devices'.
- ///
- protected virtual bool IsOnlineAdbDevice (string adbPath, string deviceId)
- {
- bool found = false;
-
- MonoAndroidHelper.RunProcess (
- adbPath, "devices",
- Log,
- onOutput: (sender, e) => {
- if (e.Data != null && e.Data.Contains ("device") && !e.Data.Contains ("List of devices")) {
- var parts = e.Data.Split (['\t', ' '], StringSplitOptions.RemoveEmptyEntries);
- if (parts.Length >= 2 && parts [1] == "device" &&
- string.Equals (parts [0], deviceId, StringComparison.OrdinalIgnoreCase)) {
- found = true;
- }
- }
- },
- logWarningOnFailure: false
- );
-
- return found;
- }
-
- ///
- /// Checks if an emulator with the specified AVD name is already running by querying
- /// 'adb devices' and then 'adb -s serial emu avd name' for each running emulator.
- ///
- protected virtual string? FindRunningEmulatorForAvd (string adbPath, string avdName)
- {
- var emulatorSerials = new List ();
-
- MonoAndroidHelper.RunProcess (
- adbPath, "devices",
- Log,
- onOutput: (sender, e) => {
- if (e.Data != null && e.Data.StartsWith ("emulator-", StringComparison.OrdinalIgnoreCase) && e.Data.Contains ("device")) {
- var parts = e.Data.Split (['\t', ' '], StringSplitOptions.RemoveEmptyEntries);
- if (parts.Length >= 2 && parts [1] == "device") {
- emulatorSerials.Add (parts [0]);
- }
- }
- },
- logWarningOnFailure: false
- );
-
- foreach (var serial in emulatorSerials) {
- var name = GetRunningAvdName (adbPath, serial);
- if (string.Equals (name, avdName, StringComparison.OrdinalIgnoreCase)) {
- return serial;
- }
- }
-
- return null;
- }
-
- ///
- /// Gets the AVD name from a running emulator via 'adb -s serial emu avd name'.
- ///
- protected virtual string? GetRunningAvdName (string adbPath, string serial)
- {
- string? avdName = null;
- try {
- var outputLines = new List ();
- MonoAndroidHelper.RunProcess (
- adbPath, $"-s {serial} emu avd name",
- Log,
- onOutput: (sender, e) => {
- if (!e.Data.IsNullOrEmpty ()) {
- outputLines.Add (e.Data);
- }
- },
- logWarningOnFailure: false
- );
-
- if (outputLines.Count > 0) {
- var name = outputLines [0].Trim ();
- if (!name.IsNullOrEmpty () && !name.Equals ("OK", StringComparison.OrdinalIgnoreCase)) {
- avdName = name;
- }
- }
- } catch (Exception ex) {
- Log.LogDebugMessage ($"Failed to get AVD name for {serial}: {ex.Message}");
- }
-
- return avdName;
- }
-
- ///
- /// Launches the emulator process in the background. The emulator window is shown by default,
- /// but this can be customized (for example, by passing -no-window) via EmulatorExtraArguments.
- ///
- protected virtual Process? LaunchEmulatorProcess (string emulatorPath, string avdName)
- {
- var arguments = $"-avd \"{avdName}\"";
- if (!EmulatorExtraArguments.IsNullOrEmpty ()) {
- arguments += $" {EmulatorExtraArguments}";
- }
-
- Log.LogMessage (MessageImportance.Normal, $"Starting: {emulatorPath} {arguments}");
-
- try {
- var psi = new ProcessStartInfo {
- FileName = emulatorPath,
- Arguments = arguments,
- UseShellExecute = false,
- RedirectStandardOutput = true,
- RedirectStandardError = true,
- CreateNoWindow = true,
- };
-
- var process = new Process { StartInfo = psi };
-
- // Capture output for diagnostics but don't block on it
- process.OutputDataReceived += EmulatorOutputDataReceived;
- process.ErrorDataReceived += EmulatorErrorDataReceived;
-
- process.Start ();
- process.BeginOutputReadLine ();
- process.BeginErrorReadLine ();
-
- return process;
- } catch (Exception ex) {
- Log.LogCodedError ("XA0143", Properties.Resources.XA0143, avdName, ex.Message);
- return null;
- }
- }
-
- void EmulatorOutputDataReceived (object sender, DataReceivedEventArgs e)
- {
- if (e.Data != null) {
- Log.LogDebugMessage ($"emulator stdout: {e.Data}");
- }
- }
-
- void EmulatorErrorDataReceived (object sender, DataReceivedEventArgs e)
- {
- if (e.Data != null) {
- Log.LogDebugMessage ($"emulator stderr: {e.Data}");
- }
- }
-
- ///
- /// Polls 'adb devices' until a new emulator serial appears with state "device" (online).
- /// Returns the serial or null on timeout / emulator process exit.
- ///
- string? WaitForEmulatorOnline (string adbPath, string avdName, Process emulatorProcess, Stopwatch stopwatch, TimeSpan timeout)
- {
- while (stopwatch.Elapsed < timeout) {
- if (emulatorProcess.HasExited) {
- return null;
- }
-
- var serial = FindRunningEmulatorForAvd (adbPath, avdName);
- if (serial != null) {
- return serial;
- }
-
- Thread.Sleep (PollIntervalMilliseconds);
- }
-
- return null;
- }
-
- ///
- /// Waits for the emulator to fully boot by checking:
- /// 1. sys.boot_completed property equals "1"
- /// 2. Package manager is responsive (pm path android returns "package:")
- ///
- bool WaitForFullBoot (string adbPath, string avdName, string serial)
- {
- Log.LogMessage (MessageImportance.Normal, "Waiting for emulator to fully boot...");
- var stopwatch = Stopwatch.StartNew ();
- var timeout = TimeSpan.FromSeconds (BootTimeoutSeconds);
-
- // Phase 1: Wait for sys.boot_completed == 1
- while (stopwatch.Elapsed < timeout) {
- var bootCompleted = GetShellProperty (adbPath, serial, "sys.boot_completed");
- if (bootCompleted == "1") {
- Log.LogMessage (MessageImportance.Normal, "sys.boot_completed = 1");
- break;
- }
-
- Thread.Sleep (PollIntervalMilliseconds);
- }
-
- if (stopwatch.Elapsed >= timeout) {
- Log.LogCodedError ("XA0145", Properties.Resources.XA0145, avdName, BootTimeoutSeconds);
- return false;
- }
-
- var remaining = timeout - stopwatch.Elapsed;
- Log.LogMessage (MessageImportance.Normal, $"Phase 1 complete. {remaining.TotalSeconds:F0}s remaining for package manager.");
-
- // Phase 2: Wait for package manager to be responsive
- while (stopwatch.Elapsed < timeout) {
- var pmResult = RunShellCommand (adbPath, serial, "pm path android");
- if (pmResult != null && pmResult.StartsWith ("package:", StringComparison.OrdinalIgnoreCase)) {
- Log.LogMessage (MessageImportance.High, $"Emulator '{avdName}' ({serial}) is fully booted and ready.");
- return true;
- }
-
- Thread.Sleep (PollIntervalMilliseconds);
- }
-
- Log.LogCodedError ("XA0145", Properties.Resources.XA0145, avdName, BootTimeoutSeconds);
- return false;
- }
-
- ///
- /// Gets a system property from the device via 'adb -s serial shell getprop property'.
- ///
- protected virtual string? GetShellProperty (string adbPath, string serial, string propertyName)
- {
- string? value = null;
- try {
- MonoAndroidHelper.RunProcess (
- adbPath, $"-s {serial} shell getprop {propertyName}",
- Log,
- onOutput: (sender, e) => {
- if (!e.Data.IsNullOrEmpty ()) {
- value = e.Data.Trim ();
- }
- },
- logWarningOnFailure: false
- );
- } catch (Exception ex) {
- Log.LogDebugMessage ($"Failed to get property '{propertyName}' from {serial}: {ex.Message}");
- }
-
- return value;
- }
-
- ///
- /// Runs a shell command on the device and returns the first line of output.
- ///
- protected virtual string? RunShellCommand (string adbPath, string serial, string command)
- {
- string? result = null;
- try {
- MonoAndroidHelper.RunProcess (
- adbPath, $"-s {serial} shell {command}",
- Log,
- onOutput: (sender, e) => {
- if (result == null && !e.Data.IsNullOrEmpty ()) {
- result = e.Data.Trim ();
- }
- },
- logWarningOnFailure: false
- );
- } catch (Exception ex) {
- Log.LogDebugMessage ($"Failed to run shell command '{command}' on {serial}: {ex.Message}");
- }
-
- return result;
- }
}
diff --git a/src/Xamarin.Android.Build.Tasks/Tests/Xamarin.Android.Build.Tests/Tasks/BootAndroidEmulatorTests.cs b/src/Xamarin.Android.Build.Tasks/Tests/Xamarin.Android.Build.Tests/Tasks/BootAndroidEmulatorTests.cs
index 58ab1ae095e..9eb4b15c819 100644
--- a/src/Xamarin.Android.Build.Tasks/Tests/Xamarin.Android.Build.Tests/Tasks/BootAndroidEmulatorTests.cs
+++ b/src/Xamarin.Android.Build.Tasks/Tests/Xamarin.Android.Build.Tests/Tasks/BootAndroidEmulatorTests.cs
@@ -8,6 +8,7 @@
using Microsoft.Build.Utilities;
using NUnit.Framework;
using Xamarin.Android.Tasks;
+using Xamarin.Android.Tools;
namespace Xamarin.Android.Build.Tests;
@@ -26,73 +27,23 @@ public void Setup ()
}
///
- /// Mock version of BootAndroidEmulator that overrides all process-dependent methods
- /// so we can test the task logic without launching real emulators or adb.
+ /// Mock version of BootAndroidEmulator that overrides
+ /// to return a configurable without launching real processes.
///
class MockBootAndroidEmulator : BootAndroidEmulator
{
- public HashSet OnlineDevices { get; set; } = [];
- public Dictionary RunningEmulatorAvdNames { get; set; } = new ();
- public Dictionary EmulatorBootBehavior { get; set; } = new ();
- public Dictionary BootCompletedValues { get; set; } = new ();
- public Dictionary PmPathResults { get; set; } = new ();
- public bool SimulateLaunchFailure { get; set; }
- public string? LastLaunchAvdName { get; private set; }
-
- readonly Dictionary findCallCounts = new ();
-
- protected override bool IsOnlineAdbDevice (string adbPath, string deviceId)
- => OnlineDevices.Contains (deviceId);
-
- protected override string? FindRunningEmulatorForAvd (string adbPath, string avdName)
+ public EmulatorBootResult BootResult { get; set; } = new () { Success = true, Serial = "emulator-5554" };
+ public string? LastBootedDevice { get; private set; }
+
+ protected override EmulatorBootResult ExecuteBoot (
+ string adbPath,
+ string emulatorPath,
+ Action logger,
+ string device,
+ EmulatorBootOptions options)
{
- foreach (var kvp in RunningEmulatorAvdNames) {
- if (string.Equals (kvp.Value, avdName, StringComparison.OrdinalIgnoreCase) &&
- OnlineDevices.Contains (kvp.Key)) {
- return kvp.Key;
- }
- }
-
- if (EmulatorBootBehavior.TryGetValue (avdName, out var behavior)) {
- findCallCounts.TryAdd (avdName, 0);
- findCallCounts [avdName]++;
- if (findCallCounts [avdName] >= behavior.PollsUntilOnline) {
- OnlineDevices.Add (behavior.Serial);
- RunningEmulatorAvdNames [behavior.Serial] = avdName;
- return behavior.Serial;
- }
- }
-
- return null;
- }
-
- protected override string? GetRunningAvdName (string adbPath, string serial)
- => RunningEmulatorAvdNames.TryGetValue (serial, out var name) ? name : null;
-
- protected override Process? LaunchEmulatorProcess (string emulatorPath, string avdName)
- {
- LastLaunchAvdName = avdName;
-
- if (SimulateLaunchFailure) {
- Log.LogError ("XA0143: Failed to launch emulator for AVD '{0}': {1}", avdName, "Simulated launch failure");
- return null;
- }
-
- return Process.GetCurrentProcess ();
- }
-
- protected override string? GetShellProperty (string adbPath, string serial, string propertyName)
- {
- if (propertyName == "sys.boot_completed" && BootCompletedValues.TryGetValue (serial, out var value))
- return value;
- return null;
- }
-
- protected override string? RunShellCommand (string adbPath, string serial, string command)
- {
- if (command == "pm path android" && PmPathResults.TryGetValue (serial, out var result))
- return result;
- return null;
+ LastBootedDevice = device;
+ return BootResult;
}
}
@@ -112,8 +63,12 @@ MockBootAndroidEmulator CreateTask (string device = "Pixel_6_API_33")
[Test]
public void AlreadyOnlineDevice_PassesThrough ()
{
+ // BootEmulatorAsync returns success immediately (device is already online)
var task = CreateTask ("emulator-5554");
- task.OnlineDevices = ["emulator-5554"];
+ task.BootResult = new EmulatorBootResult {
+ Success = true,
+ Serial = "emulator-5554",
+ };
Assert.IsTrue (task.RunTask (), "RunTask should succeed");
Assert.AreEqual ("emulator-5554", task.ResolvedDevice);
@@ -125,7 +80,10 @@ public void AlreadyOnlineDevice_PassesThrough ()
public void AlreadyOnlinePhysicalDevice_PassesThrough ()
{
var task = CreateTask ("0A041FDD400327");
- task.OnlineDevices = ["0A041FDD400327"];
+ task.BootResult = new EmulatorBootResult {
+ Success = true,
+ Serial = "0A041FDD400327",
+ };
Assert.IsTrue (task.RunTask (), "RunTask should succeed");
Assert.AreEqual ("0A041FDD400327", task.ResolvedDevice);
@@ -136,73 +94,54 @@ public void AlreadyOnlinePhysicalDevice_PassesThrough ()
public void AvdAlreadyRunning_WaitsForFullBoot ()
{
var task = CreateTask ("Pixel_6_API_33");
- task.OnlineDevices = ["emulator-5554"];
- task.RunningEmulatorAvdNames = new () {
- { "emulator-5554", "Pixel_6_API_33" }
+ task.BootResult = new EmulatorBootResult {
+ Success = true,
+ Serial = "emulator-5554",
};
- task.BootCompletedValues = new () { { "emulator-5554", "1" } };
- task.PmPathResults = new () { { "emulator-5554", "package:/system/framework/framework-res.apk" } };
Assert.IsTrue (task.RunTask (), "RunTask should succeed");
Assert.AreEqual ("emulator-5554", task.ResolvedDevice);
Assert.AreEqual ("-s emulator-5554", task.AdbTarget);
+ Assert.AreEqual ("Pixel_6_API_33", task.LastBootedDevice);
}
[Test]
public void BootEmulator_AppearsAfterPolling ()
{
var task = CreateTask ("Pixel_6_API_33");
- // Not online initially, will appear after 2 polls
- task.EmulatorBootBehavior = new () {
- { "Pixel_6_API_33", ("emulator-5556", 2) }
+ task.BootResult = new EmulatorBootResult {
+ Success = true,
+ Serial = "emulator-5556",
};
- task.BootCompletedValues = new () { { "emulator-5556", "1" } };
- task.PmPathResults = new () { { "emulator-5556", "package:/system/framework/framework-res.apk" } };
Assert.IsTrue (task.RunTask (), "RunTask should succeed");
Assert.AreEqual ("emulator-5556", task.ResolvedDevice);
Assert.AreEqual ("-s emulator-5556", task.AdbTarget);
- Assert.AreEqual ("Pixel_6_API_33", task.LastLaunchAvdName);
+ Assert.AreEqual ("Pixel_6_API_33", task.LastBootedDevice);
}
[Test]
public void LaunchFailure_ReturnsError ()
{
var task = CreateTask ("Pixel_6_API_33");
- task.SimulateLaunchFailure = true;
-
- Assert.IsFalse (task.RunTask (), "RunTask should fail");
- Assert.IsTrue (errors.Any (e => e.Message != null && e.Message.Contains ("XA0143")), "Should have XA0143 error");
- Assert.IsNull (task.ResolvedDevice, "ResolvedDevice should be null");
- }
-
- [Test]
- public void BootTimeout_BootCompletedNeverReaches1 ()
- {
- var task = CreateTask ("Pixel_6_API_33");
- task.BootTimeoutSeconds = 0; // Immediate timeout
- // Emulator appears immediately but never finishes booting
- task.OnlineDevices = ["emulator-5554"];
- task.RunningEmulatorAvdNames = new () {
- { "emulator-5554", "Pixel_6_API_33" }
+ task.BootResult = new EmulatorBootResult {
+ Success = false,
+ ErrorMessage = "Failed to launch emulator: Simulated launch failure",
};
- task.BootCompletedValues = new () { { "emulator-5554", "0" } };
Assert.IsFalse (task.RunTask (), "RunTask should fail");
- Assert.IsTrue (errors.Any (e => e.Code == "XA0145"), "Should have XA0145 timeout error");
+ Assert.IsTrue (errors.Any (e => e.Code == "XA0143"), "Should have XA0143 error");
+ Assert.IsNull (task.ResolvedDevice, "ResolvedDevice should be null");
}
[Test]
- public void BootTimeout_PmNeverResponds ()
+ public void BootTimeout_ReturnsError ()
{
var task = CreateTask ("Pixel_6_API_33");
- task.BootTimeoutSeconds = 0; // Immediate timeout
- task.OnlineDevices = ["emulator-5554"];
- task.RunningEmulatorAvdNames = new () {
- { "emulator-5554", "Pixel_6_API_33" }
+ task.BootResult = new EmulatorBootResult {
+ Success = false,
+ ErrorMessage = "Timed out waiting for emulator 'Pixel_6_API_33' to boot within 10s.",
};
- task.BootCompletedValues = new () { { "emulator-5554", "1" } };
- // PmPathResults not set — pm never responds
Assert.IsFalse (task.RunTask (), "RunTask should fail");
Assert.IsTrue (errors.Any (e => e.Code == "XA0145"), "Should have XA0145 timeout error");
@@ -212,13 +151,10 @@ public void BootTimeout_PmNeverResponds ()
public void MultipleEmulators_FindsCorrectAvd ()
{
var task = CreateTask ("Pixel_9_Pro_XL");
- task.OnlineDevices = ["emulator-5554", "emulator-5556"];
- task.RunningEmulatorAvdNames = new () {
- { "emulator-5554", "pixel_7_-_api_35" },
- { "emulator-5556", "Pixel_9_Pro_XL" }
+ task.BootResult = new EmulatorBootResult {
+ Success = true,
+ Serial = "emulator-5556",
};
- task.BootCompletedValues = new () { { "emulator-5556", "1" } };
- task.PmPathResults = new () { { "emulator-5556", "package:/system/framework/framework-res.apk" } };
Assert.IsTrue (task.RunTask (), "RunTask should succeed");
Assert.AreEqual ("emulator-5556", task.ResolvedDevice);
@@ -233,12 +169,49 @@ public void ToolPaths_ResolvedFromAndroidSdkDirectory ()
Device = "emulator-5554",
AndroidSdkDirectory = "/android/sdk",
BootTimeoutSeconds = 10,
+ BootResult = new EmulatorBootResult {
+ Success = true,
+ Serial = "emulator-5554",
+ },
};
- task.OnlineDevices = ["emulator-5554"];
- // Tool paths are not set explicitly — ResolveAdbPath/ResolveEmulatorPath
- // should compute them from AndroidSdkDirectory
Assert.IsTrue (task.RunTask (), "RunTask should succeed");
Assert.AreEqual ("emulator-5554", task.ResolvedDevice);
}
+
+ [Test]
+ public void ExtraArguments_PassedToOptions ()
+ {
+ string[]? capturedArgs = null;
+ var task = new MockBootAndroidEmulator {
+ BuildEngine = engine,
+ Device = "Pixel_6_API_33",
+ EmulatorToolPath = "/sdk/emulator/",
+ EmulatorToolExe = "emulator",
+ AdbToolPath = "/sdk/platform-tools/",
+ AdbToolExe = "adb",
+ BootTimeoutSeconds = 10,
+ EmulatorExtraArguments = "-no-snapshot-load -gpu auto",
+ BootResult = new EmulatorBootResult {
+ Success = true,
+ Serial = "emulator-5554",
+ },
+ };
+
+ Assert.IsTrue (task.RunTask (), "RunTask should succeed");
+ Assert.AreEqual ("emulator-5554", task.ResolvedDevice);
+ }
+
+ [Test]
+ public void UnknownError_MapsToXA0145 ()
+ {
+ var task = CreateTask ("Pixel_6_API_33");
+ task.BootResult = new EmulatorBootResult {
+ Success = false,
+ ErrorMessage = "Some unexpected error occurred",
+ };
+
+ Assert.IsFalse (task.RunTask (), "RunTask should fail");
+ Assert.IsTrue (errors.Any (e => e.Code == "XA0145"), "Unknown errors should map to XA0145");
+ }
}