Exemple #1
0
        public NpmScriptRunner(string workingDirectory, string scriptName, string arguments, IDictionary <string, string> envVars)
        {
            if (string.IsNullOrEmpty(workingDirectory))
            {
                throw new ArgumentException("Cannot be null or empty.", nameof(workingDirectory));
            }

            if (string.IsNullOrEmpty(scriptName))
            {
                throw new ArgumentException("Cannot be null or empty.", nameof(scriptName));
            }

            var npmExe            = "npm";
            var completeArguments = $"run {scriptName} -- {arguments ?? string.Empty}";

            if (RuntimeInformation.IsOSPlatform(OSPlatform.Windows))
            {
                // On Windows, the NPM executable is a .cmd file, so it can't be executed
                // directly (except with UseShellExecute=true, but that's no good, because
                // it prevents capturing stdio). So we need to invoke it via "cmd /c".
                npmExe            = "cmd";
                completeArguments = $"/c npm {completeArguments}";
            }

            var processStartInfo = new ProcessStartInfo(npmExe)
            {
                Arguments              = completeArguments,
                UseShellExecute        = false,
                RedirectStandardInput  = true,
                RedirectStandardOutput = true,
                RedirectStandardError  = true,
                WorkingDirectory       = workingDirectory
            };

            if (envVars != null)
            {
                foreach (var keyValuePair in envVars)
                {
                    processStartInfo.Environment[keyValuePair.Key] = keyValuePair.Value;
                }
            }

            var process = LaunchNodeProcess(processStartInfo);

            ProcessTracker.Add(process);
            StdOut = new EventedStreamReader(process.StandardOutput);
            StdErr = new EventedStreamReader(process.StandardError);
        }
    private async Task <(Uri url, CancellationToken hostExitToken)> StartIISExpressAsync(string contentRoot)
    {
        using (Logger.BeginScope("StartIISExpress"))
        {
            var iisExpressPath = GetIISExpressPath();

            for (var attempt = 0; attempt < MaximumAttempts; attempt++)
            {
                var uri  = TestUriHelper.BuildTestUri(ServerType.IISExpress, DeploymentParameters.ApplicationBaseUriHint);
                var port = uri.Port;
                if (port == 0)
                {
                    port = (uri.Scheme == "https") ? TestPortHelper.GetNextSSLPort() : TestPortHelper.GetNextPort();
                }

                Logger.LogInformation("Attempting to start IIS Express on port: {port}", port);
                PrepareConfig(contentRoot, port);

                var parameters = string.IsNullOrEmpty(DeploymentParameters.ServerConfigLocation) ?
                                 string.Format(CultureInfo.InvariantCulture, "/port:{0} /path:\"{1}\" /trace:error /systray:false", uri.Port, contentRoot) :
                                 string.Format(CultureInfo.InvariantCulture, "/site:{0} /config:{1} /trace:error /systray:false", DeploymentParameters.SiteName, DeploymentParameters.ServerConfigLocation);

                Logger.LogInformation("Executing command : {iisExpress} {parameters}", iisExpressPath, parameters);

                var startInfo = new ProcessStartInfo
                {
                    FileName               = iisExpressPath,
                    Arguments              = parameters,
                    UseShellExecute        = false,
                    CreateNoWindow         = true,
                    RedirectStandardError  = true,
                    RedirectStandardOutput = true,
                    // VS sets current directory to C:\Program Files\IIS Express
                    WorkingDirectory = Path.GetDirectoryName(iisExpressPath)
                };

                AddEnvironmentVariablesToProcess(startInfo, DeploymentParameters.EnvironmentVariables);

                Uri url     = null;
                var started = new TaskCompletionSource <bool>();

                var process = new Process()
                {
                    StartInfo = startInfo
                };
                process.OutputDataReceived += (sender, dataArgs) =>
                {
                    if (string.Equals(dataArgs.Data, UnableToStartIISExpressMessage, StringComparison.Ordinal))
                    {
                        // We completely failed to start and we don't really know why
                        started.TrySetException(new InvalidOperationException("Failed to start IIS Express"));
                    }
                    else if (string.Equals(dataArgs.Data, FailedToInitializeBindingsMessage, StringComparison.Ordinal))
                    {
                        started.TrySetResult(false);
                    }
                    else if (string.Equals(dataArgs.Data, IISExpressRunningMessage, StringComparison.Ordinal))
                    {
                        started.TrySetResult(true);
                    }
                    else if (!string.IsNullOrEmpty(dataArgs.Data))
                    {
                        var m = UrlDetectorRegex.Match(dataArgs.Data);
                        if (m.Success)
                        {
                            url = new Uri(m.Groups["url"].Value);
                        }
                    }
                };

                process.EnableRaisingEvents = true;
                var hostExitTokenSource = new CancellationTokenSource();
                process.Exited += (sender, e) =>
                {
                    Logger.LogInformation("iisexpress Process {pid} shut down", process.Id);

                    // If TrySetResult was called above, this will just silently fail to set the new state, which is what we want
                    started.TrySetException(new Exception($"Command exited unexpectedly with exit code: {process.ExitCode}"));

                    TriggerHostShutdown(hostExitTokenSource);
                };
                process.StartAndCaptureOutAndErrToLogger("iisexpress", Logger);
                Logger.LogInformation("iisexpress Process {pid} started", process.Id);

                if (process.HasExited)
                {
                    Logger.LogError("Host process {processName} {pid} exited with code {exitCode} or failed to start.", startInfo.FileName, process.Id, process.ExitCode);
                    throw new Exception("Failed to start host");
                }

                // Wait for the app to start
                // The timeout here is large, because we don't know how long the test could need
                // We cover a lot of error cases above, but I want to make sure we eventually give up and don't hang the build
                // just in case we missed one -anurse
                if (!await started.Task.TimeoutAfter(TimeSpan.FromMinutes(10)))
                {
                    Logger.LogInformation("iisexpress Process {pid} failed to bind to port {port}, trying again", process.Id, port);

                    // Wait for the process to exit and try again
                    process.WaitForExit(30 * 1000);
                    await Task.Delay(1000); // Wait a second to make sure the socket is completely cleaned up
                }
                else
                {
                    _hostProcess = process;

                    // Ensure iisexpress.exe is killed if test process termination is non-graceful.
                    // Prevents locked files when stop debugging unit test.
                    ProcessTracker.Add(_hostProcess);

                    // cache the process start time for verifying log file name.
                    var _ = _hostProcess.StartTime;

                    Logger.LogInformation("Started iisexpress successfully. Process Id : {processId}, Port: {port}", _hostProcess.Id, port);
                    return(url : url, hostExitToken : hostExitTokenSource.Token);
                }
            }

            var message = $"Failed to initialize IIS Express after {MaximumAttempts} attempts to select a port";
            Logger.LogError(message);
            throw new TimeoutException(message);
        }
    }
