Start in the andorid tests
This commit is contained in:
Родитель
405864895b
Коммит
c0e5600a11
|
@ -1,4 +1,5 @@
|
||||||
using DotNetDevices.Android;
|
using DotNetDevices.Android;
|
||||||
|
using System;
|
||||||
using System.Threading.Tasks;
|
using System.Threading.Tasks;
|
||||||
using Xunit;
|
using Xunit;
|
||||||
|
|
||||||
|
@ -21,6 +22,71 @@ namespace DotNetDevices.Tests
|
||||||
Assert.Equal(id, device.Id);
|
Assert.Equal(id, device.Id);
|
||||||
Assert.Equal(name, device.Name);
|
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;
|
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)
|
public async Task<IEnumerable<VirtualDevice>> GetVirtualDevicesAsync(CancellationToken cancellationToken = default)
|
||||||
{
|
{
|
||||||
logger?.LogInformation("Retrieving all the virtual devices...");
|
logger?.LogInformation("Retrieving all the virtual devices...");
|
||||||
|
@ -94,7 +122,27 @@ namespace DotNetDevices.Android
|
||||||
|
|
||||||
var args = $"delete avd --name \"{name}\"";
|
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)
|
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)
|
if (options?.Overwrite == true)
|
||||||
args += " --force";
|
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)
|
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...");
|
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 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())
|
foreach (var output in result.GetOutput())
|
||||||
{
|
{
|
||||||
avd.Add(new VirtualDevice(output, output));
|
avd.Add(output.Trim());
|
||||||
}
|
}
|
||||||
return avd;
|
return avd;
|
||||||
}
|
}
|
||||||
|
|
|
@ -4,21 +4,76 @@ namespace DotNetDevices.Android
|
||||||
{
|
{
|
||||||
public class VirtualDevice
|
public class VirtualDevice
|
||||||
{
|
{
|
||||||
private readonly string? configPath;
|
public VirtualDevice(string id, string name, string package, VirtualDeviceType type, int apiLevel, string? configPath)
|
||||||
|
|
||||||
public VirtualDevice(string id, string name, string? configPath = null)
|
|
||||||
{
|
{
|
||||||
Id = id ?? throw new ArgumentNullException(nameof(id));
|
Id = id ?? throw new ArgumentNullException(nameof(id));
|
||||||
Name = name ?? throw new ArgumentNullException(nameof(name));
|
Name = name ?? throw new ArgumentNullException(nameof(name));
|
||||||
|
Package = package ?? throw new ArgumentNullException(nameof(package));
|
||||||
this.configPath = configPath;
|
Type = type;
|
||||||
|
ApiLevel = apiLevel;
|
||||||
|
ConfigPath = configPath;
|
||||||
}
|
}
|
||||||
|
|
||||||
public string Id { get; }
|
public string Id { get; }
|
||||||
|
|
||||||
public string Name { 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() =>
|
public override string ToString() =>
|
||||||
$"{Name}";
|
$"{Name} (API {ApiLevel})";
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -2,6 +2,7 @@
|
||||||
using System;
|
using System;
|
||||||
using System.Collections.Generic;
|
using System.Collections.Generic;
|
||||||
using System.IO;
|
using System.IO;
|
||||||
|
using System.Text.RegularExpressions;
|
||||||
using System.Threading;
|
using System.Threading;
|
||||||
using System.Threading.Tasks;
|
using System.Threading.Tasks;
|
||||||
|
|
||||||
|
@ -9,6 +10,8 @@ namespace DotNetDevices.Android
|
||||||
{
|
{
|
||||||
public class VirtualDeviceConfig
|
public class VirtualDeviceConfig
|
||||||
{
|
{
|
||||||
|
private static readonly Regex androidApiRegex = new Regex(@"android-(\d+)");
|
||||||
|
|
||||||
private readonly string configPath;
|
private readonly string configPath;
|
||||||
private readonly ILogger? logger;
|
private readonly ILogger? logger;
|
||||||
|
|
||||||
|
@ -47,12 +50,80 @@ namespace DotNetDevices.Android
|
||||||
var props = await GetPropertiesAsync(cancellationToken).ConfigureAwait(false);
|
var props = await GetPropertiesAsync(cancellationToken).ConfigureAwait(false);
|
||||||
|
|
||||||
if (!props.TryGetValue("avdid", out var id))
|
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.");
|
throw new Exception($"Invalid config.ini. Unable to find the virtual device ID.");
|
||||||
|
|
||||||
if (!props.TryGetValue("avd.ini.displayname", out var name))
|
if (!props.TryGetValue("avd.ini.displayname", out var name))
|
||||||
name = id;
|
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)
|
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?>("NAME", "The name of the new virtual device."),
|
||||||
new Argument<string?>("PACKAGE", "The package to use for 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))!)),
|
}.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 Command("boot", "Boot a particular virtual device.")
|
||||||
{
|
{
|
||||||
new Option<string?>(new[] { "--sdk" }, "The path to the Android SDK directory."),
|
new Option<string?>(new[] { "--sdk" }, "The path to the Android SDK directory."),
|
||||||
|
@ -60,31 +66,6 @@ namespace DotNetDevices.Commands
|
||||||
|
|
||||||
var filtered = (IEnumerable<VirtualDevice>)devices;
|
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();
|
//term = term?.ToLowerInvariant()?.Trim();
|
||||||
|
|
||||||
//var filtered = (IEnumerable<Simulator>)simulators;
|
//var filtered = (IEnumerable<Simulator>)simulators;
|
||||||
|
@ -128,6 +109,10 @@ namespace DotNetDevices.Commands
|
||||||
var table = new TableView<VirtualDevice>();
|
var table = new TableView<VirtualDevice>();
|
||||||
table.AddColumn(s => s.Id, "Id");
|
table.AddColumn(s => s.Id, "Id");
|
||||||
table.AddColumn(s => s.Name, "Name");
|
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;
|
table.Items = all;
|
||||||
|
|
||||||
console.Append(new StackLayoutView { table });
|
console.Append(new StackLayoutView { table });
|
||||||
|
@ -146,14 +131,45 @@ namespace DotNetDevices.Commands
|
||||||
|
|
||||||
var avdmanager = new AVDManager(sdk, logger);
|
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
|
var options = new CreateVirtualDeviceOptions
|
||||||
{
|
{
|
||||||
Overwrite = replace
|
Overwrite = replace,
|
||||||
};
|
};
|
||||||
|
|
||||||
await avdmanager.CreateVirtualDeviceAsync(name, package, options, cancellationToken);
|
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(
|
public static async Task<int> HandleBootAsync(
|
||||||
string name,
|
string name,
|
||||||
string? sdk = null,
|
string? sdk = null,
|
||||||
|
@ -166,7 +182,7 @@ namespace DotNetDevices.Commands
|
||||||
var emulator = new EmulatorManager(sdk, logger);
|
var emulator = new EmulatorManager(sdk, logger);
|
||||||
|
|
||||||
var avds = await emulator.GetVirtualDevicesAsync(cancellationToken);
|
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.");
|
logger.LogError($"No virtual device with name {name} was found.");
|
||||||
return 1;
|
return 1;
|
||||||
|
|
|
@ -1,6 +1,8 @@
|
||||||
using DotNetDevices.Android;
|
using DotNetDevices.Android;
|
||||||
using Microsoft.Extensions.Logging;
|
using Microsoft.Extensions.Logging;
|
||||||
using System;
|
using System;
|
||||||
|
using System.Collections.Generic;
|
||||||
|
using System.Linq;
|
||||||
using System.Threading;
|
using System.Threading;
|
||||||
using System.Threading.Tasks;
|
using System.Threading.Tasks;
|
||||||
|
|
||||||
|
@ -45,193 +47,198 @@ namespace DotNetDevices.Commands
|
||||||
|
|
||||||
logger.LogInformation($"Running tests on '{packageName}'...");
|
logger.LogInformation($"Running tests on '{packageName}'...");
|
||||||
|
|
||||||
//// validate requested OS
|
// validate requested OS
|
||||||
//var simulatorType = ParseSimulatorType(deviceType);
|
var avdRuntime = ParseDeviceRuntime(runtimeString);
|
||||||
//var runtime = ParseSimulatorRuntime(runtimeString);
|
var avdTypes = ParseDeviceTypes(deviceType, avdRuntime);
|
||||||
//var runtimeVersion = await ParseVersionAsync(versionString, runtime, cancellationToken);
|
var avdApiLevel = ParseApiLevel(versionString);
|
||||||
|
if (avdApiLevel == 0)
|
||||||
|
latest = true;
|
||||||
|
|
||||||
//logger.LogInformation($"Looking for an available {simulatorType} ({runtimeVersion}) simulator...");
|
logger.LogInformation($"Looking for an available {string.Join("|", avdTypes)}{(avdApiLevel == 0 ? "" : $" (API {avdApiLevel})")} virtual device...");
|
||||||
//var available = await GetAvailableSimulatorsAsync(simulatorType, runtime, runtimeVersion, latest, cancellationToken);
|
var available = await GetAvailableDevicesAsync(deviceName, avdTypes, avdApiLevel, latest, cancellationToken);
|
||||||
|
|
||||||
//// first look for a booted device
|
// get the first device
|
||||||
//var simulator = available.FirstOrDefault(s => s.State == SimulatorState.Booted) ?? available.FirstOrDefault();
|
var avd = available.FirstOrDefault();
|
||||||
//logger.LogInformation($"Using simulator {simulator.Name} ({simulator.Runtime} {simulator.Version}): {simulator.Udid}");
|
logger.LogInformation($"Using virtual device {avd.Name} ({avd.Runtime} {avd.Version}): {avd.Id}");
|
||||||
|
|
||||||
//try
|
try
|
||||||
//{
|
{
|
||||||
// if (reset)
|
// if (reset)
|
||||||
// await simctl.EraseSimulatorAsync(simulator.Udid, true, cancellationToken);
|
// await simctl.EraseSimulatorAsync(simulator.Udid, true, cancellationToken);
|
||||||
|
|
||||||
// await simctl.InstallAppAsync(simulator.Udid, app, true, cancellationToken);
|
// await simctl.InstallAppAsync(simulator.Udid, app, true, cancellationToken);
|
||||||
|
|
||||||
// try
|
try
|
||||||
// {
|
{
|
||||||
// var parser = new TestResultsParser();
|
// var parser = new TestResultsParser();
|
||||||
|
|
||||||
// var cts = CancellationTokenSource.CreateLinkedTokenSource(cancellationToken);
|
// var cts = CancellationTokenSource.CreateLinkedTokenSource(cancellationToken);
|
||||||
|
|
||||||
// var launched = await simctl.LaunchAppAsync(simulator.Udid, bundleId, new LaunchAppOptions
|
// var launched = await simctl.LaunchAppAsync(simulator.Udid, bundleId, new LaunchAppOptions
|
||||||
// {
|
// {
|
||||||
// CaptureOutput = true,
|
// CaptureOutput = true,
|
||||||
// BootSimulator = true,
|
// BootSimulator = true,
|
||||||
// HandleOutput = output =>
|
// HandleOutput = output =>
|
||||||
// {
|
// {
|
||||||
// parser.ParseTestOutput(
|
// parser.ParseTestOutput(
|
||||||
// output,
|
// output,
|
||||||
// line => logger?.LogWarning(line),
|
// line => logger?.LogWarning(line),
|
||||||
// async () =>
|
// async () =>
|
||||||
// {
|
// {
|
||||||
// try
|
// try
|
||||||
// {
|
// {
|
||||||
// // wait a few seconds before terminating
|
// // wait a few seconds before terminating
|
||||||
// await Task.Delay(1000, cts.Token);
|
// await Task.Delay(1000, cts.Token);
|
||||||
|
|
||||||
// await simctl.TerminateAppAsync(simulator.Udid, bundleId, cts.Token);
|
// await simctl.TerminateAppAsync(simulator.Udid, bundleId, cts.Token);
|
||||||
// }
|
// }
|
||||||
// catch (OperationCanceledException)
|
// catch (OperationCanceledException)
|
||||||
// {
|
// {
|
||||||
// // we expected this
|
// // we expected this
|
||||||
// }
|
// }
|
||||||
// });
|
// });
|
||||||
// },
|
// },
|
||||||
// }, cancellationToken);
|
// }, cancellationToken);
|
||||||
|
|
||||||
// cts.Cancel();
|
// cts.Cancel();
|
||||||
|
|
||||||
// if (deviceResults != null)
|
// if (deviceResults != null)
|
||||||
// {
|
// {
|
||||||
// var dest = outputResults ?? Path.GetFileName(deviceResults);
|
// 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 dataPath = await simctl.GetDataDirectoryAsync(simulator.Udid, bundleId, cancellationToken);
|
||||||
// var results = Path.Combine(dataPath, "Documents", deviceResults);
|
// var results = Path.Combine(dataPath, "Documents", deviceResults);
|
||||||
// if (File.Exists(results))
|
// if (File.Exists(results))
|
||||||
// File.Copy(results, dest, true);
|
// File.Copy(results, dest, true);
|
||||||
// else
|
// else
|
||||||
// logger.LogInformation($"No test results found.");
|
// logger.LogInformation($"No test results found.");
|
||||||
// }
|
// }
|
||||||
// else
|
// else
|
||||||
// {
|
// {
|
||||||
// logger.LogInformation($"Unable to determine the test results file.");
|
// logger.LogInformation($"Unable to determine the test results file.");
|
||||||
// }
|
// }
|
||||||
// }
|
}
|
||||||
// finally
|
finally
|
||||||
// {
|
{
|
||||||
// await simctl.UninstallAppAsync(simulator.Udid, bundleId, false, cancellationToken);
|
// await simctl.UninstallAppAsync(simulator.Udid, bundleId, false, cancellationToken);
|
||||||
// }
|
}
|
||||||
//}
|
}
|
||||||
//finally
|
finally
|
||||||
//{
|
{
|
||||||
// if (shutdown)
|
// if (shutdown)
|
||||||
// await simctl.ShutdownSimulatorAsync(simulator.Udid, cancellationToken);
|
// await simctl.ShutdownSimulatorAsync(simulator.Udid, cancellationToken);
|
||||||
//}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
//private async Task<List<Simulator>> GetAvailableSimulatorsAsync(SimulatorType type, SimulatorRuntime runtime, Version version, bool useLatest = true, CancellationToken cancellationToken = default)
|
private async Task<List<VirtualDevice>> GetAvailableDevicesAsync(string? deviceName, VirtualDeviceType[] types, int apiLevel, bool useLatest = true, CancellationToken cancellationToken = default)
|
||||||
//{
|
{
|
||||||
// // load all simulators
|
// load all virtual devices
|
||||||
// var simulators = await simctl.GetSimulatorsAsync(cancellationToken);
|
var avds = await avdmanager.GetVirtualDevicesAsync(cancellationToken);
|
||||||
|
|
||||||
// // find ones that can be used
|
// use the name directly
|
||||||
// var available = simulators
|
if (!string.IsNullOrEmpty(deviceName))
|
||||||
// .Where(s => s.Availability == SimulatorAvailability.Available)
|
return avds.Where(d => d.Id == deviceName || d.Name == deviceName).ToList();
|
||||||
// .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}");
|
|
||||||
// }
|
|
||||||
|
|
||||||
// // filter by version info
|
// find ones that can be used
|
||||||
// string matchingPattern;
|
var available = avds
|
||||||
// if (useLatest)
|
.Where(s => types.Contains(s.Type));
|
||||||
// {
|
logger.LogDebug($"Found some available virtual devices:");
|
||||||
// var min = version;
|
foreach (var avd in available)
|
||||||
// var max = new Version(min.Major + 1, 0);
|
{
|
||||||
// available = available.Where(s => s.Version >= min && s.Version < max);
|
logger.LogDebug($" {avd.Name} ({avd.Runtime} API {avd.ApiLevel}): {avd.Id}");
|
||||||
// matchingPattern = $"[{min}, {max})";
|
}
|
||||||
// }
|
|
||||||
// else
|
|
||||||
// {
|
|
||||||
// available = available.Where(s => s.Version == version);
|
|
||||||
// matchingPattern = $"[{version}]";
|
|
||||||
// }
|
|
||||||
|
|
||||||
// var matching = available.ToList();
|
// filter by version info
|
||||||
// if (matching.Count > 0)
|
string matchingPattern;
|
||||||
// {
|
if (useLatest)
|
||||||
// logger.LogDebug($"Found matching simulators {matchingPattern}:");
|
{
|
||||||
// foreach (var sim in matching)
|
var max = available.Where(d => d.ApiLevel >= apiLevel).Max(d => d.ApiLevel);
|
||||||
// {
|
available = available.Where(d => d.ApiLevel == max);
|
||||||
// logger.LogDebug($" {sim.Name} ({sim.Runtime} {sim.Version}): {sim.Udid}");
|
matchingPattern = apiLevel > 0 ? $"[{apiLevel})" : $"[{max}]";
|
||||||
// }
|
}
|
||||||
// }
|
else
|
||||||
// else
|
{
|
||||||
// {
|
available = available.Where(d => d.ApiLevel == apiLevel);
|
||||||
// throw new Exception($"Unable to find any simulators that match version {matchingPattern}.");
|
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)
|
logger.LogDebug($"Found matching virtual devices {matchingPattern}:");
|
||||||
//{
|
foreach (var avd in matching)
|
||||||
// var osVersion = version?.ToLowerInvariant().Trim();
|
{
|
||||||
// if (!Version.TryParse(osVersion, out var numberVersion))
|
logger.LogDebug($" {avd.Name} ({avd.Runtime} API {avd.ApiLevel}): {avd.Id}");
|
||||||
// {
|
}
|
||||||
// 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}.");
|
|
||||||
// }
|
|
||||||
|
|
||||||
// return numberVersion;
|
return matching;
|
||||||
//}
|
}
|
||||||
|
|
||||||
//private static SimulatorRuntime ParseSimulatorRuntime(string? runtime)
|
private int ParseApiLevel(string? version)
|
||||||
//{
|
{
|
||||||
// var osName = runtime?.ToLowerInvariant()?.Trim();
|
var osVersion = version?.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 static SimulatorType ParseSimulatorType(string? deviceType)
|
if (string.IsNullOrEmpty(osVersion))
|
||||||
//{
|
return 0;
|
||||||
// var deviceTypeName = deviceType?.ToLowerInvariant()?.Trim();
|
|
||||||
// var device = deviceTypeName switch
|
if (!int.TryParse(osVersion, out var numberVersion))
|
||||||
// {
|
throw new Exception($"Unable to determine the version for {osVersion}.");
|
||||||
// // iPhone
|
|
||||||
// null => SimulatorType.iPhone,
|
return numberVersion;
|
||||||
// "" => SimulatorType.iPhone,
|
}
|
||||||
// "iphone" => SimulatorType.iPhone,
|
|
||||||
// "phone" => SimulatorType.iPhone,
|
private static VirtualDeviceRuntime ParseDeviceRuntime(string? runtime)
|
||||||
// // iPad
|
{
|
||||||
// "ipad" => SimulatorType.iPad,
|
var osName = runtime?.ToLowerInvariant()?.Trim();
|
||||||
// "tablet" => SimulatorType.iPad,
|
var os = osName switch
|
||||||
// // iPod
|
{
|
||||||
// "ipod" => SimulatorType.iPod,
|
null => VirtualDeviceRuntime.Android,
|
||||||
// // Apple TV
|
"" => VirtualDeviceRuntime.Android,
|
||||||
// "tv" => SimulatorType.AppleTV,
|
"android" => VirtualDeviceRuntime.Android,
|
||||||
// "appletv" => SimulatorType.AppleTV,
|
"watch" => VirtualDeviceRuntime.AndroidWear,
|
||||||
// // Apple Watch
|
"wear" => VirtualDeviceRuntime.AndroidWear,
|
||||||
// "watch" => SimulatorType.AppleWatch,
|
"androidwear" => VirtualDeviceRuntime.AndroidWear,
|
||||||
// "applewatch" => SimulatorType.AppleWatch,
|
"wearable" => VirtualDeviceRuntime.AndroidWear,
|
||||||
// //
|
"tv" => VirtualDeviceRuntime.AndroidTV,
|
||||||
// _ => throw new Exception($"Unable to determine the simulator type for {deviceType}.")
|
"androidtv" => VirtualDeviceRuntime.AndroidTV,
|
||||||
// };
|
_ => throw new Exception($"Unable to determine the OS for {runtime}.")
|
||||||
// return device;
|
};
|
||||||
//}
|
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;
|
||||||
using System.Collections.Generic;
|
using System.Collections.Generic;
|
||||||
using System.Text;
|
using System.Text;
|
||||||
using DotNetDevices.Android;
|
|
||||||
|
|
||||||
namespace DotNetDevices.Processes
|
namespace DotNetDevices.Processes
|
||||||
{
|
{
|
||||||
|
@ -43,6 +42,13 @@ namespace DotNetDevices.Processes
|
||||||
yield return item.Data;
|
yield return item.Data;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public IEnumerable<string> GetErrorOutput()
|
||||||
|
{
|
||||||
|
foreach (var item in outputItems)
|
||||||
|
if (item.IsError)
|
||||||
|
yield return item.Data;
|
||||||
|
}
|
||||||
|
|
||||||
public override string ToString() =>
|
public override string ToString() =>
|
||||||
$"Completed with exit code {ExitCode} in {Elapsed}.";
|
$"Completed with exit code {ExitCode} in {Elapsed}.";
|
||||||
|
|
||||||
|
|
Загрузка…
Ссылка в новой задаче