' Gambas class file ''' Assertions which print TAP. Export Create Static Public Struct Subtest Printer As TapPrinter Description As String Indent As Integer Success As Boolean ' Directive to apply to the next "ok" line Directive As Integer Comment As String End Struct Property Output As Stream Property Read SubtestLevel As Integer Property Read Finished As Boolean Private $aActiveTests As Subtest[] Private $hCurrent As New Subtest Private $hOutput As Stream Public Sub _new() Reset() End Public Sub Reset() $aActiveTests = New Subtest[] $hOutput = File.Out With $hCurrent = New Subtest .Printer = New TapPrinter As "Printer" .Indent = 0 .Success = True .Directive = Tap.NONE End With End Public Sub Setup(Optional Tests As Integer, Optional Comment As String, Optional {Output} As Stream) If IsMissing({Output}) Then {Output} = $hOutput $hCurrent.Printer.Output = {Output} Plan(Tests, Comment) End Public Sub Subtest(Description As String, Optional Tests As Integer) Dim iIndent As Integer = $hCurrent.Indent Dim hOutput As Stream = $hCurrent.Printer.Output $aActiveTests.Push($hCurrent) With $hCurrent = New Subtest .Printer = New TapPrinter As "Printer" .Printer.Output = hOutput .Description = Description .Indent = iIndent + 1 .Success = True .Directive = Tap.NONE End With If Tests Then Plan(Tests) End Public Sub Finish() Dim hFinished As Subtest With $hCurrent .Printer.Finish() .Success = .Success And (.Printer.SkippedAll Or (.Printer.Planned > 0 And .Printer.Count = .Printer.Planned)) End With hFinished = $hCurrent Try $hCurrent = $aActiveTests.Pop() If Not Error Then $hCurrent.Printer.Test(hFinished.Success,, hFinished.Description) Endif End Public Sub Printer_Filter() Last.Line = String$($hCurrent.Indent, "\t") & Last.Line End Public Sub Plan(Tests As Integer, Optional Comment As String) $hCurrent.Printer.Plan(Tests, Comment) End Public Sub SkipAll(Optional Comment As String) $hCurrent.Printer.SkipAll(Comment) End Public Sub Ok(Result As Boolean, Optional Description As String) As Boolean With $hCurrent If .Directive <> Tap.NONE Then .Printer.Test(Result,, Description, .Directive, .Comment) Else .Printer.Test(Result,, Description) .Success = .Success And Result Endif .Directive = Tap.NONE .Comment = Null End With Return Result End Public Sub Todo(Optional Comment As String) $hCurrent.Directive = Tap.TODO $hCurrent.Comment = Comment End Public Sub Skip(Optional Comment As String) $hCurrent.Directive = Tap.SKIP $hCurrent.Comment = Comment Pass() End '' Unwinds the subtest stack. BailOut will always bail out of all the '' subtests, for example. Private Sub Unwind() While $hCurrent.Indent > 0 Note("Unwinding subtest stack!") Finish() Wend End Public Sub BailOut(Optional Comment As String) Unwind() $hCurrent.Printer.BailOut(Comment) $hCurrent.Success = False End Public Sub Diagnostic(Comment As String) $hCurrent.Printer.Diagnostic(Comment) End Public Sub Note(Comment As String) Diagnostic(Comment) End Public Sub Print({Line} As String) $hCurrent.Printer.Print({Line}) End Private Function Output_Read() As Stream Return $hOutput End Private Sub Output_Write(Value As Stream) $hOutput = Value $hCurrent.Printer.Output = Value End Private Function SubtestLevel_Read() As Integer Return $hCurrent.Indent End Private Function Finished_Read() As Boolean Return $hCurrent.Printer.Finished End ' -------------------- High-level test functions -------------------- Public Sub Pass(Optional Description As String) As Boolean Return Ok(True, Description) End Public Sub Fail(Optional Description As String) As Boolean Return Ok(False, Description) End Public Sub NotOk(Result As Boolean, Optional Description As String) As Boolean Return Ok(Not Result, Description) End Public Sub Equals(Got As Variant, Expected As Variant, Optional Description As String) As Boolean Dim bRes As Boolean bRes = Ok(Got = Expected, Description) If Not bRes Then Note(Subst$((" Got: &1"), Got)) Note(Subst$((" Expected: &1"), Expected)) Endif Return bRes End Public Sub NotEquals(Got As Variant, UnExpected As Variant, Optional Description As String) As Boolean Return Ok(Got <> UnExpected, Description) End Public Sub LessEqual(Got As Variant, Bound As Variant, Optional Description As String) As Boolean Dim bRes As Boolean bRes = Ok(Got <= Bound, Description) If Not bRes Then Note(Subst$((" Got: &1"), Got)) Note(Subst$((" Expected: <= &1"), Bound)) Endif Return bRes End Public Sub Less(Got As Variant, Bound As Variant, Optional Description As String) As Boolean Dim bRes As Boolean bRes = Ok(Got < Bound, Description) If Not bRes Then Note(Subst$((" Got: &1"), Got)) Note(Subst$((" Expected: < &1"), Bound)) Endif Return bRes End Public Sub GreaterEqual(Got As Variant, Bound As Variant, Optional Description As String) As Boolean Dim bRes As Boolean bRes = Ok(Got >= Bound, Description) If Not bRes Then Note(Subst$((" Got: &1"), Got)) Note(Subst$((" Expected: >= &1"), Bound)) Endif Return bRes End Public Sub Greater(Got As Variant, Bound As Variant, Optional Description As String) As Boolean Dim bRes As Boolean bRes = Ok(Got > Bound, Description) If Not bRes Then Note(Subst$((" Got: &1"), Got)) Note(Subst$((" Expected: > &1"), Bound)) Endif Return bRes End Public Sub Approximate(Got As Float, Expected As Float, Precision As Float, Optional Description As String) As Boolean Return LessEqual(Abs(Got - Expected), Precision, Description) End Public Sub RelativeApproximate(Got As Float, Expected As Float, RelPrecision As Float, Optional Description As String) As Boolean Return LessEqual(Abs((Got - Expected) / Expected), RelPrecision, Description) End Public Sub IsType(Got As Variant, Type As Integer, Optional Description As String) As Boolean Return Equals(TypeOf(Got), Type, Description) End Public Sub Null(Got As Variant, Optional Description As String) As Boolean Return Equals(Got, Null, Description) End Public Sub NotNull(Got As Variant, Optional Description As String) As Boolean Return NotEquals(Got, Null, Description) End Public Sub Like(Got As String, Pattern As String, Optional Description As String) As Boolean Dim bRes As Boolean bRes = Ok(Got Like Pattern, Description) If Not bRes Then Note(Subst$((" Got: &1"), Got)) Note(Subst$((" Expected: &1"), Pattern)) Endif Return bRes End Public Sub Match(Got As String, Pattern As String, Optional Description As String) As Boolean Dim bRes As Boolean bRes = Ok(Got Match Pattern, Description) If Not bRes Then Note(Subst$((" Got: &1"), Got)) Note(Subst$((" Expected: &1"), Pattern)) Endif Return bRes End Public Sub StringEquals(Got As String, Expected As String, Optional Description As String) As Boolean Dim bRes As Boolean Dim iPos As Integer bRes = Equals(Got, Expected, Description) If Not bRes Then If Len(Got) <> Len(Expected) Then Note(Subst$(("Strings are of different lengths &1 and &2, respectively."), Len(Got), Len(Expected))) Endif For iPos = 1 To Min(Len(Got), Len(Expected)) If Mid$(Got, iPos, 1) <> Mid$(Expected, iPos, 1) Then Break Next Note(Subst$(("Strings differ at position &1."), iPos)) Endif Return bRes End Public Sub Error(Optional Description As String) As Boolean Return Ok( Error , Description) End Public Sub ErrorCode(Code As Integer, Optional Description As String) As Boolean Dim bRes As Boolean If Not Error Then bRes = Fail(Description) Note(("No error happened")) Else bRes = Equals(Error.Code, Code) If Not bRes Then Note(Subst$(("Error was: &1 (code: &2) at &3"), Error.Text, Error.Code, Error.Where)) Endif Endif Error.Clear() Return bRes End Public Sub NotError(Optional Description As String) As Boolean Dim bRes As Boolean bRes = Ok(Not Error , Description) If Not bRes Then Note(Subst$(("Error was: &1 (code: &2) at &3"), Error.Text, Error.Code, Error.Where)) Endif Error.Clear() Return bRes End