From 893a3cb977d32b49f0ca41fd21ec20f0351c75a2 Mon Sep 17 00:00:00 2001 From: iSazonov Date: Fri, 29 Dec 2017 17:44:16 +0300 Subject: [PATCH 1/5] Add ForEach and Where methods to [PSCustomobject] --- .../engine/runtime/Binding/Binders.cs | 26 ++ test/powershell/engine/ETS/Adapter.Tests.ps1 | 301 +++++++++++------- 2 files changed, 219 insertions(+), 108 deletions(-) diff --git a/src/System.Management.Automation/engine/runtime/Binding/Binders.cs b/src/System.Management.Automation/engine/runtime/Binding/Binders.cs index a43841d7317..1ba6af7b371 100644 --- a/src/System.Management.Automation/engine/runtime/Binding/Binders.cs +++ b/src/System.Management.Automation/engine/runtime/Binding/Binders.cs @@ -7095,6 +7095,32 @@ internal static object InvokeAdaptedMember(object obj, string methodName, object return methodInfo.Invoke(args); } + // The object haven't Where and ForEach methods. + // As last resort, we invoking Where and ForEach operators on singletions like + // ([pscustomobject]@{ foo = 'bar' }).Foreach({$_}) + // ([pscustomobject]@{ foo = 'bar' }).Where({1}) + if (string.Equals(methodName, "Where", StringComparison.OrdinalIgnoreCase)) + { + var enumerator = (new object[] {obj}).GetEnumerator(); + switch (args.Length) + { + case 1: + return EnumerableOps.Where(enumerator, args[0] as ScriptBlock, WhereOperatorSelectionMode.Default, 0); + case 2: + return EnumerableOps.Where(enumerator, args[0] as ScriptBlock, + (WhereOperatorSelectionMode)Enum.Parse(typeof(WhereOperatorSelectionMode), args[1].ToString()), 0); + case 3: + return EnumerableOps.Where(enumerator, args[0] as ScriptBlock, + (WhereOperatorSelectionMode)Enum.Parse(typeof(WhereOperatorSelectionMode), args[1].ToString()), int.Parse(args[2].ToString())); + } + } + + if (string.Equals(methodName, "Foreach", StringComparison.OrdinalIgnoreCase)) + { + var enumerator = (new object[] {obj}).GetEnumerator(); + return EnumerableOps.ForEach(enumerator, args[0], Utils.EmptyArray()); + } + throw InterpreterError.NewInterpreterException(methodName, typeof(RuntimeException), null, "MethodNotFound", ParserStrings.MethodNotFound, ParserOps.GetTypeFullName(obj), methodName); } diff --git a/test/powershell/engine/ETS/Adapter.Tests.ps1 b/test/powershell/engine/ETS/Adapter.Tests.ps1 index a9ba2159735..5f93015f801 100644 --- a/test/powershell/engine/ETS/Adapter.Tests.ps1 +++ b/test/powershell/engine/ETS/Adapter.Tests.ps1 @@ -1,143 +1,228 @@ Describe "Adapter Tests" -tags "CI" { - BeforeAll { - $pso = [System.Diagnostics.Process]::GetCurrentProcess() - $processName = $pso.Name - - if(-not ('TestCodeMethodClass' -as "type")) - { - class TestCodeMethodClass { - static [int] TestCodeMethod([PSObject] $target, [int] $i) - { - return 1; + Context "Property Adapter Tests" { + BeforeAll { + $pso = [System.Diagnostics.Process]::GetCurrentProcess() + $processName = $pso.Name + + if(-not ('TestCodeMethodClass' -as "type")) + { + class TestCodeMethodClass { + static [int] TestCodeMethod([PSObject] $target, [int] $i) + { + return 1; + } } } + + $psmemberset = new-object System.Management.Automation.PSMemberSet 'setname1' + $psmemberset | Add-Member -MemberType NoteProperty -Name NoteName -Value 1 + $testmethod = [TestCodeMethodClass].GetMethod("TestCodeMethod") + $psmemberset | Add-Member -MemberType CodeMethod -Name TestCodeMethod -Value $testmethod + + $document = new-object System.Xml.XmlDocument + $document.LoadXml("Pride And Prejudice19.95") + $doc = $document.DocumentElement + } + It "can get a Dotnet parameterized property" { + $col = $pso.psobject.Properties.Match("*") + $prop = $col.psobject.Members["Item"] + $prop | Should Not BeNullOrEmpty + $prop.IsGettable | Should be $true + $prop.IsSettable | Should be $false + $prop.TypeNameOfValue | Should be "System.Management.Automation.PSPropertyInfo" + $prop.Invoke("ProcessName").Value | Should be $processName } - $psmemberset = new-object System.Management.Automation.PSMemberSet 'setname1' - $psmemberset | Add-Member -MemberType NoteProperty -Name NoteName -Value 1 - $testmethod = [TestCodeMethodClass].GetMethod("TestCodeMethod") - $psmemberset | Add-Member -MemberType CodeMethod -Name TestCodeMethod -Value $testmethod + It "can get a property" { + $pso.psobject.Properties["ProcessName"] | should Not BeNullOrEmpty + } - $document = new-object System.Xml.XmlDocument - $document.LoadXml("Pride And Prejudice19.95") - $doc = $document.DocumentElement - } - It "can get a Dotnet parameterized property" { - $col = $pso.psobject.Properties.Match("*") - $prop = $col.psobject.Members["Item"] - $prop | Should Not BeNullOrEmpty - $prop.IsGettable | Should be $true - $prop.IsSettable | Should be $false - $prop.TypeNameOfValue | Should be "System.Management.Automation.PSPropertyInfo" - $prop.Invoke("ProcessName").Value | Should be $processName - } + It "Can access all properties" { + $props = $pso.psobject.Properties.Match("*") + $props | should Not BeNullOrEmpty + $props["ProcessName"].Value |Should be $processName + } - It "can get a property" { - $pso.psobject.Properties["ProcessName"] | should Not BeNullOrEmpty - } + It "Can invoke a method" { + $method = $pso.psobject.Methods["ToString"] + $method.Invoke() | should be ($pso.ToString()) + } - It "Can access all properties" { - $props = $pso.psobject.Properties.Match("*") - $props | should Not BeNullOrEmpty - $props["ProcessName"].Value |Should be $processName - } + It "Access a Method via MemberSet adapter" { + $prop = $psmemberset.psobject.Members["TestCodeMethod"] + $prop.Invoke(2) | Should be 1 + } - It "Can invoke a method" { - $method = $pso.psobject.Methods["ToString"] - $method.Invoke() | should be ($pso.ToString()) - } + It "Access misc properties via MemberSet adapter" { + $prop = $psmemberset.psobject.Properties["NoteName"] + $prop | Should Not BeNullOrEmpty + $prop.IsGettable | Should be $true + $prop.IsSettable | Should be $true + $prop.TypeNameOfValue | Should be "System.Int32" + } - It "Access a Method via MemberSet adapter" { - $prop = $psmemberset.psobject.Members["TestCodeMethod"] - $prop.Invoke(2) | Should be 1 - } + It "Access all the properties via XmlAdapter" { + $col = $doc.psobject.Properties.Match("*") + $col.Count | Should Not Be 0 + $prop = $col["price"] + $prop | Should Not BeNullOrEmpty + } - It "Access misc properties via MemberSet adapter" { - $prop = $psmemberset.psobject.Properties["NoteName"] - $prop | Should Not BeNullOrEmpty - $prop.IsGettable | Should be $true - $prop.IsSettable | Should be $true - $prop.TypeNameOfValue | Should be "System.Int32" - } + It "Access all the properties via XmlAdapter" { + $prop = $doc.psobject.Properties["price"] + $prop.Value | Should Be "19.95" + $prop.IsGettable | Should Not BeNullOrEmpty + $prop.IsSettable | Should Not BeNullOrEmpty + $prop.TypeNameOfValue | Should be "System.String" + } - It "Access all the properties via XmlAdapter" { - $col = $doc.psobject.Properties.Match("*") - $col.Count | Should Not Be 0 - $prop = $col["price"] - $prop | Should Not BeNullOrEmpty - } + It "Call to string on a XmlNode object" { + $val = $doc.ToString() + $val | Should Be "book" + } - It "Access all the properties via XmlAdapter" { - $prop = $doc.psobject.Properties["price"] - $prop.Value | Should Be "19.95" - $prop.IsGettable | Should Not BeNullOrEmpty - $prop.IsSettable | Should Not BeNullOrEmpty - $prop.TypeNameOfValue | Should be "System.String" - } + It "Calls CodeMethod with void result" { - It "Call to string on a XmlNode object" { - $val = $doc.ToString() - $val | Should Be "book" - } + class TestCodeMethodInvokationWithVoidReturn { + [int] $CallCounter - It "Calls CodeMethod with void result" { + static [int] IntMethodCM([PSObject] $self) { + return $self.CallCounter + } - class TestCodeMethodInvokationWithVoidReturn { - [int] $CallCounter + static [void] VoidMethodCM([PSObject] $self) { + $self.CallCounter++ + } - static [int] IntMethodCM([PSObject] $self) { - return $self.CallCounter + static [Reflection.MethodInfo] GetMethodInfo([string] $name) { + return [TestCodeMethodInvokationWithVoidReturn].GetMethod($name) + } } - static [void] VoidMethodCM([PSObject] $self) { - $self.CallCounter++ - } + Update-TypeData -Force -TypeName TestCodeMethodInvokationWithVoidReturn -MemberType CodeMethod -MemberName IntMethod -Value ([TestCodeMethodInvokationWithVoidReturn]::GetMethodInfo('IntMethodCM')) + Update-TypeData -Force -TypeName TestCodeMethodInvokationWithVoidReturn -MemberType CodeMethod -MemberName VoidMethod -Value ([TestCodeMethodInvokationWithVoidReturn]::GetMethodInfo('VoidMethodCM')) + try { + $o = [TestCodeMethodInvokationWithVoidReturn]::new() + $o.CallCounter | Should Be 0 + $o.VoidMethod() + $o.CallCounter | Should be 1 - static [Reflection.MethodInfo] GetMethodInfo([string] $name) { - return [TestCodeMethodInvokationWithVoidReturn].GetMethod($name) + $o.IntMethod() | Should be 1 + } + finally { + Remove-TypeData TestCodeMethodInvokationWithVoidReturn } } - Update-TypeData -Force -TypeName TestCodeMethodInvokationWithVoidReturn -MemberType CodeMethod -MemberName IntMethod -Value ([TestCodeMethodInvokationWithVoidReturn]::GetMethodInfo('IntMethodCM')) - Update-TypeData -Force -TypeName TestCodeMethodInvokationWithVoidReturn -MemberType CodeMethod -MemberName VoidMethod -Value ([TestCodeMethodInvokationWithVoidReturn]::GetMethodInfo('VoidMethodCM')) - try { - $o = [TestCodeMethodInvokationWithVoidReturn]::new() - $o.CallCounter | Should Be 0 - $o.VoidMethod() - $o.CallCounter | Should be 1 + It "Count and length property works for singletons" { + # Return magic Count and Length property if it absent. + $x = 5 + $x.Count | Should Be 1 + $x.Length | Should Be 1 - $o.IntMethod() | Should be 1 - } - finally { - Remove-TypeData TestCodeMethodInvokationWithVoidReturn + $null.Count | Should Be 0 + $null.Length | Should Be 0 + + (10).Count | Should Be 1 + (10).Length | Should Be 1 + + ("a").Count | Should Be 1 + # The Length property exists for strings, so we skip the test in the context. + # ("a").Length | Should Be 1 + + ([psobject] @{ foo = 'bar' }).Count | Should Be 1 + ([psobject] @{ foo = 'bar' }).Length | Should Be 1 + + ([pscustomobject] @{ foo = 'bar' }).Count | Should Be 1 + ([pscustomobject] @{ foo = 'bar' }).Length | Should Be 1 + + # Return real Count and Length property if it present. + ([pscustomobject] @{ foo = 'bar'; count = 5 }).Count | Should Be 5 + ([pscustomobject] @{ foo = 'bar'; length = 5 }).Length | Should Be 5 } } - It "Count and length property works for singletons" { - # Return magic Count and Length property if it absent. - $x = 5 - $x.Count | Should Be 1 - $x.Length | Should Be 1 - - $null.Count | Should Be 0 - $null.Length | Should Be 0 + Context "Null Magic Method Adapter Tests" { + It "ForEach and Where works for Null" { + $res = $null.ForEach({1}) + $res.Count | Should Be 0 + $res.GetType().Name | Should BeExactly "Collection``1" - (10).Count | Should Be 1 - (10).Length | Should Be 1 + $null.Where({$true}) + $res.Count | Should Be 0 + $res.GetType().Name | Should BeExactly "Collection``1" + } + } - ("a").Count | Should Be 1 - # The Length property exists for strings, so we skip the test in the context. - # ("a").Length | Should Be 1 + Context "ForEach Magic Method Adapter Tests" { + It "Common ForEach magic method tests" -Pending:$true { + } - ([psobject] @{ foo = 'bar' }).Count | Should Be 1 - ([psobject] @{ foo = 'bar' }).Length | Should Be 1 + It "ForEach magic method works for singletions" { + $x = 5 + $x.ForEach({$_}) | Should Be 5 + (5).ForEach({$_}) | Should Be 5 + ("a").ForEach({$_}) | Should Be "a" + + ([pscustomobject]@{ foo = 'bar' }).ForEach({1}) | Should Be 1 + + $x = ([pscustomobject]@{ foo = 'bar' }).ForEach({$_}) + $x.Count | Should Be 1 + $x[0].foo | Should BeExactly "bar" + + $x = ([pscustomobject]@{ foo = 'bar' }).Foreach({$_ | Add-Member -NotePropertyName "foo2" -NotePropertyValue "bar2" -PassThru}) + $x.Count | Should Be 1 + $x[0].foo | Should BeExactly "bar" + $x[0].foo2 | Should BeExactly "bar2" + + # We call ForEach method defined in an object if it is present (not magic ForEach method). + $x = [pscustomobject]@{ foo = 'bar' } + $x | Add-Member -MemberType ScriptMethod -Name ForEach -Value { + param ( [int]$param1 ) + $param1*2 + } -PassThru -Force + $x.ForEach(5) | Should Be 10 + } + } - ([pscustomobject] @{ foo = 'bar' }).Count | Should Be 1 - ([pscustomobject] @{ foo = 'bar' }).Length | Should Be 1 + Context "Where Magic Method Adapter Tests" { + It "Common Where magic method tests" -Pending:$true { + } - # Return real Count and Length property if it present. - ([pscustomobject] @{ foo = 'bar'; count = 5 }).Count | Should Be 5 - ([pscustomobject] @{ foo = 'bar'; length = 5 }).Length | Should Be 5 + It "Where magic method works for singletions" { + $x = 5 + $x.Where({$true}) | Should Be 5 + (5).Where({$true}) | Should Be 5 + ("a").Where({$true}) | Should Be "a" + + $x = ([pscustomobject] @{ foo = 'bar' }).Where({$true}) + $x.Count | Should Be 1 + $x[0].foo | Should BeExactly "bar" + + $x = ([pscustomobject] @{ foo = 'bar' }).Where({$true}, 0) + $x.Count | Should Be 1 + $x[0].foo | Should BeExactly "bar" + + $x = ([pscustomobject] @{ foo = 'bar' }).Where({$true}, "Default") + $x.Count | Should Be 1 + $x[0].foo | Should BeExactly "bar" + + $x = ([pscustomobject] @{ foo = 'bar' }).Where({$true}, "Default", 0) + $x.Count | Should Be 1 + $x[0].foo | Should BeExactly "bar" + + $x = ([pscustomobject] @{ foo = 'bar' }).Where({$true}, "Default", "0") + $x.Count | Should Be 1 + $x[0].foo | Should BeExactly "bar" + + # We call Where method defined in an object if it is present (not magic Where method). + $x = [pscustomobject]@{ foo = 'bar' } + $x | Add-Member -MemberType ScriptMethod -Name Where -Value { + param ( [int]$param1 ) + $param1*2 + } -PassThru -Force + $x.Where(5) | Should Be 10 + } } } From 42319857054eae8c5a5bd8a22cad150b4312ecfa Mon Sep 17 00:00:00 2001 From: Ilya Date: Sun, 31 Dec 2017 18:30:00 +0500 Subject: [PATCH 2/5] Use LanguagePrimitives.ConvertTo --- .../engine/runtime/Binding/Binders.cs | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/src/System.Management.Automation/engine/runtime/Binding/Binders.cs b/src/System.Management.Automation/engine/runtime/Binding/Binders.cs index 1ba6af7b371..1c6e480b68c 100644 --- a/src/System.Management.Automation/engine/runtime/Binding/Binders.cs +++ b/src/System.Management.Automation/engine/runtime/Binding/Binders.cs @@ -7108,10 +7108,10 @@ internal static object InvokeAdaptedMember(object obj, string methodName, object return EnumerableOps.Where(enumerator, args[0] as ScriptBlock, WhereOperatorSelectionMode.Default, 0); case 2: return EnumerableOps.Where(enumerator, args[0] as ScriptBlock, - (WhereOperatorSelectionMode)Enum.Parse(typeof(WhereOperatorSelectionMode), args[1].ToString()), 0); + LanguagePrimitives.ConvertTo(args[1]), 0); case 3: return EnumerableOps.Where(enumerator, args[0] as ScriptBlock, - (WhereOperatorSelectionMode)Enum.Parse(typeof(WhereOperatorSelectionMode), args[1].ToString()), int.Parse(args[2].ToString())); + LanguagePrimitives.ConvertTo(args[1]), LanguagePrimitives.ConvertTo(args[2])); } } From 3f23849b608e01d7da694a4e44a2b238adf293eb Mon Sep 17 00:00:00 2001 From: Ilya Date: Tue, 2 Jan 2018 22:30:23 +0500 Subject: [PATCH 3/5] Fix typo --- .../engine/runtime/Binding/Binders.cs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/System.Management.Automation/engine/runtime/Binding/Binders.cs b/src/System.Management.Automation/engine/runtime/Binding/Binders.cs index 1c6e480b68c..b14594922c3 100644 --- a/src/System.Management.Automation/engine/runtime/Binding/Binders.cs +++ b/src/System.Management.Automation/engine/runtime/Binding/Binders.cs @@ -7096,7 +7096,7 @@ internal static object InvokeAdaptedMember(object obj, string methodName, object } // The object haven't Where and ForEach methods. - // As last resort, we invoking Where and ForEach operators on singletions like + // As last resort, we invoking Where and ForEach operators on singletons like // ([pscustomobject]@{ foo = 'bar' }).Foreach({$_}) // ([pscustomobject]@{ foo = 'bar' }).Where({1}) if (string.Equals(methodName, "Where", StringComparison.OrdinalIgnoreCase)) From 179c8b38673a49daad2683db020e352b2448a538 Mon Sep 17 00:00:00 2001 From: Ilya Date: Wed, 3 Jan 2018 16:22:49 +0500 Subject: [PATCH 4/5] Fix formatting and typos --- .../engine/runtime/Binding/Binders.cs | 4 +- test/powershell/engine/ETS/Adapter.Tests.ps1 | 44 ++++++++++--------- 2 files changed, 25 insertions(+), 23 deletions(-) diff --git a/src/System.Management.Automation/engine/runtime/Binding/Binders.cs b/src/System.Management.Automation/engine/runtime/Binding/Binders.cs index b14594922c3..7ca75110f26 100644 --- a/src/System.Management.Automation/engine/runtime/Binding/Binders.cs +++ b/src/System.Management.Automation/engine/runtime/Binding/Binders.cs @@ -7095,8 +7095,8 @@ internal static object InvokeAdaptedMember(object obj, string methodName, object return methodInfo.Invoke(args); } - // The object haven't Where and ForEach methods. - // As last resort, we invoking Where and ForEach operators on singletons like + // The object doesn't have 'Where' and 'ForEach' methods. + // As a last resort, we invoke 'Where' and 'ForEach' operators on singlestons like // ([pscustomobject]@{ foo = 'bar' }).Foreach({$_}) // ([pscustomobject]@{ foo = 'bar' }).Where({1}) if (string.Equals(methodName, "Where", StringComparison.OrdinalIgnoreCase)) diff --git a/test/powershell/engine/ETS/Adapter.Tests.ps1 b/test/powershell/engine/ETS/Adapter.Tests.ps1 index 5f93015f801..72efe1388dd 100644 --- a/test/powershell/engine/ETS/Adapter.Tests.ps1 +++ b/test/powershell/engine/ETS/Adapter.Tests.ps1 @@ -14,7 +14,7 @@ Describe "Adapter Tests" -tags "CI" { } } - $psmemberset = new-object System.Management.Automation.PSMemberSet 'setname1' + $psmemberset = New-Object System.Management.Automation.PSMemberSet 'setname1' $psmemberset | Add-Member -MemberType NoteProperty -Name NoteName -Value 1 $testmethod = [TestCodeMethodClass].GetMethod("TestCodeMethod") $psmemberset | Add-Member -MemberType CodeMethod -Name TestCodeMethod -Value $testmethod @@ -23,42 +23,43 @@ Describe "Adapter Tests" -tags "CI" { $document.LoadXml("Pride And Prejudice19.95") $doc = $document.DocumentElement } - It "can get a Dotnet parameterized property" { + + It "Can get a Dotnet parameterized property" { $col = $pso.psobject.Properties.Match("*") $prop = $col.psobject.Members["Item"] $prop | Should Not BeNullOrEmpty - $prop.IsGettable | Should be $true - $prop.IsSettable | Should be $false - $prop.TypeNameOfValue | Should be "System.Management.Automation.PSPropertyInfo" - $prop.Invoke("ProcessName").Value | Should be $processName + $prop.IsGettable | Should Be $true + $prop.IsSettable | Should Be $false + $prop.TypeNameOfValue | Should Be "System.Management.Automation.PSPropertyInfo" + $prop.Invoke("ProcessName").Value | Should Be $processName } - It "can get a property" { - $pso.psobject.Properties["ProcessName"] | should Not BeNullOrEmpty + It "Can get a property" { + $pso.psobject.Properties["ProcessName"] | Should Not BeNullOrEmpty } It "Can access all properties" { $props = $pso.psobject.Properties.Match("*") - $props | should Not BeNullOrEmpty - $props["ProcessName"].Value |Should be $processName + $props | Should Not BeNullOrEmpty + $props["ProcessName"].Value | Should Be $processName } It "Can invoke a method" { $method = $pso.psobject.Methods["ToString"] - $method.Invoke() | should be ($pso.ToString()) + $method.Invoke() | Should Be ($pso.ToString()) } It "Access a Method via MemberSet adapter" { $prop = $psmemberset.psobject.Members["TestCodeMethod"] - $prop.Invoke(2) | Should be 1 + $prop.Invoke(2) | Should Be 1 } It "Access misc properties via MemberSet adapter" { $prop = $psmemberset.psobject.Properties["NoteName"] $prop | Should Not BeNullOrEmpty - $prop.IsGettable | Should be $true - $prop.IsSettable | Should be $true - $prop.TypeNameOfValue | Should be "System.Int32" + $prop.IsGettable | Should Be $true + $prop.IsSettable | Should Be $true + $prop.TypeNameOfValue | Should Be "System.Int32" } It "Access all the properties via XmlAdapter" { @@ -73,7 +74,7 @@ Describe "Adapter Tests" -tags "CI" { $prop.Value | Should Be "19.95" $prop.IsGettable | Should Not BeNullOrEmpty $prop.IsSettable | Should Not BeNullOrEmpty - $prop.TypeNameOfValue | Should be "System.String" + $prop.TypeNameOfValue | Should Be "System.String" } It "Call to string on a XmlNode object" { @@ -105,9 +106,9 @@ Describe "Adapter Tests" -tags "CI" { $o = [TestCodeMethodInvokationWithVoidReturn]::new() $o.CallCounter | Should Be 0 $o.VoidMethod() - $o.CallCounter | Should be 1 + $o.CallCounter | Should Be 1 - $o.IntMethod() | Should be 1 + $o.IntMethod() | Should Be 1 } finally { Remove-TypeData TestCodeMethodInvokationWithVoidReturn @@ -127,8 +128,9 @@ Describe "Adapter Tests" -tags "CI" { (10).Length | Should Be 1 ("a").Count | Should Be 1 - # The Length property exists for strings, so we skip the test in the context. - # ("a").Length | Should Be 1 + # The Length property exists in String type, so here we check that we don't break strings. + ("a").Length | Should Be 1 + ("aa").Length | Should Be 2 ([psobject] @{ foo = 'bar' }).Count | Should Be 1 ([psobject] @{ foo = 'bar' }).Length | Should Be 1 @@ -162,7 +164,7 @@ Describe "Adapter Tests" -tags "CI" { $x = 5 $x.ForEach({$_}) | Should Be 5 (5).ForEach({$_}) | Should Be 5 - ("a").ForEach({$_}) | Should Be "a" + ("a").ForEach({$_}) | Should BeExactly "a" ([pscustomobject]@{ foo = 'bar' }).ForEach({1}) | Should Be 1 From cee31dc4c6af35efd68c968a83ee34efe5112f2a Mon Sep 17 00:00:00 2001 From: Jason Shirk Date: Wed, 3 Jan 2018 13:10:39 -0800 Subject: [PATCH 5/5] Fix spelling --- .../engine/runtime/Binding/Binders.cs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/System.Management.Automation/engine/runtime/Binding/Binders.cs b/src/System.Management.Automation/engine/runtime/Binding/Binders.cs index 7ca75110f26..536350cc661 100644 --- a/src/System.Management.Automation/engine/runtime/Binding/Binders.cs +++ b/src/System.Management.Automation/engine/runtime/Binding/Binders.cs @@ -7096,7 +7096,7 @@ internal static object InvokeAdaptedMember(object obj, string methodName, object } // The object doesn't have 'Where' and 'ForEach' methods. - // As a last resort, we invoke 'Where' and 'ForEach' operators on singlestons like + // As a last resort, we invoke 'Where' and 'ForEach' operators on singletons like // ([pscustomobject]@{ foo = 'bar' }).Foreach({$_}) // ([pscustomobject]@{ foo = 'bar' }).Where({1}) if (string.Equals(methodName, "Where", StringComparison.OrdinalIgnoreCase))