Skip to content
Merged
Show file tree
Hide file tree
Changes from 6 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
Original file line number Diff line number Diff line change
Expand Up @@ -336,6 +336,10 @@ internal static Dictionary<string, object> GetUsingValuesForEachParallel(
// Foreach-Object -Parallel call scope. This will filter the using variable map to variables
// only within the current (outer) Foreach-Object -Parallel call scope.
var usingAsts = UsingExpressionAstSearcher.FindAllUsingExpressions(scriptBlock.Ast).ToList();

// If the scriptblock ast parent is null, then it comes from a scriptblock variable and does
Comment thread
PaulHigin marked this conversation as resolved.
Outdated
// not include the initiating Foreach-Object -Parallel command.
bool astIncludesForEachCommand = scriptBlock.Ast.Parent is not null;
Comment thread
PaulHigin marked this conversation as resolved.
Outdated
UsingExpressionAst usingAst = null;
var usingValueMap = new Dictionary<string, object>(usingAsts.Count);
Version oldStrictVersion = null;
Expand All @@ -350,7 +354,10 @@ internal static Dictionary<string, object> GetUsingValuesForEachParallel(
for (int i = 0; i < usingAsts.Count; ++i)
{
usingAst = (UsingExpressionAst)usingAsts[i];
if (IsInForeachParallelCallingScope(usingAst, foreachNames))
if (IsInForeachParallelCallingScope(
usingAst: usingAst,
astIncludesForEachCommand: astIncludesForEachCommand,
foreachNames: foreachNames))
{
var value = Compiler.GetExpressionValue(usingAst.SubExpression, isTrustedInput, context);
string usingAstKey = PsUtils.GetUsingExpressionKey(usingAst);
Expand Down Expand Up @@ -387,10 +394,17 @@ internal static Dictionary<string, object> GetUsingValuesForEachParallel(
/// and parameter set scope, and not from within a nested foreach-object -parallel call.
/// </summary>
/// <param name="usingAst">Using Ast to check.</param>
/// <param name="astIncludesForEachCommand">
/// True when the provided ast includes a parent containing the originating ForEach-Object command
/// e.g.,
/// '1 | ForEach-Object -Parallel { }' - script block ast includes parent with initial 'ForEach-Object'
/// '1 | ForEach-Object -Parallel $scriptBlock' - script block ast *does not* include parent with initial 'ForEach-Object'
/// </param>
/// <param name-"foreachNames">List of foreach-object command names.</param>
/// <returns>True if using expression is in current call scope.</returns>
private static bool IsInForeachParallelCallingScope(
UsingExpressionAst usingAst,
bool astIncludesForEachCommand,
string[] foreachNames)
{
/*
Expand Down Expand Up @@ -452,7 +466,8 @@ private static bool IsInForeachParallelCallingScope(
currentParent = currentParent.Parent;
}

return foreachNestedCount == 1;
// Expected foreachNestedCount depends on provided script block ast (see above).
return astIncludesForEachCommand ? foreachNestedCount == 1 : foreachNestedCount == 0;
}

/// <summary>
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -87,6 +87,101 @@ Describe 'ForEach-Object -Parallel Basic Tests' -Tags 'CI' {
$usingErrors[0].FullyQualifiedErrorId | Should -BeExactly 'UsingVariableIsUndefined,Microsoft.PowerShell.Commands.ForEachObjectCommand'
}

It 'Verifies using variables with passed-in script block object' {

$var = "Hello"
$varArray = "Hello","There"
$sBlock = [scriptblock]::Create('$using:var; $using:varArray[1]')
$result = 1..1 | ForEach-Object -Parallel $sBlock
$result[0] | Should -BeExactly $var
$result[1] | Should -BeExactly $varArray[1]
}

It 'Verifies in scope using same-name variables in nested calls for passed-in script block objects' {

$Test1 = "Test1"
$sBlock = [scriptblock]::Create(@'
$using:Test1
$Test2 = "Test2"
$sBlock2 = [scriptblock]::Create('$using:Test2')
1..2 | ForEach-Object -Parallel $sBlock2
'@)
$results = 1..2 | ForEach-Object -Parallel $sBlock
$results.Count | Should -BeExactly 6
$groups = $results | Group-Object -AsHashTable
$groups['Test1'].Count | Should -BeExactly 2
$groups['Test2'].Count | Should -BeExactly 4
}

It 'Verifies in scope using same-name variables in nested calls for mixed script block objects' {

$Test1 = "Test1"
$sBlock = [scriptblock]::Create(@'
$using:Test1
$Test2 = "Test2"
1..2 | ForEach-Object -Parallel { $using:Test2 }
'@)
$results = 1..2 | ForEach-Object -Parallel $sBlock
$results.Count | Should -BeExactly 6
$groups = $results | Group-Object -AsHashTable
$groups['Test1'].Count | Should -BeExactly 2
$groups['Test2'].Count | Should -BeExactly 4
}

It 'Verifies in scope using different-name variables in nested calls for passed-in script block objects' {

$Test1 = "Test1"
$sBlock = [scriptblock]::Create(@'
$using:Test1
$Test2 = "Test2"
$sBlock2 = [scriptblock]::Create('$using:Test2')
1..2 | ForEach-Object -Parallel $sBlock2
'@)
$results = 1..2 | ForEach-Object -Parallel $sBlock
$results.Count | Should -BeExactly 6
$groups = $results | Group-Object -AsHashTable
$groups['Test1'].Count | Should -BeExactly 2
$groups['Test2'].Count | Should -BeExactly 4
}

It 'Verifies in scope using different-name variables in nested calls for mixed script block objects' {

$Test1 = "Test1"
$sBlock = [scriptblock]::Create(@'
$using:Test1
$Test2 = "Test2"
1..2 | ForEach-Object -Parallel { $using:Test2 }
'@)
$results = 1..2 | ForEach-Object -Parallel $sBlock
$results.Count | Should -BeExactly 6
$groups = $results | Group-Object -AsHashTable
$groups['Test1'].Count | Should -BeExactly 2
$groups['Test2'].Count | Should -BeExactly 4
}

It 'Verifies expected error for out of scope using variable in nested calls for passed-in script block objects' {

$Test = "TestZ"
$sBlock = [scriptblock]::Create(@'
$using:Test
$sBlock2 = [scriptblock]::Create('$using:Test')
1..1 | ForEach-Object -Parallel $sBlock2
'@)
1..1 | ForEach-Object -Parallel $SBlock -ErrorVariable usingErrors 2>$null
$usingErrors[0].FullyQualifiedErrorId | Should -BeExactly 'UsingVariableIsUndefined,Microsoft.PowerShell.Commands.ForEachObjectCommand'
}

It 'Verifies expected error for out of scope using variable in nested calls for mixed script block objects' {

$Test = "TestZ"
$sBlock = [scriptblock]::Create(@'
$using:Test
1..1 | ForEach-Object -Parallel { $using:Test }
'@)
1..1 | ForEach-Object -Parallel $SBlock -ErrorVariable usingErrors 2>$null
$usingErrors[0].FullyQualifiedErrorId | Should -BeExactly 'UsingVariableIsUndefined,Microsoft.PowerShell.Commands.ForEachObjectCommand'
}

It 'Verifies terminating error streaming' {

$result = 1..1 | ForEach-Object -Parallel { throw 'Terminating Error!'; "Hello" } 2>&1
Expand Down Expand Up @@ -150,7 +245,7 @@ Describe 'ForEach-Object -Parallel Basic Tests' -Tags 'CI' {
$pr[0].StatusDescription | Should -Be Running
$ps.Dispose()
}

It 'Verifies information data streaming' {

$actualInformation = 1..1 | ForEach-Object -Parallel { Write-Information "Information!" } 6>&1
Expand Down