예제 #1
0
    public async Task <ProcessExecutionResult> InstallApp(
        AppBundleInformation appBundleInformation,
        TestTargetOs target,
        IDevice device,
        CancellationToken cancellationToken = default)
    {
        if (!Directory.Exists(appBundleInformation.LaunchAppPath))
        {
            throw new DirectoryNotFoundException("Failed to find the app bundle directory");
        }

        var args = new MlaunchArguments();

        if (target.Platform.IsSimulator())
        {
            args.Add(new SimulatorUDIDArgument(device));
            args.Add(new InstallAppOnSimulatorArgument(appBundleInformation.LaunchAppPath));
        }
        else
        {
            args.Add(new DeviceNameArgument(device));
            args.Add(new InstallAppOnDeviceArgument(appBundleInformation.LaunchAppPath));

            if (target.Platform.IsWatchOSTarget())
            {
                args.Add(new DeviceArgument("ios,watchos"));
            }
        }

        var totalSize = Directory.GetFiles(appBundleInformation.LaunchAppPath, "*", SearchOption.AllDirectories).Select((v) => new FileInfo(v).Length).Sum();

        _mainLog.WriteLine($"Installing '{appBundleInformation.LaunchAppPath}' to '{device.Name}' ({totalSize / 1024.0 / 1024.0:N2} MB)");

        return(await _processManager.ExecuteCommandAsync(args, _mainLog, TimeSpan.FromMinutes(15), verbosity : 2, cancellationToken : cancellationToken));
    }
예제 #2
0
        public async Task <(string deviceName, ProcessExecutionResult result)> InstallApp(string appPath, TestTarget target, string deviceName = null, CancellationToken cancellationToken = default)
        {
            if (target.IsSimulator())
            {
                // We reset the simulator when running, so a separate install step does not make much sense.
                throw new InvalidOperationException("Installing to a simulator is not supported.");
            }

            if (!Directory.Exists(appPath))
            {
                throw new DirectoryNotFoundException("Failed to find the app bundle directory");
            }

            if (deviceName == null)
            {
                var device = await _deviceLoader.FindDevice(target.ToRunMode(), _mainLog, false, false);

                if (target.IsWatchOSTarget())
                {
                    deviceName = (await _deviceLoader.FindCompanionDevice(_mainLog, device)).Name;
                }
                else
                {
                    deviceName = device.Name;
                }
            }

            if (deviceName == null)
            {
                throw new NoDeviceFoundException();
            }

            var args = new MlaunchArguments();

            for (int i = -1; i < _verbosity; i++)
            {
                args.Add(new VerbosityArgument());
            }

            args.Add(new InstallAppOnDeviceArgument(appPath));
            args.Add(new DeviceNameArgument(deviceName));

            if (target.IsWatchOSTarget())
            {
                args.Add(new DeviceArgument("ios,watchos"));
            }

            var totalSize = Directory.GetFiles(appPath, "*", SearchOption.AllDirectories).Select((v) => new FileInfo(v).Length).Sum();

            _mainLog.WriteLine($"Installing '{appPath}' to '{deviceName}' ({totalSize / 1024.0 / 1024.0:N2} MB)");

            ProcessExecutionResult result = await _processManager.ExecuteCommandAsync(args, _mainLog, TimeSpan.FromHours(1), cancellation_token : cancellationToken);

            return(deviceName, result);
        }
예제 #3
0
        public async Task <ProcessExecutionResult> UninstallApp(string deviceName, string appBundleId, CancellationToken cancellationToken = default)
        {
            var args = new MlaunchArguments();

            for (var i = -1; i < _verbosity; i++)
            {
                args.Add(new VerbosityArgument());
            }

            args.Add(new UninstallAppFromDeviceArgument(appBundleId));
            args.Add(new DeviceNameArgument(deviceName));

            return(await _processManager.ExecuteCommandAsync(args, _mainLog, TimeSpan.FromMinutes(1), cancellationToken : cancellationToken));
        }
