From afa6c9a65cc2f1d8850087816180241cd6aec0dd Mon Sep 17 00:00:00 2001 From: vexx32 <32407840+vexx32@users.noreply.github.com> Date: Sun, 17 Nov 2019 22:05:30 -0500 Subject: [PATCH 01/11] :sparkles: Allow referencing hashtable members - Fix an issue where Select-Object and similar could not resolve member names from hashtables due to them carelessly converting them. - Allows both hashtable keys/values and actual members to be referenced. Precedence is given to the hashtable keys first to avoid collisions. --- .../common/Utilities/Mshexpression.cs | 60 +++++++++++++------ 1 file changed, 41 insertions(+), 19 deletions(-) diff --git a/src/System.Management.Automation/FormatAndOutput/common/Utilities/Mshexpression.cs b/src/System.Management.Automation/FormatAndOutput/common/Utilities/Mshexpression.cs index bf3acfab2a1..db5413a9c21 100644 --- a/src/System.Management.Automation/FormatAndOutput/common/Utilities/Mshexpression.cs +++ b/src/System.Management.Automation/FormatAndOutput/common/Utilities/Mshexpression.cs @@ -159,27 +159,41 @@ public List ResolveNames(PSObject target, bool expand) // If the object passed in is a hashtable, then turn it into a PSCustomObject so // that property expressions can work on it. - target = IfHashtableWrapAsPSCustomObject(target); + var wrappedTarget = IfHashtableWrapAsPSCustomObject(target, out bool wasHashtable); // we have a string value - IEnumerable members = null; + List members = new List(); if (HasWildCardCharacters) { // get the members first: this will expand the globbing on each parameter - members = target.Members.Match(_stringValue, - PSMemberTypes.Properties | PSMemberTypes.PropertySet | PSMemberTypes.Dynamic); + members.AddRange(wrappedTarget.Members.Match( + _stringValue, + PSMemberTypes.Properties | PSMemberTypes.PropertySet | PSMemberTypes.Dynamic)); + + // if target was a hashtable and no result is found from the keys, then use property value if available + if (wasHashtable && members.Count == 0) + { + members.AddRange(target.Members.Match( + _stringValue, + PSMemberTypes.Properties | PSMemberTypes.PropertySet | PSMemberTypes.Dynamic)); + } } else { // we have no globbing: try an exact match, because this is quicker. - PSMemberInfo x = target.Members[_stringValue]; + PSMemberInfo x = wrappedTarget.Members[_stringValue]; - if ((x == null) && (target.BaseObject is System.Dynamic.IDynamicMetaObjectProvider)) + if (x == null) { - // We could check if GetDynamicMemberNames includes the name... but - // GetDynamicMemberNames is only a hint, not a contract, so we'd want - // to attempt the binding whether it's in there or not. - x = new PSDynamicMember(_stringValue); + x = target.Members[_stringValue]; + + if ((x == null) && (wrappedTarget.BaseObject is System.Dynamic.IDynamicMetaObjectProvider)) + { + // We could check if GetDynamicMemberNames includes the name... but + // GetDynamicMemberNames is only a hint, not a contract, so we'd want + // to attempt the binding whether it's in there or not. + x = new PSDynamicMember(_stringValue); + } } List temp = new List(); @@ -271,24 +285,30 @@ public List GetValues(PSObject target, bool expand, // If the object passed in is a hashtable, then turn it into a PSCustomObject so // that property expressions can work on it. - target = IfHashtableWrapAsPSCustomObject(target); + var wrappedTarget = IfHashtableWrapAsPSCustomObject(target, out bool wasHashtable); // process the script case if (Script != null) { PSPropertyExpression scriptExpression = new PSPropertyExpression(Script); - PSPropertyExpressionResult r = scriptExpression.GetValue(target, eatExceptions); + PSPropertyExpressionResult r = scriptExpression.GetValue(wrappedTarget, eatExceptions); retVal.Add(r); return retVal; } - // process the expression - List resolvedExpressionList = this.ResolveNames(target, expand); + foreach (PSPropertyExpression resolvedName in ResolveNames(wrappedTarget, expand)) + { + PSPropertyExpressionResult result = resolvedName.GetValue(wrappedTarget, eatExceptions); + retVal.Add(result); + } - foreach (PSPropertyExpression re in resolvedExpressionList) + if (retVal.Count == 0 && wasHashtable) { - PSPropertyExpressionResult r = re.GetValue(target, eatExceptions); - retVal.Add(r); + foreach (PSPropertyExpression resolvedName in ResolveNames(target, expand)) + { + PSPropertyExpressionResult result = resolvedName.GetValue(target, eatExceptions); + retVal.Add(result); + } } return retVal; @@ -344,13 +364,16 @@ private PSPropertyExpressionResult GetValue(PSObject target, bool eatExceptions) } } - private PSObject IfHashtableWrapAsPSCustomObject(PSObject target) + private PSObject IfHashtableWrapAsPSCustomObject(PSObject target, out bool wrapped) { + wrapped = false; + // If the object passed in is a hashtable, then turn it into a PSCustomObject so // that property expressions can work on it. if (PSObject.Base(target) is Hashtable targetAsHash) { target = (PSObject)(LanguagePrimitives.ConvertPSObjectToType(targetAsHash, typeof(PSObject), false, null, true)); + wrapped = true; } return target; @@ -363,4 +386,3 @@ private PSObject IfHashtableWrapAsPSCustomObject(PSObject target) #endregion Private Members } } - From d406d2e267f66de803e3411e395a5cce38830325 Mon Sep 17 00:00:00 2001 From: vexx32 <32407840+vexx32@users.noreply.github.com> Date: Sun, 17 Nov 2019 22:07:08 -0500 Subject: [PATCH 02/11] :art: Whitespace fixes Review this commit separately to the next. Committed separately due to the file being old and needing a reformat. --- .../Select-Object.Tests.ps1 | 405 +++++++++--------- 1 file changed, 201 insertions(+), 204 deletions(-) diff --git a/test/powershell/Modules/Microsoft.PowerShell.Utility/Select-Object.Tests.ps1 b/test/powershell/Modules/Microsoft.PowerShell.Utility/Select-Object.Tests.ps1 index 4e060c516b0..333070d5d17 100644 --- a/test/powershell/Modules/Microsoft.PowerShell.Utility/Select-Object.Tests.ps1 +++ b/test/powershell/Modules/Microsoft.PowerShell.Utility/Select-Object.Tests.ps1 @@ -6,249 +6,246 @@ Add-TestDynamicType Describe "Select-Object" -Tags "CI" { BeforeEach { - $dirObject = GetFileMock - $TestLength = 3 + $dirObject = GetFileMock + $TestLength = 3 } It "Handle piped input without error" { - { $dirObject | Select-Object } | Should -Not -Throw + { $dirObject | Select-Object } | Should -Not -Throw } It "Should treat input as a single object with the inputObject parameter" { - $result = $(Select-Object -InputObject $dirObject -Last $TestLength).Length - $expected = $dirObject.Length + $result = $(Select-Object -inputObject $dirObject -last $TestLength).Length + $expected = $dirObject.Length - $result | Should -Be $expected + $result | Should -Be $expected } It "Should be able to use the alias" { - { $dirObject | select } | Should -Not -Throw + { $dirObject | select } | Should -Not -Throw } It "Should have same result when using alias" { - $result = $dirObject | select - $expected = $dirObject | Select-Object + $result = $dirObject | select + $expected = $dirObject | Select-Object - $result | Should -Be $expected + $result | Should -Be $expected } It "Should return correct object with First parameter" { - $result = $dirObject | Select-Object -First $TestLength + $result = $dirObject | Select-Object -First $TestLength - $result.Length | Should -Be $TestLength + $result.Length | Should -Be $TestLength - for ($i=0; $i -lt $TestLength; $i++) - { - $result[$i].Name | Should -Be $dirObject[$i].Name - } + for ($i = 0; $i -lt $TestLength; $i++) { + $result[$i].Name | Should -Be $dirObject[$i].Name + } } It "Should return correct object with Last parameter" { - $result = $dirObject | Select-Object -Last $TestLength + $result = $dirObject | Select-Object -Last $TestLength - $result.Length | Should -Be $TestLength + $result.Length | Should -Be $TestLength - for ($i=0; $i -lt $TestLength; $i++) - { - $result[$i].Name | Should -Be $dirObject[$dirObject.Length - $TestLength + $i].Name - } + for ($i = 0; $i -lt $TestLength; $i++) { + $result[$i].Name | Should -Be $dirObject[$dirObject.Length - $TestLength + $i].Name + } } It "Should work correctly with Unique parameter" { - $result = ("a","b","c","a","a","a" | Select-Object -Unique).Length - $expected = 3 + $result = ("a", "b", "c", "a", "a", "a" | Select-Object -Unique).Length + $expected = 3 - $result | Should -Be $expected + $result | Should -Be $expected } It "Should return correct object with Skip parameter" { - $result = $dirObject | Select-Object -Skip $TestLength + $result = $dirObject | Select-Object -Skip $TestLength - $result.Length | Should -Be ($dirObject.Length - $TestLength) + $result.Length | Should -Be ($dirObject.Length - $TestLength) - for ($i=0; $i -lt $TestLength; $i++) - { - $result[$i].Name | Should -Be $dirObject[$TestLength + $i].Name - } + for ($i = 0; $i -lt $TestLength; $i++) { + $result[$i].Name | Should -Be $dirObject[$TestLength + $i].Name + } } It "Should return an object with selected columns" { - $result = $dirObject | Select-Object -Property Name, Size + $result = $dirObject | Select-Object -Property Name, Size - $result.Length | Should -Be $dirObject.Length - $result[0].Name | Should -Be $dirObject[0].Name - $result[0].Size | Should -Be $dirObject[0].Size - $result[0].Mode | Should -BeNullOrEmpty + $result.Length | Should -Be $dirObject.Length + $result[0].Name | Should -Be $dirObject[0].Name + $result[0].Size | Should -Be $dirObject[0].Size + $result[0].Mode | Should -BeNullOrEmpty } It "Should send output to pipe properly" { - {$dirObject | Select-Object -Unique | pipelineConsume} | Should -Not -Throw + { $dirObject | Select-Object -Unique | pipelineConsume } | Should -Not -Throw } It "Should select array indices with Index parameter" { - $firstIndex = 2 - $secondIndex = 4 - $result = $dirObject | Select-Object -Index $firstIndex, $secondIndex + $firstIndex = 2 + $secondIndex = 4 + $result = $dirObject | Select-Object -Index $firstIndex, $secondIndex - $result[0].Name | Should -Be $dirObject[$firstIndex].Name - $result[1].Name | Should -Be $dirObject[$secondIndex].Name + $result[0].Name | Should -Be $dirObject[$firstIndex].Name + $result[1].Name | Should -Be $dirObject[$secondIndex].Name } # Note that these two tests will modify original values of $dirObject It "Should not wait when used without -Wait option" { - $orig1 = $dirObject[0].Size - $orig2 = $dirObject[$TestLength].Size - $result = $dirObject | addOneToSizeProperty | Select-Object -First $TestLength + $orig1 = $dirObject[0].Size + $orig2 = $dirObject[$TestLength].Size + $result = $dirObject | addOneToSizeProperty | Select-Object -First $TestLength - $result[0].Size | Should -Be ($orig1 + 1) - $dirObject[0].Size | Should -Be ($orig1 + 1) - $dirObject[$TestLength].Size | Should -Be $orig2 + $result[0].Size | Should -Be ($orig1 + 1) + $dirObject[0].Size | Should -Be ($orig1 + 1) + $dirObject[$TestLength].Size | Should -Be $orig2 } It "Should wait when used with -Wait option" { - $orig1 = $dirObject[0].Size - $orig2 = $dirObject[$TestLength].Size - $result = $dirObject | addOneToSizeProperty | Select-Object -First $TestLength -Wait - - $result[0].Size | Should -Be ($orig1 + 1) - $dirObject[0].Size | Should -Be ($orig1 + 1) - $dirObject[$TestLength].Size | Should -Be ($orig2 + 1) - } - - It "Should not leak 'StopUpstreamCommandsException' internal exception for stopping upstream" { - 1,2 | Select-Object -First 1 -ErrorVariable err - $err | Should -BeNullOrEmpty - } + $orig1 = $dirObject[0].Size + $orig2 = $dirObject[$TestLength].Size + $result = $dirObject | addOneToSizeProperty | Select-Object -First $TestLength -Wait + + $result[0].Size | Should -Be ($orig1 + 1) + $dirObject[0].Size | Should -Be ($orig1 + 1) + $dirObject[$TestLength].Size | Should -Be ($orig2 + 1) + } + + It "Should not leak 'StopUpstreamCommandsException' internal exception for stopping upstream" { + 1, 2 | Select-Object -First 1 -ErrorVariable err + $err | Should -BeNullOrEmpty + } } Describe "Select-Object DRT basic functionality" -Tags "CI" { - BeforeAll { - $employees = [pscustomobject]@{"FirstName"="joseph"; "LastName"="smith"; "YearsInMS"=15}, - [pscustomobject]@{"FirstName"="paul"; "LastName"="smith"; "YearsInMS"=15}, - [pscustomobject]@{"FirstName"="mary"; "LastName"="soe"; "YearsInMS"=5}, - [pscustomobject]@{"FirstName"="edmund"; "LastName"="bush"; "YearsInMS"=9} - } + BeforeAll { + $employees = [pscustomobject]@{"FirstName" = "joseph"; "LastName" = "smith"; "YearsInMS" = 15 }, + [pscustomobject]@{"FirstName" = "paul"; "LastName" = "smith"; "YearsInMS" = 15 }, + [pscustomobject]@{"FirstName" = "mary"; "LastName" = "soe"; "YearsInMS" = 5 }, + [pscustomobject]@{"FirstName" = "edmund"; "LastName" = "bush"; "YearsInMS" = 9 } + } - It "Select-Object with empty script block property should throw"{ - $e = { "bar" | Select-Object -Prop {} -ErrorAction Stop } | - Should -Throw -ErrorId "EmptyScriptBlockAndNoName,Microsoft.PowerShell.Commands.SelectObjectCommand" -PassThru - $e.CategoryInfo | Should -Match "PSArgumentException" - } + It "Select-Object with empty script block property should throw" { + $e = { "bar" | Select-Object -Prop { } -ErrorAction Stop } | + Should -Throw -ErrorId "EmptyScriptBlockAndNoName,Microsoft.PowerShell.Commands.SelectObjectCommand" -PassThru + $e.CategoryInfo | Should -Match "PSArgumentException" + } - It "Select-Object with string property should work"{ - $result = "bar" | Select-Object -Prop foo | Measure-Object - $result.Count | Should -Be 1 - } + It "Select-Object with string property should work" { + $result = "bar" | Select-Object -Prop foo | Measure-Object + $result.Count | Should -Be 1 + } - It "Select-Object with Property First Last Overlap should work"{ - $results = $employees | Select-Object -Property "YearsInMS", "L*" -First 2 -Last 3 + It "Select-Object with Property First Last Overlap should work" { + $results = $employees | Select-Object -Property "YearsInMS", "L*" -First 2 -Last 3 - $results.Count | Should -Be 4 + $results.Count | Should -Be 4 - $results[0].LastName | Should -Be $employees[0].LastName - $results[1].LastName | Should -Be $employees[1].LastName - $results[2].LastName | Should -Be $employees[2].LastName - $results[3].LastName | Should -Be $employees[3].LastName + $results[0].LastName | Should -Be $employees[0].LastName + $results[1].LastName | Should -Be $employees[1].LastName + $results[2].LastName | Should -Be $employees[2].LastName + $results[3].LastName | Should -Be $employees[3].LastName - $results[0].YearsInMS | Should -Be $employees[0].YearsInMS - $results[1].YearsInMS | Should -Be $employees[1].YearsInMS - $results[2].YearsInMS | Should -Be $employees[2].YearsInMS - $results[3].YearsInMS | Should -Be $employees[3].YearsInMS - } + $results[0].YearsInMS | Should -Be $employees[0].YearsInMS + $results[1].YearsInMS | Should -Be $employees[1].YearsInMS + $results[2].YearsInMS | Should -Be $employees[2].YearsInMS + $results[3].YearsInMS | Should -Be $employees[3].YearsInMS + } - It "Select-Object with Property First Last should work"{ - $results = $employees | Select-Object -Property "YearsInMS", "L*" -First 2 -Last 1 + It "Select-Object with Property First Last should work" { + $results = $employees | Select-Object -Property "YearsInMS", "L*" -First 2 -Last 1 - $results.Count | Should -Be 3 + $results.Count | Should -Be 3 - $results[0].LastName | Should -Be $employees[0].LastName - $results[1].LastName | Should -Be $employees[1].LastName - $results[2].LastName | Should -Be $employees[3].LastName + $results[0].LastName | Should -Be $employees[0].LastName + $results[1].LastName | Should -Be $employees[1].LastName + $results[2].LastName | Should -Be $employees[3].LastName - $results[0].YearsInMS | Should -Be $employees[0].YearsInMS - $results[1].YearsInMS | Should -Be $employees[1].YearsInMS - $results[2].YearsInMS | Should -Be $employees[3].YearsInMS - } + $results[0].YearsInMS | Should -Be $employees[0].YearsInMS + $results[1].YearsInMS | Should -Be $employees[1].YearsInMS + $results[2].YearsInMS | Should -Be $employees[3].YearsInMS + } - It "Select-Object with Property First should work"{ - $results = $employees | Select-Object -Property "YearsInMS", "L*" -First 2 + It "Select-Object with Property First should work" { + $results = $employees | Select-Object -Property "YearsInMS", "L*" -First 2 - $results.Count | Should -Be 2 + $results.Count | Should -Be 2 - $results[0].LastName | Should -Be $employees[0].LastName - $results[1].LastName | Should -Be $employees[1].LastName + $results[0].LastName | Should -Be $employees[0].LastName + $results[1].LastName | Should -Be $employees[1].LastName - $results[0].YearsInMS | Should -Be $employees[0].YearsInMS - $results[1].YearsInMS | Should -Be $employees[1].YearsInMS - } + $results[0].YearsInMS | Should -Be $employees[0].YearsInMS + $results[1].YearsInMS | Should -Be $employees[1].YearsInMS + } - It "Select-Object with Property First Zero should work"{ - $results = $employees | Select-Object -Property "YearsInMS", "L*" -First 0 + It "Select-Object with Property First Zero should work" { + $results = $employees | Select-Object -Property "YearsInMS", "L*" -First 0 - $results.Count | Should -Be 0 - } + $results.Count | Should -Be 0 + } - It "Select-Object with Property Last Zero should work"{ - $results = $employees | Select-Object -Property "YearsInMS", "L*" -Last 0 + It "Select-Object with Property Last Zero should work" { + $results = $employees | Select-Object -Property "YearsInMS", "L*" -Last 0 - $results.Count | Should -Be 0 - } + $results.Count | Should -Be 0 + } - It "Select-Object with Unique should work"{ - $results = $employees | Select-Object -Property "YearsInMS", "L*" -Unique:$true + It "Select-Object with Unique should work" { + $results = $employees | Select-Object -Property "YearsInMS", "L*" -Unique:$true - $results.Count | Should -Be 3 + $results.Count | Should -Be 3 - $results[0].LastName | Should -Be $employees[1].LastName - $results[1].LastName | Should -Be $employees[2].LastName - $results[2].LastName | Should -Be $employees[3].LastName + $results[0].LastName | Should -Be $employees[1].LastName + $results[1].LastName | Should -Be $employees[2].LastName + $results[2].LastName | Should -Be $employees[3].LastName - $results[0].YearsInMS | Should -Be $employees[1].YearsInMS - $results[1].YearsInMS | Should -Be $employees[2].YearsInMS - $results[2].YearsInMS | Should -Be $employees[3].YearsInMS - } + $results[0].YearsInMS | Should -Be $employees[1].YearsInMS + $results[1].YearsInMS | Should -Be $employees[2].YearsInMS + $results[2].YearsInMS | Should -Be $employees[3].YearsInMS + } - It "Select-Object with Simple should work"{ - $employee1 = [pscustomobject]@{"FirstName"="joesph"; "LastName"="smith"; "YearsInMS"=15} - $employee2 = [pscustomobject]@{"FirstName"="paul"; "LastName"="smith"; "YearsInMS"=15} - $employee3 = [pscustomobject]@{"FirstName"="mary"; "LastName"="soe"; "YearsInMS"=15} - $employees3 = @($employee1,$employee2,$employee3,$employee4) - $results = $employees3 | Select-Object -Property "FirstName", "YearsInMS" + It "Select-Object with Simple should work" { + $employee1 = [pscustomobject]@{"FirstName" = "joesph"; "LastName" = "smith"; "YearsInMS" = 15 } + $employee2 = [pscustomobject]@{"FirstName" = "paul"; "LastName" = "smith"; "YearsInMS" = 15 } + $employee3 = [pscustomobject]@{"FirstName" = "mary"; "LastName" = "soe"; "YearsInMS" = 15 } + $employees3 = @($employee1, $employee2, $employee3, $employee4) + $results = $employees3 | Select-Object -Property "FirstName", "YearsInMS" - $results.Count | Should -Be 3 + $results.Count | Should -Be 3 - $results[0].FirstName | Should -Be $employees3[0].FirstName - $results[1].FirstName | Should -Be $employees3[1].FirstName - $results[2].FirstName | Should -Be $employees3[2].FirstName + $results[0].FirstName | Should -Be $employees3[0].FirstName + $results[1].FirstName | Should -Be $employees3[1].FirstName + $results[2].FirstName | Should -Be $employees3[2].FirstName - $results[0].YearsInMS | Should -Be $employees3[0].YearsInMS - $results[1].YearsInMS | Should -Be $employees3[1].YearsInMS - $results[2].YearsInMS | Should -Be $employees3[2].YearsInMS - } + $results[0].YearsInMS | Should -Be $employees3[0].YearsInMS + $results[1].YearsInMS | Should -Be $employees3[1].YearsInMS + $results[2].YearsInMS | Should -Be $employees3[2].YearsInMS + } - It "Select-Object with no input should work"{ - $results = $null | Select-Object -Property "FirstName", "YearsInMS", "FirstNa*" - $results.Count | Should -Be 0 - } + It "Select-Object with no input should work" { + $results = $null | Select-Object -Property "FirstName", "YearsInMS", "FirstNa*" + $results.Count | Should -Be 0 + } - It "Select-Object with Start-Time In Idle Process should work" { - $results = Get-Process * | Select-Object ProcessName - $results.Count | Should -Not -Be 0 - } + It "Select-Object with Start-Time In Idle Process should work" { + $results = Get-Process * | Select-Object ProcessName + $results.Count | Should -Not -Be 0 + } - It "Select-Object with Skip should work"{ - $results = "1","2","3" | Select-Object -Skip 1 - $results.Count | Should -Be 2 - $results[0] | Should -Be 2 - $results[1] | Should -Be 3 - } + It "Select-Object with Skip should work" { + $results = "1", "2", "3" | Select-Object -Skip 1 + $results.Count | Should -Be 2 + $results[0] | Should -Be 2 + $results[1] | Should -Be 3 + } - It "Select-Object with Index should work"{ - $results = "1","2","3" | Select-Object -Index 2 - $results.Count | Should -Be 1 - $results[0] | Should -BeExactly "3" - } + It "Select-Object with Index should work" { + $results = "1", "2", "3" | Select-Object -Index 2 + $results.Count | Should -Be 1 + $results[0] | Should -BeExactly "3" + } It "Select-Object with SkipIndex should work" { $results = "1", "2", "3" | Select-Object -SkipIndex 0, 2 @@ -262,7 +259,7 @@ Describe "Select-Object DRT basic functionality" -Tags "CI" { $results -join ',' | Should -BeExactly "0,1,2,3,4,9,10" } - It "Select-Object should handle dynamic (DLR) properties"{ + It "Select-Object should handle dynamic (DLR) properties" { $dynObj = [TestDynamic]::new() $results = $dynObj, $dynObj | Select-Object -ExpandProperty FooProp $results.Count | Should -Be 2 @@ -270,7 +267,7 @@ Describe "Select-Object DRT basic functionality" -Tags "CI" { $results[1] | Should -Be 123 } - It "Select-Object should handle dynamic (DLR) properties without GetDynamicMemberNames hint"{ + It "Select-Object should handle dynamic (DLR) properties without GetDynamicMemberNames hint" { $dynObj = [TestDynamic]::new() $results = $dynObj, $dynObj | Select-Object -ExpandProperty HiddenProp $results.Count | Should -Be 2 @@ -278,7 +275,7 @@ Describe "Select-Object DRT basic functionality" -Tags "CI" { $results[1] | Should -Be 789 } - It "Select-Object should handle wildcarded dynamic (DLR) properties when hinted by GetDynamicMemberNames"{ + It "Select-Object should handle wildcarded dynamic (DLR) properties when hinted by GetDynamicMemberNames" { $dynObj = [TestDynamic]::new() $results = $dynObj, $dynObj | Select-Object -ExpandProperty FooP* $results.Count | Should -Be 2 @@ -286,7 +283,7 @@ Describe "Select-Object DRT basic functionality" -Tags "CI" { $results[1] | Should -Be 123 } - It "Select-Object should work when multiple dynamic (DLR) properties match"{ + It "Select-Object should work when multiple dynamic (DLR) properties match" { $dynObj = [TestDynamic]::new() $results = $dynObj, $dynObj | Select-Object *Prop $results.Count | Should -Be 2 @@ -296,9 +293,9 @@ Describe "Select-Object DRT basic functionality" -Tags "CI" { $results[1].BarProp | Should -Be 456 } - It "Select-Object -ExpandProperty should yield errors if multiple dynamic (DLR) properties match"{ + It "Select-Object -ExpandProperty should yield errors if multiple dynamic (DLR) properties match" { $dynObj = [TestDynamic]::new() - $e = { $results = $dynObj, $dynObj | Select-Object -ExpandProperty *Prop -ErrorAction Stop} | + $e = { $results = $dynObj, $dynObj | Select-Object -ExpandProperty *Prop -ErrorAction Stop } | Should -Throw -PassThru -ErrorId "MutlipleExpandProperties,Microsoft.PowerShell.Commands.SelectObjectCommand" $e.CategoryInfo | Should -Match "PSArgumentException" } @@ -306,49 +303,49 @@ Describe "Select-Object DRT basic functionality" -Tags "CI" { Describe "Select-Object with Property = '*'" -Tags "CI" { - # Issue #2420 - It "Select-Object with implicit Property = '*' don't return property named '*'"{ - $results = [pscustomobject]@{Thing="thing1"} | Select-Object -ExcludeProperty thing - $results.psobject.Properties.Item("*") | Should -BeNullOrEmpty - } - - # Issue #2420 - It "Select-Object with explicit Property = '*' don't return property named '*'"{ - $results = [pscustomobject]@{Thing="thing1"} | Select-Object -Property * -ExcludeProperty thing - $results.psobject.Properties.Item("*") | Should -BeNullOrEmpty - } - - # Issue #2351 - It "Select-Object with implicit Property = '*' exclude single property"{ - $results = [pscustomobject]@{Thing="thing1"} | Select-Object -ExcludeProperty thing - $results.psobject.Properties.Item("Thing") | Should -BeNullOrEmpty - $results.psobject.Properties.Item("*") | Should -BeNullOrEmpty - } - - # Issue #2351 - It "Select-Object with explicit Property = '*' exclude single property"{ - $results = [pscustomobject]@{Thing="thing1"} | Select-Object -Property * -ExcludeProperty thing - $results.psobject.Properties.Item("Thing") | Should -BeNullOrEmpty - $results.psobject.Properties.Item("*") | Should -BeNullOrEmpty - } - - # Issue #2351 - It "Select-Object with implicit Property = '*' exclude not single property"{ - $results = [pscustomobject]@{Thing="thing1";Param2="param2"} | Select-Object -ExcludeProperty Param2 - $results.Param2 | Should -BeNullOrEmpty - $results.Thing | Should -BeExactly "thing1" - } - - # Issue #2351 - It "Select-Object with explicit Property = '*' exclude not single property"{ - $results = [pscustomobject]@{Thing="thing1";Param2="param2"} | Select-Object -Property * -ExcludeProperty Param2 - $results.Param2 | Should -BeNullOrEmpty - $results.Thing | Should -BeExactly "thing1" - } + # Issue #2420 + It "Select-Object with implicit Property = '*' don't return property named '*'" { + $results = [pscustomobject]@{Thing = "thing1" } | Select-Object -ExcludeProperty thing + $results.psobject.Properties.Item("*") | Should -BeNullOrEmpty + } + + # Issue #2420 + It "Select-Object with explicit Property = '*' don't return property named '*'" { + $results = [pscustomobject]@{Thing = "thing1" } | Select-Object -Property * -ExcludeProperty thing + $results.psobject.Properties.Item("*") | Should -BeNullOrEmpty + } + + # Issue #2351 + It "Select-Object with implicit Property = '*' exclude single property" { + $results = [pscustomobject]@{Thing = "thing1" } | Select-Object -ExcludeProperty thing + $results.psobject.Properties.Item("Thing") | Should -BeNullOrEmpty + $results.psobject.Properties.Item("*") | Should -BeNullOrEmpty + } + + # Issue #2351 + It "Select-Object with explicit Property = '*' exclude single property" { + $results = [pscustomobject]@{Thing = "thing1" } | Select-Object -Property * -ExcludeProperty thing + $results.psobject.Properties.Item("Thing") | Should -BeNullOrEmpty + $results.psobject.Properties.Item("*") | Should -BeNullOrEmpty + } + + # Issue #2351 + It "Select-Object with implicit Property = '*' exclude not single property" { + $results = [pscustomobject]@{Thing = "thing1"; Param2 = "param2" } | Select-Object -ExcludeProperty Param2 + $results.Param2 | Should -BeNullOrEmpty + $results.Thing | Should -BeExactly "thing1" + } + + # Issue #2351 + It "Select-Object with explicit Property = '*' exclude not single property" { + $results = [pscustomobject]@{Thing = "thing1"; Param2 = "param2" } | Select-Object -Property * -ExcludeProperty Param2 + $results.Param2 | Should -BeNullOrEmpty + $results.Thing | Should -BeExactly "thing1" + } It "Select-Object with ExpandProperty and Property don't skip processing ExcludeProperty" { - $p = Get-Process -Id $PID | Select-Object -Property Process* -ExcludeProperty ProcessorAffinity -ExpandProperty Modules - $p[0].psobject.Properties.Item("ProcessorAffinity") | Should -BeNullOrEmpty + $p = Get-Process -Id $pid | Select-Object -Property Process* -ExcludeProperty ProcessorAffinity -ExpandProperty Modules + $p[0].psobject.Properties.Item("ProcessorAffinity") | Should -BeNullOrEmpty } It "Select-Object add 'Selected.*' type only once" { From 2e7dd02755a739cc2d2c2dc7cd3eee017bd357b2 Mon Sep 17 00:00:00 2001 From: vexx32 <32407840+vexx32@users.noreply.github.com> Date: Sun, 17 Nov 2019 22:29:00 -0500 Subject: [PATCH 03/11] :white_check_mark: Add new tests --- .../Select-Object.Tests.ps1 | 32 +++++++++++++++++++ 1 file changed, 32 insertions(+) diff --git a/test/powershell/Modules/Microsoft.PowerShell.Utility/Select-Object.Tests.ps1 b/test/powershell/Modules/Microsoft.PowerShell.Utility/Select-Object.Tests.ps1 index 333070d5d17..e657ff7cd4b 100644 --- a/test/powershell/Modules/Microsoft.PowerShell.Utility/Select-Object.Tests.ps1 +++ b/test/powershell/Modules/Microsoft.PowerShell.Utility/Select-Object.Tests.ps1 @@ -369,3 +369,35 @@ Describe "Select-Object with Property = '*'" -Tags "CI" { $obj.psobject.TypeNames[2] | Should -Not -BeLike "Selected*" } } + +Describe 'Select-Object behaviour with hashtable entries and actual members' { + + It 'can retrieve a hashtable entry as a property' { + $hashtable = @{ Entry = 100 } + + $result = $hashtable | Select-Object -Property Entry + $result.Entry | Should -Be 100 + + $hashtable | + Select-Object -ExpandProperty Entry | + Should -Be 100 + } + + It 'can retrieve true hashtable members' { + $hashtable = @{ Value = 10 } + $result = $hashtable | Select-Object -Property Keys + $result.Keys | Should -Be 'Value' + + $hashtable | + Select-Object -ExpandProperty Keys | + Should -Be 'Value' + } + + It 'should prioritise hashtable entries where available' { + $hashtable = @{ Keys = 10 } + $result = $hashtable | Select-Object -Property Keys + $result.Keys | Should -Be 10 + + $hashtable | Select-Object -ExpandProperty Keys | Should -Be 10 + } +} From 41ce650416f75f0cb21fa30226c32e9a4fc1f7e1 Mon Sep 17 00:00:00 2001 From: vexx32 <32407840+vexx32@users.noreply.github.com> Date: Mon, 18 Nov 2019 00:17:51 -0500 Subject: [PATCH 04/11] :bug: Fix remaining bugs - Fixed issue where -expandproperty found members but -property didn't - Removed unnecessary second iteration of property names --- .../common/Utilities/Mshexpression.cs | 15 ++++++--------- 1 file changed, 6 insertions(+), 9 deletions(-) diff --git a/src/System.Management.Automation/FormatAndOutput/common/Utilities/Mshexpression.cs b/src/System.Management.Automation/FormatAndOutput/common/Utilities/Mshexpression.cs index db5413a9c21..0bcaf95bc9a 100644 --- a/src/System.Management.Automation/FormatAndOutput/common/Utilities/Mshexpression.cs +++ b/src/System.Management.Automation/FormatAndOutput/common/Utilities/Mshexpression.cs @@ -296,19 +296,16 @@ public List GetValues(PSObject target, bool expand, return retVal; } - foreach (PSPropertyExpression resolvedName in ResolveNames(wrappedTarget, expand)) + foreach (PSPropertyExpression resolvedName in ResolveNames(target, expand)) { PSPropertyExpressionResult result = resolvedName.GetValue(wrappedTarget, eatExceptions); - retVal.Add(result); - } - if (retVal.Count == 0 && wasHashtable) - { - foreach (PSPropertyExpression resolvedName in ResolveNames(target, expand)) + if (result.Result == null && wasHashtable) { - PSPropertyExpressionResult result = resolvedName.GetValue(target, eatExceptions); - retVal.Add(result); + result = resolvedName.GetValue(target, eatExceptions); } + + retVal.Add(result); } return retVal; @@ -372,8 +369,8 @@ private PSObject IfHashtableWrapAsPSCustomObject(PSObject target, out bool wrapp // that property expressions can work on it. if (PSObject.Base(target) is Hashtable targetAsHash) { - target = (PSObject)(LanguagePrimitives.ConvertPSObjectToType(targetAsHash, typeof(PSObject), false, null, true)); wrapped = true; + return (PSObject)(LanguagePrimitives.ConvertPSObjectToType(targetAsHash, typeof(PSObject), false, null, true)); } return target; From 989bde84c70ad048100113416c285bb153d807e7 Mon Sep 17 00:00:00 2001 From: vexx32 <32407840+vexx32@users.noreply.github.com> Date: Mon, 18 Nov 2019 00:42:56 -0500 Subject: [PATCH 05/11] :white_check_mark: add missing CI tag --- .../Microsoft.PowerShell.Utility/Select-Object.Tests.ps1 | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/test/powershell/Modules/Microsoft.PowerShell.Utility/Select-Object.Tests.ps1 b/test/powershell/Modules/Microsoft.PowerShell.Utility/Select-Object.Tests.ps1 index e657ff7cd4b..f2ae56c2ee5 100644 --- a/test/powershell/Modules/Microsoft.PowerShell.Utility/Select-Object.Tests.ps1 +++ b/test/powershell/Modules/Microsoft.PowerShell.Utility/Select-Object.Tests.ps1 @@ -370,7 +370,7 @@ Describe "Select-Object with Property = '*'" -Tags "CI" { } } -Describe 'Select-Object behaviour with hashtable entries and actual members' { +Describe 'Select-Object behaviour with hashtable entries and actual members' -Tags CI { It 'can retrieve a hashtable entry as a property' { $hashtable = @{ Entry = 100 } From 532c83effe73c9e0220c9eb55bdda19960280e41 Mon Sep 17 00:00:00 2001 From: "Joel Sallow (/u/ta11ow)" <32407840+vexx32@users.noreply.github.com> Date: Mon, 18 Nov 2019 18:23:47 -0500 Subject: [PATCH 06/11] :white_check_mark: Fix test that was broken The reformat broke this test (it relies on being a truly empty scriptblock, no spaces at all). Remove the added space to fix the test. --- .../Microsoft.PowerShell.Utility/Select-Object.Tests.ps1 | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/test/powershell/Modules/Microsoft.PowerShell.Utility/Select-Object.Tests.ps1 b/test/powershell/Modules/Microsoft.PowerShell.Utility/Select-Object.Tests.ps1 index f2ae56c2ee5..e884d39881c 100644 --- a/test/powershell/Modules/Microsoft.PowerShell.Utility/Select-Object.Tests.ps1 +++ b/test/powershell/Modules/Microsoft.PowerShell.Utility/Select-Object.Tests.ps1 @@ -128,7 +128,7 @@ Describe "Select-Object DRT basic functionality" -Tags "CI" { } It "Select-Object with empty script block property should throw" { - $e = { "bar" | Select-Object -Prop { } -ErrorAction Stop } | + $e = { "bar" | Select-Object -Prop {} -ErrorAction Stop } | Should -Throw -ErrorId "EmptyScriptBlockAndNoName,Microsoft.PowerShell.Commands.SelectObjectCommand" -PassThru $e.CategoryInfo | Should -Match "PSArgumentException" } From 74e066cdd27627a46644dec7518df705d916b317 Mon Sep 17 00:00:00 2001 From: Joel Sallow <32407840+vexx32@users.noreply.github.com> Date: Wed, 10 Jun 2020 22:17:30 -0400 Subject: [PATCH 07/11] :recycle: Address @daxian-dbw review comments - Add test for Count & Length members - Use base target object instead of wrapping hashtable --- .../common/Utilities/Mshexpression.cs | 35 +- .../Select-Object.Tests.ps1 | 417 ++++++++++-------- 2 files changed, 235 insertions(+), 217 deletions(-) diff --git a/src/System.Management.Automation/FormatAndOutput/common/Utilities/Mshexpression.cs b/src/System.Management.Automation/FormatAndOutput/common/Utilities/Mshexpression.cs index 0bcaf95bc9a..25e7eb18c2d 100644 --- a/src/System.Management.Automation/FormatAndOutput/common/Utilities/Mshexpression.cs +++ b/src/System.Management.Automation/FormatAndOutput/common/Utilities/Mshexpression.cs @@ -5,6 +5,7 @@ using System.Collections; using System.Collections.Generic; using System.Collections.ObjectModel; +using System.Linq; using System.Management.Automation; using System.Management.Automation.Internal; using System.Management.Automation.Language; @@ -162,20 +163,20 @@ public List ResolveNames(PSObject target, bool expand) var wrappedTarget = IfHashtableWrapAsPSCustomObject(target, out bool wasHashtable); // we have a string value - List members = new List(); + IEnumerable members; if (HasWildCardCharacters) { // get the members first: this will expand the globbing on each parameter - members.AddRange(wrappedTarget.Members.Match( + members = wrappedTarget.Members.Match( _stringValue, - PSMemberTypes.Properties | PSMemberTypes.PropertySet | PSMemberTypes.Dynamic)); + PSMemberTypes.Properties | PSMemberTypes.PropertySet | PSMemberTypes.Dynamic); // if target was a hashtable and no result is found from the keys, then use property value if available - if (wasHashtable && members.Count == 0) + if (wasHashtable && !members.Any()) { - members.AddRange(target.Members.Match( + members = target.Members.Match( _stringValue, - PSMemberTypes.Properties | PSMemberTypes.PropertySet | PSMemberTypes.Dynamic)); + PSMemberTypes.Properties | PSMemberTypes.PropertySet | PSMemberTypes.Dynamic); } } else @@ -185,9 +186,11 @@ public List ResolveNames(PSObject target, bool expand) if (x == null) { - x = target.Members[_stringValue]; - - if ((x == null) && (wrappedTarget.BaseObject is System.Dynamic.IDynamicMetaObjectProvider)) + if (wasHashtable) + { + x = target.Members[_stringValue]; + } + else if (wrappedTarget.BaseObject is System.Dynamic.IDynamicMetaObjectProvider) { // We could check if GetDynamicMemberNames includes the name... but // GetDynamicMemberNames is only a hint, not a contract, so we'd want @@ -283,28 +286,18 @@ public List GetValues(PSObject target, bool expand, { List retVal = new List(); - // If the object passed in is a hashtable, then turn it into a PSCustomObject so - // that property expressions can work on it. - var wrappedTarget = IfHashtableWrapAsPSCustomObject(target, out bool wasHashtable); - // process the script case if (Script != null) { PSPropertyExpression scriptExpression = new PSPropertyExpression(Script); - PSPropertyExpressionResult r = scriptExpression.GetValue(wrappedTarget, eatExceptions); + PSPropertyExpressionResult r = scriptExpression.GetValue(target, eatExceptions); retVal.Add(r); return retVal; } foreach (PSPropertyExpression resolvedName in ResolveNames(target, expand)) { - PSPropertyExpressionResult result = resolvedName.GetValue(wrappedTarget, eatExceptions); - - if (result.Result == null && wasHashtable) - { - result = resolvedName.GetValue(target, eatExceptions); - } - + PSPropertyExpressionResult result = resolvedName.GetValue(target, eatExceptions); retVal.Add(result); } diff --git a/test/powershell/Modules/Microsoft.PowerShell.Utility/Select-Object.Tests.ps1 b/test/powershell/Modules/Microsoft.PowerShell.Utility/Select-Object.Tests.ps1 index e884d39881c..4ce0a0bc173 100644 --- a/test/powershell/Modules/Microsoft.PowerShell.Utility/Select-Object.Tests.ps1 +++ b/test/powershell/Modules/Microsoft.PowerShell.Utility/Select-Object.Tests.ps1 @@ -6,129 +6,129 @@ Add-TestDynamicType Describe "Select-Object" -Tags "CI" { BeforeEach { - $dirObject = GetFileMock - $TestLength = 3 + $dirObject = GetFileMock + $TestLength = 3 } It "Handle piped input without error" { - { $dirObject | Select-Object } | Should -Not -Throw + { $dirObject | Select-Object } | Should -Not -Throw } It "Should treat input as a single object with the inputObject parameter" { $result = $(Select-Object -inputObject $dirObject -last $TestLength).Length $expected = $dirObject.Length - - $result | Should -Be $expected - } - - It "Should be able to use the alias" { - { $dirObject | select } | Should -Not -Throw + { $dirObject | select } | Should -Not -Throw } It "Should have same result when using alias" { - $result = $dirObject | select - $expected = $dirObject | Select-Object + $result = $dirObject | select + $expected = $dirObject | Select-Object - $result | Should -Be $expected + $result | Should -Be $expected } It "Should return correct object with First parameter" { - $result = $dirObject | Select-Object -First $TestLength + $result = $dirObject | Select-Object -First $TestLength - $result.Length | Should -Be $TestLength + $result.Length | Should -Be $TestLength - for ($i = 0; $i -lt $TestLength; $i++) { - $result[$i].Name | Should -Be $dirObject[$i].Name - } + for ($i=0; $i -lt $TestLength; $i++) + { + $result[$i].Name | Should -Be $dirObject[$i].Name + } } It "Should return correct object with Last parameter" { - $result = $dirObject | Select-Object -Last $TestLength + $result = $dirObject | Select-Object -Last $TestLength - $result.Length | Should -Be $TestLength + $result.Length | Should -Be $TestLength - for ($i = 0; $i -lt $TestLength; $i++) { - $result[$i].Name | Should -Be $dirObject[$dirObject.Length - $TestLength + $i].Name - } + for ($i=0; $i -lt $TestLength; $i++) + { + $result[$i].Name | Should -Be $dirObject[$dirObject.Length - $TestLength + $i].Name + } } It "Should work correctly with Unique parameter" { - $result = ("a", "b", "c", "a", "a", "a" | Select-Object -Unique).Length - $expected = 3 + $result = ("a","b","c","a","a","a" | Select-Object -Unique).Length + $expected = 3 - $result | Should -Be $expected + $result | Should -Be $expected } It "Should return correct object with Skip parameter" { - $result = $dirObject | Select-Object -Skip $TestLength + $result = $dirObject | Select-Object -Skip $TestLength - $result.Length | Should -Be ($dirObject.Length - $TestLength) + $result.Length | Should -Be ($dirObject.Length - $TestLength) - for ($i = 0; $i -lt $TestLength; $i++) { - $result[$i].Name | Should -Be $dirObject[$TestLength + $i].Name - } + for ($i=0; $i -lt $TestLength; $i++) + { + $result[$i].Name | Should -Be $dirObject[$TestLength + $i].Name + } } It "Should return an object with selected columns" { - $result = $dirObject | Select-Object -Property Name, Size + $result = $dirObject | Select-Object -Property Name, Size - $result.Length | Should -Be $dirObject.Length - $result[0].Name | Should -Be $dirObject[0].Name - $result[0].Size | Should -Be $dirObject[0].Size - $result[0].Mode | Should -BeNullOrEmpty + $result.Length | Should -Be $dirObject.Length + $result[0].Name | Should -Be $dirObject[0].Name + $result[0].Size | Should -Be $dirObject[0].Size + $result[0].Mode | Should -BeNullOrEmpty } It "Should send output to pipe properly" { - { $dirObject | Select-Object -Unique | pipelineConsume } | Should -Not -Throw + {$dirObject | Select-Object -Unique | pipelineConsume} | Should -Not -Throw } It "Should select array indices with Index parameter" { - $firstIndex = 2 - $secondIndex = 4 - $result = $dirObject | Select-Object -Index $firstIndex, $secondIndex + $firstIndex = 2 + $secondIndex = 4 + $result = $dirObject | Select-Object -Index $firstIndex, $secondIndex - $result[0].Name | Should -Be $dirObject[$firstIndex].Name - $result[1].Name | Should -Be $dirObject[$secondIndex].Name + $result[0].Name | Should -Be $dirObject[$firstIndex].Name + $result[1].Name | Should -Be $dirObject[$secondIndex].Name } # Note that these two tests will modify original values of $dirObject It "Should not wait when used without -Wait option" { - $orig1 = $dirObject[0].Size - $orig2 = $dirObject[$TestLength].Size - $result = $dirObject | addOneToSizeProperty | Select-Object -First $TestLength + $orig1 = $dirObject[0].Size + $orig2 = $dirObject[$TestLength].Size + $result = $dirObject | addOneToSizeProperty | Select-Object -First $TestLength - $result[0].Size | Should -Be ($orig1 + 1) - $dirObject[0].Size | Should -Be ($orig1 + 1) - $dirObject[$TestLength].Size | Should -Be $orig2 + $result[0].Size | Should -Be ($orig1 + 1) + $dirObject[0].Size | Should -Be ($orig1 + 1) + $dirObject[$TestLength].Size | Should -Be $orig2 } It "Should wait when used with -Wait option" { - $orig1 = $dirObject[0].Size - $orig2 = $dirObject[$TestLength].Size - $result = $dirObject | addOneToSizeProperty | Select-Object -First $TestLength -Wait - - $result[0].Size | Should -Be ($orig1 + 1) - $dirObject[0].Size | Should -Be ($orig1 + 1) - $dirObject[$TestLength].Size | Should -Be ($orig2 + 1) - } - - It "Should not leak 'StopUpstreamCommandsException' internal exception for stopping upstream" { - 1, 2 | Select-Object -First 1 -ErrorVariable err - $err | Should -BeNullOrEmpty - } + $orig1 = $dirObject[0].Size + $orig2 = $dirObject[$TestLength].Size + $result = $dirObject | addOneToSizeProperty | Select-Object -First $TestLength -Wait + + $result[0].Size | Should -Be ($orig1 + 1) + $dirObject[0].Size | Should -Be ($orig1 + 1) + $dirObject[$TestLength].Size | Should -Be ($orig2 + 1) + } + + It "Should not leak 'StopUpstreamCommandsException' internal exception for stopping upstream" { + 1,2 | Select-Object -First 1 -ErrorVariable err + $err | Should -BeNullOrEmpty + } } Describe "Select-Object DRT basic functionality" -Tags "CI" { - BeforeAll { - $employees = [pscustomobject]@{"FirstName" = "joseph"; "LastName" = "smith"; "YearsInMS" = 15 }, - [pscustomobject]@{"FirstName" = "paul"; "LastName" = "smith"; "YearsInMS" = 15 }, - [pscustomobject]@{"FirstName" = "mary"; "LastName" = "soe"; "YearsInMS" = 5 }, - [pscustomobject]@{"FirstName" = "edmund"; "LastName" = "bush"; "YearsInMS" = 9 } - } - + BeforeAll { + $employees = [pscustomobject]@{"FirstName"="joseph"; "LastName"="smith"; "YearsInMS"=15}, + [pscustomobject]@{"FirstName"="paul"; "LastName"="smith"; "YearsInMS"=15}, + [pscustomobject]@{"FirstName"="mary"; "LastName"="soe"; "YearsInMS"=5}, + [pscustomobject]@{"FirstName"="edmund"; "LastName"="bush"; "YearsInMS"=9} + } + + It "Select-Object with empty script block property should throw"{ + $e = { "bar" | Select-Object -Prop {} -ErrorAction Stop } | It "Select-Object with empty script block property should throw" { - $e = { "bar" | Select-Object -Prop {} -ErrorAction Stop } | + $e = { "bar" | Select-Object -Prop { } -ErrorAction Stop } | Should -Throw -ErrorId "EmptyScriptBlockAndNoName,Microsoft.PowerShell.Commands.SelectObjectCommand" -PassThru $e.CategoryInfo | Should -Match "PSArgumentException" } @@ -137,115 +137,100 @@ Describe "Select-Object DRT basic functionality" -Tags "CI" { $result = "bar" | Select-Object -Prop foo | Measure-Object $result.Count | Should -Be 1 } + } - It "Select-Object with Property First Last Overlap should work" { - $results = $employees | Select-Object -Property "YearsInMS", "L*" -First 2 -Last 3 - - $results.Count | Should -Be 4 - - $results[0].LastName | Should -Be $employees[0].LastName - $results[1].LastName | Should -Be $employees[1].LastName - $results[2].LastName | Should -Be $employees[2].LastName - $results[3].LastName | Should -Be $employees[3].LastName - - $results[0].YearsInMS | Should -Be $employees[0].YearsInMS - $results[1].YearsInMS | Should -Be $employees[1].YearsInMS - $results[2].YearsInMS | Should -Be $employees[2].YearsInMS - $results[3].YearsInMS | Should -Be $employees[3].YearsInMS - } - - It "Select-Object with Property First Last should work" { - $results = $employees | Select-Object -Property "YearsInMS", "L*" -First 2 -Last 1 + It "Select-Object with Property First Last should work"{ + $results = $employees | Select-Object -Property "YearsInMS", "L*" -First 2 -Last 1 - $results.Count | Should -Be 3 + $results.Count | Should -Be 3 - $results[0].LastName | Should -Be $employees[0].LastName - $results[1].LastName | Should -Be $employees[1].LastName - $results[2].LastName | Should -Be $employees[3].LastName + $results[0].LastName | Should -Be $employees[0].LastName + $results[1].LastName | Should -Be $employees[1].LastName + $results[2].LastName | Should -Be $employees[3].LastName - $results[0].YearsInMS | Should -Be $employees[0].YearsInMS - $results[1].YearsInMS | Should -Be $employees[1].YearsInMS - $results[2].YearsInMS | Should -Be $employees[3].YearsInMS - } + $results[0].YearsInMS | Should -Be $employees[0].YearsInMS + $results[1].YearsInMS | Should -Be $employees[1].YearsInMS + $results[2].YearsInMS | Should -Be $employees[3].YearsInMS + } - It "Select-Object with Property First should work" { - $results = $employees | Select-Object -Property "YearsInMS", "L*" -First 2 + It "Select-Object with Property First should work"{ + $results = $employees | Select-Object -Property "YearsInMS", "L*" -First 2 - $results.Count | Should -Be 2 + $results.Count | Should -Be 2 - $results[0].LastName | Should -Be $employees[0].LastName - $results[1].LastName | Should -Be $employees[1].LastName + $results[0].LastName | Should -Be $employees[0].LastName + $results[1].LastName | Should -Be $employees[1].LastName - $results[0].YearsInMS | Should -Be $employees[0].YearsInMS - $results[1].YearsInMS | Should -Be $employees[1].YearsInMS - } + $results[0].YearsInMS | Should -Be $employees[0].YearsInMS + $results[1].YearsInMS | Should -Be $employees[1].YearsInMS + } - It "Select-Object with Property First Zero should work" { - $results = $employees | Select-Object -Property "YearsInMS", "L*" -First 0 + It "Select-Object with Property First Zero should work"{ + $results = $employees | Select-Object -Property "YearsInMS", "L*" -First 0 - $results.Count | Should -Be 0 - } + $results.Count | Should -Be 0 + } - It "Select-Object with Property Last Zero should work" { - $results = $employees | Select-Object -Property "YearsInMS", "L*" -Last 0 + It "Select-Object with Property Last Zero should work"{ + $results = $employees | Select-Object -Property "YearsInMS", "L*" -Last 0 - $results.Count | Should -Be 0 - } + $results.Count | Should -Be 0 + } - It "Select-Object with Unique should work" { - $results = $employees | Select-Object -Property "YearsInMS", "L*" -Unique:$true + It "Select-Object with Unique should work"{ + $results = $employees | Select-Object -Property "YearsInMS", "L*" -Unique:$true - $results.Count | Should -Be 3 + $results.Count | Should -Be 3 - $results[0].LastName | Should -Be $employees[1].LastName - $results[1].LastName | Should -Be $employees[2].LastName - $results[2].LastName | Should -Be $employees[3].LastName + $results[0].LastName | Should -Be $employees[1].LastName + $results[1].LastName | Should -Be $employees[2].LastName + $results[2].LastName | Should -Be $employees[3].LastName - $results[0].YearsInMS | Should -Be $employees[1].YearsInMS - $results[1].YearsInMS | Should -Be $employees[2].YearsInMS - $results[2].YearsInMS | Should -Be $employees[3].YearsInMS - } + $results[0].YearsInMS | Should -Be $employees[1].YearsInMS + $results[1].YearsInMS | Should -Be $employees[2].YearsInMS + $results[2].YearsInMS | Should -Be $employees[3].YearsInMS + } - It "Select-Object with Simple should work" { - $employee1 = [pscustomobject]@{"FirstName" = "joesph"; "LastName" = "smith"; "YearsInMS" = 15 } - $employee2 = [pscustomobject]@{"FirstName" = "paul"; "LastName" = "smith"; "YearsInMS" = 15 } - $employee3 = [pscustomobject]@{"FirstName" = "mary"; "LastName" = "soe"; "YearsInMS" = 15 } - $employees3 = @($employee1, $employee2, $employee3, $employee4) - $results = $employees3 | Select-Object -Property "FirstName", "YearsInMS" + It "Select-Object with Simple should work"{ + $employee1 = [pscustomobject]@{"FirstName"="joesph"; "LastName"="smith"; "YearsInMS"=15} + $employee2 = [pscustomobject]@{"FirstName"="paul"; "LastName"="smith"; "YearsInMS"=15} + $employee3 = [pscustomobject]@{"FirstName"="mary"; "LastName"="soe"; "YearsInMS"=15} + $employees3 = @($employee1,$employee2,$employee3,$employee4) + $results = $employees3 | Select-Object -Property "FirstName", "YearsInMS" - $results.Count | Should -Be 3 + $results.Count | Should -Be 3 - $results[0].FirstName | Should -Be $employees3[0].FirstName - $results[1].FirstName | Should -Be $employees3[1].FirstName - $results[2].FirstName | Should -Be $employees3[2].FirstName + $results[0].FirstName | Should -Be $employees3[0].FirstName + $results[1].FirstName | Should -Be $employees3[1].FirstName + $results[2].FirstName | Should -Be $employees3[2].FirstName - $results[0].YearsInMS | Should -Be $employees3[0].YearsInMS - $results[1].YearsInMS | Should -Be $employees3[1].YearsInMS - $results[2].YearsInMS | Should -Be $employees3[2].YearsInMS - } + $results[0].YearsInMS | Should -Be $employees3[0].YearsInMS + $results[1].YearsInMS | Should -Be $employees3[1].YearsInMS + $results[2].YearsInMS | Should -Be $employees3[2].YearsInMS + } - It "Select-Object with no input should work" { - $results = $null | Select-Object -Property "FirstName", "YearsInMS", "FirstNa*" - $results.Count | Should -Be 0 - } + It "Select-Object with no input should work"{ + $results = $null | Select-Object -Property "FirstName", "YearsInMS", "FirstNa*" + $results.Count | Should -Be 0 + } - It "Select-Object with Start-Time In Idle Process should work" { - $results = Get-Process * | Select-Object ProcessName - $results.Count | Should -Not -Be 0 - } + It "Select-Object with Start-Time In Idle Process should work" { + $results = Get-Process * | Select-Object ProcessName + $results.Count | Should -Not -Be 0 + } - It "Select-Object with Skip should work" { - $results = "1", "2", "3" | Select-Object -Skip 1 - $results.Count | Should -Be 2 - $results[0] | Should -Be 2 - $results[1] | Should -Be 3 - } + It "Select-Object with Skip should work"{ + $results = "1","2","3" | Select-Object -Skip 1 + $results.Count | Should -Be 2 + $results[0] | Should -Be 2 + $results[1] | Should -Be 3 + } - It "Select-Object with Index should work" { - $results = "1", "2", "3" | Select-Object -Index 2 - $results.Count | Should -Be 1 - $results[0] | Should -BeExactly "3" - } + It "Select-Object with Index should work"{ + $results = "1","2","3" | Select-Object -Index 2 + $results.Count | Should -Be 1 + $results[0] | Should -BeExactly "3" + } It "Select-Object with SkipIndex should work" { $results = "1", "2", "3" | Select-Object -SkipIndex 0, 2 @@ -259,7 +244,7 @@ Describe "Select-Object DRT basic functionality" -Tags "CI" { $results -join ',' | Should -BeExactly "0,1,2,3,4,9,10" } - It "Select-Object should handle dynamic (DLR) properties" { + It "Select-Object should handle dynamic (DLR) properties"{ $dynObj = [TestDynamic]::new() $results = $dynObj, $dynObj | Select-Object -ExpandProperty FooProp $results.Count | Should -Be 2 @@ -267,7 +252,7 @@ Describe "Select-Object DRT basic functionality" -Tags "CI" { $results[1] | Should -Be 123 } - It "Select-Object should handle dynamic (DLR) properties without GetDynamicMemberNames hint" { + It "Select-Object should handle dynamic (DLR) properties without GetDynamicMemberNames hint"{ $dynObj = [TestDynamic]::new() $results = $dynObj, $dynObj | Select-Object -ExpandProperty HiddenProp $results.Count | Should -Be 2 @@ -275,7 +260,7 @@ Describe "Select-Object DRT basic functionality" -Tags "CI" { $results[1] | Should -Be 789 } - It "Select-Object should handle wildcarded dynamic (DLR) properties when hinted by GetDynamicMemberNames" { + It "Select-Object should handle wildcarded dynamic (DLR) properties when hinted by GetDynamicMemberNames"{ $dynObj = [TestDynamic]::new() $results = $dynObj, $dynObj | Select-Object -ExpandProperty FooP* $results.Count | Should -Be 2 @@ -283,7 +268,7 @@ Describe "Select-Object DRT basic functionality" -Tags "CI" { $results[1] | Should -Be 123 } - It "Select-Object should work when multiple dynamic (DLR) properties match" { + It "Select-Object should work when multiple dynamic (DLR) properties match"{ $dynObj = [TestDynamic]::new() $results = $dynObj, $dynObj | Select-Object *Prop $results.Count | Should -Be 2 @@ -293,9 +278,9 @@ Describe "Select-Object DRT basic functionality" -Tags "CI" { $results[1].BarProp | Should -Be 456 } - It "Select-Object -ExpandProperty should yield errors if multiple dynamic (DLR) properties match" { + It "Select-Object -ExpandProperty should yield errors if multiple dynamic (DLR) properties match"{ $dynObj = [TestDynamic]::new() - $e = { $results = $dynObj, $dynObj | Select-Object -ExpandProperty *Prop -ErrorAction Stop } | + $e = { $results = $dynObj, $dynObj | Select-Object -ExpandProperty *Prop -ErrorAction Stop} | Should -Throw -PassThru -ErrorId "MutlipleExpandProperties,Microsoft.PowerShell.Commands.SelectObjectCommand" $e.CategoryInfo | Should -Match "PSArgumentException" } @@ -303,52 +288,67 @@ Describe "Select-Object DRT basic functionality" -Tags "CI" { Describe "Select-Object with Property = '*'" -Tags "CI" { - # Issue #2420 - It "Select-Object with implicit Property = '*' don't return property named '*'" { - $results = [pscustomobject]@{Thing = "thing1" } | Select-Object -ExcludeProperty thing - $results.psobject.Properties.Item("*") | Should -BeNullOrEmpty - } - - # Issue #2420 - It "Select-Object with explicit Property = '*' don't return property named '*'" { - $results = [pscustomobject]@{Thing = "thing1" } | Select-Object -Property * -ExcludeProperty thing - $results.psobject.Properties.Item("*") | Should -BeNullOrEmpty - } + # Issue #2420 + It "Select-Object with implicit Property = '*' don't return property named '*'"{ + $results = [pscustomobject]@{Thing="thing1"} | Select-Object -ExcludeProperty thing + $results.psobject.Properties.Item("*") | Should -BeNullOrEmpty + } + + # Issue #2420 + It "Select-Object with explicit Property = '*' don't return property named '*'"{ + $results = [pscustomobject]@{Thing="thing1"} | Select-Object -Property * -ExcludeProperty thing + $results.psobject.Properties.Item("*") | Should -BeNullOrEmpty + } + + # Issue #2351 + It "Select-Object with implicit Property = '*' exclude single property"{ + $results = [pscustomobject]@{Thing="thing1"} | Select-Object -ExcludeProperty thing + $results.psobject.Properties.Item("Thing") | Should -BeNullOrEmpty + $results.psobject.Properties.Item("*") | Should -BeNullOrEmpty + } + + # Issue #2351 + It "Select-Object with explicit Property = '*' exclude single property"{ + $results = [pscustomobject]@{Thing="thing1"} | Select-Object -Property * -ExcludeProperty thing + $results.psobject.Properties.Item("Thing") | Should -BeNullOrEmpty + $results.psobject.Properties.Item("*") | Should -BeNullOrEmpty + } + + # Issue #2351 + It "Select-Object with implicit Property = '*' exclude not single property"{ + $results = [pscustomobject]@{Thing="thing1";Param2="param2"} | Select-Object -ExcludeProperty Param2 + $results.Param2 | Should -BeNullOrEmpty + $results.Thing | Should -BeExactly "thing1" + } + + # Issue #2351 + It "Select-Object with explicit Property = '*' exclude not single property"{ + $results = [pscustomobject]@{Thing="thing1";Param2="param2"} | Select-Object -Property * -ExcludeProperty Param2 + $results.Param2 | Should -BeNullOrEmpty + $results.Thing | Should -BeExactly "thing1" + } - # Issue #2351 - It "Select-Object with implicit Property = '*' exclude single property" { - $results = [pscustomobject]@{Thing = "thing1" } | Select-Object -ExcludeProperty thing - $results.psobject.Properties.Item("Thing") | Should -BeNullOrEmpty - $results.psobject.Properties.Item("*") | Should -BeNullOrEmpty + It "Select-Object with ExpandProperty and Property don't skip processing ExcludeProperty" { + $p = Get-Process -Id $PID | Select-Object -Property Process* -ExcludeProperty ProcessorAffinity -ExpandProperty Modules + $p[0].psobject.Properties.Item("ProcessorAffinity") | Should -BeNullOrEmpty } - # Issue #2351 - It "Select-Object with explicit Property = '*' exclude single property" { - $results = [pscustomobject]@{Thing = "thing1" } | Select-Object -Property * -ExcludeProperty thing - $results.psobject.Properties.Item("Thing") | Should -BeNullOrEmpty - $results.psobject.Properties.Item("*") | Should -BeNullOrEmpty - } + It "Select-Object add 'Selected.*' type only once" { + $obj = [PSCustomObject]@{ Name = 1 } - # Issue #2351 - It "Select-Object with implicit Property = '*' exclude not single property" { - $results = [pscustomobject]@{Thing = "thing1"; Param2 = "param2" } | Select-Object -ExcludeProperty Param2 - $results.Param2 | Should -BeNullOrEmpty - $results.Thing | Should -BeExactly "thing1" - } + $obj.psobject.TypeNames.Count | Should -Be 2 + $obj.psobject.TypeNames | Should -Not -BeLike "Selected*" - # Issue #2351 - It "Select-Object with explicit Property = '*' exclude not single property" { - $results = [pscustomobject]@{Thing = "thing1"; Param2 = "param2" } | Select-Object -Property * -ExcludeProperty Param2 - $results.Param2 | Should -BeNullOrEmpty - $results.Thing | Should -BeExactly "thing1" - } + $obj = $obj | Select-Object - It "Select-Object with ExpandProperty and Property don't skip processing ExcludeProperty" { + $obj.psobject.TypeNames.Count | Should -Be 3 + $obj.psobject.TypeNames[0] | Should -BeLike "Selected*" + $obj.psobject.TypeNames[1] | Should -Not -BeLike "Selected*" $p = Get-Process -Id $pid | Select-Object -Property Process* -ExcludeProperty ProcessorAffinity -ExpandProperty Modules $p[0].psobject.Properties.Item("ProcessorAffinity") | Should -BeNullOrEmpty + $obj.psobject.TypeNames[2] | Should -Not -BeLike "Selected*" } - - It "Select-Object add 'Selected.*' type only once" { +} $obj = [PSCustomObject]@{ Name = 1 } $obj.psobject.TypeNames.Count | Should -Be 2 @@ -400,4 +400,29 @@ Describe 'Select-Object behaviour with hashtable entries and actual members' -Ta $hashtable | Select-Object -ExpandProperty Keys | Should -Be 10 } + + It 'should get the hashtable Count member' { + $hashtable = @{ a = 10; b = 20; c = 30 } + $result = $hashtable | Select-Object -Property Count + $result.Count | Should -Be 3 + + $hashtable | Select-Object -ExpandProperty Count | Should -Be 3 + } + + It 'should get the hashtable Count member' { + $hashtable = @{ a = 10; b = 20; c = 30 } + $result = $hashtable | Select-Object -Property Count + $result.Count | Should -Be 3 + + $hashtable | Select-Object -ExpandProperty Count | Should -Be 3 + } + + It 'should get the Length member from a hashtable' { + $hashtable = @{ a = 10; b = 20; c = 30 } + + $result = $hashtable | Select-Object -Property Length + $result.Length | Should -Be 1 + + $hashtable | Select-Object -ExpandProperty Length | Should -Be 1 + } } From cfea794666e6a003ddece1222c51124a226f1073 Mon Sep 17 00:00:00 2001 From: Joel Sallow <32407840+vexx32@users.noreply.github.com> Date: Thu, 11 Jun 2020 09:52:19 -0400 Subject: [PATCH 08/11] :recycle: un-break test file - Still had tab stops in some places, replaced everything with spaces. - removed some accidentally duplicated code - 'Format Document' to ensure consistent indentation. Sorry about the diff, this file was a bit of a mess :pray: --- .../Select-Object.Tests.ps1 | 384 ++++++++---------- 1 file changed, 180 insertions(+), 204 deletions(-) diff --git a/test/powershell/Modules/Microsoft.PowerShell.Utility/Select-Object.Tests.ps1 b/test/powershell/Modules/Microsoft.PowerShell.Utility/Select-Object.Tests.ps1 index 4ce0a0bc173..9c70947da6c 100644 --- a/test/powershell/Modules/Microsoft.PowerShell.Utility/Select-Object.Tests.ps1 +++ b/test/powershell/Modules/Microsoft.PowerShell.Utility/Select-Object.Tests.ps1 @@ -6,127 +6,124 @@ Add-TestDynamicType Describe "Select-Object" -Tags "CI" { BeforeEach { - $dirObject = GetFileMock - $TestLength = 3 + $dirObject = GetFileMock + $TestLength = 3 } It "Handle piped input without error" { - { $dirObject | Select-Object } | Should -Not -Throw + { $dirObject | Select-Object } | Should -Not -Throw } It "Should treat input as a single object with the inputObject parameter" { - $result = $(Select-Object -inputObject $dirObject -last $TestLength).Length + $result = $(Select-Object -InputObject $dirObject -Last $TestLength).Length $expected = $dirObject.Length - { $dirObject | select } | Should -Not -Throw + { $dirObject | Select-Object } | Should -Not -Throw } It "Should have same result when using alias" { - $result = $dirObject | select - $expected = $dirObject | Select-Object + $result = $dirObject | Select-Object + $expected = $dirObject | Select-Object - $result | Should -Be $expected + $result | Should -Be $expected } It "Should return correct object with First parameter" { - $result = $dirObject | Select-Object -First $TestLength + $result = $dirObject | Select-Object -First $TestLength - $result.Length | Should -Be $TestLength + $result.Length | Should -Be $TestLength - for ($i=0; $i -lt $TestLength; $i++) - { - $result[$i].Name | Should -Be $dirObject[$i].Name - } + for ($i = 0; $i -lt $TestLength; $i++) { + $result[$i].Name | Should -Be $dirObject[$i].Name + } } It "Should return correct object with Last parameter" { - $result = $dirObject | Select-Object -Last $TestLength + $result = $dirObject | Select-Object -Last $TestLength - $result.Length | Should -Be $TestLength + $result.Length | Should -Be $TestLength - for ($i=0; $i -lt $TestLength; $i++) - { - $result[$i].Name | Should -Be $dirObject[$dirObject.Length - $TestLength + $i].Name - } + for ($i = 0; $i -lt $TestLength; $i++) { + $result[$i].Name | Should -Be $dirObject[$dirObject.Length - $TestLength + $i].Name + } } It "Should work correctly with Unique parameter" { - $result = ("a","b","c","a","a","a" | Select-Object -Unique).Length - $expected = 3 + $result = ("a", "b", "c", "a", "a", "a" | Select-Object -Unique).Length + $expected = 3 - $result | Should -Be $expected + $result | Should -Be $expected } It "Should return correct object with Skip parameter" { - $result = $dirObject | Select-Object -Skip $TestLength + $result = $dirObject | Select-Object -Skip $TestLength - $result.Length | Should -Be ($dirObject.Length - $TestLength) + $result.Length | Should -Be ($dirObject.Length - $TestLength) - for ($i=0; $i -lt $TestLength; $i++) - { - $result[$i].Name | Should -Be $dirObject[$TestLength + $i].Name - } + for ($i = 0; $i -lt $TestLength; $i++) { + $result[$i].Name | Should -Be $dirObject[$TestLength + $i].Name + } } It "Should return an object with selected columns" { - $result = $dirObject | Select-Object -Property Name, Size + $result = $dirObject | Select-Object -Property Name, Size - $result.Length | Should -Be $dirObject.Length - $result[0].Name | Should -Be $dirObject[0].Name - $result[0].Size | Should -Be $dirObject[0].Size - $result[0].Mode | Should -BeNullOrEmpty + $result.Length | Should -Be $dirObject.Length + $result[0].Name | Should -Be $dirObject[0].Name + $result[0].Size | Should -Be $dirObject[0].Size + $result[0].Mode | Should -BeNullOrEmpty } It "Should send output to pipe properly" { - {$dirObject | Select-Object -Unique | pipelineConsume} | Should -Not -Throw + { $dirObject | Select-Object -Unique | pipelineConsume } | Should -Not -Throw } It "Should select array indices with Index parameter" { - $firstIndex = 2 - $secondIndex = 4 - $result = $dirObject | Select-Object -Index $firstIndex, $secondIndex + $firstIndex = 2 + $secondIndex = 4 + $result = $dirObject | Select-Object -Index $firstIndex, $secondIndex - $result[0].Name | Should -Be $dirObject[$firstIndex].Name - $result[1].Name | Should -Be $dirObject[$secondIndex].Name + $result[0].Name | Should -Be $dirObject[$firstIndex].Name + $result[1].Name | Should -Be $dirObject[$secondIndex].Name } # Note that these two tests will modify original values of $dirObject It "Should not wait when used without -Wait option" { - $orig1 = $dirObject[0].Size - $orig2 = $dirObject[$TestLength].Size - $result = $dirObject | addOneToSizeProperty | Select-Object -First $TestLength + $orig1 = $dirObject[0].Size + $orig2 = $dirObject[$TestLength].Size + $result = $dirObject | addOneToSizeProperty | Select-Object -First $TestLength - $result[0].Size | Should -Be ($orig1 + 1) - $dirObject[0].Size | Should -Be ($orig1 + 1) - $dirObject[$TestLength].Size | Should -Be $orig2 + $result[0].Size | Should -Be ($orig1 + 1) + $dirObject[0].Size | Should -Be ($orig1 + 1) + $dirObject[$TestLength].Size | Should -Be $orig2 } It "Should wait when used with -Wait option" { - $orig1 = $dirObject[0].Size - $orig2 = $dirObject[$TestLength].Size - $result = $dirObject | addOneToSizeProperty | Select-Object -First $TestLength -Wait - - $result[0].Size | Should -Be ($orig1 + 1) - $dirObject[0].Size | Should -Be ($orig1 + 1) - $dirObject[$TestLength].Size | Should -Be ($orig2 + 1) - } - - It "Should not leak 'StopUpstreamCommandsException' internal exception for stopping upstream" { - 1,2 | Select-Object -First 1 -ErrorVariable err - $err | Should -BeNullOrEmpty - } + $orig1 = $dirObject[0].Size + $orig2 = $dirObject[$TestLength].Size + $result = $dirObject | addOneToSizeProperty | Select-Object -First $TestLength -Wait + + $result[0].Size | Should -Be ($orig1 + 1) + $dirObject[0].Size | Should -Be ($orig1 + 1) + $dirObject[$TestLength].Size | Should -Be ($orig2 + 1) + } + + It "Should not leak 'StopUpstreamCommandsException' internal exception for stopping upstream" { + 1, 2 | Select-Object -First 1 -ErrorVariable err + $err | Should -BeNullOrEmpty + } } Describe "Select-Object DRT basic functionality" -Tags "CI" { - BeforeAll { - $employees = [pscustomobject]@{"FirstName"="joseph"; "LastName"="smith"; "YearsInMS"=15}, - [pscustomobject]@{"FirstName"="paul"; "LastName"="smith"; "YearsInMS"=15}, - [pscustomobject]@{"FirstName"="mary"; "LastName"="soe"; "YearsInMS"=5}, - [pscustomobject]@{"FirstName"="edmund"; "LastName"="bush"; "YearsInMS"=9} - } - - It "Select-Object with empty script block property should throw"{ - $e = { "bar" | Select-Object -Prop {} -ErrorAction Stop } | + BeforeAll { + $employees = @( + [pscustomobject]@{"FirstName" = "joseph"; "LastName" = "smith"; "YearsInMS" = 15 } + [pscustomobject]@{"FirstName" = "paul"; "LastName" = "smith"; "YearsInMS" = 15 } + [pscustomobject]@{"FirstName" = "mary"; "LastName" = "soe"; "YearsInMS" = 5 } + [pscustomobject]@{"FirstName" = "edmund"; "LastName" = "bush"; "YearsInMS" = 9 } + ) + } + It "Select-Object with empty script block property should throw" { $e = { "bar" | Select-Object -Prop { } -ErrorAction Stop } | Should -Throw -ErrorId "EmptyScriptBlockAndNoName,Microsoft.PowerShell.Commands.SelectObjectCommand" -PassThru @@ -137,100 +134,99 @@ Describe "Select-Object DRT basic functionality" -Tags "CI" { $result = "bar" | Select-Object -Prop foo | Measure-Object $result.Count | Should -Be 1 } - } - It "Select-Object with Property First Last should work"{ - $results = $employees | Select-Object -Property "YearsInMS", "L*" -First 2 -Last 1 + It "Select-Object with Property First Last should work" { + $results = $employees | Select-Object -Property "YearsInMS", "L*" -First 2 -Last 1 - $results.Count | Should -Be 3 + $results.Count | Should -Be 3 - $results[0].LastName | Should -Be $employees[0].LastName - $results[1].LastName | Should -Be $employees[1].LastName - $results[2].LastName | Should -Be $employees[3].LastName + $results[0].LastName | Should -Be $employees[0].LastName + $results[1].LastName | Should -Be $employees[1].LastName + $results[2].LastName | Should -Be $employees[3].LastName - $results[0].YearsInMS | Should -Be $employees[0].YearsInMS - $results[1].YearsInMS | Should -Be $employees[1].YearsInMS - $results[2].YearsInMS | Should -Be $employees[3].YearsInMS - } + $results[0].YearsInMS | Should -Be $employees[0].YearsInMS + $results[1].YearsInMS | Should -Be $employees[1].YearsInMS + $results[2].YearsInMS | Should -Be $employees[3].YearsInMS + } - It "Select-Object with Property First should work"{ - $results = $employees | Select-Object -Property "YearsInMS", "L*" -First 2 + It "Select-Object with Property First should work" { + $results = $employees | Select-Object -Property "YearsInMS", "L*" -First 2 - $results.Count | Should -Be 2 + $results.Count | Should -Be 2 - $results[0].LastName | Should -Be $employees[0].LastName - $results[1].LastName | Should -Be $employees[1].LastName + $results[0].LastName | Should -Be $employees[0].LastName + $results[1].LastName | Should -Be $employees[1].LastName - $results[0].YearsInMS | Should -Be $employees[0].YearsInMS - $results[1].YearsInMS | Should -Be $employees[1].YearsInMS - } + $results[0].YearsInMS | Should -Be $employees[0].YearsInMS + $results[1].YearsInMS | Should -Be $employees[1].YearsInMS + } - It "Select-Object with Property First Zero should work"{ - $results = $employees | Select-Object -Property "YearsInMS", "L*" -First 0 + It "Select-Object with Property First Zero should work" { + $results = $employees | Select-Object -Property "YearsInMS", "L*" -First 0 - $results.Count | Should -Be 0 - } + $results.Count | Should -Be 0 + } - It "Select-Object with Property Last Zero should work"{ - $results = $employees | Select-Object -Property "YearsInMS", "L*" -Last 0 + It "Select-Object with Property Last Zero should work" { + $results = $employees | Select-Object -Property "YearsInMS", "L*" -Last 0 - $results.Count | Should -Be 0 - } + $results.Count | Should -Be 0 + } - It "Select-Object with Unique should work"{ - $results = $employees | Select-Object -Property "YearsInMS", "L*" -Unique:$true + It "Select-Object with Unique should work" { + $results = $employees | Select-Object -Property "YearsInMS", "L*" -Unique:$true - $results.Count | Should -Be 3 + $results.Count | Should -Be 3 - $results[0].LastName | Should -Be $employees[1].LastName - $results[1].LastName | Should -Be $employees[2].LastName - $results[2].LastName | Should -Be $employees[3].LastName + $results[0].LastName | Should -Be $employees[1].LastName + $results[1].LastName | Should -Be $employees[2].LastName + $results[2].LastName | Should -Be $employees[3].LastName - $results[0].YearsInMS | Should -Be $employees[1].YearsInMS - $results[1].YearsInMS | Should -Be $employees[2].YearsInMS - $results[2].YearsInMS | Should -Be $employees[3].YearsInMS - } + $results[0].YearsInMS | Should -Be $employees[1].YearsInMS + $results[1].YearsInMS | Should -Be $employees[2].YearsInMS + $results[2].YearsInMS | Should -Be $employees[3].YearsInMS + } - It "Select-Object with Simple should work"{ - $employee1 = [pscustomobject]@{"FirstName"="joesph"; "LastName"="smith"; "YearsInMS"=15} - $employee2 = [pscustomobject]@{"FirstName"="paul"; "LastName"="smith"; "YearsInMS"=15} - $employee3 = [pscustomobject]@{"FirstName"="mary"; "LastName"="soe"; "YearsInMS"=15} - $employees3 = @($employee1,$employee2,$employee3,$employee4) - $results = $employees3 | Select-Object -Property "FirstName", "YearsInMS" + It "Select-Object with Simple should work" { + $employee1 = [pscustomobject]@{"FirstName" = "joesph"; "LastName" = "smith"; "YearsInMS" = 15 } + $employee2 = [pscustomobject]@{"FirstName" = "paul"; "LastName" = "smith"; "YearsInMS" = 15 } + $employee3 = [pscustomobject]@{"FirstName" = "mary"; "LastName" = "soe"; "YearsInMS" = 15 } + $employees3 = @($employee1, $employee2, $employee3, $employee4) + $results = $employees3 | Select-Object -Property "FirstName", "YearsInMS" - $results.Count | Should -Be 3 + $results.Count | Should -Be 3 - $results[0].FirstName | Should -Be $employees3[0].FirstName - $results[1].FirstName | Should -Be $employees3[1].FirstName - $results[2].FirstName | Should -Be $employees3[2].FirstName + $results[0].FirstName | Should -Be $employees3[0].FirstName + $results[1].FirstName | Should -Be $employees3[1].FirstName + $results[2].FirstName | Should -Be $employees3[2].FirstName - $results[0].YearsInMS | Should -Be $employees3[0].YearsInMS - $results[1].YearsInMS | Should -Be $employees3[1].YearsInMS - $results[2].YearsInMS | Should -Be $employees3[2].YearsInMS - } + $results[0].YearsInMS | Should -Be $employees3[0].YearsInMS + $results[1].YearsInMS | Should -Be $employees3[1].YearsInMS + $results[2].YearsInMS | Should -Be $employees3[2].YearsInMS + } - It "Select-Object with no input should work"{ - $results = $null | Select-Object -Property "FirstName", "YearsInMS", "FirstNa*" - $results.Count | Should -Be 0 - } + It "Select-Object with no input should work" { + $results = $null | Select-Object -Property "FirstName", "YearsInMS", "FirstNa*" + $results.Count | Should -Be 0 + } - It "Select-Object with Start-Time In Idle Process should work" { - $results = Get-Process * | Select-Object ProcessName - $results.Count | Should -Not -Be 0 - } + It "Select-Object with Start-Time In Idle Process should work" { + $results = Get-Process * | Select-Object ProcessName + $results.Count | Should -Not -Be 0 + } - It "Select-Object with Skip should work"{ - $results = "1","2","3" | Select-Object -Skip 1 - $results.Count | Should -Be 2 - $results[0] | Should -Be 2 - $results[1] | Should -Be 3 - } + It "Select-Object with Skip should work" { + $results = "1", "2", "3" | Select-Object -Skip 1 + $results.Count | Should -Be 2 + $results[0] | Should -Be 2 + $results[1] | Should -Be 3 + } - It "Select-Object with Index should work"{ - $results = "1","2","3" | Select-Object -Index 2 - $results.Count | Should -Be 1 - $results[0] | Should -BeExactly "3" - } + It "Select-Object with Index should work" { + $results = "1", "2", "3" | Select-Object -Index 2 + $results.Count | Should -Be 1 + $results[0] | Should -BeExactly "3" + } It "Select-Object with SkipIndex should work" { $results = "1", "2", "3" | Select-Object -SkipIndex 0, 2 @@ -244,7 +240,7 @@ Describe "Select-Object DRT basic functionality" -Tags "CI" { $results -join ',' | Should -BeExactly "0,1,2,3,4,9,10" } - It "Select-Object should handle dynamic (DLR) properties"{ + It "Select-Object should handle dynamic (DLR) properties" { $dynObj = [TestDynamic]::new() $results = $dynObj, $dynObj | Select-Object -ExpandProperty FooProp $results.Count | Should -Be 2 @@ -252,7 +248,7 @@ Describe "Select-Object DRT basic functionality" -Tags "CI" { $results[1] | Should -Be 123 } - It "Select-Object should handle dynamic (DLR) properties without GetDynamicMemberNames hint"{ + It "Select-Object should handle dynamic (DLR) properties without GetDynamicMemberNames hint" { $dynObj = [TestDynamic]::new() $results = $dynObj, $dynObj | Select-Object -ExpandProperty HiddenProp $results.Count | Should -Be 2 @@ -260,7 +256,7 @@ Describe "Select-Object DRT basic functionality" -Tags "CI" { $results[1] | Should -Be 789 } - It "Select-Object should handle wildcarded dynamic (DLR) properties when hinted by GetDynamicMemberNames"{ + It "Select-Object should handle wildcarded dynamic (DLR) properties when hinted by GetDynamicMemberNames" { $dynObj = [TestDynamic]::new() $results = $dynObj, $dynObj | Select-Object -ExpandProperty FooP* $results.Count | Should -Be 2 @@ -268,7 +264,7 @@ Describe "Select-Object DRT basic functionality" -Tags "CI" { $results[1] | Should -Be 123 } - It "Select-Object should work when multiple dynamic (DLR) properties match"{ + It "Select-Object should work when multiple dynamic (DLR) properties match" { $dynObj = [TestDynamic]::new() $results = $dynObj, $dynObj | Select-Object *Prop $results.Count | Should -Be 2 @@ -278,9 +274,9 @@ Describe "Select-Object DRT basic functionality" -Tags "CI" { $results[1].BarProp | Should -Be 456 } - It "Select-Object -ExpandProperty should yield errors if multiple dynamic (DLR) properties match"{ + It "Select-Object -ExpandProperty should yield errors if multiple dynamic (DLR) properties match" { $dynObj = [TestDynamic]::new() - $e = { $results = $dynObj, $dynObj | Select-Object -ExpandProperty *Prop -ErrorAction Stop} | + $e = { $results = $dynObj, $dynObj | Select-Object -ExpandProperty *Prop -ErrorAction Stop } | Should -Throw -PassThru -ErrorId "MutlipleExpandProperties,Microsoft.PowerShell.Commands.SelectObjectCommand" $e.CategoryInfo | Should -Match "PSArgumentException" } @@ -288,67 +284,52 @@ Describe "Select-Object DRT basic functionality" -Tags "CI" { Describe "Select-Object with Property = '*'" -Tags "CI" { - # Issue #2420 - It "Select-Object with implicit Property = '*' don't return property named '*'"{ - $results = [pscustomobject]@{Thing="thing1"} | Select-Object -ExcludeProperty thing - $results.psobject.Properties.Item("*") | Should -BeNullOrEmpty - } - - # Issue #2420 - It "Select-Object with explicit Property = '*' don't return property named '*'"{ - $results = [pscustomobject]@{Thing="thing1"} | Select-Object -Property * -ExcludeProperty thing - $results.psobject.Properties.Item("*") | Should -BeNullOrEmpty - } - - # Issue #2351 - It "Select-Object with implicit Property = '*' exclude single property"{ - $results = [pscustomobject]@{Thing="thing1"} | Select-Object -ExcludeProperty thing - $results.psobject.Properties.Item("Thing") | Should -BeNullOrEmpty - $results.psobject.Properties.Item("*") | Should -BeNullOrEmpty - } - - # Issue #2351 - It "Select-Object with explicit Property = '*' exclude single property"{ - $results = [pscustomobject]@{Thing="thing1"} | Select-Object -Property * -ExcludeProperty thing - $results.psobject.Properties.Item("Thing") | Should -BeNullOrEmpty - $results.psobject.Properties.Item("*") | Should -BeNullOrEmpty - } - - # Issue #2351 - It "Select-Object with implicit Property = '*' exclude not single property"{ - $results = [pscustomobject]@{Thing="thing1";Param2="param2"} | Select-Object -ExcludeProperty Param2 - $results.Param2 | Should -BeNullOrEmpty - $results.Thing | Should -BeExactly "thing1" - } - - # Issue #2351 - It "Select-Object with explicit Property = '*' exclude not single property"{ - $results = [pscustomobject]@{Thing="thing1";Param2="param2"} | Select-Object -Property * -ExcludeProperty Param2 - $results.Param2 | Should -BeNullOrEmpty - $results.Thing | Should -BeExactly "thing1" - } + # Issue #2420 + It "Select-Object with implicit Property = '*' don't return property named '*'" { + $results = [pscustomobject]@{Thing = "thing1" } | Select-Object -ExcludeProperty thing + $results.psobject.Properties.Item("*") | Should -BeNullOrEmpty + } - It "Select-Object with ExpandProperty and Property don't skip processing ExcludeProperty" { - $p = Get-Process -Id $PID | Select-Object -Property Process* -ExcludeProperty ProcessorAffinity -ExpandProperty Modules - $p[0].psobject.Properties.Item("ProcessorAffinity") | Should -BeNullOrEmpty + # Issue #2420 + It "Select-Object with explicit Property = '*' don't return property named '*'" { + $results = [pscustomobject]@{Thing = "thing1" } | Select-Object -Property * -ExcludeProperty thing + $results.psobject.Properties.Item("*") | Should -BeNullOrEmpty } - It "Select-Object add 'Selected.*' type only once" { - $obj = [PSCustomObject]@{ Name = 1 } + # Issue #2351 + It "Select-Object with implicit Property = '*' exclude single property" { + $results = [pscustomobject]@{Thing = "thing1" } | Select-Object -ExcludeProperty thing + $results.psobject.Properties.Item("Thing") | Should -BeNullOrEmpty + $results.psobject.Properties.Item("*") | Should -BeNullOrEmpty + } - $obj.psobject.TypeNames.Count | Should -Be 2 - $obj.psobject.TypeNames | Should -Not -BeLike "Selected*" + # Issue #2351 + It "Select-Object with explicit Property = '*' exclude single property" { + $results = [pscustomobject]@{Thing = "thing1" } | Select-Object -Property * -ExcludeProperty thing + $results.psobject.Properties.Item("Thing") | Should -BeNullOrEmpty + $results.psobject.Properties.Item("*") | Should -BeNullOrEmpty + } - $obj = $obj | Select-Object + # Issue #2351 + It "Select-Object with implicit Property = '*' exclude not single property" { + $results = [pscustomobject]@{Thing = "thing1"; Param2 = "param2" } | Select-Object -ExcludeProperty Param2 + $results.Param2 | Should -BeNullOrEmpty + $results.Thing | Should -BeExactly "thing1" + } - $obj.psobject.TypeNames.Count | Should -Be 3 - $obj.psobject.TypeNames[0] | Should -BeLike "Selected*" - $obj.psobject.TypeNames[1] | Should -Not -BeLike "Selected*" - $p = Get-Process -Id $pid | Select-Object -Property Process* -ExcludeProperty ProcessorAffinity -ExpandProperty Modules + # Issue #2351 + It "Select-Object with explicit Property = '*' exclude not single property" { + $results = [pscustomobject]@{Thing = "thing1"; Param2 = "param2" } | Select-Object -Property * -ExcludeProperty Param2 + $results.Param2 | Should -BeNullOrEmpty + $results.Thing | Should -BeExactly "thing1" + } + + It "Select-Object with ExpandProperty and Property don't skip processing ExcludeProperty" { + $p = Get-Process -Id $PID | Select-Object -Property Process* -ExcludeProperty ProcessorAffinity -ExpandProperty Modules $p[0].psobject.Properties.Item("ProcessorAffinity") | Should -BeNullOrEmpty - $obj.psobject.TypeNames[2] | Should -Not -BeLike "Selected*" } -} + + It "Select-Object add 'Selected.*' type only once" { $obj = [PSCustomObject]@{ Name = 1 } $obj.psobject.TypeNames.Count | Should -Be 2 @@ -359,13 +340,8 @@ Describe "Select-Object with Property = '*'" -Tags "CI" { $obj.psobject.TypeNames.Count | Should -Be 3 $obj.psobject.TypeNames[0] | Should -BeLike "Selected*" $obj.psobject.TypeNames[1] | Should -Not -BeLike "Selected*" - $obj.psobject.TypeNames[2] | Should -Not -BeLike "Selected*" - - $obj = $obj | Select-Object - - $obj.psobject.TypeNames.Count | Should -Be 3 - $obj.psobject.TypeNames[0] | Should -BeLike "Selected*" - $obj.psobject.TypeNames[1] | Should -Not -BeLike "Selected*" + $p = Get-Process -Id $pid | Select-Object -Property Process* -ExcludeProperty ProcessorAffinity -ExpandProperty Modules + $p[0].psobject.Properties.Item("ProcessorAffinity") | Should -BeNullOrEmpty $obj.psobject.TypeNames[2] | Should -Not -BeLike "Selected*" } } From f7845eba7abbb72c556fb2caa2cef4b5f8808b44 Mon Sep 17 00:00:00 2001 From: Joel Sallow <32407840+vexx32@users.noreply.github.com> Date: Fri, 12 Jun 2020 12:13:58 -0400 Subject: [PATCH 09/11] :white_check_mark: Resolve test issues --- .../Select-Object.Tests.ps1 | 19 +------------------ 1 file changed, 1 insertion(+), 18 deletions(-) diff --git a/test/powershell/Modules/Microsoft.PowerShell.Utility/Select-Object.Tests.ps1 b/test/powershell/Modules/Microsoft.PowerShell.Utility/Select-Object.Tests.ps1 index 9c70947da6c..2dc63f6d002 100644 --- a/test/powershell/Modules/Microsoft.PowerShell.Utility/Select-Object.Tests.ps1 +++ b/test/powershell/Modules/Microsoft.PowerShell.Utility/Select-Object.Tests.ps1 @@ -125,7 +125,7 @@ Describe "Select-Object DRT basic functionality" -Tags "CI" { } It "Select-Object with empty script block property should throw" { - $e = { "bar" | Select-Object -Prop { } -ErrorAction Stop } | + $e = { "bar" | Select-Object -Prop {} -ErrorAction Stop } | Should -Throw -ErrorId "EmptyScriptBlockAndNoName,Microsoft.PowerShell.Commands.SelectObjectCommand" -PassThru $e.CategoryInfo | Should -Match "PSArgumentException" } @@ -384,21 +384,4 @@ Describe 'Select-Object behaviour with hashtable entries and actual members' -Ta $hashtable | Select-Object -ExpandProperty Count | Should -Be 3 } - - It 'should get the hashtable Count member' { - $hashtable = @{ a = 10; b = 20; c = 30 } - $result = $hashtable | Select-Object -Property Count - $result.Count | Should -Be 3 - - $hashtable | Select-Object -ExpandProperty Count | Should -Be 3 - } - - It 'should get the Length member from a hashtable' { - $hashtable = @{ a = 10; b = 20; c = 30 } - - $result = $hashtable | Select-Object -Property Length - $result.Length | Should -Be 1 - - $hashtable | Select-Object -ExpandProperty Length | Should -Be 1 - } } From 3d8300020ad070497fc1af50a8758fa45e6524ab Mon Sep 17 00:00:00 2001 From: Joel Sallow <32407840+vexx32@users.noreply.github.com> Date: Tue, 16 Jun 2020 01:49:32 -0400 Subject: [PATCH 10/11] :recycle: Address review comments --- .../FormatAndOutput/common/Utilities/Mshexpression.cs | 7 ++++++- 1 file changed, 6 insertions(+), 1 deletion(-) diff --git a/src/System.Management.Automation/FormatAndOutput/common/Utilities/Mshexpression.cs b/src/System.Management.Automation/FormatAndOutput/common/Utilities/Mshexpression.cs index 25e7eb18c2d..a4b51ec36c7 100644 --- a/src/System.Management.Automation/FormatAndOutput/common/Utilities/Mshexpression.cs +++ b/src/System.Management.Automation/FormatAndOutput/common/Utilities/Mshexpression.cs @@ -363,7 +363,12 @@ private PSObject IfHashtableWrapAsPSCustomObject(PSObject target, out bool wrapp if (PSObject.Base(target) is Hashtable targetAsHash) { wrapped = true; - return (PSObject)(LanguagePrimitives.ConvertPSObjectToType(targetAsHash, typeof(PSObject), false, null, true)); + return (PSObject)(LanguagePrimitives.ConvertPSObjectToType( + targetAsHash, + typeof(PSObject), + recursion: false, + formatProvider: null, + ignoreUnknownMembers: true)); } return target; From f0802debcde64aaab47c8e65373bc57e9e31ac4d Mon Sep 17 00:00:00 2001 From: Joel Sallow <32407840+vexx32@users.noreply.github.com> Date: Fri, 19 Jun 2020 12:16:43 -0400 Subject: [PATCH 11/11] :white_check_mark: Restore mangled tests Some of the Select-Object tests got a bit mangled during format. This commit should restore them all back to proper order. --- .../Select-Object.Tests.ps1 | 13 ++++++++++--- 1 file changed, 10 insertions(+), 3 deletions(-) diff --git a/test/powershell/Modules/Microsoft.PowerShell.Utility/Select-Object.Tests.ps1 b/test/powershell/Modules/Microsoft.PowerShell.Utility/Select-Object.Tests.ps1 index 2dc63f6d002..05885698392 100644 --- a/test/powershell/Modules/Microsoft.PowerShell.Utility/Select-Object.Tests.ps1 +++ b/test/powershell/Modules/Microsoft.PowerShell.Utility/Select-Object.Tests.ps1 @@ -17,7 +17,11 @@ Describe "Select-Object" -Tags "CI" { It "Should treat input as a single object with the inputObject parameter" { $result = $(Select-Object -InputObject $dirObject -Last $TestLength).Length $expected = $dirObject.Length - { $dirObject | Select-Object } | Should -Not -Throw + $result | Should -Be $expected + } + + It "Should be able to use the alias" { + { $dirObject | select } | Should -Not -Throw } It "Should have same result when using alias" { @@ -130,6 +134,11 @@ Describe "Select-Object DRT basic functionality" -Tags "CI" { $e.CategoryInfo | Should -Match "PSArgumentException" } + It "Select-Object with Property First Last Overlap should work" { + $results = $employees | Select-Object -Property "YearsInMS", "L*" -First 2 -Last 3 + $results.Count | Should -Be 4 + } + It "Select-Object with string property should work" { $result = "bar" | Select-Object -Prop foo | Measure-Object $result.Count | Should -Be 1 @@ -340,8 +349,6 @@ Describe "Select-Object with Property = '*'" -Tags "CI" { $obj.psobject.TypeNames.Count | Should -Be 3 $obj.psobject.TypeNames[0] | Should -BeLike "Selected*" $obj.psobject.TypeNames[1] | Should -Not -BeLike "Selected*" - $p = Get-Process -Id $pid | Select-Object -Property Process* -ExcludeProperty ProcessorAffinity -ExpandProperty Modules - $p[0].psobject.Properties.Item("ProcessorAffinity") | Should -BeNullOrEmpty $obj.psobject.TypeNames[2] | Should -Not -BeLike "Selected*" } }