Skip to content
Original file line number Diff line number Diff line change
Expand Up @@ -109,6 +109,16 @@ protected override void BeginProcessing()
/// </summary>
protected override void ProcessRecord()
{
if (InternalTestHooks.ActivateSleepForStoppingTest != 0)
{
// We'll wait the verbose stream marker in tests and then call Stop().
// The cmdlet is very fast and likely to end before the test sees the marker
// so we have to slow down it by Sleep(50).
// We can not sleep for a long time because Stop() doesn't work on sleeping runspace.
WriteVerbose("ConvertTo-Json started");
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

what are you trying to achieve with the WriteVerbose? I'm not sure there's much value here and I would rather reduce the chatty output.

Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

The verbose message here is used to tell the test that the cmdlet has started so that it can issue the stop. Otherwise there's a window where stop is issued before the cmdlet under test has started.

Copy link
Collaborator Author

@iSazonov iSazonov Sep 26, 2018

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Yes, we need an event to start the stopping.
(We could use another test hook for the flag.)

System.Threading.Thread.Sleep(InternalTestHooks.ActivateSleepForStoppingTest);
}

if (InputObject != null)
{
_inputObjects.Add(InputObject);
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -131,6 +131,12 @@ protected override
void
ProcessRecord()
{
if (InternalTestHooks.ActivateSleepForStoppingTest != 0)
{
WriteVerbose("Export-Clixml started");
System.Threading.Thread.Sleep(InternalTestHooks.ActivateSleepForStoppingTest);
}

if (_serializer != null)
{
_serializer.Serialize(InputObject);
Expand Down Expand Up @@ -334,6 +340,12 @@ protected override void ProcessRecord()
{
foreach (string path in Path)
{
if (InternalTestHooks.ActivateSleepForStoppingTest != 0)
{
WriteVerbose("Import-Clixml started");
System.Threading.Thread.Sleep(InternalTestHooks.ActivateSleepForStoppingTest);
}

_helper = new ImportXmlHelper(path, this, _isLiteralPath);
_helper.Import();
}
Expand Down Expand Up @@ -423,6 +435,12 @@ protected override void BeginProcessing()
/// </summary>
protected override void ProcessRecord()
{
if (InternalTestHooks.ActivateSleepForStoppingTest != 0)
{
WriteVerbose("ConvertTo-Xml started");
System.Threading.Thread.Sleep(InternalTestHooks.ActivateSleepForStoppingTest);
}

if (As.Equals("Stream", StringComparison.OrdinalIgnoreCase))
{
CreateMemoryStream();
Expand Down
4 changes: 4 additions & 0 deletions src/System.Management.Automation/engine/Utils.cs
Original file line number Diff line number Diff line change
Expand Up @@ -1366,6 +1366,7 @@ namespace System.Management.Automation.Internal
{
/// <summary>This class is used for internal test purposes.</summary>
[SuppressMessage("Microsoft.MSInternal", "CA903:InternalNamespaceShouldNotContainPublicTypes", Justification = "Needed Internal use only")]
[SuppressMessage("StyleCop.CSharp.MaintainabilityRules", "SA1401:FieldsMustBePrivate", Justification = "Reviewed.")]
public static class InternalTestHooks
{
internal static bool BypassGroupPolicyCaching;
Expand Down Expand Up @@ -1395,6 +1396,9 @@ public static class InternalTestHooks

internal static bool ShowMarkdownOutputBypass;

// Slow down a cmdlet to predictably check the cmdlet stopping (that 'PowerShell.Stop()' or 'Ctrl-C' work).
internal static int ActivateSleepForStoppingTest;

/// <summary>This member is used for internal test purposes.</summary>
public static void SetTestHook(string property, object value)
{
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -22,22 +22,12 @@ Describe 'ConvertTo-Json' -tags "CI" {
}

It "StopProcessing should succeed" {
$ps = [PowerShell]::Create()
$null = $ps.AddScript({
$sb = {
$obj = [PSCustomObject]@{P1 = ''; P2 = ''; P3 = ''; P4 = ''; P5 = ''; P6 = ''}
$obj.P1 = $obj.P2 = $obj.P3 = $obj.P4 = $obj.P5 = $obj.P6 = $obj
1..100 | Foreach-Object { $obj } | ConvertTo-Json -Depth 10 -Verbose
# the conversion is expected to take some time, this throw is in case it doesn't
throw "Should not have thrown exception"
})
$null = $ps.BeginInvoke()
# wait for verbose message from ConvertTo-Json to ensure cmdlet is processing
Wait-UntilTrue { $ps.Streams.Verbose.Count -gt 0 }
$null = $ps.BeginStop($null, $null)
# wait a bit to ensure state has changed, not using synchronous Stop() to avoid blocking Pester
Start-Sleep -Milliseconds 100
$ps.InvocationStateInfo.State | Should -BeExactly "Stopped"
$ps.Dispose()
}
Test-Stopping $sb -IntervalInMilliseconds 50
}

It "The result string is packed in an array symbols when AsArray parameter is used." {
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -59,22 +59,16 @@ Describe "ConvertTo-Xml DRT Unit Tests" -Tags "CI" {
}

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 -BeExactly "Stopped"
Test-Stopping { Get-Process | ConvertTo-Xml -Depth 2 -Verbose } -IntervalInMilliseconds 50
}

# 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" {
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 -BeTrue
$cmd = [Microsoft.PowerShell.Commands.ConvertToXmlCommand]::new()
$cmd.NoTypeInformation = $true
$cmd.NoTypeInformation | Should -BeTrue
}

It "Serialize primitive type" {
Expand Down
238 changes: 112 additions & 126 deletions test/powershell/Modules/Microsoft.PowerShell.Utility/XMLCommand.Tests.ps1
Original file line number Diff line number Diff line change
Expand Up @@ -2,11 +2,11 @@
# Licensed under the MIT License.
Describe "XmlCommand DRT basic functionality Tests" -Tags "CI" {

BeforeAll {
if(-not ('IsHiddenTestType' -as "type"))
{
Add-Type -TypeDefinition @"
public class IsHiddenTestType
BeforeAll {
if(-not ('IsHiddenTestType' -as "type"))
{
Add-Type -TypeDefinition @"
public class IsHiddenTestType
{
public IsHiddenTestType()
{
Expand All @@ -24,139 +24,125 @@ Describe "XmlCommand DRT basic functionality Tests" -Tags "CI" {
public string Property2;
}
"@
}
}
}

BeforeEach {
$testfile = Join-Path -Path $TestDrive -ChildPath "clixml-directive.xml"
}
BeforeEach {
$testfile = Join-Path -Path $TestDrive -ChildPath "clixml-directive.xml"
}

AfterEach {
remove-item $testfile -Force -ErrorAction SilentlyContinue
remove-item $testfile -Force -ErrorAction SilentlyContinue
}

It "Import with CliXml directive should work" {
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 -BeExactly 3
$results = Import-Clixml $testfile
$results.Count | Should -BeExactly 3
$results[0].PSTypeNames[0] | Should -Be "Deserialized.System.Management.Automation.CmdletInfo"
}

It "Import with Rehydration should work" {
$property1 = 256
$property2 = "abcdef"
$isHiddenTestType = [IsHiddenTestType]::New($property1,$property2)
$isHiddenTestType | Export-Clixml $testfile
$results = Import-Clixml $testfile
$results.Property1 | Should -Be $property1
$results.Property2 | Should -Be $property2
It "Import with Rehydration should work" {
$property1 = 256
$property2 = "abcdef"
$isHiddenTestType = [IsHiddenTestType]::New($property1,$property2)
$isHiddenTestType | Export-Clixml $testfile
$results = Import-Clixml $testfile
$results.Property1 | Should -Be $property1
$results.Property2 | Should -Be $property2
}

It "Export-Clixml StopProcessing should succeed" {
$sb = { 1..20000 | Export-CliXml -Path $testfile -Verbose }
Test-Stopping $sb -IntervalInMilliseconds 50
}

It "Import-Clixml StopProcessing should succeed" {
1..20000 | Export-Clixml -Path $testfile
$sb = { Import-CliXml -Path $testfile,$testfile,$testfile,$testfile,$testfile -Verbose }
Test-Stopping $sb -IntervalInMilliseconds 20
}

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 pwsh.exe" {
# need to create separate process so that current powershell doesn't interpret clixml output
Start-Process -FilePath $pshome\pwsh -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
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 pwsh.exe" {
# need to create separate process so that current powershell doesn't interpret clixml output
Start-Process -FilePath $pshome\pwsh -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 -BeTrue

$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]"<a/>"
$cmd.Xml = $xml
$cmd.Xml | Should -Be $xml
}
}

# 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 -BeTrue

$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]"<a/>"
$cmd.Xml = $xml
$cmd.Xml | Should -Be $xml
}
}
1 change: 1 addition & 0 deletions test/tools/Modules/HelpersCommon/HelpersCommon.psd1
Original file line number Diff line number Diff line change
Expand Up @@ -30,6 +30,7 @@ FunctionsToExport = @(
'Test-IsVstsLinux'
'Test-IsVstsWindows'
'Test-TesthookIsSet'
'Test-Stopping'
'Wait-FileToBePresent'
'Wait-UntilTrue'
)
Expand Down
Loading