Esempio n. 1
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");

            Assert.AreEqual(appName, appRunner.AppInformation.AppName);
            Assert.AreEqual(appPath, appRunner.AppInformation.AppPath);
            Assert.AreEqual(appPath, appRunner.AppInformation.LaunchAppPath);
            Assert.AreEqual(appName, appRunner.AppInformation.BundleIdentifier);
        }
Esempio n. 2
0
        public static int Main(string[] args)
        {
            var serviceProvider =
                new ServiceCollection()
                .AddSingleton <IConfigurationService, ConfigurationService>()
                .AddSingleton <App>()
                .AddSingleton <App.Apps>()
                .AddSingleton <App.Backup>()
                .AddSingleton <App.Config>()
                .AddSingleton <App.Content>()
                .AddSingleton <App.Schemas>()
                .AddSingleton <App.Sync>()
                .AddSingleton <App.Twitter>()
                .AddSingleton <ISynchronizer, AppSynchronizer>()
                .AddSingleton <ISynchronizer, ContentsSynchronizer>()
                .AddSingleton <ISynchronizer, RulesSynchronizer>()
                .AddSingleton <ISynchronizer, SchemasSynchronizer>()
                .AddSingleton <ISynchronizer, WorkflowsSynchronizer>()
                .AddSingleton <ILogger, ConsoleLogger>()
                .AddSingleton <Synchronizer>()
                .BuildServiceProvider();

            try
            {
                var appRunner =
                    new AppRunner <App>()
                    .UseMicrosoftDependencyInjection(serviceProvider)
                    .UseFluentValidation(true);

                return(appRunner.Run(args));
            }
            catch (Exception ex)
            {
                Console.WriteLine("ERROR: {0}", ex.Message);
                return(-1);
            }
        }
        public static AppRunner Expect <TEx>(this AppRunner appRunner,
                                             Action <TEx>?assertEx = null)
            where TEx : Exception
        {
            const int exceptionAsserted = int.MinValue + 1234;

            return(appRunner
                   .UseErrorHandler((ctx, ex) =>
            {
                if (ex is not TEx)
                {
                    throw new AssertFailedException($"exception {ex.GetType()} is not of type {typeof(TEx)}");
                }
                assertEx?.Invoke((TEx)ex);
                return exceptionAsserted;
            })
                   .AssertAfterRun(r =>
            {
                if (r.ExitCode != exceptionAsserted)
                {
                    throw new AssertFailedException($"exception {typeof(TEx)} was not raised in the test run. {nameof(appRunner.UseErrorHandler)}");
                }
            }));
        }
Esempio n. 4
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");

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

            Assert.ThrowsAsync <NoDeviceFoundException> (
                async() => await appRunner.InstallAsync(new CancellationToken()),
                "Install requires connected devices");
        }
Esempio n. 5
0
        internal static AppRunner UsePrompting(
            AppRunner appRunner,
            Func <CommandContext, IPrompter> prompterOverride = null,
            bool promptForMissingArguments = true,
            Func <CommandContext, IArgument, string> argumentPromptTextOverride = null,
            Predicate <IArgument> argumentFilter = null)
        {
            return(appRunner.Configure(c =>
            {
                prompterOverride = prompterOverride
                                   ?? c.Services.Get <Func <CommandContext, IPrompter> >()
                                   ?? (ctx =>
                {
                    // create only one prompter per CommandContext
                    // in theory, an implementation could track prompts and values
                    var prompter = ctx.Services.Get <IPrompter>();
                    if (prompter == null)
                    {
                        prompter = new Prompter(ctx.Console);
                        ctx.Services.AddOrUpdate(prompter);
                    }
                    return prompter;
                });

                c.UseParameterResolver(ctx => prompterOverride(ctx));

                if (promptForMissingArguments)
                {
                    argumentFilter = argumentFilter ?? (a => a.Arity.RequiresAtLeastOne());
                    c.UseMiddleware(
                        (ctx, next) => PromptForMissingArguments(ctx, next,
                                                                 new ArgumentPrompter(prompterOverride(ctx), argumentPromptTextOverride), argumentFilter),
                        MiddlewareSteps.ValuePromptMissingArguments);
                }
            }));
        }
            /// <summary>
            /// The delegate to return the value for the <see cref="IArgument"/>
            /// </summary>
            /// <param name="appRunner"></param>
            /// <param name="envVars">A dictionary containing the environment variables</param>
            /// <param name="getKeysDelegates">
            ///     The delegates that will return the keys for an argument.
            ///     Uses <see cref="GetKeyFromAttribute"/> if none provided.
            /// </param>
            public static Func <IArgument, ArgumentDefault?> GetDefaultValue(
                AppRunner appRunner, IDictionary?envVars = null,
                params GetArgumentKeysDelegate[]?getKeysDelegates)
            {
                IDictionary?ev = envVars;

                // AppConfig should never be null by the time this runs
                IDictionary GetEnvVars()
                {
                    if (appRunner.AppConfig is null)
                    {
                        throw new InvalidConfigurationException(
                                  "AppRunner.AppConfig is null. This method has been called before AppRunner.Run was executed.");
                    }
                    return(ev ??= appRunner.AppConfig !.Environment.GetEnvironmentVariables());
                }

                return(GetValueFunc(Resources.A.ValueSource_EnvVar,
                                    key => GetEnvVars().GetValueOrDefault <string>(key),
                                    getKeysDelegates ?? new GetArgumentKeysDelegate[]
                {
                    GetKeyFromAttribute
                }));
            }
