Index: mojom/mojom_parser/lexer/lexer_test.go |
diff --git a/mojom/mojom_parser/lexer/lexer_test.go b/mojom/mojom_parser/lexer/lexer_test.go |
index 1b378a77bbc61b8f178c6843d37b198ebc079c20..19f9aae6e4caebd3f7d90bbc002bd54aac947cd3 100644 |
--- a/mojom/mojom_parser/lexer/lexer_test.go |
+++ b/mojom/mojom_parser/lexer/lexer_test.go |
@@ -6,10 +6,207 @@ package lexer |
import "testing" |
-// TODO(rudominer) This dummy test is here in order to be able to test the |
-// go unit test infrastructure. It will eventually be replaced by a real test. |
-func TestDummyLexerTest(t *testing.T) { |
- if 5.1 > 2.1*3.1 { |
- t.Fatalf("Something is wrong.") |
+func checkEq(t *testing.T, expected, actual interface{}) { |
+ if expected != actual { |
+ t.Fatalf("Failed check: Expected (%v), Actual (%v)", expected, actual) |
} |
} |
+ |
+// pumpTokens pumps all the tokens from a channel into a slice. |
+func pumpTokens(tokensChan chan Token) []Token { |
+ tokens := []Token{} |
+ for token := range tokensChan { |
+ tokens = append(tokens, token) |
+ } |
+ return tokens |
+} |
+ |
+// TestAllSingleTokens tests for each token that a valid string is accepted as |
+// the correct token. |
+func TestAllSingleTokens(t *testing.T) { |
+ testData := []struct { |
+ source string |
+ token TokenKind |
+ }{ |
+ {"(", LParen}, |
+ {")", RParen}, |
+ {"[", LBracket}, |
+ {"]", RBracket}, |
+ {"{", LBrace}, |
+ {"}", RBrace}, |
+ {"<", LAngle}, |
+ {">", RAngle}, |
+ {";", Semi}, |
+ {",", Comma}, |
+ {".", Dot}, |
+ {"-", Minus}, |
+ {"+", Plus}, |
+ {"&", Amp}, |
+ {"?", Qstn}, |
+ {"=", Equals}, |
+ {"=>", Response}, |
+ {"somet_hi3ng", Name}, |
+ {"import", Import}, |
+ {"module", Module}, |
+ {"struct", Struct}, |
+ {"union", Union}, |
+ {"interface", Interface}, |
+ {"enum", Enum}, |
+ {"const", Const}, |
+ {"true", True}, |
+ {"false", False}, |
+ {"default", Default}, |
+ {"@10", Ordinal}, |
+ {"10", IntConstDec}, |
+ {"0", IntConstDec}, |
+ {"0xA10", IntConstHex}, |
+ {"0xa10", IntConstHex}, |
+ {"0XA10", IntConstHex}, |
+ {"0Xa10", IntConstHex}, |
+ {"10.5", FloatConst}, |
+ {"10e5", FloatConst}, |
+ {"0.5", FloatConst}, |
+ {"0e5", FloatConst}, |
+ {"10e+5", FloatConst}, |
+ {"10e-5", FloatConst}, |
+ {"\"hello world\"", StringLiteral}, |
+ {"\"hello \\\"real\\\" world\"", StringLiteral}, |
+ } |
+ |
+ for i := range testData { |
+ l := lexer{source: testData[i].source, tokens: make(chan Token)} |
+ go l.run() |
+ tokens := pumpTokens(l.tokens) |
+ |
+ if len(tokens) != 1 { |
+ t.Fatalf("Source('%v'): Expected 1 token but got %v instead: %v", |
+ testData[i].source, len(tokens), tokens) |
+ } |
+ |
+ checkEq(t, testData[i].source, tokens[0].Text) |
+ checkEq(t, testData[i].token, tokens[0].Kind) |
+ } |
+} |
+ |
+// TestTokenPosition tests that the position in the source string, the line |
+// number and the position in the line of the lexed token are correctly found. |
+func TestTokenPosition(t *testing.T) { |
+ source := " \n ." |
+ l := lexer{source: source, tokens: make(chan Token)} |
+ go l.run() |
+ tokens := pumpTokens(l.tokens) |
+ token := tokens[0] |
+ |
+ checkEq(t, 5, token.CharPos) |
+ checkEq(t, 1, token.LineNo) |
+ checkEq(t, 2, token.LinePos) |
+} |
+ |
+// TestTokenPositionChineseString tests that CharPos is expressed as a number |
+// of runes and not a number of bytes. |
+func TestTokenPositionChineseString(t *testing.T) { |
+ source := "\"您好\" is" |
+ ts := Tokenize(source) |
+ checkEq(t, StringLiteral, ts.PeekNext().Kind) |
+ ts.ConsumeNext() |
+ checkEq(t, 5, ts.PeekNext().CharPos) |
+} |
+ |
+// TestSkipSkippable tests that all skippable characters are skipped. |
+func TestSkipSkippable(t *testing.T) { |
+ source := " \t \r \n ." |
+ l := lexer{source: source, tokens: make(chan Token)} |
+ go l.run() |
+ tokens := pumpTokens(l.tokens) |
+ |
+ checkEq(t, Dot, tokens[0].Kind) |
+} |
+ |
+// TestTokenize tests that a single token embedded in a larger string is |
+// correctly lexed. |
+func TestTokenize(t *testing.T) { |
+ ts := Tokenize(" \t . ") |
+ token := ts.PeekNext() |
+ checkEq(t, Dot, token.Kind) |
+ |
+ ts.ConsumeNext() |
+ token = ts.PeekNext() |
+ checkEq(t, EOF, token.Kind) |
+} |
+ |
+// TestTokenizeBadUTF8String tests that an invalid UTF8 string is handled. |
+func TestTokenizeBadUTF8String(t *testing.T) { |
+ ts := Tokenize("\xF0") |
+ checkEq(t, ErrorIllegalChar, ts.PeekNext().Kind) |
+} |
+ |
+// TestTokenizeEmptyString tests that empty strings are handled correctly. |
+func TestTokenizeEmptyString(t *testing.T) { |
+ ts := Tokenize("") |
+ checkEq(t, EOF, ts.PeekNext().Kind) |
+} |
+ |
+// TestTokenizeMoreThanOne tests that more than one token is correctly lexed. |
+func TestTokenizeMoreThanOne(t *testing.T) { |
+ ts := Tokenize("()") |
+ checkEq(t, LParen, ts.PeekNext().Kind) |
+ ts.ConsumeNext() |
+ checkEq(t, RParen, ts.PeekNext().Kind) |
+ ts.ConsumeNext() |
+ checkEq(t, EOF, ts.PeekNext().Kind) |
+} |
+ |
+// TestIllegalChar tests that an illegal character is correctly spotted. |
+func TestIllegalChar(t *testing.T) { |
+ ts := Tokenize(" \t $ ") |
+ checkEq(t, ErrorIllegalChar, ts.PeekNext().Kind) |
+} |
+ |
+// TestUnterminatedStringLiteralEos tests that the correct error is emitted if |
+// a quoted string is never closed. |
+func TestUnterminatedStringLiteralEos(t *testing.T) { |
+ ts := Tokenize("\"hello world") |
+ checkEq(t, ErrorUnterminatedStringLiteral, ts.PeekNext().Kind) |
+} |
+ |
+// TestUnterminatedStringLiteralEol tests that the correct error is emitted if |
+// a quoted string is closed on a subsequent line. |
+func TestUnterminatedStringLiteralEol(t *testing.T) { |
+ ts := Tokenize("\"hello\n world\"") |
+ checkEq(t, ErrorUnterminatedStringLiteral, ts.PeekNext().Kind) |
+} |
+ |
+// TestSingleLineComment tests that single line comments are correctly skipped. |
+func TestSingleLineComment(t *testing.T) { |
+ ts := Tokenize("( // some stuff\n)") |
+ checkEq(t, LParen, ts.PeekNext().Kind) |
+ ts.ConsumeNext() |
+ checkEq(t, RParen, ts.PeekNext().Kind) |
+} |
+ |
+// TestMultiLineComment tests that multi line comments are correctly skipped. |
+func TestMultiLineComment(t *testing.T) { |
+ ts := Tokenize("( /* hello world/ * *\n */)") |
+ checkEq(t, LParen, ts.PeekNext().Kind) |
+ ts.ConsumeNext() |
+ checkEq(t, RParen, ts.PeekNext().Kind) |
+} |
+ |
+// TestUnterminatedMultiLineComment tests that unterminated multiline comments |
+// emit the correct error. |
+func TestUnterminatedMultiLineComment(t *testing.T) { |
+ ts := Tokenize("( /* hello world/ * *\n )") |
+ checkEq(t, LParen, ts.PeekNext().Kind) |
+ ts.ConsumeNext() |
+ checkEq(t, ErrorUnterminatedComment, ts.PeekNext().Kind) |
+} |
+ |
+// TestUnterminatedMultiLineCommentAtStar tests that if the string ends at a * |
+// (which could be the beginning of the close of a multiline comment) the right |
+// error is emitted. |
+func TestUnterminatedMultiLineCommentAtStar(t *testing.T) { |
+ ts := Tokenize("( /* hello world/ *") |
+ checkEq(t, LParen, ts.PeekNext().Kind) |
+ ts.ConsumeNext() |
+ checkEq(t, ErrorUnterminatedComment, ts.PeekNext().Kind) |
+} |