示例#1
0
        public void InstallWhenNoDevicesTest()
        {
            var appRunner = new AppRunner(processManager.Object,
                                          appBundleInformationParser,
                                          simulatorsFactory,
                                          listenerFactory,
                                          devicesFactory,
                                          snapshotReporterFactory,
                                          Mock.Of <ICaptureLogFactory> (),
                                          Mock.Of <IDeviceLogCapturerFactory> (),
                                          Mock.Of <ITestReporterFactory> (),
                                          TestTarget.Device_iOS,
                                          Mock.Of <IHarness> (),
                                          mainLog.Object,
                                          logs.Object,
                                          projectFilePath: projectFilePath,
                                          buildConfiguration: "Debug");

            appRunner.InitializeAsync().Wait();

            devices.Setup(x => x.ConnectedDevices).Returns(new IHardwareDevice [0]);

            Assert.ThrowsAsync <NoDeviceFoundException> (
                async() => await appRunner.InstallAsync(new CancellationToken()),
                "Install requires connected devices");
        }
示例#2
0
        public void InitializeTest()
        {
            var appRunner = new AppRunner(processManager.Object,
                                          appBundleInformationParser,
                                          simulatorsFactory,
                                          listenerFactory,
                                          devicesFactory,
                                          snapshotReporterFactory,
                                          Mock.Of <ICaptureLogFactory> (),
                                          Mock.Of <IDeviceLogCapturerFactory> (),
                                          Mock.Of <ITestReporterFactory> (),
                                          TestTarget.Simulator_iOS64,
                                          Mock.Of <IHarness> (),
                                          mainLog.Object,
                                          logs.Object,
                                          projectFilePath: projectFilePath,
                                          buildConfiguration: "Debug");

            appRunner.InitializeAsync().Wait();

            Assert.AreEqual(appName, appRunner.AppInformation.AppName);
            Assert.AreEqual(appPath, appRunner.AppInformation.AppPath);
            Assert.AreEqual(appPath, appRunner.AppInformation.LaunchAppPath);
            Assert.AreEqual(appName, appRunner.AppInformation.BundleIdentifier);
        }
示例#3
0
        public void RunOnDeviceWithNoAvailableSimulatorTest()
        {
            devices.Setup(d => d.FindDevice(It.IsAny <RunMode> (), It.IsAny <ILog> (), false, false)).ReturnsAsync((IHardwareDevice)null);
            simulators
            .Setup(s => s.FindSimulators(It.IsAny <TestTarget> (), It.IsAny <ILog> (), true, false))
            .ReturnsAsync((null, null));

            // Crash reporter
            var crashReporterFactory = new Mock <ICrashSnapshotReporterFactory> ();

            crashReporterFactory
            .Setup(x => x.Create(mainLog.Object, It.IsAny <ILogs> (), false, null))
            .Returns(snapshotReporter.Object);

            var listenerLogFile     = new Mock <IFileBackedLog> ();
            var testReporterFactory = new Mock <ITestReporterFactory> ();
            var testReporter        = new Mock <ITestReporter> ();

            testReporterFactory.SetReturnsDefault(testReporter.Object);
            testReporter.Setup(r => r.ParseResult()).Returns(() => {
                return(Task.FromResult <(TestExecutingResult, string)> ((TestExecutingResult.Succeeded, null)));
            });
            testReporter.Setup(r => r.Success).Returns(true);

            logs
            .Setup(x => x.Create(It.IsAny <string> (), "TestLog", It.IsAny <bool> ()))
            .Returns(listenerLogFile.Object);

            simpleListener.SetupGet(x => x.ConnectedTask).Returns(Task.FromResult(true));

            // Act
            var appRunner = new AppRunner(processManager.Object,
                                          appBundleInformationParser,
                                          simulatorsFactory,
                                          listenerFactory,
                                          devicesFactory,
                                          crashReporterFactory.Object,
                                          Mock.Of <ICaptureLogFactory> (),
                                          Mock.Of <IDeviceLogCapturerFactory> (),
                                          testReporterFactory.Object,
                                          TestTarget.Device_tvOS,
                                          GetMockedHarness(),
                                          mainLog.Object,
                                          logs.Object,
                                          projectFilePath: projectFilePath,
                                          buildConfiguration: "Debug",
                                          timeoutMultiplier: 2);

            appRunner.InitializeAsync().Wait();

            Assert.ThrowsAsync <NoDeviceFoundException> (
                async() => await appRunner.RunAsync(),
                "Running requires connected devices");
        }