Esempio n. 7
0
        public void List_ByDefault_ReturnsSuccess()
        {
            var mock = new Mock <IConnectorManager>();

            mock.Setup(m => m.Verify(It.IsAny <CancellationToken>()))
            .ReturnsAsync(true);

            mock.Setup(m => m.List(It.IsAny <string>()))
            .Returns(
                () => new List <(string name, ConnectorData data)>
            {
                ("Connector", new ConnectorData(new ConnectorSettings(), null))
            }
                );

            var sp = GetDefaultServiceProvider(mock.Object);

            var result = new AppRunner <EDRMethods>()
                         .UseMicrosoftDependencyInjection(sp)
                         .UseDefaultMiddleware()
                         .RunInMem("connector list filter");

            result.ExitCode.Should().Be(0);
        }
Esempio n. 8
0
        public MainWindow()
        {
            InitializeComponent();

            var scopeBuilder = new SkitanaScopeBuilder();

            scopeBuilder.RegisterSkiaRenderer()
            .RegisterWpfInputServices(skiaElement)
            .RegisterSkitanaAppFramework()
            .RegisterAllControlsServices()
            .RegisterSingleton(skiaElement);

            var services = scopeBuilder.Build();

            onCloseDisposable = services as IDisposable;

            var factory = (IIoCFactory)services.GetService(typeof(IIoCFactory));

            appRunner = factory.Create <WpfAppRunner>();
            var app = factory.Create <AllControlsApp>();

            cancellationTokenSource = new CancellationTokenSource();
            appLoopTask             = appRunner.RunAsync(app, cancellationTokenSource.Token);
        }
