2020-02-23 15:54:16 +01:00
' Gambas class file
''' Low-level parser for Test Anything Protocol ([TAP]) output.
''' It can only be used for one test process.
'''
''' [TAP]: http://testanything.org/tap-specification.html
2020-06-01 06:06:11 +02:00
'''
''' Supports parsing subtests as generated by TapPrinter:
''' A new subtest is announced by a "Test ..." line and the
''' whole subtest is on a new indentation level.
2020-02-23 15:54:16 +01:00
Event Version(Version As Integer)
2020-06-01 06:29:33 +02:00
Event Plan(Start As Integer, {End} As Integer, SkipAll As Boolean)
2020-04-25 05:22:10 +02:00
Event Assert(Ok As Boolean, Id As Long, Description As String, Directive As Integer, Comment As String)
2020-02-23 15:54:16 +01:00
Event BailOut(Comment As String)
Event Diagnostic(Comment As String)
Event Else({Line} As String)
2020-06-01 06:06:11 +02:00
' Subtest events
Event BeginSubtest(Description As String)
Event EndSubtest
2020-02-23 15:54:16 +01:00
Private $iLine As Integer = 0
Private $iTestsRun As Integer = 0
2020-06-01 06:06:11 +02:00
Private $iIndent As Integer = 0
2020-02-23 15:54:16 +01:00
Public Sub Parse(TapStream As Stream)
While Not Eof(TapStream)
ParseLine(TapStream.ReadLine())
Wend
End
Public Sub ParseLine({Line} As String)
Dim sLine As String = {Line}
Dim iVersion As Integer
Dim bResult As Boolean
2020-04-25 05:22:10 +02:00
Dim iId As Long
2020-06-01 06:29:33 +02:00
Dim iStart, iEnd As Integer
Dim bSkipAll As Boolean
2020-02-23 15:54:16 +01:00
Dim sDescription As String
Dim iDirective As Integer
Dim sComment As String
2020-06-01 06:06:11 +02:00
' Subtract current subtest indentation. TapPrinter uses 2 spaces.
If $iIndent > 0 And If Left$(sLine, 2 * $iIndent) Not Like " " Then
Dec $iIndent
If $iIndent > 0 And If Left$(sLine, 2 * $iIndent) Not Like " " Then Error.Raise(Subst$(("Indentation on line '&1' is malformed"), sLine))
Raise EndSubtest
Endif
If $iIndent > 0 Then sLine = Right$(sLine, -2 * $iIndent)
2020-02-23 15:54:16 +01:00
If $iLine = 0 And If sLine Begins "TAP version " Then
Try iVersion = CInt(Trim$(Mid$(sLine, 13)))
' At present a "TAP version" line is always an error:
' 1. It might not be an integer, which is an error
' 2. A version lower than 13 is an error by the specification
' 3. We don't support version 13 or above.
If Error Then Error.Raise(Subst$(("Unrecognised TAP version '&1'"), Trim$(Mid$(sLine, 13))))
If iVersion < 13 Then Error.Raise(Subst$(("Illegal TAP version '&1'"), iVersion))
If iVersion > 12 Then Error.Raise(Subst$(("Unsupported TAP version &1"), iVersion))
Endif
If $iLine = 0 Then Raise Version(12)
If sLine Match "^\\d+..\\d+" Then
2020-06-01 06:29:33 +02:00
bResult = ParsePlan(sLine, ByRef iStart, ByRef iEnd, ByRef bSkipAll)
If Not bResult Then Error.Raise(Subst$(("Couldn't extract test plan from '&1'"), sLine))
Raise Plan(iStart, iEnd, bSkipAll)
2020-02-23 15:54:16 +01:00
Else If sLine Begins "ok" Or If sLine Begins "not ok" Then
2020-04-25 05:22:10 +02:00
bResult = ParseTest(sLine, ByRef iId, ByRef sDescription, ByRef iDirective, ByRef sComment)
Raise Assert(bResult, iId, sDescription, iDirective, sComment)
2020-02-23 15:54:16 +01:00
Else If sLine Begins "Bail out!" Then
Raise BailOut(Trim$(Mid$(sLine, 10)))
Else If sLine Begins "#" Then
Raise Diagnostic(Trim$(Mid$(sLine, 2)))
2020-06-01 06:06:11 +02:00
Else If sLine Begins "Test " Then
Raise BeginSubtest(Trim$(Mid$(sLine, 5)))
Inc $iIndent
2020-02-23 15:54:16 +01:00
Else
Raise Else(sLine)
Endif
Inc $iLine
End
2020-06-01 06:29:33 +02:00
Private Function ParsePlan(sLine As String, ByRef Start As Integer, ByRef {End} As Integer, ByRef SkipAll As Boolean) As Boolean
Dim sComment As String
SkipAll = False
With Scan(sLine, "*..*")
If Not IsInteger(.[0]) Then Return False
Start = CInt(.[0])
If IsInteger(.[1]) Then
{End} = CInt(.[1])
Else If .[1] Match "^\\d+\\s*#"
With Scan(.[1], "*#*")
{End} = CInt(Trim$(.[0]))
sComment = Trim$(.[1])
If Upper$(sComment) Begins "SKIP" Then
SkipAll = True
Endif
End With
Else
Return False
Endif
End With
Return True
End
2020-04-25 05:22:10 +02:00
Private Function ParseTest(sLine As String, ByRef Id As Long, ByRef Description As String, ByRef Directive As Integer, ByRef Comment As String) As Boolean
2020-02-23 15:54:16 +01:00
Dim bResult As Boolean
Dim aWords As String[]
Dim sWord As String
' Tidy up caller's local variables
2020-04-25 05:22:10 +02:00
Id = 0
2020-02-23 15:54:16 +01:00
Description = ""
2020-05-07 12:57:55 +02:00
Directive = TestAssertion.NONE
2020-02-23 15:54:16 +01:00
Comment = ""
Inc $iTestsRun
' "ok" or "not ok"
If sLine Begins "ok" Then
bResult = True
sLine = Trim$(Mid$(sLine, 3))
Else If sLine Begins "not ok"
bResult = False
sLine = Trim$(Mid$(sLine, 7))
Else
Error.Raise(Subst$(("Not a test line '&1'"), sLine))
Endif
' Make sure that if a "#" occurs, it will be after a space
sLine = Replace$(sLine, "#", " #")
aWords = Split(sLine, " ", "", True).Reverse()
2020-04-25 05:22:10 +02:00
' Test ID
2020-02-23 15:54:16 +01:00
Try sWord = aWords.Pop()
2020-04-25 05:22:10 +02:00
Try Id = CLong(sWord)
2020-02-23 15:54:16 +01:00
If Error Then
aWords.Push(sWord)
2020-04-25 05:22:10 +02:00
Id = $iTestsRun
2020-02-23 15:54:16 +01:00
Endif
' Description
While aWords.Count
sWord = aWords.Pop()
If sWord Begins "#" Then Break
Description &= sWord & " "
Wend
Description = Trim$(Description)
' Directive
If sWord Begins "#" Then
If sWord = "#" Then
Try sWord = aWords.Pop()
If Error Then Error.Raise(("Premature end of directive"))
Endif
Select Case Lower$(sWord)
Case "todo"
2020-05-07 12:57:55 +02:00
Directive = TestAssertion.TODO
2020-02-23 15:54:16 +01:00
Case "skip"
2020-05-07 12:57:55 +02:00
Directive = TestAssertion.SKIP
2020-02-23 15:54:16 +01:00
Default
Error.Raise(Subst$(("Invalid directive '&1'"), sWord))
End Select
Endif
' Comment
Comment = Trim$(aWords.Reverse().Join(" "))
Return bResult
End