示例#4
0
        public async Task InstallOnDeviceTest()
        {
            var harness = Mock.Of <IHarness> (x => x.Verbosity == 2);

            var processResult = new ProcessExecutionResult()
            {
                ExitCode = 1, TimedOut = false
            };

            processManager.SetReturnsDefault(Task.FromResult(processResult));

            devices.Setup(d => d.FindDevice(It.IsAny <RunMode> (), It.IsAny <ILog> (), false, false)).ReturnsAsync(mockDevices [0]);

            // Act
            var appRunner = new AppRunner(processManager.Object,
                                          appBundleInformationParser,
                                          simulatorsFactory,
                                          listenerFactory,
                                          devicesFactory,
                                          snapshotReporterFactory,
                                          Mock.Of <ICaptureLogFactory> (),
                                          Mock.Of <IDeviceLogCapturerFactory> (),
                                          Mock.Of <ITestReporterFactory> (),
                                          TestTarget.Device_iOS,
                                          harness,
                                          mainLog.Object,
                                          logs.Object,
                                          projectFilePath: projectFilePath,
                                          buildConfiguration: "Debug");

            appRunner.InitializeAsync().Wait();

            CancellationToken cancellationToken = new CancellationToken();
            var result = await appRunner.InstallAsync(cancellationToken);

            // Verify
            Assert.AreEqual(1, result.ExitCode);

            var expectedArgs = $"-v -v -v --installdev {StringUtils.FormatArguments (appPath)} --devname \"{mockDevices[0].Name}\"";

            processManager.Verify(x => x.ExecuteCommandAsync(
                                      It.Is <MlaunchArguments> (args => args.AsCommandLine() == expectedArgs),
                                      mainLog.Object,
                                      TimeSpan.FromHours(1),
                                      null,
                                      5,
                                      cancellationToken));
        }
示例#5
0
        public async Task UninstallFromDeviceTest()
        {
            var harness = Mock.Of <IHarness> (x => x.Verbosity == 1);

            var processResult = new ProcessExecutionResult()
            {
                ExitCode = 3, TimedOut = false
            };

            processManager.SetReturnsDefault(Task.FromResult(processResult));

            devices.Setup(d => d.FindDevice(It.IsAny <RunMode> (), It.IsAny <ILog> (), false, false)).ReturnsAsync(mockDevices[1]);

            // Act
            var appRunner = new AppRunner(processManager.Object,
                                          appBundleInformationParser,
                                          simulatorsFactory,
                                          listenerFactory,
                                          devicesFactory,
                                          snapshotReporterFactory,
                                          Mock.Of <ICaptureLogFactory> (),
                                          Mock.Of <IDeviceLogCapturerFactory> (),
                                          Mock.Of <ITestReporterFactory> (),
                                          TestTarget.Device_iOS,
                                          harness,
                                          mainLog.Object,
                                          logs.Object,
                                          projectFilePath: Path.Combine(sampleProjectPath, "SystemXunit.csproj"),
                                          buildConfiguration: "Debug");

            appRunner.InitializeAsync().Wait();

            var result = await appRunner.UninstallAsync();

            // Verify
            Assert.AreEqual(3, result.ExitCode);

            var expectedArgs = $"-v -v --uninstalldevbundleid {StringUtils.FormatArguments (appName)} --devname \"Test iPad\"";

            processManager.Verify(x => x.ExecuteCommandAsync(
                                      It.Is <MlaunchArguments> (args => args.AsCommandLine() == expectedArgs),
                                      mainLog.Object,
                                      TimeSpan.FromMinutes(1),
                                      null,
                                      5,
                                      null));
        }