Esempio n. 9
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");
        }
 internal static AppRunner AppendPipedInputToOperandList(AppRunner appRunner)
 {
     // -1 to ensure this middleware runs before any prompting so the value won't appear null
     return(appRunner.Configure(c =>
                                c.UseMiddleware(InjectPipedInputToOperandList, MiddlewareSteps.PipedInput)));
 }
 public static AppRunner TrackingInvocations(this AppRunner runner)
 {
     return(runner.Configure(c => c.UseMiddleware(TrackingInvocationMiddleware,
                                                  new MiddlewareStep(MiddlewareStages.PostBindValuesPreInvoke, short.MaxValue))));
 }
 public void SubcommandsCannotFollowOperandsOfDefaultCommand()
 {
     var results = new AppRunner <DefaultApp>()
                   .Verify(new Scenario
     {
         When = { Args = "1 Do" },
 private static void PrintContext(AppRunner appRunner, ILogger logger)
 {
     logger.WriteLine("");
     logger.WriteLine(appRunner.ToString());
 }
 /// <summary>Run and verify the scenario expectations, output results to <see cref="Console"/></summary>
 public static AppRunnerResult VerifyScenario(this AppRunner appRunner, IScenario scenario)
 {
     return(appRunner.VerifyScenario(new Logger(Console.WriteLine), scenario));
 }
Esempio n. 15
0
        public async Task RunOnDeviceSuccessfullyTest()
        {
            var deviceSystemLog = new Mock <IFileBackedLog>();

            deviceSystemLog.SetupGet(x => x.FullPath).Returns(Path.GetTempFileName());
            deviceSystemLog.SetupGet(x => x.Description).Returns(LogType.SystemLog.ToString());

            SetupLogList(new[] { deviceSystemLog.Object });
            _logs
            .Setup(x => x.Create("device-" + DeviceName + "-mocked_timestamp.log", LogType.SystemLog.ToString(), It.IsAny <bool?>()))
            .Returns(deviceSystemLog.Object);

            var deviceLogCapturer = new Mock <IDeviceLogCapturer>();

            var deviceLogCapturerFactory = new Mock <IDeviceLogCapturerFactory>();

            deviceLogCapturerFactory
            .Setup(x => x.Create(_mainLog.Object, deviceSystemLog.Object, DeviceName))
            .Returns(deviceLogCapturer.Object);

            var x = _logs.Object.First();

            var appInformation = GetMockedAppBundleInfo();

            // Act
            var appRunner = new AppRunner(_processManager.Object,
                                          _hardwareDeviceLoader.Object,
                                          _simulatorLoader.Object,
                                          _snapshotReporterFactory,
                                          Mock.Of <ICaptureLogFactory>(),
                                          deviceLogCapturerFactory.Object,
                                          _mainLog.Object,
                                          _logs.Object,
                                          _helpers.Object,
                                          new[] { "--appArg1=value1", "-g" });

            var(deviceName, result) = await appRunner.RunApp(
                appInformation,
                new TestTargetOs(TestTarget.Device_iOS, null),
                TimeSpan.FromSeconds(30));

            // Verify
            Assert.Equal(DeviceName, deviceName);
            Assert.True(result.Succeeded);

            var expectedArgs = GetExpectedDeviceMlaunchArgs();

            _processManager
            .Verify(
                x => x.ExecuteCommandAsync(
                    It.Is <MlaunchArguments>(args => args.AsCommandLine() == expectedArgs),
                    It.IsAny <ILog>(),
                    It.IsAny <TimeSpan>(),
                    null,
                    It.IsAny <CancellationToken>()),
                Times.Once);

            _hardwareDeviceLoader.VerifyAll();

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

            deviceSystemLog.Verify(x => x.Dispose(), Times.AtLeastOnce);
        }
Esempio n. 16
0
        public async Task RunOnSimulatorSuccessfullyTest()
        {
            _simulatorLoader
            .Setup(x => x.FindSimulators(It.Is <TestTargetOs>(t => t.Platform == TestTarget.Simulator_tvOS), _mainLog.Object, It.IsAny <int>(), true, false))
            .ReturnsAsync((_mockSimulator.Object, null));

            var captureLog = new Mock <ICaptureLog>();

            captureLog.SetupGet(x => x.FullPath).Returns(_simulatorLogPath);
            captureLog.SetupGet(x => x.Description).Returns(LogType.SystemLog.ToString());

            var captureLogFactory = new Mock <ICaptureLogFactory>();

            captureLogFactory
            .Setup(x => x.Create(
                       Path.Combine(_logs.Object.Directory, _mockSimulator.Object.Name + ".log"),
                       _mockSimulator.Object.SystemLog,
                       false,
                       LogType.SystemLog))
            .Returns(captureLog.Object);

            SetupLogList(new[] { captureLog.Object });

            var appInformation = GetMockedAppBundleInfo();

            // Act
            var appRunner = new AppRunner(_processManager.Object,
                                          _hardwareDeviceLoader.Object,
                                          _simulatorLoader.Object,
                                          _snapshotReporterFactory,
                                          captureLogFactory.Object,
                                          Mock.Of <IDeviceLogCapturerFactory>(),
                                          _mainLog.Object,
                                          _logs.Object,
                                          _helpers.Object,
                                          new[] { "--appArg1=value1", "-g" });

            var(deviceName, result) = await appRunner.RunApp(
                appInformation,
                new TestTargetOs(TestTarget.Simulator_tvOS, null),
                TimeSpan.FromSeconds(30),
                ensureCleanSimulatorState : true);

            // Verify
            Assert.Equal(SimulatorDeviceName, deviceName);
            Assert.True(result.Succeeded);

            var expectedArgs = GetExpectedSimulatorMlaunchArgs();

            _processManager
            .Verify(
                x => x.ExecuteCommandAsync(
                    It.Is <MlaunchArguments>(args => args.AsCommandLine() == expectedArgs),
                    _mainLog.Object,
                    It.IsAny <TimeSpan>(),
                    null,
                    It.IsAny <CancellationToken>()),
                Times.Once);

            _simulatorLoader.VerifyAll();

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

            // When ensureCleanSimulatorState == true
            _mockSimulator.Verify(x => x.PrepareSimulator(_mainLog.Object, AppBundleIdentifier));
            _mockSimulator.Verify(x => x.KillEverything(_mainLog.Object));
        }
Esempio n. 17
0
        static int Main(string[] args)
        {
            AppRunner <GodfatherChars> appRunner = new AppRunner <GodfatherChars>();

            return(appRunner.Run(args));
        }
Esempio n. 18
0
 public static void Start(string environmentName, string[] args, System.Drawing.Icon appIcon = null)
 {
     AppRunner.RunAsAdmin(args, new AppBootstrapper(environmentName, appIcon));
 }
Esempio n. 19
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));
        }
