blob: 1f3109192178a79175d5f95f93c7e72c428b93e7 [file] [log] [blame]
// Copyright 2023 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.query2.testutil;
import static com.google.common.truth.Truth.assertThat;
import com.google.common.base.Function;
import com.google.common.collect.ImmutableList;
import com.google.common.collect.Iterables;
import com.google.devtools.build.lib.packages.BuildType;
import com.google.devtools.build.lib.packages.Target;
import com.google.devtools.build.lib.packages.util.MockToolsConfig;
import com.google.devtools.build.lib.query2.engine.QueryEnvironment.QueryFunction;
import com.google.devtools.build.lib.query2.engine.QueryEnvironment.Setting;
import java.io.IOException;
import java.util.Set;
import org.junit.Test;
/** Tests for the blaze query implementation, --nokeep_going. */
public abstract class QueryTest extends AbstractQueryTest<Target> {
@Override
protected QueryHelper<Target> createQueryHelper() {
return new SkyframeQueryHelper() {
@Override
protected String getRootDirectoryNameForSetup() {
return "/workspace";
}
@Override
protected void performAdditionalClientSetup(MockToolsConfig mockToolsConfig)
throws IOException {}
@Override
protected Iterable<QueryFunction> getExtraQueryFunctions() {
return ImmutableList.of();
}
};
}
@Override
protected boolean includeCppToolchainDependencies() {
return false;
}
@Test
public void testFindsAllTargets_nativeRuleMacro() throws Exception {
writeFile(
"test/starlark/extension.bzl",
"def macro(name):",
" native.genrule(name = name, outs = [name + '.txt'], cmd = 'echo hi >$@')");
writeFile(
"test//starlark/BUILD",
"load('//test/starlark:extension.bzl', 'macro')",
"",
"macro(name = 'rule1')",
"macro(name = 'rule2')");
assertThat(targetLabels(eval("//test/starlark:*")))
.containsExactly(
"//test/starlark:rule1",
"//test/starlark:rule2",
"//test/starlark:BUILD",
"//test/starlark:rule1.txt",
"//test/starlark:rule2.txt");
}
@Test
public void testFindsAllTargets_starlarkRuleMacro() throws Exception {
writeFile(
"test//starlark/extension.bzl",
"def impl(ctx):",
" return None",
"",
"starlark_rule = rule(implementation = impl)",
"",
"def macro(name):",
" starlark_rule(name = name)");
writeFile(
"test//starlark/BUILD",
"load('//test/starlark:extension.bzl', 'macro')",
"",
"macro(name = 'rule1')",
"macro(name = 'rule2')");
assertThat(targetLabels(eval("//test/starlark:*")))
.containsExactly("//test/starlark:rule1", "//test/starlark:rule2", "//test/starlark:BUILD");
}
@Test
public void testBuildfiles_starlarkDep() throws Exception {
writeFile(
"test//starlark/extension.bzl",
"def macro(name):",
" native.genrule(name = name, outs = [name + '.txt'], cmd = 'echo hi >$@')");
writeFile(
"test//starlark/BUILD",
"load('//test/starlark:extension.bzl', 'macro')",
"",
"macro(name = 'rule1')");
assertThat(targetLabels(eval("buildfiles(//test/starlark:BUILD)")))
.containsExactly("//test/starlark:extension.bzl", "//test/starlark:BUILD");
}
@Test
public void testLoadfiles_starlarkDep() throws Exception {
writeFile(
"test//starlark/extension.bzl",
"def macro(name):",
" native.genrule(name = name, outs = [name + '.txt'], cmd = 'echo hi >$@')");
writeFile(
"test//starlark/BUILD",
"load('//test/starlark:extension.bzl', 'macro')",
"",
"macro(name = 'rule1')");
assertThat(targetLabels(eval("loadfiles(//test/starlark:BUILD)")))
.containsExactly("//test/starlark:extension.bzl");
}
@Test
public void testLoadfiles_sclDep() throws Exception {
writeBzlAndSclFiles();
assertThat(targetLabels(eval("loadfiles(//foo:BUILD)")))
.containsExactly("//bar:direct.scl", "//bar:indirect.scl", "//bar:intermediate.bzl");
}
@Test
public void testDeps_labelKeyedStringDictDeps() throws Exception {
writeFile(
"test//starlark/rule.bzl",
"def _impl(ctx):",
" return",
"my_rule = rule(",
" implementation = _impl,",
" attrs = {",
" 'value_dict': attr.label_keyed_string_dict(allow_files=True),",
" }",
")");
writeFile("test//starlark/dep.cc");
writeFile(
"test//starlark/BUILD",
"load('//test/starlark:rule.bzl', 'my_rule')",
"filegroup(name='group', srcs=['dep.cc'])",
"my_rule(name='rule', value_dict={':group': 'queried'})");
assertThat(targetLabels(eval("deps(//test/starlark:rule)")))
.containsExactly("//test/starlark:rule", "//test/starlark:group", "//test/starlark:dep.cc");
}
@Test
public void testBuildfiles_transitiveStarlarkDeps() throws Exception {
writeFile(
"test//starlark/extension1.bzl",
"def macro(name):",
" native.genrule(name = name, outs = [name + '.txt'], cmd = 'echo hi >$@')");
writeFile(
"test//starlark/extension2.bzl",
"load('//test/starlark:extension1.bzl', 'macro')",
"def func(name):",
" macro(name)");
writeFile(
"test//starlark/BUILD",
"load('//test/starlark:extension2.bzl', 'func')",
"",
"func(name = 'rule1')");
assertThat(targetLabels(eval("buildfiles(//test/starlark:BUILD)")))
.containsExactly(
"//test/starlark:extension1.bzl",
"//test/starlark:extension2.bzl",
"//test/starlark:BUILD");
}
@Test
public void testBuildfiles_diamondStarlarkDeps() throws Exception {
writeFile(
"test//starlark/extension1.bzl",
"my_constant = 'rule1'",
"def macro(name):",
" native.genrule(name = name, outs = [name + '.txt'], cmd = 'echo hi >$@')");
writeFile(
"test//starlark/extension2.bzl",
"load('//test/starlark:extension1.bzl', 'macro')",
"def func(name):",
" macro(name)");
writeFile(
"test//starlark/extension3.bzl",
"load('//test/starlark:extension1.bzl', 'my_constant')",
"my_rule_name = my_constant");
writeFile(
"test//starlark/extension4.bzl",
"load('//test/starlark:extension2.bzl', 'func')",
"load('//test/starlark:extension3.bzl', 'my_rule_name')",
"",
"my_dummy_name = my_rule_name");
writeFile(
"test//starlark/BUILD",
"load('//test/starlark:extension2.bzl', 'func')",
"load('//test/starlark:extension3.bzl', 'my_rule_name')",
"load('//test/starlark:extension4.bzl', 'my_dummy_name')",
"",
"func(name = my_rule_name + '-' + my_dummy_name)");
assertThat(targetLabels(eval("buildfiles(//test/starlark:BUILD)")))
.containsExactly(
"//test/starlark:extension1.bzl",
"//test/starlark:extension2.bzl",
"//test/starlark:extension3.bzl",
"//test/starlark:extension4.bzl",
"//test/starlark:BUILD");
}
@Test
public void testBuildfiles_starlarkDepPackageBuildfileIncluded() throws Exception {
writeFile("test//starlark2/BUILD");
writeFile("test//starlark2/extension.bzl", "file_ext = '.txt'");
writeFile("test//starlark1/BUILD");
writeFile(
"test//starlark1/extension.bzl",
"load('//test/starlark2:extension.bzl', 'file_ext')",
"def macro(name):",
" native.genrule(name = name, outs = [name + file_ext], cmd = 'echo hi >$@')");
writeFile(
"test/pkg/BUILD",
"load('//test/starlark1:extension.bzl', 'macro')",
"",
"macro(name = 'rule1')");
assertThat(targetLabels(eval("buildfiles(//test/pkg:BUILD)")))
.containsExactly(
"//test/pkg:BUILD",
"//test/starlark1:extension.bzl",
"//test/starlark1:BUILD",
"//test/starlark2:extension.bzl",
"//test/starlark2:BUILD");
}
@Test
public void testQueryTimeLoadingWhenPackageDoesNotExist() throws Exception {
// Given a workspace containing a package "//a",
writeFile("a/BUILD", "sh_library(name = 'a')");
// When the query environment is queried for "//a/b:b" which doesn't exist,
String nonExistentPackage = "a/b";
String s = evalThrows("//" + nonExistentPackage + ":itsNotThere", false).getMessage();
// Then an exception is thrown that says that the specified package does not exist.
assertThat(s).containsMatch("no such package '" + nonExistentPackage + "'");
}
@Test
public void testQueryTimeLoadingWhenPackageIsMalformed() throws Exception {
// Given a workspace containing a malformed package "//a",
writeFile("a/BUILD", "sh_library(name = 'a') BUT WAIT THERE'S MORE");
// When the query environment is queried for "//a:a" which belongs to a malformed package,
String s = evalThrows("//a:a", false).getMessage();
// Then an exception is thrown,
assertThat(s).isNotNull();
// And then the query output contains a description of the malformed package error.
assertContainsEvent("unclosed string literal");
}
@Test
public void testQueryTimeLoadingOfSymlinkCyclePackage() throws Exception {
// Given a workspace containing a symlink cycle that looks like a BUILD file at "//a/BUILD",
ensureSymbolicLink("a/BUILD", "a/BUILD");
// When the query environment is queried for "//a:*",
String s = evalThrows("//a:*", false).getMessage();
// Then an exception is thrown,
assertThat(s).isNotNull();
// And then the query output contains a description of the circular symlink problem.
assertContainsEvent("circular symlinks detected");
}
@Test
public void boundedDepsQueryWithError() throws Exception {
writeFile("foo/BUILD", "sh_library(name ='foo', deps = ['//bar'])");
writeFile("bar/BUILD", "sh_library(name ='bar')");
writeFile("errorparent", "sh_library(name = 'errorparent', deps = ['//error'])");
writeFile("error", "has errors");
evalThrows("deps(//foo:all + //errorparent:all, 25)", false);
}
@Test
public void testIgnoredPackagePrefixes() throws Exception {
writeFile(helper.getIgnoredPackagePrefixesFile().getPathString(), "a/b", "a/c");
writeFile("a/BUILD", "filegroup(name = 'a')");
writeFile("b/BUILD", "filegroup(name = 'b')");
writeFile("a/b/BUILD", "filegroup(name = 'a_b')");
writeFile("a/c/BUILD", "filegroup(name = 'a_c')");
writeFile("a/d/BUILD", "filegroup(name = 'a_d')");
writeFile("a/e/BUILD", "filegroup(name = 'a_e')");
// Ensure that modified files are invalidated in the skyframe. If a file has
// already been read prior to the test's writes, this forces the query to
// pick up the modified versions.
helper.maybeHandleDiffs();
Iterable<String> result = targetLabels(eval("//..."));
assertThat(result).containsAtLeast("//a:a", "//b:b", "//a/d:a_d", "//a/e:a_e");
assertThat(result).containsNoneOf("//a/b:a_b", "//a/c:a_c");
result = targetLabels(eval("//a/..."));
assertThat(result).containsExactly("//a:a", "//a/d:a_d", "//a/e:a_e");
}
private void writeStarlarkDefinedRuleClassBzlFile() throws java.io.IOException {
writeFile(
"test//starlark/extension.bzl",
"def custom_rule_impl(ctx):",
" ftb = depset(ctx.attr._secret_labels)",
" return struct(runfiles = ctx.runfiles(), files = ftb)",
"",
"def secret_labels_func(prefix, suffix):",
" return [Label('//test/starlark:' + prefix + '01' + suffix),",
" Label('//test/starlark:' + prefix + '02' + suffix)]",
"",
"custom_rule = rule(implementation = custom_rule_impl,",
" attrs = {'prefix': attr.string(default='default_prefix'),",
" 'suffix': attr.string(default='default_suffix'),",
" '_secret_labels': attr.label_list(default=secret_labels_func)})");
}
@Test
public void testQueryStarlarkComputedDefault() throws Exception {
writeStarlarkDefinedRuleClassBzlFile();
writeFile(
"test//starlark/BUILD",
"load('//test/starlark:extension.bzl', 'custom_rule')",
"",
"custom_rule(",
" name = 'custom',",
" prefix = 'a',",
" suffix = 'b')");
Set<Target> targets = eval("//test/starlark:*");
assertThat(targetLabels(targets))
.containsExactly(
"//test/starlark:BUILD",
"//test/starlark:custom",
"//test/starlark:a01b",
"//test/starlark:a02b");
}
@Test
public void testQueryStarlarkComputedDefaultWithConfigurableDeps() throws Exception {
writeStarlarkDefinedRuleClassBzlFile();
writeFile(
"test//starlark/BUILD",
"load('//test/starlark:extension.bzl', 'custom_rule')",
"",
"config_setting(",
" name = 'cfg_a',",
" values = {'test_arg': 'something'})",
"config_setting(",
" name = 'cfg_b',",
" values = {'test_arg': 'something_else'})",
"",
"custom_rule(",
" name = 'custom',",
" prefix = select({",
" ':cfg_a':'a',",
" ':cfg_b':'b',",
" '" + BuildType.Selector.DEFAULT_CONDITION_KEY + "':'def'}),",
" suffix = select({",
" ':cfg_a':'a',",
" ':cfg_b':'b',",
" '" + BuildType.Selector.DEFAULT_CONDITION_KEY + "':'def'}))");
ImmutableList.Builder<String> computedLabelsBuilder = ImmutableList.builder();
for (String prefix : ImmutableList.of("a", "b", "def")) {
for (String middle : ImmutableList.of("01", "02")) {
for (String suffix : ImmutableList.of("a", "b", "def")) {
computedLabelsBuilder.add("//test/starlark:" + prefix + middle + suffix);
}
}
}
ImmutableList<String> computedLabels = computedLabelsBuilder.build();
Set<Target> targets = eval("//test/starlark:*");
assertThat(targetLabels(targets))
.containsAtLeastElementsIn(
Iterables.concat(
ImmutableList.of(
"//test/starlark:BUILD",
"//test/starlark:cfg_a",
"//test/starlark:cfg_b",
"//test/starlark:custom"),
computedLabels));
}
@Test
public void testQueryStarlarkComputedDefaultWithConfigurableDepsUsedTwice() throws Exception {
writeStarlarkDefinedRuleClassBzlFile();
writeFile(
"test//starlark/BUILD",
"load('//test/starlark:extension.bzl', 'custom_rule')",
"",
"config_setting(",
" name = 'cfg_a',",
" values = {'test_arg': 'something'})",
"config_setting(",
" name = 'cfg_b',",
" values = {'test_arg': 'something_else'})",
"",
"custom_rule(",
" name = 'custom_one',",
" prefix = select({",
" ':cfg_a':'a_one',",
" ':cfg_b':'b_one',",
" '" + BuildType.Selector.DEFAULT_CONDITION_KEY + "':'def_one'}),",
" suffix = select({",
" ':cfg_a':'a_one',",
" ':cfg_b':'b_one',",
" '" + BuildType.Selector.DEFAULT_CONDITION_KEY + "':'def_one'}))",
"custom_rule(",
" name = 'custom_two',",
" prefix = select({",
" ':cfg_a':'a_two',",
" ':cfg_b':'b_two',",
" '" + BuildType.Selector.DEFAULT_CONDITION_KEY + "':'def_two'}),",
" suffix = select({",
" ':cfg_a':'a_two',",
" ':cfg_b':'b_two',",
" '" + BuildType.Selector.DEFAULT_CONDITION_KEY + "':'def_two'}))");
ImmutableList.Builder<String> computedLabelsBuilder = ImmutableList.builder();
for (String prefix : ImmutableList.of("a_one", "b_one", "def_one")) {
for (String middle : ImmutableList.of("01", "02")) {
for (String suffix : ImmutableList.of("a_one", "b_one", "def_one")) {
computedLabelsBuilder.add("//test/starlark:" + prefix + middle + suffix);
}
}
}
for (String prefix : ImmutableList.of("a_two", "b_two", "def_two")) {
for (String middle : ImmutableList.of("01", "02")) {
for (String suffix : ImmutableList.of("a_two", "b_two", "def_two")) {
computedLabelsBuilder.add("//test/starlark:" + prefix + middle + suffix);
}
}
}
ImmutableList<String> computedLabels = computedLabelsBuilder.build();
Set<Target> targets = eval("//test/starlark:*");
assertThat(targetLabels(targets))
.containsExactlyElementsIn(
Iterables.concat(
ImmutableList.of(
"//test/starlark:BUILD",
"//test/starlark:cfg_a",
"//test/starlark:cfg_b",
"//test/starlark:custom_one",
"//test/starlark:custom_two"),
computedLabels));
}
@Test
public void testFileTargetLiteralInSubdirectory() throws Exception {
writeFile(
"foo/BUILD",
"exports_files(glob(['**/*.txt']))");
writeFile("foo/bar/file1.txt");
writeFile("foo/bar/file2.txt");
Set<Target> targets = eval("foo/bar/file1.txt + foo/bar/file2.txt");
assertThat(targetLabels(targets))
.containsExactly(
"//foo:bar/file1.txt",
"//foo:bar/file2.txt");
}
@Test
public void testShorthandTargetLiteralUnion() throws Exception {
writeFile(
"foo/bar/BUILD",
"sh_library(name = 'bar')");
writeFile(
"foo/baz/BUILD",
"sh_library(name = 'baz')");
Set<Target> targets = eval("foo/bar + foo/baz");
assertThat(targetLabels(targets))
.containsExactly(
"//foo/bar:bar",
"//foo/baz:baz");
}
@Test
public void testShorthandAbsoluteTargetLiteralUnion() throws Exception {
writeFile(
"foo/bar/BUILD",
"sh_library(name = 'bar')");
writeFile(
"foo/baz/BUILD",
"sh_library(name = 'baz')");
Set<Target> targets = eval("//foo/bar + //foo/baz");
assertThat(targetLabels(targets))
.containsExactly(
"//foo/bar:bar",
"//foo/baz:baz");
}
@Test
public void testLoadfilesWithDuplicates() throws Exception {
writeFile(
"foo/BUILD",
"load('//bar:bar.bzl', 'B')",
"sh_library(name = 'foo', deps = ['//bar'])");
writeFile(
"bar/BUILD",
"load('//bar:bar.bzl', 'B')",
"sh_library(name = 'bar')");
writeFile(
"bar/bar.bzl",
"B = []");
assertThat(evalToString("loadfiles(deps(//foo))")).isEqualTo("//bar:bar.bzl");
}
protected void runTestRdepsWithNonDefaultDependencyFilter(String query, String expected)
throws Exception {
writeFile(
"foo/BUILD",
"genrule(",
" name = 'gen',",
" tools = [':a'],",
" srcs = ['doesntmatter.txt'],",
" outs = ['out.txt'],",
" cmd = 'blah')",
"sh_binary(name = 'a', srcs = ['doesntmatter.sh'])",
"sh_binary(name = 'b', srcs = ['doesntmatter.sh'], data = [':a'])",
"sh_binary(name = 'c', srcs = ['doesntmatter.sh'], data = [':out.txt'])");
helper.setQuerySettings(Setting.ONLY_TARGET_DEPS);
assertThat(evalToString(query)).isEqualTo(expected);
}
@Test
public void testRdepsUnboundedWithNonDefaultDependencyFilter() throws Exception {
runTestRdepsWithNonDefaultDependencyFilter("rdeps(//foo:all, //foo:a)", "//foo:a //foo:b");
}
@Test
public void testRdepsBoundedWithNonDefaultDependencyFilter() throws Exception {
runTestRdepsWithNonDefaultDependencyFilter("rdeps(//foo:all, //foo:a, 1)", "//foo:a //foo:b");
}
protected Iterable<String> targetLabels(Set<Target> targets) {
return Iterables.transform(targets, new Function<Target, String>() {
@Override
public String apply(Target input) {
return input.getLabel().toString();
}
});
}
}