diff --git a/src/Pester.Runtime.ps1 b/src/Pester.Runtime.ps1 index 6f97e91ad..bfee2b8d2 100644 --- a/src/Pester.Runtime.ps1 +++ b/src/Pester.Runtime.ps1 @@ -1029,6 +1029,11 @@ function Invoke-ContainerDiscovery { # set correctly as if we provided -Data to New-Block $root.Data = $Container.Data + # Record which Pester.BeforeContainer.ps1 files applied, so the result says where a + # container's setup came from. The folder tree alone does not answer it, #pester:no-inherit + # can cut the chain short. + $root.BeforeContainerFile = [System.Collections.Generic.List[string]]@($BeforeContainerFile) + Reset-PerContainerState -RootBlock $root $steps = $state.Plugin.ContainerDiscoveryStart diff --git a/src/csharp/Pester/Block.cs b/src/csharp/Pester/Block.cs index a6b50f609..764db6386 100644 --- a/src/csharp/Pester/Block.cs +++ b/src/csharp/Pester/Block.cs @@ -57,6 +57,10 @@ public Block() public string ItemType { get; } = "Block"; public ContainerInfo BlockContainer { get; set; } + // Every Pester.BeforeContainer.ps1 that applied to this container, outermost first. + // Reading the folder tree is not enough to work this out, because #pester:no-inherit can + // cut the chain short. + public List BeforeContainerFile { get; set; } = new List(); public object Root { get; set; } public bool IsRoot { get; set; } public object Parent { get; set; } diff --git a/src/csharp/Pester/Container.cs b/src/csharp/Pester/Container.cs index b3532fe11..2a1ec46c2 100644 --- a/src/csharp/Pester/Container.cs +++ b/src/csharp/Pester/Container.cs @@ -34,7 +34,8 @@ public static Container CreateFromBlock (Block block) { Item = block.BlockContainer.Item, Blocks = block.Blocks, Data = block.Data, - StandardOutput = block.StandardOutput + StandardOutput = block.StandardOutput, + BeforeContainerFile = block.BeforeContainerFile }; } @@ -56,6 +57,9 @@ public string Type } public object Item { get; set; } public object Data { get; set; } + // Every Pester.BeforeContainer.ps1 that applied to this container, outermost first, so + // "where did this function come from" is answerable from the result object. + public List BeforeContainerFile { get; set; } = new List(); public List Blocks { get; set; } = new List(); public string Result { get; set; } = "NotRun"; public TimeSpan Duration { get => DiscoveryDuration + UserDuration + FrameworkDuration; } diff --git a/tst/Pester.RSpec.Parallel.ts.ps1 b/tst/Pester.RSpec.Parallel.ts.ps1 index 52b2aefbc..ffd635079 100644 --- a/tst/Pester.RSpec.Parallel.ts.ps1 +++ b/tst/Pester.RSpec.Parallel.ts.ps1 @@ -479,6 +479,54 @@ Describe 'Second' { finally { Remove-Item -Path $folder -Recurse -Force } } + t "reports which Pester.BeforeContainer.ps1 files applied to each container" { + # The folder tree alone does not answer this, #pester:no-inherit can cut the chain + # short, so the applied files are on the container. (#3007) + $folder = New-CascadingBeforeContainerFolder + try { + $c = [PesterConfiguration]::Default + $c.Run.Path = $folder + $c.Run.RepoRoot = $folder + $c.Run.PassThru = $true + $c.Output.Verbosity = 'None' + $r = Invoke-Pester -Configuration $c + + $r.FailedCount | Verify-Equal 0 + + $unit = $r.Containers | Where-Object { $_.Item.FullName -like '*unit*' } + $integration = $r.Containers | Where-Object { $_.Item.FullName -like '*integration*' } + + # unit sees root + tests + unit, integration sees root + tests, outermost first. + $unit.BeforeContainerFile.Count | Verify-Equal 3 + $integration.BeforeContainerFile.Count | Verify-Equal 2 + (Split-Path $unit.BeforeContainerFile[0] -Parent) | Verify-Equal $folder + } + finally { Remove-Item -Path $folder -Recurse -Force } + } + + t "reports only the files that survived #pester:no-inherit" { + $folder = New-NoInheritBeforeContainerFolder + try { + $c = [PesterConfiguration]::Default + $c.Run.Path = $folder + $c.Run.RepoRoot = $folder + $c.Run.PassThru = $true + $c.Output.Verbosity = 'None' + $r = Invoke-Pester -Configuration $c + + $r.FailedCount | Verify-Equal 0 + + $docs = $r.Containers | Where-Object { $_.Item.FullName -like '*docs*' } + $normal = $r.Containers | Where-Object { $_.Item.FullName -notlike '*docs*' } + + # docs cut the chain, so it reports only its own file, not the root one. + $docs.BeforeContainerFile.Count | Verify-Equal 1 + $normal.BeforeContainerFile.Count | Verify-Equal 1 + (Split-Path $docs.BeforeContainerFile[0] -Parent) | Verify-Equal (Join-Path $folder 'docs') + } + finally { Remove-Item -Path $folder -Recurse -Force } + } + t "does not overwrite a Run.RepoRoot the user set" { # A real directory. Resolving the chain runs the value through GetFullPath, and a made # up path is not portable: on Windows something like 'TestDrive:whatever' reads as a