| // Copyright 2020 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.packages; |
| |
| import static com.google.common.truth.Truth.assertThat; |
| import static org.junit.Assert.assertThrows; |
| |
| import com.google.common.collect.ImmutableMap; |
| import com.google.devtools.build.lib.analysis.ConfiguredRuleClassProvider; |
| import com.google.devtools.build.lib.analysis.util.BuildViewTestCase; |
| import com.google.devtools.build.lib.analysis.util.MockRule; |
| import com.google.devtools.build.lib.packages.PackageFactory.InjectionException; |
| import com.google.devtools.build.lib.testutil.TestRuleClassProvider; |
| import java.util.HashMap; |
| import java.util.Map; |
| import java.util.Set; |
| import net.starlark.java.eval.Structure; |
| import org.junit.Test; |
| import org.junit.runner.RunWith; |
| import org.junit.runners.JUnit4; |
| |
| /** Unit tests for PackageFactory's management of the predeclared Starlark symbols. */ |
| @RunWith(JUnit4.class) |
| public final class BazelStarlarkEnvironmentTest extends BuildViewTestCase { |
| |
| private static final MockRule OVERRIDABLE_RULE = () -> MockRule.define("overridable_rule"); |
| |
| @Override |
| protected ConfiguredRuleClassProvider createRuleClassProvider() { |
| // Add a fake rule and top-level symbol to override. |
| ConfiguredRuleClassProvider.Builder builder = |
| new ConfiguredRuleClassProvider.Builder() |
| // While reading, feel free to mentally substitute overridable_rule -> cc_library and |
| // overridable_symbol -> CcInfo. |
| .addRuleDefinition(OVERRIDABLE_RULE) |
| .addStarlarkAccessibleTopLevels("overridable_symbol", "original_value"); |
| TestRuleClassProvider.addStandardRules(builder); |
| return builder.build(); |
| } |
| |
| // TODO(#11954): We want BUILD- and WORKSPACE-loaded bzl files to have the exact same environment. |
| // In the meantime these two tests help avoid regressions. |
| |
| // This property is important for BzlCompileFunction, which relies on the symbol names in the env |
| // matching even if the symbols themselves differ. |
| @Test |
| public void buildAndWorkspaceBzlEnvsDeclareSameNames() throws Exception { |
| Set<String> buildBzlNames = pkgFactory.getUninjectedBuildBzlEnv().keySet(); |
| Set<String> workspaceBzlNames = pkgFactory.getWorkspaceBzlEnv().keySet(); |
| assertThat(buildBzlNames).isEqualTo(workspaceBzlNames); |
| } |
| |
| @Test |
| public void buildAndWorkspaceBzlEnvsAreSameExceptForNative() throws Exception { |
| Map<String, Object> buildBzlEnv = new HashMap<>(); |
| buildBzlEnv.putAll(pkgFactory.getUninjectedBuildBzlEnv()); |
| buildBzlEnv.remove("native"); |
| Map<String, Object> workspaceBzlEnv = new HashMap<>(); |
| workspaceBzlEnv.putAll(pkgFactory.getWorkspaceBzlEnv()); |
| workspaceBzlEnv.remove("native"); |
| assertThat(buildBzlEnv).isEqualTo(workspaceBzlEnv); |
| } |
| |
| @Test |
| public void builtinsBzlEnvCanSeeGeneralToplevels() throws Exception { |
| assertThat(pkgFactory.getBuiltinsBzlEnv()).containsKey("rule"); |
| } |
| |
| @Test |
| public void builtinsBzlEnvCannotSeeRuleSpecificToplevels() throws Exception { |
| assertThat(pkgFactory.getBuiltinsBzlEnv()).doesNotContainKey("overridable_symbol"); |
| } |
| |
| @Test |
| public void injection() throws Exception { |
| Map<String, Object> env = |
| pkgFactory.createBuildBzlEnvUsingInjection( |
| ImmutableMap.of("overridable_symbol", "new_value"), |
| ImmutableMap.of("overridable_rule", "new_rule")); |
| assertThat(env).containsEntry("overridable_symbol", "new_value"); |
| assertThat(((Structure) env.get("native")).getValue("overridable_rule")).isEqualTo("new_rule"); |
| } |
| |
| /** Asserts that injection with the given maps fails with the given error substring. */ |
| private void assertInjectionFailure( |
| ImmutableMap<String, Object> injectedToplevels, |
| ImmutableMap<String, Object> injectedRules, |
| String message) { |
| InjectionException ex = |
| assertThrows( |
| InjectionException.class, |
| () -> pkgFactory.createBuildBzlEnvUsingInjection(injectedToplevels, injectedRules)); |
| assertThat(ex).hasMessageThat().contains(message); |
| } |
| |
| @Test |
| public void injectedNameMustOverrideExistingName_toplevel() throws Exception { |
| assertInjectionFailure( |
| ImmutableMap.of("brand_new_toplevel", "foo"), |
| ImmutableMap.of(), |
| "Injected top-level symbol 'brand_new_toplevel' must override an existing symbol by" |
| + " that name"); |
| } |
| |
| @Test |
| public void injectedNameMustOverrideExistingName_nativeField() throws Exception { |
| assertInjectionFailure( |
| ImmutableMap.of(), |
| ImmutableMap.of("brand_new_field", "foo"), |
| "Injected native module field 'brand_new_field' must override an existing symbol by " |
| + "that name"); |
| } |
| |
| @Test |
| public void cannotInjectGeneralSymbol_toplevel() { |
| assertInjectionFailure( |
| ImmutableMap.of("provider", "new_builtin"), |
| ImmutableMap.of(), |
| "Cannot override top-level builtin 'provider' with an injected value"); |
| } |
| |
| @Test |
| public void cannotInjectGeneralSymbol_nativeField() { |
| assertInjectionFailure( |
| ImmutableMap.of(), |
| ImmutableMap.of("glob", "new_builtin"), |
| "Cannot override native module field 'glob' with an injected value"); |
| } |
| |
| @Test |
| public void cannotInjectGeneralSymbol_nativeModuleItself() { |
| assertInjectionFailure( |
| ImmutableMap.of("native", "new_builtin"), |
| ImmutableMap.of(), |
| "Cannot override top-level builtin 'native' with an injected value"); |
| } |
| |
| @Test |
| public void cannotInjectGeneralSymbol_universe() { |
| assertInjectionFailure( |
| ImmutableMap.of("len", "new_builtin"), |
| ImmutableMap.of(), |
| "Cannot override top-level builtin 'len' with an injected value"); |
| } |
| } |