/*
* Copyright 2016-present Facebook, Inc.
*
* 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.facebook.buck.shell;
import static org.junit.Assert.assertEquals;
import static org.junit.Assert.fail;
import com.facebook.buck.testutil.integration.ProjectWorkspace;
import com.facebook.buck.testutil.integration.TemporaryPaths;
import com.facebook.buck.testutil.integration.TestDataHelper;
import com.facebook.buck.util.ObjectMappers;
import com.facebook.buck.util.environment.Platform;
import com.fasterxml.jackson.databind.JsonNode;
import com.google.common.collect.ImmutableList;
import java.nio.file.Path;
import java.nio.file.Paths;
import java.util.Arrays;
import java.util.List;
import java.util.stream.Collectors;
import org.junit.Assume;
import org.junit.Before;
import org.junit.Rule;
import org.junit.Test;
public class GenruleDescriptionIntegrationTest {
@Rule public TemporaryPaths temporaryFolder = new TemporaryPaths();
private ProjectWorkspace workspace;
@Before
public void setUp() throws Exception {
workspace =
TestDataHelper.createProjectWorkspaceForScenario(
this, "genrule_classpath_filtering", temporaryFolder)
.setUp();
}
@Test
public void classpathMacro() throws Exception {
expectGenruleOutput(
":echo_all",
ImmutableList.of("//:lib_a", "//:lib_b", "//:lib_d", "//annotations:proc-lib"));
}
@Test
public void depsFromClasspathMacroAreFilteredByDep() throws Exception {
expectGenruleOutput(":echo_with_ap_dep_is_proc", ImmutableList.of("//:lib_b"));
}
@Test
public void depsFromClasspathMacroAreFilteredByAnnotationProcessor() throws Exception {
expectGenruleOutput(":echo_with_annotation_processor_is_proc", ImmutableList.of("//:lib_b"));
}
@Test
public void depsFromClasspathMacroAreFilteredByPlugin() throws Exception {
expectGenruleOutput(":echo_with_plugin_is_proc", ImmutableList.of("//:lib_d"));
}
@Test
public void classpathMacroDepthLimited() throws Exception {
expectGenruleOutput(":echo_classpath_0", ImmutableList.of("//:lib_a"));
expectGenruleOutput(
":echo_classpath_1",
ImmutableList.of("//:lib_a", "//:lib_b", "//:lib_d", "//annotations:proc-lib"));
}
@Test
public void depsFromSetAreFilteredByKind() throws Exception {
expectGenruleOutput(":echo_with_kind_is_binary", ImmutableList.of("//:app"));
expectGenruleOutput(":echo_with_kind_is_library", ImmutableList.of("//:lib_a", "//:lib_b"));
}
@Test
public void depsFromDepsQuery() throws Exception {
expectGenruleOutput(
":echo_with_deps",
ImmutableList.of(
"//:app",
"//:lib_a",
"//:lib_b",
"//:lib_d",
"//annotations:proc",
"//annotations:proc-lib"));
}
@Test
public void depsFromDepsQueryWithDep1() throws Exception {
expectOutputPathsGenruleOutput(":echo_with_deps_1", ImmutableList.of(":app", ":lib_a"));
}
@Test
public void depsFromGenruleTransitive() throws Exception {
expectGenruleOutput(
":echo_with_genrule_deps",
ImmutableList.of(
"//:echo_with_annotation_processor_is_proc",
"//:lib_a",
"//:lib_b",
"//:lib_d",
"//annotations:proc",
"//annotations:proc-lib"));
}
@Test
public void filteredFromSet() throws Exception {
expectOutputPathsGenruleOutput(
":echo_from_filtered_set",
ImmutableList.of("//annotations:proc-lib", "//:app", "//:lib_a"));
}
@Test
public void testQueryResultsAreInvalidatedWhenDirectDepChanges() throws Exception {
// Build once to warm cache
workspace.runBuckCommand("build", "//:echo_deps_of_a").assertSuccess();
workspace.getBuildLog().assertTargetBuiltLocally("//:echo_deps_of_a");
// Now, build again, assert we get the previous result
workspace.runBuckCommand("build", "//:echo_deps_of_a").assertSuccess();
workspace.getBuildLog().assertNotTargetBuiltLocally("//:echo_deps_of_a");
// Now, add a dep to lib_a, which is the only explictly mentioned target and
// assert the query is run
workspace.replaceFileContents("BUCK", "#add_import_to_lib_a", "");
workspace.runBuckCommand("build", "//:echo_deps_of_a").assertSuccess();
// Query targets should NOT build dependencies
workspace.getBuildLog().assertTargetIsAbsent("//:lib_a");
// But should still be invalidated by their changes
workspace.getBuildLog().assertTargetBuiltLocally("//:echo_deps_of_a");
}
@Test
public void testQueryResultsAreInvalidatedWhenTransitiveDepChanges() throws Exception {
// Build once to warm cache
workspace.runBuckCommand("build", "//:echo_deps_of_a").assertSuccess();
workspace.getBuildLog().assertTargetBuiltLocally("//:echo_deps_of_a");
// Now, build again, assert we get the previous result
workspace.runBuckCommand("build", "//:echo_deps_of_a").assertSuccess();
workspace.getBuildLog().assertNotTargetBuiltLocally("//:echo_deps_of_a");
// Now, edit lib_b, which is NOT mentioned in the genrule, but IS a transitive dependency,
// and assert the query is re-run
workspace.replaceFileContents("BUCK", "#add_import_to_lib_b", "");
workspace.runBuckCommand("build", "//:echo_deps_of_a").assertSuccess();
// Query targets should NOT build dependencies
workspace.getBuildLog().assertTargetIsAbsent("//:lib_b");
// But should still be invalidated by their changes
workspace.getBuildLog().assertTargetBuiltLocally("//:echo_deps_of_a");
}
@Test
public void testQueryResultsReflectTransitiveChanges() throws Exception {
Assume.assumeFalse(Platform.detect().equals(Platform.WINDOWS));
// Assert the query result starts empty
expectGenruleOutput(":echo_with_has_debug_flag", ImmutableList.of());
// Now edit the annotation processor target and add the flag
workspace.replaceFileContents("annotations/BUCK", "#placeholder", "extra_arguments=['-g']");
// Assert that the query output updates
expectGenruleOutput(":echo_with_has_debug_flag", ImmutableList.of("//annotations:proc-lib"));
}
@Test
public void testQueryCanBeTheOnlyThingReferencingAFile() throws Exception {
// Assert the query can find the target even though it's the only reference to it
expectGenruleOutput(":ensure_parsing_if_this_is_only_ref", ImmutableList.of("//other:other"));
}
private void expectOutputPathsGenruleOutput(String genrule, List<String> expectedOutputs)
throws Exception {
expectGenruleOutput(
genrule,
expectedOutputs
.stream()
.map(this::getOutputFile)
.map(Path::toString)
.collect(Collectors.toList()));
}
private void expectGenruleOutput(String genrule, List<String> expectedOutputs) throws Exception {
ProjectWorkspace.ProcessResult buildResult = workspace.runBuckCommand("build", genrule);
buildResult.assertSuccess();
String outputFileContents = workspace.getFileContents(getOutputFile(genrule));
List<String> actualOutput =
Arrays.stream(outputFileContents.split("\\s"))
.map(String::trim)
.collect(Collectors.toList());
assertEquals(expectedOutputs, actualOutput);
}
private Path getOutputFile(String targetName) {
try {
ProjectWorkspace.ProcessResult buildResult =
workspace.runBuckCommand("targets", targetName, "--show-full-output", "--json");
buildResult.assertSuccess();
JsonNode jsonNode = ObjectMappers.READER.readTree(buildResult.getStdout()).get(0);
assert jsonNode.has("buck.outputPath");
return Paths.get(jsonNode.get("buck.outputPath").asText());
} catch (Exception e) {
fail(e.getMessage());
return Paths.get("");
}
}
}