예제 #4
0
        private async Task <IFileBackedLog> ProcessCrash(string crashFile)
        {
            var name            = Path.GetFileName(crashFile);
            var crashReportFile = _logs.Create(name, $"Crash report: {name}", timestamp: false);
            var args            = new MlaunchArguments(
                new DownloadCrashReportArgument(crashFile),
                new DownloadCrashReportToArgument(crashReportFile.FullPath));

            if (!string.IsNullOrEmpty(_deviceName))
            {
                args.Add(new DeviceNameArgument(_deviceName));
            }

            var result = await _processManager.ExecuteCommandAsync(args, _log, TimeSpan.FromMinutes(1));

            if (result.Succeeded)
            {
                _log.WriteLine("Downloaded crash report {0} to {1}", crashFile, crashReportFile.FullPath);
                return(await GetSymbolicateCrashReportAsync(crashReportFile));
            }
            else
            {
                _log.WriteLine("Could not download crash report {0}", crashFile);
                return(null);
            }
        }
예제 #5
0
        private MlaunchArguments GetCommonArguments(
            int verbosity,
            XmlResultJargon xmlResultJargon,
            string[]?skippedMethods,
            string[]?skippedTestClasses,
            ListenerTransport listenerTransport,
            int listenerPort,
            string listenerTmpFile)
        {
            var args = new MlaunchArguments();

            for (var i = -1; i < verbosity; i++)
            {
                args.Add(new VerbosityArgument());
            }

            // Environment variables
            var envVariables = GetEnvVariables(xmlResultJargon, skippedMethods, skippedTestClasses, listenerTransport, listenerPort, listenerTmpFile);

            args.AddRange(envVariables.Select(pair => new SetEnvVariableArgument(pair.Key, pair.Value)));

            // Arguments passed to the iOS app bundle
            args.AddRange(_appArguments.Select(arg => new SetAppArgumentArgument(arg)));

            return(args);
        }
예제 #6
0
        private MlaunchArguments GetCommonArguments(int verbosity)
        {
            var args = new MlaunchArguments();

            for (var i = -1; i < verbosity; i++)
            {
                args.Add(new VerbosityArgument());
            }

            // Arguments passed to the iOS app bundle
            args.AddRange(_appArguments.Select(arg => new SetAppArgumentArgument(arg)));

            return(args);
        }
예제 #7
0
        private async Task <HashSet <string> > CreateCrashReportsSnapshotAsync()
        {
            var crashes = new HashSet <string>();

            if (!_isDevice)
            {
                var dir = Path.Combine(Environment.GetEnvironmentVariable("HOME"), "Library", "Logs", "DiagnosticReports");
                if (Directory.Exists(dir))
                {
                    crashes.UnionWith(Directory.EnumerateFiles(dir));
                }
            }
            else
            {
                var tempFile = _tempFileProvider();
                try
                {
                    var args = new MlaunchArguments(new ListCrashReportsArgument(tempFile));

                    if (!string.IsNullOrEmpty(_deviceName))
                    {
                        args.Add(new DeviceNameArgument(_deviceName));
                    }

                    var result = await _processManager.ExecuteCommandAsync(args, _log, TimeSpan.FromMinutes(1));

                    if (result.Succeeded)
                    {
                        crashes.UnionWith(File.ReadAllLines(tempFile));
                    }
                }
                finally
                {
                    File.Delete(tempFile);
                }
            }

            return(crashes);
        }
