Start in the andorid tests
This commit is contained in:
Родитель
405864895b
Коммит
c0e5600a11
|
@ -1,4 +1,5 @@
|
|||
using DotNetDevices.Android;
|
||||
using System;
|
||||
using System.Threading.Tasks;
|
||||
using Xunit;
|
||||
|
||||
|
@ -21,6 +22,71 @@ namespace DotNetDevices.Tests
|
|||
Assert.Equal(id, device.Id);
|
||||
Assert.Equal(name, device.Name);
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public async Task CanReadTV()
|
||||
{
|
||||
var config = new VirtualDeviceConfig("TestData/Android/AvdConfigIni_TV.txt");
|
||||
|
||||
var device = await config.CreateVirtualDeviceAsync();
|
||||
|
||||
Assert.Equal(VirtualDeviceType.TV, device.Type);
|
||||
Assert.Equal(new Version(10, 0), device.Version);
|
||||
Assert.Equal(29, device.ApiLevel);
|
||||
Assert.Equal(VirtualDeviceRuntime.AndroidTV, device.Runtime);
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public async Task CanReadWear()
|
||||
{
|
||||
var config = new VirtualDeviceConfig("TestData/Android/AvdConfigIni_Wear.txt");
|
||||
|
||||
var device = await config.CreateVirtualDeviceAsync();
|
||||
|
||||
Assert.Equal(VirtualDeviceType.Wearable, device.Type);
|
||||
Assert.Equal(new Version(9, 0), device.Version);
|
||||
Assert.Equal(28, device.ApiLevel);
|
||||
Assert.Equal(VirtualDeviceRuntime.AndroidWear, device.Runtime);
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public async Task CanReadTablet()
|
||||
{
|
||||
var config = new VirtualDeviceConfig("TestData/Android/AvdConfigIni_Tablet.txt");
|
||||
|
||||
var device = await config.CreateVirtualDeviceAsync();
|
||||
|
||||
Assert.Equal(VirtualDeviceType.Tablet, device.Type);
|
||||
Assert.Equal(new Version(9, 0), device.Version);
|
||||
Assert.Equal(28, device.ApiLevel);
|
||||
Assert.Equal(VirtualDeviceRuntime.Android, device.Runtime);
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public async Task CanReadGeneric()
|
||||
{
|
||||
var config = new VirtualDeviceConfig("TestData/Android/AvdConfigIni_Generic.txt");
|
||||
|
||||
var device = await config.CreateVirtualDeviceAsync();
|
||||
|
||||
Assert.Equal(VirtualDeviceType.Phone, device.Type);
|
||||
Assert.Equal(new Version(9, 0), device.Version);
|
||||
Assert.Equal(28, device.ApiLevel);
|
||||
Assert.Equal(VirtualDeviceRuntime.Android, device.Runtime);
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public async Task CanReadPhone()
|
||||
{
|
||||
var config = new VirtualDeviceConfig("TestData/Android/AvdConfigIni_Phone.txt");
|
||||
|
||||
var device = await config.CreateVirtualDeviceAsync();
|
||||
|
||||
Assert.Equal(VirtualDeviceType.Phone, device.Type);
|
||||
Assert.Equal(new Version(10, 0), device.Version);
|
||||
Assert.Equal(29, device.ApiLevel);
|
||||
Assert.Equal(VirtualDeviceRuntime.Android, device.Runtime);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
@ -0,0 +1,40 @@
|
|||
AvdId=phone_xh-dpi_4_7in_pie_9_0_-_api_28
|
||||
PlayStore.enabled=false
|
||||
abi.type=x86
|
||||
avd.ini.displayname=Phone Xh-DPI 4.7in Pie 9.0 - API 28
|
||||
avd.ini.encoding=UTF-8
|
||||
disk.dataPartition.size=2G
|
||||
fastboot.forceColdBoot=no
|
||||
fastboot.forceFastBoot=yes
|
||||
hw.accelerometer=yes
|
||||
hw.arc=no
|
||||
hw.audioInput=yes
|
||||
hw.battery=yes
|
||||
hw.camera.back=virtualscene
|
||||
hw.cpu.arch=x86
|
||||
hw.cpu.ncore=4
|
||||
hw.dPad=no
|
||||
hw.device.hash2=MD5:ed3db32523e6d6f8210c6377d2c66883
|
||||
hw.device.manufacturer=Generic
|
||||
hw.device.name=4.7in WXGA
|
||||
hw.gps=yes
|
||||
hw.gpu.enabled=yes
|
||||
hw.gpu.mode=auto
|
||||
hw.keyboard=yes
|
||||
hw.lcd.density=320
|
||||
hw.lcd.height=720
|
||||
hw.lcd.width=1280
|
||||
hw.mainKeys=yes
|
||||
hw.ramSize=512
|
||||
hw.sdCard=no
|
||||
hw.sensors.orientation=yes
|
||||
hw.sensors.proximity=yes
|
||||
hw.trackBall=no
|
||||
image.sysdir.1=system-images\android-28\default\x86\
|
||||
sdcard.size=512M
|
||||
showDeviceFrame=no
|
||||
skin.dynamic=yes
|
||||
skin.name=1280x720
|
||||
tag.display=Default
|
||||
tag.id=default
|
||||
vm.heapSize=256
|
|
@ -0,0 +1,41 @@
|
|||
AvdId=pixel_2_q_10_0_-_api_29
|
||||
PlayStore.enabled=true
|
||||
abi.type=x86
|
||||
avd.ini.displayname=Pixel 2 Q 10.0 - API 29
|
||||
avd.ini.encoding=UTF-8
|
||||
disk.dataPartition.size=6442450944
|
||||
fastboot.forceColdBoot=no
|
||||
fastboot.forceFastBoot=yes
|
||||
hw.accelerometer=yes
|
||||
hw.arc=no
|
||||
hw.audioInput=yes
|
||||
hw.battery=yes
|
||||
hw.camera.back=virtualscene
|
||||
hw.camera.front=emulated
|
||||
hw.cpu.arch=x86
|
||||
hw.cpu.ncore=4
|
||||
hw.dPad=no
|
||||
hw.device.hash2=MD5:55acbc835978f326788ed66a5cd4c9a7
|
||||
hw.device.manufacturer=Google
|
||||
hw.device.name=pixel_2
|
||||
hw.gps=yes
|
||||
hw.gpu.enabled=yes
|
||||
hw.gpu.mode=auto
|
||||
hw.keyboard=yes
|
||||
hw.lcd.density=420
|
||||
hw.lcd.height=1920
|
||||
hw.lcd.width=1080
|
||||
hw.mainKeys=no
|
||||
hw.ramSize=1536
|
||||
hw.sdCard=yes
|
||||
hw.sensors.orientation=yes
|
||||
hw.sensors.proximity=yes
|
||||
hw.trackBall=no
|
||||
image.sysdir.1=system-images\android-29\google_apis_playstore\x86\
|
||||
sdcard.size=512M
|
||||
showDeviceFrame=yes
|
||||
skin.name=pixel_2
|
||||
skin.path=C:\Android\android-sdk\skins\pixel_2
|
||||
tag.display=Google Play
|
||||
tag.id=google_apis_playstore
|
||||
vm.heapSize=256
|
|
@ -0,0 +1,41 @@
|
|||
AvdId=android_tv_720p_q_10_0_-_api_29
|
||||
PlayStore.enabled=false
|
||||
abi.type=x86
|
||||
avd.ini.displayname=Android TV (720p) Q 10.0 - API 29
|
||||
avd.ini.encoding=UTF-8
|
||||
disk.dataPartition.size=2G
|
||||
fastboot.forceColdBoot=no
|
||||
fastboot.forceFastBoot=yes
|
||||
hw.accelerometer=no
|
||||
hw.arc=no
|
||||
hw.audioInput=yes
|
||||
hw.battery=no
|
||||
hw.cpu.arch=x86
|
||||
hw.cpu.ncore=4
|
||||
hw.dPad=yes
|
||||
hw.device.hash2=MD5:62a27947d2faec95c32cddffb87aa6ec
|
||||
hw.device.manufacturer=Google
|
||||
hw.device.name=tv_720p
|
||||
hw.gps=yes
|
||||
hw.gpu.enabled=yes
|
||||
hw.gpu.mode=auto
|
||||
hw.initialOrientation=landscape
|
||||
hw.keyboard=yes
|
||||
hw.keyboard.lid=yes
|
||||
hw.lcd.density=213
|
||||
hw.lcd.height=720
|
||||
hw.lcd.width=1280
|
||||
hw.mainKeys=yes
|
||||
hw.ramSize=1536
|
||||
hw.sdCard=yes
|
||||
hw.sensors.orientation=no
|
||||
hw.sensors.proximity=no
|
||||
hw.trackBall=no
|
||||
image.sysdir.1=system-images\android-29\android-tv\x86\
|
||||
sdcard.size=512M
|
||||
showDeviceFrame=yes
|
||||
skin.name=tv_720p
|
||||
skin.path=C:\Program Files (x86)\Microsoft Visual Studio\2019\Enterprise\Common7\IDE\Extensions\Xamarin\AndroidDeviceManager\SystemSkins\tv_720p
|
||||
tag.display=Android TV
|
||||
tag.id=android-tv
|
||||
vm.heapSize=256
|
|
@ -0,0 +1,42 @@
|
|||
AvdId=nexus_10_pie_9_0_-_api_28
|
||||
PlayStore.enabled=false
|
||||
abi.type=x86
|
||||
avd.ini.displayname=Nexus 10 Pie 9.0 - API 28
|
||||
avd.ini.encoding=UTF-8
|
||||
disk.dataPartition.size=2G
|
||||
fastboot.forceColdBoot=no
|
||||
fastboot.forceFastBoot=yes
|
||||
hw.accelerometer=yes
|
||||
hw.arc=no
|
||||
hw.audioInput=yes
|
||||
hw.battery=yes
|
||||
hw.camera.back=virtualscene
|
||||
hw.camera.front=emulated
|
||||
hw.cpu.arch=x86
|
||||
hw.cpu.ncore=4
|
||||
hw.dPad=no
|
||||
hw.device.hash2=MD5:8323741337b2d5dd4418b47c06a242b0
|
||||
hw.device.manufacturer=Google
|
||||
hw.device.name=Nexus 10
|
||||
hw.gps=yes
|
||||
hw.gpu.enabled=yes
|
||||
hw.gpu.mode=auto
|
||||
hw.initialOrientation=landscape
|
||||
hw.keyboard=yes
|
||||
hw.lcd.density=320
|
||||
hw.lcd.height=1600
|
||||
hw.lcd.width=2560
|
||||
hw.mainKeys=no
|
||||
hw.ramSize=1536
|
||||
hw.sdCard=no
|
||||
hw.sensors.orientation=yes
|
||||
hw.sensors.proximity=no
|
||||
hw.trackBall=no
|
||||
image.sysdir.1=system-images\android-28\default\x86\
|
||||
sdcard.size=512M
|
||||
showDeviceFrame=yes
|
||||
skin.name=nexus_10
|
||||
skin.path=C:\Program Files (x86)\Microsoft Visual Studio\2019\Enterprise\Common7\IDE\Extensions\Xamarin\AndroidDeviceManager\SystemSkins\nexus_10
|
||||
tag.display=Default
|
||||
tag.id=default
|
||||
vm.heapSize=256
|
|
@ -0,0 +1,41 @@
|
|||
AvdId=android_wear_round_pie_9_0_-_api_28
|
||||
PlayStore.enabled=false
|
||||
abi.type=x86
|
||||
avd.ini.displayname=Android Wear Round Pie 9.0 - API 28
|
||||
avd.ini.encoding=UTF-8
|
||||
disk.dataPartition.size=2G
|
||||
fastboot.forceColdBoot=no
|
||||
fastboot.forceFastBoot=yes
|
||||
hw.accelerometer=yes
|
||||
hw.arc=no
|
||||
hw.audioInput=yes
|
||||
hw.battery=yes
|
||||
hw.cpu.arch=x86
|
||||
hw.cpu.ncore=4
|
||||
hw.dPad=no
|
||||
hw.device.hash2=MD5:3c452b52cce72363917e3cdd3c1c5954
|
||||
hw.device.manufacturer=Google
|
||||
hw.device.name=wear_round
|
||||
hw.gps=yes
|
||||
hw.gpu.enabled=yes
|
||||
hw.gpu.mode=auto
|
||||
hw.initialOrientation=landscape
|
||||
hw.keyboard=yes
|
||||
hw.keyboard.lid=yes
|
||||
hw.lcd.density=240
|
||||
hw.lcd.height=320
|
||||
hw.lcd.width=320
|
||||
hw.mainKeys=yes
|
||||
hw.ramSize=512
|
||||
hw.sdCard=no
|
||||
hw.sensors.orientation=yes
|
||||
hw.sensors.proximity=yes
|
||||
hw.trackBall=no
|
||||
image.sysdir.1=system-images\android-28\android-wear\x86\
|
||||
sdcard.size=512M
|
||||
showDeviceFrame=yes
|
||||
skin.name=wear_round
|
||||
skin.path=C:\Program Files (x86)\Microsoft Visual Studio\2019\Enterprise\Common7\IDE\Extensions\Xamarin\AndroidDeviceManager\SystemSkins\wear_round
|
||||
tag.display=Android Wear
|
||||
tag.id=android-wear
|
||||
vm.heapSize=256
|
|
@ -0,0 +1,51 @@
|
|||
N: android=http://schemas.android.com/apk/res/android (line=2)
|
||||
E: manifest (line=2)
|
||||
A: http://schemas.android.com/apk/res/android:versionCode(0x0101021b)=1
|
||||
A: http://schemas.android.com/apk/res/android:versionName(0x0101021c)="1.0.1.0" (Raw: "1.0.1.0")
|
||||
A: http://schemas.android.com/apk/res/android:installLocation(0x010102b7)=0
|
||||
A: http://schemas.android.com/apk/res/android:compileSdkVersion(0x01010572)=29
|
||||
A: http://schemas.android.com/apk/res/android:compileSdkVersionCodename(0x01010573)="10" (Raw: "10")
|
||||
A: package="net.dot.devicetests" (Raw: "net.dot.devicetests")
|
||||
A: platformBuildVersionCode=29
|
||||
A: platformBuildVersionName=10
|
||||
E: uses-sdk (line=3)
|
||||
A: http://schemas.android.com/apk/res/android:minSdkVersion(0x0101020c)=19
|
||||
A: http://schemas.android.com/apk/res/android:targetSdkVersion(0x01010270)=29
|
||||
E: uses-permission (line=4)
|
||||
A: http://schemas.android.com/apk/res/android:name(0x01010003)="android.permission.INTERNET" (Raw: "android.permission.INTERNET")
|
||||
E: uses-permission (line=5)
|
||||
A: http://schemas.android.com/apk/res/android:name(0x01010003)="android.permission.READ_EXTERNAL_STORAGE" (Raw: "android.permission.READ_EXTERNAL_STORAGE")
|
||||
E: application (line=6)
|
||||
A: http://schemas.android.com/apk/res/android:theme(0x01010000)=@0x7f0d00c7
|
||||
A: http://schemas.android.com/apk/res/android:label(0x01010001)=@0x7f0c001b
|
||||
A: http://schemas.android.com/apk/res/android:icon(0x01010002)=@0x7f07006f
|
||||
A: http://schemas.android.com/apk/res/android:name(0x01010003)="android.app.Application" (Raw: "android.app.Application")
|
||||
A: http://schemas.android.com/apk/res/android:debuggable(0x0101000f)=true
|
||||
A: http://schemas.android.com/apk/res/android:allowBackup(0x01010280)=true
|
||||
A: http://schemas.android.com/apk/res/android:appComponentFactory(0x0101057a)="androidx.core.app.CoreComponentFactory" (Raw: "androidx.core.app.CoreComponentFactory")
|
||||
E: activity (line=7)
|
||||
A: http://schemas.android.com/apk/res/android:name(0x01010003)="crc645c25d208dccba52c.MainActivity" (Raw: "crc645c25d208dccba52c.MainActivity")
|
||||
A: http://schemas.android.com/apk/res/android:configChanges(0x0101001f)=0x00000480
|
||||
E: intent-filter (line=8)
|
||||
E: action (line=9)
|
||||
A: http://schemas.android.com/apk/res/android:name(0x01010003)="android.intent.action.MAIN" (Raw: "android.intent.action.MAIN")
|
||||
E: category (line=10)
|
||||
A: http://schemas.android.com/apk/res/android:name(0x01010003)="android.intent.category.LAUNCHER" (Raw: "android.intent.category.LAUNCHER")
|
||||
E: service (line=13)
|
||||
A: http://schemas.android.com/apk/res/android:name(0x01010003)="crc64396a3fe5f8138e3f.KeepAliveService" (Raw: "crc64396a3fe5f8138e3f.KeepAliveService")
|
||||
E: receiver (line=14)
|
||||
A: http://schemas.android.com/apk/res/android:name(0x01010003)="crc643f46942d9dd1fff9.PowerSaveModeBroadcastReceiver" (Raw: "crc643f46942d9dd1fff9.PowerSaveModeBroadcastReceiver")
|
||||
A: http://schemas.android.com/apk/res/android:enabled(0x0101000e)=true
|
||||
A: http://schemas.android.com/apk/res/android:exported(0x01010010)=false
|
||||
E: provider (line=15)
|
||||
A: http://schemas.android.com/apk/res/android:name(0x01010003)="mono.MonoRuntimeProvider" (Raw: "mono.MonoRuntimeProvider")
|
||||
A: http://schemas.android.com/apk/res/android:exported(0x01010010)=false
|
||||
A: http://schemas.android.com/apk/res/android:authorities(0x01010018)="net.dot.devicetests.mono.MonoRuntimeProvider.__mono_init__" (Raw: "net.dot.devicetests.mono.MonoRuntimeProvider.__mono_init__")
|
||||
A: http://schemas.android.com/apk/res/android:initOrder(0x0101001a)=1999999999
|
||||
E: receiver (line=17)
|
||||
A: http://schemas.android.com/apk/res/android:name(0x01010003)="mono.android.Seppuku" (Raw: "mono.android.Seppuku")
|
||||
E: intent-filter (line=18)
|
||||
E: action (line=19)
|
||||
A: http://schemas.android.com/apk/res/android:name(0x01010003)="mono.android.intent.action.SEPPUKU" (Raw: "mono.android.intent.action.SEPPUKU")
|
||||
E: category (line=20)
|
||||
A: http://schemas.android.com/apk/res/android:name(0x01010003)="mono.android.intent.category.SEPPUKU.net.dot.devicetests" (Raw: "mono.android.intent.category.SEPPUKU.net.dot.devicetests")
|
|
@ -58,6 +58,34 @@ namespace DotNetDevices.Android
|
|||
return targets;
|
||||
}
|
||||
|
||||
public async Task<IEnumerable<string>> GetVirtualDeviceNamesAsync(CancellationToken cancellationToken = default)
|
||||
{
|
||||
logger?.LogInformation("Retrieving all the virtual devices...");
|
||||
|
||||
var args = $"list avd";
|
||||
|
||||
var result = await processRunner.RunAsync(avdmanager, args, null, cancellationToken).ConfigureAwait(false);
|
||||
|
||||
var avds = new List<string>();
|
||||
|
||||
foreach (var output in GetListResults(result))
|
||||
{
|
||||
var pathMatch = virtualDevicePathRegex.Match(output);
|
||||
if (pathMatch.Success)
|
||||
{
|
||||
var path = pathMatch.Groups[1].Value;
|
||||
if (Directory.Exists(path))
|
||||
{
|
||||
var avd = Path.GetFileNameWithoutExtension(path);
|
||||
|
||||
avds.Add(avd);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return avds;
|
||||
}
|
||||
|
||||
public async Task<IEnumerable<VirtualDevice>> GetVirtualDevicesAsync(CancellationToken cancellationToken = default)
|
||||
{
|
||||
logger?.LogInformation("Retrieving all the virtual devices...");
|
||||
|
@ -94,7 +122,27 @@ namespace DotNetDevices.Android
|
|||
|
||||
var args = $"delete avd --name \"{name}\"";
|
||||
|
||||
await processRunner.RunAsync(avdmanager, args, null, cancellationToken).ConfigureAwait(false);
|
||||
try
|
||||
{
|
||||
await processRunner.RunAsync(avdmanager, args, null, cancellationToken).ConfigureAwait(false);
|
||||
}
|
||||
catch (ProcessResultException ex) when (WasExisting(ex.ProcessResult))
|
||||
{
|
||||
// no-op
|
||||
}
|
||||
|
||||
bool WasExisting(ProcessResult result)
|
||||
{
|
||||
var expected = $"Error: There is no Android Virtual Device named '{name}'.";
|
||||
|
||||
foreach (var output in result.GetErrorOutput())
|
||||
{
|
||||
if (output.Contains(expected, StringComparison.OrdinalIgnoreCase))
|
||||
return true;
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
public async Task CreateVirtualDeviceAsync(string name, string package, CreateVirtualDeviceOptions? options = null, CancellationToken cancellationToken = default)
|
||||
|
@ -105,7 +153,27 @@ namespace DotNetDevices.Android
|
|||
if (options?.Overwrite == true)
|
||||
args += " --force";
|
||||
|
||||
await processRunner.RunWithInputAsync("no", avdmanager, args, null, cancellationToken).ConfigureAwait(false);
|
||||
try
|
||||
{
|
||||
await processRunner.RunWithInputAsync("no", avdmanager, args, null, cancellationToken).ConfigureAwait(false);
|
||||
}
|
||||
catch (ProcessResultException ex) when (WasExisting(ex.ProcessResult))
|
||||
{
|
||||
// no-op
|
||||
}
|
||||
|
||||
bool WasExisting(ProcessResult result)
|
||||
{
|
||||
var expected = $"Error: Android Virtual Device '{name}' already exists.";
|
||||
|
||||
foreach (var output in result.GetErrorOutput())
|
||||
{
|
||||
if (output.Contains(expected, StringComparison.OrdinalIgnoreCase))
|
||||
return true;
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
public async Task RenameVirtualDeviceAsync(string name, string newName, CancellationToken cancellationToken = default)
|
||||
|
|
|
@ -90,7 +90,7 @@ namespace DotNetDevices.Android
|
|||
}
|
||||
}
|
||||
|
||||
public async Task<IEnumerable<VirtualDevice>> GetVirtualDevicesAsync(CancellationToken cancellationToken = default)
|
||||
public async Task<IEnumerable<string>> GetVirtualDevicesAsync(CancellationToken cancellationToken = default)
|
||||
{
|
||||
logger?.LogInformation("Retrieving all the virtual devices...");
|
||||
|
||||
|
@ -98,10 +98,10 @@ namespace DotNetDevices.Android
|
|||
|
||||
var result = await processRunner.RunAsync(emulator, args, null, cancellationToken).ConfigureAwait(false);
|
||||
|
||||
var avd = new List<VirtualDevice>(result.OutputCount);
|
||||
var avd = new List<string>(result.OutputCount);
|
||||
foreach (var output in result.GetOutput())
|
||||
{
|
||||
avd.Add(new VirtualDevice(output, output));
|
||||
avd.Add(output.Trim());
|
||||
}
|
||||
return avd;
|
||||
}
|
||||
|
|
|
@ -4,21 +4,76 @@ namespace DotNetDevices.Android
|
|||
{
|
||||
public class VirtualDevice
|
||||
{
|
||||
private readonly string? configPath;
|
||||
|
||||
public VirtualDevice(string id, string name, string? configPath = null)
|
||||
public VirtualDevice(string id, string name, string package, VirtualDeviceType type, int apiLevel, string? configPath)
|
||||
{
|
||||
Id = id ?? throw new ArgumentNullException(nameof(id));
|
||||
Name = name ?? throw new ArgumentNullException(nameof(name));
|
||||
|
||||
this.configPath = configPath;
|
||||
Package = package ?? throw new ArgumentNullException(nameof(package));
|
||||
Type = type;
|
||||
ApiLevel = apiLevel;
|
||||
ConfigPath = configPath;
|
||||
}
|
||||
|
||||
public string Id { get; }
|
||||
|
||||
public string Name { get; }
|
||||
|
||||
public string Package { get; }
|
||||
|
||||
public VirtualDeviceType Type { get; }
|
||||
|
||||
public int ApiLevel { get; }
|
||||
|
||||
public string? ConfigPath { get; }
|
||||
|
||||
public Version Version =>
|
||||
ApiLevel switch
|
||||
{
|
||||
1 => new Version(1, 0),
|
||||
2 => new Version(1, 1),
|
||||
3 => new Version(1, 5),
|
||||
4 => new Version(1, 6),
|
||||
5 => new Version(2, 0),
|
||||
6 => new Version(2, 0, 1),
|
||||
7 => new Version(2, 1),
|
||||
8 => new Version(2, 2),
|
||||
9 => new Version(2, 3),
|
||||
10 => new Version(2, 3, 3),
|
||||
11 => new Version(3, 0),
|
||||
12 => new Version(3, 1),
|
||||
13 => new Version(3, 2),
|
||||
14 => new Version(4, 0),
|
||||
15 => new Version(4, 0, 3),
|
||||
16 => new Version(4, 1),
|
||||
17 => new Version(4, 2),
|
||||
18 => new Version(4, 3),
|
||||
19 => new Version(4, 4),
|
||||
20 => new Version(4, 4), // 4.4W (wear)
|
||||
21 => new Version(5, 0),
|
||||
22 => new Version(5, 1),
|
||||
23 => new Version(6, 0),
|
||||
24 => new Version(7, 0),
|
||||
25 => new Version(7, 1),
|
||||
26 => new Version(8, 0),
|
||||
27 => new Version(8, 1),
|
||||
28 => new Version(9, 0),
|
||||
29 => new Version(10, 0),
|
||||
30 => new Version(11, 0),
|
||||
_ => new Version(),
|
||||
};
|
||||
|
||||
public VirtualDeviceRuntime Runtime =>
|
||||
Type switch
|
||||
{
|
||||
VirtualDeviceType.Unknown => VirtualDeviceRuntime.Android,
|
||||
VirtualDeviceType.Phone => VirtualDeviceRuntime.Android,
|
||||
VirtualDeviceType.Tablet => VirtualDeviceRuntime.Android,
|
||||
VirtualDeviceType.Wearable => VirtualDeviceRuntime.AndroidWear,
|
||||
VirtualDeviceType.TV => VirtualDeviceRuntime.AndroidTV,
|
||||
_ => VirtualDeviceRuntime.Android,
|
||||
};
|
||||
|
||||
public override string ToString() =>
|
||||
$"{Name}";
|
||||
$"{Name} (API {ApiLevel})";
|
||||
}
|
||||
}
|
||||
|
|
|
@ -2,6 +2,7 @@
|
|||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.IO;
|
||||
using System.Text.RegularExpressions;
|
||||
using System.Threading;
|
||||
using System.Threading.Tasks;
|
||||
|
||||
|
@ -9,6 +10,8 @@ namespace DotNetDevices.Android
|
|||
{
|
||||
public class VirtualDeviceConfig
|
||||
{
|
||||
private static readonly Regex androidApiRegex = new Regex(@"android-(\d+)");
|
||||
|
||||
private readonly string configPath;
|
||||
private readonly ILogger? logger;
|
||||
|
||||
|
@ -47,12 +50,80 @@ namespace DotNetDevices.Android
|
|||
var props = await GetPropertiesAsync(cancellationToken).ConfigureAwait(false);
|
||||
|
||||
if (!props.TryGetValue("avdid", out var id))
|
||||
{
|
||||
var avdDir = Path.GetDirectoryName(configPath);
|
||||
id = Path.GetFileNameWithoutExtension(avdDir);
|
||||
}
|
||||
|
||||
if (string.IsNullOrEmpty(id))
|
||||
throw new Exception($"Invalid config.ini. Unable to find the virtual device ID.");
|
||||
|
||||
if (!props.TryGetValue("avd.ini.displayname", out var name))
|
||||
name = id;
|
||||
|
||||
return new VirtualDevice(id, name, configPath);
|
||||
if (!props.TryGetValue("image.sysdir.1", out var package))
|
||||
package = "";
|
||||
var packageParts = package.Split(new[] { '\\', '/', ';' }, StringSplitOptions.RemoveEmptyEntries);
|
||||
package = string.Join(";", packageParts);
|
||||
|
||||
var apiLevel = 0;
|
||||
if (packageParts.Length == 4)
|
||||
{
|
||||
var apiMatch = androidApiRegex.Match(packageParts[1]);
|
||||
if (apiMatch.Success)
|
||||
apiLevel = int.Parse(apiMatch.Groups[1].Value);
|
||||
}
|
||||
|
||||
if (!TryGetType(props, out var type))
|
||||
type = VirtualDeviceType.Unknown;
|
||||
|
||||
return new VirtualDevice(id, name, package, type, apiLevel, configPath);
|
||||
}
|
||||
|
||||
private static bool TryGetType(IReadOnlyDictionary<string, string> props, out VirtualDeviceType value)
|
||||
{
|
||||
if (props.TryGetValue("tag.id", out var type))
|
||||
{
|
||||
switch (type.Trim().ToLowerInvariant())
|
||||
{
|
||||
case "android-tv":
|
||||
value = VirtualDeviceType.TV;
|
||||
return true;
|
||||
case "android-wear":
|
||||
value = VirtualDeviceType.Wearable;
|
||||
return true;
|
||||
case "default":
|
||||
case "google_apis":
|
||||
case "google_apis_playstore":
|
||||
value =
|
||||
TryGetDimensions(props, out var width, out var height, out var density)
|
||||
&& Math.Min(width, height) / (density / 160) >= 600
|
||||
? VirtualDeviceType.Tablet
|
||||
: VirtualDeviceType.Phone;
|
||||
return true;
|
||||
}
|
||||
}
|
||||
|
||||
value = VirtualDeviceType.Unknown;
|
||||
return false;
|
||||
}
|
||||
|
||||
private static bool TryGetDimensions(IReadOnlyDictionary<string, string> props, out int width, out int height, out double density)
|
||||
{
|
||||
width = 0;
|
||||
height = 0;
|
||||
density = 160;
|
||||
|
||||
if (!props.TryGetValue("hw.lcd.width", out var widthString) || !int.TryParse(widthString, out width))
|
||||
return false;
|
||||
|
||||
if (!props.TryGetValue("hw.lcd.height", out var heightString) || !int.TryParse(heightString, out height))
|
||||
return false;
|
||||
|
||||
if (!props.TryGetValue("hw.lcd.density", out var densityString) || !double.TryParse(densityString, out density))
|
||||
density = 160;
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
private static Dictionary<string, string> ParseConfig(string contents)
|
||||
|
|
|
@ -0,0 +1,9 @@
|
|||
namespace DotNetDevices.Android
|
||||
{
|
||||
public enum VirtualDeviceRuntime
|
||||
{
|
||||
Android,
|
||||
AndroidWear,
|
||||
AndroidTV
|
||||
}
|
||||
}
|
|
@ -0,0 +1,11 @@
|
|||
namespace DotNetDevices.Android
|
||||
{
|
||||
public enum VirtualDeviceType
|
||||
{
|
||||
Unknown,
|
||||
Phone,
|
||||
Tablet,
|
||||
Wearable,
|
||||
TV
|
||||
}
|
||||
}
|
|
@ -32,6 +32,12 @@ namespace DotNetDevices.Commands
|
|||
new Argument<string?>("NAME", "The name of the new virtual device."),
|
||||
new Argument<string?>("PACKAGE", "The package to use for the new virtual device."),
|
||||
}.WithHandler(CommandHandler.Create(typeof(AndroidCommand).GetMethod(nameof(HandleCreateAsync))!)),
|
||||
new Command("delete", "Delete an existing virtual device.")
|
||||
{
|
||||
new Option<string?>(new[] { "--sdk" }, "The path to the Android SDK directory."),
|
||||
CommandLine.CreateVerbosity(),
|
||||
new Argument<string?>("NAME", "The name of the new virtual device."),
|
||||
}.WithHandler(CommandHandler.Create(typeof(AndroidCommand).GetMethod(nameof(HandleDeleteAsync))!)),
|
||||
new Command("boot", "Boot a particular virtual device.")
|
||||
{
|
||||
new Option<string?>(new[] { "--sdk" }, "The path to the Android SDK directory."),
|
||||
|
@ -60,31 +66,6 @@ namespace DotNetDevices.Commands
|
|||
|
||||
var filtered = (IEnumerable<VirtualDevice>)devices;
|
||||
|
||||
|
||||
//try
|
||||
//{
|
||||
// await avdmanager.DeleteVirtualDeviceAsync("TESTING");
|
||||
//}
|
||||
//catch { }
|
||||
|
||||
//try
|
||||
//{
|
||||
// await avdmanager.DeleteVirtualDeviceAsync("TESTED");
|
||||
//}
|
||||
//catch { }
|
||||
|
||||
//await avdmanager.CreateVirtualDeviceAsync("TESTING", "system-images;android-28;google_apis_playstore;x86_64");
|
||||
|
||||
//await avdmanager.CreateVirtualDeviceAsync("TESTING", "system-images;android-28;google_apis_playstore;x86_64", new VirtualDeviceCreateOptions { Overwrite = true });
|
||||
|
||||
//await avdmanager.RenameVirtualDeviceAsync("TESTING", "TESTED");
|
||||
//await avdmanager.MoveVirtualDeviceAsync("TESTED", "/Users/matthew/.android/avd/tested.avd");
|
||||
|
||||
//await avdmanager.DeleteVirtualDeviceAsync("TESTING");
|
||||
|
||||
|
||||
|
||||
|
||||
//term = term?.ToLowerInvariant()?.Trim();
|
||||
|
||||
//var filtered = (IEnumerable<Simulator>)simulators;
|
||||
|
@ -128,6 +109,10 @@ namespace DotNetDevices.Commands
|
|||
var table = new TableView<VirtualDevice>();
|
||||
table.AddColumn(s => s.Id, "Id");
|
||||
table.AddColumn(s => s.Name, "Name");
|
||||
table.AddColumn(s => s.Type, "Type");
|
||||
table.AddColumn(s => s.Version, "Version");
|
||||
table.AddColumn(s => s.ApiLevel, "API Level");
|
||||
//table.AddColumn(s => s.State, "State");
|
||||
table.Items = all;
|
||||
|
||||
console.Append(new StackLayoutView { table });
|
||||
|
@ -146,14 +131,45 @@ namespace DotNetDevices.Commands
|
|||
|
||||
var avdmanager = new AVDManager(sdk, logger);
|
||||
|
||||
if (!replace)
|
||||
{
|
||||
var devices = await avdmanager.GetVirtualDeviceNamesAsync(cancellationToken);
|
||||
if (devices.Any(d => d.Equals(name, StringComparison.OrdinalIgnoreCase)))
|
||||
{
|
||||
logger.LogInformation($"Virtual device already exists.");
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
||||
var options = new CreateVirtualDeviceOptions
|
||||
{
|
||||
Overwrite = replace
|
||||
Overwrite = replace,
|
||||
};
|
||||
|
||||
await avdmanager.CreateVirtualDeviceAsync(name, package, options, cancellationToken);
|
||||
}
|
||||
|
||||
public static async Task HandleDeleteAsync(
|
||||
string name,
|
||||
string? sdk = null,
|
||||
string? verbosity = null,
|
||||
IConsole console = null!,
|
||||
CancellationToken cancellationToken = default)
|
||||
{
|
||||
var logger = console.CreateLogger(verbosity);
|
||||
|
||||
var avdmanager = new AVDManager(sdk, logger);
|
||||
|
||||
var devices = await avdmanager.GetVirtualDeviceNamesAsync(cancellationToken);
|
||||
if (devices.All(d => !d.Equals(name, StringComparison.OrdinalIgnoreCase)))
|
||||
{
|
||||
logger.LogInformation($"Virtual device does not exist.");
|
||||
return;
|
||||
}
|
||||
|
||||
await avdmanager.DeleteVirtualDeviceAsync(name, cancellationToken);
|
||||
}
|
||||
|
||||
public static async Task<int> HandleBootAsync(
|
||||
string name,
|
||||
string? sdk = null,
|
||||
|
@ -166,7 +182,7 @@ namespace DotNetDevices.Commands
|
|||
var emulator = new EmulatorManager(sdk, logger);
|
||||
|
||||
var avds = await emulator.GetVirtualDevicesAsync(cancellationToken);
|
||||
if (avds.All(a => !a.Id.Equals(name, StringComparison.OrdinalIgnoreCase)))
|
||||
if (avds.All(a => !a.Equals(name, StringComparison.OrdinalIgnoreCase)))
|
||||
{
|
||||
logger.LogError($"No virtual device with name {name} was found.");
|
||||
return 1;
|
||||
|
|
|
@ -1,6 +1,8 @@
|
|||
using DotNetDevices.Android;
|
||||
using Microsoft.Extensions.Logging;
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Linq;
|
||||
using System.Threading;
|
||||
using System.Threading.Tasks;
|
||||
|
||||
|
@ -45,193 +47,198 @@ namespace DotNetDevices.Commands
|
|||
|
||||
logger.LogInformation($"Running tests on '{packageName}'...");
|
||||
|
||||
//// validate requested OS
|
||||
//var simulatorType = ParseSimulatorType(deviceType);
|
||||
//var runtime = ParseSimulatorRuntime(runtimeString);
|
||||
//var runtimeVersion = await ParseVersionAsync(versionString, runtime, cancellationToken);
|
||||
// validate requested OS
|
||||
var avdRuntime = ParseDeviceRuntime(runtimeString);
|
||||
var avdTypes = ParseDeviceTypes(deviceType, avdRuntime);
|
||||
var avdApiLevel = ParseApiLevel(versionString);
|
||||
if (avdApiLevel == 0)
|
||||
latest = true;
|
||||
|
||||
//logger.LogInformation($"Looking for an available {simulatorType} ({runtimeVersion}) simulator...");
|
||||
//var available = await GetAvailableSimulatorsAsync(simulatorType, runtime, runtimeVersion, latest, cancellationToken);
|
||||
logger.LogInformation($"Looking for an available {string.Join("|", avdTypes)}{(avdApiLevel == 0 ? "" : $" (API {avdApiLevel})")} virtual device...");
|
||||
var available = await GetAvailableDevicesAsync(deviceName, avdTypes, avdApiLevel, latest, cancellationToken);
|
||||
|
||||
//// first look for a booted device
|
||||
//var simulator = available.FirstOrDefault(s => s.State == SimulatorState.Booted) ?? available.FirstOrDefault();
|
||||
//logger.LogInformation($"Using simulator {simulator.Name} ({simulator.Runtime} {simulator.Version}): {simulator.Udid}");
|
||||
// get the first device
|
||||
var avd = available.FirstOrDefault();
|
||||
logger.LogInformation($"Using virtual device {avd.Name} ({avd.Runtime} {avd.Version}): {avd.Id}");
|
||||
|
||||
//try
|
||||
//{
|
||||
// if (reset)
|
||||
// await simctl.EraseSimulatorAsync(simulator.Udid, true, cancellationToken);
|
||||
try
|
||||
{
|
||||
// if (reset)
|
||||
// await simctl.EraseSimulatorAsync(simulator.Udid, true, cancellationToken);
|
||||
|
||||
// await simctl.InstallAppAsync(simulator.Udid, app, true, cancellationToken);
|
||||
// await simctl.InstallAppAsync(simulator.Udid, app, true, cancellationToken);
|
||||
|
||||
// try
|
||||
// {
|
||||
// var parser = new TestResultsParser();
|
||||
try
|
||||
{
|
||||
// var parser = new TestResultsParser();
|
||||
|
||||
// var cts = CancellationTokenSource.CreateLinkedTokenSource(cancellationToken);
|
||||
// var cts = CancellationTokenSource.CreateLinkedTokenSource(cancellationToken);
|
||||
|
||||
// var launched = await simctl.LaunchAppAsync(simulator.Udid, bundleId, new LaunchAppOptions
|
||||
// {
|
||||
// CaptureOutput = true,
|
||||
// BootSimulator = true,
|
||||
// HandleOutput = output =>
|
||||
// {
|
||||
// parser.ParseTestOutput(
|
||||
// output,
|
||||
// line => logger?.LogWarning(line),
|
||||
// async () =>
|
||||
// {
|
||||
// try
|
||||
// {
|
||||
// // wait a few seconds before terminating
|
||||
// await Task.Delay(1000, cts.Token);
|
||||
// var launched = await simctl.LaunchAppAsync(simulator.Udid, bundleId, new LaunchAppOptions
|
||||
// {
|
||||
// CaptureOutput = true,
|
||||
// BootSimulator = true,
|
||||
// HandleOutput = output =>
|
||||
// {
|
||||
// parser.ParseTestOutput(
|
||||
// output,
|
||||
// line => logger?.LogWarning(line),
|
||||
// async () =>
|
||||
// {
|
||||
// try
|
||||
// {
|
||||
// // wait a few seconds before terminating
|
||||
// await Task.Delay(1000, cts.Token);
|
||||
|
||||
// await simctl.TerminateAppAsync(simulator.Udid, bundleId, cts.Token);
|
||||
// }
|
||||
// catch (OperationCanceledException)
|
||||
// {
|
||||
// // we expected this
|
||||
// }
|
||||
// });
|
||||
// },
|
||||
// }, cancellationToken);
|
||||
// await simctl.TerminateAppAsync(simulator.Udid, bundleId, cts.Token);
|
||||
// }
|
||||
// catch (OperationCanceledException)
|
||||
// {
|
||||
// // we expected this
|
||||
// }
|
||||
// });
|
||||
// },
|
||||
// }, cancellationToken);
|
||||
|
||||
// cts.Cancel();
|
||||
// cts.Cancel();
|
||||
|
||||
// if (deviceResults != null)
|
||||
// {
|
||||
// var dest = outputResults ?? Path.GetFileName(deviceResults);
|
||||
// if (deviceResults != null)
|
||||
// {
|
||||
// var dest = outputResults ?? Path.GetFileName(deviceResults);
|
||||
|
||||
// logger.LogInformation($"Copying test results from simulator to {dest}...");
|
||||
// logger.LogInformation($"Copying test results from simulator to {dest}...");
|
||||
|
||||
// var dataPath = await simctl.GetDataDirectoryAsync(simulator.Udid, bundleId, cancellationToken);
|
||||
// var results = Path.Combine(dataPath, "Documents", deviceResults);
|
||||
// if (File.Exists(results))
|
||||
// File.Copy(results, dest, true);
|
||||
// else
|
||||
// logger.LogInformation($"No test results found.");
|
||||
// }
|
||||
// else
|
||||
// {
|
||||
// logger.LogInformation($"Unable to determine the test results file.");
|
||||
// }
|
||||
// }
|
||||
// finally
|
||||
// {
|
||||
// await simctl.UninstallAppAsync(simulator.Udid, bundleId, false, cancellationToken);
|
||||
// }
|
||||
//}
|
||||
//finally
|
||||
//{
|
||||
// if (shutdown)
|
||||
// await simctl.ShutdownSimulatorAsync(simulator.Udid, cancellationToken);
|
||||
//}
|
||||
// var dataPath = await simctl.GetDataDirectoryAsync(simulator.Udid, bundleId, cancellationToken);
|
||||
// var results = Path.Combine(dataPath, "Documents", deviceResults);
|
||||
// if (File.Exists(results))
|
||||
// File.Copy(results, dest, true);
|
||||
// else
|
||||
// logger.LogInformation($"No test results found.");
|
||||
// }
|
||||
// else
|
||||
// {
|
||||
// logger.LogInformation($"Unable to determine the test results file.");
|
||||
// }
|
||||
}
|
||||
finally
|
||||
{
|
||||
// await simctl.UninstallAppAsync(simulator.Udid, bundleId, false, cancellationToken);
|
||||
}
|
||||
}
|
||||
finally
|
||||
{
|
||||
// if (shutdown)
|
||||
// await simctl.ShutdownSimulatorAsync(simulator.Udid, cancellationToken);
|
||||
}
|
||||
}
|
||||
|
||||
//private async Task<List<Simulator>> GetAvailableSimulatorsAsync(SimulatorType type, SimulatorRuntime runtime, Version version, bool useLatest = true, CancellationToken cancellationToken = default)
|
||||
//{
|
||||
// // load all simulators
|
||||
// var simulators = await simctl.GetSimulatorsAsync(cancellationToken);
|
||||
private async Task<List<VirtualDevice>> GetAvailableDevicesAsync(string? deviceName, VirtualDeviceType[] types, int apiLevel, bool useLatest = true, CancellationToken cancellationToken = default)
|
||||
{
|
||||
// load all virtual devices
|
||||
var avds = await avdmanager.GetVirtualDevicesAsync(cancellationToken);
|
||||
|
||||
// // find ones that can be used
|
||||
// var available = simulators
|
||||
// .Where(s => s.Availability == SimulatorAvailability.Available)
|
||||
// .Where(s => s.Runtime == runtime)
|
||||
// .Where(s => s.Type == type);
|
||||
// logger.LogDebug($"Found some available simulators:");
|
||||
// foreach (var sim in available)
|
||||
// {
|
||||
// logger.LogDebug($" {sim.Name} ({sim.Runtime} {sim.Version}): {sim.Udid}");
|
||||
// }
|
||||
// use the name directly
|
||||
if (!string.IsNullOrEmpty(deviceName))
|
||||
return avds.Where(d => d.Id == deviceName || d.Name == deviceName).ToList();
|
||||
|
||||
// // filter by version info
|
||||
// string matchingPattern;
|
||||
// if (useLatest)
|
||||
// {
|
||||
// var min = version;
|
||||
// var max = new Version(min.Major + 1, 0);
|
||||
// available = available.Where(s => s.Version >= min && s.Version < max);
|
||||
// matchingPattern = $"[{min}, {max})";
|
||||
// }
|
||||
// else
|
||||
// {
|
||||
// available = available.Where(s => s.Version == version);
|
||||
// matchingPattern = $"[{version}]";
|
||||
// }
|
||||
// find ones that can be used
|
||||
var available = avds
|
||||
.Where(s => types.Contains(s.Type));
|
||||
logger.LogDebug($"Found some available virtual devices:");
|
||||
foreach (var avd in available)
|
||||
{
|
||||
logger.LogDebug($" {avd.Name} ({avd.Runtime} API {avd.ApiLevel}): {avd.Id}");
|
||||
}
|
||||
|
||||
// var matching = available.ToList();
|
||||
// if (matching.Count > 0)
|
||||
// {
|
||||
// logger.LogDebug($"Found matching simulators {matchingPattern}:");
|
||||
// foreach (var sim in matching)
|
||||
// {
|
||||
// logger.LogDebug($" {sim.Name} ({sim.Runtime} {sim.Version}): {sim.Udid}");
|
||||
// }
|
||||
// }
|
||||
// else
|
||||
// {
|
||||
// throw new Exception($"Unable to find any simulators that match version {matchingPattern}.");
|
||||
// }
|
||||
// filter by version info
|
||||
string matchingPattern;
|
||||
if (useLatest)
|
||||
{
|
||||
var max = available.Where(d => d.ApiLevel >= apiLevel).Max(d => d.ApiLevel);
|
||||
available = available.Where(d => d.ApiLevel == max);
|
||||
matchingPattern = apiLevel > 0 ? $"[{apiLevel})" : $"[{max}]";
|
||||
}
|
||||
else
|
||||
{
|
||||
available = available.Where(d => d.ApiLevel == apiLevel);
|
||||
matchingPattern = $"[{apiLevel}]";
|
||||
}
|
||||
|
||||
// return matching;
|
||||
//}
|
||||
var matching = available.ToList();
|
||||
if (matching.Count == 0)
|
||||
throw new Exception($"Unable to find any virtual devices that match version {matchingPattern}.");
|
||||
|
||||
//private async Task<Version> ParseVersionAsync(string? version, SimulatorRuntime os, CancellationToken cancellationToken = default)
|
||||
//{
|
||||
// var osVersion = version?.ToLowerInvariant().Trim();
|
||||
// if (!Version.TryParse(osVersion, out var numberVersion))
|
||||
// {
|
||||
// if (int.TryParse(osVersion, out var v))
|
||||
// numberVersion = new Version(v, 0);
|
||||
// else if (string.IsNullOrEmpty(osVersion) || osVersion == "default")
|
||||
// numberVersion = await simctl.GetDefaultVersionAsync(os, cancellationToken);
|
||||
// else
|
||||
// throw new Exception($"Unable to determine the version for {osVersion}.");
|
||||
// }
|
||||
logger.LogDebug($"Found matching virtual devices {matchingPattern}:");
|
||||
foreach (var avd in matching)
|
||||
{
|
||||
logger.LogDebug($" {avd.Name} ({avd.Runtime} API {avd.ApiLevel}): {avd.Id}");
|
||||
}
|
||||
|
||||
// return numberVersion;
|
||||
//}
|
||||
return matching;
|
||||
}
|
||||
|
||||
//private static SimulatorRuntime ParseSimulatorRuntime(string? runtime)
|
||||
//{
|
||||
// var osName = runtime?.ToLowerInvariant()?.Trim();
|
||||
// var os = osName switch
|
||||
// {
|
||||
// null => SimulatorRuntime.iOS,
|
||||
// "" => SimulatorRuntime.iOS,
|
||||
// "ios" => SimulatorRuntime.iOS,
|
||||
// "watchos" => SimulatorRuntime.watchOS,
|
||||
// "tvos" => SimulatorRuntime.tvOS,
|
||||
// _ => throw new Exception($"Unable to determine the OS for {runtime}.")
|
||||
// };
|
||||
// return os;
|
||||
//}
|
||||
private int ParseApiLevel(string? version)
|
||||
{
|
||||
var osVersion = version?.ToLowerInvariant().Trim();
|
||||
|
||||
//private static SimulatorType ParseSimulatorType(string? deviceType)
|
||||
//{
|
||||
// var deviceTypeName = deviceType?.ToLowerInvariant()?.Trim();
|
||||
// var device = deviceTypeName switch
|
||||
// {
|
||||
// // iPhone
|
||||
// null => SimulatorType.iPhone,
|
||||
// "" => SimulatorType.iPhone,
|
||||
// "iphone" => SimulatorType.iPhone,
|
||||
// "phone" => SimulatorType.iPhone,
|
||||
// // iPad
|
||||
// "ipad" => SimulatorType.iPad,
|
||||
// "tablet" => SimulatorType.iPad,
|
||||
// // iPod
|
||||
// "ipod" => SimulatorType.iPod,
|
||||
// // Apple TV
|
||||
// "tv" => SimulatorType.AppleTV,
|
||||
// "appletv" => SimulatorType.AppleTV,
|
||||
// // Apple Watch
|
||||
// "watch" => SimulatorType.AppleWatch,
|
||||
// "applewatch" => SimulatorType.AppleWatch,
|
||||
// //
|
||||
// _ => throw new Exception($"Unable to determine the simulator type for {deviceType}.")
|
||||
// };
|
||||
// return device;
|
||||
//}
|
||||
if (string.IsNullOrEmpty(osVersion))
|
||||
return 0;
|
||||
|
||||
if (!int.TryParse(osVersion, out var numberVersion))
|
||||
throw new Exception($"Unable to determine the version for {osVersion}.");
|
||||
|
||||
return numberVersion;
|
||||
}
|
||||
|
||||
private static VirtualDeviceRuntime ParseDeviceRuntime(string? runtime)
|
||||
{
|
||||
var osName = runtime?.ToLowerInvariant()?.Trim();
|
||||
var os = osName switch
|
||||
{
|
||||
null => VirtualDeviceRuntime.Android,
|
||||
"" => VirtualDeviceRuntime.Android,
|
||||
"android" => VirtualDeviceRuntime.Android,
|
||||
"watch" => VirtualDeviceRuntime.AndroidWear,
|
||||
"wear" => VirtualDeviceRuntime.AndroidWear,
|
||||
"androidwear" => VirtualDeviceRuntime.AndroidWear,
|
||||
"wearable" => VirtualDeviceRuntime.AndroidWear,
|
||||
"tv" => VirtualDeviceRuntime.AndroidTV,
|
||||
"androidtv" => VirtualDeviceRuntime.AndroidTV,
|
||||
_ => throw new Exception($"Unable to determine the OS for {runtime}.")
|
||||
};
|
||||
return os;
|
||||
}
|
||||
|
||||
private static VirtualDeviceType[] ParseDeviceTypes(string? deviceType, VirtualDeviceRuntime runtime)
|
||||
{
|
||||
var fallback = runtime switch
|
||||
{
|
||||
VirtualDeviceRuntime.Android => new[] { VirtualDeviceType.Phone, VirtualDeviceType.Tablet },
|
||||
VirtualDeviceRuntime.AndroidWear => new[] { VirtualDeviceType.Wearable },
|
||||
VirtualDeviceRuntime.AndroidTV => new[] { VirtualDeviceType.TV },
|
||||
_ => new[] { VirtualDeviceType.Phone | VirtualDeviceType.Tablet },
|
||||
};
|
||||
|
||||
var deviceTypeName = deviceType?.ToLowerInvariant()?.Trim();
|
||||
var device = deviceTypeName switch
|
||||
{
|
||||
// phone
|
||||
null => fallback,
|
||||
"" => fallback,
|
||||
"phone" => new[] { VirtualDeviceType.Phone },
|
||||
// tablet
|
||||
"tab" => new[] { VirtualDeviceType.Tablet },
|
||||
"tablet" => new[] { VirtualDeviceType.Tablet },
|
||||
// TV
|
||||
"tv" => new[] { VirtualDeviceType.TV },
|
||||
// Wear
|
||||
"watch" => new[] { VirtualDeviceType.Wearable },
|
||||
"wear" => new[] { VirtualDeviceType.Wearable },
|
||||
"wearable" => new[] { VirtualDeviceType.Wearable },
|
||||
//
|
||||
_ => throw new Exception($"Unable to determine the virtual device type for {deviceType}.")
|
||||
};
|
||||
return device;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
@ -1,7 +1,6 @@
|
|||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Text;
|
||||
using DotNetDevices.Android;
|
||||
|
||||
namespace DotNetDevices.Processes
|
||||
{
|
||||
|
@ -43,6 +42,13 @@ namespace DotNetDevices.Processes
|
|||
yield return item.Data;
|
||||
}
|
||||
|
||||
public IEnumerable<string> GetErrorOutput()
|
||||
{
|
||||
foreach (var item in outputItems)
|
||||
if (item.IsError)
|
||||
yield return item.Data;
|
||||
}
|
||||
|
||||
public override string ToString() =>
|
||||
$"Completed with exit code {ExitCode} in {Elapsed}.";
|
||||
|
||||
|
|
Загрузка…
Ссылка в новой задаче