| /* |
| * Copyright 2013-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.java; |
| |
| import static java.nio.file.StandardOpenOption.WRITE; |
| import static org.hamcrest.CoreMatchers.equalTo; |
| import static org.hamcrest.CoreMatchers.not; |
| import static org.junit.Assert.assertEquals; |
| import static org.junit.Assert.assertFalse; |
| import static org.junit.Assert.assertNotEquals; |
| import static org.junit.Assert.assertThat; |
| import static org.junit.Assert.assertTrue; |
| |
| import com.facebook.buck.java.abi.AbiWriterProtocol; |
| import com.facebook.buck.testutil.integration.DebuggableTemporaryFolder; |
| import com.facebook.buck.testutil.integration.ProjectWorkspace; |
| import com.facebook.buck.testutil.integration.ProjectWorkspace.ProcessResult; |
| import com.facebook.buck.testutil.integration.TestDataHelper; |
| import com.google.common.base.Charsets; |
| import com.google.common.base.Strings; |
| import com.google.common.io.Files; |
| |
| import org.junit.Rule; |
| import org.junit.Test; |
| |
| import java.io.File; |
| import java.io.IOException; |
| import java.nio.file.FileSystem; |
| import java.nio.file.FileSystems; |
| import java.nio.file.Path; |
| |
| /** |
| * Integration test that verifies that a {@link DefaultJavaLibraryRule} writes its ABI key as part |
| * of compilation. |
| */ |
| public class DefaultJavaLibraryRuleIntegrationTest { |
| |
| @Rule |
| public DebuggableTemporaryFolder tmp = new DebuggableTemporaryFolder(); |
| |
| private ProjectWorkspace workspace; |
| |
| @Test |
| public void testBuildJavaLibraryWithoutSrcsAndVerifyAbi() throws IOException { |
| workspace = TestDataHelper.createProjectWorkspaceForScenario( |
| this, "abi", tmp); |
| workspace.setUp(); |
| |
| // Run `buck build`. |
| ProcessResult buildResult = workspace.runBuckCommand("build", "//:no_srcs"); |
| buildResult.assertExitCode("Successful build should exit with 0.", 0); |
| File outputFile = workspace.getFile("buck-out/gen/lib__no_srcs__output/no_srcs.jar"); |
| assertTrue(outputFile.exists()); |
| // TODO(mbolin): When we produce byte-for-byte identical JAR files across builds, do: |
| // |
| // HashCode hashOfOriginalJar = Files.hash(outputFile, Hashing.sha1()); |
| // |
| // And then compare that to the output when //:no_srcs is built again with --no-cache. |
| long sizeOfOriginalJar = outputFile.length(); |
| |
| // This verifies that the ABI key was written correctly. |
| workspace.verify(); |
| |
| // Verify the build cache. |
| File buildCache = workspace.getFile("cache_dir"); |
| assertTrue(buildCache.isDirectory()); |
| assertEquals("There should be one entry (a zip) in the build cache.", |
| 1, |
| buildCache.listFiles().length); |
| |
| // Verify the ABI key entry in the build cache. |
| Path artifactZip = buildCache.listFiles()[0].toPath(); |
| FileSystem zipFs = FileSystems.newFileSystem(artifactZip, /* loader */ null); |
| Path abiKeyEntry = zipFs.getPath("/buck-out/bin/.no_srcs/metadata/ABI_KEY"); |
| assertEquals(AbiWriterProtocol.EMPTY_ABI_KEY, |
| new String(java.nio.file.Files.readAllBytes(abiKeyEntry))); |
| |
| // Run `buck clean`. |
| ProcessResult cleanResult = workspace.runBuckCommand("clean"); |
| cleanResult.assertExitCode("Successful clean should exit with 0.", 0); |
| assertEquals("The build cache should still exist.", 1, buildCache.listFiles().length); |
| |
| // Corrupt the build cache! |
| Path outputInZip = zipFs.getPath("/buck-out/gen/lib__no_srcs__output/no_srcs.jar"); |
| java.nio.file.Files.write(outputInZip, "Hello world!".getBytes(), WRITE); |
| zipFs.close(); |
| |
| // Run `buck build` again. |
| ProcessResult buildResult2 = workspace.runBuckCommand("build", "//:no_srcs"); |
| buildResult2.assertExitCode("Successful build should exit with 0.", 0); |
| assertTrue(outputFile.isFile()); |
| assertEquals( |
| "The content of the output file will be 'Hello World!' if it is read from the build cache.", |
| "Hello world!", |
| Files.toString(outputFile, Charsets.UTF_8)); |
| |
| // Run `buck clean` followed by `buck build` yet again, but this time, specify `--no-cache`. |
| ProcessResult cleanResult2 = workspace.runBuckCommand("clean"); |
| cleanResult2.assertExitCode("Successful clean should exit with 0.", 0); |
| ProcessResult buildResult3 = workspace.runBuckCommand("build", "--no-cache", "//:no_srcs"); |
| buildResult3.assertExitCode(0); |
| assertNotEquals( |
| "The contents of the file should no longer be pulled from the corrupted build cache.", |
| "Hello world!", |
| Files.toString(outputFile, Charsets.UTF_8)); |
| assertEquals( |
| "We cannot do a byte-for-byte comparision with the original JAR because timestamps might " + |
| "have changed, but we verify that they are the same size, as a proxy.", |
| sizeOfOriginalJar, |
| outputFile.length()); |
| } |
| |
| @Test |
| public void testFileChangeThatDoesNotModifyAbiAvoidsRebuild() throws IOException { |
| workspace = TestDataHelper.createProjectWorkspaceForScenario( |
| this, "rulekey_changed_while_abi_stable", tmp); |
| workspace.setUp(); |
| |
| // Run `buck build`. |
| ProcessResult buildResult = workspace.runBuckCommand("build", "//:biz"); |
| buildResult.assertExitCode("Successful build should exit with 0.", 0); |
| |
| String utilRuleKey = getContents("buck-out/bin/.util/metadata/RULE_KEY"); |
| String utilRuleKeyNoDeps = getContents("buck-out/bin/.util/metadata/RULE_KEY_NO_DEPS"); |
| String utilAbi = getContents("buck-out/bin/.util/metadata/ABI_KEY"); |
| String utilAbiForDeps = getContents("buck-out/bin/.util/metadata/ABI_KEY_FOR_DEPS"); |
| |
| String bizRuleKey = getContents("buck-out/bin/.biz/metadata/RULE_KEY"); |
| String bizRuleKeyNoDeps = getContents("buck-out/bin/.biz/metadata/RULE_KEY_NO_DEPS"); |
| String bizAbi = getContents("buck-out/bin/.biz/metadata/ABI_KEY"); |
| String bizAbiForDeps = getContents("buck-out/bin/.biz/metadata/ABI_KEY_FOR_DEPS"); |
| |
| long utilJarSize = workspace.getFile("buck-out/gen/lib__util__output/util.jar").length(); |
| long bizJarLastModified = workspace.getFile("buck-out/gen/lib__biz__output/biz.jar") |
| .lastModified(); |
| |
| // TODO(mbolin): Run uber-biz.jar and verify it prints "Hello World!\n". |
| |
| // Edit Util.java in a way that does not affect its ABI. |
| String originalUtilJava = getContents("Util.java"); |
| String replacementContents = originalUtilJava.replace("Hello World", "Hola Mundo"); |
| Files.write(replacementContents, workspace.getFile("Util.java"), Charsets.UTF_8); |
| |
| // Run `buck build` again. |
| ProcessResult buildResult2 = workspace.runBuckCommand("build", "//:biz"); |
| buildResult2.assertExitCode("Successful build should exit with 0.", 0); |
| |
| assertThat(utilRuleKey, not(equalTo(getContents("buck-out/bin/.util/metadata/RULE_KEY")))); |
| assertThat(utilRuleKeyNoDeps, not(equalTo(getContents("buck-out/bin/.util/metadata/RULE_KEY_NO_DEPS")))); |
| assertEquals(utilAbi, getContents("buck-out/bin/.util/metadata/ABI_KEY")); |
| assertEquals(utilAbiForDeps, getContents("buck-out/bin/.util/metadata/ABI_KEY_FOR_DEPS")); |
| |
| assertThat(bizRuleKey, not(equalTo(getContents("buck-out/bin/.biz/metadata/RULE_KEY")))); |
| assertEquals(bizRuleKeyNoDeps, getContents("buck-out/bin/.biz/metadata/RULE_KEY_NO_DEPS")); |
| assertEquals(bizAbi, getContents("buck-out/bin/.biz/metadata/ABI_KEY")); |
| assertEquals(bizAbiForDeps, getContents("buck-out/bin/.biz/metadata/ABI_KEY_FOR_DEPS")); |
| |
| assertThat( |
| "util.jar should have been rewritten, so its file size should have changed.", |
| utilJarSize, |
| not(equalTo(workspace.getFile("buck-out/gen/lib__util__output/util.jar").length()))); |
| assertEquals( |
| "biz.jar should not have been rewritten, so its last-modified time should be the same.", |
| bizJarLastModified, |
| workspace.getFile("buck-out/gen/lib__biz__output/biz.jar").lastModified()); |
| |
| // TODO(mbolin): Run uber-biz.jar and verify it prints "Hola Mundo!\n". |
| |
| // TODO(mbolin): This last scenario that is being tested would be better as a unit test. |
| // Run `buck build` one last time. This ensures that a dependency java_library() rule (:util) |
| // that is built via BuildRuleSuccess.Type.MATCHING_DEPS_ABI_AND_RULE_KEY_NO_DEPS does not |
| // explode when its dependent rule (:biz) invokes the dependency's getAbiKey() method as part of |
| // its own getAbiKeyForDeps(). |
| ProcessResult buildResult3 = workspace.runBuckCommand("build", "//:biz"); |
| buildResult3.assertExitCode("Successful build should exit with 0.", 0); |
| } |
| |
| /** |
| * Asserts that the specified file exists and returns its contents. |
| */ |
| private String getContents(String relativePathToFile) throws IOException { |
| File file = workspace.getFile(relativePathToFile); |
| assertTrue(relativePathToFile + " should exist and be an ordinary file.", file.exists()); |
| String content = Strings.nullToEmpty(Files.toString(file, Charsets.UTF_8)).trim(); |
| assertFalse(relativePathToFile + " should not be empty.", content.isEmpty()); |
| return content; |
| } |
| } |