public async Task KillEverything(ILog log) { await processManager.ExecuteCommandAsync("launchctl", new[] { "remove", "com.apple.CoreSimulator.CoreSimulatorService" }, log, TimeSpan.FromSeconds(10)); var to_kill = new string[] { "iPhone Simulator", "iOS Simulator", "Simulator", "Simulator (Watch)", "com.apple.CoreSimulator.CoreSimulatorService", "ibtoold" }; var args = new List <string>(); args.Add("-9"); args.AddRange(to_kill); await processManager.ExecuteCommandAsync("killall", args, log, TimeSpan.FromSeconds(10)); var dirsToBeDeleted = new[] { Path.Combine(Environment.GetFolderPath(Environment.SpecialFolder.UserProfile), "Library", "Saved Application State", "com.apple.watchsimulator.savedState"), Path.Combine(Environment.GetFolderPath(Environment.SpecialFolder.UserProfile), "Library", "Saved Application State", "com.apple.iphonesimulator.savedState"), }; foreach (var dir in dirsToBeDeleted) { try { if (Directory.Exists(dir)) { Directory.Delete(dir, true); } } catch (Exception e) { log.WriteLine("Could not delete the directory '{0}': {1}", dir, e.Message); } } }
async Task <ILog> ProcessCrash(string crashFile) { var name = Path.GetFileName(crashFile); var crashReportFile = logs.Create(name, $"Crash report: {name}", timestamp: false); var args = new MlaunchArguments( new DownloadCrashReportArgument(crashFile), new DownloadCrashReportToArgument(crashReportFile.FullPath)); if (!string.IsNullOrEmpty(deviceName)) { args.Add(new DeviceNameArgument(deviceName)); } var result = await processManager.ExecuteCommandAsync(args, log, TimeSpan.FromMinutes(1)); if (result.Succeeded) { log.WriteLine("Downloaded crash report {0} to {1}", crashFile, crashReportFile.FullPath); return(await GetSymbolicateCrashReportAsync(crashReportFile)); } else { log.WriteLine("Could not download crash report {0}", crashFile); return(null); } }
protected async Task <ProcessExecutionResult> RunMacCatalystApp( AppBundleInformation appInfo, TimeSpan timeout, IEnumerable <string> appArguments, Dictionary <string, object> environmentVariables, CancellationToken cancellationToken) { using var systemLog = _captureLogFactory.Create( path: _logs.CreateFile("MacCatalyst.system.log", LogType.SystemLog), systemLogPath: SystemLogPath, entireFile: false, LogType.SystemLog); // We need to make the binary executable var binaryPath = Path.Combine(appInfo.AppPath, "Contents", "MacOS", appInfo.BundleExecutable ?? appInfo.AppName); if (File.Exists(binaryPath)) { await _processManager.ExecuteCommandAsync("chmod", new[] { "+x", binaryPath }, _mainLog, TimeSpan.FromSeconds(10), cancellationToken : cancellationToken); } var arguments = new List <string> { "-W", appInfo.LaunchAppPath }; arguments.AddRange(appArguments); var envVars = environmentVariables.ToDictionary( p => p.Key, p => p.Value is bool?p.Value.ToString().ToLowerInvariant() : p.Value.ToString()); // turns "True" to "true" systemLog.StartCapture(); try { return(await _processManager.ExecuteCommandAsync("open", arguments, _mainLog, timeout, envVars, cancellationToken)); } finally { systemLog.StopCapture(waitIfEmpty: TimeSpan.FromSeconds(10)); } }
public async Task <(string deviceName, ProcessExecutionResult result)> InstallApp(string appPath, TestTarget target, string deviceName = null, CancellationToken cancellationToken = default) { if (target.IsSimulator()) { // We reset the simulator when running, so a separate install step does not make much sense. throw new InvalidOperationException("Installing to a simulator is not supported."); } if (!Directory.Exists(appPath)) { throw new DirectoryNotFoundException("Failed to find the app bundle directory"); } if (deviceName == null) { var device = await _deviceLoader.FindDevice(target.ToRunMode(), _mainLog, false, false); if (target.IsWatchOSTarget()) { deviceName = (await _deviceLoader.FindCompanionDevice(_mainLog, device)).Name; } else { deviceName = device.Name; } } if (deviceName == null) { throw new NoDeviceFoundException(); } var args = new MlaunchArguments(); for (int i = -1; i < _verbosity; i++) { args.Add(new VerbosityArgument()); } args.Add(new InstallAppOnDeviceArgument(appPath)); args.Add(new DeviceNameArgument(deviceName)); if (target.IsWatchOSTarget()) { args.Add(new DeviceArgument("ios,watchos")); } var totalSize = Directory.GetFiles(appPath, "*", SearchOption.AllDirectories).Select((v) => new FileInfo(v).Length).Sum(); _mainLog.WriteLine($"Installing '{appPath}' to '{deviceName}' ({totalSize / 1024.0 / 1024.0:N2} MB)"); ProcessExecutionResult result = await _processManager.ExecuteCommandAsync(args, _mainLog, TimeSpan.FromHours(1), cancellation_token : cancellationToken); return(deviceName, result); }
public async Task <ProcessExecutionResult> InstallAsync(CancellationToken cancellation_token) { Initialize(); if (isSimulator) { // We reset the simulator when running, so a separate install step does not make much sense. throw new Exception("Installing to a simulator is not supported."); } FindDevice(); var args = new List <string> (); if (!string.IsNullOrEmpty(Harness.XcodeRoot)) { args.Add("--sdkroot"); args.Add(Harness.XcodeRoot); } for (int i = -1; i < Harness.Verbosity; i++) { args.Add("-v"); } args.Add("--installdev"); args.Add(appPath); AddDeviceName(args, companion_device_name ?? device_name); if (mode == "watchos") { args.Add("--device"); args.Add("ios,watchos"); } var totalSize = Directory.GetFiles(appPath, "*", SearchOption.AllDirectories).Select((v) => new FileInfo(v).Length).Sum(); main_log.WriteLine($"Installing '{appPath}' to '{companion_device_name ?? device_name}'. Size: {totalSize} bytes = {totalSize / 1024.0 / 1024.0:N2} MB"); return(await ProcessManager.ExecuteCommandAsync(Harness.MlaunchPath, args, main_log, TimeSpan.FromHours(1), cancellation_token : cancellation_token)); }
public async Task <ProcessExecutionResult> UninstallApp(string deviceName, string appBundleId, CancellationToken cancellationToken = default) { var args = new MlaunchArguments(); for (int i = -1; i < _verbosity; i++) { args.Add(new VerbosityArgument()); } args.Add(new UninstallAppFromDeviceArgument(appBundleId)); args.Add(new DeviceNameArgument(deviceName)); return(await _processManager.ExecuteCommandAsync(args, _mainLog, TimeSpan.FromMinutes(1), cancellation_token : cancellationToken)); }
public void Open(string device, ITunnelListener simpleListener, TimeSpan timeout, ILog mainLog) { if (device == null) { throw new ArgumentNullException(nameof(device)); } if (simpleListener == null) { throw new ArgumentNullException(nameof(simpleListener)); } if (mainLog == null) { throw new ArgumentNullException(nameof(mainLog)); } lock (_processExecutionLock) { // launch app, but do not await for the result, since we need to create the tunnel var tcpArgs = new MlaunchArguments { new TcpTunnelArgument(simpleListener.Port), new VerbosityArgument(), new DeviceNameArgument(device), }; // use a cancelation token, later will be used to kill the tcp tunnel process _cancellationToken = new CancellationTokenSource(); mainLog.WriteLine($"Starting tcp tunnel between mac port: {simpleListener.Port} and devie port {simpleListener.Port}."); Port = simpleListener.Port; var tunnelbackLog = new CallbackLog((line) => { mainLog.WriteLine($"The tcp tunnel output is {line}"); if (line.Contains("Tcp tunnel started on device")) { mainLog.Write($"Tcp tunnel created on port {simpleListener.Port}"); startedCompletionSource.TrySetResult(true); simpleListener.TunnelHoleThrough.TrySetResult(true); } }); // do not await since we are going to be running the process in parallel _tcpTunnelExecutionTask = _processManager.ExecuteCommandAsync(tcpArgs, tunnelbackLog, timeout, cancellationToken: _cancellationToken.Token); _tcpTunnelExecutionTask.ContinueWith(delegate(Task <ProcessExecutionResult> task) { // if the task completes, means that we had issues with the creation of the tunnel and the process // exited, if that is the case, we do not want to make the app wait, therefore, set the hole to false // which will throw an exception from the listener. simpleListener.TunnelHoleThrough.TrySetResult(task.Result.Succeeded); }); } }
Task BuildTestLibrariesAsync() { var sb = new StringBuilder(); var callback_log = new CallbackLog((v) => sb.Append(v)); var log = Log.CreateAggregatedLog(callback_log, MainLog); return(processManager.ExecuteCommandAsync("make", new [] { "all", $"-j{Environment.ProcessorCount}", "-C", Path.Combine(HarnessConfiguration.RootDirectory, "test-libraries") }, log, TimeSpan.FromMinutes(10)).ContinueWith((v) => { var per = v.Result; if (!per.Succeeded) { // Only show the log if something went wrong. using var fn = Logs.Create("build-test-libraries.log", "⚠️ Build test/test-libraries failed ⚠️"); File.WriteAllText(fn.FullPath, sb.ToString()); } })); }
private async Task <string> GetPlistProperty(string plistPath, string propertyName, ILog log, CancellationToken cancellationToken = default) { var args = new[] { "-c", $"Print {propertyName}", plistPath, }; var commandOutput = new MemoryLog { Timestamp = false }; var result = await _processManager.ExecuteCommandAsync(PlistBuddyPath, args, log, commandOutput, commandOutput, TimeSpan.FromSeconds(15), cancellationToken : cancellationToken); if (!result.Succeeded) { throw new Exception($"Failed to get bundle information: {commandOutput}"); } return(commandOutput.ToString().Trim()); }
public void Open(string device, ITunnelListener simpleListener, TimeSpan timeout, ILog mainLog) { if (device == null) { throw new ArgumentNullException(nameof(device)); } if (simpleListener == null) { throw new ArgumentNullException(nameof(simpleListener)); } if (mainLog == null) { throw new ArgumentNullException(nameof(mainLog)); } lock (processExecutionLock) { // launch app, but do not await for the result, since we need to create the tunnel var tcpArgs = new MlaunchArguments { new TcpTunnelArgument(simpleListener.Port), new VerbosityArgument(), new DeviceNameArgument(device), }; // use a cancelation token, later will be used to kill the tcp tunnel proces cancellationToken = new CancellationTokenSource(); mainLog.WriteLine($"Starting tcp tunnel between mac port: {simpleListener.Port} and devie port {simpleListener.Port}."); Port = simpleListener.Port; var tunnelbackLog = new CallbackLog((line) => { mainLog.WriteLine($"The tcp tunnel output is {line}"); if (line.Contains("Tcp tunnel started on device")) { mainLog.Write($"Tcp tunnel created on port {simpleListener.Port}"); startedCompletionSource.TrySetResult(true); simpleListener.TunnelHoleThrough.TrySetResult(true); } }); // do not await since we are going to be running the process is parallel tcpTunnelExecutionTask = processManager.ExecuteCommandAsync(tcpArgs, tunnelbackLog, timeout, cancellation_token: cancellationToken.Token); } }
private async Task <string> GetPlistProperty( string plistPath, string propertyName, ILog log, CancellationToken cancellationToken = default, int attempt = 1, int maxAttempts = 3) { var args = new[] { "-c", $"Print {propertyName}", plistPath, }; var commandOutput = new MemoryLog { Timestamp = false }; var result = await _processManager.ExecuteCommandAsync( PlistBuddyPath, args, log, commandOutput, commandOutput, TimeSpan.FromSeconds(10), cancellationToken : cancellationToken); if (!result.Succeeded) { if (result.TimedOut && attempt < maxAttempts) { log.WriteLine($"Attempt to get {propertyName} from {plistPath} timed out, retrying {attempt + 1} out of {maxAttempts}..."); return(await GetPlistProperty(plistPath, propertyName, log, cancellationToken, attempt + 1, maxAttempts)); } throw new Exception($"Failed to get bundle information: {commandOutput}"); } return(commandOutput.ToString().Trim()); }
public async Task<(string DeviceName, TestExecutingResult Result, string ResultMessage)> RunApp( AppBundleInformation appInformation, TestTarget target, TimeSpan timeout, TimeSpan testLaunchTimeout, string? deviceName = null, string? companionDeviceName = null, bool ensureCleanSimulatorState = false, int verbosity = 1, XmlResultJargon xmlResultJargon = XmlResultJargon.xUnit, CancellationToken cancellationToken = default) { var args = new MlaunchArguments { new SetAppArgumentArgument("-connection-mode"), new SetAppArgumentArgument("none"), // This will prevent the app from trying to connect to any IDEs new SetAppArgumentArgument("-autostart", true), new SetEnvVariableArgument(EnviromentVariables.AutoStart, true), new SetAppArgumentArgument("-autoexit", true), new SetEnvVariableArgument(EnviromentVariables.AutoExit, true), new SetAppArgumentArgument("-enablenetwork", true), new SetEnvVariableArgument(EnviromentVariables.EnableNetwork, true), // On macOS we can't edit the TCC database easily // (it requires adding the mac has to be using MDM: https://carlashley.com/2018/09/28/tcc-round-up/) // So by default ignore any tests that would pop up permission dialogs in CI. new SetEnvVariableArgument(EnviromentVariables.DisableSystemPermissionTests, 1), }; for (int i = -1; i < verbosity; i++) { args.Add(new VerbosityArgument()); } var isSimulator = target.IsSimulator(); if (isSimulator) { args.Add(new SetAppArgumentArgument("-hostname:127.0.0.1", true)); args.Add(new SetEnvVariableArgument(EnviromentVariables.HostName, "127.0.0.1")); } else { var ipAddresses = System.Net.Dns.GetHostEntry(System.Net.Dns.GetHostName()).AddressList.Select(ip => ip.ToString()); var ips = string.Join(",", ipAddresses); args.Add(new SetAppArgumentArgument($"-hostname:{ips}", true)); args.Add(new SetEnvVariableArgument(EnviromentVariables.HostName, ips)); } var listenerLog = _logs.Create($"test-{target.AsString()}-{_helpers.Timestamp}.log", LogType.TestLog.ToString(), timestamp: true); var (transport, listener, listenerTmpFile) = _listenerFactory.Create(target.ToRunMode(), log: _mainLog, testLog: listenerLog, isSimulator: isSimulator, autoExit: true, xmlOutput: true); // cli always uses xml // Initialize has to be called before we try to get Port (internal implementation of the listener says so) // TODO: Improve this to not get into a broken state - it was really hard to debug when I moved this lower listener.Initialize(); args.Add(new SetAppArgumentArgument($"-transport:{transport}", true)); args.Add(new SetEnvVariableArgument(EnviromentVariables.Transport, transport.ToString().ToUpper())); if (transport == ListenerTransport.File) { args.Add(new SetEnvVariableArgument(EnviromentVariables.LogFilePath, listenerTmpFile)); } args.Add(new SetAppArgumentArgument($"-hostport:{listener.Port}", true)); args.Add(new SetEnvVariableArgument(EnviromentVariables.HostPort, listener.Port)); if (_listenerFactory.UseTunnel && !isSimulator) // simulators do not support tunnels { args.Add(new SetEnvVariableArgument(EnviromentVariables.UseTcpTunnel, true)); } if (_useXmlOutput) { // let the runner now via envars that we want to get a xml output, else the runner will default to plain text args.Add (new SetEnvVariableArgument (EnviromentVariables.EnableXmlOutput, true)); args.Add (new SetEnvVariableArgument (EnviromentVariables.XmlMode, "wrapped")); args.Add (new SetEnvVariableArgument (EnviromentVariables.XmlVersion, $"{xmlResultJargon}")); } listener.StartAsync(); var crashLogs = new Logs(_logs.Directory); if (appInformation.Extension.HasValue) { switch (appInformation.Extension) { case Extension.TodayExtension: args.Add(isSimulator ? (MlaunchArgument)new LaunchSimulatorExtensionArgument(appInformation.LaunchAppPath, appInformation.BundleIdentifier) : new LaunchDeviceExtensionArgument(appInformation.LaunchAppPath, appInformation.BundleIdentifier)); break; case Extension.WatchKit2: default: throw new NotImplementedException(); } } else { args.Add(isSimulator ? (MlaunchArgument)new LaunchSimulatorArgument(appInformation.LaunchAppPath) : new LaunchDeviceArgument(appInformation.LaunchAppPath)); } var runMode = target.ToRunMode(); ICrashSnapshotReporter crashReporter; ITestReporter testReporter; if (isSimulator) { crashReporter = _snapshotReporterFactory.Create(_mainLog, crashLogs, isDevice: !isSimulator, deviceName: null!); testReporter = _testReporterFactory.Create(_mainLog, _mainLog, _logs, crashReporter, listener, new XmlResultParser(), appInformation, runMode, xmlResultJargon, device: null, timeout, null, (level, message) => _mainLog.WriteLine(message)); using var linkedCts = CancellationTokenSource.CreateLinkedTokenSource(testReporter.CancellationToken, cancellationToken); listener.ConnectedTask .TimeoutAfter(testLaunchTimeout) .ContinueWith(testReporter.LaunchCallback) .DoNotAwait(); await _simulatorLoader.LoadDevices(_logs.Create($"simulator-list-{_helpers.Timestamp}.log", "Simulator list"), false, false); var simulators = await _simulatorLoader.FindSimulators(target, _mainLog); if (!(simulators?.Any() ?? false)) { _mainLog.WriteLine("Didn't find any suitable simulators"); throw new NoDeviceFoundException(); } var simulator = string.IsNullOrEmpty(deviceName) ? simulators.FirstOrDefault() : simulators.FirstOrDefault(s => string.Equals(s.Name, deviceName, StringComparison.InvariantCultureIgnoreCase)); if (simulator == null) { throw new NoDeviceFoundException(); } deviceName = simulator.Name; if (!target.IsWatchOSTarget()) { var stderrTty = _helpers.GetTerminalName(2); if (!string.IsNullOrEmpty(stderrTty)) { args.Add(new SetStderrArgument(stderrTty)); } else { var stdoutLog = _logs.CreateFile($"mlaunch-stdout-{_helpers.Timestamp}.log", "Standard output"); var stderrLog = _logs.CreateFile($"mlaunch-stderr-{_helpers.Timestamp}.log", "Standard error"); args.Add(new SetStdoutArgument(stdoutLog)); args.Add(new SetStderrArgument(stderrLog)); } } var systemLogs = new List<ICaptureLog>(); foreach (var sim in simulators) { // Upload the system log _mainLog.WriteLine("System log for the '{1}' simulator is: {0}", sim.SystemLog, sim.Name); bool isCompanion = sim != simulator; var logDescription = isCompanion ? LogType.CompanionSystemLog.ToString() : LogType.SystemLog.ToString(); var log = _captureLogFactory.Create( Path.Combine(_logs.Directory, sim.Name + ".log"), sim.SystemLog, true, logDescription); log.StartCapture(); _logs.Add(log); systemLogs.Add(log); } _mainLog.WriteLine("*** Executing {0}/{1} in the simulator ***", appInformation.AppName, target); if (ensureCleanSimulatorState) { foreach (var sim in simulators) { await sim.PrepareSimulator(_mainLog, appInformation.BundleIdentifier); } } args.Add(new SimulatorUDIDArgument(simulator.UDID)); await crashReporter.StartCaptureAsync(); _mainLog.WriteLine("Starting test run"); var result = _processManager.ExecuteCommandAsync(args, _mainLog, timeout, cancellationToken: linkedCts.Token); await testReporter.CollectSimulatorResult(result); // cleanup after us if (ensureCleanSimulatorState) { await simulator.KillEverything(_mainLog); } foreach (var log in systemLogs) { log.StopCapture(); } } else { args.Add(new DisableMemoryLimitsArgument()); if (deviceName == null) { IHardwareDevice? companionDevice = null; IHardwareDevice device = await _hardwareDeviceLoader.FindDevice(runMode, _mainLog, includeLocked: false, force: false); if (target.IsWatchOSTarget()) { companionDevice = await _hardwareDeviceLoader.FindCompanionDevice(_mainLog, device); } deviceName = companionDevice?.Name ?? device.Name; } if (deviceName == null) { throw new NoDeviceFoundException(); } crashReporter = _snapshotReporterFactory.Create(_mainLog, crashLogs, isDevice: !isSimulator, deviceName); testReporter = _testReporterFactory.Create(_mainLog, _mainLog, _logs, crashReporter, listener, new XmlResultParser(), appInformation, runMode, xmlResultJargon, deviceName, timeout, null, (level, message) => _mainLog.WriteLine(message)); using var linkedCts = CancellationTokenSource.CreateLinkedTokenSource(testReporter.CancellationToken, cancellationToken); listener.ConnectedTask .TimeoutAfter(testLaunchTimeout) .ContinueWith(testReporter.LaunchCallback) .DoNotAwait(); _mainLog.WriteLine("*** Executing {0}/{1} on device '{2}' ***", appInformation.AppName, target, deviceName); if (target.IsWatchOSTarget()) { args.Add(new AttachNativeDebuggerArgument()); // this prevents the watch from backgrounding the app. } else { args.Add(new WaitForExitArgument()); } args.Add(new DeviceNameArgument(deviceName)); var deviceSystemLog = _logs.Create($"device-{deviceName}-{_helpers.Timestamp}.log", "Device log"); var deviceLogCapturer = _deviceLogCapturerFactory.Create(_mainLog, deviceSystemLog, deviceName); deviceLogCapturer.StartCapture(); try { await crashReporter.StartCaptureAsync(); // create a tunnel to communicate with the device if (transport == ListenerTransport.Tcp && _listenerFactory.UseTunnel && listener is SimpleTcpListener tcpListener) { // create a new tunnel using the listener var tunnel = _listenerFactory.TunnelBore.Create(deviceName, _mainLog); tunnel.Open(deviceName, tcpListener, timeout, _mainLog); // wait until we started the tunnel await tunnel.Started; } _mainLog.WriteLine("Starting test run"); // We need to check for MT1111 (which means that mlaunch won't wait for the app to exit). var aggregatedLog = Log.CreateAggregatedLog(testReporter.CallbackLog, _mainLog); Task<ProcessExecutionResult> runTestTask = _processManager.ExecuteCommandAsync( args, aggregatedLog, timeout, cancellationToken: linkedCts.Token); await testReporter.CollectDeviceResult(runTestTask); } finally { deviceLogCapturer.StopCapture(); deviceSystemLog.Dispose(); // close a tunnel if it was created if (!isSimulator && _listenerFactory.UseTunnel) await _listenerFactory.TunnelBore.Close(deviceName); } // Upload the system log if (File.Exists(deviceSystemLog.FullPath)) { _mainLog.WriteLine("A capture of the device log is: {0}", deviceSystemLog.FullPath); } } listener.Cancel(); listener.Dispose(); // check the final status, copy all the required data var (testResult, resultMessage) = await testReporter.ParseResult(); return (deviceName, testResult, resultMessage); }
public async Task <bool> AgreeToPromptsAsync(string simRuntime, string TCCDb, string udid, ILog log, params string [] bundle_identifiers) { if (bundle_identifiers == null || bundle_identifiers.Length == 0) { log.WriteLine("No bundle identifiers given when requested permission editing."); return(false); } var sim_services = new string [] { "kTCCServiceAll", // You'd think 'All' means all prompts, but some prompts still show up. "kTCCServiceAddressBook", "kTCCServiceCalendar", "kTCCServicePhotos", "kTCCServiceMediaLibrary", "kTCCServiceMicrophone", "kTCCServiceUbiquity", "kTCCServiceWillow" }; var failure = false; var tcc_edit_timeout = 3; var watch = new Stopwatch(); watch.Start(); var format = GetTCCFormat(simRuntime); if (format >= 4) { // We don't care if booting fails (it'll fail if it's already booted for instance) await processManager.ExecuteXcodeCommandAsync("simctl", new [] { "boot", udid }, log, TimeSpan.FromMinutes(1)); // execute 'simctl privacy <udid> grant all <bundle identifier>' for each bundle identifier foreach (var bundle_identifier in bundle_identifiers) { foreach (var bundle_id in new [] { bundle_identifier, bundle_identifier + ".watchkitapp" }) { foreach (var service in sim_services) { var args = new List <string> (); args.Add("privacy"); args.Add(udid); args.Add("grant"); args.Add(service); args.Add(bundle_id); var rv = await processManager.ExecuteXcodeCommandAsync("simctl", args, log, TimeSpan.FromSeconds(30)); if (!rv.Succeeded) { failure = true; break; } } } if (failure) { break; } } } else { 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 args = new List <string> (); var sql = new System.Text.StringBuilder("\n"); args.Add(TCCDb); foreach (var bundle_id in new [] { bundle_identifier, bundle_identifier + ".watchkitapp" }) { foreach (var service in sim_services) { switch (format) { case 1: // CREATE TABLE access (service TEXT NOT NULL, client TEXT NOT NULL, client_type INTEGER NOT NULL, allowed INTEGER NOT NULL, prompt_count INTEGER NOT NULL, csreq BLOB, CONSTRAINT key PRIMARY KEY (service, client, client_type)); sql.AppendFormat("DELETE FROM access WHERE service = '{0}' AND client = '{1}';\n", service, bundle_id); sql.AppendFormat("INSERT INTO access VALUES('{0}','{1}',0,1,0,NULL);\n", service, bundle_id); break; case 2: // CREATE TABLE access (service TEXT NOT NULL, client TEXT NOT NULL, client_type INTEGER NOT NULL, allowed INTEGER NOT NULL, prompt_count INTEGER NOT NULL, csreq BLOB, policy_id INTEGER, PRIMARY KEY (service, client, client_type), FOREIGN KEY (policy_id) REFERENCES policies(id) ON DELETE CASCADE ON UPDATE CASCADE); sql.AppendFormat("DELETE FROM access WHERE service = '{0}' AND client = '{1}';\n", service, bundle_id); sql.AppendFormat("INSERT INTO access VALUES('{0}','{1}',0,1,0,NULL,NULL);\n", service, bundle_id); break; case 3: // Xcode 10+ // CREATE TABLE access ( service TEXT NOT NULL, client TEXT NOT NULL, client_type INTEGER NOT NULL, allowed INTEGER NOT NULL, prompt_count INTEGER NOT NULL, csreq BLOB, policy_id INTEGER, indirect_object_identifier_type INTEGER, indirect_object_identifier TEXT, indirect_object_code_identity BLOB, flags INTEGER, last_modified INTEGER NOT NULL DEFAULT (CAST(strftime('%s','now') AS INTEGER)), PRIMARY KEY (service, client, client_type, indirect_object_identifier), FOREIGN KEY (policy_id) REFERENCES policies(id) ON DELETE CASCADE ON UPDATE CASCADE) sql.AppendFormat("INSERT OR REPLACE INTO access VALUES('{0}','{1}',0,1,0,NULL,NULL,NULL,'UNUSED',NULL,NULL,{2});\n", service, bundle_id, DateTimeOffset.Now.ToUnixTimeSeconds()); break; default: throw new NotImplementedException(); } } } args.Add(sql.ToString()); var rv = await processManager.ExecuteCommandAsync("sqlite3", args, 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"); } log.WriteLine("Current TCC database contents:"); await processManager.ExecuteCommandAsync("sqlite3", new [] { TCCDb, ".dump" }, log, TimeSpan.FromSeconds(5)); return(!failure); }
public async Task <(string deviceName, ProcessExecutionResult result)> InstallApp(AppBundleInformation appBundleInformation, TestTarget target, string?deviceName = null, CancellationToken cancellationToken = default) { if (target.IsSimulator()) { // We reset the simulator when running, so a separate install step does not make much sense. throw new InvalidOperationException("Installing to a simulator is not supported."); } if (!Directory.Exists(appBundleInformation.LaunchAppPath)) { throw new DirectoryNotFoundException("Failed to find the app bundle directory"); } if (deviceName == null) { // the _deviceLoader.FindDevice will return the fist device of the type, but we want to make sure that // the device we use is if the correct arch, therefore, we will use the LoadDevices and return the // correct one await _deviceLoader.LoadDevices(_mainLog, false, false); IHardwareDevice?device = null; if (appBundleInformation.Supports32Bit) { // we only support 32b on iOS, therefore we can ignore the target device = _deviceLoader.Connected32BitIOS.FirstOrDefault(); } else { device = target switch { TestTarget.Device_iOS => _deviceLoader.Connected64BitIOS.FirstOrDefault(), TestTarget.Device_tvOS => _deviceLoader.ConnectedTV.FirstOrDefault(), _ => device }; } deviceName = target.IsWatchOSTarget() ? (await _deviceLoader.FindCompanionDevice(_mainLog, device)).Name : device?.Name; } if (deviceName == null) { throw new NoDeviceFoundException(); } var args = new MlaunchArguments(); for (int i = -1; i < _verbosity; i++) { args.Add(new VerbosityArgument()); } args.Add(new InstallAppOnDeviceArgument(appBundleInformation.LaunchAppPath)); args.Add(new DeviceNameArgument(deviceName)); if (target.IsWatchOSTarget()) { args.Add(new DeviceArgument("ios,watchos")); } var totalSize = Directory.GetFiles(appBundleInformation.LaunchAppPath, "*", SearchOption.AllDirectories).Select((v) => new FileInfo(v).Length).Sum(); _mainLog.WriteLine($"Installing '{appBundleInformation.LaunchAppPath}' to '{deviceName}' ({totalSize / 1024.0 / 1024.0:N2} MB)"); ProcessExecutionResult result = await _processManager.ExecuteCommandAsync(args, _mainLog, TimeSpan.FromHours(1), cancellationToken : cancellationToken); return(deviceName, result); } }