blob: 606f0a020c38b195069980e08943628eb2d4216e [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.skyframe;
import static com.google.common.truth.Truth.assertThat;
import static com.google.devtools.build.skyframe.ErrorInfoSubjectFactory.assertThatErrorInfo;
import static com.google.devtools.build.skyframe.EvaluationResultSubjectFactory.assertThatEvaluationResult;
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.skyframe.StarlarkBuiltinsFunction.BuiltinsFailedException;
import com.google.devtools.build.lib.skyframe.util.SkyframeExecutorTestUtils;
import com.google.devtools.build.lib.syntax.ClassObject;
import com.google.devtools.build.lib.testutil.TestRuleClassProvider;
import com.google.devtools.build.skyframe.EvaluationResult;
import com.google.devtools.build.skyframe.SkyKey;
import org.junit.Test;
import org.junit.runner.RunWith;
import org.junit.runners.JUnit4;
/** Tests for {@link StarlarkBuiltinsFunction}. */
@RunWith(JUnit4.class)
public class StarlarkBuiltinsFunctionTest 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()
.addRuleDefinition(OVERRIDABLE_RULE)
.addStarlarkAccessibleTopLevels("overridable_symbol", "original_value");
TestRuleClassProvider.addStandardRules(builder);
return builder.build();
}
@Test
public void getNativeRuleLogicBindings_inPackageFactory() throws Exception {
assertThat(getPackageFactory().getNativeRules()).containsKey("cc_library");
assertThat(getPackageFactory().getNativeRules()).doesNotContainKey("glob");
assertThat(getPackageFactory().getNativeRules()).containsKey("overridable_rule");
}
@Test
public void getNativeRuleLogicBindings_inRuleClassProvider() throws Exception {
assertThat(getRuleClassProvider().getNativeRuleSpecificBindings()).containsKey("CcInfo");
assertThat(getRuleClassProvider().getNativeRuleSpecificBindings()).doesNotContainKey("rule");
assertThat(getRuleClassProvider().getNativeRuleSpecificBindings())
.containsKey("overridable_symbol");
}
// TODO(#11437): Add tests for predeclared env of BUILD (and WORKSPACE?) files, once
// StarlarkBuiltinsFunction manages that functionality.
/** Sets up exports.bzl with the given contents and evaluates the {@code @builtins}. */
private EvaluationResult<StarlarkBuiltinsValue> evalBuiltins(String... lines) throws Exception {
scratch.file("tools/builtins_staging/BUILD");
scratch.file("tools/builtins_staging/exports.bzl", lines);
SkyKey key = StarlarkBuiltinsValue.key();
return SkyframeExecutorTestUtils.evaluate(
getSkyframeExecutor(), key, /*keepGoing=*/ false, reporter);
}
/**
* Sets up exports.bzl with the given contents, evaluates the {@code @builtins}, and returns an
* expected non-transient Exception.
*/
private Exception evalBuiltinsToException(String... lines) throws Exception {
EvaluationResult<StarlarkBuiltinsValue> result = evalBuiltins(lines);
SkyKey key = StarlarkBuiltinsValue.key();
assertThatEvaluationResult(result).hasError();
assertThatErrorInfo(result.getError(key)).isNotTransient();
return result.getError(key).getException();
}
@Test
public void evalExportsSuccess() throws Exception {
EvaluationResult<StarlarkBuiltinsValue> result =
evalBuiltins(
"exported_toplevels = {'overridable_symbol': 'new_value'}",
"exported_rules = {'overridable_rule': 'new_rule'}",
"exported_to_java = {'for_native_code': 'secret_sauce'}");
SkyKey key = StarlarkBuiltinsValue.key();
assertThatEvaluationResult(result).hasNoError();
StarlarkBuiltinsValue value = result.get(key);
// Universe symbols are omitted (they're added by the interpreter).
assertThat(value.predeclaredForBuildBzl).doesNotContainKey("print");
// Generic Bazel symbols are present.
assertThat(value.predeclaredForBuildBzl).containsKey("rule");
// Non-overridden symbols are present.
assertThat(value.predeclaredForBuildBzl).containsKey("CcInfo");
// Overridden symbol.
assertThat(value.predeclaredForBuildBzl).containsEntry("overridable_symbol", "new_value");
// Overridden native field.
Object nativeField =
((ClassObject) value.predeclaredForBuildBzl.get("native")).getValue("overridable_rule");
assertThat(nativeField).isEqualTo("new_rule");
// Stuff for native rules.
assertThat(value.exportedToJava).containsExactly("for_native_code", "secret_sauce").inOrder();
// No test of the digest.
}
@Test
public void evalExportsSuccess_withLoad() throws Exception {
// TODO(#11437): Use @builtins//:... syntax, once supported. Don't create a real package.
scratch.file("builtins_helper/BUILD");
scratch.file(
"builtins_helper/dummy.bzl", //
"toplevels = {'overridable_symbol': 'new_value'}");
EvaluationResult<StarlarkBuiltinsValue> result =
evalBuiltins(
"load('//builtins_helper:dummy.bzl', 'toplevels')",
"exported_toplevels = toplevels",
"exported_rules = {}",
"exported_to_java = {}");
SkyKey key = StarlarkBuiltinsValue.key();
assertThatEvaluationResult(result).hasNoError();
StarlarkBuiltinsValue value = result.get(key);
assertThat(value.predeclaredForBuildBzl).containsEntry("overridable_symbol", "new_value");
}
@Test
public void evalExportsFails_missingDictSymbol() throws Exception {
Exception ex =
evalBuiltinsToException(
"exported_toplevels = {}", //
"# exported_rules missing",
"exported_to_java = {}");
assertThat(ex).isInstanceOf(BuiltinsFailedException.class);
assertThat(ex)
.hasMessageThat()
.contains(
"Failed to apply declared builtins: expected a 'exported_rules' dictionary to be "
+ "defined");
}
@Test
public void evalExportsFails_badSymbolType() throws Exception {
Exception ex =
evalBuiltinsToException(
"exported_toplevels = {}", //
"exported_rules = None",
"exported_to_java = {}");
assertThat(ex).isInstanceOf(BuiltinsFailedException.class);
assertThat(ex)
.hasMessageThat()
.contains(
"Failed to apply declared builtins: got NoneType for 'exported_rules dict', want dict");
}
@Test
public void evalExportsFails_badDictKey() throws Exception {
Exception ex =
evalBuiltinsToException(
"exported_toplevels = {}", //
"exported_rules = {1: 'a'}",
"exported_to_java = {}");
assertThat(ex).isInstanceOf(BuiltinsFailedException.class);
assertThat(ex)
.hasMessageThat()
.contains(
"Failed to apply declared builtins: got dict<int, string> for 'exported_rules dict', "
+ "want dict<string, unknown>");
}
@Test
public void evalExportsFails_parseError() throws Exception {
reporter.removeHandler(failFastHandler);
Exception ex =
evalBuiltinsToException(
"exported_toplevels = {}",
"exported_rules = {}",
"exported_to_java = {}",
"asdf asdf # <-- parse error");
assertThat(ex).isInstanceOf(BuiltinsFailedException.class);
assertThat(ex)
.hasMessageThat()
.contains(
"Failed to load builtins sources: Extension 'tools/builtins_staging/exports.bzl' has "
+ "errors");
}
@Test
public void evalExportsFails_evalError() throws Exception {
reporter.removeHandler(failFastHandler);
Exception ex =
evalBuiltinsToException(
"exported_toplevels = {}",
"exported_rules = {}",
"exported_to_java = {}",
"1 // 0 # <-- dynamic error");
assertThat(ex).isInstanceOf(BuiltinsFailedException.class);
assertThat(ex)
.hasMessageThat()
.contains(
"Failed to load builtins sources: Extension file 'tools/builtins_staging/exports.bzl' "
+ "has errors");
}
@Test
public void evalExportsFails_errorInDependency() throws Exception {
reporter.removeHandler(failFastHandler);
// TODO(#11437): Use @builtins//:... syntax, once supported. Don't create a real package.
scratch.file("builtins_helper/BUILD");
scratch.file(
"builtins_helper/dummy.bzl", //
"1 // 0 # <-- dynamic error");
Exception ex =
evalBuiltinsToException(
"load('//builtins_helper:dummy.bzl', 'dummy')",
"exported_toplevels = {}",
"exported_rules = {}",
"exported_to_java = {}");
assertThat(ex).isInstanceOf(BuiltinsFailedException.class);
assertThat(ex)
.hasMessageThat()
.contains(
"Failed to load builtins sources: in /workspace/tools/builtins_staging/exports.bzl: "
+ "Extension file 'builtins_helper/dummy.bzl' has errors");
}
}