예제 #8
0
        private MlaunchArguments GetCommonArguments(int verbosity)
        {
            var args = new MlaunchArguments
            {
                new SetAppArgumentArgument("-connection-mode"),
                new SetAppArgumentArgument("none"), // This will prevent the app from trying to connect to any IDEs

                // On macOS we can't edit the TCC database easily
                // (it requires adding the mac has to be using MDM: https://carlashley.com/2018/09/28/tcc-round-up/)
                // So by default ignore any tests that would pop up permission dialogs in CI.
                new SetEnvVariableArgument(EnviromentVariables.DisableSystemPermissionTests, 1),
            };

            for (int i = -1; i < verbosity; i++)
            {
                args.Add(new VerbosityArgument());
            }

            // Arguments passed to the iOS app bundle
            args.AddRange(_appArguments.Select(arg => new SetAppArgumentArgument(arg, true)));

            return(args);
        }
        public async Task LoadDevices(ILog log, bool includeLocked = false, bool forceRefresh = false, bool listExtraData = false)
        {
            if (loaded)
            {
                if (!forceRefresh)
                {
                    return;
                }
                connectedDevices.Reset();
            }

            loaded = true;

            var tmpfile = Path.GetTempFileName();

            try {
                using (var process = new Process()) {
                    var arguments = new MlaunchArguments(
                        new ListDevicesArgument(tmpfile),
                        new XmlOutputFormatArgument());

                    if (listExtraData)
                    {
                        arguments.Add(new ListExtraDataArgument());
                    }

                    var task = processManager.RunAsync(process, arguments, log, timeout: TimeSpan.FromSeconds(120));
                    log.WriteLine("Launching {0} {1}", process.StartInfo.FileName, process.StartInfo.Arguments);

                    var result = await task;

                    if (!result.Succeeded)
                    {
                        throw new Exception("Failed to list devices.");
                    }
                    log.WriteLine("Result:");
                    log.WriteLine(File.ReadAllText(tmpfile));
                    log.Flush();

                    var doc = new XmlDocument();
                    doc.LoadWithoutNetworkAccess(tmpfile);

                    foreach (XmlNode dev in doc.SelectNodes("/MTouch/Device"))
                    {
                        var d = GetDevice(dev);
                        if (d == null)
                        {
                            continue;
                        }
                        if (!includeLocked && d.IsLocked)
                        {
                            log.WriteLine($"Skipping device {d.Name} ({d.DeviceIdentifier}) because it's locked.");
                            continue;
                        }
                        if (d.IsUsableForDebugging.HasValue && !d.IsUsableForDebugging.Value)
                        {
                            log.WriteLine($"Skipping device {d.Name} ({d.DeviceIdentifier}) because it's not usable for debugging.");
                            continue;
                        }
                        connectedDevices.Add(d);
                    }
                }
            } finally {
                connectedDevices.SetCompleted();
                File.Delete(tmpfile);
                log.Flush();
            }
        }
