Overview
The vast majority of the PowerShell projects I undertake are in some partial stage of development. Most projects lack a standardized approach to logging and lack a standardized approach to testing. Rather than writing logging and testing infrastructure for each project, this post contains basic but comprensive implementation of logging and testing including modules (psm1 files) and corresponding test scripts (ps1 files)
- QEDLogging.psm1: basic logging to file/console using standard formatting (tsv/cvs, Json, Xml)
- QEDLoggingTest.ps1: unit tests for QEDLogging.psm1 that use the unit test framework provided by PsUnit.psm1
- PsUnit.psm1: practical but basic unit test framework.
- PsUnitTest.ps1: test code for PsUnit.psm1
Source Code
QEDLogging.psm1
Import-Module '.\PsUnit.psm1'
Set-StrictMode -Version 3.0
enum LogLevel {
Error
Warn
Info
Verbose
}
enum LogItemIndex {
Date
LogLevel
Filename
LineNumber
Tags
Message
}
class QEDLogCommon {
hidden static [string] $tagException
hidden static [char] $delimeterElement
hidden static [char] $delimeterTag
static QEDLogEntry() {
[QEDLogEntry]::delimeterElement = "`t"
[QEDLogEntry]::delimeterTag = ','
[QEDLogEntry]::tagException = 'Exception'
}
static [string] GetTagException() {
return [QEDLogCommon]::tagException
}
static [char] GetDelimeterElement() {
return [QEDLogCommon]::delimeterElement
}
static [char] GetDelimeterTag() {
return [QEDLogCommon]::delimeterTag
}
}
class ErrorRecordExtension {
hidden static [char] $messageSeparator
hidden [System.Management.Automation.ErrorRecord] $errorRecord
hidden [int] $exceptionHandledAtLineNumber
hidden [string] $exceptionHandledAtFilename
static ErrorRecordExtension() {
[ErrorRecordExtension]::messageSeparator = ':'
}
ErrorRecordExtension() {
}
ErrorRecordExtension(
[System.Management.Automation.ErrorRecord] $errorRecord,
[int] $exceptionHandledAtLineNumber,
[string] $exceptionHandledAtFilename) {
$this.exceptionHandledAtLineNumber =
$exceptionHandledAtLineNumber
$this.exceptionHandledAtFilename =
$exceptionHandledAtFilename
$this.errorRecord = $errorRecord
}
static [char] GetMessageSeparator() {
return [ErrorRecordExtension]::messageSeparator
}
static [char] SetMessageSeparator([char] $messageSeparator) {
[char] $originalValue = [ErrorRecordExtension]::messageSeparator
[ErrorRecordExtension]::messageSeparator = $messageSeparator
return $originalValue
}
hidden static [string] Flatten([string] $text) {
return $text.Replace("`n", '').Replace("`r", '')
}
hidden static [string] GetFilename([string] $fullPath) {
if ([string]::IsNullOrEmpty($fullPath)) {
return ''
}
return Split-Path $fullPath -Leaf
}
[int] GetHandledAtLineNumber() {
return $this.exceptionHandledAtLineNumber
}
[string] GetHandledAtScriptName() {
return [ErrorRecordExtension]::GetFilename(
$this.exceptionHandledAtFilename)
}
[string] GetInvocationLine() {
return [ErrorRecordExtension]::Flatten(
$this.errorRecord.InvocationInfo.Line)
}
[int] GetInvocationScriptLineNumber() {
return $this.errorRecord.InvocationInfo.ScriptLineNumber
}
[string] GetInvocationScriptName() {
return [ErrorRecordExtension]::GetFilename(
$this.errorRecord.InvocationInfo.ScriptName)
}
[string] GetInvocationPosition() {
return [ErrorRecordExtension]::Flatten(
$this.errorRecord.InvocationInfo.PositionMessage).
Replace('~', '').Replace('+', '')
}
hidden static [string] GetStackTrace([Exception] $exception) {
return [ErrorRecordExtension]::Flatten(
$exception.StackTrace) -replace '\s+', ' '
}
hidden static [string] GetException([Exception] $exception) {
[string] $message =
"$($exception.Message)$([ErrorRecordExtension]::messageSeparator)" +
"$($exception.TargetSite)$([ErrorRecordExtension]::messageSeparator)" +
"$([ErrorRecordExtension]::GetStackTrace($exception))"
if ($null -ne $exception.InnerException) {
$message +=
[ErrorRecordExtension]::messageSeparator +
[ErrorRecordExtension]::GetException(
$exception.InnerException)
}
return $message
}
[string] GetException() {
return [ErrorRecordExtension]::GetException(
$this.errorRecord.Exception)
}
[System.Management.Automation.ErrorRecord] GetErrorRecord() {
return $this.errorRecord
}
}
class QEDLogEntry {
hidden [DateTime] $dateTime
hidden [LogLevel] $logLevel
hidden [string] $filename
hidden [int] $lineNumber
hidden [System.Collections.ObjectModel.ReadOnlyCollection[string]] $tags
hidden [string] $message
QEDLogEntry(
[LogLevel] $logLevel,
[string] $filename,
[int] $lineNumber,
[string] $message) {
$this.Assign(
[QEDLogEntry]::GetUtcNow(),
$logLevel,
$filename,
$lineNumber,
$null,
$message)
}
QEDLogEntry(
[LogLevel] $logLevel,
[string] $filename,
[int] $lineNumber,
[string[]] $tags,
[string] $message) {
$this.Assign(
[QEDLogEntry]::GetUtcNow(),
$logLevel,
$filename,
$lineNumber,
$tags,
$message)
}
QEDLogEntry([string] $logLine) {
[string[]] $items =
$logLine -split [QEDLogCommon]::GetDelimeterElement()
$this.Assign(
([DateTime]$items[[LogItemIndex]::Date]).ToUniversalTime(),
[LogLevel]$items[[LogItemIndex]::LogLevel],
$items[[LogItemIndex]::Filename],
$items[[LogItemIndex]::LineNumber],
$items[[LogItemIndex]::Tags] -split [QEDLogCommon]::GetDelimeterTag(),
$items[[LogItemIndex]::Message])
}
static [QEDLogEntry[]] Create(
[LogLevel] $logLevel,
[System.Management.Automation.ErrorRecord] $errorRecord,
[int] $exceptionHandledAtLineNumber,
[string] $exceptionHandledAtFilename) {
return [QEDLogEntry]::Create(
$logLevel,
$errorRecord,
$exceptionHandledAtFilename,
$exceptionHandledAtLineNumber,
[string[]]@())
}
static [QEDLogEntry[]] Create(
[LogLevel] $logLevel,
[System.Management.Automation.ErrorRecord] $errorRecord,
[string] $exceptionHandledAtFilename,
[int] $exceptionHandledAtLineNumber,
[string[]] $tags) {
[string[]] $localTags = $tags + [QEDLogCommon]::GetTagException()
[DateTime] $utcNow = [QEDLogEntry]::GetUtcNow()
[System.Collections.ObjectModel.ReadOnlyCollection[string]] $readOnlyTags =
[System.Collections.ObjectModel.ReadOnlyCollection[string]]::new($localTags.Clone())
[ErrorRecordExtension] $errorRecordExtension =
[ErrorRecordExtension]::new($errorRecord, $exceptionHandledAtLineNumber, $exceptionHandledAtFilename)
[QEDLogEntry] $exceptionLogEntry = [QEDLogEntry]::new(
$utcNow,
$logLevel,
$errorRecordExtension.GetHandledAtScriptName(),
$errorRecordExtension.GetHandledAtLineNumber(),
$readOnlyTags,
$errorRecordExtension.GetException())
[QEDLogEntry] $invocationLocationLogEntry = [QEDLogEntry]::new(
$utcNow,
$logLevel,
$errorRecordExtension.GetInvocationScriptName(),
$errorRecordExtension.GetHandledAtLineNumber(),
$readOnlyTags,
$errorRecordExtension.GetInvocationPosition())
return @($exceptionLogEntry, $invocationLocationLogEntry)
}
hidden [void] Assign(
[DateTime] $date,
[LogLevel] $logLevel,
[string] $filename,
[int] $lineNumber,
[System.Collections.ObjectModel.ReadOnlyCollection[string]] $tags,
[string] $message) {
$this.dateTime = $date
$this.logLevel = $logLevel
$this.lineNumber = $lineNumber
$this.filename = $filename
$this.message = $message
if ($null -eq $tags) {
$this.tags = [System.Collections.ObjectModel.ReadOnlyCollection[string]]::new([string[]]@())
}
else {
$this.tags = $tags
}
}
static [DateTime] GetUtcNow() {
return (Get-Date).ToUniversalTime()
}
[DateTime] GetDateTime() {
return $this.dateTime
}
[string] GetDateTimeText() {
return $this.dateTime.ToString('o')
}
[LogLevel] GetLogLevel() {
return $this.logLevel
}
[string] GetFilename() {
return $this.filename
}
[string] GetLineNumber() {
return $this.lineNumber
}
[System.Collections.ObjectModel.ReadOnlyCollection[string]] GetTags() {
return $this.tags
}
[string] GetMessage() {
return $this.message
}
[bool] IsError() {
return [LogLevel]::Error -eq $this.logLevel
}
[bool] IsWarn() {
return [LogLevel]::Warn -eq $this.logLevel
}
[bool] IsInfo() {
return [LogLevel]::Info -eq $this.logLevel
}
[bool] IsVerbose() {
return [LogLevel]::Verbose -eq $this.logLevel
}
[bool] IsException() {
return $this.tags -contains [QEDLogCommon]::exceptionTagName
}
[bool] Contains([string] $tag) {
return $this.tags -contains $tag
}
hidden [string] GetTagText() {
if (0 -eq $this.tags.Count) {
return ''
}
else {
return $this.tags -join [QEDLogCommon]::GetDelimeterTag()
}
}
[string] ToString() {
return `
"$($this.GetDateTimeText())$([QEDLogCommon]::GetDelimeterElement())" +
"$($this.logLevel)$([QEDLogCommon]::GetDelimeterElement())" +
"$($this.filename)$([QEDLogCommon]::GetDelimeterElement())" +
"$($this.lineNumber)$([QEDLogCommon]::GetDelimeterElement())" +
"$($this.GetTagText())$([QEDLogCommon]::GetDelimeterElement())" +
"$($this.message)"
}
}
class QEDLog {
hidden static [bool] $filenamePrefix
hidden [bool] $consoleEnabled
hidden [LogLevel] $logLevelConsole
hidden [bool] $fileEnabled
hidden [LogLevel] $logLevelFile
hidden [string] $filename
static QEDLog() {
[QEDLog]::filenamePrefix = 'QED'
}
QEDLog(
[LogLevel] $logLevelConsole,
[LogLevel] $logLevelFile,
[string] $filename) {
$this.Assign(
$logLevelConsole,
$logLevelFile,
$filename)
}
QEDLog([LogLevel] $logLevelConsole, [LogLevel] $logLevelFile) {
$this.Assign(
$logLevelConsole,
$logLevelFile,
[QEDLog]::CreateFilename())
}
hidden [void] Assign(
[LogLevel] $logLevelConsole,
[LogLevel] $logLevelFile,
[string] $filename) {
$this.consoleEnabled = $true
$this.fileEnabled = $true
$this.logLevelConsole = $logLevelConsole
$this.logLevelFile = $logLevelFile
$this.filename = $filename
New-Item -Path $this.filename -ItemType File | Out-Null
Set-ItemProperty -Path $this.filename -Name IsReadOnly -Value $True
}
hidden static [string] CreaterRandomName() {
return (Get-Date).ToUniversalTime().ToString('yyyyMMddhhmmssfff')
}
hidden static [string] CreateFilename() {
return Join-Path `
-Path $($Env:Temp) `
-ChildPath "$([QEDLog]::filenamePrefix)$([QEDLog]::CreaterRandomName()).log"
}
[string] GetConsoleEnabled() {
return $this.consoleEnabled
}
[bool] SetConsoleEnabled([bool] $enabled) {
[bool] $originaValue = $this.consoleEnabled
$this.consoleEnabled = $enabled
return $originaValue
}
[string] GetFileEnabled() {
return $this.fileEnabled
}
[bool] SetFileEnabled([bool] $enabled) {
[bool] $originaValue = $this.fileEnabled
$this.fileEnabled = $enabled
return $originaValue
}
[string] GetFilename() {
return $this.filename
}
[LogLevel] GetLevelConsole() {
return $this.logLevelConsole
}
[LogLevel] SetLevelConole([LogLevel] $logLevel) {
[LogLevel] $originalValue = $this.logLevelConsole
$this.logLevelConsole = $logLevel
return $originalValue
}
[LogLevel] GetLevelFile() {
return $this.logLevelFile
}
[LogLevel] SetLevelFile([LogLevel] $logLevel) {
[LogLevel] $originalValue = $this.logLevelFile
$this.logLevelFile = $logLevel
return $originalValue
}
[void] Write([QEDLogEntry] $logEntry) {
if (!($this.fileEnabled -or $this.consoleEnabled)) {
return
}
[LogLevel] $logLevel = $logEntry.GetLogLevel()
if (($logLevel -lt $this.logLevelConsole) -and ($logLevel -lt $this.logLevelFile)) {
return
}
[string] $line = $logEntry.ToString()
if ($logLevel -le $this.logLevelConsole) {
Write-Host $line
}
if ($logLevel -le $this.logLevelFile) {
Add-Content -Path $this.filename -Value $line -Force
}
}
}
function Write-LogErrorRecord {
Param(
[Parameter(Mandatory=$true)]
[QEDLog] $log,
[Parameter(Mandatory=$true)]
[System.Management.Automation.ErrorRecord] $errorRecord,
[string[]] $tags = @()
)
[QEDLogEntry[]] $logEntries = [QEDLogEntry]::Create(
[LogLevel]::Error,
$errorRecord,
$($MyInvocation.ScriptName),
$($MyInvocation.ScriptLineNumber),
$tags)
foreach ($logEntry in $logEntries) {
$log.Write($logEntry)
}
}
function Write-LogError {
Param(
[Parameter(Mandatory=$true)]
[QEDLog] $log,
[Parameter(Mandatory=$true)]
[string] $message,
[string[]] $tags = @()
)
$log.Write(
[LogLevel]::Error,
$($MyInvocation.ScriptName),
$($MyInvocation.ScriptLineNumber),
$tags,
$message)
}
function Write-LogInfo {
Param(
[Parameter(Mandatory=$true)]
[QEDLog] $log,
[Parameter(Mandatory=$true)]
[string] $message,
[string[]] $tags = @()
)
$log.Write(
[LogLevel]::Info,
$($MyInvocation.ScriptName),
$($MyInvocation.ScriptLineNumber),
$tags,
$message)
}
function Write-LogWarn {
Param(
[Parameter(Mandatory=$true)]
[QEDLog] $log,
[Parameter(Mandatory=$true)]
[string] $message,
[string[]] $tags = @()
)
$log.Write(
[LogLevel]::Warn,
$($MyInvocation.ScriptName),
$($MyInvocation.ScriptLineNumber),
$tags,
$message)
}
QEDLoggingTest.ps1
function Test-LogEntry {
param
(
[Parameter(Mandatory=$true)]
[LogLevel] $logLevel,
[string[]] $tags,
[Parameter(Mandatory=$true)]
[string] $message
)
[QEDLogEntry] $logEntry = $null
[DateTime] $startDate = [QEDLogEntry]::GetUtcNow()
if ($null -eq $tags) {
$logEntry = [QEDLogEntry]::new(
$logLevel,
$MyInvocation.ScriptName,
$MyInvocation.ScriptLineNumber,
$message)
$tags = [string[]]@()
}
else {
$logEntry = [QEDLogEntry]::new(
$logLevel,
$MyInvocation.ScriptName,
$MyInvocation.ScriptLineNumber,
$tags,
$message)
}
[DateTime] $endDate = [QEDLogEntry]::GetUtcNow()
[string] $line = $logEntry.ToString()
[QEDLogEntry] $logEntryFromParse =
[QEDLogEntry]::new($line)
[string] $lineFromParse = $logEntryFromParse.ToString()
IsTrue $($startDate -lt $logEntry.GetDateTime())
IsTrue $($endDate -gt $logEntry.GetDateTime())
AreEqual $logLevel $logEntry.GetLogLevel()
AreEqualCollection $tags $logEntry.GetTags()
AreEqual $message $logEntry.GetMessage()
if ($lineFromParse -eq $line) {
return $logEntry
}
throw "Log entries differ '$lineFromParse' -ne '$line'"
}
function Write-LogEntry {
param
(
[Parameter(Mandatory=$true)]
[QEDLogEntry] $logEntry
)
if ($logEntry.GetLogLevel() -gt $globalLogLevel) {
return
}
Write-Host $logEntry
}
[LogLevel] $globalLogLevel = [LogLevel]::Warn
function Test-LogEntries {
[string[]] $tags = {'a', 'b', 'c'}
[QEDLogEntry] $logEntry = Test-LogEntry `
([LogLevel]::Error) $tags 'A message to log'
Write-LogEntry $logEntry
$logEntry = Test-LogEntry `
([LogLevel]::Error) $null 'A message to log'
Write-LogEntry $logEntry
$logEntry = Test-LogEntry `
([LogLevel]::Warn) $tags 'Any log message'
Write-LogEntry $logEntry
$logEntry = Test-LogEntry `
([LogLevel]::Warn) $null 'Any log message'
Write-LogEntry $logEntry
$logEntry = Test-LogEntry `
([LogLevel]::Info) $tags 'Another log message'
Write-LogEntry $logEntry
$logEntry = Test-LogEntry `
([LogLevel]::Info) $null 'Another log message'
Write-LogEntry $logEntry
}
function Test-Log {
Write-LogError
Write-LogErrorRecord
Write-LogInfo
Write-LogWarn
}
try {
Test-LogEntries
Test-Log
Write-Host 'Success'
}
catch {
$errorRecord = $_
Write-Host $errorRecord.Exception
Write-Host $errorRecord[0].InvocationInfo.PositionMessage
Write-Host 'Error'
}
PsUnit.psm1
Set-StrictMode -Version 3.0
class PsUnit {
hidden static [string] $filenameLineNumberSeparator
hidden static [string] $filenameLineNumberStart
hidden static [string] $filenameLineNumberEnd
hidden static [string] $boolMismatchMessage
static PsUnit() {
[PsUnit]::filenameLineNumberStart = '('
[PsUnit]::filenameLineNumberEnd = ')'
[PsUnit]::filenameLineNumberSeparator = ':'
[PsUnit]::boolMismatchMessage = 'Test expected value of '
}
hidden static [string] CreateMessage(
[string] $message,
[string] $filename,
[int] $lineNumber) {
return "$message " +
"$([PsUnit]::filenameLineNumberStart)$filename" +
"$([PsUnit]::filenameLineNumberSeparator)" +
"$lineNumber$([PsUnit]::filenameLineNumberEnd)"
}
hidden static [void] IsTrue(
[bool] $shouldBeTrue,
[string] $filename,
[int] $lineNumber) {
if ($shouldBeTrue) {
return
}
throw [PsUnit]::CreateMessage(
"$([PsUnit]::boolMismatchMessage) $true",
$filename,
$lineNumber)
}
hidden static [void] IsFalse(
[bool] $shouldBeFalse,
[string] $filename,
[int] $lineNumber) {
if (!($shouldBeFalse)) {
return
}
throw [PsUnit]::CreateMessage(
"$([PsUnit]::boolMismatchMessage) $false",
$filename,
$lineNumber)
}
hidden static [bool] AreEqualCoreValidations(
[object] $leftValue,
[object] $rightValue,
[string] $filename,
[int] $lineNumber) {
if (($null -eq $leftValue) -and ($null -eq $rightValue)) {
return $true
}
if ($null -eq $rightValue) {
throw [PsUnit]::CreateMessage(
"Test failed, assigned left value ($leftValue) " +
"cannot compared to right value of null",
$filename,
$lineNumber)
}
if ($null -eq $leftValue) {
throw [PsUnit]::CreateMessage(
"Test failed, assigned right value " +
"($rightValue) cannot compared " +
"to left value of null",
$filename,
$lineNumber)
}
if ($leftValue.GetType() -ne $rightValue.GetType()) {
throw [PsUnit]::CreateMessage(
"Test failed type differ " +
"$($leftValue.GetType()) -ne " +
"$($rightValue.GetType())",
$filename,
$lineNumber)
}
return $false
}
hidden static [void] AreEqual(
[double] $leftValue,
[double] $rightValue,
[double] $tolerance,
[string] $filename,
[int] $lineNumber) {
if ([PsUnit]::AreEqualCoreValidations(
$leftValue,
$rightValue,
$filename,
$lineNumber)) {
return
}
if ([System.Math]::Abs($leftValue - $rightValue) -le
$tolerance) {
return
}
throw [PsUnit]::CreateMessage(
"Test failed: difference in values " +
"($([System.Math]::Abs($leftValue - $rightValue)))"+
" exceeds tolerance ($tolerance)",
$filename,
$lineNumber)
}
hidden static [void] AreEqual(
[object] $leftValue,
[object] $rightValue,
[string] $filename,
[int] $lineNumber) {
if ([PsUnit]::AreEqualCoreValidations(
$leftValue,
$rightValue,
$filename,
$lineNumber)) {
return
}
if ($leftValue -eq $rightValue) {
return
}
throw [PsUnit]::CreateMessage(
"Test failed: values not equal " +
"$leftValue -ne $rightValue",
$filename,
$lineNumber)
}
hidden static [void] AreEqualCollection(
[System.Collections.ICollection] $leftValue,
[System.Collections.ICollection] $rightValue,
[string] $filename,
[int] $lineNumber) {
if ([PsUnit]::AreEqualCoreValidations(
$leftValue,
$rightValue,
$filename,
$lineNumber)) {
return
}
if ($leftValue.Count -ne $rightValue.Count) {
throw "Collections are different item counts " +
"($($leftValue.Count) -ne $($rightValue.Count))"
}
for ($i = 0; $i -lt $leftValue.Count; $i++) {
[PsUnit]::AreEqual(
$leftValue[$i],
$rightValue[$i],
$filename,
$lineNumber)
}
}
}
function IsTrue {
Param(
[Parameter(Mandatory=$true)]
[bool] $shouldBeTrue
)
[PsUnit]::IsTrue(
$shouldBeTrue,
$MyInvocation.ScriptName,
$MyInvocation.ScriptLineNumber)
}
function IsFalse {
Param(
[Parameter(Mandatory=$true)]
[bool] $shouldBeFalse
)
[PsUnit]::IsFalse(
$shouldBeFalse,
$MyInvocation.ScriptName,
$MyInvocation.ScriptLineNumber)
}
function AreEqual {
Param(
[object] $leftValue,
[object] $rightValue
)
[PsUnit]::AreEqual(
$leftValue,
$rightValue,
$MyInvocation.ScriptName,
$MyInvocation.ScriptLineNumber)
}
function AreEqualCollection {
Param(
[System.Collections.ICollection] $leftValue,
[System.Collections.ICollection] $rightValue
)
[PsUnit]::AreEqualCollection(
$leftValue,
$rightValue,
$MyInvocation.ScriptName,
$MyInvocation.ScriptLineNumber)
}
function AreEqualDouble {
Param(
[double] $leftValue,
[double] $rightValue,
[double] $tolerance
)
[PsUnit]::AreEqual(
$leftValue,
$rightValue,
$tolerance,
$MyInvocation.ScriptName,
$MyInvocation.ScriptLineNumber)
}
Export-ModuleMember -Function `
IsFalse, `
IsTrue, `
AreEqual, `
AreEqualCollection, `
AreEqualDouble
PsUnitTest.ps1
Import-Module '.\PsUnit.psm1'
function Test-AreEqualUnequal {
Param(
[object] $leftValue,
[object] $rightValue
)
try {
AreEqual $leftValue $rightValue
}
catch {
return
}
throw "Exception should have been thrown " +
"($($MyInvocation.ScriptName):" +
"$($MyInvocation.ScriptLineNumber))"
}
function Test-AreEqual {
try {
AreEqual $false $false
AreEqual $true $true
AreEqual $null $null
AreEqual 10 10
AreEqual 'xyZ' 'XYZ'
}
catch {
throw
}
Test-AreEqualUnequal 'xyZ' $null
Test-AreEqualUnequal $null 'xyZ'
Test-AreEqualUnequal 'qed' $null
Test-AreEqualUnequal $true $false
Test-AreEqualUnequal $false $true
Test-AreEqualUnequal 10 5
Test-AreEqualUnequal 'x' 5
}
function Test-AreEqualUnequalCollection {
Param(
[System.Collections.ICollection] $leftValue,
[System.Collections.ICollection] $rightValue
)
try {
AreEqualCollection $leftValue $rightValue
}
catch {
return
}
throw "Exception should have been thrown " +
"($($MyInvocation.ScriptName):" +
"$($MyInvocation.ScriptLineNumber))"
}
function Test-AreEqualCollection {
[string[]] $leftStrings = @()
[string[]] $rightStrings = @()
[int[]] $leftInts = @()
[int[]] $rightInts = @()
[bool[]] $leftBools = @()
[bool[]] $rightBools = @()
try {
AreEqualCollection $null $null
AreEqualCollection $leftStrings $rightStrings
$leftStrings = @('x', 'y', 'z')
$rightStrings = @('x', 'y', 'z')
AreEqualCollection $leftStrings $rightStrings
AreEqualCollection $leftInts $rightInts
$leftInts = @(1, 2, 3, 4)
$rightInts = @(1, 2, 3, 4)
AreEqualCollection $leftInts $rightInts
AreEqualCollection $leftBools $rightBools
$leftBools = @($true, $false, $true, $true)
$rightBools = @($true, $false, $true, $true)
AreEqualCollection $leftBools $rightBools
}
catch {
throw
}
Test-AreEqualUnequalCollection $null $rightStrings
Test-AreEqualUnequalCollection $null $rightInts
Test-AreEqualUnequalCollection $null $rightBools
Test-AreEqualUnequalCollection $leftStrings $null
Test-AreEqualUnequalCollection $leftInts $null
Test-AreEqualUnequalCollection $leftBools $null
Test-AreEqualUnequalCollection $leftStrings ([string[]]@())
Test-AreEqualUnequalCollection $leftInts ([int[]]@())
Test-AreEqualUnequalCollection $leftBools ([bool[]]@())
Test-AreEqualUnequalCollection ([string[]]@()) $rightStrings
Test-AreEqualUnequalCollection ([int[]]@()) $rightInts
Test-AreEqualUnequalCollection ([bool[]]@()) $rightBools
$leftStrings = @('x', 'y')
$leftInts = @(1, 2, 3)
$leftBools = @($true, $false, $true)
Test-AreEqualUnequalCollection $leftStrings $rightStrings
Test-AreEqualUnequalCollection $leftInts $rightInts
Test-AreEqualUnequalCollection $leftBools $rightBools
Test-AreEqualUnequalCollection $leftStrings $rightInts
Test-AreEqualUnequalCollection $leftStrings $rightBools
Test-AreEqualUnequalCollection $leftInts $rightStrings
Test-AreEqualUnequalCollection $leftInts $rightBools
}
function Test-AreEqualUnequalDouble {
Param(
[double] $leftValue,
[double] $rightValue,
[double] $tolerance
)
try {
AreEqualDouble $leftValue $rightValue $tolerance
}
catch {
return
}
throw "Exception should have been thrown " +
"($($MyInvocation.ScriptName):" +
"$($MyInvocation.ScriptLineNumber))"
}
function Test-AreEqualDouble {
[double] $value01 = 0.1
[double] $value02 = 0.11
[double] $tolerance = 0.015
try {
AreEqualDouble $value01 $value02 $tolerance
$value02 = 0.09
AreEqualDouble $value01 $value02 $tolerance
$value01 = 100.0
$value02 = 110.0
$tolerance = 10.001
AreEqualDouble $value01 $value02 $tolerance
$value01 = -100.0
$value02 = -110.0
$tolerance = 10.001
AreEqualDouble $value01 $value02 $tolerance
$value01 = 0.01
$value02 = 0.01
$tolerance = 0
AreEqualDouble $value01 $value02 $tolerance
}
catch {
throw
}
$value01 = 100.0
$value02 = 110.0
$tolerance = 9.999
Test-AreEqualUnequal $value01 $value02 $tolerance
$value01 = -100.0
$value02 = -110.0
Test-AreEqualUnequal $value01 $value02 $tolerance
$value01 = 0.01
$value02 = 0.0101
$tolerance = 00009
Test-AreEqualUnequal $value01 $value02 $tolerance
Test-AreEqualUnequal $value01 $value02 $tolerance
$value01 = -0.01
$value02 = -0.0101
Test-AreEqualUnequal $value01 $value02 $tolerance
}
function Test-IsFalse {
[bool] $expectedException = $false
try {
IsFalse $false
IsFalse $($null -eq '')
$expectedException = $true
IsFalse $true
}
catch {
if ($expectedException) {
return
}
throw
}
}
function Test-IsTrue {
[bool] $expectedException = $false
try {
IsTrue $true
IsTrue $('AbC' -eq 'aBC')
$expectedException = $true
IsTrue $false
}
catch {
if ($expectedException) {
return
}
throw
}
}
try {
Test-AreEqual
Test-AreEqualCollection
Test-AreEqualDouble
Test-IsFalse
Test-IsTrue
Write-Host 'Success'
}
catch {
$errorRecord = $_
Write-Host "Error: $errorRecord.Exception"
Write-Host 'Failure'
}
No comments :
Post a Comment