“Quality is free, but only to those who are willing to pay heavily for it” – T. DeMarco
Now unit testing is not that heavy to pay off so lets start the blog.
In this blog we will be continuing the voyage to the kingdom of ANTLR4
In previous blog we discussed how to test whether a string is grammatically correct or not using console. To refer the first blog [HIT ME]
In this blog we will programmatically check whether a file containing the input string is grammatically correct or not.
STEPS TO TREASURE
Step 1 “Staring up” ->
Create new sbt project in intellij.
Step 2 “Including required dependencies in code” ->
Add following dependencies in build.sbt
a) libraryDependencies += “org.apache.commons” % “commons-io” % “1.3.2”
This dependency is included to do input\output from file. Its an optional dependency if you are using some other mechanism to perform file i/o.
b) libraryDependencies += “org.antlr” % “antlr4-runtime” % “4.5”
This dependency adds the antlr jar to project.
c) libraryDependencies += “org.scalatest” %% “scalatest” % “2.2.4” % “test”
It includes the scalatest jar to project which facilitates code testing.
d) libraryDependencies += “org.mockito” % “mockito-all” % “1.9.5”
It facilitates mocking in unit testing.
e) libraryDependencies += “com.typesafe.scala-logging” % “scala-logging_2.11” % “3.1.0”
Used for logging the errors if any wrong test input is provided.
Also following plugins were used in the project (add in plugins.sbt)
a) addSbtPlugin(“org.scoverage” % “sbt-scoverage” % “1.3.5”)
Used to see the code coverage by unit tests
b) addSbtPlugin(“org.scalastyle” %% “scalastyle-sbt-plugin” % “0.8.0”)
this plugin checks if any scalastyle warning is present or not.
Step 3 “Creating Desired Directory Structure” ->
The following image shows the directory structure of the project
In main, the ‘antlr’ folder contains the grammar file(.g4 extension), ‘java’ folder contains the auto generated grammar corresponding code, ‘scala’ folder has the computing logic. In ‘test’, resources folder has 2 sub folders. ‘expression’ folder contains input files and ‘expression_out’ folder contains corresponding output. The ‘scala’ folder has ‘ComputeArithmeticResultSpec’ which contains all the test case and the ‘util’ folder contains ‘TestHelper’ which facilitates testing.
Step 4 “Writing grammar” (My Favorite) ->
Here the grammar is written by keeping track of following conditions :
1) The grammar is based on performing arithmatic operations of addition, subtraction, division and multiplication on the operands.
2) An operand must be a positive integer including 0.
3) General syntax of a syntactically valid grammar expression is –
operation operand operand;
operation can be ADD,SUB,DIV,MUL (either uppercase or lowercase)
operand must be any positive integer
and the expression must end with a semicolon.
The grammar file ‘ArithmaticGrammar.g4’ has following content
Step 5 “Generating corresponding java files for grammar” (the magical step) ->
Follow steps below to generate java code of parser, lexer and visitor for the grammar file
1) Open the ‘ArithmaticGrammar.g4’ file and keep the control over the file by clicking anywhere on the file.
2) Click TOOLS from File Menu Bar and select ‘Configure ANTLR…’. Follow the steps in the link below to successfully configure the antlr
In my case the the confugration window looks like :
Please remeber to check both the check boxes on bottom left of the ‘Configure Antlr box’ to successfully generate the visitor for the grammar
3) Now from Menu Bar click Run and select ‘Generate ANTLR Recogniser’ (And Hocus Focus The java files are present exactly where we specified)
Step 6 “Making classes to use grammar” ->
Following classes are created :
This class parses the string from the input file and generates corresponding parse tree . It has 2 methods :
This method takes path of the input file as input and returns Either[Int,String]; Int is the result of the operation which is returned if the expression in the input file is parsed correctly and String is the error message returned when the expression is not parsed correctly.
This method generates the parse tree and from it generates the context (ctx) of type StartRuleContext, a context is defined for every grammar rule and is like a scope through which one can access all the components of that grammar rule. If the parsing is correct, result of the expression is computed and returned. Else the error message is returned.
It is a private method which reads the content of the error stream and returns the corresponding string. If the returned string is empty, this implies that there were no errors in parsing.
This class overrides ‘ArithmaticGrammarBaseVisitor’ (AutoGenerated Java file) and gives the custom implementation of ‘visitStartRule’ in which we define the logic of fetching the operands and the operator from the ctx(parameter) and evaluating the corresponding result.
Step 7 “Testing” (The Climax) ->
‘Funsuite’ is used for testing. ‘TestHelper’ object is created to facilitate testing and it has 2 public methods :
Via this method we test the ‘CustomArithmetic’ class. Mocking is used to mock the ‘visitStartRule’ method of ‘CustomVisitor’ class to purely implement unit testing. This method calls ‘computeArithmeticResult’ method with input file path as parameter and its return value(expected result) is compared with the corresponding output file(actual result). If the content of input file as well as the output file are same, the test case is successful otherwise the mismatching returned data of ‘computeArithmeticResult'(expected result) method and output file text(actual result) are logged. Following is the corresponding code :
This method tests the ‘CustomVisitor’ class. The method take the input file path as parameter and returns the result of evaluation of the input expression. Here initially context is created by calling ‘getContext’ method which takes the path of input file as input and returns its corresponding context. Then a call to ‘visitStartRule’ method of ‘CustomVisitor’ class is made which returns integer value(computed result of arithmetic operation). Following is the code :
The methods of the ‘TestHelper’ are used by ‘computeArithmeticResultSpec’ which extends ‘FunSuite’. Here the actual result(res) is computed by calling corresponding methods of ‘TestHelper’ and is then checked with the expected result(right side of ===). If actual result is equal to the expected result, the test case pass. Following are some test cases for ‘CustomArithmetic’ and ‘CustomVisitor’ :
The above code snippet successfully conclude this blog. One thing to note here is that we have not written the test cases for the auto generated java code, instead we have covered only the code which was overridden from the auto generated code, example – the ‘visitStartRule’ method of auto generated ‘ArithmaticGrammarBaseVisitor’ class is been overridden in ‘CustomVisitor’ class and thus it is covered in unit testing.
The code is available on git :- https://github.com/sahil-sawhney/ANTLR4GrammarWithUnitTesting
Comments are welcomed.
3) The Definitive ANTLR4 Reference by Terence Parr