| // 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(); |
| } |
| } |