// async test runner, called once for each test container/file private async Task RunTestFileAsync(IRunContext runContext, VsHttpServer listener, string issConfig, string testFilename, IEnumerable <TestCase> selectedTests, IFrameworkHandle frameworkHandle) { Log($"Entered RunTestFileAsync(), testFilename={testFilename}"); // TODO: perhaps add wwwroot path in .runsettings file? string wwwRoot = GetParentProjectFolder(testFilename).Replace('\\', '/'); string relativeFilePath = testFilename.Substring(wwwRoot.Length).Replace('\\', '/'); // get start time var startTime = DateTime.Now; // start browser & run the tests, then get the results back from browser var browser = new Browser(runContext, wwwRoot, relativeFilePath); await browser.LaunchAsync().ConfigureAwait(false); List <TsTestResult> testFileResults = await GetBrowserResultsAsync(listener).ConfigureAwait(false); // calculate total time to run tests + get results, and also the average time of each test //browserResults.TestStartTime = startTime; //browserResults.TestEndTime = DateTime.Now; //int numTests = (from test in tests where test.Source == testFilename select test).Count(); //browserResults.Duration = new TimeSpan((browserResults.TestEndTime - browserResults.TestStartTime).Ticks / numTests); // process and return test results back to Visual Studio ProcessTestFileResults(testFilename, testFileResults, selectedTests, frameworkHandle); // close the headless browser Log($"RunTestsAsync(): Attempting to close browser..."); await browser.CloseAsync().ConfigureAwait(false); Log($"RunTestsAsync(): ...browser closed"); }
private async Task <List <TsTestResult> > GetBrowserResultsAsync(VsHttpServer listener) { //Debugger.Break(); // receive results from chrome via http PUT Log("GetBrowserResultsAsync(): awaiting browser response..."); string json = await listener.GetResponseFromBrowserAsync().ConfigureAwait(false); Log("GetBrowserResultsAsync(): ...json response received"); Log($"json = {json}"); // deserialise json test results Log("GetBrowserResultsAsync(): about to deserialise..."); List <TsTestResult> browserResults = new List <TsTestResult>(); try { browserResults = Json.Deserialize <List <TsTestResult> >(json); Log("GetBrowserResultsAsync(): ...json deserialised"); } catch (Exception ex) { Log($"GetBrowserResultsAsync(): ...json deserialisation {ex.GetType().Name} thrown: {ex.Message}"); } return(browserResults); }
// called by VS when user wants to run selection of tests public void RunTests(IEnumerable <TestCase> selectedTests, IRunContext runContext, IFrameworkHandle frameworkHandle) { try { Log("RunTests(IEnumerable<TestCase> ...) called"); // because multiple tests may be within the same source file, or scattered across several, we need to // optimise things so that we only fire up the browser once for each separate test container file found // switch to C# 8.0/VS2019 and use using shorthand syntax: var listener = new VsHttpServer(8638); (no trailing braces required) _cancelled = false; using (var listener = new VsHttpServer(8638)) // ensure the listener is started first { // get list of distinct filenames for requested tests IEnumerable <string> testFilenames = (from TestCase test in selectedTests select test.Source).Distinct(); // find IIS config file for parent solution of tests, for now assume tests are all in single solution (this will need to be changed) string iisConfig = GetIISConfigFile(testFilenames.First()); // launch IIS Express (if not already running) Browser.LaunchIISExpress(iisConfig); // Launch all tests asynchronously (should really limit concurrency with a SemaphoreSlim to 16 browsers or so in case someone has 50+ test containers). // Unless there are a large number of long-running tests, it will be faster to use a single browser and fire all the test files at it sequentially // (browser startup and process exit takes a long time, ~6 seconds, executing a page of tests is likely to be < 50-100ms) var tasks = testFilenames.Select(filename => RunTestFileAsync(runContext, listener, iisConfig, filename, selectedTests, frameworkHandle)); // and then thunk back to synchronous world using blocking wait-for-all // we need to block here to ensure all the browsers that were started have been closed Task.WhenAll(tasks).GetAwaiter().GetResult(); Log("RunTests(IEnumerable<TestCase> ...) all tasks completed"); } } catch (Exception ex) { string errMsg = $"RunTests(IEnumerable<TestCase> ...): Exception thrown , exception={ex.ToString()}"; Log(errMsg); Console.Error.WriteLine(errMsg); } }