blob: a9b7e8431bf8bacb354abbe70a7e3302c120891b [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",
srcs = ["doesntmatter.txt"],
outs = ["out.txt"],
cmd = "blah",
tools = [":a"],
)
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();
}
});
}
}