From e23d2e53d564eaf686c894f0e712abc79d22398a Mon Sep 17 00:00:00 2001 From: Steve Lee Date: Fri, 21 Jul 2017 13:50:14 -0700 Subject: [PATCH] Add Test coverage for XmlCommand.cs (#4201) --- .../commands/utility/XmlCommands.cs | 79 +++++------- .../ConvertTo-Xml.Tests.ps1 | 19 +++ .../XMLCommand.Tests.ps1 | 120 +++++++++++++++++- .../xml.tests.ps1 | 48 ++++++- 4 files changed, 205 insertions(+), 61 deletions(-) diff --git a/src/Microsoft.PowerShell.Commands.Utility/commands/utility/XmlCommands.cs b/src/Microsoft.PowerShell.Commands.Utility/commands/utility/XmlCommands.cs index 71dfd9e27..2a9263060 100644 --- a/src/Microsoft.PowerShell.Commands.Utility/commands/utility/XmlCommands.cs +++ b/src/Microsoft.PowerShell.Commands.Utility/commands/utility/XmlCommands.cs @@ -240,12 +240,6 @@ namespace Microsoft.PowerShell.Commands _fs.Dispose(); _fs = null; } - // reset the read-only attribute - if (null != _readOnlyFileInfo) - { - _readOnlyFileInfo.Attributes |= FileAttributes.ReadOnly; - _readOnlyFileInfo = null; - } } #endregion file @@ -572,11 +566,11 @@ namespace Microsoft.PowerShell.Commands if (Depth == 0) { - _serializer = new CustomSerialization(_xw, _notypeinformation); + _serializer = new CustomSerialization(_xw, NoTypeInformation); } else { - _serializer = new CustomSerialization(_xw, _notypeinformation, Depth); + _serializer = new CustomSerialization(_xw, NoTypeInformation, Depth); } } @@ -645,14 +639,8 @@ namespace Microsoft.PowerShell.Commands internal ImportXmlHelper(string fileName, PSCmdlet cmdlet, bool isLiteralPath) { - if (fileName == null) - { - throw PSTraceSource.NewArgumentNullException("fileName"); - } - if (cmdlet == null) - { - throw PSTraceSource.NewArgumentNullException("cmdlet"); - } + Dbg.Assert(fileName != null, "filename is mandatory"); + Dbg.Assert(cmdlet != null, "cmdlet is mandatory"); _path = fileName; _cmdlet = cmdlet; _isLiteralPath = isLiteralPath; @@ -749,17 +737,10 @@ namespace Microsoft.PowerShell.Commands // if paging is not specified then keep the old V2 behavior if (skip == 0 && first == ulong.MaxValue) { - ulong item = 0; while (!_deserializer.Done()) { object result = _deserializer.Deserialize(); - if (item++ < skip) - continue; - if (first == 0) - break; - _cmdlet.WriteObject(result); - first--; } } // else try to flatten the output if possible @@ -772,36 +753,38 @@ namespace Microsoft.PowerShell.Commands object result = _deserializer.Deserialize(); PSObject psObject = result as PSObject; - if (psObject == null && skipped++ >= skip) + if (psObject != null) + { + ICollection c = psObject.BaseObject as ICollection; + if (c != null) + { + foreach (object o in c) + { + if (count >= first) + break; + + if (skipped++ >= skip) + { + count++; + _cmdlet.WriteObject(o); + } + } + } + else + { + if (skipped++ >= skip) + { + count++; + _cmdlet.WriteObject(result); + } + } + } + else if (skipped++ >= skip) { count++; _cmdlet.WriteObject(result); continue; } - - ICollection c = psObject.BaseObject as ICollection; - if (c != null) - { - foreach (object o in c) - { - if (count >= first) - break; - - if (skipped++ >= skip) - { - count++; - _cmdlet.WriteObject(o); - } - } - } - else - { - if (skipped++ >= skip) - { - count++; - _cmdlet.WriteObject(result); - } - } } } } diff --git a/test/powershell/Modules/Microsoft.PowerShell.Utility/ConvertTo-Xml.Tests.ps1 b/test/powershell/Modules/Microsoft.PowerShell.Utility/ConvertTo-Xml.Tests.ps1 index fab41749a..08a1387fa 100644 --- a/test/powershell/Modules/Microsoft.PowerShell.Utility/ConvertTo-Xml.Tests.ps1 +++ b/test/powershell/Modules/Microsoft.PowerShell.Utility/ConvertTo-Xml.Tests.ps1 @@ -49,5 +49,24 @@ $expectedValue = 'val1val2' $returnObject.OuterXml | Should Be $expectedValue } + + It "StopProcessing should work" { + $ps = [PowerShell]::Create() + $ps.AddCommand("Get-Process") + $ps.AddCommand("ConvertTo-Xml") + $ps.AddParameter("Depth", 2) + $ps.BeginInvoke() + $ps.Stop() + $ps.InvocationStateInfo.State | Should Be "Stopped" + } + + # these tests just cover aspects that aren't normally exercised being used as a cmdlet + It "Can read back switch and parameter values using api" { + Add-Type -AssemblyName "${pshome}/Microsoft.PowerShell.Commands.Utility.dll" + + $cmd = [Microsoft.PowerShell.Commands.ConvertToXmlCommand]::new() + $cmd.NoTypeInformation = $true + $cmd.NoTypeInformation | Should Be $true + } } diff --git a/test/powershell/Modules/Microsoft.PowerShell.Utility/XMLCommand.Tests.ps1 b/test/powershell/Modules/Microsoft.PowerShell.Utility/XMLCommand.Tests.ps1 index 310d7dd00..e5c1de69c 100644 --- a/test/powershell/Modules/Microsoft.PowerShell.Utility/XMLCommand.Tests.ps1 +++ b/test/powershell/Modules/Microsoft.PowerShell.Utility/XMLCommand.Tests.ps1 @@ -30,17 +30,17 @@ Describe "XmlCommand DRT basic functionality Tests" -Tags "CI" { } AfterEach { - remove-item $testfile + remove-item $testfile -Force -ErrorAction SilentlyContinue } - It "Import with CliXml directive should work" -Skip:$IsOSX{ - Get-Process | Export-Clixml $testfile + It "Import with CliXml directive should work" { + Get-Command export* -Type Cmdlet | Select-Object -First 3 | Export-Clixml -Path $testfile $results = Import-Clixml $testfile - $results.Count | Should BeGreaterThan 0 - $results[0].ToString() | Should Match "System.Diagnostics.Process" + $results.Count | Should BeExactly 3 + $results[0].PSTypeNames[0] | Should Be "Deserialized.System.Management.Automation.CmdletInfo" } - It "Import with Rehydration should work" -Skip:$IsOSX{ + It "Import with Rehydration should work" { $property1 = 256 $property2 = "abcdef" $isHiddenTestType = [IsHiddenTestType]::New($property1,$property2) @@ -49,4 +49,112 @@ Describe "XmlCommand DRT basic functionality Tests" -Tags "CI" { $results.Property1 | Should Be $property1 $results.Property2 | Should Be $property2 } + + It "Export-Clixml StopProcessing should succeed" { + $ps = [PowerShell]::Create() + $null = $ps.AddScript("1..10") + $null = $ps.AddCommand("foreach-object") + $null = $ps.AddParameter("Process", { $_; start-sleep 1 }) + $null = $ps.AddCommand("Export-CliXml") + $null = $ps.AddParameter("Path", $testfile) + $null = $ps.BeginInvoke() + Start-Sleep 1 + $null = $ps.Stop() + $ps.InvocationStateInfo.State | should be "Stopped" + $ps.Dispose() + } + + It "Import-Clixml StopProcessing should succeed" { + 1,2,3 | Export-Clixml -Path $testfile + $ps = [PowerShell]::Create() + $ps.AddCommand("Get-Process") + $ps.AddCommand("Import-CliXml") + $ps.AddParameter("Path", $testfile) + $ps.BeginInvoke() + $ps.Stop() + $ps.InvocationStateInfo.State | Should Be "Stopped" + } + + It "Export-Clixml using -Depth should work" { + class Three + { + [int] $num = 3; + } + + class Two + { + [Three] $three = [Three]::New(); + [int] $value = 2; + } + + class One + { + [Two] $two = [Two]::New(); + [int] $value = 1; + } + + $one = [One]::New() + $one | Export-Clixml -Depth 2 -Path $testfile + $deserialized_one = Import-Clixml -Path $testfile + $deserialized_one.Value | Should Be 1 + $deserialized_one.two.Value | Should Be 2 + $deserialized_one.two.Three | Should Not BeNullOrEmpty + $deserialized_one.two.three.num | Should BeNullOrEmpty + } + + It "Import-Clixml should work with XML serialization from powershell.exe" { + # need to create separate process so that current powershell doesn't interpret clixml output + Start-Process -FilePath $pshome\powershell -RedirectStandardOutput $testfile -Args "-noprofile -nologo -outputformat xml -command get-command import-clixml" -Wait + $out = Import-Clixml -Path $testfile + $out.Name | Should Be "Import-CliXml" + $out.CommandType.ToString() | Should Be "Cmdlet" + $out.Source | Should Be "Microsoft.PowerShell.Utility" + } + + It "Import-Clixml -IncludeTotalCount always returns unknown total count" { + # this cmdlets supports paging, but not this switch + [PSCustomObject]@{foo=1;bar=@{hello="world"}} | Export-Clixml -Path $testfile + $out = Import-Clixml -Path $testfile -IncludeTotalCount + $out[0].ToString() | Should BeExactly "Unknown total count" + } + + It "Import-Clixml -First and -Skip work together for simple types" { + "one","two","three","four" | Export-Clixml -Path $testfile + $out = Import-Clixml -Path $testfile -First 2 -Skip 1 + $out.Count | Should Be 2 + $out[0] | Should BeExactly "two" + $out[1] | Should BeExactly "three" + } + + It "Import-Clixml -First and -Skip work together for collections" { + @{a=1;b=2;c=3;d=4} | Export-Clixml -Path $testfile + # order not guaranteed, even with [ordered] so we have to be smart here and compare against the full result + $out1 = Import-Clixml -Path $testfile # this results in a hashtable + $out2 = Import-Clixml -Path $testfile -First 2 -Skip 1 # this results in a dictionary entry + $out2.Count | Should Be 2 + ($out2.Name) -join ":" | should be (@($out1.Keys)[1, 2] -join ":") + ($out2.Value) -join ":" | should be (@($out1.Values)[1, 2] -join ":") + } + + # these tests just cover aspects that aren't normally exercised being used as a cmdlet + It "Can read back switch and parameter values using api" { + Add-Type -AssemblyName "${pshome}/Microsoft.PowerShell.Commands.Utility.dll" + + $cmd = [Microsoft.PowerShell.Commands.ExportClixmlCommand]::new() + $cmd.LiteralPath = "foo" + $cmd.LiteralPath | Should BeExactly "foo" + $cmd.NoClobber = $true + $cmd.NoClobber | Should Be $true + + $cmd = [Microsoft.PowerShell.Commands.ImportClixmlCommand]::new() + $cmd.LiteralPath = "bar" + $cmd.LiteralPath | Should BeExactly "bar" + + $cmd = [Microsoft.PowerShell.Commands.SelectXmlCommand]::new() + $cmd.LiteralPath = "foo" + $cmd.LiteralPath | Should BeExactly "foo" + $xml = [xml]"" + $cmd.Xml = $xml + $cmd.Xml | Should Be $xml + } } diff --git a/test/powershell/Modules/Microsoft.PowerShell.Utility/xml.tests.ps1 b/test/powershell/Modules/Microsoft.PowerShell.Utility/xml.tests.ps1 index 8555ae472..f0a0e1e6a 100644 --- a/test/powershell/Modules/Microsoft.PowerShell.Utility/xml.tests.ps1 +++ b/test/powershell/Modules/Microsoft.PowerShell.Utility/xml.tests.ps1 @@ -50,19 +50,53 @@ It $_.testName { @(Select-XML @params).Count | Should Be 1 + (Select-XML @params).Path | Should Be $fileName.FullName } } - It "literalpath with non filesystem path" { + It "non filesystem path using " -TestCases @( + @{parameter="literalPath"}, + @{parameter="path"} + ) { + param($parameter) $__data = "abcdefg" - Select-XML -literalPath variable:__data "Root" -ErrorVariable selectXmlError -ErrorAction SilentlyContinue - $selectXmlError.FullyQualifiedErrorId | Should Be 'ProcessingFile,Microsoft.PowerShell.Commands.SelectXmlCommand' + $params = @{$parameter="variable:__data"} + { Select-XML @params "Root" | Should BeErrorId 'ProcessingFile,Microsoft.PowerShell.Commands.SelectXmlCommand' } } - It "path with non filesystem path" { - $__data = "abcdefg" - Select-XML -Path variable:\__data "Root" -ErrorVariable selectXmlError -ErrorAction SilentlyContinue - $selectXmlError.FullyQualifiedErrorId | Should Be 'ProcessingFile,Microsoft.PowerShell.Commands.SelectXmlCommand' + It "Invalid xml file" { + $testfile = "$testdrive/test.xml" + Set-Content -Path $testfile -Value "" + { Select-Xml -Path $testfile -XPath foo | ShouldBeErrorId 'ProcessingFile,Microsoft.PowerShell.Commands.SelectXmlCommand' } + } + + It "-xml works with inputstream" { + [xml]$xml = "helloworld" + $node = Select-Xml -Xml $xml -XPath "//c:b" -Namespace @{c='bar'} + $node.Path | Should BeExactly "InputStream" + $node.Pattern = "//c:b" + $node.ToString() | Should BeExactly "hello" + } + + It "Returns error for invalid xmlnamespace" { + [xml]$xml = "helloworld" + { Select-Xml -Xml $xml -XPath foo -Namespace @{c=$null} | ShouldBeErrorId "PrefixError,Microsoft.PowerShell.Commands.SelectXmlCommand" } + } + + It "Returns error for invalid content" { + { Select-Xml -Content "hello" -XPath foo | ShouldBeErrorId "InvalidCastToXmlDocument,Microsoft.PowerShell.Commands.SelectXmlCommand" } + } + + It "ToString() works correctly on nested node" { + $node = Select-Xml -Content "onehello" -XPath "//b" + $node.ToString() | Should BeExactly "onehello" + } + + It "ToString() works correctly with file" { + $testfile = Join-Path "$testdrive" "test.xml" + Set-Content -Path $testfile -Value "hello" + $node = Select-Xml -Path $testfile -XPath "//b" + $node.ToString() | Should BeExactly "hello:$testfile" } } }