Skip to content
Closed
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
5 changes: 3 additions & 2 deletions src/Main.ps1
Original file line number Diff line number Diff line change
Expand Up @@ -663,7 +663,8 @@ function Invoke-Pester {
# If every file opted out with #pester:no-parallel, the run is effectively sequential,
# so fall through to the sequential path - which fires the framework's own global
# plugin steps at the correct interleaved points.
if ($useParallel -and $parallelSupported -and $allFileContainers -and -not $coverageEnabled -and -not $skipRemainingRunScope -and 0 -lt $parallelContainers.Count) {
$ranInParallel = $useParallel -and $parallelSupported -and $allFileContainers -and -not $coverageEnabled -and -not $skipRemainingRunScope -and 0 -lt $parallelContainers.Count
if ($ranInParallel) {
$foldedContainers = [System.Collections.Generic.List[object]]@()
$hasNonParallel = 0 -lt $nonParallelContainers.Count

Expand Down Expand Up @@ -866,7 +867,7 @@ function Invoke-Pester {
$run.Containers.Add($i)
}

PostProcess-RSpecTestRun -TestRun $run
PostProcess-RSpecTestRun -TestRun $run -Parallel:$ranInParallel

$steps = $Plugins.End
if ($null -ne $steps -and 0 -lt @($steps).Count) {
Expand Down
13 changes: 12 additions & 1 deletion src/Pester.RSpec.ps1
Original file line number Diff line number Diff line change
Expand Up @@ -178,7 +178,7 @@ function Add-RSpecBlockObjectProperties ($BlockObject) {
}
}

function PostProcess-RspecTestRun ($TestRun) {
function PostProcess-RspecTestRun ($TestRun, [switch] $Parallel) {
$discoveryOnly = $PesterPreference.Run.SkipRun.Value

Fold-Run $Run -OnTest {
Expand Down Expand Up @@ -276,12 +276,23 @@ function PostProcess-RspecTestRun ($TestRun) {
$TestRun.FailedContainers.Add($b)
}

# Sum the per-phase durations across containers. These are cumulative measurements of the
# time spent in user code, in the framework and in discovery, so they are valid in both
# sequential and parallel runs - in a parallel run they add up to the total work done
# across the overlapping workers.
$TestRun.Duration += $b.Duration
$TestRun.UserDuration += $b.UserDuration
$TestRun.FrameworkDuration += $b.FrameworkDuration
$TestRun.DiscoveryDuration += $b.DiscoveryDuration
}

if ($Parallel) {
# The summed container Duration is the total work done, which overstates a parallel run
# because the files overlap in time. Measure the actual elapsed wall-clock for the run
# total instead; the per-phase totals stay as the cumulative work across the workers.
$TestRun.Duration = [DateTime]::Now - $TestRun.ExecutedAt
}

$TestRun.PassedCount = $TestRun.Passed.Count
$TestRun.FailedCount = $TestRun.Failed.Count
$TestRun.SkippedCount = $TestRun.Skipped.Count
Expand Down
37 changes: 37 additions & 0 deletions tst/Pester.RSpec.Parallel.ts.ps1
Original file line number Diff line number Diff line change
Expand Up @@ -103,6 +103,43 @@ i -PassThru:$PassThru {
}
}

b "Run duration in parallel mode" {
t "uses the measured wall-clock for the total and still sums the per-phase work" {
# Two files that each sleep run sequentially would total ~1.2s; in parallel the wall-clock
# is closer to a single file, so summing container durations overstates the run. (#2794)
$folder = Join-Path ([IO.Path]::GetTempPath()) ([Guid]::NewGuid().Guid)
$null = New-Item -ItemType Directory -Path $folder -Force
Set-Content -Path (Join-Path $folder 'Slow1.Tests.ps1') -Value @'
Describe 'Slow1' { BeforeAll { Start-Sleep -Milliseconds 600 }; It 'p' { 1 | Should -Be 1 } }
'@
Set-Content -Path (Join-Path $folder 'Slow2.Tests.ps1') -Value @'
Describe 'Slow2' { BeforeAll { Start-Sleep -Milliseconds 600 }; It 'p' { 1 | Should -Be 1 } }
'@
try {
$c = [PesterConfiguration]::Default
$c.Run.Path = $folder
$c.Run.Parallel = $true
$c.Run.PassThru = $true
$c.Output.Verbosity = 'None'
$r = Invoke-Pester -Configuration $c

if ($PSVersionTable.PSVersion.Major -ge 7) {
$containerSum = [TimeSpan]::Zero
foreach ($container in $r.Containers) { $containerSum += $container.Duration }
# Wall-clock run duration is measured, so it is less than the naive sum of the
# two slow files that overlap in parallel.
($r.Duration -lt $containerSum) | Verify-True
($r.Duration -gt [TimeSpan]::Zero) | Verify-True
# Per-phase totals are still measured (summed across the overlapping workers),
# not blanked, so they add up to the total container work.
($r.UserDuration -gt [TimeSpan]::Zero) | Verify-True
($r.DiscoveryDuration + $r.UserDuration + $r.FrameworkDuration) | Verify-Equal $containerSum
}
}
finally { Remove-Item -Path $folder -Recurse -Force }
}
}

b "Run.Parallel data passing" {
t "passes container -Data to each parallel worker's param() block" {
# New-PesterContainer -Path ... -Data must bind the file's param() block under parallel
Expand Down
Loading