aboutsummaryrefslogtreecommitdiffhomepage
path: root/src/test/java
diff options
context:
space:
mode:
authorGravatar brandjon <brandjon@google.com>2017-06-27 18:14:54 +0200
committerGravatar Marcel Hlopko <hlopko@google.com>2017-06-28 10:17:45 +0200
commite2ffd5d3eb8a24efc26568543ae32637c6d26380 (patch)
tree63ec3358f98aa758b8d2a83c3d5e6218e6a37c03 /src/test/java
parent3bc1547a5cd78bd6639d968b3cfddbe26b1c31d5 (diff)
Add a pretty printer for Skylark ASTs
This can be used to canonically compare ASTs for equality, e.g. in tests. RELNOTES: None PiperOrigin-RevId: 160283160
Diffstat (limited to 'src/test/java')
-rw-r--r--src/test/java/com/google/devtools/build/lib/syntax/ASTNodeTest.java3
-rw-r--r--src/test/java/com/google/devtools/build/lib/syntax/ASTPrettyPrintTest.java432
-rw-r--r--src/test/java/com/google/devtools/build/lib/syntax/EvaluationTest.java8
3 files changed, 435 insertions, 8 deletions
diff --git a/src/test/java/com/google/devtools/build/lib/syntax/ASTNodeTest.java b/src/test/java/com/google/devtools/build/lib/syntax/ASTNodeTest.java
index 877bbe5653..651c1f11b7 100644
--- a/src/test/java/com/google/devtools/build/lib/syntax/ASTNodeTest.java
+++ b/src/test/java/com/google/devtools/build/lib/syntax/ASTNodeTest.java
@@ -15,6 +15,7 @@ package com.google.devtools.build.lib.syntax;
import static org.junit.Assert.fail;
+import java.io.IOException;
import org.junit.Before;
import org.junit.Test;
import org.junit.runner.RunWith;
@@ -32,6 +33,8 @@ public class ASTNodeTest {
public final void createNode() throws Exception {
node = new ASTNode() {
@Override
+ public void prettyPrint(Appendable buffer, int indentLevel) throws IOException {}
+ @Override
public String toString() {
return null;
}
diff --git a/src/test/java/com/google/devtools/build/lib/syntax/ASTPrettyPrintTest.java b/src/test/java/com/google/devtools/build/lib/syntax/ASTPrettyPrintTest.java
new file mode 100644
index 0000000000..7c7b02acfa
--- /dev/null
+++ b/src/test/java/com/google/devtools/build/lib/syntax/ASTPrettyPrintTest.java
@@ -0,0 +1,432 @@
+// Copyright 2017 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.syntax;
+
+import static com.google.common.truth.Truth.assertThat;
+import static com.google.devtools.build.lib.syntax.Parser.ParsingLevel.LOCAL_LEVEL;
+import static com.google.devtools.build.lib.syntax.Parser.ParsingLevel.TOP_LEVEL;
+
+import com.google.common.base.Joiner;
+import com.google.common.collect.ImmutableMap;
+import com.google.devtools.build.lib.syntax.util.EvaluationTestCase;
+import java.io.IOException;
+import org.junit.Test;
+import org.junit.runner.RunWith;
+import org.junit.runners.JUnit4;
+
+/** Tests the {@code toString} and pretty printing methods for {@link ASTNode} subclasses. */
+@RunWith(JUnit4.class)
+public class ASTPrettyPrintTest extends EvaluationTestCase {
+
+ private String join(String... lines) {
+ return Joiner.on("\n").join(lines);
+ }
+
+ /**
+ * Asserts that the given node's pretty print at a given indent level matches the given string.
+ */
+ private void assertPrettyMatches(ASTNode node, int indentLevel, String expected) {
+ StringBuilder prettyBuilder = new StringBuilder();
+ try {
+ node.prettyPrint(prettyBuilder, indentLevel);
+ } catch (IOException e) {
+ // Impossible for StringBuilder.
+ throw new AssertionError(e);
+ }
+ assertThat(prettyBuilder.toString()).isEqualTo(expected);
+ }
+
+ /** Asserts that the given node's pretty print with no indent matches the given string. */
+ private void assertPrettyMatches(ASTNode node, String expected) {
+ assertPrettyMatches(node, 0, expected);
+ }
+
+ /** Asserts that the given node's pretty print with one indent matches the given string. */
+ private void assertIndentedPrettyMatches(ASTNode node, String expected) {
+ assertPrettyMatches(node, 1, expected);
+ }
+
+ /** Asserts that the given node's {@code toString} matches the given string. */
+ private void assertTostringMatches(ASTNode node, String expected) {
+ assertThat(node.toString()).isEqualTo(expected);
+ }
+
+ /**
+ * Parses the given string as an expression, and asserts that its pretty print matches the given
+ * string.
+ */
+ private void assertExprPrettyMatches(String source, String expected) {
+ Expression node = parseExpression(source);
+ assertPrettyMatches(node, expected);
+ }
+
+ /**
+ * Parses the given string as an expression, and asserts that its {@code toString} matches the
+ * given string.
+ */
+ private void assertExprTostringMatches(String source, String expected) {
+ Expression node = parseExpression(source);
+ assertThat(node.toString()).isEqualTo(expected);
+ }
+
+ /**
+ * Parses the given string as an expression, and asserts that both its pretty print and {@code
+ * toString} return the original string.
+ */
+ private void assertExprBothRoundTrip(String source) {
+ assertExprPrettyMatches(source, source);
+ assertExprTostringMatches(source, source);
+ }
+
+ /**
+ * Parses the given string as a statement, and asserts that its pretty print with one indent
+ * matches the given string.
+ */
+ private void assertStmtIndentedPrettyMatches(
+ Parser.ParsingLevel parsingLevel, String source, String expected) {
+ Statement node = parseStatement(parsingLevel, source);
+ assertIndentedPrettyMatches(node, expected);
+ }
+
+ /**
+ * Parses the given string as an statement, and asserts that its {@code toString} matches the
+ * given string.
+ */
+ private void assertStmtTostringMatches(
+ Parser.ParsingLevel parsingLevel, String source, String expected) {
+ Statement node = parseStatement(parsingLevel, source);
+ assertThat(node.toString()).isEqualTo(expected);
+ }
+
+ // Expressions.
+
+ @Test
+ public void abstractComprehension() {
+ // Covers DictComprehension and ListComprehension.
+ assertExprBothRoundTrip("[z for y in x if True for z in y]");
+ assertExprBothRoundTrip("{z: x for y in x if True for z in y}");
+ }
+
+ @Test
+ public void binaryOperatorExpression() {
+ assertExprPrettyMatches("1 + 2", "(1 + 2)");
+ assertExprTostringMatches("1 + 2", "1 + 2");
+
+ assertExprPrettyMatches("1 + (2 * 3)", "(1 + (2 * 3))");
+ assertExprTostringMatches("1 + (2 * 3)", "1 + 2 * 3");
+ }
+
+ @Test
+ public void conditionalExpression() {
+ assertExprBothRoundTrip("1 if True else 2");
+ }
+
+ @Test
+ public void dictionaryLiteral() {
+ assertExprBothRoundTrip("{1: \"a\", 2: \"b\"}");
+ }
+
+ @Test
+ public void dotExpression() {
+ assertExprBothRoundTrip("o.f");
+ }
+
+ @Test
+ public void funcallExpression() {
+ assertExprBothRoundTrip("f()");
+ assertExprBothRoundTrip("f(a)");
+ assertExprBothRoundTrip("f(a, b = B, *c, d = D, **e)");
+ assertExprBothRoundTrip("o.f()");
+ }
+
+ @Test
+ public void identifier() {
+ assertExprBothRoundTrip("foo");
+ }
+
+ @Test
+ public void indexExpression() {
+ assertExprBothRoundTrip("a[i]");
+ }
+
+ @Test
+ public void integerLiteral() {
+ assertExprBothRoundTrip("5");
+ }
+
+ @Test
+ public void listLiteralShort() {
+ assertExprBothRoundTrip("[]");
+ assertExprBothRoundTrip("[5]");
+ assertExprBothRoundTrip("[5, 6]");
+ assertExprBothRoundTrip("()");
+ assertExprBothRoundTrip("(5,)");
+ assertExprBothRoundTrip("(5, 6)");
+ }
+
+ @Test
+ public void listLiteralLong() {
+ // List literals with enough elements to trigger the abbreviated toString() format.
+ assertExprPrettyMatches("[1, 2, 3, 4, 5, 6]", "[1, 2, 3, 4, 5, 6]");
+ assertExprTostringMatches("[1, 2, 3, 4, 5, 6]", "[1, 2, 3, 4, <2 more arguments>]");
+
+ assertExprPrettyMatches("(1, 2, 3, 4, 5, 6)", "(1, 2, 3, 4, 5, 6)");
+ assertExprTostringMatches("(1, 2, 3, 4, 5, 6)", "(1, 2, 3, 4, <2 more arguments>)");
+ }
+
+ @Test
+ public void listLiteralNested() {
+ // Make sure that the inner list doesn't get abbreviated when the outer list is printed using
+ // prettyPrint().
+ assertExprPrettyMatches(
+ "[1, 2, 3, [10, 20, 30, 40, 50, 60], 4, 5, 6]",
+ "[1, 2, 3, [10, 20, 30, 40, 50, 60], 4, 5, 6]");
+ // It doesn't matter as much what toString does. This case demonstrates an apparent bug in how
+ // Printer#printList abbreviates the nested contents. We can keep this test around to help
+ // monitor changes in the buggy behavior or eventually fix it.
+ assertExprTostringMatches(
+ "[1, 2, 3, [10, 20, 30, 40, 50, 60], 4, 5, 6]",
+ "[1, 2, 3, [10, 20, 30, 40, <2 more argu...<2 more arguments>], <3 more arguments>]");
+ }
+
+ @Test
+ public void sliceExpression() {
+ assertExprBothRoundTrip("a[b:c:d]");
+ assertExprBothRoundTrip("a[b:c]");
+ assertExprBothRoundTrip("a[b:]");
+ assertExprBothRoundTrip("a[:c:d]");
+ assertExprBothRoundTrip("a[:c]");
+ assertExprBothRoundTrip("a[::d]");
+ assertExprBothRoundTrip("a[:]");
+ }
+
+ @Test
+ public void stringLiteral() {
+ assertExprBothRoundTrip("\"foo\"");
+ assertExprBothRoundTrip("\"quo\\\"ted\"");
+ }
+
+ @Test
+ public void unaryOperatorExpression() {
+ assertExprPrettyMatches("not True", "not (True)");
+ assertExprTostringMatches("not True", "not True");
+ assertExprPrettyMatches("-5", "-(5)");
+ assertExprTostringMatches("-5", "-5");
+ }
+
+ // Statements.
+
+ @Test
+ public void assignmentStatement() {
+ assertStmtIndentedPrettyMatches(LOCAL_LEVEL, "x = y", " x = y\n");
+ assertStmtTostringMatches(LOCAL_LEVEL, "x = y", "x = y\n");
+ }
+
+ @Test
+ public void augmentedAssignmentStatement() {
+ assertStmtIndentedPrettyMatches(LOCAL_LEVEL, "x += y", " x += y\n");
+ assertStmtTostringMatches(LOCAL_LEVEL, "x += y", "x += y\n");
+ }
+
+ @Test
+ public void expressionStatement() {
+ assertStmtIndentedPrettyMatches(LOCAL_LEVEL, "5", " 5\n");
+ assertStmtTostringMatches(LOCAL_LEVEL, "5", "5\n");
+ }
+
+ @Test
+ public void functionDefStatement() {
+ assertStmtIndentedPrettyMatches(
+ TOP_LEVEL,
+ join("def f(x):",
+ " print(x)"),
+ join(" def f(x):",
+ " print(x)",
+ ""));
+ assertStmtTostringMatches(
+ TOP_LEVEL,
+ join("def f(x):",
+ " print(x)"),
+ "def f(x): ...\n");
+
+ assertStmtIndentedPrettyMatches(
+ TOP_LEVEL,
+ join("def f(a, b=B, *c, d=D, **e):",
+ " print(x)"),
+ join(" def f(a, b=B, *c, d=D, **e):",
+ " print(x)",
+ ""));
+ assertStmtTostringMatches(
+ TOP_LEVEL,
+ join("def f(a, b=B, *c, d=D, **e):",
+ " print(x)"),
+ "def f(a, b = B, *c, d = D, **e): ...\n");
+
+ assertStmtIndentedPrettyMatches(
+ TOP_LEVEL,
+ join("def f():",
+ " pass"),
+ join(" def f():",
+ " pass",
+ ""));
+ assertStmtTostringMatches(
+ TOP_LEVEL,
+ join("def f():",
+ " pass"),
+ "def f(): ...\n");
+ }
+
+ @Test
+ public void flowStatement() {
+ // The parser would complain if we tried to construct them from source.
+ ASTNode breakNode = new FlowStatement(FlowStatement.Kind.BREAK);
+ assertIndentedPrettyMatches(breakNode, " break\n");
+ assertTostringMatches(breakNode, "break\n");
+
+ ASTNode continueNode = new FlowStatement(FlowStatement.Kind.CONTINUE);
+ assertIndentedPrettyMatches(continueNode, " continue\n");
+ assertTostringMatches(continueNode, "continue\n");
+ }
+
+ @Test
+ public void forStatement() {
+ assertStmtIndentedPrettyMatches(
+ LOCAL_LEVEL,
+ join("for x in y:",
+ " print(x)"),
+ join(" for x in y:",
+ " print(x)",
+ ""));
+ assertStmtTostringMatches(
+ LOCAL_LEVEL,
+ join("for x in y:",
+ " print(x)"),
+ "for x in y: ...\n");
+
+ assertStmtIndentedPrettyMatches(
+ LOCAL_LEVEL,
+ join("for x in y:",
+ " pass"),
+ join(" for x in y:",
+ " pass",
+ ""));
+ assertStmtTostringMatches(
+ LOCAL_LEVEL,
+ join("for x in y:",
+ " pass"),
+ "for x in y: ...\n");
+ }
+
+ @Test
+ public void ifStatement() {
+ assertStmtIndentedPrettyMatches(
+ LOCAL_LEVEL,
+ join("if True:",
+ " print(x)"),
+ join(" if True:",
+ " print(x)",
+ ""));
+ assertStmtTostringMatches(
+ LOCAL_LEVEL,
+ join("if True:",
+ " print(x)"),
+ "if True: ...\n");
+
+ assertStmtIndentedPrettyMatches(
+ LOCAL_LEVEL,
+ join("if True:",
+ " print(x)",
+ "elif False:",
+ " print(y)",
+ "else:",
+ " print(z)"),
+ join(" if True:",
+ " print(x)",
+ " elif False:",
+ " print(y)",
+ " else:",
+ " print(z)",
+ ""));
+ assertStmtTostringMatches(
+ LOCAL_LEVEL,
+ join("if True:",
+ " print(x)",
+ "elif False:",
+ " print(y)",
+ "else:",
+ " print(z)"),
+ "if True: ...\n");
+ }
+
+ @Test
+ public void loadStatement() {
+ // load("foo.bzl", a="A", "B")
+ ASTNode loadStatement = new LoadStatement(
+ new StringLiteral("foo.bzl"),
+ ImmutableMap.of(new Identifier("a"), "A", new Identifier("B"), "B"));
+ assertIndentedPrettyMatches(
+ loadStatement,
+ " load(\"foo.bzl\", a=\"A\", \"B\")\n");
+ assertTostringMatches(
+ loadStatement,
+ "load(\"foo.bzl\", a=\"A\", \"B\")\n");
+ }
+
+ @Test
+ public void returnStatement() {
+ assertIndentedPrettyMatches(
+ new ReturnStatement(new StringLiteral("foo")),
+ " return \"foo\"\n");
+ assertTostringMatches(
+ new ReturnStatement(new StringLiteral("foo")),
+ "return \"foo\"\n");
+
+ assertIndentedPrettyMatches(
+ new ReturnStatement(new Identifier("None")),
+ " return\n");
+ assertTostringMatches(
+ new ReturnStatement(new Identifier("None")),
+ "return\n");
+ }
+
+ // Miscellaneous.
+
+ @Test
+ public void buildFileAST() {
+ ASTNode node = parseBuildFileASTWithoutValidation("print(x)\nprint(y)");
+ assertIndentedPrettyMatches(
+ node,
+ join(" print(x)",
+ " print(y)",
+ ""));
+ assertTostringMatches(
+ node,
+ "<BuildFileAST with 2 statements>");
+ }
+
+ @Test
+ public void comment() {
+ Comment node = new Comment("foo");
+ assertIndentedPrettyMatches(node, " # foo");
+ assertTostringMatches(node, "foo");
+ }
+
+ /* Not tested explicitly because they're covered implicitly by tests for other nodes:
+ * - LValue
+ * - DictionaryEntryLiteral
+ * - passed arguments / formal parameters
+ * - ConditionalStatements
+ */
+}
diff --git a/src/test/java/com/google/devtools/build/lib/syntax/EvaluationTest.java b/src/test/java/com/google/devtools/build/lib/syntax/EvaluationTest.java
index 606c4ceee8..afaf9aaab7 100644
--- a/src/test/java/com/google/devtools/build/lib/syntax/EvaluationTest.java
+++ b/src/test/java/com/google/devtools/build/lib/syntax/EvaluationTest.java
@@ -426,14 +426,6 @@ public class EvaluationTest extends EvaluationTestCase {
}
@Test
- public void testDictComprehensions_ToString() throws Exception {
- assertThat(parseExpression("{x : x for x in [1, 2]}").toString())
- .isEqualTo("{x: x for x in [1, 2]}");
- assertThat(parseExpression("{x + 'a' : x for x in [1, 2]}").toString())
- .isEqualTo("{x + \"a\": x for x in [1, 2]}");
- }
-
- @Test
public void testListConcatenation() throws Exception {
newTest()
.testStatement("[1, 2] + [3, 4]", MutableList.of(env, 1, 2, 3, 4))