blob: 0e8ee92d50e146ed9b3af33880855c3e256d668c [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.query2.cquery;
import static com.google.common.truth.Truth.assertThat;
import com.google.common.collect.ImmutableList;
import com.google.common.eventbus.EventBus;
import com.google.devtools.build.lib.events.Event;
import com.google.devtools.build.lib.events.Reporter;
import com.google.devtools.build.lib.packages.LabelPrinter;
import com.google.devtools.build.lib.query2.PostAnalysisQueryEnvironment;
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 java.io.ByteArrayOutputStream;
import java.io.PrintStream;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.LinkedHashSet;
import java.util.List;
import java.util.Set;
import java.util.stream.Collectors;
import org.junit.Before;
import org.junit.Test;
/** Tests cquery's {@link --output=graph} format. */
public class GraphOutputFormatterCallbackTest extends ConfiguredTargetQueryTest {
private CqueryOptions options;
private Reporter reporter;
private final List<Event> events = new ArrayList<>();
@Before
public final void defineSimpleRule() throws Exception {
writeFile(
"defs/defs.bzl",
"def _impl(ctx):",
" pass",
"simple_rule = rule(",
" implementation = _impl,",
" attrs = {",
" 'deps': attr.label_list(allow_files = True),",
" 'host_deps': attr.label_list(cfg = 'host')",
" }",
")");
writeFile("defs/BUILD");
}
@Before
public final void setUpCqueryOptions() {
this.options = new CqueryOptions();
options.graphNodeStringLimit = 512;
this.reporter = new Reporter(new EventBus(), events::add);
}
private List<String> getOutput(String queryExpression) throws Exception {
QueryExpression expression = QueryParser.parse(queryExpression, getDefaultFunctions());
Set<String> targetPatternSet = new LinkedHashSet<>();
expression.collectTargetPatterns(targetPatternSet);
helper.setQuerySettings(Setting.NO_IMPLICIT_DEPS);
PostAnalysisQueryEnvironment<KeyedConfiguredTarget> env =
((ConfiguredTargetQueryHelper) helper).getPostAnalysisQueryEnvironment(targetPatternSet);
ByteArrayOutputStream output = new ByteArrayOutputStream();
GraphOutputFormatterCallback callback =
new GraphOutputFormatterCallback(
reporter,
options,
new PrintStream(output),
getHelper().getSkyframeExecutor(),
env.getAccessor(),
ct -> env.getFwdDeps(ImmutableList.of(ct)),
LabelPrinter.legacy());
env.evaluateQuery(expression, callback);
return Arrays.asList(output.toString().split(System.lineSeparator()));
}
/** Convenience method for easily injecting a config hash into an expected output sequence. */
private static List<String> withConfigHash(String configHash, String... pattern) {
return Arrays.stream(pattern)
.map(entry -> entry.replace("%s", configHash))
.collect(Collectors.toList());
}
@Test
public void basicGraph() throws Exception {
writeFile(
"test/BUILD",
"load('//defs:defs.bzl', 'simple_rule')",
"simple_rule(name = 'a', deps = [':b', ':c'])",
"simple_rule(name = 'b', deps = [':d'])",
"simple_rule(name = 'c')",
"simple_rule(name = 'd')");
List<String> output = getOutput("deps(//test:a)");
String firstNode = output.get(2);
String configHash = firstNode.substring(firstNode.indexOf("(") + 1, firstNode.length() - 2);
assertThat(getOutput("deps(//test:a)"))
.isEqualTo(
withConfigHash(
configHash,
"digraph mygraph {",
" node [shape=box];",
" \"//test:a (%s)\"",
" \"//test:a (%s)\" -> \"//test:b (%s)\"",
" \"//test:a (%s)\" -> \"//test:c (%s)\"",
" \"//test:c (%s)\"",
" \"//test:b (%s)\"",
" \"//test:b (%s)\" -> \"//test:d (%s)\"",
" \"//test:d (%s)\"",
"}"));
}
@Test
public void factorEquivalentNodes() throws Exception {
options.graphFactored = true;
writeFile(
"test/BUILD",
"load('//defs:defs.bzl', 'simple_rule')",
"simple_rule(name = 'a', deps = [':b', ':c'])",
"simple_rule(name = 'b', deps = [':d'])",
"simple_rule(name = 'c', deps = [':d'])",
"simple_rule(name = 'd')");
List<String> output = getOutput("deps(//test:a)");
String firstNode = output.get(2);
String configHash = firstNode.substring(firstNode.indexOf("(") + 1, firstNode.length() - 2);
assertThat(getOutput("deps(//test:a)"))
.isEqualTo(
withConfigHash(
configHash,
"digraph mygraph {",
" node [shape=box];",
" \"//test:a (%s)\"",
" \"//test:a (%s)\" -> \"//test:b (%s)\\n//test:c (%s)\"",
" \"//test:b (%s)\\n//test:c (%s)\"",
" \"//test:b (%s)\\n//test:c (%s)\" -> \"//test:d (%s)\"",
" \"//test:d (%s)\"",
"}"));
}
// TODO(b/203203933): Replace "host" with "exec" throughout this test.
@Test
public void nullAndHostDeps() throws Exception {
writeFile(
"test/BUILD",
"load('//defs:defs.bzl', 'simple_rule')",
"simple_rule(name = 'a', deps = [':b', ':file.src'], host_deps = [':host_dep'])",
"simple_rule(name = 'b')",
"simple_rule(name = 'host_dep')");
writeFile("test/file.src");
List<String> output = getOutput("deps(//test:a)");
String firstNode = output.get(2);
String configHash = firstNode.substring(firstNode.indexOf("(") + 1, firstNode.length() - 2);
String hostNode = output.get(6);
String execConfigHash = hostNode.substring(hostNode.indexOf("(") + 1, hostNode.length() - 2);
assertThat(getOutput("deps(//test:a)"))
.isEqualTo(
withConfigHash(
configHash,
"digraph mygraph {",
" node [shape=box];",
" \"//test:a (%s)\"",
" \"//test:a (%s)\" -> \"//test:b (%s)\"",
" \"//test:a (%s)\" -> \"//test:file.src (null)\"",
" \"//test:a (%s)\" -> \"//test:host_dep (" + execConfigHash + ")\"",
" \"//test:host_dep (" + execConfigHash + ")\"",
" \"//test:file.src (null)\"",
" \"//test:b (%s)\"",
"}"));
}
@Test
public void selectsResolvedAndRemoved() throws Exception {
writeFile(
"test/BUILD",
"load('//defs:defs.bzl', 'simple_rule')",
"config_setting(name = 'use_a', define_values = {'a': '1'})",
"simple_rule(name = 'a', deps = select({",
" ':use_a': [':dep_with_a'],",
" '//conditions:default': [':default_dep'],",
"}))",
"simple_rule(name = 'dep_with_a')",
"simple_rule(name = 'default_dep')");
getHelper().useConfiguration("--define", "a=1");
List<String> output = getOutput("deps(//test:a)");
String firstNode = output.get(2);
String configHash = firstNode.substring(firstNode.indexOf("(") + 1, firstNode.length() - 2);
assertThat(getOutput("deps(//test:a)"))
.isEqualTo(
withConfigHash(
configHash,
"digraph mygraph {",
" node [shape=box];",
" \"//test:a (%s)\"",
" \"//test:a (%s)\" -> \"//test:dep_with_a (%s)\"",
" \"//test:a (%s)\" -> \"//test:use_a (%s)\"",
" \"//test:use_a (%s)\"",
" \"//test:dep_with_a (%s)\"",
"}"));
}
}