Exemple #3
0
    private void WaitUntilSiteStarted(string contentRoot)
    {
        ServiceController serviceController = new ServiceController("w3svc");

        Logger.LogInformation("W3SVC status " + serviceController.Status);

        if (serviceController.Status != ServiceControllerStatus.Running &&
            serviceController.Status != ServiceControllerStatus.StartPending)
        {
            Logger.LogInformation("Starting W3SVC");

            serviceController.Start();
            serviceController.WaitForStatus(ServiceControllerStatus.Running, _timeout);
        }

        RetryServerManagerAction(serverManager =>
        {
            var site    = serverManager.Sites.Single();
            var appPool = serverManager.ApplicationPools.Single();

            var actualPath = site.Applications.FirstOrDefault().VirtualDirectories.Single().PhysicalPath;
            if (actualPath != contentRoot)
            {
                throw new InvalidOperationException($"Wrong physical path. Expected: {contentRoot} Actual: {actualPath}");
            }

            if (appPool.State != ObjectState.Started && appPool.State != ObjectState.Starting)
            {
                var state = appPool.Start();
                Logger.LogInformation($"Starting pool, state: {state.ToString()}");
            }

            if (site.State != ObjectState.Started && site.State != ObjectState.Starting)
            {
                var state = site.Start();
                Logger.LogInformation($"Starting site, state: {state.ToString()}");
            }

            if (site.State != ObjectState.Started)
            {
                throw new InvalidOperationException("Site not started yet");
            }

            var workerProcess = appPool.WorkerProcesses.SingleOrDefault();
            if (workerProcess == null)
            {
                throw new InvalidOperationException("Site is started but no worked process found");
            }

            HostProcess = Process.GetProcessById(workerProcess.ProcessId);

            // Ensure w3wp.exe is killed if test process termination is non-graceful.
            // Prevents locked files when stop debugging unit test.
            ProcessTracker.Add(HostProcess);

            // cache the process start time for verifying log file name.
            var _ = HostProcess.StartTime;

            Logger.LogInformation("Site has started.");
        });
    }
Exemple #4
0
    private void WaitUntilSiteStarted(string contentRoot)
    {
        ServiceController serviceController = new ServiceController("w3svc");

        Logger.LogInformation("W3SVC status " + serviceController.Status);

        if (serviceController.Status != ServiceControllerStatus.Running &&
            serviceController.Status != ServiceControllerStatus.StartPending)
        {
            Logger.LogInformation("Starting W3SVC");

            serviceController.Start();
            serviceController.WaitForStatus(ServiceControllerStatus.Running, _timeout);
        }

        RetryServerManagerAction(serverManager =>
        {
            var site = FindSite(serverManager, contentRoot);
            if (site == null)
            {
                PreserveConfigFiles("nositetostart");
                throw new InvalidOperationException($"Can't find site for: {contentRoot} to start.");
            }

            var appPool = serverManager.ApplicationPools.Single();
            if (appPool.State != ObjectState.Started && appPool.State != ObjectState.Starting)
            {
                var state = appPool.Start();
                Logger.LogInformation($"Starting pool, state: {state}");
            }

            if (site.State != ObjectState.Started && site.State != ObjectState.Starting)
            {
                var state = site.Start();
                Logger.LogInformation($"Starting site, state: {state}");
            }

            if (site.State != ObjectState.Started)
            {
                throw new InvalidOperationException("Site not started yet");
            }

            var workerProcess = appPool.WorkerProcesses.SingleOrDefault();
            if (workerProcess == null)
            {
                PreserveConfigFiles("noworkerprocess");
                throw new InvalidOperationException("Site is started but no worker process found");
            }

            HostProcess = Process.GetProcessById(workerProcess.ProcessId);

            // Ensure w3wp.exe is killed if test process termination is non-graceful.
            // Prevents locked files when stop debugging unit test.
            ProcessTracker.Add(HostProcess);

            // cache the process start time for verifying log file name.
            var _ = HostProcess.StartTime;

            Logger.LogInformation("Site has started.");
        });
    }