pf-assert.ps1

$Global:AssertEnabled = $true

function Assert ( $eq = $true, $throw, $extrainfo, [switch]$PassThru ) {
    
    function GetAssertInvokeFrame { 
        foreach ( $frame in Get-PSCallStack ) {
            if ( $frame.FunctionName -eq 'Assert' ) {
                return $frame
            }
        } 
    }

    function Get-FrameDisplayMessage ($frame) {
        return "$($frame.InvocationInfo.PositionMessage)`n" 
    }
    
    function Fail ( $message ){ 
        $frame = GetAssertInvokeFrame
        $message = $message + "`nAssert " + ( Get-FrameDisplayMessage $frame ) 
        if ($extrainfo) {
            $message = $extrainfo + "`n" + $message
        }
        throw "Assert Failed: $message"
    }
    
    function Check($condition, $eqItem) {
        if (-not $AssertEnabled ) {
            return
        }
        if ( $condition -is [Hashtable] ){
            [Hashtable]$remainder = Remove-HashTable $condition $eqItem
            $result = $remainder.Count -eq 0
            if (-not $result) {
                $message = "result='$condition'`nexpected='$eqItem'`ndifference='$remainder'`nposition='$position'"
                Fail $message
            }
            return
        }

        if ( $condition -is [scriptblock] ) {
            if ($throw) {
                try {
                    Disable-BreakPoint_StackTrace
                    $returned = Invoke-Command -ScriptBlock $condition
                    Enable-BreakPoint_StackTrace
                }
                catch {
                    $throwExpression = if ($throw -is [Type]) { $throw.Fullname } else { "*$throw*" }
                    $returned = ( $_.Exception.Message -like $throwExpression ) -or ( $_.Exception.GetType().Fullname -like $throwExpression )
                }
            }
            else {
                $returned = if ($eqItem -is [scriptblock]) {
                    $condition
                }
                else {
                    Invoke-Command -ScriptBlock $condition
                }
            }
        }
        else {
            $returned = $condition
        }
        $result = $returned -eq $eqItem 
        if (-not $result -and ( $null -ne $returned ) -and ( $null -ne $eqItem ) ) {
            $result = $returned.ToString() -eq $eqItem.ToString()
        }

        if (-not $result) {
            $message = "result='$returned' expected='$eqItem' position='$position'"
            Fail $message
        }
    }

    # Do not use the following as it wull consume the input therefore no allowing further checks
    # if ($eq -eq $input) { return }

    $eqArray = [Array]$eq

    if ( ( $eqArray -isnot [Array] ) -and ( $eqArray -eq $null ) ) {
        $eqArray = @( $null )
    }
    [int]$position = 0;
    foreach ( $value in $input ) {
        Check $value $eqArray[$position]
        if ($PassThru) {
            $value
        }
        $position++
    }

    if ($null -eq $eq -and $position -eq 0) { return }
    if ( $eqArray.Count -gt $position ) {
        Fail "Expected '$($eqArray.Count)' elements but just '$position' was received in the pipeline"
    }
}
function Assert:::Test {
    $null | assert -eq $null
    @() | assert -eq @()
    { assert -eq 2 } | assert -throw 'Assert Failed:'
    1 | assert -eq 1
    { 1 + 2 } | assert -eq 3
    { 1 | assert -eq 2 } | assert -throw 'failed' 
    2 | assert -eq 2 -PassThru | assert -eq 2 
    1,2 | assert -eq 1,2  
    { 1,2 | assert -eq 1,3  } | assert -throw 'failed' 
    { 1,2,3 | assert -eq 1,3  } | assert -throw 'failed' 
    $null, $true, $false, $null | assert $null, $true, $false, $null
    { 1 + 2 } | assert { 1 + 2 }
    @{'K1' = 1; 'K2' = 9} | assert @{'K1' = 1; 'K2' = 9}
    @{} | assert @{}
}