/*
* Copyright 2015-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.rules.keys;
import static org.junit.Assert.assertThat;
import com.facebook.buck.log.LogFormatter;
import com.facebook.buck.log.Logger;
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.ProcessExecutor;
import com.facebook.buck.util.environment.Platform;
import com.google.common.base.Joiner;
import com.google.common.base.Preconditions;
import com.google.common.collect.ImmutableList;
import java.io.IOException;
import java.nio.file.Files;
import java.nio.file.Path;
import java.nio.file.Paths;
import java.util.Optional;
import java.util.logging.FileHandler;
import java.util.logging.Level;
import java.util.stream.Stream;
import org.hamcrest.Matchers;
import org.junit.After;
import org.junit.Assume;
import org.junit.Before;
import org.junit.Rule;
import org.junit.Test;
public class DiffRuleKeysScriptIntegrationTest {
@Rule public TemporaryPaths tmp = new TemporaryPaths();
private Logger ruleKeyBuilderLogger;
private Level previousRuleKeyBuilderLevel;
private int lastPositionInLog;
@Before
public void enableVerboseRuleKeys() throws Exception {
lastPositionInLog = 0;
ruleKeyBuilderLogger = Logger.get(RuleKeyBuilder.class);
previousRuleKeyBuilderLevel = ruleKeyBuilderLogger.getLevel();
ruleKeyBuilderLogger.setLevel(Level.FINER);
Path fullLogFilePath = tmp.getRoot().resolve(getLogFilePath());
Files.createDirectories(fullLogFilePath.getParent());
FileHandler handler = new FileHandler(fullLogFilePath.toString());
handler.setFormatter(new LogFormatter());
ruleKeyBuilderLogger.addHandler(handler);
}
@After
public void disableVerboseRuleKeys() {
ruleKeyBuilderLogger.setLevel(previousRuleKeyBuilderLevel);
}
@Test
public void fileContentsChanged() throws Exception {
ProjectWorkspace workspace =
TestDataHelper.createProjectWorkspaceForScenario(this, "diff_rulekeys_script", tmp);
workspace.setUp();
invokeBuckCommand(workspace, "buck-0.log");
workspace.writeContentsToPath("public class JavaLib1 { /* change */ }", "JavaLib1.java");
invokeBuckCommand(workspace, "buck-1.log");
String expectedResult =
Joiner.on('\n')
.join(
"Change details for [//:java_lib_1]",
" (srcs):",
" -[path(JavaLib1.java:e3506ff7c11f638458d08120d54f186dc79ddada)]",
" +[path(JavaLib1.java:7d82c86f964af479abefa21da1f19b1030649314)]",
"");
assertThat(runRuleKeyDiffer(workspace), Matchers.equalTo(expectedResult));
}
@Test
public void pathAdded() throws Exception {
ProjectWorkspace workspace =
TestDataHelper.createProjectWorkspaceForScenario(this, "diff_rulekeys_script", tmp);
workspace.setUp();
invokeBuckCommand(workspace, "buck-0.log");
workspace.writeContentsToPath("public class JavaLib3 { /* change */ }", "JavaLib3.java");
invokeBuckCommand(workspace, "buck-1.log");
String expectedResult =
Joiner.on('\n')
.join(
"Change details for [//:java_lib_2]",
" (srcs):",
" -[<missing>]",
" -[container(LIST,len=1)]",
" +[container(LIST,len=2)]",
" +[path(JavaLib3.java:3396c5e71e9fad8e8f177af9d842f1b9b67bfb46)]",
"");
assertThat(runRuleKeyDiffer(workspace), Matchers.equalTo(expectedResult));
}
@Test
public void javacOptionsChanged() throws Exception {
ProjectWorkspace workspace =
TestDataHelper.createProjectWorkspaceForScenario(this, "diff_rulekeys_script", tmp);
workspace.setUp();
writeBuckConfig(workspace, "6");
invokeBuckCommand(workspace, "buck-0.log");
writeBuckConfig(workspace, "7");
invokeBuckCommand(workspace, "buck-1.log");
String expectedResult =
Joiner.on('\n')
.join(
"Change details for " + "[//:java_lib_2->compileStepFactory->javacOptions]",
" (sourceLevel):",
" -[string(\"6\")]",
" +[string(\"7\")]",
" (targetLevel):",
" -[string(\"6\")]",
" +[string(\"7\")]",
"");
assertThat(runRuleKeyDiffer(workspace), Matchers.equalTo(expectedResult));
}
@Test
public void cxxHeaderChanged() throws Exception {
Assume.assumeTrue(Platform.detect() != Platform.WINDOWS);
ProjectWorkspace workspace =
TestDataHelper.createProjectWorkspaceForScenario(this, "diff_rulekeys_script", tmp);
workspace.setUp();
invokeBuckCommand(workspace, ImmutableList.of("//cxx:cxx_bin"), "buck-0.log");
workspace.writeContentsToPath("// Different contents", "cxx/a.h");
invokeBuckCommand(workspace, ImmutableList.of("//cxx:cxx_bin"), "buck-1.log");
assertThat(
runRuleKeyDiffer(workspace, "//cxx:cxx_bin"),
Matchers.stringContainsInOrder(
"Change details for [//cxx:cxx_bin#compile-a.cpp.", /* hash */
",default->preprocessDelegate->includes]",
"(include(cxx/a.h)):",
"-[path(cxx/a.h:", /*hash*/
")]",
"+[path(cxx/a.h:", /*hash*/
")]"));
}
@Test
public void dependencyAdded() throws Exception {
ProjectWorkspace workspace =
TestDataHelper.createProjectWorkspaceForScenario(this, "diff_rulekeys_script", tmp);
workspace.setUp();
invokeBuckCommand(workspace, "buck-0.log");
workspace.writeContentsToPath(
Joiner.on('\n')
.join(
"java_library(",
" name = 'java_lib_1',",
" srcs = [ 'JavaLib1.java'],",
")",
"java_library(",
" name = 'java_lib_3',",
" srcs = ['JavaLib1.java'],",
" deps = [':java_lib_1']",
")",
"java_library(",
" name = 'java_lib_2',",
" srcs = ['JavaLib2.java'],",
" deps = [':java_lib_1', ':java_lib_3']",
")"),
"BUCK");
invokeBuckCommand(workspace, "buck-1.log");
assertThat(
runRuleKeyDiffer(workspace),
Matchers.stringContainsInOrder(
"Change details for [//:java_lib_2]",
" (abiClasspath):",
" -[<missing>]",
" +[\"//:java_lib_3#class-abi\"@ruleKey(sha1=", /* some rulekey */
")]",
" (buck.declaredDeps):",
" -[<missing>]",
" +[\"//:java_lib_3\"@ruleKey(sha1=", /* some rulekey */
")]",
" (buck.extraDeps):",
" -[<missing>]",
" +[\"//:java_lib_3#class-abi\"@ruleKey(sha1=", /* some rulekey */
")]"));
}
@Test
public void diffAll() throws Exception {
ProjectWorkspace workspace =
TestDataHelper.createProjectWorkspaceForScenario(this, "diff_rulekeys_script", tmp);
workspace.setUp();
invokeBuckCommand(workspace, "buck-0.log");
workspace.writeContentsToPath("public class JavaLib1 { /* change */ }", "JavaLib1.java");
workspace.writeContentsToPath("public class JavaLib3 { /* change */ }", "JavaLib3.java");
invokeBuckCommand(workspace, "buck-1.log");
assertThat(
runRuleKeyDiffer(workspace, ""),
Matchers.stringContainsInOrder(
"Change details for [//:java_lib_all]",
" (srcs):",
" -[<missing>]",
" +[container(LIST,len=1)]",
" +[path(JavaLib3.java:3396c5e71e9fad8e8f177af9d842f1b9b67bfb46)]",
"Change details for [//:java_lib_2]",
" (srcs):",
" -[<missing>]",
" -[container(LIST,len=1)]",
" +[container(LIST,len=2)]",
" +[path(JavaLib3.java:3396c5e71e9fad8e8f177af9d842f1b9b67bfb46)]",
"Change details for [//:java_lib_1]",
" (srcs):",
" -[path(JavaLib1.java:e3506ff7c11f638458d08120d54f186dc79ddada)]",
" +[path(JavaLib1.java:7d82c86f964af479abefa21da1f19b1030649314)]"));
}
private void writeBuckConfig(ProjectWorkspace projectWorkspace, String javaVersion)
throws Exception {
projectWorkspace.writeContentsToPath(
Joiner.on('\n')
.join("[java]", "source_level = " + javaVersion, "target_level = " + javaVersion),
".buckconfig");
}
private String runRuleKeyDiffer(ProjectWorkspace workspace)
throws IOException, InterruptedException {
return runRuleKeyDiffer(workspace, "//:java_lib_2");
}
private String runRuleKeyDiffer(ProjectWorkspace workspace, String target)
throws IOException, InterruptedException {
String cmd = Platform.detect() == Platform.WINDOWS ? "python" : "python2.7";
ProcessExecutor.Result result =
workspace.runCommand(
cmd,
Paths.get("scripts", "diff_rulekeys.py").toAbsolutePath().toString(),
tmp.getRoot().resolve("buck-0.log").toString(),
tmp.getRoot().resolve("buck-1.log").toString(),
target);
assertThat(result.getStderr(), Matchers.equalTo(Optional.of("")));
assertThat(result.getExitCode(), Matchers.is(0));
String stdout = result.getStdout().get();
String comparingRuleString = "Comparing rules...\n";
int i = stdout.indexOf(comparingRuleString);
Preconditions.checkState(i != -1);
return stdout.substring(i + comparingRuleString.length());
}
private void invokeBuckCommand(ProjectWorkspace workspace, String logOut) throws IOException {
invokeBuckCommand(workspace, ImmutableList.of("//:"), logOut);
}
private void invokeBuckCommand(
ProjectWorkspace workspace, ImmutableList<String> targets, String logOut) throws IOException {
ImmutableList<String> args = ImmutableList.of("targets", "--show-rulekey");
ProjectWorkspace.ProcessResult buckCommandResult =
workspace.runBuckCommand(
Stream.concat(args.stream(), targets.stream()).toArray(String[]::new));
buckCommandResult.assertSuccess();
String fullLogContents = workspace.getFileContents(getLogFilePath());
String logContentsForThisInvocation = fullLogContents.substring(lastPositionInLog);
lastPositionInLog += logContentsForThisInvocation.length();
workspace.writeContentsToPath(logContentsForThisInvocation, logOut);
}
private Path getLogFilePath() {
return tmp.getRoot().resolve("buck.test.log");
}
}