diff --git a/src/Microsoft.PowerShell.Commands.Utility/commands/utility/XmlCommands.cs b/src/Microsoft.PowerShell.Commands.Utility/commands/utility/XmlCommands.cs index 5b0af6473f4..1e27eae31b8 100644 --- a/src/Microsoft.PowerShell.Commands.Utility/commands/utility/XmlCommands.cs +++ b/src/Microsoft.PowerShell.Commands.Utility/commands/utility/XmlCommands.cs @@ -632,6 +632,86 @@ private void CleanUp() #endregion IDisposable Members } + /// + /// Implements ConvertTo-CliXml command. + /// + [Cmdlet(VerbsData.ConvertTo, "CliXml", HelpUri = "https://go.microsoft.com/fwlink/?LinkID=2280866")] + [OutputType(typeof(string))] + public sealed class ConvertToClixmlCommand : PSCmdlet + { + #region Parameters + + /// + /// Gets or sets input objects to be converted to CliXml object. + /// + [Parameter(Position = 0, Mandatory = true, ValueFromPipeline = true)] + public PSObject InputObject { get; set; } + + /// + /// Gets or sets depth of serialization. + /// + [Parameter] + [ValidateRange(1, int.MaxValue)] + public int Depth { get; set; } = 2; + + #endregion Parameters + + #region Private Members + + private readonly List _inputObjectBuffer = new(); + + #endregion Private Members + + #region Overrides + + /// + /// Process record. + /// + protected override void ProcessRecord() + { + _inputObjectBuffer.Add(InputObject); + } + + /// + /// End Processing. + /// + protected override void EndProcessing() + { + WriteObject(PSSerializer.Serialize(_inputObjectBuffer, Depth, enumerate: true)); + } + + #endregion Overrides + } + + /// + /// Implements ConvertFrom-CliXml command. + /// + [Cmdlet(VerbsData.ConvertFrom, "CliXml", HelpUri = "https://go.microsoft.com/fwlink/?LinkID=2280770")] + public sealed class ConvertFromClixmlCommand : PSCmdlet + { + #region Parameters + + /// + /// Gets or sets input object which is written in CliXml format. + /// + [Parameter(Position = 0, Mandatory = true, ValueFromPipeline = true)] + public string InputObject { get; set; } + + #endregion Parameters + + #region Overrides + + /// + /// Process record. + /// + protected override void ProcessRecord() + { + WriteObject(PSSerializer.Deserialize(InputObject)); + } + + #endregion Overrides + } + /// /// Helper class to import single XML file. /// diff --git a/src/Modules/Unix/Microsoft.PowerShell.Utility/Microsoft.PowerShell.Utility.psd1 b/src/Modules/Unix/Microsoft.PowerShell.Utility/Microsoft.PowerShell.Utility.psd1 index b17465abc5c..1d31d5889e8 100644 --- a/src/Modules/Unix/Microsoft.PowerShell.Utility/Microsoft.PowerShell.Utility.psd1 +++ b/src/Modules/Unix/Microsoft.PowerShell.Utility/Microsoft.PowerShell.Utility.psd1 @@ -25,7 +25,8 @@ CmdletsToExport = @( 'Get-TraceSource', 'Set-TraceSource', 'Add-Type', 'Get-TypeData', 'Remove-TypeData', 'Update-TypeData', 'Get-UICulture', 'Get-Unique', 'Get-Uptime', 'Clear-Variable', 'Get-Variable', 'New-Variable', 'Remove-Variable', 'Set-Variable', 'Get-Verb', 'Write-Verbose', 'Write-Warning', 'Invoke-WebRequest', - 'Format-Wide', 'ConvertTo-Xml', 'Select-Xml', 'Get-Error', 'Update-List', 'Unblock-File' + 'Format-Wide', 'ConvertTo-Xml', 'Select-Xml', 'Get-Error', 'Update-List', 'Unblock-File', 'ConvertTo-CliXml', + 'ConvertFrom-CliXml' ) FunctionsToExport = @() AliasesToExport = @('fhx') diff --git a/src/Modules/Windows/Microsoft.PowerShell.Utility/Microsoft.PowerShell.Utility.psd1 b/src/Modules/Windows/Microsoft.PowerShell.Utility/Microsoft.PowerShell.Utility.psd1 index 396ed51a8fb..33db09feb9c 100644 --- a/src/Modules/Windows/Microsoft.PowerShell.Utility/Microsoft.PowerShell.Utility.psd1 +++ b/src/Modules/Windows/Microsoft.PowerShell.Utility/Microsoft.PowerShell.Utility.psd1 @@ -24,7 +24,7 @@ CmdletsToExport = @( 'Add-Type', 'Get-TypeData', 'Remove-TypeData', 'Update-TypeData', 'Get-UICulture', 'Get-Unique', 'Get-Uptime', 'Clear-Variable', 'Get-Variable', 'New-Variable', 'Remove-Variable', 'Set-Variable', 'Get-Verb', 'Write-Verbose', 'Write-Warning', 'Invoke-WebRequest', 'Format-Wide', 'ConvertTo-Xml', 'Select-Xml', 'Get-Error', 'Update-List', - 'Out-GridView', 'Show-Command', 'Out-Printer' + 'Out-GridView', 'Show-Command', 'Out-Printer', 'ConvertTo-CliXml', 'ConvertFrom-CliXml' ) FunctionsToExport = @() AliasesToExport = @('fhx') diff --git a/src/System.Management.Automation/engine/serialization.cs b/src/System.Management.Automation/engine/serialization.cs index 8c05240ecf0..244e83d6af9 100644 --- a/src/System.Management.Automation/engine/serialization.cs +++ b/src/System.Management.Automation/engine/serialization.cs @@ -122,6 +122,45 @@ public static string Serialize(object source, int depth) return sb.ToString(); } + /// + /// Serializes list of objects into PowerShell CliXml. + /// + /// The input objects to serialize. + /// The depth of the members to serialize. + /// Enumerates input objects and serializes one at a time. + /// The serialized object, as CliXml. + internal static string Serialize(IList source, int depth, bool enumerate) + { + StringBuilder sb = new(); + + XmlWriterSettings xmlSettings = new() + { + CloseOutput = true, + Encoding = Encoding.Unicode, + Indent = true, + OmitXmlDeclaration = true + }; + + XmlWriter xw = XmlWriter.Create(sb, xmlSettings); + Serializer serializer = new(xw, depth, useDepthFromTypes: true); + + if (enumerate) + { + foreach (object item in source) + { + serializer.Serialize(item); + } + } + else + { + serializer.Serialize(source); + } + + serializer.Done(); + + return sb.ToString(); + } + /// /// Deserializes PowerShell CliXml into an object. /// diff --git a/test/powershell/Modules/Microsoft.PowerShell.Utility/XMLCommand.Tests.ps1 b/test/powershell/Modules/Microsoft.PowerShell.Utility/XMLCommand.Tests.ps1 index 53e4fbf8d7d..3b5b5276358 100644 --- a/test/powershell/Modules/Microsoft.PowerShell.Utility/XMLCommand.Tests.ps1 +++ b/test/powershell/Modules/Microsoft.PowerShell.Utility/XMLCommand.Tests.ps1 @@ -25,6 +25,29 @@ Describe "XmlCommand DRT basic functionality Tests" -Tags "CI" { } "@ } + + class Four + { + [int] $num = 4; + } + + class Three + { + [Four] $four = [Four]::New(); + [int] $num = 3; + } + + class Two + { + [Three] $three = [Three]::New(); + [int] $value = 2; + } + + class One + { + [Two] $two = [Two]::New(); + [int] $value = 1; + } } BeforeEach { @@ -259,23 +282,6 @@ Describe "XmlCommand DRT basic functionality Tests" -Tags "CI" { } 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 @@ -340,4 +346,251 @@ Describe "XmlCommand DRT basic functionality Tests" -Tags "CI" { $cmd.Xml = $xml $cmd.Xml | Should -Be $xml } + + Context "ConvertTo-CliXml & ConvertFrom-CliXml" { + + It "Getting cmdlet info should work" { + $content = $content = Get-Command ConvertTo-CliXml,ConvertFrom-CliXml | ConvertTo-Clixml + $results = ConvertFrom-CliXml $content + $results.Count | Should -Be 2 + $results[0].PSTypeNames[0] | Should -BeExactly "Deserialized.System.Management.Automation.CmdletInfo" + $results[1].PSTypeNames[0] | Should -BeExactly "Deserialized.System.Management.Automation.CmdletInfo" + } + + It "Rehydration should work" { + $property1 = 256 + $property2 = "abcdef" + $isHiddenTestType = [IsHiddenTestType]::New($property1,$property2) + $content = $isHiddenTestType | ConvertTo-CliXml + $results = ConvertFrom-CliXml $content + $results.Property1 | Should -Be $property1 + $results.Property2 | Should -BeExactly $property2 + } + + It "ConvertTo-CliXml StopProcessing should succeed" { + $ps = [PowerShell]::Create() + $null = $ps.AddScript("1..10") + $null = $ps.AddCommand("foreach-object") + $null = $ps.AddParameter("Process", { $_; Start-Sleep -Seconds 1 }) + $null = $ps.AddCommand("ConvertTo-CliXml") + + Wait-UntilTrue { $ps.BeginInvoke() } -IntervalInMilliseconds 1000 + $null = $ps.Stop() + $ps.InvocationStateInfo.State | Should -BeExactly "Stopped" + $ps.Dispose() + } + + It "ConvertFrom-CliXml StopProcessing should succeed" { + $content = 1,2,3 | ConvertTo-CliXml + $ps = [PowerShell]::Create() + $ps.AddCommand("Get-Process") + $ps.AddCommand("ConvertFrom-CliXml") + $ps.AddParameter("InputObject", $content) + $ps.BeginInvoke() + $ps.Stop() + $ps.InvocationStateInfo.State | Should -BeExactly "Stopped" + } + + It "Should serialize integers correctly using ValueFromPipeline" { + $testObject = 1,2,3 + $content = $testObject | ConvertTo-CliXml + $testObject | Export-CliXml -Path $testfile + (Get-Content -Path $testfile -Raw) | Should -Be $content + $out = ConvertFrom-CliXml -InputObject $content + $out -is [array] | Should -BeTrue + $out.Count | Should -Be 3 + $out[0] | Should -BeOfType [int] + $out[0] | Should -Be 1 + $out[1] | Should -BeOfType [int] + $out[1] | Should -Be 2 + $out[2] | Should -BeOfType [int] + $out[2] | Should -Be 3 + } + + It "Using default depth of 2 should work" { + $testObject = [One]::New() + $content = $testObject | ConvertTo-CliXml + $testObject | Export-Clixml -Path $testfile + (Get-Content -Path $testfile -Raw) | Should -Be $content + $deserialized_one = ConvertFrom-CliXml -InputObject $content + $deserialized_one.value | Should -Be 1 + $deserialized_one.two | Should -Not -BeNullOrEmpty + $deserialized_one.two.value | Should -Be 2 + $deserialized_one.two.three | Should -Not -BeNullOrEmpty + $deserialized_one.two.three.num | Should -BeNullOrEmpty + } + + It "Using -Depth 3 should work" { + $testObject = [One]::New() + $content = $testObject | ConvertTo-CliXml -Depth 3 + $testObject | Export-CliXml -Path $testfile -Depth 3 + (Get-Content -Path $testfile -Raw) | Should -Be $content + $deserialized_one = ConvertFrom-CliXml -InputObject $content + $deserialized_one.value | Should -Be 1 + $deserialized_one.two | Should -Not -BeNullOrEmpty + $deserialized_one.two.value | Should -Be 2 + $deserialized_one.two.three | Should -Not -BeNullOrEmpty + $deserialized_one.two.three.num | Should -Be 3 + $deserialized_one.two.three.four | Should -Not -BeNullOrEmpty + $deserialized_one.two.three.four.num | Should -BeNullOrEmpty + } + + It "Using -Depth 4 should work" { + $testObject = [One]::New() + $content = $testObject | ConvertTo-CliXml -Depth 4 + $testObject | Export-CliXml -Path $testfile -Depth 4 + (Get-Content -Path $testfile -Raw) | Should -Be $content + $deserialized_one = ConvertFrom-CliXml -InputObject $content + $deserialized_one.value | Should -Be 1 + $deserialized_one.two | Should -Not -BeNullOrEmpty + $deserialized_one.two.value | Should -Be 2 + $deserialized_one.two.three | Should -Not -BeNullOrEmpty + $deserialized_one.two.three.num | Should -Be 3 + $deserialized_one.two.three.four | Should -Not -BeNullOrEmpty + $deserialized_one.two.three.four.num | Should -Be 4 + } + + It "Using -Depth 2 cannot get value beyond depth" { + $testObject = [One]::New() + $content = $testObject | ConvertTo-CliXml -Depth 2 + $testObject | Export-CliXml -Path $testfile -Depth 2 + (Get-Content -Path $testfile -Raw) | Should -Be $content + $deserialized_one = ConvertFrom-CliXml -InputObject $content + $deserialized_one.value | Should -Be 1 + $deserialized_one.two | Should -Not -BeNullOrEmpty + $deserialized_one.two.value | Should -Be 2 + $deserialized_one.two.three | Should -Not -BeNullOrEmpty + $deserialized_one.two.three.num | Should -BeNullOrEmpty + } + + It "Should serialize array correctly using ValueFromPipeline" { + $testObject = @(1,2,3,4) + $content = $testObject | ConvertTo-CliXml + $testObject | Export-CliXml -Path $testfile + (Get-Content -Path $testfile -Raw) | Should -Be $content + $out = ConvertFrom-CliXml -InputObject $content + $out -is [array] | Should -BeTrue + $out.Count | Should -Be 4 + $out[0] | Should -BeOfType [int] + $out[0] | Should -Be 1 + $out[1] | Should -BeOfType [int] + $out[1] | Should -Be 2 + $out[2] | Should -BeOfType [int] + $out[2] | Should -Be 3 + $out[3] | Should -BeOfType [int] + $out[3] | Should -Be 4 + } + + It "Should serialize array correctly using -InputObject" { + $testObject = @(1,2,3,4) + $content = ConvertTo-CliXml -InputObject $testObject + Export-CliXml -Path $testfile -InputObject $testObject + (Get-Content -Path $testfile -Raw) | Should -Be $content + $out = ConvertFrom-CliXml -InputObject $content + $out -is [System.Collections.ArrayList] | Should -BeTrue + $out.Count | Should -Be 4 + $out[0] | Should -BeOfType [int] + $out[0] | Should -Be 1 + $out[1] | Should -BeOfType [int] + $out[1] | Should -Be 2 + $out[2] | Should -BeOfType [int] + $out[2] | Should -Be 3 + $out[3] | Should -BeOfType [int] + $out[3] | Should -Be 4 + } + + It "Should serialize hashtable correctly" { + $testObject = [ordered]@{ a = 1; b = 2; c = 3; d = 4 } + $content = $testObject | ConvertTo-CliXml + $testObject | Export-CliXml -Path $testfile + (Get-Content -Path $testfile -Raw) | Should -Be $content + $out = ConvertFrom-CliXml -InputObject $content + $out -is [ordered] | Should -BeTrue + $out.Count | Should -Be 4 + $out.Keys | Should -BeIn @('a', 'b', 'c', 'd') + $out.Values | Should -BeIn @(1, 2, 3, 4) + } + + It "Should serialize PSCustomObject correctly" { + $testObject = [PSCustomObject]@{ a = 1; b = 2; c = 3; d = 4 } + $content = $testObject | ConvertTo-CliXml + $testObject | Export-CliXml -Path $testfile + (Get-Content -Path $testfile -Raw) | Should -Be $content + $out = ConvertFrom-CliXml -InputObject $content + $out -is [pscustomobject] | Should -BeTrue + $out.a | Should -BeOfType [int] + $out.a | Should -Be 1 + $out.b | Should -BeOfType [int] + $out.b | Should -Be 2 + $out.c | Should -BeOfType [int] + $out.c | Should -Be 3 + $out.d | Should -BeOfType [int] + $out.d | Should -Be 4 + } + + It "Should serialize nested PSCustomObject correctly" { + $testObject = [PSCustomObject]@{ a = 1; b = 2; c = 3; d = [PSCustomObject]@{ e = 4 } } + $content = $testObject | ConvertTo-CliXml + $testObject | Export-CliXml -Path $testfile + (Get-Content -Path $testfile -Raw) | Should -Be $content + $out = ConvertFrom-CliXml -InputObject $content + $out -is [pscustomobject] | Should -BeTrue + $out.a | Should -BeOfType [int] + $out.a | Should -Be 1 + $out.b | Should -BeOfType [int] + $out.b | Should -Be 2 + $out.c | Should -BeOfType [int] + $out.c | Should -Be 3 + $out.d -is [pscustomobject] | Should -BeTrue + $out.d.e | Should -BeOfType [int] + $out.d.e | Should -Be 4 + } + + It "Should serialize array of PSCustomObjects correctly" { + $testObject = @( + [PSCustomObject]@{ Property = 1 } + [PSCustomObject]@{ Property = 2 } + [PSCustomObject]@{ Property = 3 } + ) + $content = $testObject | ConvertTo-CliXml + $testObject | Export-CliXml -Path $testfile + (Get-Content -Path $testfile -Raw) | Should -Be $content + $out = ConvertFrom-CliXml -InputObject $content + $out -is [array] | Should -BeTrue + $out.Count | Should -Be 3 + $out[0].Property | Should -BeOfType [int] + $out[0].Property | Should -Be 1 + $out[1].Property | Should -BeOfType [int] + $out[1].Property | Should -Be 2 + $out[2].Property | Should -BeOfType [int] + $out[2].Property | Should -Be 3 + } + + It "Should serialize array of single PSCustomObject when using ValueFromPipeline" { + $testObject = @( + [PSCustomObject]@{ Property = 1 } + ) + $content = $testObject | ConvertTo-CliXml + $testObject | Export-CliXml -Path $testfile + (Get-Content -Path $testfile -Raw) | Should -Be $content + $out = ConvertFrom-CliXml -InputObject $content + $out -is [pscustomobject] | Should -BeTrue + $out.Property | Should -BeOfType [int] + $out.Property | Should -Be 1 + } + + It "Should serialize array of single PSCustomObject when using -InputObject" { + $testObject = @( + [PSCustomObject]@{ Property = 1 } + ) + $content = ConvertTo-CliXml -InputObject $testObject + Export-CliXml -InputObject $testObject -Path $testfile + (Get-Content -Path $testfile -Raw) | Should -Be $content + $out = ConvertFrom-CliXml -InputObject $content + $out -is [System.Collections.ArrayList] | Should -BeTrue + $out.Count | Should -Be 1 + $out[0].Property | Should -BeOfType [int] + $out[0].Property | Should -Be 1 + } + } } diff --git a/test/powershell/Modules/Microsoft.PowerShell.Utility/clixml.tests.ps1 b/test/powershell/Modules/Microsoft.PowerShell.Utility/clixml.tests.ps1 index 46b04594f99..27d5cfb8359 100644 --- a/test/powershell/Modules/Microsoft.PowerShell.Utility/clixml.tests.ps1 +++ b/test/powershell/Modules/Microsoft.PowerShell.Utility/clixml.tests.ps1 @@ -1,5 +1,9 @@ # Copyright (c) Microsoft Corporation. # Licensed under the MIT License. + +[Diagnostics.CodeAnalysis.SuppressMessageAttribute('PSAvoidUsingConvertToSecureStringWithPlainText', '')] +param() + Describe "CliXml test" -Tags "CI" { BeforeAll { @@ -181,6 +185,97 @@ Describe "CliXml test" -Tags "CI" { $cred.Password | Should -BeOfType System.Security.SecureString } } + + Context "ConvertTo-CliXml"{ + BeforeAll { + $gpsList = Get-Process pwsh + $gps = $gpsList | Select-Object -First 1 + } + + It "Create by passing as parameter" { + $content = ConvertTo-CliXml -Depth 1 -InputObject ($gpsList | Select-Object -First 1) + $isExisted = $false + + foreach($item in $content) + { + foreach($gpsItem in $gpsList) + { + $checkId = $gpsItem.Id + if (($null -ne $(Select-String -InputObject $item -SimpleMatch $checkId)) -and ($null -ne $(Select-String -InputObject $item -SimpleMatch "Id"))) + { + $isExisted = $true + break; + } + } + } + + $isExisted | Should -BeTrue + } + + It "Create by passing as pipeline" { + $content = ($gpsList | Select-Object -First 1) | ConvertTo-CliXml -Depth 1 + + $isExisted = $false + + foreach($item in $content) + { + foreach($gpsItem in $gpsList) + { + $checkId = $gpsItem.Id + if (($null -ne $(Select-String -InputObject $item -SimpleMatch $checkId)) -and ($null -ne $(Select-String -InputObject $item -SimpleMatch "Id"))) + { + $isExisted = $true + break; + } + } + } + + $isExisted | Should -BeTrue + } + } + + Context "ConvertFrom-CliXml" { + BeforeAll { + $gpsList = Get-Process pwsh + $gps = $gpsList | Select-Object -First 1 + } + + It "Create by passing as parameter" { + $content = ConvertTo-CliXml -Depth 1 -InputObject $gps + + $content | Should -Not -Be $null + + $importedProcess = ConvertFrom-CliXml -InputObject $content + $importedProcess.ProcessName | Should -Not -BeNullOrEmpty + $gps.ProcessName | Should -Be $importedProcess.ProcessName + $importedProcess.Id | Should -Not -BeNullOrEmpty + $gps.Id | Should -Be $importedProcess.Id + } + + It "Create by passing as pipeline" { + $content = $gps | ConvertTo-CliXml -Depth 1 + + $content | Should -Not -Be $null + + $importedProcess = $content | ConvertFrom-CliXml + $importedProcess.ProcessName | Should -Not -BeNullOrEmpty + $gps.ProcessName | Should -Be $importedProcess.ProcessName + $importedProcess.Id | Should -Not -BeNullOrEmpty + $gps.Id | Should -Be $importedProcess.Id + } + + It "Should import PSCredential" { + $UserName = "Foo" + $pass = ConvertTo-SecureString (New-RandomHexString) -AsPlainText -Force + $cred = [PSCredential]::new($UserName, $pass) + + $content = $cred | ConvertTo-CliXml + $cred2 = ConvertFrom-CliXml -InputObject $content + $cred2.UserName | Should -BeExactly $cred.UserName + $cred2.Password | Should -BeOfType System.Security.SecureString + $cred2.GetNetworkCredential().Password | Should -BeExactly $cred.GetNetworkCredential().Password + } + } } ## diff --git a/test/powershell/engine/Basic/DefaultCommands.Tests.ps1 b/test/powershell/engine/Basic/DefaultCommands.Tests.ps1 index 6744bf0295c..312c34b6706 100644 --- a/test/powershell/engine/Basic/DefaultCommands.Tests.ps1 +++ b/test/powershell/engine/Basic/DefaultCommands.Tests.ps1 @@ -231,6 +231,7 @@ Describe "Verify aliases and cmdlets" -Tags "CI" { "Cmdlet", "Complete-Transaction", "", $($FullCLR ), "", "", "" "Cmdlet", "Connect-PSSession", "", $($FullCLR -or $CoreWindows ), "", "", "Medium" "Cmdlet", "Connect-WSMan", "", $($FullCLR -or $CoreWindows ), "", "", "None" +"Cmdlet", "ConvertFrom-CliXml", "", $($CoreWindows -or $CoreUnix), "", "", "None" "Cmdlet", "ConvertFrom-Csv", "", $($FullCLR -or $CoreWindows -or $CoreUnix), "", "", "None" "Cmdlet", "ConvertFrom-Json", "", $($FullCLR -or $CoreWindows -or $CoreUnix), "", "", "None" "Cmdlet", "ConvertFrom-Markdown", "", $( $CoreWindows -or $CoreUnix), "", "", "None" @@ -240,6 +241,7 @@ Describe "Verify aliases and cmdlets" -Tags "CI" { "Cmdlet", "ConvertFrom-StringData", "", $($FullCLR -or $CoreWindows -or $CoreUnix), "", "", "None" "Cmdlet", "Convert-Path", "", $($FullCLR -or $CoreWindows -or $CoreUnix), "", "", "None" "Cmdlet", "Convert-String", "", $($FullCLR ), "", "", "" +"Cmdlet", "ConvertTo-CliXml", "", $($CoreWindows -or $CoreUnix), "", "", "None" "Cmdlet", "ConvertTo-Csv", "", $($FullCLR -or $CoreWindows -or $CoreUnix), "", "", "None" "Cmdlet", "ConvertTo-Html", "", $($FullCLR -or $CoreWindows -or $CoreUnix), "", "", "None" "Cmdlet", "ConvertTo-Json", "", $($FullCLR -or $CoreWindows -or $CoreUnix), "", "", "None" diff --git a/test/xUnit/csharp/test_Serialization.cs b/test/xUnit/csharp/test_Serialization.cs new file mode 100644 index 00000000000..7ac520693b8 --- /dev/null +++ b/test/xUnit/csharp/test_Serialization.cs @@ -0,0 +1,50 @@ +// Copyright (c) Microsoft Corporation. +// Licensed under the MIT License. + +using System; +using System.Collections; +using System.Collections.Generic; +using System.Linq; +using System.Management.Automation; +using Xunit; + +namespace PSTests.Parallel +{ + public static class SerializationTests + { + [Fact] + public static void TestSerializerEnumerate() + { + var source = new List { 1, 2, 3 }; + var expected = $"{Environment.NewLine} 1{Environment.NewLine} 2{Environment.NewLine} 3{Environment.NewLine}"; + var serialized = PSSerializer.Serialize(source, depth: 2, enumerate: true); + Assert.Equal(expected, serialized); + var deserialized = PSSerializer.Deserialize(serialized); + Assert.IsType(deserialized); + var array = ((IEnumerable)deserialized).Cast().ToArray(); + Assert.Equal(3, array.Length); + Assert.Equal(1, array[0]); + Assert.Equal(2, array[1]); + Assert.Equal(3, array[2]); + } + + [Fact] + public static void TestSerializerWithoutEnumerate() + { + var listAssemblyDisplayName = System.Reflection.Assembly.GetAssembly(typeof(List)).FullName; + var source = new List { 1, 2, 3 }; + var expected = $"{Environment.NewLine} {Environment.NewLine} {Environment.NewLine} System.Collections.Generic.List`1[[System.Object, {listAssemblyDisplayName}]]{Environment.NewLine} System.Object{Environment.NewLine} {Environment.NewLine} {Environment.NewLine} 1{Environment.NewLine} 2{Environment.NewLine} 3{Environment.NewLine} {Environment.NewLine} {Environment.NewLine}"; + var serialized = PSSerializer.Serialize(source, depth: 2, enumerate: false); + Assert.Equal(expected, serialized); + var deserialized = PSSerializer.Deserialize(serialized); + Assert.IsType(deserialized); + var baseObject = PSObject.AsPSObject(deserialized).BaseObject; + Assert.IsType(baseObject); + var arrayList = (ArrayList)baseObject; + Assert.Equal(3, arrayList.Count); + Assert.Equal(1, arrayList[0]); + Assert.Equal(2, arrayList[1]); + Assert.Equal(3, arrayList[2]); + } + } +}