示例#1
0
        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);
                }
            }
        }
示例#2
0
        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);
            }
        }
示例#3
0
        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));
            }
        }
示例#4
0
        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);
        }
示例#5
0
        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));
        }
示例#6
0
        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));
        }
示例#7
0
        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);
                });
            }
        }
示例#8
0
        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());
        }
示例#10
0
        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);
            }
        }
示例#11
0
    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());
    }
示例#12
0
        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);
        }
示例#13
0
        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);
        }
示例#14
0
        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);
        }
    }