diff --git a/external/xamarin-android-tools b/external/xamarin-android-tools
index d3c269de161..f9416d4bacd 160000
--- a/external/xamarin-android-tools
+++ b/external/xamarin-android-tools
@@ -1 +1 @@
-Subproject commit d3c269de161a9f70438ff9db6979f52868ed4d6f
+Subproject commit f9416d4bacd34ed1191abf161cdb788bf259e038
diff --git a/src/Xamarin.Android.Build.Tasks/Tasks/GetAvailableAndroidDevices.cs b/src/Xamarin.Android.Build.Tasks/Tasks/GetAvailableAndroidDevices.cs
index d1da642cde0..bb2dbcde7ec 100644
--- a/src/Xamarin.Android.Build.Tasks/Tasks/GetAvailableAndroidDevices.cs
+++ b/src/Xamarin.Android.Build.Tasks/Tasks/GetAvailableAndroidDevices.cs
@@ -2,12 +2,11 @@
using System;
using System.Collections.Generic;
-using System.Globalization;
using System.IO;
-using System.Text.RegularExpressions;
using Microsoft.Android.Build.Tasks;
using Microsoft.Build.Framework;
using Microsoft.Build.Utilities;
+using Xamarin.Android.Tools;
namespace Xamarin.Android.Tasks;
@@ -16,20 +15,11 @@ namespace Xamarin.Android.Tasks;
/// and 'emulator -list-avds'. Merges the results to provide a complete list of available
/// devices including emulators that are not currently running.
/// Returns a list of devices with metadata for device selection in dotnet run.
+///
+/// Parsing and merging logic is delegated to in Xamarin.Android.Tools.AndroidSdk.
///
public class GetAvailableAndroidDevices : AndroidAdb
{
- enum DeviceType
- {
- Device,
- Emulator
- }
-
- // Pattern to match device lines: [key:value ...]
- // Example: emulator-5554 device product:sdk_gphone64_arm64 model:sdk_gphone64_arm64
- static readonly Regex AdbDevicesRegex = new(@"^([^\s]+)\s+(device|offline|unauthorized|no permissions)\s*(.*)$", RegexOptions.Compiled);
- static readonly Regex ApiRegex = new(@"\bApi\b", RegexOptions.Compiled);
-
readonly List output = [];
///
@@ -64,23 +54,63 @@ public override bool RunTask ()
if (!base.RunTask ())
return false;
- // Parse devices from adb
- var adbDevices = ParseAdbDevicesOutput (output);
+ // Parse devices from adb using shared AdbRunner logic
+ var adbDevices = AdbRunner.ParseAdbDevicesOutput (output);
Log.LogDebugMessage ($"Found {adbDevices.Count} device(s) from adb");
+ // For emulators, query AVD names
+ var logger = this.CreateTaskLogger ();
+ foreach (var device in adbDevices) {
+ if (device.Type == AdbDeviceType.Emulator) {
+ device.AvdName = GetEmulatorAvdName (device.Serial);
+ device.Description = AdbRunner.BuildDeviceDescription (device, logger);
+ }
+ }
+
// Get available emulators from 'emulator -list-avds'
var availableEmulators = GetAvailableEmulators ();
Log.LogDebugMessage ($"Found {availableEmulators.Count} available emulator(s) from 'emulator -list-avds'");
- // Merge the lists
- var mergedDevices = MergeDevicesAndEmulators (adbDevices, availableEmulators);
- Devices = mergedDevices.ToArray ();
+ // Merge using shared logic
+ var mergedDevices = AdbRunner.MergeDevicesAndEmulators (adbDevices, availableEmulators, logger);
+
+ // Convert to ITaskItem array
+ Devices = ConvertToTaskItems (mergedDevices);
Log.LogDebugMessage ($"Total {Devices.Length} Android device(s)/emulator(s) after merging");
return !Log.HasLoggedErrors;
}
+ ///
+ /// Converts AdbDeviceInfo list to ITaskItem array for MSBuild output.
+ ///
+ internal static ITaskItem [] ConvertToTaskItems (IReadOnlyList devices)
+ {
+ var items = new ITaskItem [devices.Count];
+ for (int i = 0; i < devices.Count; i++) {
+ var device = devices [i];
+ var item = new TaskItem (device.Serial);
+ item.SetMetadata ("Description", device.Description);
+ item.SetMetadata ("Type", device.Type.ToString ());
+ item.SetMetadata ("Status", device.Status.ToString ());
+
+ if (!device.AvdName.IsNullOrEmpty ())
+ item.SetMetadata ("AvdName", device.AvdName);
+ if (!device.Model.IsNullOrEmpty ())
+ item.SetMetadata ("Model", device.Model);
+ if (!device.Product.IsNullOrEmpty ())
+ item.SetMetadata ("Product", device.Product);
+ if (!device.Device.IsNullOrEmpty ())
+ item.SetMetadata ("Device", device.Device);
+ if (!device.TransportId.IsNullOrEmpty ())
+ item.SetMetadata ("TransportId", device.TransportId);
+
+ items [i] = item;
+ }
+ return items;
+ }
+
///
/// Gets the list of available AVDs using 'emulator -list-avds'.
///
@@ -125,186 +155,7 @@ protected virtual List GetAvailableEmulators ()
}
///
- /// Merges devices from adb with available emulators.
- /// Running emulators (already in adb list) are not duplicated.
- /// Non-running emulators are added with Status="NotRunning".
- /// Results are sorted: online devices first, then not-running emulators, alphabetically by description within each group.
- ///
- internal List MergeDevicesAndEmulators (List adbDevices, List availableEmulators)
- {
- var result = new List (adbDevices);
-
- // Build a set of AVD names that are already running (from adb devices)
- var runningAvdNames = new HashSet (StringComparer.OrdinalIgnoreCase);
- foreach (var device in adbDevices) {
- var avdName = device.GetMetadata ("AvdName");
- if (!avdName.IsNullOrEmpty ()) {
- runningAvdNames.Add (avdName);
- }
- }
-
- Log.LogDebugMessage ($"Running emulators AVD names: {string.Join (", ", runningAvdNames)}");
-
- // Add non-running emulators
- foreach (var avdName in availableEmulators) {
- if (runningAvdNames.Contains (avdName)) {
- Log.LogDebugMessage ($"Emulator '{avdName}' is already running, skipping");
- continue;
- }
-
- // Create item for non-running emulator
- // Use the AVD name as the ItemSpec since there's no serial yet
- var item = new TaskItem (avdName);
- var displayName = FormatDisplayName (avdName, avdName);
- item.SetMetadata ("Description", $"{displayName} (Not Running)");
- item.SetMetadata ("Type", DeviceType.Emulator.ToString ());
- item.SetMetadata ("Status", "NotRunning");
- item.SetMetadata ("AvdName", avdName);
-
- result.Add (item);
- Log.LogDebugMessage ($"Added non-running emulator: {avdName}");
- }
-
- // Sort: online devices first, then not-running emulators, alphabetically by description within each group
- result.Sort ((a, b) => {
- var aNotRunning = string.Equals (a.GetMetadata ("Status"), "NotRunning", StringComparison.OrdinalIgnoreCase);
- var bNotRunning = string.Equals (b.GetMetadata ("Status"), "NotRunning", StringComparison.OrdinalIgnoreCase);
-
- if (aNotRunning != bNotRunning) {
- return aNotRunning ? 1 : -1;
- }
-
- return string.Compare (a.GetMetadata ("Description"), b.GetMetadata ("Description"), StringComparison.OrdinalIgnoreCase);
- });
-
- return result;
- }
-
- ///
- /// Parses the output of 'adb devices -l' command.
- /// Example output:
- /// List of devices attached
- /// emulator-5554 device product:sdk_gphone64_arm64 model:sdk_gphone64_arm64 device:emu64a transport_id:1
- /// 0A041FDD400327 device usb:1-1 product:raven model:Pixel_6_Pro device:raven transport_id:2
- ///
- List ParseAdbDevicesOutput (List lines)
- {
- var devices = new List ();
-
- foreach (var line in lines) {
- // Skip the header line "List of devices attached"
- if (line.Contains ("List of devices") || line.IsNullOrWhiteSpace ())
- continue;
-
- var match = AdbDevicesRegex.Match (line);
- if (!match.Success)
- continue;
-
- var serial = match.Groups [1].Value.Trim ();
- var state = match.Groups [2].Value.Trim ();
- var properties = match.Groups [3].Value.Trim ();
-
- // Parse key:value pairs from the properties string
- var propDict = new Dictionary (StringComparer.OrdinalIgnoreCase);
- if (!properties.IsNullOrWhiteSpace ()) {
- // Split by whitespace and parse key:value pairs
- var pairs = properties.Split ([' '], StringSplitOptions.RemoveEmptyEntries);
- foreach (var pair in pairs) {
- var colonIndex = pair.IndexOf (':');
- if (colonIndex > 0 && colonIndex < pair.Length - 1) {
- var key = pair.Substring (0, colonIndex);
- var value = pair.Substring (colonIndex + 1);
- propDict [key] = value;
- }
- }
- }
-
- // Determine device type: Emulator or Device
- var deviceType = serial.StartsWith ("emulator-", StringComparison.OrdinalIgnoreCase) ? DeviceType.Emulator : DeviceType.Device;
-
- // For emulators, get the AVD name for duplicate detection
- string? avdName = null;
- if (deviceType == DeviceType.Emulator) {
- avdName = GetEmulatorAvdName (serial);
- }
-
- // Build a friendly description
- var description = BuildDeviceDescription (serial, propDict, deviceType, avdName);
-
- // Map adb state to device status
- var status = MapAdbStateToStatus (state);
-
- // Create the MSBuild item
- var item = new TaskItem (serial);
- item.SetMetadata ("Description", description);
- item.SetMetadata ("Type", deviceType.ToString ());
- item.SetMetadata ("Status", status);
-
- // Add AVD name for emulators (used for duplicate detection)
- if (!avdName.IsNullOrEmpty ()) {
- item.SetMetadata ("AvdName", avdName);
- }
-
- // Add optional metadata for additional information
- if (propDict.TryGetValue ("model", out var model))
- item.SetMetadata ("Model", model);
- if (propDict.TryGetValue ("product", out var product))
- item.SetMetadata ("Product", product);
- if (propDict.TryGetValue ("device", out var device))
- item.SetMetadata ("Device", device);
- if (propDict.TryGetValue ("transport_id", out var transportId))
- item.SetMetadata ("TransportId", transportId);
-
- devices.Add (item);
- }
-
- return devices;
- }
-
- string BuildDeviceDescription (string serial, Dictionary properties, DeviceType deviceType, string? avdName)
- {
- // Try to build a human-friendly description
- // Priority: AVD name (for emulators) > model > product > device > serial
-
- // For emulators, try to get the AVD display name
- if (deviceType == DeviceType.Emulator && !avdName.IsNullOrEmpty ()) {
- return FormatDisplayName (serial, avdName!);
- }
-
- if (properties.TryGetValue ("model", out var model) && !model.IsNullOrEmpty ()) {
- // Clean up model name - replace underscores with spaces
- model = model.Replace ('_', ' ');
- return model;
- }
-
- if (properties.TryGetValue ("product", out var product) && !product.IsNullOrEmpty ()) {
- product = product.Replace ('_', ' ');
- return product;
- }
-
- if (properties.TryGetValue ("device", out var device) && !device.IsNullOrEmpty ()) {
- device = device.Replace ('_', ' ');
- return device;
- }
-
- // Fallback to serial number
- return serial;
- }
-
- static string MapAdbStateToStatus (string adbState)
- {
- // Map adb device states to the spec's status values
- return adbState.ToLowerInvariant () switch {
- "device" => "Online",
- "offline" => "Offline",
- "unauthorized" => "Unauthorized",
- "no permissions" => "NoPermissions",
- _ => "Unknown",
- };
- }
-
- ///
- /// Queries the emulator for its AVD name using 'adb -s emu avd name'.
+ /// Queries the emulator for its AVD name using 'adb -s <serial> emu avd name'.
/// Returns the raw AVD name (not formatted).
///
protected virtual string? GetEmulatorAvdName (string serial)
@@ -339,21 +190,4 @@ static string MapAdbStateToStatus (string adbState)
return null;
}
-
- ///
- /// Formats the AVD name into a more user-friendly display name. Replace underscores with spaces and title case.
- ///
- public string FormatDisplayName (string serial, string avdName)
- {
- Log.LogDebugMessage ($"Emulator {serial}, original AVD name: {avdName}");
-
- // Title case and replace underscores with spaces
- var textInfo = CultureInfo.InvariantCulture.TextInfo;
- avdName = textInfo.ToTitleCase (avdName.Replace ('_', ' '));
-
- // Replace "Api" with "API"
- avdName = ApiRegex.Replace (avdName, "API");
- Log.LogDebugMessage ($"Emulator {serial}, formatted AVD display name: {avdName}");
- return avdName;
- }
}
diff --git a/src/Xamarin.Android.Build.Tasks/Tests/Xamarin.Android.Build.Tests/Tasks/GetAvailableAndroidDevicesTests.cs b/src/Xamarin.Android.Build.Tasks/Tests/Xamarin.Android.Build.Tests/Tasks/GetAvailableAndroidDevicesTests.cs
index 5dca488ebd7..83a2027b363 100644
--- a/src/Xamarin.Android.Build.Tasks/Tests/Xamarin.Android.Build.Tests/Tasks/GetAvailableAndroidDevicesTests.cs
+++ b/src/Xamarin.Android.Build.Tasks/Tests/Xamarin.Android.Build.Tests/Tasks/GetAvailableAndroidDevicesTests.cs
@@ -1,9 +1,11 @@
using Microsoft.Build.Framework;
using Microsoft.Build.Utilities;
using NUnit.Framework;
+using System;
using System.Collections.Generic;
-using System.Reflection;
+using System.Linq;
using Xamarin.Android.Tasks;
+using Xamarin.Android.Tools;
namespace Xamarin.Android.Build.Tests
{
@@ -41,6 +43,11 @@ class MockGetAvailableAndroidDevices : GetAvailableAndroidDevices
return null;
}
+ ///
+ /// Public accessor for tests to call the protected GetEmulatorAvdName.
+ ///
+ public string? GetEmulatorAvdNameForTest (string serial) => GetEmulatorAvdName (serial);
+
protected override List GetAvailableEmulators ()
{
return MockAvailableEmulators;
@@ -48,14 +55,22 @@ protected override List GetAvailableEmulators ()
}
///
- /// Helper method to invoke the private ParseAdbDevicesOutput method via reflection
+ /// Helper method that parses adb output using AdbRunner, applies mock AVD name resolution,
+ /// and converts to ITaskItem array via GetAvailableAndroidDevices.ConvertToTaskItems.
///
ITaskItem [] ParseAdbDevicesOutput (MockGetAvailableAndroidDevices task, List lines)
{
- var method = typeof (GetAvailableAndroidDevices).GetMethod ("ParseAdbDevicesOutput", BindingFlags.NonPublic | BindingFlags.Instance);
- Assert.IsNotNull (method, "ParseAdbDevicesOutput method should exist");
- var result = (List) method.Invoke (task, [lines]);
- return result.ToArray ();
+ var devices = AdbRunner.ParseAdbDevicesOutput (lines);
+
+ // Apply AVD name resolution for emulators (same logic as RunTask)
+ foreach (var device in devices) {
+ if (device.Type == AdbDeviceType.Emulator) {
+ device.AvdName = task.GetEmulatorAvdNameForTest (device.Serial);
+ device.Description = AdbRunner.BuildDeviceDescription (device);
+ }
+ }
+
+ return GetAvailableAndroidDevices.ConvertToTaskItems (devices);
}
[Test]
@@ -451,11 +466,7 @@ public void ParseAdbDaemonStarting ()
[Test]
public void FormatDisplayName_ReplacesUnderscoresWithSpaces ()
{
- var task = new MockGetAvailableAndroidDevices {
- BuildEngine = engine,
- };
-
- var result = task.FormatDisplayName ("emulator-5554", "pixel_7_pro");
+ var result = AdbRunner.FormatDisplayName ("pixel_7_pro");
Assert.AreEqual ("Pixel 7 Pro", result, "Should replace underscores with spaces");
}
@@ -463,11 +474,7 @@ public void FormatDisplayName_ReplacesUnderscoresWithSpaces ()
[Test]
public void FormatDisplayName_AppliesTitleCase ()
{
- var task = new MockGetAvailableAndroidDevices {
- BuildEngine = engine,
- };
-
- var result = task.FormatDisplayName ("emulator-5554", "pixel 7 pro");
+ var result = AdbRunner.FormatDisplayName ("pixel 7 pro");
Assert.AreEqual ("Pixel 7 Pro", result, "Should apply title case");
}
@@ -475,11 +482,7 @@ public void FormatDisplayName_AppliesTitleCase ()
[Test]
public void FormatDisplayName_ReplacesApiWithAPIUppercase ()
{
- var task = new MockGetAvailableAndroidDevices {
- BuildEngine = engine,
- };
-
- var result = task.FormatDisplayName ("emulator-5554", "pixel_5_api_34");
+ var result = AdbRunner.FormatDisplayName ("pixel_5_api_34");
Assert.AreEqual ("Pixel 5 API 34", result, "Should replace 'Api' with 'API'");
}
@@ -487,11 +490,7 @@ public void FormatDisplayName_ReplacesApiWithAPIUppercase ()
[Test]
public void FormatDisplayName_HandlesMultipleApiOccurrences ()
{
- var task = new MockGetAvailableAndroidDevices {
- BuildEngine = engine,
- };
-
- var result = task.FormatDisplayName ("emulator-5554", "test_api_device_api_35");
+ var result = AdbRunner.FormatDisplayName ("test_api_device_api_35");
Assert.AreEqual ("Test API Device API 35", result, "Should replace all 'Api' occurrences with 'API'");
}
@@ -499,11 +498,7 @@ public void FormatDisplayName_HandlesMultipleApiOccurrences ()
[Test]
public void FormatDisplayName_HandlesMixedCaseInput ()
{
- var task = new MockGetAvailableAndroidDevices {
- BuildEngine = engine,
- };
-
- var result = task.FormatDisplayName ("emulator-5554", "PiXeL_7_API_35");
+ var result = AdbRunner.FormatDisplayName ("PiXeL_7_API_35");
Assert.AreEqual ("Pixel 7 API 35", result, "Should normalize mixed case input");
}
@@ -511,11 +506,7 @@ public void FormatDisplayName_HandlesMixedCaseInput ()
[Test]
public void FormatDisplayName_HandlesComplexNames ()
{
- var task = new MockGetAvailableAndroidDevices {
- BuildEngine = engine,
- };
-
- var result = task.FormatDisplayName ("emulator-5554", "pixel_9_pro_xl_api_36");
+ var result = AdbRunner.FormatDisplayName ("pixel_9_pro_xl_api_36");
Assert.AreEqual ("Pixel 9 Pro Xl API 36", result, "Should format complex names correctly");
}
@@ -523,11 +514,7 @@ public void FormatDisplayName_HandlesComplexNames ()
[Test]
public void FormatDisplayName_PreservesNumbersAndSpecialChars ()
{
- var task = new MockGetAvailableAndroidDevices {
- BuildEngine = engine,
- };
-
- var result = task.FormatDisplayName ("emulator-5554", "pixel_7-pro_api_35");
+ var result = AdbRunner.FormatDisplayName ("pixel_7-pro_api_35");
Assert.AreEqual ("Pixel 7-Pro API 35", result, "Should preserve hyphens and numbers");
}
@@ -535,11 +522,7 @@ public void FormatDisplayName_PreservesNumbersAndSpecialChars ()
[Test]
public void FormatDisplayName_HandlesEmptyString ()
{
- var task = new MockGetAvailableAndroidDevices {
- BuildEngine = engine,
- };
-
- var result = task.FormatDisplayName ("emulator-5554", "");
+ var result = AdbRunner.FormatDisplayName ("");
Assert.AreEqual ("", result, "Should handle empty string");
}
@@ -547,11 +530,7 @@ public void FormatDisplayName_HandlesEmptyString ()
[Test]
public void FormatDisplayName_HandlesSingleWord ()
{
- var task = new MockGetAvailableAndroidDevices {
- BuildEngine = engine,
- };
-
- var result = task.FormatDisplayName ("emulator-5554", "pixel");
+ var result = AdbRunner.FormatDisplayName ("pixel");
Assert.AreEqual ("Pixel", result, "Should capitalize single word");
}
@@ -559,11 +538,7 @@ public void FormatDisplayName_HandlesSingleWord ()
[Test]
public void FormatDisplayName_DoesNotReplaceApiInsideWords ()
{
- var task = new MockGetAvailableAndroidDevices {
- BuildEngine = engine,
- };
-
- var result = task.FormatDisplayName ("emulator-5554", "erapidevice");
+ var result = AdbRunner.FormatDisplayName ("erapidevice");
Assert.AreEqual ("Erapidevice", result, "Should not replace 'api' when it's part of a larger word");
}
@@ -571,36 +546,30 @@ public void FormatDisplayName_DoesNotReplaceApiInsideWords ()
[Test]
public void MergeDevicesAndEmulators_NoEmulators_ReturnsAdbDevicesOnly ()
{
- var task = new MockGetAvailableAndroidDevices {
- BuildEngine = engine,
- };
-
- var adbDevices = new List {
- CreateDeviceItem ("0A041FDD400327", "Pixel 5", "Device", "Online"),
+ var adbDevices = new List {
+ CreateDeviceInfo ("0A041FDD400327", "Pixel 5", AdbDeviceType.Device, AdbDeviceStatus.Online),
};
var availableEmulators = new List ();
- var result = task.MergeDevicesAndEmulators (adbDevices, availableEmulators);
+ var merged = AdbRunner.MergeDevicesAndEmulators (adbDevices, availableEmulators);
+ var result = GetAvailableAndroidDevices.ConvertToTaskItems (merged);
- Assert.AreEqual (1, result.Count, "Should return only adb devices");
+ Assert.AreEqual (1, result.Length, "Should return only adb devices");
Assert.AreEqual ("0A041FDD400327", result [0].ItemSpec);
}
[Test]
public void MergeDevicesAndEmulators_NoRunningEmulators_AddsAllAvailableEmulators ()
{
- var task = new MockGetAvailableAndroidDevices {
- BuildEngine = engine,
- };
-
- var adbDevices = new List {
- CreateDeviceItem ("0A041FDD400327", "Pixel 5", "Device", "Online"),
+ var adbDevices = new List {
+ CreateDeviceInfo ("0A041FDD400327", "Pixel 5", AdbDeviceType.Device, AdbDeviceStatus.Online),
};
var availableEmulators = new List { "pixel_7_api_35", "pixel_9_api_36" };
- var result = task.MergeDevicesAndEmulators (adbDevices, availableEmulators);
+ var merged = AdbRunner.MergeDevicesAndEmulators (adbDevices, availableEmulators);
+ var result = GetAvailableAndroidDevices.ConvertToTaskItems (merged);
- Assert.AreEqual (3, result.Count, "Should return adb device + 2 available emulators");
+ Assert.AreEqual (3, result.Length, "Should return adb device + 2 available emulators");
// First item: physical device (online, sorted first)
Assert.AreEqual ("0A041FDD400327", result [0].ItemSpec);
@@ -621,20 +590,17 @@ public void MergeDevicesAndEmulators_NoRunningEmulators_AddsAllAvailableEmulator
[Test]
public void MergeDevicesAndEmulators_RunningEmulator_NoDuplicate ()
{
- var task = new MockGetAvailableAndroidDevices {
- BuildEngine = engine,
- };
-
// Emulator is running (has adb entry with AvdName metadata)
- var runningEmulator = CreateDeviceItem ("emulator-5554", "Pixel 7 API 35", "Emulator", "Online");
- runningEmulator.SetMetadata ("AvdName", "pixel_7_api_35");
+ var runningEmulator = CreateDeviceInfo ("emulator-5554", "Pixel 7 API 35", AdbDeviceType.Emulator, AdbDeviceStatus.Online);
+ runningEmulator.AvdName = "pixel_7_api_35";
- var adbDevices = new List { runningEmulator };
+ var adbDevices = new List { runningEmulator };
var availableEmulators = new List { "pixel_7_api_35" };
- var result = task.MergeDevicesAndEmulators (adbDevices, availableEmulators);
+ var merged = AdbRunner.MergeDevicesAndEmulators (adbDevices, availableEmulators);
+ var result = GetAvailableAndroidDevices.ConvertToTaskItems (merged);
- Assert.AreEqual (1, result.Count, "Should not duplicate running emulator");
+ Assert.AreEqual (1, result.Length, "Should not duplicate running emulator");
Assert.AreEqual ("emulator-5554", result [0].ItemSpec, "Should keep the running emulator entry");
Assert.AreEqual ("Online", result [0].GetMetadata ("Status"));
}
@@ -642,22 +608,19 @@ public void MergeDevicesAndEmulators_RunningEmulator_NoDuplicate ()
[Test]
public void MergeDevicesAndEmulators_MixedRunningAndNotRunning ()
{
- var task = new MockGetAvailableAndroidDevices {
- BuildEngine = engine,
- };
-
// One emulator is running
- var runningEmulator = CreateDeviceItem ("emulator-5554", "Pixel 7 API 35", "Emulator", "Online");
- runningEmulator.SetMetadata ("AvdName", "pixel_7_api_35");
+ var runningEmulator = CreateDeviceInfo ("emulator-5554", "Pixel 7 API 35", AdbDeviceType.Emulator, AdbDeviceStatus.Online);
+ runningEmulator.AvdName = "pixel_7_api_35";
- var physicalDevice = CreateDeviceItem ("0A041FDD400327", "Pixel 5", "Device", "Online");
+ var physicalDevice = CreateDeviceInfo ("0A041FDD400327", "Pixel 5", AdbDeviceType.Device, AdbDeviceStatus.Online);
- var adbDevices = new List { runningEmulator, physicalDevice };
+ var adbDevices = new List { runningEmulator, physicalDevice };
var availableEmulators = new List { "pixel_7_api_35", "pixel_9_api_36", "nexus_5_api_30" };
- var result = task.MergeDevicesAndEmulators (adbDevices, availableEmulators);
+ var merged = AdbRunner.MergeDevicesAndEmulators (adbDevices, availableEmulators);
+ var result = GetAvailableAndroidDevices.ConvertToTaskItems (merged);
- Assert.AreEqual (4, result.Count, "Should have: 1 running emulator + 1 device + 2 non-running emulators");
+ Assert.AreEqual (4, result.Length, "Should have: 1 running emulator + 1 device + 2 non-running emulators");
// Online devices come first, sorted alphabetically by description
Assert.AreEqual ("0A041FDD400327", result [0].ItemSpec);
@@ -679,36 +642,30 @@ public void MergeDevicesAndEmulators_MixedRunningAndNotRunning ()
[Test]
public void MergeDevicesAndEmulators_CaseInsensitiveAvdNameMatching ()
{
- var task = new MockGetAvailableAndroidDevices {
- BuildEngine = engine,
- };
-
// Running emulator with different case
- var runningEmulator = CreateDeviceItem ("emulator-5554", "Pixel 7 API 35", "Emulator", "Online");
- runningEmulator.SetMetadata ("AvdName", "Pixel_7_API_35");
+ var runningEmulator = CreateDeviceInfo ("emulator-5554", "Pixel 7 API 35", AdbDeviceType.Emulator, AdbDeviceStatus.Online);
+ runningEmulator.AvdName = "Pixel_7_API_35";
- var adbDevices = new List { runningEmulator };
+ var adbDevices = new List { runningEmulator };
var availableEmulators = new List { "pixel_7_api_35" }; // lowercase
- var result = task.MergeDevicesAndEmulators (adbDevices, availableEmulators);
+ var merged = AdbRunner.MergeDevicesAndEmulators (adbDevices, availableEmulators);
+ var result = GetAvailableAndroidDevices.ConvertToTaskItems (merged);
- Assert.AreEqual (1, result.Count, "Should match AVD names case-insensitively");
+ Assert.AreEqual (1, result.Length, "Should match AVD names case-insensitively");
Assert.AreEqual ("emulator-5554", result [0].ItemSpec);
}
[Test]
public void MergeDevicesAndEmulators_EmptyAdbDevices_ReturnsAllAvailableEmulators ()
{
- var task = new MockGetAvailableAndroidDevices {
- BuildEngine = engine,
- };
-
- var adbDevices = new List ();
+ var adbDevices = new List ();
var availableEmulators = new List { "pixel_7_api_35", "pixel_9_api_36" };
- var result = task.MergeDevicesAndEmulators (adbDevices, availableEmulators);
+ var merged = AdbRunner.MergeDevicesAndEmulators (adbDevices, availableEmulators);
+ var result = GetAvailableAndroidDevices.ConvertToTaskItems (merged);
- Assert.AreEqual (2, result.Count, "Should return all available emulators");
+ Assert.AreEqual (2, result.Length, "Should return all available emulators");
Assert.AreEqual ("pixel_7_api_35", result [0].ItemSpec);
Assert.AreEqual ("Pixel 7 API 35 (Not Running)", result [0].GetMetadata ("Description"));
Assert.AreEqual ("pixel_9_api_36", result [1].ItemSpec);
@@ -718,53 +675,48 @@ public void MergeDevicesAndEmulators_EmptyAdbDevices_ReturnsAllAvailableEmulator
[Test]
public void MergeDevicesAndEmulators_AllEmulatorsRunning_NoDuplicates ()
{
- var task = new MockGetAvailableAndroidDevices {
- BuildEngine = engine,
- };
+ var emulator1 = CreateDeviceInfo ("emulator-5554", "Pixel 7 API 35", AdbDeviceType.Emulator, AdbDeviceStatus.Online);
+ emulator1.AvdName = "pixel_7_api_35";
- var emulator1 = CreateDeviceItem ("emulator-5554", "Pixel 7 API 35", "Emulator", "Online");
- emulator1.SetMetadata ("AvdName", "pixel_7_api_35");
+ var emulator2 = CreateDeviceInfo ("emulator-5556", "Pixel 9 API 36", AdbDeviceType.Emulator, AdbDeviceStatus.Online);
+ emulator2.AvdName = "pixel_9_api_36";
- var emulator2 = CreateDeviceItem ("emulator-5556", "Pixel 9 API 36", "Emulator", "Online");
- emulator2.SetMetadata ("AvdName", "pixel_9_api_36");
-
- var adbDevices = new List { emulator1, emulator2 };
+ var adbDevices = new List { emulator1, emulator2 };
var availableEmulators = new List { "pixel_7_api_35", "pixel_9_api_36" };
- var result = task.MergeDevicesAndEmulators (adbDevices, availableEmulators);
+ var merged = AdbRunner.MergeDevicesAndEmulators (adbDevices, availableEmulators);
+ var result = GetAvailableAndroidDevices.ConvertToTaskItems (merged);
- Assert.AreEqual (2, result.Count, "Should not add duplicates when all emulators are running");
+ Assert.AreEqual (2, result.Length, "Should not add duplicates when all emulators are running");
Assert.AreEqual ("Pixel 7 API 35", result [0].GetMetadata ("Description"), "First should be alphabetically first");
Assert.AreEqual ("Pixel 9 API 36", result [1].GetMetadata ("Description"), "Second should be alphabetically second");
- Assert.IsTrue (result.TrueForAll (d => d.GetMetadata ("Status") == "Online"), "All should be Online (running)");
+ Assert.IsTrue (result.All (d => d.GetMetadata ("Status") == "Online"), "All should be Online (running)");
}
[Test]
public void MergeDevicesAndEmulators_NonRunningEmulatorHasFormattedDescription ()
{
- var task = new MockGetAvailableAndroidDevices {
- BuildEngine = engine,
- };
-
- var adbDevices = new List ();
+ var adbDevices = new List ();
var availableEmulators = new List { "pixel_7_pro_api_35" };
- var result = task.MergeDevicesAndEmulators (adbDevices, availableEmulators);
+ var merged = AdbRunner.MergeDevicesAndEmulators (adbDevices, availableEmulators);
+ var result = GetAvailableAndroidDevices.ConvertToTaskItems (merged);
- Assert.AreEqual (1, result.Count);
+ Assert.AreEqual (1, result.Length);
Assert.AreEqual ("Pixel 7 Pro API 35 (Not Running)", result [0].GetMetadata ("Description"), "Description should be formatted with (Not Running) suffix");
}
///
- /// Helper method to create a device ITaskItem for testing
+ /// Helper method to create an AdbDeviceInfo for testing
///
- static ITaskItem CreateDeviceItem (string serial, string description, string type, string status)
+ static AdbDeviceInfo CreateDeviceInfo (string serial, string description, AdbDeviceType type, AdbDeviceStatus status)
{
- var item = new TaskItem (serial);
- item.SetMetadata ("Description", description);
- item.SetMetadata ("Type", type);
- item.SetMetadata ("Status", status);
- return item;
+ return new AdbDeviceInfo {
+ Serial = serial,
+ Description = description,
+ Type = type,
+ Status = status,
+ };
}
}
}