示例#6
0
        public void UninstallToSimulatorTest()
        {
            var appRunner = new AppRunner(processManager.Object,
                                          appBundleInformationParser,
                                          simulatorsFactory,
                                          listenerFactory,
                                          devicesFactory,
                                          snapshotReporterFactory,
                                          Mock.Of <ICaptureLogFactory> (),
                                          Mock.Of <IDeviceLogCapturerFactory> (),
                                          Mock.Of <ITestReporterFactory> (),
                                          TestTarget.Simulator_iOS64,
                                          Mock.Of <IHarness> (),
                                          mainLog.Object,
                                          logs.Object,
                                          projectFilePath: projectFilePath,
                                          buildConfiguration: "Debug");

            appRunner.InitializeAsync().Wait();

            var exception = Assert.ThrowsAsync <InvalidOperationException> (
                async() => await appRunner.UninstallAsync(),
                "Uninstall should not be allowed on a simulator");
        }
示例#7
0
        public async Task RunOnDeviceWithFailedTestsTest()
        {
            var harness = GetMockedHarness();

            devices.Setup(d => d.FindDevice(It.IsAny <RunMode> (), It.IsAny <ILog> (), false, false)).ReturnsAsync(mockDevices[1]);

            // Crash reporter
            var crashReporterFactory = new Mock <ICrashSnapshotReporterFactory> ();

            crashReporterFactory
            .Setup(x => x.Create(mainLog.Object, It.IsAny <ILogs> (), true, "Test iPad"))
            .Returns(snapshotReporter.Object);

            var deviceSystemLog = new Mock <IFileBackedLog> ();

            deviceSystemLog.SetupGet(x => x.FullPath).Returns(Path.GetTempFileName());

            var testResultFilePath = Path.GetTempFileName();
            var listenerLogFile    = Mock.Of <IFileBackedLog> (x => x.FullPath == testResultFilePath);

            File.WriteAllLines(testResultFilePath, new [] { "Some result here", "[FAIL] This test failed", "Some result there", "Tests run: 3" });

            logs
            .Setup(x => x.Create(It.Is <string> (s => s.StartsWith("test-ios-")), "TestLog", It.IsAny <bool?> ()))
            .Returns(listenerLogFile);

            logs
            .Setup(x => x.Create(It.Is <string> (s => s.StartsWith("device-Test iPad-")), "Device log", It.IsAny <bool?> ()))
            .Returns(deviceSystemLog.Object);

            simpleListener.SetupGet(x => x.ConnectedTask).Returns(Task.FromResult(true));

            var deviceLogCapturer = new Mock <IDeviceLogCapturer> ();

            var deviceLogCapturerFactory = new Mock <IDeviceLogCapturerFactory> ();

            deviceLogCapturerFactory
            .Setup(x => x.Create(mainLog.Object, deviceSystemLog.Object, "Test iPad"))
            .Returns(deviceLogCapturer.Object);

            var ips         = new StringBuilder();
            var ipAddresses = System.Net.Dns.GetHostEntry(System.Net.Dns.GetHostName()).AddressList;

            for (int i = 0; i < ipAddresses.Length; i++)
            {
                if (i > 0)
                {
                    ips.Append(',');
                }
                ips.Append(ipAddresses [i].ToString());
            }

            var expectedArgs = $"-v -v -argument=-connection-mode -argument=none " +
                               $"-argument=-app-arg:-autostart -setenv=NUNIT_AUTOSTART=true -argument=-app-arg:-autoexit " +
                               $"-setenv=NUNIT_AUTOEXIT=true -argument=-app-arg:-enablenetwork -setenv=NUNIT_ENABLE_NETWORK=true " +
                               $"-setenv=DISABLE_SYSTEM_PERMISSION_TESTS=1 -argument=-app-arg:-hostname:{ips} -setenv=NUNIT_HOSTNAME={ips} " +
                               $"-argument=-app-arg:-transport:Tcp -setenv=NUNIT_TRANSPORT=TCP -argument=-app-arg:-hostport:{listenerPort} " +
                               $"-setenv=NUNIT_HOSTPORT={listenerPort} -setenv=env1=value1 -setenv=env2=value2 " +
                               $"--launchdev {StringUtils.FormatArguments (appPath)} --disable-memory-limits --wait-for-exit --devname \"Test iPad\"";

            processManager
            .Setup(x => x.ExecuteCommandAsync(
                       It.Is <MlaunchArguments> (args => args.AsCommandLine() == expectedArgs),
                       It.IsAny <ILog> (),
                       TimeSpan.FromMinutes(harness.Timeout * 2),
                       null,
                       It.IsAny <CancellationToken> ()))
            .ReturnsAsync(new ProcessExecutionResult()
            {
                ExitCode = 0
            });

            var xmlResultFile       = Path.ChangeExtension(testResultFilePath, "xml");
            var testReporterFactory = new Mock <ITestReporterFactory> ();
            var testReporter        = new Mock <ITestReporter> ();

            testReporterFactory.SetReturnsDefault(testReporter.Object);
            testReporter.Setup(r => r.ParseResult()).Returns(() => {
                return(Task.FromResult <(TestExecutingResult, string)> ((TestExecutingResult.Failed, null)));
            });
            testReporter.Setup(r => r.Success).Returns(false);

            // Act
            var appRunner = new AppRunner(processManager.Object,
                                          appBundleInformationParser,
                                          simulatorsFactory,
                                          listenerFactory,
                                          devicesFactory,
                                          crashReporterFactory.Object,
                                          Mock.Of <ICaptureLogFactory> (), // Used for simulators only
                                          deviceLogCapturerFactory.Object,
                                          testReporterFactory.Object,
                                          TestTarget.Device_iOS,
                                          harness,
                                          mainLog.Object,
                                          logs.Object,
                                          projectFilePath: projectFilePath,
                                          buildConfiguration: "Debug",
                                          timeoutMultiplier: 2);

            appRunner.InitializeAsync().Wait();

            var result = await appRunner.RunAsync();

            // Verify
            Assert.AreEqual(1, result);

            processManager.VerifyAll();

            simpleListener.Verify(x => x.InitializeAndGetPort(), Times.AtLeastOnce);
            simpleListener.Verify(x => x.StartAsync(), Times.AtLeastOnce);
            simpleListener.Verify(x => x.Cancel(), Times.AtLeastOnce);
            simpleListener.Verify(x => x.Dispose(), Times.AtLeastOnce);

            snapshotReporter.Verify(x => x.StartCaptureAsync(), Times.AtLeastOnce);
            snapshotReporter.Verify(x => x.StartCaptureAsync(), Times.AtLeastOnce);

            deviceSystemLog.Verify(x => x.Dispose(), Times.AtLeastOnce);
        }
