blob: a73dd42a9ef11e90b9faa2396e9c536e597c5406 [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 static com.google.devtools.build.lib.testutil.MoreAsserts.assertThrows;
import com.google.common.collect.ImmutableList;
import com.google.common.collect.ImmutableSortedMap;
import com.google.devtools.build.lib.bazel.rules.ninja.file.ByteBufferFragment;
import com.google.devtools.build.lib.bazel.rules.ninja.file.GenericParsingException;
import com.google.devtools.build.lib.bazel.rules.ninja.lexer.NinjaLexer;
import com.google.devtools.build.lib.bazel.rules.ninja.parser.NinjaParser;
import com.google.devtools.build.lib.bazel.rules.ninja.parser.NinjaRule;
import com.google.devtools.build.lib.bazel.rules.ninja.parser.NinjaRuleVariable;
import com.google.devtools.build.lib.bazel.rules.ninja.parser.NinjaScope;
import com.google.devtools.build.lib.bazel.rules.ninja.parser.NinjaVariableValue;
import com.google.devtools.build.lib.util.Pair;
import java.nio.ByteBuffer;
import java.nio.charset.StandardCharsets;
import java.util.List;
import java.util.stream.Collectors;
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.parser.NinjaScope} */
@RunWith(JUnit4.class)
public class NinjaScopeTest {
@Test
public void testSortVariables() {
NinjaScope scope = new NinjaScope();
scope.addVariable("abc", 12, NinjaVariableValue.createPlainText("cba"));
scope.addVariable("abc", 1, NinjaVariableValue.createPlainText("cba1"));
scope.addVariable("abc", 14, NinjaVariableValue.createPlainText("cba2"));
scope.sortResults();
List<Integer> offsets =
scope.getVariables().get("abc").stream().map(Pair::getFirst).collect(Collectors.toList());
assertThat(offsets).isInOrder();
}
@Test
public void testSortRules() {
// We can just use the same rule value here.
NinjaRule rule = rule("rule1");
NinjaScope scope = new NinjaScope();
scope.addRule(10, rule);
scope.addRule(1115, rule);
scope.addRule(5, rule);
scope.sortResults();
List<Integer> offsets =
scope.getRules().get(rule.getName()).stream()
.map(Pair::getFirst)
.collect(Collectors.toList());
assertThat(offsets).isInOrder();
}
@Test
public void testMerge() {
NinjaRule rule1 = rule("rule1");
NinjaRule rule2 = rule("rule2");
NinjaScope scope1 = new NinjaScope();
scope1.addRule(10, rule1);
scope1.addVariable("from1", 7, NinjaVariableValue.createPlainText("111"));
scope1.addVariable("abc", 5, NinjaVariableValue.createPlainText("5"));
scope1.addVariable("abc", 115, NinjaVariableValue.createPlainText("7"));
NinjaScope scope2 = new NinjaScope();
scope2.addRule(10, rule2);
scope2.addVariable("from2", 20017, NinjaVariableValue.createPlainText("222"));
scope2.addVariable("abc", 2005, NinjaVariableValue.createPlainText("15"));
scope2.addVariable("abc", 20015, NinjaVariableValue.createPlainText("17"));
NinjaScope result = NinjaScope.mergeScopeParts(ImmutableList.of(scope1, scope2));
assertThat(result.getRules()).hasSize(2);
assertThat(result.getRules()).containsKey("rule1");
assertThat(result.getRules()).containsKey("rule2");
assertThat(result.getVariables()).hasSize(3);
assertThat(result.getVariables()).containsKey("from1");
assertThat(result.getVariables()).containsKey("from2");
assertThat(result.getVariables()).containsKey("abc");
List<Pair<Integer, NinjaVariableValue>> abc = result.getVariables().get("abc");
assertThat(abc).hasSize(4);
assertThat(abc.stream().map(Pair::getFirst).collect(Collectors.toList())).isInOrder();
}
@Test
public void testFindVariable() {
NinjaScope scope = new NinjaScope();
scope.addVariable("abc", 12, NinjaVariableValue.createPlainText("cba"));
scope.addVariable("abc", 5, NinjaVariableValue.createPlainText("cba1"));
scope.addVariable("abc", 14, NinjaVariableValue.createPlainText("cba2"));
scope.sortResults();
assertThat(scope.findVariable(1, "not_there")).isNull();
assertThat(scope.findVariable(1, "abc")).isNull();
NinjaVariableValue abc = scope.findVariable(6, "abc");
assertThat(abc).isNotNull();
assertThat(abc.getRawText()).isEqualTo("cba1");
abc = scope.findVariable(13, "abc");
assertThat(abc).isNotNull();
assertThat(abc.getRawText()).isEqualTo("cba");
abc = scope.findVariable(130, "abc");
assertThat(abc).isNotNull();
assertThat(abc.getRawText()).isEqualTo("cba2");
}
@Test
public void testFindVariableErrors() {
NinjaScope scope = new NinjaScope();
scope.addVariable("abc", 12, NinjaVariableValue.createPlainText("cba"));
scope.addVariable("abc", 5, NinjaVariableValue.createPlainText("cba1"));
scope.addVariable("abc", 14, NinjaVariableValue.createPlainText("cba2"));
scope.sortResults();
IllegalStateException exception =
assertThrows(IllegalStateException.class, () -> scope.findVariable(5, "abc"));
assertThat(exception)
.hasMessageThat()
.isEqualTo("Trying to interpret declaration as reference.");
}
@Test
public void testFindRule() {
NinjaScope scope = new NinjaScope();
scope.addRule(10, rule("rule1", "10"));
scope.addRule(1115, rule("rule1", "1115"));
scope.addRule(5, rule("rule1", "5"));
scope.sortResults();
assertThat(scope.findRule(1, "non-existent")).isNull();
assertThat(scope.findRule(1, "rule1")).isNull();
NinjaRule rule1 = scope.findRule(6, "rule1");
assertThat(rule1).isNotNull();
assertThat(rule1.getVariables().get(NinjaRuleVariable.COMMAND).getRawText()).isEqualTo("5");
rule1 = scope.findRule(15, "rule1");
assertThat(rule1).isNotNull();
assertThat(rule1.getVariables().get(NinjaRuleVariable.COMMAND).getRawText()).isEqualTo("10");
}
@Test
public void testFindVariableInParentScope() {
NinjaScope parent = new NinjaScope();
parent.addVariable("abc", 12, NinjaVariableValue.createPlainText("abc"));
parent.addVariable("edf", 120, NinjaVariableValue.createPlainText("edf"));
parent.addVariable("xyz", 1000, NinjaVariableValue.createPlainText("xyz"));
// This is subninja scope, not include scope.
NinjaScope child = new NinjaScope(parent, 140);
// Shadows this variable from parent.
child.addVariable("edf", 1, NinjaVariableValue.createPlainText("11111"));
NinjaVariableValue abcVar = child.findVariable(2, "abc");
assertThat(abcVar).isNotNull();
assertThat(abcVar.getRawText()).isEqualTo("abc");
NinjaVariableValue edfVar = child.findVariable(2, "edf");
assertThat(edfVar).isNotNull();
assertThat(edfVar.getRawText()).isEqualTo("11111");
assertThat(child.findVariable(2, "xyz")).isNull();
}
@Test
public void testFindVariableInIncludedScope() {
NinjaScope parent = new NinjaScope();
parent.addVariable("abc", 12, NinjaVariableValue.createPlainText("abc"));
parent.addVariable("edf", 120, NinjaVariableValue.createPlainText("edf"));
parent.addVariable("xyz", 1000, NinjaVariableValue.createPlainText("xyz"));
NinjaScope child = parent.createIncludeScope(140);
// Shadows this variable from parent.
child.addVariable("edf", 1, NinjaVariableValue.createPlainText("11111"));
child.addVariable("child", 2, NinjaVariableValue.createPlainText("child"));
NinjaScope child2 = parent.createIncludeScope(200);
child2.addVariable("edf", 1, NinjaVariableValue.createPlainText("22222"));
NinjaVariableValue edfVar = parent.findVariable(160, "edf");
assertThat(edfVar).isNotNull();
assertThat(edfVar.getRawText()).isEqualTo("11111");
NinjaVariableValue edfVarFromChild2 = parent.findVariable(220, "edf");
assertThat(edfVarFromChild2).isNotNull();
assertThat(edfVarFromChild2.getRawText()).isEqualTo("22222");
NinjaVariableValue edfVarBefore = parent.findVariable(125, "edf");
assertThat(edfVarBefore).isNotNull();
assertThat(edfVarBefore.getRawText()).isEqualTo("edf");
NinjaVariableValue childVar = parent.findVariable(145, "child");
assertThat(childVar).isNotNull();
assertThat(childVar.getRawText()).isEqualTo("child");
}
@Test
public void testFindInRecursivelyIncluded() {
NinjaScope parent = new NinjaScope();
parent.addVariable("abc", 12, NinjaVariableValue.createPlainText("abc"));
parent.addVariable("edf", 120, NinjaVariableValue.createPlainText("edf"));
parent.addVariable("xyz", 1000, NinjaVariableValue.createPlainText("xyz"));
NinjaScope child = parent.createIncludeScope(140);
// Shadows this variable from parent.
child.addVariable("edf", 1, NinjaVariableValue.createPlainText("11111"));
child.addVariable("child", 2, NinjaVariableValue.createPlainText("child"));
NinjaScope child2 = child.createIncludeScope(3);
child2.addVariable("edf", 1, NinjaVariableValue.createPlainText("22222"));
NinjaVariableValue edfVarFromChild2 = parent.findVariable(220, "edf");
assertThat(edfVarFromChild2).isNotNull();
assertThat(edfVarFromChild2.getRawText()).isEqualTo("22222");
}
@Test
public void testVariableExpand() throws GenericParsingException {
NinjaScope scope = new NinjaScope();
scope.addVariable("abc", 12, NinjaVariableValue.createPlainText("abc"));
scope.addVariable("edf", 120, parseValue("=> $abc = ?"));
scope.addVariable("abc", 130, NinjaVariableValue.createPlainText("redefined"));
scope.addVariable("edf", 180, parseValue("now$: $abc!"));
scope.expandVariables();
assertThat(scope.findExpandedVariable(15, "abc")).isEqualTo("abc");
assertThat(scope.findExpandedVariable(150, "edf")).isEqualTo("=> abc = ?");
assertThat(scope.findExpandedVariable(140, "abc")).isEqualTo("redefined");
assertThat(scope.findExpandedVariable(181, "edf")).isEqualTo("now: redefined!");
}
@Test
public void testExpandWithParentChild() throws GenericParsingException {
NinjaScope parent = new NinjaScope();
parent.addVariable("abc", 12, NinjaVariableValue.createPlainText("abc"));
parent.addVariable("edf", 120, parseValue("$abc === ${ abc }"));
NinjaScope includeScope = parent.createIncludeScope(140);
includeScope.addVariable("included", 1, parseValue("<$abc and ${ edf }>"));
NinjaScope child = new NinjaScope(parent, 150);
child.addVariable("subninja", 2, parseValue("$edf = ${ included }*"));
parent.expandVariables();
child.expandVariables();
assertThat(includeScope.findExpandedVariable(2, "included")).isEqualTo("<abc and abc === abc>");
assertThat(child.findExpandedVariable(3, "subninja"))
.isEqualTo("abc === abc = <abc and abc === abc>*");
assertThat(parent.findExpandedVariable(150, "included")).isEqualTo("<abc and abc === abc>");
}
private static NinjaRule rule(String name) {
return rule(name, "command");
}
private static NinjaRule rule(String name, String command) {
return new NinjaRule(
ImmutableSortedMap.of(
NinjaRuleVariable.NAME, NinjaVariableValue.createPlainText(name),
NinjaRuleVariable.COMMAND, NinjaVariableValue.createPlainText(command)));
}
private static NinjaVariableValue parseValue(String text) throws GenericParsingException {
ByteBuffer bb = ByteBuffer.wrap(text.getBytes(StandardCharsets.ISO_8859_1));
NinjaLexer lexer = new NinjaLexer(new ByteBufferFragment(bb, 0, bb.limit()));
return new NinjaParser(lexer).parseVariableValue("test");
}
}