예제 #10
0
        public async Task<(string DeviceName, TestExecutingResult Result, string ResultMessage)> RunApp(
            AppBundleInformation appInformation,
            TestTarget target,
            TimeSpan timeout,
            TimeSpan testLaunchTimeout,
            string? deviceName = null,
            string? companionDeviceName = null,
            bool ensureCleanSimulatorState = false,
            int verbosity = 1,
            XmlResultJargon xmlResultJargon = XmlResultJargon.xUnit,
            CancellationToken cancellationToken = default)
        {
            var args = new MlaunchArguments
            {
                new SetAppArgumentArgument("-connection-mode"),
                new SetAppArgumentArgument("none"), // This will prevent the app from trying to connect to any IDEs
                new SetAppArgumentArgument("-autostart", true),
                new SetEnvVariableArgument(EnviromentVariables.AutoStart, true),
                new SetAppArgumentArgument("-autoexit", true),
                new SetEnvVariableArgument(EnviromentVariables.AutoExit, true),
                new SetAppArgumentArgument("-enablenetwork", true),
                new SetEnvVariableArgument(EnviromentVariables.EnableNetwork, true),

                // On macOS we can't edit the TCC database easily
                // (it requires adding the mac has to be using MDM: https://carlashley.com/2018/09/28/tcc-round-up/)
                // So by default ignore any tests that would pop up permission dialogs in CI.
                new SetEnvVariableArgument(EnviromentVariables.DisableSystemPermissionTests, 1),
            };

            for (int i = -1; i < verbosity; i++)
            {
                args.Add(new VerbosityArgument());
            }

            var isSimulator = target.IsSimulator();

            if (isSimulator)
            {
                args.Add(new SetAppArgumentArgument("-hostname:127.0.0.1", true));
                args.Add(new SetEnvVariableArgument(EnviromentVariables.HostName, "127.0.0.1"));
            }
            else
            {
                var ipAddresses = System.Net.Dns.GetHostEntry(System.Net.Dns.GetHostName()).AddressList.Select(ip => ip.ToString());
                var ips = string.Join(",", ipAddresses);
                args.Add(new SetAppArgumentArgument($"-hostname:{ips}", true));
                args.Add(new SetEnvVariableArgument(EnviromentVariables.HostName, ips));
            }

            var listenerLog = _logs.Create($"test-{target.AsString()}-{_helpers.Timestamp}.log", LogType.TestLog.ToString(), timestamp: true);
            var (transport, listener, listenerTmpFile) = _listenerFactory.Create(target.ToRunMode(),
                log: _mainLog,
                testLog: listenerLog,
                isSimulator: isSimulator,
                autoExit: true,
                xmlOutput: true); // cli always uses xml

            // Initialize has to be called before we try to get Port (internal implementation of the listener says so)
            // TODO: Improve this to not get into a broken state - it was really hard to debug when I moved this lower
            listener.Initialize();

            args.Add(new SetAppArgumentArgument($"-transport:{transport}", true));
            args.Add(new SetEnvVariableArgument(EnviromentVariables.Transport, transport.ToString().ToUpper()));

            if (transport == ListenerTransport.File)
            {
                args.Add(new SetEnvVariableArgument(EnviromentVariables.LogFilePath, listenerTmpFile));
            }

            args.Add(new SetAppArgumentArgument($"-hostport:{listener.Port}", true));
            args.Add(new SetEnvVariableArgument(EnviromentVariables.HostPort, listener.Port));

            if (_listenerFactory.UseTunnel && !isSimulator) // simulators do not support tunnels
            {
                args.Add(new SetEnvVariableArgument(EnviromentVariables.UseTcpTunnel, true));
            }

            if (_useXmlOutput)
            {
                // let the runner now via envars that we want to get a xml output, else the runner will default to plain text
                args.Add (new SetEnvVariableArgument (EnviromentVariables.EnableXmlOutput, true));
                args.Add (new SetEnvVariableArgument (EnviromentVariables.XmlMode, "wrapped"));
                args.Add (new SetEnvVariableArgument (EnviromentVariables.XmlVersion, $"{xmlResultJargon}"));
            }

            listener.StartAsync();

            var crashLogs = new Logs(_logs.Directory);

            if (appInformation.Extension.HasValue)
            {
                switch (appInformation.Extension)
                {
                    case Extension.TodayExtension:
                        args.Add(isSimulator
                            ? (MlaunchArgument)new LaunchSimulatorExtensionArgument(appInformation.LaunchAppPath, appInformation.BundleIdentifier)
                            : new LaunchDeviceExtensionArgument(appInformation.LaunchAppPath, appInformation.BundleIdentifier));
                        break;
                    case Extension.WatchKit2:
                    default:
                        throw new NotImplementedException();
                }
            }
            else
            {
                args.Add(isSimulator
                    ? (MlaunchArgument)new LaunchSimulatorArgument(appInformation.LaunchAppPath)
                    : new LaunchDeviceArgument(appInformation.LaunchAppPath));
            }

            var runMode = target.ToRunMode();
            ICrashSnapshotReporter crashReporter;
            ITestReporter testReporter;

            if (isSimulator)
            {
                crashReporter = _snapshotReporterFactory.Create(_mainLog, crashLogs, isDevice: !isSimulator, deviceName: null!);
                testReporter = _testReporterFactory.Create(_mainLog,
                    _mainLog,
                    _logs,
                    crashReporter,
                    listener,
                    new XmlResultParser(),
                    appInformation,
                    runMode,
                    xmlResultJargon,
                    device: null,
                    timeout,
                    null,
                    (level, message) => _mainLog.WriteLine(message));

                using var linkedCts = CancellationTokenSource.CreateLinkedTokenSource(testReporter.CancellationToken, cancellationToken);

                listener.ConnectedTask
                    .TimeoutAfter(testLaunchTimeout)
                    .ContinueWith(testReporter.LaunchCallback)
                    .DoNotAwait();

                await _simulatorLoader.LoadDevices(_logs.Create($"simulator-list-{_helpers.Timestamp}.log", "Simulator list"), false, false);

                var simulators = await _simulatorLoader.FindSimulators(target, _mainLog);
                if (!(simulators?.Any() ?? false))
                {
                    _mainLog.WriteLine("Didn't find any suitable simulators");
                    throw new NoDeviceFoundException();
                }

                var simulator = string.IsNullOrEmpty(deviceName)
                    ? simulators.FirstOrDefault()
                    : simulators.FirstOrDefault(s => string.Equals(s.Name, deviceName, StringComparison.InvariantCultureIgnoreCase));

                if (simulator == null)
                {
                    throw new NoDeviceFoundException();
                }

                deviceName = simulator.Name;

                if (!target.IsWatchOSTarget())
                {
                    var stderrTty = _helpers.GetTerminalName(2);
                    if (!string.IsNullOrEmpty(stderrTty))
                    {
                        args.Add(new SetStderrArgument(stderrTty));
                    }
                    else
                    {
                        var stdoutLog = _logs.CreateFile($"mlaunch-stdout-{_helpers.Timestamp}.log", "Standard output");
                        var stderrLog = _logs.CreateFile($"mlaunch-stderr-{_helpers.Timestamp}.log", "Standard error");
                        args.Add(new SetStdoutArgument(stdoutLog));
                        args.Add(new SetStderrArgument(stderrLog));
                    }
                }

                var systemLogs = new List<ICaptureLog>();
                foreach (var sim in simulators)
                {
                    // Upload the system log
                    _mainLog.WriteLine("System log for the '{1}' simulator is: {0}", sim.SystemLog, sim.Name);
                    bool isCompanion = sim != simulator;

                    var logDescription = isCompanion ? LogType.CompanionSystemLog.ToString() : LogType.SystemLog.ToString();
                    var log = _captureLogFactory.Create(
                        Path.Combine(_logs.Directory, sim.Name + ".log"),
                        sim.SystemLog,
                        true,
                        logDescription);

                    log.StartCapture();
                    _logs.Add(log);
                    systemLogs.Add(log);
                }

                _mainLog.WriteLine("*** Executing {0}/{1} in the simulator ***", appInformation.AppName, target);

                if (ensureCleanSimulatorState)
                {
                    foreach (var sim in simulators)
                    {
                        await sim.PrepareSimulator(_mainLog, appInformation.BundleIdentifier);
                    }
                }

                args.Add(new SimulatorUDIDArgument(simulator.UDID));

                await crashReporter.StartCaptureAsync();

                _mainLog.WriteLine("Starting test run");

                var result = _processManager.ExecuteCommandAsync(args, _mainLog, timeout, cancellationToken: linkedCts.Token);

                await testReporter.CollectSimulatorResult(result);

                // cleanup after us
                if (ensureCleanSimulatorState)
                {
                    await simulator.KillEverything(_mainLog);
                }

                foreach (var log in systemLogs)
                {
                    log.StopCapture();
                }
            }
            else
            {
                args.Add(new DisableMemoryLimitsArgument());

                if (deviceName == null)
                {
                    IHardwareDevice? companionDevice = null;
                    IHardwareDevice device = await _hardwareDeviceLoader.FindDevice(runMode, _mainLog, includeLocked: false, force: false);

                    if (target.IsWatchOSTarget())
                    {
                        companionDevice = await _hardwareDeviceLoader.FindCompanionDevice(_mainLog, device);
                    }

                    deviceName = companionDevice?.Name ?? device.Name;
                }

                if (deviceName == null)
                {
                    throw new NoDeviceFoundException();
                }

                crashReporter = _snapshotReporterFactory.Create(_mainLog, crashLogs, isDevice: !isSimulator, deviceName);
                testReporter = _testReporterFactory.Create(_mainLog,
                    _mainLog,
                    _logs,
                    crashReporter,
                    listener,
                    new XmlResultParser(),
                    appInformation,
                    runMode,
                    xmlResultJargon,
                    deviceName,
                    timeout,
                    null,
                    (level, message) => _mainLog.WriteLine(message));

                using var linkedCts = CancellationTokenSource.CreateLinkedTokenSource(testReporter.CancellationToken, cancellationToken);

                listener.ConnectedTask
                    .TimeoutAfter(testLaunchTimeout)
                    .ContinueWith(testReporter.LaunchCallback)
                    .DoNotAwait();

                _mainLog.WriteLine("*** Executing {0}/{1} on device '{2}' ***", appInformation.AppName, target, deviceName);

                if (target.IsWatchOSTarget())
                {
                    args.Add(new AttachNativeDebuggerArgument()); // this prevents the watch from backgrounding the app.
                }
                else
                {
                    args.Add(new WaitForExitArgument());
                }

                args.Add(new DeviceNameArgument(deviceName));

                var deviceSystemLog = _logs.Create($"device-{deviceName}-{_helpers.Timestamp}.log", "Device log");
                var deviceLogCapturer = _deviceLogCapturerFactory.Create(_mainLog, deviceSystemLog, deviceName);
                deviceLogCapturer.StartCapture();

                try
                {
                    await crashReporter.StartCaptureAsync();

                    // create a tunnel to communicate with the device
                    if (transport == ListenerTransport.Tcp && _listenerFactory.UseTunnel && listener is SimpleTcpListener tcpListener)
                    {
                        // create a new tunnel using the listener
                        var tunnel = _listenerFactory.TunnelBore.Create(deviceName, _mainLog);
                        tunnel.Open(deviceName, tcpListener, timeout, _mainLog);
                        // wait until we started the tunnel
                        await tunnel.Started;
                    }

                    _mainLog.WriteLine("Starting test run");

                    // We need to check for MT1111 (which means that mlaunch won't wait for the app to exit).
                    var aggregatedLog = Log.CreateAggregatedLog(testReporter.CallbackLog, _mainLog);
                    Task<ProcessExecutionResult> runTestTask = _processManager.ExecuteCommandAsync(
                        args,
                        aggregatedLog,
                        timeout,
                        cancellationToken: linkedCts.Token);

                    await testReporter.CollectDeviceResult(runTestTask);
                }
                finally
                {
                    deviceLogCapturer.StopCapture();
                    deviceSystemLog.Dispose();

                    // close a tunnel if it was created
                    if (!isSimulator && _listenerFactory.UseTunnel)
                        await _listenerFactory.TunnelBore.Close(deviceName);
                }

                // Upload the system log
                if (File.Exists(deviceSystemLog.FullPath))
                {
                    _mainLog.WriteLine("A capture of the device log is: {0}", deviceSystemLog.FullPath);
                }
            }

            listener.Cancel();
            listener.Dispose();

            // check the final status, copy all the required data
            var (testResult, resultMessage) = await testReporter.ParseResult();

            return (deviceName, testResult, resultMessage);
        }
