xamarin-macios/tests/xharness/Simulators.cs

298 строки
10 KiB
C#

using System;
using System.Collections.Generic;
using System.Diagnostics;
using System.IO;
using System.Linq;
using System.Threading.Tasks;
using System.Xml;
namespace xharness
{
public class Simulators
{
public Harness Harness;
public List<SimRuntime> SupportedRuntimes = new List<SimRuntime> ();
public List<SimDeviceType> SupportedDeviceTypes = new List<SimDeviceType> ();
public List<SimDevice> AvailableDevices = new List<SimDevice> ();
public List<SimDevicePair> AvailableDevicePairs = new List<SimDevicePair> ();
public async Task LoadAsync (Log log)
{
if (SupportedRuntimes.Count > 0)
return;
var tmpfile = Path.GetTempFileName ();
try {
using (var process = new Process ()) {
process.StartInfo.FileName = Harness.MlaunchPath;
process.StartInfo.Arguments = string.Format ("--sdkroot {0} --listsim {1}", Harness.XcodeRoot, tmpfile);
log.WriteLine ("Launching {0} {1}", process.StartInfo.FileName, process.StartInfo.Arguments);
var rv = await process.RunAsync (log, false);
if (!rv.Succeeded)
throw new Exception ("Failed to list simulators.");
log.WriteLine ("Result:");
log.WriteLine (File.ReadAllText (tmpfile));
var simulator_data = new XmlDocument ();
simulator_data.LoadWithoutNetworkAccess (tmpfile);
foreach (XmlNode sim in simulator_data.SelectNodes ("/MTouch/Simulator/SupportedRuntimes/SimRuntime")) {
SupportedRuntimes.Add (new SimRuntime ()
{
Name = sim.SelectSingleNode ("Name").InnerText,
Identifier = sim.SelectSingleNode ("Identifier").InnerText,
Version = long.Parse (sim.SelectSingleNode ("Version").InnerText),
});
}
foreach (XmlNode sim in simulator_data.SelectNodes ("/MTouch/Simulator/SupportedDeviceTypes/SimDeviceType")) {
SupportedDeviceTypes.Add (new SimDeviceType ()
{
Name = sim.SelectSingleNode ("Name").InnerText,
Identifier = sim.SelectSingleNode ("Identifier").InnerText,
ProductFamilyId = sim.SelectSingleNode ("ProductFamilyId").InnerText,
MinRuntimeVersion = long.Parse (sim.SelectSingleNode ("MinRuntimeVersion").InnerText),
MaxRuntimeVersion = long.Parse (sim.SelectSingleNode ("MaxRuntimeVersion").InnerText),
Supports64Bits = bool.Parse (sim.SelectSingleNode ("Supports64Bits").InnerText),
});
}
foreach (XmlNode sim in simulator_data.SelectNodes ("/MTouch/Simulator/AvailableDevices/SimDevice")) {
AvailableDevices.Add (new SimDevice ()
{
Harness = Harness,
Name = sim.Attributes ["Name"].Value,
UDID = sim.Attributes ["UDID"].Value,
SimRuntime = sim.SelectSingleNode ("SimRuntime").InnerText,
SimDeviceType = sim.SelectSingleNode ("SimDeviceType").InnerText,
DataPath = sim.SelectSingleNode ("DataPath").InnerText,
LogPath = sim.SelectSingleNode ("LogPath").InnerText,
});
}
foreach (XmlNode sim in simulator_data.SelectNodes ("/MTouch/Simulator/AvailableDevicePairs/SimDevicePair")) {
AvailableDevicePairs.Add (new SimDevicePair ()
{
UDID = sim.Attributes ["UDID"].Value,
Companion = sim.SelectSingleNode ("Companion").InnerText,
Gizmo = sim.SelectSingleNode ("Gizmo").InnerText,
});
}
}
} finally {
File.Delete (tmpfile);
}
}
}
public class SimRuntime
{
public string Name;
public string Identifier;
public long Version;
}
public class SimDeviceType
{
public string Name;
public string Identifier;
public string ProductFamilyId;
public long MinRuntimeVersion;
public long MaxRuntimeVersion;
public bool Supports64Bits;
}
public class SimDevice
{
public string UDID;
public string Name;
public string SimRuntime;
public string SimDeviceType;
public string DataPath;
public string LogPath;
public string SystemLog { get { return Path.Combine (LogPath, "system.log"); } }
public Harness Harness;
public bool IsWatchSimulator { get { return SimRuntime.StartsWith ("com.apple.CoreSimulator.SimRuntime.watchOS", StringComparison.Ordinal); } }
public async Task EraseAsync (Log log)
{
// here we don't care if execution fails.
// erase the simulator (make sure the device isn't running first)
await Harness.ExecuteXcodeCommandAsync ("simctl", "shutdown " + UDID, log, TimeSpan.FromMinutes (1));
await Harness.ExecuteXcodeCommandAsync ("simctl", "erase " + UDID, log, TimeSpan.FromMinutes (1));
// boot & shutdown to make sure it actually works
await Harness.ExecuteXcodeCommandAsync ("simctl", "boot " + UDID, log, TimeSpan.FromMinutes (1));
await Harness.ExecuteXcodeCommandAsync ("simctl", "shutdown " + UDID, log, TimeSpan.FromMinutes (1));
}
public async Task ShutdownAsync (Log log)
{
await Harness.ExecuteXcodeCommandAsync ("simctl", "shutdown " + UDID, log, TimeSpan.FromMinutes (1));
}
public static Task KillEverythingAsync (Log log)
{
var to_kill = new string [] { "iPhone Simulator", "iOS Simulator", "Simulator", "Simulator (Watch)", "com.apple.CoreSimulator.CoreSimulatorService" };
return ProcessHelper.ExecuteCommandAsync ("killall", "-9 " + string.Join (" ", to_kill.Select ((v) => Harness.Quote (v)).ToArray ()), log, TimeSpan.FromSeconds (10));
}
public async Task AgreeToPromptsAsync (Log log, params string[] bundle_identifiers)
{
if (bundle_identifiers == null || bundle_identifiers.Length == 0) {
log.WriteLine ("No bundle identifiers given when requested permission editing.");
return;
}
var TCC_db = Path.Combine (DataPath, "data", "Library", "TCC", "TCC.db");
var sim_services = new string [] {
"kTCCServiceAddressBook",
"kTCCServicePhotos",
"kTCCServiceMediaLibrary",
"kTCCServiceUbiquity",
"kTCCServiceWillow"
};
var failure = false;
var tcc_edit_timeout = 5;
var watch = new Stopwatch ();
watch.Start ();
do {
if (failure) {
log.WriteLine ("Failed to edit TCC.db, trying again in 1 second... ", (int) (tcc_edit_timeout - watch.Elapsed.TotalSeconds));
await Task.Delay (TimeSpan.FromSeconds (1));
}
failure = false;
foreach (var bundle_identifier in bundle_identifiers) {
var sql = new System.Text.StringBuilder ();
sql.Append (Harness.Quote (TCC_db));
sql.Append (" \"");
foreach (var service in sim_services) {
sql.AppendFormat ("INSERT INTO access VALUES('{0}','{1}',0,1,0,NULL,NULL);", service, bundle_identifier);
sql.AppendFormat ("INSERT INTO access VALUES('{0}','{1}',0,1,0,NULL,NULL);", service, bundle_identifier + ".watchkitapp");
}
sql.Append ("\"");
var rv = await ProcessHelper.ExecuteCommandAsync ("sqlite3", sql.ToString (), log, TimeSpan.FromSeconds (5));
if (!rv.Succeeded) {
failure = true;
break;
}
}
} while (failure && watch.Elapsed.TotalSeconds <= tcc_edit_timeout);
if (failure) {
log.WriteLine ("Failed to edit TCC.db, the test run might hang due to permission request dialogs");
} else {
log.WriteLine ("Successfully edited TCC.db");
}
}
async Task OpenSimulator (Log log)
{
string simulator_app;
if (IsWatchSimulator) {
simulator_app = Path.Combine (Harness.XcodeRoot, "Contents", "Developer", "Applications", "Simulator (Watch).app");
} else {
simulator_app = Path.Combine (Harness.XcodeRoot, "Contents", "Developer", "Applications", "Simulator.app");
if (!Directory.Exists (simulator_app))
simulator_app = Path.Combine (Harness.XcodeRoot, "Contents", "Developer", "Applications", "iOS Simulator.app");
}
await ProcessHelper.ExecuteCommandAsync ("open", "-a " + Harness.Quote (simulator_app) + " --args -CurrentDeviceUDID " + UDID, log, TimeSpan.FromSeconds (15));
}
public async Task PrepareSimulatorAsync (Log log, params string[] bundle_identifiers)
{
// Kill all existing processes
await KillEverythingAsync (log);
// We shutdown and erase all simulators.
await EraseAsync (log);
// Edit the permissions to prevent dialog boxes in the test app
var TCC_db = Path.Combine (DataPath, "data", "Library", "TCC", "TCC.db");
if (!File.Exists (TCC_db)) {
log.WriteLine ("Opening simulator to create TCC.db");
await OpenSimulator (log);
var tcc_creation_timeout = 60;
var watch = new Stopwatch ();
watch.Start ();
while (!File.Exists (TCC_db) && watch.Elapsed.TotalSeconds < tcc_creation_timeout) {
log.WriteLine ("Waiting for simulator to create TCC.db... {0}", (int)(tcc_creation_timeout - watch.Elapsed.TotalSeconds));
await Task.Delay (TimeSpan.FromSeconds (0.250));
}
}
if (File.Exists (TCC_db)) {
await AgreeToPromptsAsync (log, bundle_identifiers);
} else {
log.WriteLine ("No TCC.db found for the simulator {0} (SimRuntime={1} and SimDeviceType={1})", UDID, SimRuntime, SimDeviceType);
}
// Make sure we're in a clean state
await KillEverythingAsync (log);
// Make 100% sure we're shutdown
await ShutdownAsync (log);
}
}
public class SimDevicePair
{
public string UDID;
public string Companion;
public string Gizmo;
}
public class Devices
{
public Harness Harness;
public List<Device> ConnectedDevices = new List<Device> ();
public async Task LoadAsync (Log log)
{
if (ConnectedDevices.Count > 0)
return;
var tmpfile = Path.GetTempFileName ();
try {
using (var process = new Process ()) {
process.StartInfo.FileName = Harness.MlaunchPath;
process.StartInfo.Arguments = string.Format ("--sdkroot {0} --listdev={1} --output-format=xml", Harness.XcodeRoot, tmpfile);
await process.RunAsync (log, false);
var doc = new XmlDocument ();
doc.LoadWithoutNetworkAccess (tmpfile);
foreach (XmlNode dev in doc.SelectNodes ("/MTouch/Device")) {
ConnectedDevices.Add (new Device ()
{
DeviceIdentifier = dev.SelectSingleNode ("DeviceIdentifier")?.InnerText,
DeviceClass = dev.SelectSingleNode ("DeviceClass")?.InnerText,
CompanionIdentifier = dev.SelectSingleNode ("CompanionIdentifier")?.InnerText,
Name = dev.SelectSingleNode ("Name")?.InnerText,
});
}
}
} finally {
File.Delete (tmpfile);
}
}
}
public class Device
{
public string DeviceIdentifier;
public string DeviceClass;
public string CompanionIdentifier;
public string Name;
}
}