/* * 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.android; import static org.junit.Assert.fail; import com.facebook.buck.jvm.java.testutil.AbiCompilationModeTest; 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.fasterxml.jackson.databind.JsonNode; import java.io.IOException; import java.nio.file.Path; import java.nio.file.Paths; import org.hamcrest.Matchers; import org.junit.Assert; import org.junit.Before; import org.junit.Rule; import org.junit.Test; /** Tests for AndroidLibraryDescription */ public class AndroidLibraryDescriptionIntegrationTest extends AbiCompilationModeTest { @Rule public TemporaryPaths tmpFolder = new TemporaryPaths(); private ProjectWorkspace workspace; @Before public void setUp() throws IOException { workspace = TestDataHelper.createProjectWorkspaceForScenario( this, "android_library_dynamic_deps", tmpFolder); workspace.setUp(); setWorkspaceCompilationMode(workspace); } @Test public void testQueryDepsNotInvalidatedWhenRuleKeyHit() throws Exception { AssumeAndroidPlatform.assumeSdkIsAvailable(); // Build once to warm cache workspace.runBuckCommand("build", "//:android_resources").assertSuccess(); workspace.getBuildLog().assertTargetBuiltLocally("//:android_resources"); // Now, build again, assert we get the previous result workspace.runBuckCommand("build", "//:android_resources").assertSuccess(); workspace.getBuildLog().assertTargetHadMatchingRuleKey("//:android_resources"); // Now, edit lib_b, which is NOT part of the query result, workspace.replaceFileContents("B.java", "// method", "public static void foo() {}"); workspace.runBuckCommand("build", "//:android_resources").assertSuccess(); // And assert that we don't get rebuilt, BUT we had to re-run the query to find out workspace.getBuildLog().assertTargetHadMatchingInputRuleKey("//:android_resources"); } @Test public void testDepQueryResultsAreInvalidatedWhenDirectDepChanges() throws Exception { AssumeAndroidPlatform.assumeSdkIsAvailable(); // Build once to warm cache workspace.runBuckCommand("build", "//:android_libraries").assertSuccess(); workspace.getBuildLog().assertTargetBuiltLocally("//:android_libraries"); // Now, edit lib_a, which is NOT part of the query result, and assert the query does not run workspace.replaceFileContents("A.java", "// method", "public static void foo() {}"); workspace.runBuckCommand("build", "//:android_libraries").assertSuccess(); workspace.getBuildLog().assertTargetHadMatchingRuleKey("//:android_libraries"); // Now, add lib_a to the 'top_level' library and build again workspace.replaceFileContents("BUCK", "#placeholder", "':lib_a',"); // Have the src for the 'top_level' rule actually use its dependency workspace.replaceFileContents("TopLevel.java", "// placeholder", "public A a;"); // Build again workspace.runBuckCommand("build", "//:android_libraries"); // Now we should rebuild top_level, re-run the query, and rebuild android_libraries workspace.getBuildLog().assertTargetBuiltLocally("//:top_level"); workspace.getBuildLog().assertTargetBuiltLocally("//:android_libraries"); } @Test public void testDepQueryResultsAreInvalidatedWhenTransitiveDepChanges() throws Exception { AssumeAndroidPlatform.assumeSdkIsAvailable(); // Build once to warm cache workspace.runBuckCommand("build", "//:android_libraries").assertSuccess(); workspace.getBuildLog().assertTargetBuiltLocally("//:android_libraries"); // Now, edit lib_b, which is part of the query result, and assert the query is invalidated workspace.replaceFileContents("B.java", "// method", "public static void foo() {}"); workspace.runBuckCommand("build", "//:android_libraries").assertSuccess(); workspace.getBuildLog().assertTargetBuiltLocally("//:lib_b"); workspace.getBuildLog().assertTargetBuiltLocally("//:android_libraries"); } @Test public void testDepQueryResultsAreUpdatedWhenAttributesChange() throws Exception { AssumeAndroidPlatform.assumeSdkIsAvailable(); // Build once to warm cache workspace.runBuckCommand("build", "//:has_proc_params").assertSuccess(); workspace.getBuildLog().assertTargetBuiltLocally("//:has_proc_params"); // Now, add annotation proc params to lib_b and assert the query is updated workspace.replaceFileContents("BUCK", "#annotation_placeholder", "'example.foo=True',"); workspace.runBuckCommand("build", "//:has_proc_params").assertSuccess(); workspace.getBuildLog().assertTargetBuiltLocally("//:has_proc_params"); } @Test public void testDepQueryResultsCanTakeAdvantageOfDepFileRuleKey() throws Exception { AssumeAndroidPlatform.assumeSdkIsAvailable(); // Build once to warm cache workspace.runBuckCommand("build", "//:java_libraries").assertSuccess(); workspace.getBuildLog().assertTargetBuiltLocally("//:java_libraries"); // Now, edit lib_c, which is part of the query result, and assert the query is invalidated workspace.replaceFileContents("D.java", "// method", "public static void foo() {}"); workspace.runBuckCommand("build", "//:java_libraries").assertSuccess(); // But the libs above get a dep file hit workspace.getBuildLog().assertTargetHadMatchingDepfileRuleKey("//:java_libraries"); } @Test public void testDepQueryCanApplyToResources() throws Exception { AssumeAndroidPlatform.assumeSdkIsAvailable(); // Build once to warm cache workspace.runBuckCommand("build", "//:resources_from_query").assertSuccess(); } @Test public void testDepQueryWithClasspathDoesNotTraverseProvidedDeps() throws Exception { AssumeAndroidPlatform.assumeSdkIsAvailable(); // Should succeed because lib_c is a provided dep workspace.runBuckBuild("build", "//:provided_only"); // Should fail becuase 'C.class' is not added to the classpath because it's a provided dep workspace.runBuckCommand("build", "//:no_provided_deps").assertFailure(); } @Test public void testProvidedDepsQuery() throws Exception { AssumeAndroidPlatform.assumeSdkIsAvailable(); // Should succeed because required dep (lib_c) will be added as provided workspace.runBuckBuild("//:has_lib_c_from_provided_query").assertSuccess(); } @Test public void testProvidedDepsQueryDoesNotAffectPackaging() throws Exception { AssumeAndroidPlatform.assumeSdkIsAvailable(); workspace.runBuckCommand("build", "//:check_output_of_does_not_package_lib_c").assertSuccess(); String[] outputs = workspace .getFileContents(getOutputFile("//:check_output_of_does_not_package_lib_c")) .split("\\s"); // There should be a class entry for UsesC.java Assert.assertThat(outputs, Matchers.hasItemInArray("com/facebook/example/UsesC.class")); // But not one for C.java Assert.assertThat( outputs, Matchers.not(Matchers.hasItemInArray("com/facebook/example/C.class"))); } private Path getOutputFile(String targetName) { try { ProjectWorkspace.ProcessResult buildResult = workspace.runBuckCommand("targets", targetName, "--show-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(""); } } }