Esempio n. 20
0
 public static void Start(string environmentName, string[] args, Optional <Icon> appIcon)
 {
     AppRunner.RunAsAdmin(args, new AppBootstrapper(environmentName, appIcon), true);
 }
Esempio n. 21
0
 static int Main(string[] args) => AppRunner.Run(args);
Esempio n. 22
0
        protected override async Task <ExitCode> RunAppInternal(
            AppBundleInformation appBundleInfo,
            string?deviceName,
            ILogger logger,
            TestTargetOs target,
            Logs logs,
            IFileBackedLog mainLog,
            CancellationToken cancellationToken)
        {
            // only add the extra callback if we do know that the feature was indeed enabled
            Action <string>?logCallback = IsLldbEnabled() ? (l) => NotifyUserLldbCommand(logger, l) : (Action <string>?)null;

            var appRunner = new AppRunner(
                ProcessManager,
                DeviceLoader,
                SimulatorLoader,
                new CrashSnapshotReporterFactory(ProcessManager),
                new CaptureLogFactory(),
                new DeviceLogCapturerFactory(ProcessManager),
                mainLog,
                logs,
                new Helpers(),
                PassThroughArguments,
                logCallback);

            ProcessExecutionResult result;

            (deviceName, result) = await appRunner.RunApp(
                appBundleInfo,
                target,
                _arguments.Timeout,
                deviceName,
                verbosity : GetMlaunchVerbosity(_arguments.Verbosity),
                cancellationToken : cancellationToken);

            if (!result.Succeeded)
            {
                if (result.TimedOut)
                {
                    logger.LogError($"App run has timed out");
                    return(ExitCode.TIMED_OUT);
                }

                logger.LogError($"App run has failed. mlaunch exited with {result.ExitCode}");
                return(ExitCode.APP_LAUNCH_FAILURE);
            }

            var systemLog = logs.FirstOrDefault(log => log.Description == LogType.SystemLog.ToString());

            if (systemLog == null)
            {
                logger.LogError("Application has finished but no system log found. Failed to determine the exit code!");
                return(ExitCode.RETURN_CODE_NOT_SET);
            }

            var exitCode = new ExitCodeDetector().DetectExitCode(appBundleInfo, systemLog);

            logger.LogInformation($"App run ended with {exitCode}");

            if (_arguments.ExpectedExitCode != exitCode)
            {
                logger.LogError($"Application has finished with exit code {exitCode} but {_arguments.ExpectedExitCode} was expected");
                return(ExitCode.GENERAL_FAILURE);
            }

            logger.LogInformation("Application has finished with exit code: " + exitCode +
                                  (_arguments.ExpectedExitCode != 0 ? " (as expected)" : null));
            return(ExitCode.SUCCESS);
        }
