Start-Jojoba.ps1
<# .SYNOPSIS Parallel processing with test case output and Jenkins integration. .DESCRIPTION For a Jojoba template function this will be the main call in the process {} block. All processing should occur within here. .PARAMETER ScriptBlock The test to carry out. It must use $InputObject or $_. .INPUTS None. All inputs are taken from the calling function ($JojobaBatch, $JojobaJenkins, $JojobaThrottle). The calling function is also probed for $InputObject and the $_ pipeline. .OUTPUTS A test case object. #> function Start-Jojoba { [CmdletBinding()] param ( [Parameter(Mandatory = $true)] [ValidateNotNullOrEmpty()] [ScriptBlock] $ScriptBlock ) begin { } process { # Inherit the verbose setting across modules if it wasn't overridden $VerbosePreference = $PSCmdlet.GetVariableValue("VerbosePreference") # This is not specified, it's for internal use $JojobaModuleName = $PSCmdlet.GetVariableValue("MyInvocation").MyCommand.ModuleName $JojobaClassName = $PSCmdlet.GetVariableValue("MyInvocation").MyCommand.Name $JojobaName = $PSCmdlet.GetVariableValue("InputObject") # Inherit the Jojoba specific variables $JojobaBatch = $PSCmdlet.GetVariableValue("JojobaBatch") if (!($JojobaCallback = $PSCmdlet.GetVariableValue("JojobaCallback"))) { $JojobaCallback = "Write-JojobaCallback" } # Not used here, used in Publish-Jojoba # $JojobaJenkins = $PSCmdlet.GetVariableValue("JojobaJenkins") # Suite can be overridden, otherwise it's the module name, or just # "(Root)" (something is needed to represent it in jUnit). if (!($JojobaSuite = $PSCmdlet.GetVariableValue("JojobaSuite"))) { if (!($JojobaSuite = $jojobaModuleName)) { $JojobaSuite = "(Root)" } } $JojobaThrottle = $PSCmdlet.GetVariableValue("JojobaThrottle") # If it's not defined, or 0, it's a direct/single run if (!$JojobaThrottle) { #region Single-threaded run Write-Verbose "Starting inside thread for $JojobaName" # Fill out the base test case, named after parts of the original caller $jojoba = [PSCustomObject] @{ UserName = [System.Security.Principal.WindowsIdentity]::GetCurrent().Name Suite = $JojobaSuite Timestamp = Get-Date Time = 0 ClassName = $JojobaClassName Name = $JojobaName Result = "Pass" Message = New-Object Collections.ArrayList Data = New-Object Collections.ArrayList } try { &$ScriptBlock } catch { # Handle uncaught exceptions as a test block failure. This saves a lot of test code. Write-JojobaFail $_.ToString() Write-JojobaData (Resolve-Error $_ -AsString) } # Calculate other useful information for the test case for use by Jenkins $jojoba.Time = ((Get-Date) - $jojoba.Timestamp).TotalSeconds # If the calling function has a Write-Jojoba then send them a copy of the test. If this fails, # it also makes the test fail and which is at least output somewhere. if ($writeJojoba = Get-Command -Module $jojoba.Suite | Where-Object { $_.Name -eq $JojobaCallback }) { try { &$writeJojoba $jojoba } catch { Write-JojobaFail $_.ToString() Write-JojobaData (Resolve-Error $_ -AsString) } } # Write out the test case after removing ArrayList {} marks $jojoba.Message = $jojoba.Message -join [Environment]::NewLine $jojoba.Data = $jojoba.Data -join [Environment]::NewLine $jojoba #endregion } else { #region Parallel run # These are arguments which will be splatted for use by PoshRSJob $jobArguments = @{ Throttle = $JojobaThrottle Batch = $JojobaBatch ModulesToImport = $JojobaModuleName FunctionsToLoad = if (!$jojobaModuleName) { $JojobaClassName } else { $null } ScriptBlock = [scriptblock]::Create("`$_ | $($JojobaClassName) -JojobaThrottle 0") } # Add any extra switches and parameters to the scriptblock so they can be passed to the caller. # This can't handle complex objects - those should be piped in instead. $PSCmdlet.GetVariableValue("MyInvocation").BoundParameters.GetEnumerator() | ForEach-Object { if ($_.Key -ne "InputObject" -and $_.Key -ne "JojobaThrottle") { if ($_.Value -is [System.Management.Automation.SwitchParameter]) { $jobArguments.ScriptBlock = [scriptblock]::Create("$($jobArguments.ScriptBlock) -$($_.Key):`$$($_.Value)") } else { $jobArguments.ScriptBlock = [scriptblock]::Create("$($jobArguments.ScriptBlock) -$($_.Key) $($_.Value)") } } } Write-Verbose "Scheduling $($JojobaName) batch $($JojobaBatch) throttle $($jobArguments.Throttle) modules $($jobArguments.ModulesToImport) functions $($jobArguments.FunctionsToLoad) script $($jobArguments.ScriptBlock)" # Here we can continue to pipe in a complex object, or, revert back to the InputObject, for simplicity $null = @(if ($PSCmdlet.GetVariableValue("_") -and $PSCmdlet.GetVariableValue("_") -isnot [string]) { $PSCmdlet.GetVariableValue("_") } else { $PSCmdlet.GetVariableValue("InputObject") }) | Start-RSJob @jobArguments #endregion } } end { } } |