private AssessmentResult RunTests(IDirectory testDirectory, int port) { const string testOutputFileName = "features_report.html"; var ruby = new PathFile(_settings.Env.RubyBin, "ruby.exe"); var cuke = new PathFile(_settings.Env.RubyBin, "cucumber"); string args = "{0} {1} --strict --format pretty --format html --out={2} {3}={4}" .FormatFrom( cuke, testDirectory.Path, testOutputFileName, "features_test_port", port); var stringWriter = new StringWriter(); var syncWriter = TextWriter.Synchronized(stringWriter); using (var process = _shell.StartBackgroundProcess( new ProcessStartInfo(ruby.Path, args) { UseShellExecute = false, WorkingDirectory = testDirectory.Path, RedirectStandardOutput = true, RedirectStandardError = true }, syncWriter.WriteLine, syncWriter.WriteLine)) { AssessmentResult assessmentResult; if (!process.WaitForExit(30000)) { assessmentResult = new AssessmentResult(AssessmentOutcome.TestFailure) { Message = "Timeout waiting for test completion" }; } else { if (process.ExitCode == 0) { assessmentResult = new AssessmentResult(AssessmentOutcome.Success) { Message = "Tests passed" }; } else { assessmentResult = new AssessmentResult(AssessmentOutcome.TestFailure) { Message = "Tests failed" }; } } try { if (!process.HasExited) { process.Kill(); } } catch(Exception ex) { _log.DebugFormat("Failed to kill test process {0} {1}: {2}", ruby.Path, args, ex.Summary()); } var testOutputFile = testDirectory.File(testOutputFileName); if (testOutputFile.Exists()) { assessmentResult.TestOutput = testOutputFile.ReadAllText(); assessmentResult.TestOutputFormat = TestOutputFormat.Html; } assessmentResult.BuildOutput = stringWriter.ToString(); return assessmentResult; } }
private bool CheckHttpResponse(Process userProcess, int port, string key, out AssessmentResult result) { string statusContent; var webClient = new WebClientExt { Timeout = TimeSpan.FromSeconds(5), Headers = {{"Accept", "application/json"}} }; try { statusContent = webClient.DownloadString(string.Format("http://localhost:{0}/status", port)); } catch (Exception ex) { _log.Debug("Process exited early", ex); if (userProcess.HasExited) { result = new AssessmentResult(AssessmentOutcome.SolutionFailure) { Message = "The solution process terminated early." }; return false; } _log.Debug("Solution HTTP error", ex); result = new AssessmentResult(AssessmentOutcome.SolutionFailure) { Message = "The solution process [{0}] did not respond to HTTP request '/status' at port {1}." .FormatFrom(Path.GetFileName(userProcess.StartInfo.FileName), port) }; return false; } bool responseIsValid = !statusContent.IsEmpty(); if (responseIsValid) { try { var statusObject = JsonConvert.DeserializeObject<JObject>(statusContent); var startupKey = statusObject.Property("startupKey").Value; if (!key.EqualsIgnoreCase(startupKey.ToString())) { responseIsValid = false; } } catch (Exception ex) { _log.Debug("Failed to parse solution HTTP response", ex); responseIsValid = false; } } ; if (!responseIsValid) { _log.Debug("Invalid response from solution HTTP"); result = new AssessmentResult(AssessmentOutcome.SolutionFailure) { Message = "The solution process [{0}] did not respond to HTTP request '/status' with a proper response. \r\n Expected: {1}\r\n Received: {2}" .FormatFrom(Path.GetFileName(userProcess.StartInfo.FileName), JsonConvert.SerializeObject(new {startupKey = key}), statusContent) }; return false; } result = null; return true; }