Esempio n. 23
0
        private async Task <ExitCode> RunTest(
            ILogger logger,
            TestTarget target,
            Logs logs,
            ProcessManager processManager,
            IHardwareDeviceLoader deviceLoader,
            ISimulatorLoader simulatorLoader,
            ITunnelBore?tunnelBore,
            CancellationToken cancellationToken = default)
        {
            logger.LogInformation($"Starting test for {target.AsString()}{ (_arguments.DeviceName != null ? " targeting " + _arguments.DeviceName : null) }..");

            string mainLogFile = Path.Join(_arguments.OutputDirectory, $"run-{target}{(_arguments.DeviceName != null ? "-" + _arguments.DeviceName : null)}.log");
            ILog   mainLog     = logs.Create(mainLogFile, LogType.ExecutionLog.ToString(), true);
            int    verbosity   = GetMlaunchVerbosity(_arguments.Verbosity);

            string?deviceName = _arguments.DeviceName;

            var appBundleInformationParser = new AppBundleInformationParser(processManager);

            AppBundleInformation appBundleInfo;

            try
            {
                appBundleInfo = await appBundleInformationParser.ParseFromAppBundle(_arguments.AppPackagePath, target, mainLog, cancellationToken);
            }
            catch (Exception e)
            {
                logger.LogError($"Failed to get bundle information: {e.Message}");
                return(ExitCode.FAILED_TO_GET_BUNDLE_INFO);
            }

            if (!target.IsSimulator())
            {
                logger.LogInformation($"Installing application '{appBundleInfo.AppName}' on " + (deviceName != null ? $" on device '{deviceName}'" : target.AsString()));

                var appInstaller = new AppInstaller(processManager, deviceLoader, mainLog, verbosity);

                ProcessExecutionResult result;

                try
                {
                    (deviceName, result) = await appInstaller.InstallApp(_arguments.AppPackagePath, target, cancellationToken : cancellationToken);
                }
                catch (NoDeviceFoundException)
                {
                    logger.LogError($"Failed to find suitable device for target {target.AsString()}");
                    return(ExitCode.DEVICE_NOT_FOUND);
                }
                catch (Exception e)
                {
                    logger.LogError($"Failed to install the app bundle:{Environment.NewLine}{e}");
                    return(ExitCode.PACKAGE_INSTALLATION_FAILURE);
                }

                if (!result.Succeeded)
                {
                    // use the knowledge base class to decide if the error is known, if it is, let the user know
                    // the failure reason
                    if (_errorKnowledgeBase.IsKnownInstallIssue(mainLog, out var errorMessage))
                    {
                        logger.LogError($"Failed to install the app bundle (exit code={result.ExitCode}): {errorMessage}");
                    }
                    else
                    {
                        logger.LogError($"Failed to install the app bundle (exit code={result.ExitCode})");
                    }

                    return(ExitCode.PACKAGE_INSTALLATION_FAILURE);
                }

                logger.LogInformation($"Application '{appBundleInfo.AppName}' was installed successfully on device '{deviceName}'");
            }

            logger.LogInformation($"Starting application '{appBundleInfo.AppName}' on " + (deviceName != null ? $"device '{deviceName}'" : target.AsString()));

            var appRunner = new AppRunner(
                processManager,
                deviceLoader,
                simulatorLoader,
                new SimpleListenerFactory(tunnelBore),
                new CrashSnapshotReporterFactory(processManager),
                new CaptureLogFactory(),
                new DeviceLogCapturerFactory(processManager),
                new TestReporterFactory(processManager),
                mainLog,
                logs,
                new Helpers(),
                useXmlOutput: true); // the cli ALWAYS will get the output as xml

            try
            {
                string resultMessage;
                TestExecutingResult testResult;
                (deviceName, testResult, resultMessage) = await appRunner.RunApp(appBundleInfo,
                                                                                 target,
                                                                                 _arguments.Timeout,
                                                                                 _arguments.LaunchTimeout,
                                                                                 deviceName,
                                                                                 verbosity : verbosity,
                                                                                 xmlResultJargon : _arguments.XmlResultJargon,
                                                                                 cancellationToken : cancellationToken);

                switch (testResult)
                {
                case TestExecutingResult.Succeeded:
                    logger.LogInformation($"Application finished the test run successfully");
                    logger.LogInformation(resultMessage);

                    return(ExitCode.SUCCESS);

                case TestExecutingResult.Failed:
                    logger.LogInformation($"Application finished the test run successfully with some failed tests");
                    logger.LogInformation(resultMessage);

                    return(ExitCode.TESTS_FAILED);

                case TestExecutingResult.Crashed:

                    if (resultMessage != null)
                    {
                        logger.LogError($"Application run crashed:{Environment.NewLine}" +
                                        $"{resultMessage}{Environment.NewLine}{Environment.NewLine}" +
                                        $"Check logs for more information.");
                    }
                    else
                    {
                        logger.LogError($"Application run crashed. Check logs for more information");
                    }

                    return(ExitCode.APP_CRASH);

                case TestExecutingResult.TimedOut:
                    logger.LogWarning($"Application run timed out");

                    return(ExitCode.TIMED_OUT);

                default:

                    if (resultMessage != null)
                    {
                        logger.LogError($"Application run ended in an unexpected way: '{testResult}'{Environment.NewLine}" +
                                        $"{resultMessage}{Environment.NewLine}{Environment.NewLine}" +
                                        $"Check logs for more information.");
                    }
                    else
                    {
                        logger.LogError($"Application run ended in an unexpected way: '{testResult}'. Check logs for more information");
                    }

                    return(ExitCode.GENERAL_FAILURE);
                }
            }
            catch (NoDeviceFoundException)
            {
                logger.LogError($"Failed to find suitable device for target {target.AsString()}");
                return(ExitCode.DEVICE_NOT_FOUND);
            }
            catch (Exception e)
            {
                if (_errorKnowledgeBase.IsKnownTestIssue(mainLog, out var failureMessage))
                {
                    logger.LogError($"Application run failed:{Environment.NewLine}{failureMessage}");
                }
                else
                {
                    logger.LogError($"Application run failed:{Environment.NewLine}{e}");
                }

                return(ExitCode.APP_CRASH);
            }
            finally
            {
                if (!target.IsSimulator() && deviceName != null)
                {
                    logger.LogInformation($"Uninstalling the application '{appBundleInfo.AppName}' from device '{deviceName}'");

                    var appUninstaller  = new AppUninstaller(processManager, mainLog, verbosity);
                    var uninstallResult = await appUninstaller.UninstallApp(deviceName, appBundleInfo.BundleIdentifier, cancellationToken);

                    if (!uninstallResult.Succeeded)
                    {
                        logger.LogError($"Failed to uninstall the app bundle with exit code: {uninstallResult.ExitCode}");
                    }
                    else
                    {
                        logger.LogInformation($"Application '{appBundleInfo.AppName}' was uninstalled successfully");
                    }
                }
            }
        }