示例#8
0
        public async Task RunOnSimulatorSuccessfullyTest()
        {
            var harness = GetMockedHarness();

            devices.Setup(x => x.ConnectedDevices).Returns(mockDevices.Reverse());

            // Crash reporter
            var crashReporterFactory = new Mock <ICrashSnapshotReporterFactory> ();

            crashReporterFactory
            .Setup(x => x.Create(mainLog.Object, It.IsAny <ILogs> (), false, null))
            .Returns(snapshotReporter.Object);

            // Mock finding simulators
            simulators
            .Setup(x => x.LoadDevices(It.IsAny <ILog> (), false, false, false))
            .Returns(Task.FromResult(true));

            string simulatorLogPath = Path.Combine(Path.GetTempPath(), "simulator-logs");

            var simulator = new Mock <ISimulatorDevice> ();

            simulator.SetupGet(x => x.Name).Returns("Test iPhone simulator");
            simulator.SetupGet(x => x.UDID).Returns("58F21118E4D34FD69EAB7860BB9B38A0");
            simulator.SetupGet(x => x.LogPath).Returns(simulatorLogPath);
            simulator.SetupGet(x => x.SystemLog).Returns(Path.Combine(simulatorLogPath, "system.log"));

            simulators
            .Setup(x => x.FindSimulators(TestTarget.Simulator_iOS64, mainLog.Object, true, false))
            .ReturnsAsync((simulator.Object, null));

            var testResultFilePath = Path.GetTempFileName();
            var listenerLogFile    = Mock.Of <IFileBackedLog> (x => x.FullPath == testResultFilePath);

            File.WriteAllLines(testResultFilePath, new [] { "Some result here", "Tests run: 124", "Some result there" });

            logs
            .Setup(x => x.Create(It.IsAny <string> (), It.IsAny <string>(), It.IsAny <bool?> ()))
            .Returns(listenerLogFile);

            logs
            .Setup(x => x.CreateFile(It.IsAny <string> (), It.IsAny <string> ()))
            .Returns($"/path/to/log-{Guid.NewGuid ()}.log");

            simpleListener.SetupGet(x => x.ConnectedTask).Returns(Task.FromResult(true));

            var captureLog = new Mock <ICaptureLog> ();

            captureLog.SetupGet(x => x.FullPath).Returns(simulatorLogPath);

            var captureLogFactory = new Mock <ICaptureLogFactory> ();

            captureLogFactory
            .Setup(x => x.Create(
                       Path.Combine(logs.Object.Directory, simulator.Object.Name + ".log"),
                       simulator.Object.SystemLog,
                       true,
                       It.IsAny <string> ()))
            .Returns(captureLog.Object);

            var expectedArgs = $"--sdkroot {StringUtils.FormatArguments (xcodePath)} -v -v " +
                               $"-argument=-connection-mode -argument=none -argument=-app-arg:-autostart " +
                               $"-setenv=NUNIT_AUTOSTART=true -argument=-app-arg:-autoexit -setenv=NUNIT_AUTOEXIT=true " +
                               $"-argument=-app-arg:-enablenetwork -setenv=NUNIT_ENABLE_NETWORK=true " +
                               $"-setenv=DISABLE_SYSTEM_PERMISSION_TESTS=1 -argument=-app-arg:-hostname:127.0.0.1 " +
                               $"-setenv=NUNIT_HOSTNAME=127.0.0.1 -argument=-app-arg:-transport:Tcp -setenv=NUNIT_TRANSPORT=TCP " +
                               $"-argument=-app-arg:-hostport:{listenerPort} -setenv=NUNIT_HOSTPORT={listenerPort} " +
                               $"-setenv=env1=value1 -setenv=env2=value2 --launchsim {StringUtils.FormatArguments (appPath)} " +
                               $"--stdout=tty1 --stderr=tty1 --device=:v2:udid={simulator.Object.UDID}";

            processManager
            .Setup(x => x.ExecuteCommandAsync(
                       It.Is <MlaunchArguments> (args => args.AsCommandLine() == expectedArgs),
                       mainLog.Object,
                       TimeSpan.FromMinutes(harness.Timeout * 2),
                       null,
                       It.IsAny <CancellationToken> ()))
            .ReturnsAsync(new ProcessExecutionResult()
            {
                ExitCode = 0
            });

            var xmlResultFile       = Path.ChangeExtension(testResultFilePath, "xml");
            var testReporterFactory = new Mock <ITestReporterFactory> ();
            var testReporter        = new Mock <ITestReporter> ();

            testReporterFactory.SetReturnsDefault(testReporter.Object);
            testReporter.Setup(r => r.ParseResult()).Returns(() => {
                return(Task.FromResult <(TestExecutingResult, string)> ((TestExecutingResult.Succeeded, null)));
            });
            testReporter.Setup(r => r.Success).Returns(true);

            // Act
            var appRunner = new AppRunner(processManager.Object,
                                          appBundleInformationParser,
                                          simulatorsFactory,
                                          listenerFactory,
                                          devicesFactory,
                                          crashReporterFactory.Object,
                                          captureLogFactory.Object,
                                          Mock.Of <IDeviceLogCapturerFactory> (), // Use for devices only
                                          testReporterFactory.Object,
                                          TestTarget.Simulator_iOS64,
                                          harness,
                                          mainLog.Object,
                                          logs.Object,
                                          projectFilePath: projectFilePath,
                                          buildConfiguration: "Debug",
                                          timeoutMultiplier: 2,
                                          ensureCleanSimulatorState: true);

            appRunner.InitializeAsync().Wait();

            var result = await appRunner.RunAsync();

            // Verify
            Assert.AreEqual(0, result);

            simpleListener.Verify(x => x.InitializeAndGetPort(), Times.AtLeastOnce);
            simpleListener.Verify(x => x.StartAsync(), Times.AtLeastOnce);
            simpleListener.Verify(x => x.Cancel(), Times.AtLeastOnce);
            simpleListener.Verify(x => x.Dispose(), Times.AtLeastOnce);

            simulators.VerifyAll();

            captureLog.Verify(x => x.StartCapture(), Times.AtLeastOnce);
            captureLog.Verify(x => x.StopCapture(), Times.AtLeastOnce);

            // When ensureCleanSimulatorState == true
            simulator.Verify(x => x.PrepareSimulator(It.IsAny <ILog> (), appName));
            simulator.Verify(x => x.KillEverything(mainLog.Object));
        }
