blob: 92d872dd3fa89a287df5537774c3efe97d0adeec [file] [log] [blame]
// Copyright 2018 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 static com.google.devtools.build.lib.packages.Attribute.attr;
import static com.google.devtools.build.lib.packages.BuildType.LABEL;
import static com.google.devtools.build.lib.testutil.TestConstants.PLATFORM_LABEL;
import com.google.common.collect.Iterables;
import com.google.devtools.build.lib.analysis.ConfiguredRuleClassProvider;
import com.google.devtools.build.lib.analysis.config.BuildConfiguration;
import com.google.devtools.build.lib.analysis.config.BuildOptions;
import com.google.devtools.build.lib.analysis.config.transitions.PatchTransition;
import com.google.devtools.build.lib.analysis.test.TestConfiguration.TestOptions;
import com.google.devtools.build.lib.analysis.util.MockRule;
import com.google.devtools.build.lib.cmdline.Label;
import com.google.devtools.build.lib.events.EventHandler;
import com.google.devtools.build.lib.packages.Attribute;
import com.google.devtools.build.lib.packages.AttributeMap;
import com.google.devtools.build.lib.packages.Type;
import com.google.devtools.build.lib.query2.PostAnalysisQueryEnvironment;
import com.google.devtools.build.lib.query2.engine.QueryEnvironment.QueryFunction;
import com.google.devtools.build.lib.query2.engine.QueryEnvironment.Setting;
import com.google.devtools.build.lib.query2.engine.QueryExpression;
import com.google.devtools.build.lib.query2.engine.QueryParser;
import com.google.devtools.build.lib.testutil.TestRuleClassProvider;
import com.google.devtools.build.lib.util.FileTypeSet;
import java.util.Collections;
import java.util.HashMap;
import java.util.Iterator;
import java.util.LinkedHashSet;
import java.util.Set;
import org.junit.After;
import org.junit.Before;
import org.junit.Test;
/** Tests for {@link PostAnalysisQueryEnvironment}. */
public abstract class PostAnalysisQueryTest<T> extends AbstractQueryTest<T> {
// Also filter out platform dependencies.
@Override
protected String getDependencyCorrection() {
return " - deps(" + PLATFORM_LABEL + ")";
}
static final String DEFAULT_UNIVERSE = "DEFAULT_UNIVERSE";
@Before
public final void disableOrderedResults() {
helper.setOrderedResults(false);
}
@Before
public final void setMockToolsConfig() {
this.mockToolsConfig = getHelper().getMockToolsConfig();
}
/**
* In production, cquery constructs the universe by parsing targets from the query expression and
* building them at the top level. If this is not viable (e.g. component functions) or not desired
* (e.g. somepath(//foo-built-in-target, //bar-built-in-host), the user must specify the
* --universe_scope flag. Enforce the same behavior in this test by initializing universe scope to
* an invalid target expression.
*/
@Override
protected String getDefaultUniverseScope() {
return DEFAULT_UNIVERSE;
}
protected PostAnalysisQueryHelper<T> getHelper() {
return (PostAnalysisQueryHelper<T>) helper;
}
/**
* At the end of each eval, reset the universe scope to the default if the test doesn't use a
* single universe scope.
*/
@Override
protected Set<T> eval(String query) throws Exception {
maybeParseUniverseScope(query);
Set<T> queryResult = super.eval(query);
if (!getHelper().isWholeTestUniverse()) {
helper.setUniverseScope(getDefaultUniverseScope());
}
return queryResult;
}
@Override
protected String evalThrows(String query, boolean unconditionallyThrows) throws Exception {
maybeParseUniverseScope(query);
String queryResult = super.evalThrows(query, unconditionallyThrows);
if (!getHelper().isWholeTestUniverse()) {
helper.setUniverseScope(getDefaultUniverseScope());
}
return queryResult;
}
// Parse the universe if the universe has not been set manually through the helper.
private void maybeParseUniverseScope(String query) throws Exception {
if (!getHelper()
.getUniverseScope()
.equals(Collections.singletonList(getDefaultUniverseScope()))) {
return;
}
QueryExpression expression = QueryParser.parse(query, getDefaultFunctions());
Set<String> targetPatternSet = new LinkedHashSet<>();
expression.collectTargetPatterns(targetPatternSet);
if (!targetPatternSet.isEmpty()) {
StringBuilder universeScope = new StringBuilder();
for (String target : targetPatternSet) {
universeScope.append(target).append(",");
}
helper.setUniverseScope(universeScope.toString());
}
}
protected abstract HashMap<String, QueryFunction> getDefaultFunctions();
protected abstract BuildConfiguration getConfiguration(T target);
protected ConfiguredRuleClassProvider.Builder setRuleClassProviders(MockRule... mockRules) {
ConfiguredRuleClassProvider.Builder builder = new ConfiguredRuleClassProvider.Builder();
TestRuleClassProvider.addStandardRules(builder);
for (MockRule rule : mockRules) {
builder.addRuleDefinition(rule);
}
return builder;
}
@Override
protected boolean testConfigurableAttributes() {
// ConfiguredTargetQuery knows the actual configuration, so it doesn't falsely overapproximate.
return false;
}
@After
public void cleanUpHelper() {
getHelper().cleanUp();
helper = null;
}
@Override
@Test
public void testTargetLiteralWithMissingTargets() throws Exception {
getHelper().turnOffFailFast();
super.testTargetLiteralWithMissingTargets();
}
@Override
@Test
public void testBadTargetLiterals() throws Exception {
getHelper().turnOffFailFast();
super.testBadTargetLiterals();
}
@Override
@Test
public void testNoImplicitDeps() throws Exception {
MockRule ruleWithImplicitDeps =
() ->
MockRule.define(
"implicit_deps_rule",
attr("explicit", LABEL).allowedFileTypes(FileTypeSet.ANY_FILE),
attr("explicit_with_default", LABEL)
.value(Label.parseAbsoluteUnchecked("//test:explicit_with_default"))
.allowedFileTypes(FileTypeSet.ANY_FILE),
attr("$implicit", LABEL).value(Label.parseAbsoluteUnchecked("//test:implicit")),
attr(":latebound", LABEL)
.value(
Attribute.LateBoundDefault.fromConstantForTesting(
Label.parseAbsoluteUnchecked("//test:latebound"))));
helper.useRuleClassProvider(setRuleClassProviders(ruleWithImplicitDeps).build());
writeFile(
"test/BUILD",
"implicit_deps_rule(",
" name = 'my_rule',",
" explicit = ':explicit',",
" explicit_with_default = ':explicit_with_default',",
")",
"cc_library(name = 'explicit')",
"cc_library(name = 'explicit_with_default')",
"cc_library(name = 'implicit')",
"cc_library(name = 'latebound')");
final String implicits = "//test:implicit + //test:latebound";
final String explicits = "//test:my_rule + //test:explicit + //test:explicit_with_default";
// Check for implicit dependencies (late bound attributes, implicit attributes, platforms)
assertThat(evalToListOfStrings("deps(//test:my_rule)"))
.containsAtLeastElementsIn(
evalToListOfStrings(explicits + " + " + implicits + " + " + PLATFORM_LABEL));
helper.setQuerySettings(Setting.NO_IMPLICIT_DEPS);
assertThat(evalToListOfStrings("deps(//test:my_rule)"))
.containsAtLeastElementsIn(evalToListOfStrings(explicits));
assertThat(evalToListOfStrings("deps(//test:my_rule)"))
.doesNotContain(evalToListOfStrings(implicits));
}
@Test
public void testNoImplicitDeps_toolchains() throws Exception {
MockRule ruleWithImplicitDeps =
() ->
MockRule.define(
"implicit_toolchain_deps_rule",
(builder, env) ->
builder.addRequiredToolchains(
Label.parseAbsoluteUnchecked("//test:toolchain_type")));
helper.useRuleClassProvider(setRuleClassProviders(ruleWithImplicitDeps).build());
writeFile(
"test/toolchain.bzl",
"def _impl(ctx):",
" toolchain = platform_common.ToolchainInfo()",
" return [toolchain]",
"test_toolchain = rule(",
" implementation = _impl,",
")");
writeFile(
"test/BUILD",
"load(':toolchain.bzl', 'test_toolchain')",
"implicit_toolchain_deps_rule(",
" name = 'my_rule',",
")",
"toolchain_type(name = 'toolchain_type')",
"toolchain(",
" name = 'toolchain',",
" toolchain_type = ':toolchain_type',",
" toolchain = ':toolchain_impl',",
")",
"test_toolchain(name = 'toolchain_impl')");
((PostAnalysisQueryHelper<T>) helper).useConfiguration("--extra_toolchains=//test:toolchain");
String implicits = "//test:toolchain_impl";
String explicits = "//test:my_rule";
// Check for implicit toolchain dependencies
assertThat(evalToListOfStrings("deps(//test:my_rule)"))
.containsAtLeastElementsIn(
evalToListOfStrings(explicits + " + " + implicits + " + " + PLATFORM_LABEL));
helper.setQuerySettings(Setting.NO_IMPLICIT_DEPS);
assertThat(evalToListOfStrings("deps(//test:my_rule)"))
.containsAtLeastElementsIn(evalToListOfStrings(explicits));
assertThat(evalToListOfStrings("deps(//test:my_rule)"))
.doesNotContain(evalToListOfStrings(implicits));
}
// Regression test for b/148550864
@Test
public void testNoImplicitDeps_platformDeps() throws Exception {
MockRule simpleRule = () -> MockRule.define("simple_rule");
helper.useRuleClassProvider(setRuleClassProviders(simpleRule).build());
writeFile(
"test/BUILD",
"simple_rule(name = 'my_rule')",
"platform(name = 'host_platform')",
"platform(name = 'execution_platform')");
((PostAnalysisQueryHelper<T>) helper)
.useConfiguration(
"--host_platform=//test:host_platform",
"--extra_execution_platforms=//test:execution_platform");
// Check for platform dependencies
assertThat(evalToListOfStrings("deps(//test:my_rule)"))
.containsAtLeastElementsIn(
evalToListOfStrings("//test:execution_platform + //test:host_platform"));
helper.setQuerySettings(Setting.NO_IMPLICIT_DEPS);
assertThat(evalToListOfStrings("deps(//test:my_rule)")).containsExactly("//test:my_rule");
}
@Test
public void testNoImplicitDeps_computedDefault() throws Exception {
MockRule computedDefaultRule =
() ->
MockRule.define(
"computed_default_rule",
attr("conspiracy", Type.STRING).value("space jam was a documentary"),
attr("dep", LABEL)
.allowedFileTypes(FileTypeSet.ANY_FILE)
.value(
new Attribute.ComputedDefault("conspiracy") {
@Override
public Object getDefault(AttributeMap rule) {
return rule.get("conspiracy", Type.STRING)
.equals("space jam was a documentary")
? Label.parseAbsoluteUnchecked("//test:foo")
: null;
}
}));
helper.useRuleClassProvider(setRuleClassProviders(computedDefaultRule).build());
writeFile("test/BUILD", "cc_library(name = 'foo')", "computed_default_rule(name = 'my_rule')");
String target = "//test:my_rule";
assertThat(evalToListOfStrings("deps(" + target + ")")).contains("//test:foo");
helper.setQuerySettings(Setting.NO_IMPLICIT_DEPS);
assertThat(eval("deps(" + target + ")")).isEqualTo(eval(target));
}
@Override
@Test
public void testLet() throws Exception {
getHelper().setWholeTestUniverseScope("//a,//b,//c,//d");
super.testLet();
}
@Override
@Test
public void testSet() throws Exception {
getHelper().setWholeTestUniverseScope("//a:*,//b:*,//c:*,//d:*");
super.testSet();
}
/** PatchTransition on --test_arg */
public static class TestArgPatchTransition implements PatchTransition {
String toOption;
String name;
public TestArgPatchTransition(String toOption, String name) {
this.toOption = toOption;
this.name = name;
}
public TestArgPatchTransition(String toOption) {
this(toOption, "TestArgPatchTransition");
}
@Override
public String getName() {
return this.name;
}
@Override
public BuildOptions patch(BuildOptions options, EventHandler eventHandler) {
BuildOptions result = options.clone();
result.get(TestOptions.class).testArguments = Collections.singletonList(toOption);
return result;
}
}
@Test
public void testMultipleTopLevelConfigurations() throws Exception {
MockRule transitionedRule =
() ->
MockRule.define(
"transitioned_rule",
(builder, env) -> builder.cfg(new TestArgPatchTransition("SET BY PATCH")).build());
MockRule untransitionedRule = () -> MockRule.define("untransitioned_rule");
helper.useRuleClassProvider(
setRuleClassProviders(transitionedRule, untransitionedRule).build());
writeFile(
"test/BUILD",
"transitioned_rule(name = 'transitioned_rule')",
"untransitioned_rule(name = 'untransitioned_rule')");
Set<T> result = eval("//test:transitioned_rule+//test:untransitioned_rule");
assertThat(result).hasSize(2);
Iterator<T> resultIterator = result.iterator();
assertThat(getConfiguration(resultIterator.next()))
.isNotEqualTo(getConfiguration(resultIterator.next()));
}
@Test
public abstract void testMultipleTopLevelConfigurations_nullConfigs() throws Exception;
@Test
public void testMultipleTopLevelConfigurations_multipleConfigsPrefersTopLevel() throws Exception {
MockRule ruleWithTransitionAndDep =
() ->
MockRule.define(
"rule_with_transition_and_dep",
(builder, env) ->
builder
.cfg(new TestArgPatchTransition("SET BY PATCH"))
.addAttribute(
attr("dep", LABEL).allowedFileTypes(FileTypeSet.ANY_FILE).build())
.build());
MockRule simpleRule = () -> MockRule.define("simple_rule");
helper.useRuleClassProvider(
setRuleClassProviders(ruleWithTransitionAndDep, simpleRule).build());
writeFile(
"test/BUILD",
"rule_with_transition_and_dep(name = 'top-level', dep = ':dep')",
"simple_rule(name = 'dep')");
helper.setUniverseScope("//test:*");
assertThat(getConfiguration(Iterables.getOnlyElement(eval("//test:dep"))))
.isNotEqualTo(getConfiguration(Iterables.getOnlyElement(eval("//test:top-level"))));
}
// LabelListAttr not currently supported.
@Override
public void testLabelsOperator() {}
// Wants to get the query environment without evaluation -- not worth it.
@Override
@Test
public void testEqualityOfOrderedThreadSafeImmutableSet() {}
@Override
public void testDefaultCopts() {}
@Override
public void testHdrsCheck() {}
@Override
public void testFilesetPackageDeps() {}
@Override
public void testRegressionBug1686119() {}
// The actual crosstool-related targets depended on are not the nominal crosstool label the test
// expects.
// "Extended rules" don't play nicely with actual analysis.
@Override
public void testNoDepsOnAspectAttributeWhenAspectMissing() {}
@Override
public void testNoDepsOnAspectAttributeWithNoImpicitDeps() {}
@Override
public void testHaveDepsOnAspectsAttributes() {}
// Can't handle loading-phase errors.
@Override
public void testStrictTestSuiteWithFile() {}
@Override
public void testTestsOperatorReportsMissingTargets() {}
@Override
public void testCycleInSkylark() {}
@Override
public void testCycleInSkylarkParentDir() {}
@Override
public void testCycleInSubpackage() {}
@Override
public void testRegression1309697() {}
// Can't handle cycles.
@Override
public void testDotDotDotWithCycle() {}
@Override
public void testDotDotDotWithUnrelatedCycle() {}
// ...
@Override
public void testQueryTimeLoadingTargetsBelowNonPackageDirectory() {}
@Override
public void testQueryTimeLoadingOfTargetsBelowPackageHappyPath() {}
@Override
public void testQueryTimeLoadingTargetsBelowMissingPackage() {}
// These tests clear the universe, getting rid of mock tools that are needed for analysis. Disable
// at least for now. Other than testSlashSlashDotDotDot, they're only testing visibility anyway.
@Override
public void testSlashSlashDotDotDot() {}
@Override
public void testVisible_default_private() {}
@Override
public void testVisible_default_public() {}
@Override
public void testPackageGroupAllBeneath() {}
@Override
public void testVisible_java_javatests() {}
@Override
public void testVisible_java_javatests_different_package() {}
@Override
public void testVisible_javatests_java() {}
@Override
public void testVisible_package_group() {}
@Override
public void testVisible_package_group_include() {}
@Override
public void testVisible_package_group_invisible() {}
@Override
public void testVisible_private_same_package() {}
@Override
public void testVisible_simple_different_subpackages() {}
@Override
public void testVisible_simple_package() {}
@Override
public void testVisible_simple_private() {}
@Override
public void testVisible_simple_public() {}
@Override
public void testVisible_simple_subpackages() {}
// test_suite rules aren't supported, since they're not configured targets.
@Override
public void testTestsOperatorFiltersByNegativeTag() {}
@Override
public void testTestsOperatorCrossesPackages() {}
@Override
public void testTestsOperatorHandlesCyclesGracefully() {}
@Override
public void testTestSuiteInTestsAttributeAndViceVersa() {}
@Override
public void testAmbiguousAllResolvesToTestSuiteNamedAll() {}
@Override
public void testTestSuiteWithFile() {}
@Override
public void testTestsOperatorFiltersByTagSizeAndEnv() {}
@Override
public void testTestsOperatorExpandsTestsAndExcludesNonTests() {}
// buildfiles() operator.
@Override
public void testBuildFiles() {}
@Override
public void testBuildFilesDoesNotReturnVisibilityOfBUILD() {}
@Override
public void testBuildFilesDoesNotReturnVisibilityOfRule() {}
@Override
public void testBuildfilesOfBuildfiles() {}
@Override
public void testBuildfilesWithDuplicates() {}
@Override
public void testTargetsFromBuildfilesAndRealTargets() {}
// siblings() operator.
@Override
public void testSiblings_DuplicatePackages() {}
@Override
public void testSiblings_SamePackageRdeps() {}
@Override
public void testSiblings_MatchesTargetNamedAll() {}
@Override
public void testSiblings_Simple() {}
@Override
public void testSiblings_WithBuildfiles() {}
// same_pkg_direct_rdeps() operator.
@Override
public void testSamePackageRdeps_simple() throws Exception {}
@Override
public void testSamePackageRdeps_duplicate() throws Exception {}
@Override
public void testSamePackageRdeps_two() throws Exception {}
@Override
public void testSamePackageRdeps_twoPackages() throws Exception {}
@Override
public void testSamePackageRdeps_crissCross() throws Exception {}
// We eagerly load all packages, so can't test that we don't load one.
@Override
@Test
public void testWildcardsDontLoadUnnecessaryPackages() {}
// Query needs a graph.
@Override
@Test
public void testGraphOrderOfWildcards() {}
// Visibility is checked in the analysis phase, so the post-analysis query done in this unit test
// would never occur because the visibility error would occur first.
@Override
@Test
public void testVisibleWithNonPackageGroupVisibility() throws Exception {}
// Visibility is checked in the analysis phase, so the post-analysis query done in this unit test
// would never occur because the visibility error would occur first.
@Override
@Test
public void testVisibleWithPackageGroupWithNonPackageGroupIncludes() throws Exception {}
// We don't support --nodep_deps=false.
@Override
@Test
public void testNodepDeps_False() throws Exception {}
// package_group instances have a null configuration and are filtered out by --host_deps=false.
@Override
@Test
public void testDefaultVisibilityReturnedInDeps_NonEmptyDependencyFilter() throws Exception {}
}