blob: 271a2e38c856e426aaefd2fb4f976849bb3d4ce5 [file] [log] [blame]
/*******************************************************************************
* Copyright (c) 2013, 2014 Etienne Bergeron
*
* All rights reserved. This program and the accompanying materials are
* made available under the terms of the Eclipse Public License 2.0 which
* accompanies this distribution, and is available at
* https://www.eclipse.org/legal/epl-2.0/
*
* SPDX-License-Identifier: EPL-2.0
*
* Contributors:
* Etienne Bergeron - Initial API and implementation
*******************************************************************************/
package org.eclipse.tracecompass.ctf.parser.tests;
import static org.junit.Assert.assertEquals;
import static org.junit.Assert.fail;
import org.antlr.runtime.ANTLRStringStream;
import org.antlr.runtime.CharStream;
import org.antlr.runtime.CommonTokenStream;
import org.antlr.runtime.RecognitionException;
import org.antlr.runtime.tree.CommonTree;
import org.eclipse.tracecompass.ctf.parser.CTFLexer;
import org.eclipse.tracecompass.ctf.parser.CTFParser;
import org.junit.Ignore;
import org.junit.Test;
/**
* This test validates the CTF-Parser implementation.
*
* The goal of these tests is to validate syntactic rules and not the CTF
* semantic. Each test parses a string with a given rule of the compiled parser
* and validates the resulting tree by using match rules.
*
* @author Etienne Bergeron
*/
public class CtfParserTest {
// ------------------------------------------------------------------------
// Attributes
// ------------------------------------------------------------------------
private CTFParser parser;
// ------------------------------------------------------------------------
// Matches - Helper class and functions to match a parsed tree.
// ------------------------------------------------------------------------
private class TreeMatcher {
int fType;
String fText;
TreeMatcher[] fChild;
TreeMatcher(int type, String text, TreeMatcher child[]) {
fType = type;
fText = text;
fChild = child;
}
void matches(CommonTree tree) {
if (fType == -1) {
return;
}
if (tree.getType() != fType) {
fail("Type mismatch!" +
" expected:" + fType +
" actual:" + tree.getType());
}
if (fText != null) {
if (!fText.equals(tree.getText())) {
fail("Text mismatch!" +
" expected:" + fText +
" actual:" + tree.getText());
}
}
if (fChild != null) {
int size = fChild.length;
if (tree.getChildren() == null) {
if (size != 0) {
fail("Invalid children!"
+ "Expect: " + size + "child");
}
} else {
if (tree.getChildren().size() != size) {
fail("Invalid number of childs!"
+ " expected:" + size
+ " actual:" + tree.getChildren().size());
}
for (int i = 0; i < size; ++i) {
fChild[i].matches((CommonTree) tree.getChild(i));
}
}
}
}
}
void Matches(TreeMatcher matcher, CommonTree tree) {
if (tree == null) {
fail("Parsing failed!");
}
matcher.matches(tree);
}
TreeMatcher All() {
return new TreeMatcher(-1, null, null);
}
TreeMatcher Node(int type, TreeMatcher... child) {
return new TreeMatcher(type, null, child);
}
TreeMatcher Node(int type, String text, TreeMatcher... child) {
return new TreeMatcher(type, text, child);
}
TreeMatcher List(TreeMatcher... child) {
return new TreeMatcher(0, null, child);
}
// ------------------------------------------------------------------------
// Operations
// ------------------------------------------------------------------------
private void setInput(String content) {
CharStream cs = new ANTLRStringStream(content);
CTFLexer lexer = new CTFLexer(cs);
CommonTokenStream tokens = new CommonTokenStream(lexer);
parser = new CTFParser(tokens, false);
}
private CommonTree primaryExpression(String content) {
try {
setInput(content);
return parser.primaryExpression().getTree();
} catch (RecognitionException e) {
return null;
}
}
private CommonTree unaryExpression(String content) {
try {
setInput(content);
return parser.unaryExpression().getTree();
} catch (RecognitionException e) {
return null;
}
}
private CommonTree declaration(String content) {
try {
setInput(content);
return parser.declaration().getTree();
} catch (RecognitionException e) {
return null;
}
}
// ------------------------------------------------------------------------
// Test cases
// ------------------------------------------------------------------------
/**
* Validate that parsing of an empty expression is invalid.
*/
@Test
public void testPrimaryExpression() {
CommonTree tree_empty = primaryExpression("");
assertEquals(null, tree_empty);
}
/**
* Validate parsing of literals through a primary expression
*/
@Test
public void testIntegerLiteralPrimaryExpression() {
Matches(Node(CTFParser.UNARY_EXPRESSION_DEC,
Node(CTFParser.DECIMAL_LITERAL, "123")),
primaryExpression("123"));
Matches(Node(CTFParser.UNARY_EXPRESSION_HEX,
Node(CTFParser.HEX_LITERAL, "0x123")),
primaryExpression("0x123"));
Matches(Node(CTFParser.UNARY_EXPRESSION_OCT,
Node(CTFParser.OCTAL_LITERAL, "0123")),
primaryExpression("0123"));
Matches(Node(CTFParser.UNARY_EXPRESSION_DEC,
Node(CTFParser.DECIMAL_LITERAL, "123"),
Node(CTFParser.SIGN, "-")),
primaryExpression("-123"));
Matches(Node(CTFParser.UNARY_EXPRESSION_DEC,
Node(CTFParser.DECIMAL_LITERAL, "123"),
Node(CTFParser.SIGN, "-")),
primaryExpression(" - 123"));
Matches(Node(CTFParser.UNARY_EXPRESSION_DEC,
Node(CTFParser.DECIMAL_LITERAL, "123"),
Node(CTFParser.SIGN, "-"),
Node(CTFParser.SIGN, "-"),
Node(CTFParser.SIGN, "+")),
primaryExpression(" - - + 123"));
Matches(Node(CTFParser.UNARY_EXPRESSION_HEX,
Node(CTFParser.HEX_LITERAL, "0x123"),
Node(CTFParser.SIGN, "+"),
Node(CTFParser.SIGN, "-")),
primaryExpression("+ - 0x123"));
Matches(Node(CTFParser.UNARY_EXPRESSION_OCT,
Node(CTFParser.OCTAL_LITERAL, "0123"),
Node(CTFParser.SIGN, "+"),
Node(CTFParser.SIGN, "-")),
primaryExpression("+ - 0123"));
}
/**
* Validate parsing of a character literals through a primary expression
*/
@Test
public void testCharacterLiteralPrimaryExpression() {
Matches(Node(CTFParser.CHARACTER_LITERAL, "'a'"),
primaryExpression("'a'"));
Matches(Node(CTFParser.CHARACTER_LITERAL, "'\\n'"),
primaryExpression("'\\n'"));
}
/**
* Validate parsing of a string literals through a primary expression
*/
@Test
public void testStringLiteralPrimaryExpression() {
Matches(Node(CTFParser.UNARY_EXPRESSION_STRING_QUOTES,
Node(CTFParser.STRING_LITERAL, "\"aaa\"")),
primaryExpression("\"aaa\""));
Matches(Node(CTFParser.UNARY_EXPRESSION_STRING_QUOTES,
Node(CTFParser.STRING_LITERAL, "L\"aaa\"")),
primaryExpression("L\"aaa\""));
Matches(Node(CTFParser.UNARY_EXPRESSION_STRING_QUOTES,
Node(CTFParser.STRING_LITERAL, "\"aaa\\n\"")),
primaryExpression("\"aaa\\n\""));
}
/**
* Validate parsing of keywords through a primary expression
*/
@Test
public void testKeywordPrimaryExpression() {
Matches(Node(CTFParser.UNARY_EXPRESSION_STRING,
Node(CTFParser.SIGNEDTOK, "signed")),
primaryExpression("signed"));
Matches(Node(CTFParser.UNARY_EXPRESSION_STRING,
Node(CTFParser.ALIGNTOK, "align")),
primaryExpression("align"));
}
/**
* Validate parsing of identifiers through a primary expression
*/
@Test
public void testIdentifierPrimaryExpression() {
Matches(Node(CTFParser.UNARY_EXPRESSION_STRING,
Node(CTFParser.IDENTIFIER, "x")),
primaryExpression("x"));
Matches(Node(CTFParser.UNARY_EXPRESSION_STRING,
Node(CTFParser.IDENTIFIER, "_123")),
primaryExpression("_123"));
}
/**
* Validate that parsing of an empty unary expression is invalid.
*/
@Test
public void testUnaryExpression() {
CommonTree tree_empty = unaryExpression("");
assertEquals(null, tree_empty);
}
/**
* Validate parsing primary expression through an unary expression
*/
@Test
public void testSimpleUnaryExpression() {
Matches(Node(CTFParser.UNARY_EXPRESSION_DEC,
Node(CTFParser.DECIMAL_LITERAL, "123")),
unaryExpression("123"));
Matches(Node(CTFParser.UNARY_EXPRESSION_STRING,
Node(CTFParser.IDENTIFIER, "x")),
unaryExpression("x"));
}
/**
* Validate parsing array through an unary expression
*/
@Test
public void testArrayUnaryExpression() {
Matches(List(Node(CTFParser.UNARY_EXPRESSION_STRING,
Node(CTFParser.IDENTIFIER, "x")),
Node(CTFParser.OPENBRAC),
Node(CTFParser.UNARY_EXPRESSION_DEC,
Node(CTFParser.DECIMAL_LITERAL, "1"))),
unaryExpression("x[1]"));
Matches(List(Node(CTFParser.UNARY_EXPRESSION_STRING,
Node(CTFParser.IDENTIFIER, "x")),
Node(CTFParser.OPENBRAC),
Node(CTFParser.UNARY_EXPRESSION_STRING,
Node(CTFParser.IDENTIFIER, "n"))),
unaryExpression("x[n]"));
Matches(List(Node(CTFParser.UNARY_EXPRESSION_STRING,
Node(CTFParser.IDENTIFIER, "x")),
Node(CTFParser.OPENBRAC),
Node(CTFParser.UNARY_EXPRESSION_STRING,
Node(CTFParser.IDENTIFIER, "n")),
Node(CTFParser.OPENBRAC),
Node(CTFParser.UNARY_EXPRESSION_DEC,
Node(CTFParser.DECIMAL_LITERAL, "1"))),
unaryExpression("x[n][1]"));
Matches(List(Node(CTFParser.UNARY_EXPRESSION_STRING,
Node(CTFParser.IDENTIFIER, "x")),
Node(CTFParser.OPENBRAC),
Node(CTFParser.UNARY_EXPRESSION_STRING,
Node(CTFParser.IDENTIFIER, "n")),
Node(CTFParser.OPENBRAC),
Node(CTFParser.UNARY_EXPRESSION_DEC,
Node(CTFParser.DECIMAL_LITERAL, "1"),
Node(CTFParser.SIGN, "+"))),
unaryExpression("x[n][+1]"));
}
/**
* Validate parsing array with keywords through an unary expression
*/
@Test
public void testSpecialArrayUnaryExpression() {
// Added for CTF-v1.8
Matches(List(Node(CTFParser.TRACE),
Node(CTFParser.OPENBRAC),
Node(CTFParser.UNARY_EXPRESSION_STRING,
Node(CTFParser.IDENTIFIER, "n"))),
unaryExpression("trace[n]"));
Matches(List(Node(CTFParser.CLOCK),
Node(CTFParser.OPENBRAC),
Node(CTFParser.UNARY_EXPRESSION_STRING,
Node(CTFParser.IDENTIFIER, "n")),
Node(CTFParser.OPENBRAC),
Node(CTFParser.UNARY_EXPRESSION_DEC,
Node(CTFParser.DECIMAL_LITERAL, "1"))),
unaryExpression("clock[n][1]"));
}
/**
* Validate parsing member expression through an unary expression
*/
@Test
public void testMemberUnaryExpression() {
Matches(List(Node(CTFParser.UNARY_EXPRESSION_STRING,
Node(CTFParser.IDENTIFIER, "x")),
Node(CTFParser.DOT,
Node(CTFParser.UNARY_EXPRESSION_STRING,
Node(CTFParser.IDENTIFIER, "y")))),
unaryExpression("x.y"));
Matches(List(Node(CTFParser.UNARY_EXPRESSION_STRING,
Node(CTFParser.IDENTIFIER, "x")),
Node(CTFParser.DOT,
Node(CTFParser.UNARY_EXPRESSION_STRING,
Node(CTFParser.IDENTIFIER, "y"))),
Node(CTFParser.DOT,
Node(CTFParser.UNARY_EXPRESSION_STRING,
Node(CTFParser.IDENTIFIER, "z")))),
unaryExpression("x.y.z"));
}
/**
* Validate parsing pointer expression through an unary expression
*/
@Test
public void testPointerUnaryExpression() {
Matches(List(Node(CTFParser.UNARY_EXPRESSION_STRING,
Node(CTFParser.IDENTIFIER, "x")),
Node(CTFParser.ARROW,
Node(CTFParser.UNARY_EXPRESSION_STRING,
Node(CTFParser.IDENTIFIER, "y")))),
unaryExpression("x->y"));
Matches(List(Node(CTFParser.UNARY_EXPRESSION_STRING,
Node(CTFParser.IDENTIFIER, "x")),
Node(CTFParser.ARROW,
Node(CTFParser.UNARY_EXPRESSION_STRING,
Node(CTFParser.IDENTIFIER, "y"))),
Node(CTFParser.ARROW,
Node(CTFParser.UNARY_EXPRESSION_STRING,
Node(CTFParser.IDENTIFIER, "z")))),
unaryExpression("x->y->z"));
}
/**
* Validate complex expressions through an unary expression
*/
@Test
public void testMixedUnaryExpression() {
Matches(List(Node(CTFParser.UNARY_EXPRESSION_STRING,
Node(CTFParser.IDENTIFIER, "x")),
Node(CTFParser.OPENBRAC),
Node(CTFParser.UNARY_EXPRESSION_DEC,
Node(CTFParser.DECIMAL_LITERAL, "2")),
Node(CTFParser.ARROW,
Node(CTFParser.UNARY_EXPRESSION_STRING,
Node(CTFParser.IDENTIFIER, "y"))),
Node(CTFParser.DOT,
Node(CTFParser.UNARY_EXPRESSION_STRING,
Node(CTFParser.IDENTIFIER, "z"))),
Node(CTFParser.OPENBRAC),
Node(CTFParser.UNARY_EXPRESSION_DEC,
Node(CTFParser.DECIMAL_LITERAL, "1"))),
unaryExpression("x[2]->y.z[1]"));
}
/**
* Validate that parsing of an empty declaration is invalid.
*/
@Test
public void testDeclaration() {
CommonTree tree_empty = declaration("");
assertEquals(null, tree_empty);
}
/**
* Validate parsing of integer declaration
*/
@Test
public void testIntegerTypeAliasDeclaration() {
// TODO: replace the "all" match with a better tree matcher.
Matches(All(),
declaration("typealias integer { } := int;"));
Matches(All(),
declaration("typealias integer { signed=true; } := int;"));
}
/**
* Validate parsing of floating declaration
*/
@Test
public void testFloatingTypeAliasDeclaration() {
// TODO: replace the "all" match with a better tree matcher.
Matches(All(),
declaration("typealias floating_point { } := float;"));
Matches(All(),
declaration("typealias floating_point { align = 32; } := float;"));
}
/**
* Validate parsing of typedef declaration
*/
@Ignore("This need a fix to the grammar to support a dummy initial scope. ")
@Test
public void testTypedefDeclaration() {
// TODO: replace the "all" match with a better tree matcher.
Matches(All(),
declaration("typedef dummy int;"));
Matches(All(),
declaration("typedef integer { } int;"));
}
/**
* Validate parsing of an enum declaration
*/
@Test
public void testEnumDeclaration() {
Matches(Node(CTFParser.DECLARATION,
Node(CTFParser.TYPE_SPECIFIER_LIST,
Node(CTFParser.ENUM,
Node(CTFParser.ENUM_NAME,
Node(CTFParser.IDENTIFIER, "name")),
Node(CTFParser.ENUM_BODY,
Node(CTFParser.ENUM_ENUMERATOR,
Node(CTFParser.UNARY_EXPRESSION_STRING,
Node(CTFParser.IDENTIFIER, "A"))))))),
declaration("enum name { A };"));
Matches(Node(CTFParser.DECLARATION,
Node(CTFParser.TYPE_SPECIFIER_LIST,
Node(CTFParser.ENUM,
Node(CTFParser.ENUM_NAME, All()),
Node(CTFParser.ENUM_CONTAINER_TYPE,
Node(CTFParser.TYPE_SPECIFIER_LIST,
Node(CTFParser.INTTOK))),
Node(CTFParser.ENUM_BODY, All())))),
declaration("enum name : int { A };"));
Matches(Node(CTFParser.DECLARATION,
Node(CTFParser.TYPE_SPECIFIER_LIST,
Node(CTFParser.ENUM,
Node(CTFParser.ENUM_BODY, All())))),
declaration("enum { A };"));
Matches(Node(CTFParser.DECLARATION,
Node(CTFParser.TYPE_SPECIFIER_LIST,
Node(CTFParser.ENUM,
Node(CTFParser.ENUM_CONTAINER_TYPE,
Node(CTFParser.TYPE_SPECIFIER_LIST,
Node(CTFParser.INTTOK))),
Node(CTFParser.ENUM_BODY, All())))),
declaration("enum : int { A };"));
}
/**
* Validate parsing of an enumerator
*/
@Ignore("The grammar needs to be fixed.")
@Test
public void testDeclaratorOfEnumDeclaration() {
/* TODO: This test crash the parser. */
Matches(All(),
declaration("enum { };"));
Matches(Node(CTFParser.DECLARATION,
Node(CTFParser.TYPE_SPECIFIER_LIST,
Node(CTFParser.ENUM,
Node(CTFParser.ENUM_BODY,
Node(CTFParser.ENUM_ENUMERATOR,
Node(CTFParser.UNARY_EXPRESSION_STRING,
Node(CTFParser.IDENTIFIER, "A"))),
Node(CTFParser.ENUM_ENUMERATOR,
Node(CTFParser.UNARY_EXPRESSION_STRING,
Node(CTFParser.IDENTIFIER, "B")),
Node(CTFParser.ENUM_VALUE,
Node(CTFParser.UNARY_EXPRESSION_DEC,
Node(CTFParser.DECIMAL_LITERAL, "2")))),
Node(CTFParser.ENUM_ENUMERATOR,
Node(CTFParser.UNARY_EXPRESSION_STRING,
Node(CTFParser.IDENTIFIER, "C")),
Node(CTFParser.ENUM_VALUE_RANGE,
Node(CTFParser.UNARY_EXPRESSION_DEC,
Node(CTFParser.DECIMAL_LITERAL, "3")),
Node(CTFParser.UNARY_EXPRESSION_DEC,
Node(CTFParser.DECIMAL_LITERAL, "5")))))))),
declaration("enum { A, B=2, C=3...5 };"));
Matches(Node(CTFParser.DECLARATION,
Node(CTFParser.TYPE_SPECIFIER_LIST,
Node(CTFParser.ENUM,
Node(CTFParser.ENUM_BODY,
Node(CTFParser.ENUM_ENUMERATOR,
Node(CTFParser.UNARY_EXPRESSION_STRING_QUOTES,
Node(CTFParser.STRING_LITERAL, "\"A\""))),
Node(CTFParser.ENUM_ENUMERATOR,
Node(CTFParser.UNARY_EXPRESSION_STRING_QUOTES,
Node(CTFParser.STRING_LITERAL, "\"B\"")),
All()))))),
declaration("enum { \"A\", \"B\"=2 };"));
}
/**
* Validate parsing of empty declaration
*/
@Ignore("The grammar need to be fixed to support empty ctf-body.")
@Test
public void testEmptyDeclaration() {
/*
* TODO: An exception is throw when building an common tree without
* assignments in the ctf-body.
*/
Matches(All(),
declaration("env { };"));
Matches(All(),
declaration("trace { };"));
Matches(All(),
declaration("stream { };"));
Matches(All(),
declaration("event { };"));
}
/**
* Validate parsing of an environment declaration
*/
@Test
public void testEnvDeclaration() {
Matches(Node(CTFParser.ENV,
Node(CTFParser.CTF_EXPRESSION_VAL,
Node(CTFParser.CTF_LEFT,
Node(CTFParser.UNARY_EXPRESSION_STRING,
Node(CTFParser.IDENTIFIER, "pid"))),
Node(CTFParser.CTF_RIGHT,
Node(CTFParser.UNARY_EXPRESSION_STRING,
Node(CTFParser.IDENTIFIER, "value"))))),
declaration("env { pid = value; };"));
Matches(Node(CTFParser.ENV,
Node(CTFParser.CTF_EXPRESSION_VAL, All(), All()),
Node(CTFParser.CTF_EXPRESSION_VAL, All(), All()),
Node(CTFParser.CTF_EXPRESSION_VAL, All(), All())),
declaration("env { pid = value; proc_name = \"name\"; x = y;};"));
}
/**
* Validate parsing of a trace declaration
*/
@Ignore("The grammar need to be fixed.")
@Test
public void testTraceDeclaration() {
Matches(Node(CTFParser.TRACE,
Node(CTFParser.CTF_EXPRESSION_VAL,
Node(CTFParser.CTF_LEFT,
Node(CTFParser.UNARY_EXPRESSION_STRING,
Node(CTFParser.IDENTIFIER, "major"))),
Node(CTFParser.CTF_RIGHT,
Node(CTFParser.UNARY_EXPRESSION_DEC,
Node(CTFParser.DECIMAL_LITERAL, "1"))))),
declaration("trace { major = 1; };"));
Matches(Node(CTFParser.TRACE,
Node(CTFParser.CTF_EXPRESSION_TYPE,
Node(CTFParser.CTF_LEFT,
Node(CTFParser.UNARY_EXPRESSION_STRING,
Node(CTFParser.IDENTIFIER, "packet")),
Node(CTFParser.DOT,
Node(CTFParser.UNARY_EXPRESSION_STRING,
Node(CTFParser.IDENTIFIER, "header")))),
Node(CTFParser.CTF_RIGHT,
Node(CTFParser.TYPE_SPECIFIER_LIST,
Node(CTFParser.STRUCT,
Node(CTFParser.STRUCT_NAME,
Node(CTFParser.IDENTIFIER, "dummy"))))))),
declaration("trace { packet.header := struct dummy; };"));
/* TODO: This test crash the parser. */
Matches(Node(CTFParser.TRACE,
All()),
declaration("trace { typedef x y; };"));
Matches(Node(CTFParser.TRACE,
Node(CTFParser.CTF_EXPRESSION_VAL, All(), All()),
Node(CTFParser.CTF_EXPRESSION_VAL, All(), All()),
Node(CTFParser.CTF_EXPRESSION_TYPE, All(), All())),
declaration("trace { major = 1; minor = 1;"
+ "packet.header := struct dummy; };"));
}
}