blob: a5b396e2597fcfffd072546e8aa28c15f706a0fb [file] [log] [blame]
// 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");
}
}