示例#9
0
        public async Task RunOnSimulatorWithNoAvailableSimulatorTest()
        {
            devices.Setup(x => x.ConnectedDevices).Returns(mockDevices.Reverse());

            // Crash reporter
            var crashReporterFactory = new Mock <ICrashSnapshotReporterFactory> ();

            crashReporterFactory
            .Setup(x => x.Create(mainLog.Object, It.IsAny <ILogs> (), false, null))
            .Returns(snapshotReporter.Object);

            // Mock finding simulators
            simulators
            .Setup(x => x.LoadDevices(It.IsAny <ILog> (), false, false, false))
            .Returns(Task.FromResult(true));

            string simulatorLogPath = Path.Combine(Path.GetTempPath(), "simulator-logs");

            simulators
            .Setup(x => x.FindSimulators(TestTarget.Simulator_tvOS, mainLog.Object, true, false))
            .ReturnsAsync((null, null));

            var listenerLogFile = new Mock <IFileBackedLog> ();

            logs
            .Setup(x => x.Create(It.IsAny <string> (), "TestLog", It.IsAny <bool> ()))
            .Returns(listenerLogFile.Object);

            simpleListener.SetupGet(x => x.ConnectedTask).Returns(Task.FromResult(true));

            var captureLog = new Mock <ICaptureLog> ();

            captureLog.SetupGet(x => x.FullPath).Returns(simulatorLogPath);

            var captureLogFactory = new Mock <ICaptureLogFactory> ();

            captureLogFactory
            .Setup(x => x.Create(
                       Path.Combine(logs.Object.Directory, "tvos.log"),
                       "/path/to/simulator.log",
                       true,
                       It.IsAny <string> ()))
            .Returns(captureLog.Object);
            var testReporterFactory = new Mock <ITestReporterFactory> ();
            var testReporter        = new Mock <ITestReporter> ();

            testReporterFactory.SetReturnsDefault(testReporter.Object);
            testReporter.Setup(r => r.ParseResult()).Returns(() => {
                return(Task.FromResult <(TestExecutingResult, string)> ((TestExecutingResult.Succeeded, null)));
            });
            testReporter.Setup(r => r.Success).Returns(true);

            // Act
            var appRunner = new AppRunner(processManager.Object,
                                          appBundleInformationParser,
                                          simulatorsFactory,
                                          listenerFactory,
                                          devicesFactory,
                                          crashReporterFactory.Object,
                                          captureLogFactory.Object,
                                          Mock.Of <IDeviceLogCapturerFactory> (),
                                          testReporterFactory.Object,
                                          TestTarget.Simulator_tvOS,
                                          GetMockedHarness(),
                                          mainLog.Object,
                                          logs.Object,
                                          projectFilePath: projectFilePath,
                                          buildConfiguration: "Debug",
                                          timeoutMultiplier: 2);

            appRunner.InitializeAsync().Wait();

            var result = await appRunner.RunAsync();

            // Verify
            Assert.AreEqual(1, result);

            mainLog.Verify(x => x.WriteLine("Test run completed"), Times.Never);

            simpleListener.Verify(x => x.InitializeAndGetPort(), Times.AtLeastOnce);
            simpleListener.Verify(x => x.StartAsync(), Times.AtLeastOnce);

            simulators.VerifyAll();
        }
