private async Task <string> ExecuteTestsWithNativeRunnerAsync( string workingDir, string testsLibraryPath, string assemblyName, bool runInParallel, int maxParallelProcessesCount, string nativeArguments, int testAgentRunTimeout, bool isTimeBasedBalance, bool sameMachineByClass, List <TestCase> distributedTestCases, CancellationTokenSource outerCancellationTokenSource) { var outputFilesDir = _pathProvider.GetDirectoryName(testsLibraryPath); var availableCores = runInParallel ? maxParallelProcessesCount : 1; // Merge logic of CreateRunFilterArgument here. remove inner foreach var listOfDistributedTestCases = _nativeTestsRunner.SplitTestCases(availableCores, sameMachineByClass, distributedTestCases); var testRunsToBeMerged = new List <object>(); var testRunProcesses = new List <Process>(); var resultsFiles = new List <string>(); var processCreationTime = _dateTimeProvider.GetCurrentTime(); foreach (var distributedTestCasesList in listOfDistributedTestCases) { var currentTestResultsFilePath = _pathProvider.GetTempFileName(); resultsFiles.Add(currentTestResultsFilePath); var arguments = _nativeTestsRunner.BuildNativeRunnerArguments( assemblyName, testsLibraryPath, distributedTestCasesList, currentTestResultsFilePath, outputFilesDir, nativeArguments); var currentProcess = _processStarter.InitializeProcess( _nativeTestsRunner.RunnerFile, workingDir, arguments, LogStandardOutput, LogErrorOutput); testRunProcesses.Add(currentProcess); } var innerCancellationTokenSource = new CancellationTokenSource(); var waitForNativeRunnerProcessesToFinishTask = _taskProvider.StartNewLongRunning( (c) => { var ranProcesses = new List <int>(); do { var coresCount = availableCores; if (runInParallel) { foreach (var process in testRunProcesses) { if (coresCount == 0) { break; } if (!ranProcesses.Contains(process.GetHashCode())) { _processStarter.StartProcess(process, LogStandardOutput, LogErrorOutput); Thread.Sleep(500); ranProcesses.Add(process.GetHashCode()); coresCount--; } } } foreach (var process in testRunProcesses) { if (outerCancellationTokenSource.Token.IsCancellationRequested) { return; } if (!runInParallel) { // Start processes one by one, otherwise they are started all upfront. _processStarter.StartProcess(process, LogStandardOutput, LogErrorOutput); ranProcesses.Add(process.GetHashCode()); } if (ranProcesses.Contains(process.GetHashCode())) { _processStarter.WaitForProcessToFinish(testAgentRunTimeout, process); } } }while (ranProcesses.Count != testRunProcesses.Count); }, innerCancellationTokenSource); var checkCancellationRequestsTask = _taskProvider.StartNewLongRunningRepeating( innerCancellationTokenSource, () => { if (waitForNativeRunnerProcessesToFinishTask.IsCompleted || waitForNativeRunnerProcessesToFinishTask.IsFaulted) { if (waitForNativeRunnerProcessesToFinishTask.IsFaulted) { _testRunLogService.CreateTestRunLogAsync($"waitForNativeRunnerProcessesToFinishTask FAULTED- {waitForNativeRunnerProcessesToFinishTask.Exception}", _currentTestRunId).Wait(); } innerCancellationTokenSource.Cancel(); } else if (outerCancellationTokenSource.Token.IsCancellationRequested) { foreach (var processName in _nativeTestsRunner.RunnerProcessesNamesToKill) { var processes = Process.GetProcessesByName(processName); foreach (var process in processes) { try { if (process.StartTime > processCreationTime) { process.Kill(); process.WaitForExit(); } } catch (Exception e) { _consoleProvider.WriteLine(e.ToString()); } } } } }, 500); checkCancellationRequestsTask.Wait(); string result = null; if (!outerCancellationTokenSource.Token.IsCancellationRequested) { foreach (var testResultFile in resultsFiles) { if (_fileProvider.Exists(testResultFile)) { var testTestResults = _fileProvider.ReadAllText(testResultFile); var currentTestRun = _nativeTestsRunner.DeserializeTestResults(testTestResults); testRunsToBeMerged.Add(currentTestRun); } else if (_directoryProvider.Exists(testResultFile)) { // TODO: Added this because of the Protractor Plugin, since it produces folder instead of file. // Fix it later with something more generic solution. Maybe, we have to save the test results to plugin folder and // add a plugin method for deleting them at the end. var files = _directoryProvider.GetFiles(testResultFile); if (!files.Any()) { throw new Exception("No test results' file was produced for test run"); } var firstFile = _directoryProvider.GetFiles(testResultFile).First(); var testTestResults = _fileProvider.ReadAllText(firstFile); var currentTestRun = _nativeTestsRunner.DeserializeTestResults(testTestResults); testRunsToBeMerged.Add(currentTestRun); } else { throw new Exception("No test results' file was produced for test run"); } } var mergedTestRun = _nativeTestsRunner.MergeTestResults(testRunsToBeMerged); if (isTimeBasedBalance) { var testCaseRuns = _nativeTestsRunner.UpdateTestCasesHistory(mergedTestRun, assemblyName); await _testCasesHistoryService.UpdateTestCaseExecutionHistoryAsync(testCaseRuns).ConfigureAwait(false); } try { _nativeTestsRunner.ExecutePostRunActions(); } catch (Exception ex) { _testRunLogService.CreateTestRunLogAsync($"There was a problem executing ExecutePostRunActions on {Environment.MachineName}. Exception: {ex}", _currentTestRunId).Wait(); } result = mergedTestRun; } return(result); }
public async Task RunTestsForCurrentAgentAsync(string testAgentTag, int testAgentRunTimeout) { if (await IsEligibleToStartTestAgentRunAsync(testAgentTag).ConfigureAwait(false)) { var newTestAgentRun = await GetFirstNewTestAgentRunForCurrentTestAgentAsync().ConfigureAwait(false); if (newTestAgentRun != null) { await _testRunLogService.CreateTestRunLogAsync($"Test agent with tag {testAgentTag} starts tests execution on machine {_environmentService.MachineName}.", newTestAgentRun.TestRunId).ConfigureAwait(false); await _testAgentStateSwitcher.SetTestAgentAsRunningTestsAsync(testAgentTag).ConfigureAwait(false); var cancellationTokenSource = new CancellationTokenSource(); var cancellationTokenSourceLastAvailable = new CancellationTokenSource(); var testAgentUpdateAvailabilityTask = _taskProvider.StartNewLongRunningRepeating( cancellationTokenSourceLastAvailable, () => { UpdateTestAgentLastAvailable(newTestAgentRun.TestAgentRunId); }, 15000); var executeTestAgentRunTask = _taskProvider.StartNewLongRunning( (c) => { ExecuteTestAgentRunAsync(newTestAgentRun, testAgentRunTimeout, cancellationTokenSource).Wait(); }, cancellationTokenSource); var checkTestRunnerLastAvailableTask = _taskProvider.StartNewLongRunningRepeating( cancellationTokenSource, () => { if (executeTestAgentRunTask.IsCompleted) { cancellationTokenSource.Cancel(); return; } if (executeTestAgentRunTask.IsFaulted) { cancellationTokenSource.Cancel(); return; } CheckTestRunnerStatus(newTestAgentRun.TestAgentRunId, cancellationTokenSource); }, 15000); checkTestRunnerLastAvailableTask.Wait(); cancellationTokenSourceLastAvailable.Cancel(); testAgentUpdateAvailabilityTask.Wait(); try { // DEBUG: Turn-off if debugging _consoleProvider.Clear(); if (_wasTestAgentRunCompleted) { await _testRunLogService.CreateTestRunLogAsync($"Test agent with tag {testAgentTag} finished tests execution on machine {_environmentService.MachineName}.", newTestAgentRun.TestRunId).ConfigureAwait(false); await _testRunLogService.CreateTestRunLogAsync($"Test agent with tag {testAgentTag} starts waiting for new jobs on machine {_environmentService.MachineName}.", newTestAgentRun.TestRunId).ConfigureAwait(false); } else { SendTestAgentRunExceptionToRunner(newTestAgentRun, executeTestAgentRunTask); // TODO: Move logic to be executed on Test Agent Run Abort- extension. await _testRunLogService .CreateTestRunLogAsync( $"Test agent with tag {testAgentTag} starts waiting for new jobs on machine {_environmentService.MachineName}.", newTestAgentRun.TestRunId).ConfigureAwait(false); _consoleProvider.WriteLine($"Test agent run aborted."); _consoleProvider.WriteLine($"Test agent with tag {testAgentTag} starts waiting for new jobs on machine {_environmentService.MachineName}."); var cts = new CancellationTokenSource(); var waitForTestRunToCompleteTask = _taskProvider.StartNewLongRunningRepeating( cts, () => { if (IsTestRunCompleted(newTestAgentRun.TestRunId).Result) { cts.Cancel(); } }, 5000); waitForTestRunToCompleteTask.Wait(); } } finally { await _testAgentStateSwitcher.SetTestAgentAsActiveAsync(testAgentTag).ConfigureAwait(false); } } } }
private async Task <string> ExecuteTestsWithNativeRunnerAsync( string workingDir, string testsLibraryPath, string assemblyName, bool runInParallel, int maxParallelProcessesCount, string nativeArguments, int testAgentRunTimeout, bool isTimeBasedBalance, List <TestCase> distributedTestCases, CancellationTokenSource outerCancellationTokenSource) { string outputFilesDir = _pathProvider.GetDirectoryName(testsLibraryPath); ////var updatedMaxParallelProcessesCount = maxParallelProcessesCount > 1 ? maxParallelProcessesCount - 1 : maxParallelProcessesCount; int availableCores = runInParallel ? maxParallelProcessesCount : 1; // Merge logic of CreateRunFilterArgument here. remove inner foreach var listOfDistributedTestCases = _nativeTestsRunner.SplitTestCases(distributedTestCases, availableCores); var testRunsToBeMerged = new List <object>(); var testRunProcesses = new List <Process>(); var resultsFiles = new List <string>(); DateTime processCreationTime = _dateTimeProvider.GetCurrentTime(); // DEBUG: ////await _testRunLogService.CreateTestRunLogAsync($"Number of tests to be executed- {distributedTestCases.Count}", _currentTestRunId); // DEBUG: ////await _testRunLogService.CreateTestRunLogAsync($"Number of listOfDistributedTestCases- {listOfDistributedTestCases.Count()}", _currentTestRunId); int index = 1; foreach (var distributedTestCasesList in listOfDistributedTestCases) { // DEBUG: ////await _testRunLogService.CreateTestRunLogAsync($"{index++} distributedTestCasesList items- {distributedTestCasesList.Count()}", _currentTestRunId); var currentTestResultsFilePath = _pathProvider.GetTempFileName(); resultsFiles.Add(currentTestResultsFilePath); var arguments = _nativeTestsRunner.BuildNativeRunnerArguments( assemblyName, testsLibraryPath, distributedTestCasesList, currentTestResultsFilePath, outputFilesDir, nativeArguments); var currentProcess = _processStarter.InitializeProcess( _nativeTestsRunner.RunnerFile, workingDir, arguments, LogStandardOutput, LogErrorOutput); testRunProcesses.Add(currentProcess); } var innerCancellationTokenSource = new CancellationTokenSource(); // DEBUG: ////await _testRunLogService.CreateTestRunLogAsync($"The native runs will be executed in parallel- {runInParallel}. Available core on machine {Environment.MachineName}- {availableCores}", _currentTestRunId); ////await _testRunLogService.CreateTestRunLogAsync($"Number of native test runner process to be run- {testRunProcesses.Count}", _currentTestRunId); var waitForNativeRunnerProcessesToFinishTask = _taskProvider.StartNewLongRunning( (c) => { var ranProcesses = new List <int>(); do { var coresCount = availableCores; if (runInParallel) { foreach (var process in testRunProcesses) { if (coresCount == 0) { break; } if (!ranProcesses.Contains(process.GetHashCode())) { _processStarter.StartProcess(process, LogStandardOutput, LogErrorOutput); Thread.Sleep(1000); ranProcesses.Add(process.GetHashCode()); coresCount--; } } } // Do not start all processes upfront // if parallel here start all of them? // run in task and pass cancelation token. If is canceled kill all processes. DO IT for NUNIT TOO foreach (var process in testRunProcesses) { if (outerCancellationTokenSource.Token.IsCancellationRequested) { return; } if (!runInParallel) { // Start processes one by one, otherwise they are started all upfront. _processStarter.StartProcess(process, LogStandardOutput, LogErrorOutput); ranProcesses.Add(process.GetHashCode()); } if (ranProcesses.Contains(process.GetHashCode())) { _processStarter.WaitForProcessToFinish(testAgentRunTimeout, process); } } // DEBUG: ////_testRunLogService.CreateTestRunLogAsync($"ranProcesses.Count {ranProcesses.Count} testRunProcesses.Count {testRunProcesses.Count} {_dateTimeProvider.GetCurrentTime()}", _currentTestRunId).Wait(); }while (ranProcesses.Count != testRunProcesses.Count); }, innerCancellationTokenSource); var checkCancellationRequestsTask = _taskProvider.StartNewLongRunningRepeating( innerCancellationTokenSource, () => { if (waitForNativeRunnerProcessesToFinishTask.IsCompleted || waitForNativeRunnerProcessesToFinishTask.IsFaulted) { if (waitForNativeRunnerProcessesToFinishTask.IsFaulted) { _testRunLogService.CreateTestRunLogAsync($"waitForNativeRunnerProcessesToFinishTask FAULTED- {waitForNativeRunnerProcessesToFinishTask.Exception}", _currentTestRunId).Wait(); } innerCancellationTokenSource.Cancel(); return; } else if (outerCancellationTokenSource.Token.IsCancellationRequested) { foreach (var processName in _nativeTestsRunner.RunnerProcessesNamesToKill) { var processes = Process.GetProcessesByName(processName); foreach (var process in processes) { try { if (process.StartTime > processCreationTime) { process.Kill(); process.WaitForExit(); } } catch (Exception e) { _consoleProvider.WriteLine(e.ToString()); } } } return; } }, 5000); checkCancellationRequestsTask.Wait(); string result = null; if (!outerCancellationTokenSource.Token.IsCancellationRequested) { // DEBUG: _testRunLogService.CreateTestRunLogAsync($"START MERGING RESULTS- on machine {Environment.MachineName}", _currentTestRunId).Wait(); foreach (var testResultFile in resultsFiles) { if (_fileProvider.Exists(testResultFile)) { var testTestResults = _fileProvider.ReadAllText(testResultFile); var currentTestRun = _nativeTestsRunner.DeserializeTestResults(testTestResults); testRunsToBeMerged.Add(currentTestRun); } else { throw new Exception("No test results' file was produced for test run"); } } var mergedTestRun = _nativeTestsRunner.MergeTestResults(testRunsToBeMerged); if (isTimeBasedBalance) { // DEBUG: var startTime = _dateTimeProvider.GetCurrentTime(); _testRunLogService.CreateTestRunLogAsync($"START updating test case history- on machine {Environment.MachineName}", _currentTestRunId).Wait(); var testCaseRuns = _nativeTestsRunner.UpdateTestCasesHistory(mergedTestRun, assemblyName); await _testCasesHistoryService.UpdateTestCaseExecutionHistoryAsync(testCaseRuns); // DEBUG: var endTime = _dateTimeProvider.GetCurrentTime(); _testRunLogService.CreateTestRunLogAsync($"END updating test case history- on machine {Environment.MachineName} for {(endTime - startTime).Seconds} seconds", _currentTestRunId).Wait(); } result = mergedTestRun; } return(result); }