예제 #11
0
        private MlaunchArguments GetCommonArguments(
            int verbosity,
            XmlResultJargon xmlResultJargon,
            string[]?skippedMethods,
            string[]?skippedTestClasses,
            ListenerTransport listenerTransport,
            int listenerPort,
            string listenerTmpFile)
        {
            var args = new MlaunchArguments
            {
                new SetAppArgumentArgument("-connection-mode"),
                new SetAppArgumentArgument("none"), // This will prevent the app from trying to connect to any IDEs
                new SetAppArgumentArgument("-autostart", true),
                new SetEnvVariableArgument(EnviromentVariables.AutoStart, true),
                new SetAppArgumentArgument("-autoexit", true),
                new SetEnvVariableArgument(EnviromentVariables.AutoExit, true),
                new SetAppArgumentArgument("-enablenetwork", true),
                new SetEnvVariableArgument(EnviromentVariables.EnableNetwork, true),

                // On macOS we can't edit the TCC database easily
                // (it requires adding the mac has to be using MDM: https://carlashley.com/2018/09/28/tcc-round-up/)
                // So by default ignore any tests that would pop up permission dialogs in CI.
                new SetEnvVariableArgument(EnviromentVariables.DisableSystemPermissionTests, 1),
            };

            if (skippedMethods?.Any() ?? skippedTestClasses?.Any() ?? false)
            {
                // do not run all the tests, we are using filters
                args.Add(new SetEnvVariableArgument(EnviromentVariables.RunAllTestsByDefault, false));

                // add the skipped test classes and methods
                if (skippedMethods != null && skippedMethods.Length > 0)
                {
                    var skippedMethodsValue = string.Join(',', skippedMethods);
                    args.Add(new SetEnvVariableArgument(EnviromentVariables.SkippedMethods, skippedMethodsValue));
                }

                if (skippedTestClasses != null && skippedTestClasses !.Length > 0)
                {
                    var skippedClassesValue = string.Join(',', skippedTestClasses);
                    args.Add(new SetEnvVariableArgument(EnviromentVariables.SkippedClasses, skippedClassesValue));
                }
            }

            for (int i = -1; i < verbosity; i++)
            {
                args.Add(new VerbosityArgument());
            }

            // let the runner now via envars that we want to get a xml output, else the runner will default to plain text
            args.Add(new SetEnvVariableArgument(EnviromentVariables.EnableXmlOutput, true));
            args.Add(new SetEnvVariableArgument(EnviromentVariables.XmlMode, "wrapped"));
            args.Add(new SetEnvVariableArgument(EnviromentVariables.XmlVersion, $"{xmlResultJargon}"));
            args.Add(new SetAppArgumentArgument($"-transport:{listenerTransport}", true));
            args.Add(new SetEnvVariableArgument(EnviromentVariables.Transport, listenerTransport.ToString().ToUpper()));

            if (listenerTransport == ListenerTransport.File)
            {
                args.Add(new SetEnvVariableArgument(EnviromentVariables.LogFilePath, listenerTmpFile));
            }

            args.Add(new SetAppArgumentArgument($"-hostport:{listenerPort}", true));
            args.Add(new SetEnvVariableArgument(EnviromentVariables.HostPort, listenerPort));

            // Arguments passed to the iOS app bundle
            args.AddRange(_appArguments.Select(arg => new SetAppArgumentArgument(arg, true)));

            return(args);
        }
