blob: 7ea35cdefa46e9fac82fb985c6d6ba4e03e38884 [file] [log] [blame]
// Copyright 2019 The Bazel Authors. All rights reserved.
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
// http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.
//
package com.google.devtools.build.lib.bazel.rules.ninja;
import static com.google.common.truth.Truth.assertThat;
import com.google.devtools.build.lib.bazel.rules.ninja.file.ByteBufferFragment;
import com.google.devtools.build.lib.bazel.rules.ninja.lexer.NinjaLexer;
import com.google.devtools.build.lib.bazel.rules.ninja.lexer.NinjaLexer.TextKind;
import com.google.devtools.build.lib.bazel.rules.ninja.lexer.NinjaToken;
import java.nio.ByteBuffer;
import java.nio.charset.StandardCharsets;
import javax.annotation.Nullable;
import org.junit.Test;
import org.junit.runner.RunWith;
import org.junit.runners.JUnit4;
/** Tests for {@link com.google.devtools.build.lib.bazel.rules.ninja.lexer.NinjaLexer}. */
@RunWith(JUnit4.class)
public class NinjaLexerTest {
@Test
public void testReadIdentifiersAndVariables() {
String text = "abc efg $fg ${ghf}\ntext one ${ more.1-d_f } $abc.def";
NinjaLexer lexer = createLexer(text);
assertTokenBytes(lexer, NinjaToken.IDENTIFIER, "abc");
assertTokenBytes(lexer, NinjaToken.IDENTIFIER, "efg");
assertTokenBytes(lexer, NinjaToken.VARIABLE, "$fg");
assertTokenBytes(lexer, NinjaToken.VARIABLE, "${ghf}");
assertTokenBytes(lexer, NinjaToken.NEWLINE, null);
assertTokenBytes(lexer, NinjaToken.IDENTIFIER, "text");
assertTokenBytes(lexer, NinjaToken.IDENTIFIER, "one");
assertTokenBytes(lexer, NinjaToken.VARIABLE, "${ more.1-d_f }");
assertTokenBytes(lexer, NinjaToken.VARIABLE, "$abc");
assertTokenBytes(lexer, NinjaToken.IDENTIFIER, ".def");
}
@Test
public void testNewlines() {
String text = "a\nb $\nnot-newline$$\nnewline\n\nand\r\none";
NinjaLexer lexer = createLexer(text);
assertTokenBytes(lexer, NinjaToken.IDENTIFIER, "a");
assertTokenBytes(lexer, NinjaToken.NEWLINE, null);
assertTokenBytes(lexer, NinjaToken.IDENTIFIER, "b");
assertTokenBytes(lexer, NinjaToken.IDENTIFIER, "not-newline");
assertTokenBytes(lexer, NinjaToken.ESCAPED_TEXT, "$$");
assertTokenBytes(lexer, NinjaToken.NEWLINE, null);
assertTokenBytes(lexer, NinjaToken.IDENTIFIER, "newline");
assertTokenBytes(lexer, NinjaToken.NEWLINE, null);
assertTokenBytes(lexer, NinjaToken.NEWLINE, null);
assertTokenBytes(lexer, NinjaToken.IDENTIFIER, "and");
assertTokenBytes(lexer, NinjaToken.NEWLINE, null);
assertTokenBytes(lexer, NinjaToken.IDENTIFIER, "one");
assertThat(lexer.hasNextToken()).isFalse();
}
@Test
public void testTabsAreAllowed() {
String text = "abc\n\tcde";
NinjaLexer lexer = createLexer(text);
assertTokenBytes(lexer, NinjaToken.IDENTIFIER, "abc");
assertTokenBytes(lexer, NinjaToken.NEWLINE, null);
assertTokenBytes(lexer, NinjaToken.INDENT, null);
assertTokenBytes(lexer, NinjaToken.IDENTIFIER, "cde");
}
@Test
public void testDisallowedSymbols() {
assertError(
createLexer("^"),
"Symbol '^' is not allowed in the identifier, the text fragment with the symbol:\n^\n",
"^");
}
@Test
public void testComments() {
String text =
"abc#immediately after\n#Start of the line $ not escaped in comment $"
+ "\nNot-comment# Finishing : = $ | ||";
NinjaLexer lexer = createLexer(text);
assertTokenBytes(lexer, NinjaToken.IDENTIFIER, "abc");
assertTokenBytes(lexer, NinjaToken.NEWLINE, null);
assertTokenBytes(lexer, NinjaToken.NEWLINE, null);
assertTokenBytes(lexer, NinjaToken.IDENTIFIER, "Not-comment");
}
@Test
public void testBadEscape() {
NinjaLexer lexer = createLexer("abc\nbad $");
assertTokenBytes(lexer, NinjaToken.IDENTIFIER, "abc");
assertTokenBytes(lexer, NinjaToken.NEWLINE, null);
assertTokenBytes(lexer, NinjaToken.IDENTIFIER, "bad");
assertError(lexer, "Bad $-escape (literal $ must be written as $$)", "$");
NinjaLexer lexer2 = createLexer("$$$");
assertTokenBytes(lexer2, NinjaToken.ESCAPED_TEXT, "$$");
assertError(lexer2, "Bad $-escape (literal $ must be written as $$)", "$");
}
@Test
public void testBadVariable() {
assertError(createLexer("${abc"), "Variable end symbol '}' expected.", "${abc");
assertError(createLexer("${abc "), "Variable end symbol '}' expected.", "${abc ");
assertError(createLexer("${}"), "Variable identifier expected.", "${}");
assertError(createLexer("${abc&}"), "Variable end symbol '}' expected.", "${abc&");
}
@Test
public void testKeywords() {
assertTokenBytes(createLexer("build"), NinjaToken.BUILD, null);
assertTokenBytes(createLexer("rule"), NinjaToken.RULE, null);
assertTokenBytes(createLexer("default "), NinjaToken.DEFAULT, null);
assertTokenBytes(createLexer("include"), NinjaToken.INCLUDE, null);
assertTokenBytes(createLexer("subninja\n"), NinjaToken.SUBNINJA, null);
assertTokenBytes(createLexer("pool "), NinjaToken.POOL, null);
}
@Test
public void testIndent() {
NinjaLexer lexer = createLexer(" a\nb\n c d e\n ");
// We want to know if there was a starting INDENT
// (though we suppose to start with a line without INDENT)
assertTokenBytes(lexer, NinjaToken.INDENT, null);
assertTokenBytes(lexer, NinjaToken.IDENTIFIER, "a");
assertTokenBytes(lexer, NinjaToken.NEWLINE, null);
assertTokenBytes(lexer, NinjaToken.IDENTIFIER, "b");
assertTokenBytes(lexer, NinjaToken.NEWLINE, null);
assertTokenBytes(lexer, NinjaToken.INDENT, null);
assertTokenBytes(lexer, NinjaToken.IDENTIFIER, "c");
assertTokenBytes(lexer, NinjaToken.IDENTIFIER, "d");
assertTokenBytes(lexer, NinjaToken.IDENTIFIER, "e");
assertTokenBytes(lexer, NinjaToken.NEWLINE, null);
assertTokenBytes(lexer, NinjaToken.INDENT, null);
}
@Test
public void testReadTextFragment() {
NinjaLexer lexer = createLexer("my.var=Any text ^&%=@&!*: $:symbols$\n aa\nmy.var2");
assertTokenBytes(lexer, NinjaToken.IDENTIFIER, "my.var");
assertTokenBytes(lexer, NinjaToken.EQUALS, null);
lexer.setExpectedTextKind(TextKind.TEXT);
assertTokenBytes(lexer, NinjaToken.TEXT, "Any");
assertTokenBytes(lexer, NinjaToken.TEXT, "text");
assertTokenBytes(lexer, NinjaToken.TEXT, "^&%=@&!*");
assertTokenBytes(lexer, NinjaToken.COLON, null);
assertTokenBytes(lexer, NinjaToken.ESCAPED_TEXT, "$:");
assertTokenBytes(lexer, NinjaToken.TEXT, "symbols");
assertTokenBytes(lexer, NinjaToken.TEXT, "aa");
assertTokenBytes(lexer, NinjaToken.NEWLINE, null);
assertTokenBytes(lexer, NinjaToken.IDENTIFIER, "my.var2");
}
@Test
public void testUndo() {
NinjaLexer lexer = createLexer("my.var=Any\n");
assertTokenBytes(lexer, NinjaToken.IDENTIFIER, "my.var");
assertTokenBytes(lexer, NinjaToken.EQUALS, null);
assertTokenBytes(lexer, NinjaToken.IDENTIFIER, "Any");
assertTokenBytes(lexer, NinjaToken.NEWLINE, null);
lexer.undo();
assertTokenBytes(lexer, NinjaToken.NEWLINE, null);
lexer.undo();
lexer.undo();
assertTokenBytes(lexer, NinjaToken.IDENTIFIER, "Any");
assertTokenBytes(lexer, NinjaToken.NEWLINE, null);
lexer.undo();
lexer.undo();
lexer.undo();
assertTokenBytes(lexer, NinjaToken.EQUALS, null);
assertTokenBytes(lexer, NinjaToken.IDENTIFIER, "Any");
assertTokenBytes(lexer, NinjaToken.NEWLINE, null);
lexer.undo();
lexer.undo();
lexer.undo();
lexer.undo();
assertTokenBytes(lexer, NinjaToken.IDENTIFIER, "my.var");
lexer.undo();
assertTokenBytes(lexer, NinjaToken.IDENTIFIER, "my.var");
assertTokenBytes(lexer, NinjaToken.EQUALS, null);
assertTokenBytes(lexer, NinjaToken.IDENTIFIER, "Any");
assertTokenBytes(lexer, NinjaToken.NEWLINE, null);
}
@Test
public void testSpecialSymbols() {
NinjaLexer lexer = createLexer("| || : = ");
assertTokenBytes(lexer, NinjaToken.PIPE, null);
assertTokenBytes(lexer, NinjaToken.PIPE2, null);
assertTokenBytes(lexer, NinjaToken.COLON, null);
assertTokenBytes(lexer, NinjaToken.EQUALS, null);
assertTokenBytes(lexer, NinjaToken.EOF, null);
assertThat(lexer.hasNextToken()).isFalse();
}
@Test
public void testZeroByte() {
byte[] bytes = {'a', 0, 'b'};
NinjaLexer lexer =
new NinjaLexer(new ByteBufferFragment(ByteBuffer.wrap(bytes), 0, bytes.length));
assertTokenBytes(lexer, NinjaToken.IDENTIFIER, null);
assertThat(lexer.hasNextToken()).isFalse();
}
private static void assertError(NinjaLexer lexer, String errorText, String errorHolder) {
assertThat(lexer.hasNextToken()).isTrue();
assertThat(lexer.nextToken()).isEqualTo(NinjaToken.ERROR);
assertThat(lexer.getError()).isEqualTo(errorText);
assertThat(lexer.getTokenBytes()).isEqualTo(errorHolder.getBytes(StandardCharsets.ISO_8859_1));
assertThat(lexer.hasNextToken()).isFalse();
}
private static void assertTokenBytes(NinjaLexer lexer, NinjaToken token, @Nullable String text) {
assertThat(lexer.hasNextToken()).isTrue();
assertThat(lexer.nextToken()).isEqualTo(token);
if (text != null) {
assertThat(lexer.getTokenBytes()).isEqualTo(text.getBytes(StandardCharsets.ISO_8859_1));
}
}
private static NinjaLexer createLexer(String text) {
ByteBuffer buffer = ByteBuffer.wrap(text.getBytes(StandardCharsets.ISO_8859_1));
return new NinjaLexer(new ByteBufferFragment(buffer, 0, buffer.limit()));
}
}