blob: 85c29cd455d393aec5c21a344362e1157e6faead [file] [log] [blame]
// Copyright 2019 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.cquery;
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.packages.BuildType.LABEL_LIST;
import com.google.common.eventbus.EventBus;
import com.google.devtools.build.lib.analysis.ConfiguredRuleClassProvider;
import com.google.devtools.build.lib.analysis.ConfiguredTarget;
import com.google.devtools.build.lib.analysis.config.TransitionFactories;
import com.google.devtools.build.lib.analysis.config.transitions.NoTransition;
import com.google.devtools.build.lib.analysis.config.transitions.TransitionFactory;
import com.google.devtools.build.lib.analysis.util.MockRule;
import com.google.devtools.build.lib.events.Event;
import com.google.devtools.build.lib.events.Reporter;
import com.google.devtools.build.lib.packages.Rule;
import com.google.devtools.build.lib.query2.PostAnalysisQueryEnvironment;
import com.google.devtools.build.lib.query2.cquery.CqueryOptions.Transitions;
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.util.FileTypeSet;
import java.util.ArrayList;
import java.util.LinkedHashSet;
import java.util.List;
import java.util.Set;
import javax.annotation.Nullable;
import org.junit.Before;
import org.junit.Test;
import org.junit.runner.RunWith;
import org.junit.runners.JUnit4;
/** Tests for the transitions output format. */
@RunWith(JUnit4.class)
public class TransitionsOutputFormatterTest extends ConfiguredTargetQueryTest {
private CqueryOptions options;
private Reporter reporter;
private final List<Event> events = new ArrayList<>();
@Nullable private TransitionFactory<Rule> trimmingTransitionFactory;
@Before
public final void setUpCqueryOptions() {
this.options = new CqueryOptions();
this.reporter = new Reporter(new EventBus(), events::add);
}
@Test
public void testTransitions_full() throws Exception {
setUpRules();
writeFile(
"test/BUILD",
"my_rule(name = 'rule_with_patch',",
" patched = [':foo', ':foo2', ':trimmed_foo'],",
")",
"my_rule(name = 'rule_with_split',",
" split = ':bar',",
")",
"simple_rule(name = 'foo')",
"simple_rule(name = 'foo2')",
"simple_rule(name = 'trimmed_foo')",
"simple_rule(name = 'bar')");
List<String> result = getOutput("deps(//test:rule_with_patch)", Transitions.FULL);
assertThat(result.get(0)).startsWith("TestArgPatchTransition -> //test:rule_with_patch");
assertThat(result.get(1)).startsWith(" patched#//test:foo#TestArgPatch");
assertThat(result.get(2))
.isEqualTo(" test_arg:[SET BY RULE CLASS PATCH] -> [[SET BY PATCH]]");
assertThat(result.get(3)).startsWith(" patched#//test:foo2#TestArgPatch");
assertThat(result.get(4)).isEqualTo(result.get(2));
assertThat(result.get(5))
.startsWith(
" patched#//test:trimmed_foo#(TestArgPatchTransition + TestArgPatchTransition(trim))");
assertThat(result.get(6))
.isEqualTo(" test_arg:[SET BY RULE CLASS PATCH] -> [[SET BY TRIM]]");
result = getOutput("deps(//test:rule_with_split)", Transitions.FULL);
assertThat(result.get(1)).startsWith(" split#//test:bar#TestArgSplitTransition");
assertThat(result.get(2))
.isEqualTo(
" test_arg:[SET BY RULE CLASS PATCH] -> [[SET BY SPLIT 1], [SET BY SPLIT 2]]");
}
@Test
public void testTransitions_lite() throws Exception {
setUpRules();
writeFile(
"test/BUILD",
"my_rule(name = 'rule_with_patch',",
" patched = [':foo', ':foo2'],",
")",
"my_rule(name = 'rule_with_split',",
" split = ':bar',",
")",
"simple_rule(name = 'foo')",
"simple_rule(name = 'foo2')",
"simple_rule(name = 'bar')");
List<String> result = getOutput("deps(//test:rule_with_patch)", Transitions.LITE);
assertThat(result.get(0)).startsWith("TestArgPatchTransition -> //test:rule_with_patch");
assertThat(result.get(1)).startsWith(" patched#//test:foo#TestArgPatchTransition");
assertThat(result.get(2)).startsWith(" patched#//test:foo2#TestArgPatchTransition");
result = getOutput("deps(//test:rule_with_split)", Transitions.LITE);
assertThat(result.get(0)).startsWith("TestArgPatchTransition -> //test:rule_with_split");
assertThat(result.get(1)).startsWith(" split#//test:bar#TestArgSplitTransition");
}
@Test
public void testTransitions_getRightConfigurations() throws Exception {
setUpRules();
writeFile(
"test/BUILD",
"my_rule(name = 'rule_with_patch',",
" patched = [':foo'],",
")",
"simple_rule(name = 'foo')");
List<String> result = getOutput("deps(//test:rule_with_patch)", Transitions.LITE);
String postPatchConfig = result.get(2).substring("//test:foo (".length());
assertThat(result.get(1)).endsWith(postPatchConfig);
}
@Test
public void testTransitions_noTransitions() throws Exception {
setUpRules();
writeFile(
"test/BUILD",
"simple_rule(name = 'foo')");
List<String> result = getOutput("//test:foo", Transitions.NONE);
assertThat(result).isEmpty();
assertThat(events).hasSize(1);
assertThat(events.get(0).getMessage())
.isEqualTo(
"Instead of using --output=transitions, set the --transitions flag explicitly to 'lite'"
+ " or 'full'");
}
private void setUpRules() throws Exception {
TransitionFactory<Rule> infixTrimmingTransitionFactory =
(rule) -> {
if (!rule.getName().contains("trimmed")) {
return NoTransition.INSTANCE;
}
// rename the transition so it's distinguishable from the others in tests
return new TestArgPatchTransition("SET BY TRIM", "TestArgPatchTransition(trim)");
};
TestArgPatchTransition ruleClassTransition =
new TestArgPatchTransition("SET BY RULE CLASS PATCH");
TestArgPatchTransition attributePatchTransition = new TestArgPatchTransition("SET BY PATCH");
TestArgSplitTransition attributeSplitTransitions =
new TestArgSplitTransition("SET BY SPLIT 1", "SET BY SPLIT 2");
MockRule ruleWithTransitions =
() ->
MockRule.define(
"my_rule",
(builder, env) ->
builder
.cfg(ruleClassTransition)
.add(
attr("patched", LABEL_LIST)
.allowedFileTypes(FileTypeSet.ANY_FILE)
.cfg(TransitionFactories.of(attributePatchTransition)))
.add(
attr("split", LABEL)
.allowedFileTypes(FileTypeSet.ANY_FILE)
.cfg(TransitionFactories.of(attributeSplitTransitions))));
MockRule simpleRule =
() ->
MockRule.define(
"simple_rule",
(builder, env) ->
builder
.add(attr("deps", LABEL_LIST).allowedFileTypes(FileTypeSet.ANY_FILE)));
ConfiguredRuleClassProvider ruleClassProvider =
setRuleClassProviders(ruleWithTransitions, simpleRule)
.overrideTrimmingTransitionFactoryForTesting(infixTrimmingTransitionFactory)
.build();
this.trimmingTransitionFactory = ruleClassProvider.getTrimmingTransitionFactory();
helper.useRuleClassProvider(ruleClassProvider);
}
private List<String> getOutput(String queryExpression, CqueryOptions.Transitions verbosity)
throws Exception {
QueryExpression expression = QueryParser.parse(queryExpression, getDefaultFunctions());
Set<String> targetPatternSet = new LinkedHashSet<>();
expression.collectTargetPatterns(targetPatternSet);
helper.setQuerySettings(Setting.NO_IMPLICIT_DEPS);
PostAnalysisQueryEnvironment<ConfiguredTarget> env =
((ConfiguredTargetQueryHelper) helper).getPostAnalysisQueryEnvironment(targetPatternSet);
options.transitions = verbosity;
// TODO(juliexxia): Test late-bound attributes.
TransitionsOutputFormatterCallback callback =
new TransitionsOutputFormatterCallback(
reporter,
options,
/*out=*/ null,
getHelper().getSkyframeExecutor(),
env.getAccessor(),
env.getHostConfiguration(),
trimmingTransitionFactory);
env.evaluateQuery(env.transformParsedQuery(QueryParser.parse(queryExpression, env)), callback);
return callback.getResult();
}
}