blob: 15d10af17424fcf183870f873270ece0ca9f8bda [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.common.collect.Maps;
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.parser.NinjaFileParseResult;
import com.google.devtools.build.lib.bazel.rules.ninja.parser.NinjaParserStep;
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 NinjaScope} */
@RunWith(JUnit4.class)
public class NinjaScopeTest {
@Test
public void testSortVariables() {
NinjaFileParseResult parseResult = new NinjaFileParseResult();
parseResult.addVariable("abc", 12, NinjaVariableValue.createPlainText("cba"));
parseResult.addVariable("abc", 1, NinjaVariableValue.createPlainText("cba1"));
parseResult.addVariable("abc", 14, NinjaVariableValue.createPlainText("cba2"));
parseResult.sortResults();
List<Integer> offsets =
parseResult.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");
NinjaFileParseResult parseResult = new NinjaFileParseResult();
parseResult.addRule(10, rule);
parseResult.addRule(1115, rule);
parseResult.addRule(5, rule);
parseResult.sortResults();
List<Integer> offsets =
parseResult.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");
NinjaFileParseResult parseResult1 = new NinjaFileParseResult();
parseResult1.addRule(10, rule1);
parseResult1.addVariable("from1", 7, NinjaVariableValue.createPlainText("111"));
parseResult1.addVariable("abc", 5, NinjaVariableValue.createPlainText("5"));
parseResult1.addVariable("abc", 115, NinjaVariableValue.createPlainText("7"));
NinjaFileParseResult parseResult2 = new NinjaFileParseResult();
parseResult2.addRule(10, rule2);
parseResult2.addVariable("from2", 20017, NinjaVariableValue.createPlainText("222"));
parseResult2.addVariable("abc", 2005, NinjaVariableValue.createPlainText("15"));
parseResult2.addVariable("abc", 20015, NinjaVariableValue.createPlainText("17"));
NinjaFileParseResult result =
NinjaFileParseResult.merge(ImmutableList.of(parseResult1, parseResult2));
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() throws Exception {
NinjaFileParseResult parseResult = new NinjaFileParseResult();
parseResult.addVariable("abc", 12, NinjaVariableValue.createPlainText("cba"));
parseResult.addVariable("abc", 5, NinjaVariableValue.createPlainText("cba1"));
parseResult.addVariable("abc", 14, NinjaVariableValue.createPlainText("cba2"));
parseResult.sortResults();
NinjaScope scope = new NinjaScope();
parseResult.expandIntoScope(scope, Maps.newHashMap());
assertThat(scope.findExpandedVariable(1, "not_there")).isNull();
assertThat(scope.findExpandedVariable(1, "abc")).isNull();
assertThat(scope.findExpandedVariable(6, "abc")).isEqualTo("cba1");
assertThat(scope.findExpandedVariable(13, "abc")).isEqualTo("cba");
assertThat(scope.findExpandedVariable(130, "abc")).isEqualTo("cba2");
}
@Test
public void testFindVariableErrors() throws Exception {
NinjaFileParseResult parseResult = new NinjaFileParseResult();
parseResult.addVariable("abc", 12, NinjaVariableValue.createPlainText("cba"));
parseResult.addVariable("abc", 5, NinjaVariableValue.createPlainText("cba1"));
parseResult.addVariable("abc", 14, NinjaVariableValue.createPlainText("cba2"));
parseResult.sortResults();
NinjaScope scope = new NinjaScope();
parseResult.expandIntoScope(scope, Maps.newHashMap());
IllegalStateException exception =
assertThrows(IllegalStateException.class, () -> scope.findExpandedVariable(5, "abc"));
assertThat(exception)
.hasMessageThat()
.isEqualTo("Trying to interpret declaration as reference.");
}
@Test
public void testFindRule() throws Exception {
NinjaFileParseResult parseResult = new NinjaFileParseResult();
parseResult.addRule(10, rule("rule1", "10"));
parseResult.addRule(1115, rule("rule1", "1115"));
parseResult.addRule(5, rule("rule1", "5"));
parseResult.sortResults();
NinjaScope scope = new NinjaScope();
parseResult.expandIntoScope(scope, Maps.newHashMap());
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() throws Exception {
NinjaFileParseResult parentParseResult = new NinjaFileParseResult();
parentParseResult.addVariable("abc", 12, NinjaVariableValue.createPlainText("abc"));
parentParseResult.addVariable("edf", 120, NinjaVariableValue.createPlainText("edf"));
parentParseResult.addVariable("xyz", 1000, NinjaVariableValue.createPlainText("xyz"));
// This is subninja scope, not include scope.
NinjaFileParseResult childParseResult = new NinjaFileParseResult();
parentParseResult.addSubNinjaScope(140, scope -> childParseResult);
// Shadows this variable from parent.
childParseResult.addVariable("edf", 1, NinjaVariableValue.createPlainText("11111"));
parentParseResult.sortResults();
NinjaScope scope = new NinjaScope();
parentParseResult.expandIntoScope(scope, Maps.newHashMap());
assertThat(scope.getSubNinjaScopes()).hasSize(1);
NinjaScope child = scope.getSubNinjaScopes().iterator().next();
assertThat(child.findExpandedVariable(2, "abc")).isEqualTo("abc");
assertThat(child.findExpandedVariable(2, "edf")).isEqualTo("11111");
assertThat(child.findExpandedVariable(2, "xyz")).isNull();
}
@Test
public void testfindExpandedVariableInIncludedScope() throws Exception {
NinjaFileParseResult parentParseResult = new NinjaFileParseResult();
parentParseResult.addVariable("abc", 12, NinjaVariableValue.createPlainText("abc"));
parentParseResult.addVariable("edf", 120, NinjaVariableValue.createPlainText("edf"));
parentParseResult.addVariable("xyz", 1000, NinjaVariableValue.createPlainText("xyz"));
NinjaFileParseResult childParseResult = new NinjaFileParseResult();
parentParseResult.addIncludeScope(140, scope -> childParseResult);
// Shadows this variable from parent.
childParseResult.addVariable("edf", 1, NinjaVariableValue.createPlainText("11111"));
childParseResult.addVariable("child", 2, NinjaVariableValue.createPlainText("child"));
NinjaFileParseResult childParseResult2 = new NinjaFileParseResult();
parentParseResult.addIncludeScope(200, scope -> childParseResult2);
childParseResult2.addVariable("edf", 1, NinjaVariableValue.createPlainText("22222"));
parentParseResult.sortResults();
NinjaScope scope = new NinjaScope();
parentParseResult.expandIntoScope(scope, Maps.newHashMap());
assertThat(scope.findExpandedVariable(160, "edf")).isEqualTo("11111");
assertThat(scope.findExpandedVariable(220, "edf")).isEqualTo("22222");
assertThat(scope.findExpandedVariable(125, "edf")).isEqualTo("edf");
assertThat(scope.findExpandedVariable(145, "child")).isEqualTo("child");
}
@Test
public void testFindInRecursivelyIncluded() throws Exception {
NinjaFileParseResult parentParseResult = new NinjaFileParseResult();
parentParseResult.addVariable("abc", 12, NinjaVariableValue.createPlainText("abc"));
parentParseResult.addVariable("edf", 120, NinjaVariableValue.createPlainText("edf"));
parentParseResult.addVariable("xyz", 1000, NinjaVariableValue.createPlainText("xyz"));
NinjaFileParseResult childParseResult1 = new NinjaFileParseResult();
parentParseResult.addIncludeScope(140, scope -> childParseResult1);
// Shadows this variable from parent.
childParseResult1.addVariable("edf", 1, NinjaVariableValue.createPlainText("11111"));
childParseResult1.addVariable("child", 2, NinjaVariableValue.createPlainText("child"));
NinjaFileParseResult childParseResult2 = new NinjaFileParseResult();
childParseResult1.addIncludeScope(3, scope -> childParseResult2);
childParseResult2.addVariable("edf", 1, NinjaVariableValue.createPlainText("22222"));
parentParseResult.sortResults();
NinjaScope scope = new NinjaScope();
parentParseResult.expandIntoScope(scope, Maps.newHashMap());
assertThat(scope.findExpandedVariable(220, "edf")).isEqualTo("22222");
}
@Test
public void testVariableExpand() throws Exception {
NinjaFileParseResult parseResult = new NinjaFileParseResult();
parseResult.addVariable("abc", 12, NinjaVariableValue.createPlainText("abc"));
parseResult.addVariable("edf", 120, parseValue("=> $abc = ?"));
parseResult.addVariable("abc", 130, NinjaVariableValue.createPlainText("redefined"));
parseResult.addVariable("edf", 180, parseValue("now$: $abc!"));
parseResult.sortResults();
NinjaScope scope = new NinjaScope();
parseResult.expandIntoScope(scope, Maps.newHashMap());
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 Exception {
NinjaFileParseResult parentParseResult = new NinjaFileParseResult();
parentParseResult.addVariable("abc", 12, NinjaVariableValue.createPlainText("abc"));
parentParseResult.addVariable("edf", 120, parseValue("$abc === ${ abc }"));
NinjaFileParseResult includeParseResult = new NinjaFileParseResult();
parentParseResult.addIncludeScope(140, scope -> includeParseResult);
includeParseResult.addVariable("included", 1, parseValue("<$abc and ${ edf }>"));
NinjaFileParseResult childParseResult = new NinjaFileParseResult();
parentParseResult.addSubNinjaScope(150, scope -> childParseResult);
childParseResult.addVariable("subninja", 2, parseValue("$edf = ${ included }*"));
parentParseResult.sortResults();
NinjaScope parentScope = new NinjaScope();
parentParseResult.expandIntoScope(parentScope, Maps.newHashMap());
assertThat(parentScope.getIncludedScopes()).hasSize(1);
NinjaScope includeScope = parentScope.getIncludedScopes().iterator().next();
assertThat(parentScope.getSubNinjaScopes()).hasSize(1);
NinjaScope childScope = parentScope.getSubNinjaScopes().iterator().next();
assertThat(includeScope.findExpandedVariable(2, "included")).isEqualTo("<abc and abc === abc>");
assertThat(childScope.findExpandedVariable(3, "subninja"))
.isEqualTo("abc === abc = <abc and abc === abc>*");
assertThat(parentScope.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 Exception {
ByteBuffer bb = ByteBuffer.wrap(text.getBytes(StandardCharsets.ISO_8859_1));
NinjaLexer lexer = new NinjaLexer(new ByteBufferFragment(bb, 0, bb.limit()));
return new NinjaParserStep(lexer).parseVariableValue();
}
}