Testing a PetitParser class
TL;DR
Once you have a parser defined as a class, it is a good idea to establish test cases for every single rule, both as regression tests, and as test cases for further development. We show, using the PetitParser SPL case study, how to do this.
Note that we have first fully developed a parser as a script and are now turning our scripted tests into examples. An alternative approach would have been to turn an early version of the scripted parser into a class, to enable TDD for further development. See also Example-driven development by example.
Testing grammar rules
In GT, test cases are implemented as Examples, that is, methods that perform assertions on an example object returned by the example method.
Defining the Example test class
To create test examples for a PetitParser class called MyParser
, you should define MyParserExamples
. This may be a subclass of Object
, but even better is to define it as a subclass of PP2CompositeNodeExamples
. This class provides several utility methods that allow one to test that a given input can be parsed by a particular rule, and will fail with other rules.
For our SPLGrammar
parser class, we define SPLGrammarExamples
.
You should also define a parserClass
method returning the class to be used to parse the examples. In our case it is SPLGrammarExamples>>#parserClass
Defining test examples
Each test is an example method, so must be defined with a <gtExample>
pragma.
The most basic tests just check that an input is parsed by a given rule, and then return the result.
For example, we test that the integer
rule works using the PP2CompositeNodeExamples>>#parse:rule:
method.
integer42 <gtExample> ^ self parse: '42' rule: #integer
We might like to assert that a given input will be parsed by some rules and not others. For this we use PP2CompositeNodeExamples>>#fail:rule:
:
threeTimesFour <gtExample> | input | input := '3*4'. self parse: input rule: #expression. self fail: input rule: #unary. ^ self parse: input rule: #factor
Examples can also be composed. Most of the grammar tests have unique inputs, but here is an example of a composed example:
whileProgram <gtExample> | input | input := self whileProgramSource. self fail: input rule: #declaration. self fail: input rule: #statement. ^ self parse: input rule: #program