Esempio n. 24
0
        public void Verify <TApp>(
            string args,
            bool includeTransforms = false,
            bool showsHelp         = false,
            bool showsTokens       = false,
            bool showsUnableToMapTokensToArgsMsg        = false,
            bool showsHelpRequestOnlyTokensAvailableMsg = false,
            bool showsHintToUseParseT  = false,
            bool throwBeforeBind       = false,
            bool throwAtInvoke         = false,
            bool exitBeforeBind        = false,
            string expectedParseResult = null,
            string expectedResult      = null) where TApp : class
        {
            var parse = includeTransforms ? "[parse:t]" : "[parse]";

            var contains    = new List <string>();
            var notContains = new List <string>();

            void containsIf(bool yes, string value)
            {
                (yes ? contains : notContains).Add(value);
            }

            args = args ?? "";

            containsIf(showsHelp, "Usage: dotnet testhost.dll ");
            containsIf(args == "-h" || args.Contains(" -h") || args.Contains("-h "),
                       "Help requested. Only token transformations are available.");
            containsIf(showsTokens, @"token transformations:

>>> from shell");

            if (expectedParseResult != null)
            {
                contains.Add(expectedParseResult);
            }

            containsIf(showsUnableToMapTokensToArgsMsg,
                       "Unable to map tokens to arguments. Falling back to token transformations.");

            containsIf(showsHelpRequestOnlyTokensAvailableMsg,
                       "Help requested. Only token transformations are available.");

            containsIf(showsHintToUseParseT,
                       "Use [parse:T] to include token transformations");

            var appRunner = new AppRunner <TApp>();

            if (exitBeforeBind)
            {
                appRunner.Configure(c =>
                                    c.UseMiddleware((ctx, next) => ExitCodes.Success,
                                                    MiddlewareStages.PostParseInputPreBindValues));
            }

            if (throwBeforeBind)
            {
                appRunner.Configure(c =>
                                    c.UseMiddleware((ctx, next) =>
                                                    throw new Exception("throwBeforeBind exception"),
                                                    MiddlewareStages.PostParseInputPreBindValues));
            }

            if (throwAtInvoke)
            {
                appRunner.Configure(c =>
                                    c.UseMiddleware((ctx, next) =>
                                                    throw new Exception("throwAtInvoke exception"),
                                                    MiddlewareStages.Invoke, int.MinValue));
            }

            appRunner.Configure(c =>
                                c.UseMiddleware((ctx, next) =>
                                                throw new Exception("parse should exit before method invocation"),
                                                MiddlewareStages.Invoke, int.MaxValue - 1));

            var result = appRunner
                         .UseParseDirective()
                         .Verify(new Scenario
            {
                When = { Args = $"{parse} {args}" },
                Then =
                {
                    ExitCode               = throwBeforeBind ? 1 : 0,
                    Output                 = expectedResult,
                    OutputContainsTexts    = contains,
                    OutputNotContainsTexts = notContains
                }
            });

            if (showsHelp && showsTokens)
            {
                // help before token transformations
                var consoleOut  = result.Console.OutText();
                var helpIndex   = consoleOut.IndexOf("Usage: dotnet testhost.dll ");
                var tokensIndex = consoleOut.IndexOf("token transformations:");
                tokensIndex.Should().BeLessThan(helpIndex);
            }
        }
Esempio n. 25
0
 public static void VerifyScenario(this AppRunner appRunner, ITestOutputHelper output, IScenario scenario)
 {
     appRunner.VerifyScenario(output.AsLogger(), scenario);
 }
Esempio n. 26
0
        public void LongName_ShowsHelp()
        {
            var result = new AppRunner <App>().RunInMem("--help".SplitArgs());

            result.CommandContext.ShowHelpOnExit.Should().BeTrue();
        }
Esempio n. 27
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();
        }
        public void LongName_ShowsHelp()
        {
            var result = new AppRunner <App>().RunInMem("--help".SplitArgs(), _testOutputHelper);

            result.OutputContains("Usage: dotnet testhost.dll [command]");
        }
