diff --git a/Tasks/RunOpenCover/Helpers.ps1 b/Tasks/RunOpenCover/Helpers.ps1 index f929d9d..2146fcc 100644 --- a/Tasks/RunOpenCover/Helpers.ps1 +++ b/Tasks/RunOpenCover/Helpers.ps1 @@ -234,4 +234,63 @@ function CheckIfDirectory($filePath) return $true } return $false +} + +function SetupRunSettingsFileForParallel { + [cmdletbinding()] + [OutputType([System.String])] + param( + [string]$runInParallelFlag, + [string]$runSettingsFilePath, + [string]$defaultCpuCount + ) + + if($runInParallelFlag -eq "True") + { + if([string]::Compare([io.path]::GetExtension($runSettingsFilePath), ".testsettings", $True) -eq 0) + { + Write-Warning "Run in Parallel is not supported with testsettings file." + } + else + { + $runSettingsForParallel = [xml]'' + if([System.String]::IsNullOrWhiteSpace($runSettingsFilePath) ` + -Or ([string]::Compare([io.path]::GetExtension($runSettingsFilePath), ".runsettings", $True) -ne 0) ` + -Or (Test-Path $runSettingsFilePath -pathtype container)) # no file provided so create one and use it for the run + { + Write-Verbose "No runsettings file provided" + $runSettingsForParallel = [xml]' + + + 0 + + + ' + } + else + { + Write-Verbose "Adding maxcpucount element to runsettings file provided" + $runSettingsForParallel = [System.Xml.XmlDocument](Get-Content $runSettingsFilePath) + $runConfigurationElement = $runSettingsForParallel.SelectNodes("//RunSettings/RunConfiguration") + if($runConfigurationElement.Count -eq 0) + { + $runConfigurationElement = $runSettingsForParallel.RunSettings.AppendChild($runSettingsForParallel.CreateElement("RunConfiguration")) + } + + $maxCpuCountElement = $runSettingsForParallel.SelectNodes("//RunSettings/RunConfiguration/MaxCpuCount") + if($maxCpuCountElement.Count -eq 0) + { + $runConfigurationElement.AppendChild($runSettingsForParallel.CreateElement("MaxCpuCount")) + } + } + + $runSettingsForParallel.RunSettings.RunConfiguration.MaxCpuCount = $defaultCpuCount + $tempFile = [io.path]::GetTempFileName() + $runSettingsForParallel.Save($tempFile) + Write-Verbose "Temporary runsettings file created at $tempFile" + return $tempFile + } + } + + return $runSettingsFilePath } \ No newline at end of file diff --git a/Tasks/RunOpenCover/README.md b/Tasks/RunOpenCover/README.md index ca0e187..b658339 100644 --- a/Tasks/RunOpenCover/README.md +++ b/Tasks/RunOpenCover/README.md @@ -38,6 +38,8 @@ Use the following options to select tests and control how the tests are run and - **Run Settings File:** Path to a runsettings or testsettings file can be specified here. The path can be to a file in the repository or a path to file on disk. Use $(Build.SourcesDirectory) to access the root project folder. [Click here](https://msdn.microsoft.com/library/jj635153.aspx) for more information on these files. +- **Run in Parallel:** If set, tests will run in parallel leveraging available cores of the machine. [Click here](https://aka.ms/paralleltestexecution) to learn more about how tests are run in parallel. + - **OpenCover Filter Criteria:** Filter what should be considered by OpenCover for code coverage. For example, "-\*[\*Tests.\*]". - **Disable OpenCover:** This allows you to only run the tests and not run OpenCover. Thus, the tasks behaves similar to to stock VSTest task from TFS / VSTS. diff --git a/Tasks/RunOpenCover/RunOpenCover.ps1 b/Tasks/RunOpenCover/RunOpenCover.ps1 index ae4c72a..2514af0 100644 --- a/Tasks/RunOpenCover/RunOpenCover.ps1 +++ b/Tasks/RunOpenCover/RunOpenCover.ps1 @@ -170,6 +170,10 @@ try { # we should escape them by a backslash. $vsconsoleArgs = $vsconsoleArgs.Replace('"', '\"') + $openCoverReport = "$tempDir\OpenCover.xml" + $coberturaReport = "$tempDir\Cobertura.xml" + $reportDirectory = "$tempDir\CoverageReport" + $openCoverConsoleArgs = "-register:user" if ($openCoverFilters) { # Only append filters, if there actually is a value. This way, @@ -181,7 +185,7 @@ try { $openCoverConsoleArgs += " -target:""$vsconsoleExe""" $openCoverConsoleArgs += " -targetargs:""$vsconsoleArgs""" $openCoverConsoleArgs += " -mergeoutput" - $openCoverConsoleArgs += " -output:""$tempDir\OpenCover.xml""" + $openCoverConsoleArgs += " -output:""$openCoverReport""" $openCoverConsoleArgs += " -mergebyhash" $openCoverConsoleArgs += " -returntargetcode" if ($openCoverAdditionalCommandLine) { @@ -189,10 +193,6 @@ try { $openCoverConsoleArgs += $openCoverAdditionalCommandLine } - $openCoverReport = "$tempDir\OpenCover.xml" - $coberturaReport = "$tempDir\Cobertura.xml" - $reportDirectory = "$tempDir\CoverageReport" - $coberturaConverterArgs = "-input:""$openCoverReport""" $coberturaConverterArgs += " -output:""$coberturaReport""" $coberturaConverterArgs += " -sources:""$sourcesDirectory""" diff --git a/Tasks/RunOpenCover/RunOpenCoverTask.ps1 b/Tasks/RunOpenCover/RunOpenCoverTask.ps1 index 083e12d..d9511a4 100644 --- a/Tasks/RunOpenCover/RunOpenCoverTask.ps1 +++ b/Tasks/RunOpenCover/RunOpenCoverTask.ps1 @@ -25,6 +25,7 @@ try { $toolsLocationMethod = Get-VstsInput -Name toolsLocationMethod $toolsBaseDirectory = Get-VstsInput -Name toolsBaseDirectory $runSettingsFile = Get-VstsInput -Name runSettingsFile + $runInParallel = Get-VstsInput -Name runInParallel -AsBool Write-Verbose "SourcesDirectory: $sourcesDirectory" Write-Verbose "testAssembly: $testAssembly" @@ -44,6 +45,7 @@ try { Write-Verbose "runSettingsFile: $runSettingsFile" Write-Verbose "toolsLocationMethod: $toolsLocationMethod" Write-Verbose "toolsBaseDirectory: $toolsBaseDirectory" + Write-Verbose "runInParallel: $runInParallel" if ($toolsLocationMethod -and $toolsLocationMethod -eq "location") { if (-Not (Test-Path $toolsBaseDirectory)) { @@ -101,6 +103,9 @@ try { } } + $defaultCpuCount = "0" + $runSettingsFileWithParallel = [string](SetupRunSettingsFileForParallel $runInParallel $runSettingsFile $defaultCpuCount) + . $PSScriptRoot\RunOpenCover.ps1 ` -sourcesDirectory $sourcesDirectory ` -testAssembly $testAssembly ` @@ -117,7 +122,7 @@ try { -publishRunAttachments:$publishRunAttachments ` -taskMode ` -toolsBaseDirectory $toolsBaseDirectory ` - -runSettingsFile $runSettingsFile + -runSettingsFile $runSettingsFileWithParallel } catch { diff --git a/Tasks/RunOpenCover/Strings/resources.resjson/en-US/resources.resjson b/Tasks/RunOpenCover/Strings/resources.resjson/en-US/resources.resjson index c807bd8..fb3bc25 100644 --- a/Tasks/RunOpenCover/Strings/resources.resjson/en-US/resources.resjson +++ b/Tasks/RunOpenCover/Strings/resources.resjson/en-US/resources.resjson @@ -14,6 +14,8 @@ "loc.input.help.testFiltercriteria": "Additional criteria to filter tests from Test assemblies. For example: Priority=1|Name=MyTestMethod", "loc.input.label.runSettingsFile": "Run Settings File", "loc.input.help.runSettingsFile": "Path to runsettings file to use with the tests. Use `$(Build.SourcesDirectory)` to access the Project folder.", + "loc.input.label.runInParallel": "Run In Parallel (Experimental)", + "loc.input.help.runInParallel": "Enable parallel execution of your tests (Experimental).", "loc.input.label.openCoverFilters": "OpenCover Filter criteria", "loc.input.help.openCoverFilters": "Additional criteria to filter what OpenCover considers.", "loc.input.label.disableCodeCoverage": "Disable OpenCover", diff --git a/Tasks/RunOpenCover/task.json b/Tasks/RunOpenCover/task.json index 80e9ed5..081eff1 100644 --- a/Tasks/RunOpenCover/task.json +++ b/Tasks/RunOpenCover/task.json @@ -19,7 +19,7 @@ "version": { "Major": 1, "Minor": 0, - "Patch": 11 + "Patch": 15 }, "minimumAgentVersion": "1.95.0", "instanceNameFormat": "Tests/Coverage - $(testAssembly)", @@ -75,6 +75,15 @@ "helpMarkDown": "Path to runsettings file to use with the tests. Use `$(Build.SourcesDirectory)` to access the Project folder.", "groupName": "executionOptions" }, + { + "name": "runInParallel", + "type": "boolean", + "label": "Run In Parallel", + "defaultValue": "false", + "required": false, + "helpMarkDown": "Enable parallel execution of your tests.", + "groupName": "executionOptions" + }, { "name": "openCoverFilters", "type": "string", diff --git a/Tasks/RunOpenCover/task.loc.json b/Tasks/RunOpenCover/task.loc.json index 9d3851d..34d0ea4 100644 --- a/Tasks/RunOpenCover/task.loc.json +++ b/Tasks/RunOpenCover/task.loc.json @@ -19,7 +19,7 @@ "version": { "Major": 1, "Minor": 0, - "Patch": 11 + "Patch": 15 }, "minimumAgentVersion": "1.95.0", "instanceNameFormat": "ms-resource:loc.instanceNameFormat", @@ -75,6 +75,15 @@ "helpMarkDown": "ms-resource:loc.input.help.runSettingsFile", "groupName": "executionOptions" }, + { + "name": "runInParallel", + "type": "boolean", + "label": "ms-resource:loc.input.label.runInParallel", + "defaultValue": "false", + "required": false, + "helpMarkDown": "ms-resource:loc.input.help.runInParallel", + "groupName": "executionOptions" + }, { "name": "openCoverFilters", "type": "string",