Example #1
0
        public async Task RunTestAsync()
        {
            mainLog.WriteLine("Running XI on '{0}' ({2}) for {1}", testTask.Device?.Name, testTask.ProjectFile, testTask.Device?.UDID);

            testTask.ExecutionResult = testTask.ExecutionResult & ~TestExecutingResult.InProgressMask | TestExecutingResult.Running;
            await testTask.BuildTask.RunAsync();

            if (!testTask.BuildTask.Succeeded)
            {
                testTask.ExecutionResult = TestExecutingResult.BuildFailure;
                return;
            }
            using (var resource = await testTask.NotifyBlockingWaitAsync(testTask.AcquireResourceAsync())) {
                if (testTask.Runner == null)
                {
                    await SelectSimulatorAsync();
                }
                await testTask.Runner.RunAsync();
            }
            testTask.ExecutionResult = testTask.Runner.Result;

            testTask.KnownFailure = null;
            if (errorKnowledgeBase.IsKnownTestIssue(testTask.Runner.MainLog, out var failure))
            {
                testTask.KnownFailure = failure;
                mainLog.WriteLine($"Test run has a known failure: '{testTask.KnownFailure}'");
            }
        }
Example #2
0
    private async Task <ExitCode> OrchestrateOperationInternal(
        TestTargetOs target,
        string?deviceName,
        bool includeWirelessDevices,
        bool resetSimulator,
        bool enableLldb,
        GetAppBundleInfoFunc getAppBundle,
        ExecuteMacCatalystAppFunc executeMacCatalystApp,
        ExecuteAppFunc executeApp,
        CancellationToken cancellationToken)
    {
        _lldbFileCreated = false;
        var isLldbEnabled = IsLldbEnabled();

        if (isLldbEnabled && !enableLldb)
        {
            // the file is present, but the user did not set it, warn him about it
            _logger.LogWarning("Lldb will be used since '~/.mtouch-launch-with-lldb' was found in the system but it was not created by xharness.");
        }
        else if (enableLldb)
        {
            if (!File.Exists(s_mlaunchLldbConfigFile))
            {
                // create empty file
                File.WriteAllText(s_mlaunchLldbConfigFile, string.Empty);
                _lldbFileCreated = true;
            }
        }

        if (includeWirelessDevices && target.Platform.IsSimulator())
        {
            _logger.LogWarning("Including wireless devices while targeting a simulator has no effect");
        }

        if (resetSimulator && !target.Platform.IsSimulator())
        {
            _logger.LogWarning("Targeting device but requesting simulator reset has no effect");
            resetSimulator = false;
        }

        ExitCode             exitCode;
        IDevice              device;
        IDevice?             companionDevice;
        AppBundleInformation appBundleInfo;

        if (target.Platform == TestTarget.MacCatalyst)
        {
            try
            {
                appBundleInfo = await getAppBundle(target, null !, cancellationToken);
            }
            catch (Exception e)
            {
                cancellationToken.ThrowIfCancellationRequested();
                _logger.LogError(e.Message);
                return(ExitCode.PACKAGE_NOT_FOUND);
            }

            try
            {
                return(await executeMacCatalystApp(appBundleInfo));
            }
            catch (Exception e)
            {
                var message = new StringBuilder().AppendLine("Application run failed:");
                exitCode = ExitCode.APP_LAUNCH_FAILURE;

                if (_errorKnowledgeBase.IsKnownTestIssue(_mainLog, out var failure))
                {
                    message.Append(failure.HumanMessage);
                    if (failure.IssueLink != null)
                    {
                        message.AppendLine($" Find more information at {failure.IssueLink}");
                    }

                    if (failure.SuggestedExitCode.HasValue)
                    {
                        exitCode = (ExitCode)failure.SuggestedExitCode.Value;
                    }
                }
                else
                {
                    message.AppendLine(e.ToString());
                }

                _logger.LogError(message.ToString());

                return(exitCode);
            }
        }

        try
        {
            _logger.LogInformation($"Looking for available {target.AsString()} {(target.Platform.IsSimulator() ? "simulators" : "devices")}..");

            var finderLogName = $"list-{target.AsString()}-{_helpers.Timestamp}.log";
            using var finderLog = _logs.Create(finderLogName, "DeviceList", true);

            _mainLog.WriteLine(
                $"Looking for available {target.AsString()} {(target.Platform.IsSimulator() ? "simulators" : "devices")}. " +
                $"Storing logs into {finderLogName}");

            (device, companionDevice) = await _deviceFinder.FindDevice(target, deviceName, finderLog, includeWirelessDevices, cancellationToken);

            _logger.LogInformation($"Found {(target.Platform.IsSimulator() ? "simulator" : "physical")} device '{device.Name}'");

            if (companionDevice != null)
            {
                _logger.LogInformation($"Found companion {(target.Platform.IsSimulator() ? "simulator" : "physical")} device '{companionDevice.Name}'");
            }
        }
        catch (NoDeviceFoundException e)
        {
            _logger.LogError(e.Message);
            return(ExitCode.DEVICE_NOT_FOUND);
        }

        cancellationToken.ThrowIfCancellationRequested();

        try
        {
            appBundleInfo = await getAppBundle(target, device, cancellationToken);
        }
        catch (Exception e)
        {
            cancellationToken.ThrowIfCancellationRequested();
            _logger.LogError(e.Message);
            return(ExitCode.PACKAGE_NOT_FOUND);
        }

        cancellationToken.ThrowIfCancellationRequested();

        if (target.Platform.IsSimulator() && resetSimulator)
        {
            try
            {
                var simulator = (ISimulatorDevice)device;
                var bundleIds = appBundleInfo.BundleIdentifier == string.Empty ? Array.Empty <string>() : new[] { appBundleInfo.BundleIdentifier };

                _logger.LogInformation($"Reseting simulator '{device.Name}'");
                await simulator.PrepareSimulator(_mainLog, bundleIds);

                if (companionDevice != null)
                {
                    _logger.LogInformation($"Reseting companion simulator '{companionDevice.Name}'");
                    var companionSimulator = (ISimulatorDevice)companionDevice;
                    await companionSimulator.PrepareSimulator(_mainLog, bundleIds);
                }

                _logger.LogInformation("Simulator reset finished");
            }
            catch (Exception e)
            {
                _logger.LogError($"Failed to reset simulator: " + Environment.NewLine + e);
                return(ExitCode.SIMULATOR_FAILURE);
            }
        }

        cancellationToken.ThrowIfCancellationRequested();

        // Note down the actual test target
        // For simulators (e.g. "iOS 13.4"), we strip the iOS part and keep the version only, for devices there's no OS
        _diagnosticsData.TargetOS = target.Platform.IsSimulator() ? device.OSVersion.Split(' ', 2).Last() : device.OSVersion;
        _diagnosticsData.Device   = device.Name ?? device.UDID;

        // Uninstall the app first to get a clean state
        if (!resetSimulator)
        {
            await UninstallApp(target.Platform, appBundleInfo.BundleIdentifier, device, isPreparation : true, cancellationToken);

            cancellationToken.ThrowIfCancellationRequested();
        }

        exitCode = await InstallApp(appBundleInfo, device, target, cancellationToken);

        if (exitCode != ExitCode.SUCCESS)
        {
            _logger.LogInformation($"Cleaning up the failed installation from '{device.Name}'");

            var uninstallResult = await UninstallApp(target.Platform, appBundleInfo.BundleIdentifier, device, isPreparation : false, new CancellationToken());

            if (uninstallResult == ExitCode.SIMULATOR_FAILURE)
            {
                // Sometimes the simulator gets in a bad shape and we won't be able to install the app, we can tell here
                return(ExitCode.SIMULATOR_FAILURE);
            }

            return(exitCode);
        }

        try
        {
            exitCode = await executeApp(appBundleInfo, device, companionDevice);
        }
        catch (Exception e)
        {
            exitCode = ExitCode.GENERAL_FAILURE;

            var message = new StringBuilder().AppendLine("Application run failed:");

            if (_errorKnowledgeBase.IsKnownTestIssue(_mainLog, out var failure))
            {
                message.Append(failure.HumanMessage);
                if (failure.IssueLink != null)
                {
                    message.AppendLine($" Find more information at {failure.IssueLink}");
                }

                if (failure.SuggestedExitCode.HasValue)
                {
                    exitCode = (ExitCode)failure.SuggestedExitCode.Value;
                }
            }
            else
            {
                message.AppendLine(e.ToString());
            }

            _logger.LogError(message.ToString());
        }
        finally
        {
            if (target.Platform.IsSimulator() && resetSimulator)
            {
                await CleanUpSimulators(device, companionDevice);
            }
            else if (device != null) // Do not uninstall if device was cleaned up
            {
                var uninstallResult = await UninstallApp(target.Platform, appBundleInfo.BundleIdentifier, device, false, new CancellationToken());

                // We are able to detect a case when simulator is in a bad shape
                // If it also failed the test/run, we should present that as the failure
                if (uninstallResult == ExitCode.SIMULATOR_FAILURE && exitCode != ExitCode.SUCCESS && exitCode != ExitCode.TESTS_FAILED)
                {
                    exitCode = ExitCode.SIMULATOR_FAILURE;
                }
            }
        }

        return(exitCode);
    }
Example #3
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();
                    }
                }
            }
        }