e6e9b4b1b0
[GB.TEST] * NEW: TapParser can now parse subtests. Consequently, TestRunner assembles a tree of TestAssertions. * BUG: Remove trailing '\r' characters from TapStream lines. By our convention a subtest is opened by a "Test ..." line and closed by its summarizing "ok" / "not ok" assertion. This commit regresses on TInternals's reflection tests because I added tests for the parser and runner which need event handlers to do their testing. These are unintentionally detected as test methods.
198 lines
5.3 KiB
Text
198 lines
5.3 KiB
Text
' Gambas class file
|
|
|
|
''' Low-level class for planning and printing test results in Test Anything Protocol ([TAP]) format.
|
|
'''
|
|
''' [TAP]: http://testanything.org/tap-specification.html
|
|
|
|
Event Filter
|
|
|
|
Property {Output} As Stream Use $hOutput
|
|
Property Read Session As TapContext
|
|
|
|
Property Line As String Use $sLine
|
|
|
|
Private $aSubtests As New TapContext[]
|
|
Private $hCurrent As New TapContext
|
|
|
|
Public Sub _new(Optional Tests As Integer, Optional Comment As String, Optional {Output} As Stream = File.Out)
|
|
|
|
$hOutput = {Output}
|
|
With $hCurrent
|
|
.Plan = Test.NO_PLAN
|
|
.TestsRun = 0
|
|
.LastId = 0
|
|
.Finished = False
|
|
End With
|
|
If Not IsMissing(Tests) Then Plan(Tests, Comment)
|
|
|
|
End
|
|
|
|
Public Sub Subtest(Description As String, Optional Tests As Integer, Optional Comment As String)
|
|
|
|
Print(Subst$("Test &1", Description))
|
|
$aSubtests.Push($hCurrent)
|
|
With $hCurrent = New TapContext
|
|
.Summary.Description = Description
|
|
.Summary.Comment = Comment
|
|
.Plan = Test.NO_PLAN
|
|
.TestsRun = 0
|
|
.LastId = 0
|
|
.Finished = False
|
|
End With
|
|
If Tests Then Plan(Tests)
|
|
|
|
End
|
|
|
|
Public Sub Plan(Tests As Integer, Optional Comment As String)
|
|
|
|
With $hCurrent
|
|
If .Finished Or .SkipAll Then Error.Raise(("Tests already finished"))
|
|
If .TestsRun Then Error.Raise(Subst$(("Too late to plan. Already ran &1 tests"), .TestsRun))
|
|
' TAP specification lists '1..0 # Skipped: WWW::Mechanize not installed'
|
|
' as a valid example.
|
|
If Tests <= Test.NO_PLAN Then Error.Raise(("Number of tests must be non-negative"))
|
|
.Plan = Tests
|
|
End With
|
|
Print(Subst$("1..&1&2", Tests, IIf(Comment, " # " & Comment, "")))
|
|
|
|
End
|
|
|
|
Public Sub SkipAll(Optional Comment As String)
|
|
|
|
Plan(0, "SKIP" & IIf(Comment, " ", "") & Comment)
|
|
$hCurrent.SkipAll = True
|
|
|
|
End
|
|
|
|
Public Sub Finish()
|
|
|
|
Dim hFinished As TapContext
|
|
|
|
With $hCurrent
|
|
Dim hTest As TestAssertion
|
|
|
|
If .Finished Then Error.Raise(("Tests already finished"))
|
|
|
|
' Print a plan line after the fact
|
|
If .Plan = Test.NO_PLAN Then
|
|
.Plan = .TestsRun
|
|
Print("1.." & .Plan)
|
|
Endif
|
|
|
|
' Sum up this subtest
|
|
.Finished = True
|
|
.Summary.Ok = (.Plan > 0 Or .SkipAll) And (.TestsRun = .Plan)
|
|
For Each hTest In .Summary.Subtests
|
|
.Summary.Ok = .Summary.Ok And hTest.Success
|
|
Next
|
|
End With
|
|
|
|
' Go one level up if possible. Unless we are the top-level test,
|
|
' we add one assertion for this finishing subtest.
|
|
hFinished = $hCurrent
|
|
Try $hCurrent = $aSubtests.Pop()
|
|
If Not Error Then
|
|
Assert(hFinished.Summary)
|
|
Endif
|
|
|
|
End
|
|
|
|
Public Sub Assert(Assertion As TestAssertion) As TestAssertion
|
|
|
|
Dim sDirective As String
|
|
Dim sLine As String
|
|
|
|
With Assertion
|
|
If $hCurrent.Finished Or $hCurrent.SkipAll Then Error.Raise(("Tests already finished"))
|
|
|
|
' It is not advised to start a description with a number token because
|
|
' it will be interpreted as the (optional) test ID. We issue a warning
|
|
' about this but fix it anyway by always printing the ID before *and*
|
|
' prefixing the Description with a dash, as is common.
|
|
If .Description Match "^[0-9]+(\\s|$)" Then
|
|
Error Subst$(("Warning: Description '&1' should not start with a number"), .Description)
|
|
Endif
|
|
' If .Description Like "*#*" Then
|
|
' Error.Raise(Subst$(("Description '&1' may not contain a '#' character"), .Description))
|
|
' Endif
|
|
|
|
Inc $hCurrent.TestsRun
|
|
If Not .Id Then .Id = $hCurrent.TestsRun
|
|
$hCurrent.LastId = .Id
|
|
sLine = Subst$("&1 &2 - &3", IIf(.Ok, "ok", "not ok"), .Id, .Description)
|
|
|
|
If .Directive <> TestAssertion.NONE Then
|
|
' Matches the values of the Enum TestAssertion.TODO, TestAssertion.SKIP
|
|
sDirective = Choose(.Directive, "TODO", "SKIP")
|
|
If Not sDirective Then Error.Raise(Subst$(("Unsupported directive '&1'"), .Directive))
|
|
sLine &= " # " & sDirective
|
|
If .Comment Then sLine &= " " & .Comment
|
|
Endif
|
|
End With
|
|
|
|
Print(sLine)
|
|
$hCurrent.Summary.Subtests.Push(Assertion)
|
|
Return Assertion
|
|
|
|
End
|
|
|
|
'' Unwinds the subtest stack. BailOut will always bail out of all the subtests.
|
|
Private Sub Unwind()
|
|
|
|
While $aSubtests.Count > 0
|
|
Note("Unwinding subtest stack!")
|
|
Finish()
|
|
Wend
|
|
|
|
End
|
|
|
|
Public Sub BailOut(Optional Comment As String)
|
|
|
|
Unwind()
|
|
'The string "Bail out!" must not be changed!
|
|
Print("Bail out!" & IIf(Comment, " " & Comment, ""))
|
|
$hCurrent.Finished = True
|
|
|
|
End
|
|
|
|
Public Sub Diagnostic(Comment As String)
|
|
|
|
Dim sLine As String
|
|
|
|
' Split on these doesn't do what I mean: on the empty string, we get
|
|
' zero lines, on a newline we get two. I want one in both cases.
|
|
If Not Comment Or If Comment = gb.Lf Then
|
|
Print("#")
|
|
Return
|
|
Endif
|
|
|
|
For Each sLine In Split(Comment, "\n")
|
|
Print("# " & sLine)
|
|
Next
|
|
|
|
End
|
|
|
|
Public Sub Note(Comment As String)
|
|
|
|
Diagnostic(Comment)
|
|
|
|
End
|
|
|
|
Public Sub Print({Line} As String)
|
|
|
|
Dim bCancel As Boolean
|
|
|
|
$sLine = {Line}
|
|
bCancel = Raise Filter
|
|
If bCancel Then Return
|
|
Print #$hOutput, String$($aSubtests.Count, " "); $sLine
|
|
Flush #$hOutput
|
|
|
|
End
|
|
|
|
Private Function Session_Read() As TapContext
|
|
|
|
If $aSubtests.Count Then Return $aSubtests[0]
|
|
Return $hCurrent
|
|
|
|
End
|