MegaBites-AI/Windows-powershell
0372
1# Copyright (c) Microsoft Corporation.2# Licensed under the MIT License.3 4# Helper function to wait for job to reach a running or completed state5# Job state can go to "Running" before the underlying runspace thread is running6# so we always first wait 100 mSec before checking state.7function Wait-ForJobRunning8{9 param (10 $job11 )12 13 $iteration = 1014 Do15 {16 Start-Sleep -Milliseconds 10017 }18 Until (($job.State -match "Running|Completed|Failed") -or (--$iteration -eq 0))19 20 if ($job.State -notmatch "Running|Completed|Failed")21 {22 throw ("Cannot start job '{0}'. Job state is '{1}'" -f $job,$job.State)23 }24}25 26Describe 'Basic ThreadJob Tests' -Tags 'CI' {27 28 BeforeAll {29 30 $scriptFilePath1 = Join-Path $testdrive "TestThreadJobFile1.ps1"31 @'32 for ($i=0; $i -lt 10; $i++)33 {34 Write-Output "Hello $i"35 }36'@ > $scriptFilePath137 38 $scriptFilePath2 = Join-Path $testdrive "TestThreadJobFile2.ps1"39 @'40 param ($arg1, $arg2)41 Write-Output $arg142 Write-Output $arg243'@ > $scriptFilePath244 45 $scriptFilePath3 = Join-Path $testdrive "TestThreadJobFile3.ps1"46 @'47 $input | foreach {48 Write-Output $_49 }50'@ > $scriptFilePath351 52 $scriptFilePath4 = Join-Path $testdrive "TestThreadJobFile4.ps1"53 @'54 Write-Output $using:Var155 Write-Output $($using:Array1)[2]56 Write-Output @(,$using:Array1)57'@ > $scriptFilePath458 59 $scriptFilePath5 = Join-Path $testdrive "TestThreadJobFile5.ps1"60 @'61 param ([string]$param1)62 Write-Output "$param1 $using:Var1 $using:Var2"63'@ > $scriptFilePath564 65 $WaitForCountFnScript = @'66 function Wait-ForExpectedRSCount67 {68 param (69 $expectedRSCount70 )71 72 $iteration = 2073 while ((@(Get-Runspace).Count -ne $expectedRSCount) -and ($iteration-- -gt 0))74 {75 Start-Sleep -Milliseconds 10076 }77 }78'@79 }80 81 AfterEach {82 Get-Job | Where-Object PSJobTypeName -EQ "ThreadJob" | Remove-Job -Force83 }84 85 It 'ThreadJob with ScriptBlock' {86 87 $job = Start-ThreadJob -ScriptBlock { "Hello" }88 $results = $job | Receive-Job -Wait89 $results | Should -Be "Hello"90 }91 92 It 'ThreadJob with ScriptBlock and Initialization script' {93 94 $job = Start-ThreadJob -ScriptBlock { "Goodbye" } -InitializationScript { "Hello" }95 $results = $job | Receive-Job -Wait96 $results[0] | Should -Be "Hello"97 $results[1] | Should -Be "Goodbye"98 }99 100 It 'ThreadJob with ScriptBlock and Argument list' {101 102 $job = Start-ThreadJob -ScriptBlock { param ($arg1, $arg2) $arg1; $arg2 } -ArgumentList @("Hello","Goodbye")103 $results = $job | Receive-Job -Wait104 $results[0] | Should -Be "Hello"105 $results[1] | Should -Be "Goodbye"106 }107 108 It 'ThreadJob with ScriptBlock and piped input' {109 110 $job = "Hello","Goodbye" | Start-ThreadJob -ScriptBlock { $input | ForEach-Object { $_ } }111 $results = $job | Receive-Job -Wait112 $results[0] | Should -Be "Hello"113 $results[1] | Should -Be "Goodbye"114 }115 116 It 'ThreadJob with ScriptBlock and Using variables' {117 118 $Var1 = "Hello"119 $Var2 = "Goodbye"120 $Var3 = 102121 $Var4 = 1..5122 $global:GVar1 = "GlobalVar"123 $job = Start-ThreadJob -ScriptBlock {124 Write-Output $using:Var1125 Write-Output $using:Var2126 Write-Output $using:Var3127 Write-Output ($using:Var4)[1]128 Write-Output @(,$using:Var4)129 Write-Output $using:GVar1130 }131 132 $results = $job | Receive-Job -Wait133 $results[0] | Should -Be $Var1134 $results[1] | Should -Be $Var2135 $results[2] | Should -Be $Var3136 $results[3] | Should -Be 2137 $results[4] | Should -Be $Var4138 $results[5] | Should -Be $global:GVar1139 }140 141 It 'ThreadJob with ScriptBlock and Using variables and argument list' {142 143 $Var1 = "Hello"144 $Var2 = 52145 $job = Start-ThreadJob -ScriptBlock {146 param ([string] $param1)147 148 "$using:Var1 $param1 $using:Var2"149 } -ArgumentList "There"150 151 $results = $job | Receive-Job -Wait152 $results | Should -Be "Hello There 52"153 }154 155 It 'ThreadJob with ScriptFile' {156 157 $job = Start-ThreadJob -FilePath $scriptFilePath1158 $results = $job | Receive-Job -Wait159 $results | Should -HaveCount 10160 $results[9] | Should -Be "Hello 9"161 }162 163 It 'ThreadJob with ScriptFile and Initialization script' {164 165 $job = Start-ThreadJob -FilePath $scriptFilePath1 -Initialization { "Goodbye" }166 $results = $job | Receive-Job -Wait167 $results | Should -HaveCount 11168 $results[0] | Should -Be "Goodbye"169 }170 171 It 'ThreadJob with ScriptFile and Argument list' {172 173 $job = Start-ThreadJob -FilePath $scriptFilePath2 -ArgumentList @("Hello","Goodbye")174 $results = $job | Receive-Job -Wait175 $results[0] | Should -Be "Hello"176 $results[1] | Should -Be "Goodbye"177 }178 179 It 'ThreadJob with ScriptFile and piped input' {180 181 $job = "Hello","Goodbye" | Start-ThreadJob -FilePath $scriptFilePath3182 $results = $job | Receive-Job -Wait183 $results[0] | Should -Be "Hello"184 $results[1] | Should -Be "Goodbye"185 }186 187 It 'ThreadJob with ScriptFile and Using variables' {188 189 $Var1 = "Hello!"190 $Array1 = 1..10191 192 $job = Start-ThreadJob -FilePath $scriptFilePath4193 $results = $job | Receive-Job -Wait194 $results[0] | Should -Be $Var1195 $results[1] | Should -Be 3196 $results[2] | Should -Be $Array1197 }198 199 It 'ThreadJob with ScriptFile and Using variables with argument list' {200 201 $Var1 = "There"202 $Var2 = 60203 $job = Start-ThreadJob -FilePath $scriptFilePath5 -ArgumentList "Hello"204 $results = $job | Receive-Job -Wait205 $results | Should -Be "Hello There 60"206 }207 208 It 'ThreadJob with terminating error' {209 210 $job = Start-ThreadJob -ScriptBlock { throw "MyError!" }211 $job | Wait-Job212 $job.JobStateInfo.Reason.Message | Should -Be "MyError!"213 }214 215 It 'ThreadJob and Error stream output' {216 217 $job = Start-ThreadJob -ScriptBlock { Write-Error "ErrorOut" } | Wait-Job218 $job.Error | Should -Be "ErrorOut"219 }220 221 It 'ThreadJob and Warning stream output' {222 223 $job = Start-ThreadJob -ScriptBlock { Write-Warning "WarningOut" } | Wait-Job224 $job.Warning | Should -Be "WarningOut"225 }226 227 It 'ThreadJob and Verbose stream output' {228 229 $job = Start-ThreadJob -ScriptBlock { $VerbosePreference = 'Continue'; Write-Verbose "VerboseOut" } | Wait-Job230 $job.Verbose | Should -Match "VerboseOut"231 }232 233 It 'ThreadJob and Verbose stream output' {234 235 $job = Start-ThreadJob -ScriptBlock { $DebugPreference = 'Continue'; Write-Debug "DebugOut" } | Wait-Job236 $job.Debug | Should -Be "DebugOut"237 }238 239 It 'ThreadJob ThrottleLimit and Queue' {240 241 try242 {243 # Start four thread jobs with ThrottleLimit set to two244 Get-Job | Where-Object PSJobTypeName -EQ "ThreadJob" | Remove-Job -Force245 $job1 = Start-ThreadJob -ScriptBlock { Start-Sleep -Seconds 60 } -ThrottleLimit 2246 $job2 = Start-ThreadJob -ScriptBlock { Start-Sleep -Seconds 60 }247 $job3 = Start-ThreadJob -ScriptBlock { Start-Sleep -Seconds 60 }248 $job4 = Start-ThreadJob -ScriptBlock { Start-Sleep -Seconds 60 }249 250 # Allow jobs to start251 Wait-ForJobRunning $job2252 253 Get-Job | Where-Object { ($_.PSJobTypeName -eq "ThreadJob") -and ($_.State -eq "Running") } | Should -HaveCount 2254 Get-Job | Where-Object { ($_.PSJobTypeName -eq "ThreadJob") -and ($_.State -eq "NotStarted") } | Should -HaveCount 2255 }256 finally257 {258 Get-Job | Where-Object PSJobTypeName -EQ "ThreadJob" | Remove-Job -Force259 }260 261 Get-Job | Where-Object PSJobTypeName -EQ "ThreadJob" | Should -HaveCount 0262 }263 264 It 'ThreadJob Runspaces should be cleaned up at completion' {265 266 $script = $WaitForCountFnScript + @'267 $WarningPreference = 'SilentlyContinue'268 try269 {270 Get-Job | Where-Object PSJobTypeName -eq "ThreadJob" | Remove-Job -Force271 $rsStartCount = @(Get-Runspace).Count272 273 # Start four thread jobs with ThrottleLimit set to two274 $Job1 = Start-ThreadJob -ScriptBlock { "Hello 1!" } -ThrottleLimit 5275 $job2 = Start-ThreadJob -ScriptBlock { "Hello 2!" }276 $job3 = Start-ThreadJob -ScriptBlock { "Hello 3!" }277 $job4 = Start-ThreadJob -ScriptBlock { "Hello 4!" }278 279 $null = $job1,$job2,$job3,$job4 | Wait-Job280 281 # Allow for runspace clean up to happen282 Wait-ForExpectedRSCount $rsStartCount283 284 Write-Output (@(Get-Runspace).Count -eq $rsStartCount)285 }286 finally287 {288 Get-Job | Where-Object PSJobTypeName -eq "ThreadJob" | Remove-Job -Force289 }290'@291 292 $result = & "$PSHOME/pwsh" -c $script293 $result | Should -BeExactly "True"294 }295 296 It 'ThreadJob Runspaces should be cleaned up after job removal' {297 298 $script = $WaitForCountFnScript + @'299 $WarningPreference = 'SilentlyContinue'300 try {301 Get-Job | Where-Object PSJobTypeName -eq "ThreadJob" | Remove-Job -Force302 $rsStartCount = @(Get-Runspace).Count303 304 # Start four thread jobs with ThrottleLimit set to two305 $Job1 = Start-ThreadJob -ScriptBlock { Start-Sleep -Seconds 60 } -ThrottleLimit 2306 $job2 = Start-ThreadJob -ScriptBlock { Start-Sleep -Seconds 60 }307 $job3 = Start-ThreadJob -ScriptBlock { Start-Sleep -Seconds 60 }308 $job4 = Start-ThreadJob -ScriptBlock { Start-Sleep -Seconds 60 }309 310 Wait-ForExpectedRSCount ($rsStartCount + 4)311 Write-Output (@(Get-Runspace).Count -eq ($rsStartCount + 4))312 313 # Stop two jobs314 $job1 | Remove-Job -Force315 $job3 | Remove-Job -Force316 317 Wait-ForExpectedRSCount ($rsStartCount + 2)318 Write-Output (@(Get-Runspace).Count -eq ($rsStartCount + 2))319 }320 finally321 {322 Get-Job | Where-Object PSJobTypeName -eq "ThreadJob" | Remove-Job -Force323 }324 325 Wait-ForExpectedRSCount $rsStartCount326 Write-Output (@(Get-Runspace).Count -eq $rsStartCount)327'@328 329 $result = & "$PSHOME/pwsh" -c $script330 $result | Should -BeExactly "True","True","True"331 }332 333 It 'ThreadJob jobs should work with Receive-Job -AutoRemoveJob' {334 335 Get-Job | Where-Object PSJobTypeName -EQ "ThreadJob" | Remove-Job -Force336 337 $job1 = Start-ThreadJob -ScriptBlock { 1..2 | ForEach-Object { Start-Sleep -Milliseconds 100; "Output $_" } } -ThrottleLimit 5338 $job2 = Start-ThreadJob -ScriptBlock { 1..2 | ForEach-Object { Start-Sleep -Milliseconds 100; "Output $_" } }339 $job3 = Start-ThreadJob -ScriptBlock { 1..2 | ForEach-Object { Start-Sleep -Milliseconds 100; "Output $_" } }340 $job4 = Start-ThreadJob -ScriptBlock { 1..2 | ForEach-Object { Start-Sleep -Milliseconds 100; "Output $_" } }341 342 $null = $job1,$job2,$job3,$job4 | Receive-Job -Wait -AutoRemoveJob343 344 Get-Job | Where-Object PSJobTypeName -EQ "ThreadJob" | Should -HaveCount 0345 }346 347 It 'ThreadJob jobs should run in FullLanguage mode by default' {348 349 $result = Start-ThreadJob -ScriptBlock { $ExecutionContext.SessionState.LanguageMode } | Wait-Job | Receive-Job350 $result | Should -Be "FullLanguage"351 }352}353 354Describe 'Job2 class API tests' -Tags 'CI' {355 356 AfterEach {357 Get-Job | Where-Object PSJobTypeName -EQ "ThreadJob" | Remove-Job -Force358 }359 360 It 'Verifies StopJob API' {361 362 $job = Start-ThreadJob -ScriptBlock { Start-Sleep -Seconds 60 } -ThrottleLimit 5363 Wait-ForJobRunning $job364 $job.StopJob($true, "No Reason")365 $job.JobStateInfo.State | Should -Be "Stopped"366 }367 368 It 'Verifies StopJobAsync API' {369 370 $job = Start-ThreadJob -ScriptBlock { Start-Sleep -Seconds 60 } -ThrottleLimit 5371 Wait-ForJobRunning $job372 $job.StopJobAsync($true, "No Reason")373 Wait-Job $job374 $job.JobStateInfo.State | Should -Be "Stopped"375 }376 377 It 'Verifies StartJobAsync API' {378 379 $jobRunning = Start-ThreadJob -ScriptBlock { Start-Sleep -Seconds 60 } -ThrottleLimit 1380 $jobNotRunning = Start-ThreadJob -ScriptBlock { Start-Sleep -Seconds 60 }381 382 $jobNotRunning.JobStateInfo.State | Should -Be "NotStarted"383 384 # StartJobAsync starts jobs synchronously for ThreadJob jobs385 $jobNotRunning.StartJobAsync()386 Wait-ForJobRunning $jobNotRunning387 $jobNotRunning.JobStateInfo.State | Should -Be "Running"388 }389 390 It 'Verifies JobSourceAdapter Get-Jobs' {391 392 $job = Start-ThreadJob -ScriptBlock { "Hello" } | Wait-Job393 394 $getJob = Get-Job -InstanceId $job.InstanceId 2> $null395 $getJob | Should -Be $job396 397 $getJob = Get-Job -Name $job.Name 2> $null398 $getJob | Should -Be $job399 400 $getJob = Get-Job -Command ' "hello" ' 2> $null401 $getJob | Should -Be $job402 403 $getJob = Get-Job -State $job.JobStateInfo.State 2> $null404 $getJob | Should -Be $job405 406 $getJob = Get-Job -Id $job.Id 2> $null407 $getJob | Should -Be $job408 409 # Get-Job -Filter is not supported410 $result = Get-Job -Filter @{Id = ($job.Id)} 3> $null411 $result | Should -BeNullOrEmpty412 }413 414 It 'Verifies terminating job error' {415 416 $job = Start-ThreadJob -ScriptBlock { throw "My Job Error!" } | Wait-Job417 $results = $job | Receive-Job 2>&1418 $results.ToString() | Should -Be "My Job Error!"419 }420}421 