Esempio n. 29
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);
        }
Esempio n. 30
0
        public async Task MultiProcessIdentificationTest(DiagnosticPortConnectionMode mode)
        {
            DiagnosticPortHelper.Generate(
                mode,
                out DiagnosticPortConnectionMode appConnectionMode,
                out string diagnosticPortPath);

            await using MonitorCollectRunner toolRunner = new(_outputHelper);
            toolRunner.ConnectionMode        = mode;
            toolRunner.DiagnosticPortPath    = diagnosticPortPath;
            toolRunner.DisableAuthentication = true;
            await toolRunner.StartAsync();

            using HttpClient httpClient = await toolRunner.CreateHttpClientDefaultAddressAsync(_httpClientFactory);

            ApiClient apiClient = new(_outputHelper, httpClient);

            const int appCount = 3;

            AppRunner[] appRunners = new AppRunner[appCount];

            for (int i = 0; i < appCount; i++)
            {
                AppRunner runner = new(_outputHelper, Assembly.GetExecutingAssembly(), appId : i + 1);
                runner.ConnectionMode     = appConnectionMode;
                runner.DiagnosticPortPath = diagnosticPortPath;
                runner.ScenarioName       = TestAppScenarios.AsyncWait.Name;
                runner.Environment[ExpectedEnvVarName] = Guid.NewGuid().ToString("D");
                appRunners[i] = runner;
            }

            IList <ProcessIdentifier> identifiers;
            await appRunners.ExecuteAsync(async() =>
            {
                // Scope to only the processes that were launched by the test
                IList <int> unmatchedPids = new List <int>();
                foreach (AppRunner runner in appRunners)
                {
                    unmatchedPids.Add(await runner.ProcessIdTask);
                }

                // Query for process identifiers
                identifiers = (await apiClient.GetProcessesWithRetryAsync(
                                   _outputHelper,
                                   unmatchedPids.ToArray())).ToList();
                Assert.NotNull(identifiers);

                _outputHelper.WriteLine("Start enumerating discovered processes.");
                foreach (ProcessIdentifier identifier in identifiers.ToList())
                {
                    _outputHelper.WriteLine($"- PID:  {identifier.Pid}");
                    _outputHelper.WriteLine($"  UID:  {identifier.Uid}");
                    _outputHelper.WriteLine($"  Name: {identifier.Name}");

                    unmatchedPids.Remove(identifier.Pid);
                }
                _outputHelper.WriteLine("End enumerating discovered processes");

                Assert.Empty(unmatchedPids);
                Assert.Equal(appRunners.Length, identifiers.Count);

                foreach (ProcessIdentifier processIdentifier in identifiers)
                {
                    int pid     = processIdentifier.Pid;
                    Guid uid    = processIdentifier.Uid;
                    string name = processIdentifier.Name;
#if NET5_0_OR_GREATER
                    // CHECK 1: Get response for processes using PID, UID, and Name and check for consistency

                    List <ProcessInfo> processInfoQueriesCheck1 = new List <ProcessInfo>();

                    processInfoQueriesCheck1.Add(await apiClient.GetProcessWithRetryAsync(_outputHelper, pid: pid));
                    // Only check with uid if it is non-empty; this can happen in connect mode if the ProcessInfo command fails
                    // to respond within the short period of time that is used to get the additional process information.
                    if (uid == Guid.Empty)
                    {
                        _outputHelper.WriteLine("Skipped uid-only check because it is empty GUID.");
                    }
                    else
                    {
                        processInfoQueriesCheck1.Add(await apiClient.GetProcessWithRetryAsync(_outputHelper, uid: uid));
                    }

                    VerifyProcessInfoEquality(processInfoQueriesCheck1);
#endif
                    // CHECK 2: Get response for requests using PID | PID and UID | PID, UID, and Name and check for consistency

                    List <ProcessInfo> processInfoQueriesCheck2 = new List <ProcessInfo>();

                    processInfoQueriesCheck2.Add(await apiClient.GetProcessWithRetryAsync(_outputHelper, pid: pid));
                    processInfoQueriesCheck2.Add(await apiClient.GetProcessWithRetryAsync(_outputHelper, pid: pid, uid: uid));
                    processInfoQueriesCheck2.Add(await apiClient.GetProcessWithRetryAsync(_outputHelper, pid: pid, uid: uid, name: name));

                    VerifyProcessInfoEquality(processInfoQueriesCheck2);

                    // CHECK 3: Get response for processes using PID and an unassociated (randomly generated) UID and ensure the proper exception is thrown

                    await VerifyInvalidRequestException(apiClient, pid, Guid.NewGuid(), null);
                }

                // CHECK 4: Get response for processes using invalid PID, UID, or Name and ensure the proper exception is thrown

                await VerifyInvalidRequestException(apiClient, -1, null, null);
                await VerifyInvalidRequestException(apiClient, null, Guid.NewGuid(), null);
                await VerifyInvalidRequestException(apiClient, null, null, "");

                // Verify each app instance is reported and shut them down.
                foreach (AppRunner runner in appRunners)
                {
                    Assert.True(runner.Environment.TryGetValue(ExpectedEnvVarName, out string expectedEnvVarValue));

                    await VerifyProcessAsync(apiClient, identifiers, await runner.ProcessIdTask, expectedEnvVarValue);

                    await runner.SendCommandAsync(TestAppScenarios.AsyncWait.Commands.Continue);
                }
            });

            for (int i = 0; i < appCount; i++)
            {
                Assert.True(0 == appRunners[i].ExitCode, $"App {i} exit code is non-zero.");
            }

            // Query for process identifiers
            identifiers = (await apiClient.GetProcessesAsync()).ToList();
            Assert.NotNull(identifiers);

            // Verify none of the apps are reported
            List <int> runnerProcessIds = new(appCount);

            for (int i = 0; i < appCount; i++)
            {
                runnerProcessIds.Add(await appRunners[i].ProcessIdTask);
            }

            foreach (ProcessIdentifier identifier in identifiers)
            {
                Assert.DoesNotContain(identifier.Pid, runnerProcessIds);
            }
        }