예제 #12
0
        public async Task <(string deviceName, ProcessExecutionResult result)> InstallApp(AppBundleInformation appBundleInformation, TestTargetOs target, string?deviceName = null, CancellationToken cancellationToken = default)
        {
            if (target.Platform.IsSimulator())
            {
                // We reset the simulator when running, so a separate install step does not make much sense.
                throw new InvalidOperationException("Installing to a simulator is not supported.");
            }

            if (!Directory.Exists(appBundleInformation.LaunchAppPath))
            {
                throw new DirectoryNotFoundException("Failed to find the app bundle directory");
            }

            if (deviceName == null)
            {
                // the _deviceLoader.FindDevice will return the fist device of the type, but we want to make sure that
                // the device we use is if the correct arch, therefore, we will use the LoadDevices and return the
                // correct one
                await _deviceLoader.LoadDevices(_mainLog, false, false);

                IHardwareDevice?device = null;
                if (appBundleInformation.Supports32Bit)
                {
                    // we only support 32b on iOS, therefore we can ignore the target
                    device = _deviceLoader.Connected32BitIOS.FirstOrDefault();
                }
                else
                {
                    device = target.Platform switch
                    {
                        TestTarget.Device_iOS => _deviceLoader.Connected64BitIOS.FirstOrDefault(),
                        TestTarget.Device_tvOS => _deviceLoader.ConnectedTV.FirstOrDefault(),
                        _ => device
                    };
                }

                deviceName = target.Platform.IsWatchOSTarget() ? (await _deviceLoader.FindCompanionDevice(_mainLog, device)).Name : device?.Name;
            }

            if (deviceName == null)
            {
                throw new NoDeviceFoundException();
            }

            var args = new MlaunchArguments();

            for (int i = -1; i < _verbosity; i++)
            {
                args.Add(new VerbosityArgument());
            }

            args.Add(new InstallAppOnDeviceArgument(appBundleInformation.LaunchAppPath));
            args.Add(new DeviceNameArgument(deviceName));

            if (target.Platform.IsWatchOSTarget())
            {
                args.Add(new DeviceArgument("ios,watchos"));
            }

            var totalSize = Directory.GetFiles(appBundleInformation.LaunchAppPath, "*", SearchOption.AllDirectories).Select((v) => new FileInfo(v).Length).Sum();

            _mainLog.WriteLine($"Installing '{appBundleInformation.LaunchAppPath}' to '{deviceName}' ({totalSize / 1024.0 / 1024.0:N2} MB)");

            ProcessExecutionResult result = await _processManager.ExecuteCommandAsync(args, _mainLog, TimeSpan.FromHours(1), cancellationToken : cancellationToken);

            return(deviceName, result);
        }
    }