示例#10
0
        public async Task RunTestAsync()
        {
            mainLog.WriteLine("Running '{0}' on device (candidates: '{1}')", testTask.ProjectFile, string.Join("', '", testTask.Candidates.Select((v) => v.Name).ToArray()));

            var uninstall_log = testTask.Logs.Create($"uninstall-{Helpers.Timestamp}.log", "Uninstall log");

            using (var device_resource = await testTask.NotifyBlockingWaitAsync(resourceManager.GetDeviceResources(testTask.Candidates).AcquireAnyConcurrentAsync())) {
                try {
                    // Set the device we acquired.
                    testTask.Device = testTask.Candidates.First((d) => d.UDID == device_resource.Resource.Name);
                    if (testTask.Device.DevicePlatform == DevicePlatform.watchOS)
                    {
                        testTask.CompanionDevice = await devices.FindCompanionDevice(deviceLoadLog, testTask.Device);
                    }
                    mainLog.WriteLine("Acquired device '{0}' for '{1}'", testTask.Device.Name, testTask.ProjectFile);

                    ITunnelBore tunnelBore = null;
                    if (useTcpTunnel && testTask.Device.DevicePlatform != DevicePlatform.iOS &&
                        testTask.Device.DevicePlatform != DevicePlatform.tvOS)
                    {
                        mainLog.WriteLine("Ignoring request to use a tunnel because it is not supported by the specified platform");
                    }
                    else if (useTcpTunnel && (testTask.Device.DevicePlatform == DevicePlatform.iOS ||
                                              testTask.Device.DevicePlatform == DevicePlatform.tvOS))
                    {
                        tunnelBore = testTask.TunnelBore;
                        mainLog.WriteLine("Using tunnel to communicate with device.");
                    }
                    testTask.Runner = new AppRunner(testTask.ProcessManager,
                                                    new AppBundleInformationParser(),
                                                    new SimulatorLoaderFactory(testTask.ProcessManager),
                                                    new SimpleListenerFactory(tunnelBore),
                                                    new DeviceLoaderFactory(testTask.ProcessManager),
                                                    new CrashSnapshotReporterFactory(testTask.ProcessManager),
                                                    new CaptureLogFactory(),
                                                    new DeviceLogCapturerFactory(testTask.ProcessManager),
                                                    new TestReporterFactory(testTask.ProcessManager),
                                                    testTask.AppRunnerTarget,
                                                    testTask.Harness,
                                                    projectFilePath: testTask.ProjectFile,
                                                    mainLog: uninstall_log,
                                                    logs: new Logs(testTask.LogDirectory ?? defaultLogDirectory),
                                                    buildConfiguration: testTask.ProjectConfiguration,
                                                    deviceName: testTask.Device.Name,
                                                    companionDeviceName: testTask.CompanionDevice?.Name,
                                                    timeoutMultiplier: testTask.TimeoutMultiplier,
                                                    variation: testTask.Variation,
                                                    buildTask: testTask.BuildTask);
                    await testTask.Runner.InitializeAsync();

                    // Sometimes devices can't upgrade (depending on what has changed), so make sure to uninstall any existing apps first.
                    if (uninstallTestApp)
                    {
                        testTask.Runner.MainLog = uninstall_log;
                        var uninstall_result = await testTask.Runner.UninstallAsync();

                        if (!uninstall_result.Succeeded)
                        {
                            mainLog.WriteLine($"Pre-run uninstall failed, exit code: {uninstall_result.ExitCode} (this hopefully won't affect the test result)");
                        }
                    }
                    else
                    {
                        uninstall_log.WriteLine($"Pre-run uninstall skipped.");
                    }

                    if (!testTask.Failed)
                    {
                        // Install the app
                        InstallLog = new AppInstallMonitorLog(testTask.Logs.Create($"install-{Helpers.Timestamp}.log", "Install log"));
                        try {
                            testTask.Runner.MainLog = this.InstallLog;
                            var install_result = await testTask.Runner.InstallAsync(InstallLog.CancellationToken);

                            if (!install_result.Succeeded)
                            {
                                testTask.FailureMessage  = $"Install failed, exit code: {install_result.ExitCode}.";
                                testTask.ExecutionResult = TestExecutingResult.Failed;
                                if (generateXmlFailures)
                                {
                                    resultParser.GenerateFailure(
                                        testTask.Logs,
                                        "install",
                                        testTask.Runner.AppInformation.AppName,
                                        testTask.Variation,
                                        $"AppInstallation on {testTask.Device.Name}",
                                        $"Install failed on {testTask.Device.Name}, exit code: {install_result.ExitCode}",
                                        InstallLog.FullPath,
                                        xmlResultJargon);
                                }
                            }
                        } finally {
                            InstallLog.Dispose();
                            InstallLog = null;
                        }
                    }

                    if (!testTask.Failed)
                    {
                        // Run the app
                        testTask.Runner.MainLog = testTask.Logs.Create($"run-{testTask.Device.UDID}-{Helpers.Timestamp}.log", "Run log");
                        await testTask.Runner.RunAsync();

                        if (!string.IsNullOrEmpty(testTask.Runner.FailureMessage))
                        {
                            testTask.FailureMessage = testTask.Runner.FailureMessage;
                        }
                        else if (testTask.Runner.Result != TestExecutingResult.Succeeded)
                        {
                            testTask.FailureMessage = testTask.GuessFailureReason(testTask.Runner.MainLog);
                        }

                        if (string.IsNullOrEmpty(testTask.FailureMessage) && errorKnowledgeBase.IsKnownTestIssue(testTask.Runner.MainLog, out var failure))
                        {
                            testTask.KnownFailure = failure;
                            mainLog.WriteLine($"Test run has a known failure: '{testTask.KnownFailure}'");
                        }

                        if (testTask.Runner.Result == TestExecutingResult.Succeeded && testTask.Platform == TestPlatform.iOS_TodayExtension64)
                        {
                            // For the today extension, the main app is just a single test.
                            // This is because running the today extension will not wake up the device,
                            // nor will it close & reopen the today app (but launching the main app
                            // will do both of these things, preparing the device for launching the today extension).

                            AppRunner todayRunner = new AppRunner(testTask.ProcessManager,
                                                                  new AppBundleInformationParser(),
                                                                  new SimulatorLoaderFactory(testTask.ProcessManager),
                                                                  new SimpleListenerFactory(tunnelBore),
                                                                  new DeviceLoaderFactory(testTask.ProcessManager),
                                                                  new CrashSnapshotReporterFactory(testTask.ProcessManager),
                                                                  new CaptureLogFactory(),
                                                                  new DeviceLogCapturerFactory(testTask.ProcessManager),
                                                                  new TestReporterFactory(testTask.ProcessManager),
                                                                  testTask.AppRunnerTarget,
                                                                  testTask.Harness,
                                                                  projectFilePath: testTask.ProjectFile,
                                                                  mainLog: testTask.Logs.Create($"extension-run-{testTask.Device.UDID}-{Helpers.Timestamp}.log", "Extension run log"),
                                                                  logs: new Logs(testTask.LogDirectory ?? defaultLogDirectory),
                                                                  buildConfiguration: testTask.ProjectConfiguration,
                                                                  deviceName: testTask.Device.Name,
                                                                  companionDeviceName: testTask.CompanionDevice?.Name,
                                                                  timeoutMultiplier: testTask.TimeoutMultiplier,
                                                                  variation: testTask.Variation,
                                                                  buildTask: testTask.BuildTask);
                            await todayRunner.InitializeAsync();

                            testTask.AdditionalRunner = todayRunner;
                            await todayRunner.RunAsync();

                            foreach (var log in todayRunner.Logs.Where((v) => !v.Description.StartsWith("Extension ", StringComparison.Ordinal)))
                            {
                                log.Description = "Extension " + log.Description [0].ToString().ToLower() + log.Description.Substring(1);
                            }
                            testTask.ExecutionResult = todayRunner.Result;

                            if (!string.IsNullOrEmpty(todayRunner.FailureMessage))
                            {
                                testTask.FailureMessage = todayRunner.FailureMessage;
                            }
                        }
                        else
                        {
                            testTask.ExecutionResult = testTask.Runner.Result;
                        }
                    }
                } finally {
                    // Uninstall again, so that we don't leave junk behind and fill up the device.
                    if (uninstallTestApp)
                    {
                        testTask.Runner.MainLog = uninstall_log;
                        var uninstall_result = await testTask.Runner.UninstallAsync();

                        if (!uninstall_result.Succeeded)
                        {
                            mainLog.WriteLine($"Post-run uninstall failed, exit code: {uninstall_result.ExitCode} (this won't affect the test result)");
                        }
                    }
                    else
                    {
                        uninstall_log.WriteLine($"Post-run uninstall skipped.");
                    }

                    // Also clean up after us locally.
                    if (inCI || cleanSuccessfulTestRuns && testTask.Succeeded)
                    {
                        await testTask.BuildTask.CleanAsync();
                    }
                }
            }
        }