/*
* 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.io.ProjectFilesystem;
import com.facebook.buck.model.BuildTarget;
import com.facebook.buck.model.BuildTargetFactory;
import com.facebook.buck.rules.AddToRuleKey;
import com.facebook.buck.rules.ArchiveMemberSourcePath;
import com.facebook.buck.rules.BuildRuleResolver;
import com.facebook.buck.rules.DefaultBuildTargetSourcePath;
import com.facebook.buck.rules.DefaultTargetNodeToBuildRuleTransformer;
import com.facebook.buck.rules.FakeBuildRule;
import com.facebook.buck.rules.FakeDepFileBuildRule;
import com.facebook.buck.rules.PathSourcePath;
import com.facebook.buck.rules.RuleKeyAppendable;
import com.facebook.buck.rules.RuleKeyObjectSink;
import com.facebook.buck.rules.SourcePath;
import com.facebook.buck.rules.SourcePathResolver;
import com.facebook.buck.rules.SourcePathRuleFinder;
import com.facebook.buck.rules.TargetGraph;
import com.facebook.buck.testutil.FakeFileHashCache;
import com.facebook.buck.testutil.FakeProjectFilesystem;
import com.google.common.collect.ImmutableList;
import com.google.common.collect.ImmutableMap;
import com.google.common.collect.ImmutableSet;
import com.google.common.hash.HashCode;
import java.nio.file.NoSuchFileException;
import java.nio.file.Path;
import java.nio.file.Paths;
import java.util.function.Predicate;
import org.hamcrest.Matchers;
import org.junit.Assert;
import org.junit.Test;
public class DependencyFileRuleKeyFactoryTest {
@Test
public void testKeysWhenInputPathContentsChanges() throws Exception {
ProjectFilesystem filesystem = new FakeProjectFilesystem();
BuildRuleResolver ruleResolver = newRuleResolver();
SourcePathRuleFinder ruleFinder = new SourcePathRuleFinder(ruleResolver);
SourcePathResolver pathResolver = new SourcePathResolver(ruleFinder);
SourcePath usedSourcePath = new PathSourcePath(filesystem, Paths.get("usedInput"));
SourcePath unusedSourcePath = new PathSourcePath(filesystem, Paths.get("unusedInput"));
SourcePath noncoveredSourcePath = new PathSourcePath(filesystem, Paths.get("noncoveredInput"));
SourcePath interestingSourcePath = new PathSourcePath(filesystem, Paths.get("interestingIn"));
testKeysWhenInputContentsChanges(
ruleFinder,
pathResolver,
usedSourcePath,
unusedSourcePath,
noncoveredSourcePath,
interestingSourcePath,
pathResolver.getAbsolutePath(usedSourcePath),
pathResolver.getAbsolutePath(unusedSourcePath),
pathResolver.getAbsolutePath(noncoveredSourcePath),
pathResolver.getAbsolutePath(interestingSourcePath),
DependencyFileEntry.fromSourcePath(usedSourcePath, pathResolver));
}
@Test
public void testKeysWhenInputTargetOutputChanges() throws Exception {
BuildRuleResolver ruleResolver = newRuleResolver();
SourcePathRuleFinder ruleFinder = new SourcePathRuleFinder(ruleResolver);
SourcePathResolver pathResolver = new SourcePathResolver(ruleFinder);
BuildTarget usedTarget = BuildTargetFactory.newInstance("//:used");
BuildTarget unusedTarget = BuildTargetFactory.newInstance("//:unused");
BuildTarget noncoveredTarget = BuildTargetFactory.newInstance("//:noncovered");
BuildTarget interestingTarget = BuildTargetFactory.newInstance("//:interesting");
SourcePath usedSourcePath = new DefaultBuildTargetSourcePath(usedTarget);
SourcePath unusedSourcePath = new DefaultBuildTargetSourcePath(unusedTarget);
SourcePath noncoveredSourcePath = new DefaultBuildTargetSourcePath(noncoveredTarget);
SourcePath interestingSourcePath = new DefaultBuildTargetSourcePath(interestingTarget);
ruleResolver.addToIndex(new FakeBuildRule(usedTarget, pathResolver).setOutputFile("used"));
ruleResolver.addToIndex(new FakeBuildRule(unusedTarget, pathResolver).setOutputFile("unused"));
ruleResolver.addToIndex(new FakeBuildRule(noncoveredTarget, pathResolver).setOutputFile("nc"));
ruleResolver.addToIndex(new FakeBuildRule(interestingTarget, pathResolver).setOutputFile("in"));
testKeysWhenInputContentsChanges(
ruleFinder,
pathResolver,
usedSourcePath,
unusedSourcePath,
noncoveredSourcePath,
interestingSourcePath,
pathResolver.getAbsolutePath(usedSourcePath),
pathResolver.getAbsolutePath(unusedSourcePath),
pathResolver.getAbsolutePath(noncoveredSourcePath),
pathResolver.getAbsolutePath(interestingSourcePath),
DependencyFileEntry.fromSourcePath(usedSourcePath, pathResolver));
}
@Test
public void testKeysWhenInputArchiveMemberChanges() throws Exception {
ProjectFilesystem filesystem = new FakeProjectFilesystem();
BuildRuleResolver ruleResolver = newRuleResolver();
SourcePathRuleFinder ruleFinder = new SourcePathRuleFinder(ruleResolver);
SourcePathResolver pathResolver = new SourcePathResolver(ruleFinder);
SourcePath archivePath = new PathSourcePath(filesystem, Paths.get("archive"));
SourcePath usedSourcePath = ArchiveMemberSourcePath.of(archivePath, Paths.get("used"));
SourcePath unusedSourcePath = ArchiveMemberSourcePath.of(archivePath, Paths.get("unused"));
SourcePath noncoveredSourcePath = ArchiveMemberSourcePath.of(archivePath, Paths.get("nc"));
SourcePath interestingSourcePath =
ArchiveMemberSourcePath.of(archivePath, Paths.get("META-INF"));
testKeysWhenInputContentsChanges(
ruleFinder,
pathResolver,
usedSourcePath,
unusedSourcePath,
noncoveredSourcePath,
interestingSourcePath,
Paths.get(pathResolver.getAbsoluteArchiveMemberPath(usedSourcePath).toString()),
Paths.get(pathResolver.getAbsoluteArchiveMemberPath(unusedSourcePath).toString()),
Paths.get(pathResolver.getAbsoluteArchiveMemberPath(noncoveredSourcePath).toString()),
Paths.get(pathResolver.getAbsoluteArchiveMemberPath(interestingSourcePath).toString()),
DependencyFileEntry.fromSourcePath(usedSourcePath, pathResolver));
}
/** Tests all types of changes (or the lack of it): used, unused, noncovered. */
private void testKeysWhenInputContentsChanges(
SourcePathRuleFinder ruleFinder,
SourcePathResolver pathResolver,
SourcePath usedSourcePath,
SourcePath unusedSourcePath,
SourcePath noncoveredSourcePath,
SourcePath interestingSourcePath,
Path usedAbsolutePath,
Path unusedAbsolutePath,
Path noncoveredAbsolutePath,
Path interestingAbsolutePath,
DependencyFileEntry usedDepFileEntry)
throws Exception {
testDepFileRuleKeyWhenInputContentsChanges(
ruleFinder,
pathResolver,
usedSourcePath,
unusedSourcePath,
noncoveredSourcePath,
interestingSourcePath,
usedAbsolutePath,
unusedAbsolutePath,
noncoveredAbsolutePath,
interestingAbsolutePath,
usedDepFileEntry);
testManifestKeyWhenInputContentsChanges(
ruleFinder,
pathResolver,
unusedSourcePath,
noncoveredSourcePath,
interestingSourcePath,
unusedAbsolutePath,
noncoveredAbsolutePath,
interestingAbsolutePath);
}
/** Tests all types of changes (or the lack of it): used, unused, noncovered. */
private void testDepFileRuleKeyWhenInputContentsChanges(
SourcePathRuleFinder ruleFinder,
SourcePathResolver pathResolver,
SourcePath usedSourcePath,
SourcePath unusedSourcePath,
SourcePath noncoveredSourcePath,
SourcePath interestingSourcePath,
Path usedAbsolutePath,
Path unusedAbsolutePath,
Path noncoveredAbsolutePath,
Path interestingAbsolutePath,
DependencyFileEntry usedDepFileEntry)
throws Exception {
testDepFileRuleKey(
ruleFinder,
pathResolver,
ImmutableList.of(usedSourcePath, unusedSourcePath, interestingSourcePath),
ImmutableMap.of( // before
usedAbsolutePath, HashCode.fromInt(100),
unusedAbsolutePath, HashCode.fromInt(200),
interestingAbsolutePath, HashCode.fromInt(400)),
ImmutableMap.of( // after
usedAbsolutePath, HashCode.fromInt(100),
unusedAbsolutePath, HashCode.fromInt(200),
interestingAbsolutePath, HashCode.fromInt(400)),
(SourcePath path) -> true, // all inputs are covered by dep file
ImmutableSet.of(interestingSourcePath),
ImmutableList.of(usedDepFileEntry),
true, // rule key should remain same
ImmutableSet.of(usedSourcePath),
"Dep file rule key should NOT change when none of the inputs changes.");
testDepFileRuleKey(
ruleFinder,
pathResolver,
ImmutableList.of(
usedSourcePath, unusedSourcePath, noncoveredSourcePath, interestingSourcePath),
ImmutableMap.of( // before
usedAbsolutePath, HashCode.fromInt(100),
unusedAbsolutePath, HashCode.fromInt(200),
noncoveredAbsolutePath, HashCode.fromInt(300),
interestingAbsolutePath, HashCode.fromInt(400)),
ImmutableMap.of( // after
usedAbsolutePath, HashCode.fromInt(100),
unusedAbsolutePath, HashCode.fromInt(200),
noncoveredAbsolutePath, HashCode.fromInt(300),
interestingAbsolutePath, HashCode.fromInt(400)),
ImmutableSet.of(usedSourcePath, unusedSourcePath, interestingSourcePath)::contains,
ImmutableSet.of(interestingSourcePath),
ImmutableList.of(usedDepFileEntry),
true, // rule key should remain same
ImmutableSet.of(usedSourcePath),
"Dep file rule key should NOT change when none of the inputs changes.");
testDepFileRuleKey(
ruleFinder,
pathResolver,
ImmutableList.of(usedSourcePath, unusedSourcePath, interestingSourcePath),
ImmutableMap.of( // before
usedAbsolutePath, HashCode.fromInt(100),
unusedAbsolutePath, HashCode.fromInt(200),
interestingAbsolutePath, HashCode.fromInt(400)),
ImmutableMap.of( // after
usedAbsolutePath, HashCode.fromInt(100),
unusedAbsolutePath, HashCode.fromInt(205),
interestingAbsolutePath, HashCode.fromInt(400)),
(SourcePath path) -> true, // all inputs are covered by dep file
ImmutableSet.of(interestingSourcePath),
ImmutableList.of(usedDepFileEntry),
true, // rule key should remain same
ImmutableSet.of(usedSourcePath),
"Dep file rule key should NOT change when only dep-file-covered but unused inputs change.");
testDepFileRuleKey(
ruleFinder,
pathResolver,
ImmutableList.of(
usedSourcePath, unusedSourcePath, noncoveredSourcePath, interestingSourcePath),
ImmutableMap.of( // before
usedAbsolutePath, HashCode.fromInt(100),
unusedAbsolutePath, HashCode.fromInt(200),
noncoveredAbsolutePath, HashCode.fromInt(300),
interestingAbsolutePath, HashCode.fromInt(400)),
ImmutableMap.of( // after
usedAbsolutePath, HashCode.fromInt(100),
unusedAbsolutePath, HashCode.fromInt(205),
noncoveredAbsolutePath, HashCode.fromInt(300),
interestingAbsolutePath, HashCode.fromInt(400)),
ImmutableSet.of(usedSourcePath, unusedSourcePath, interestingSourcePath)::contains,
ImmutableSet.of(interestingSourcePath),
ImmutableList.of(usedDepFileEntry),
true, // rule key should remain same
ImmutableSet.of(usedSourcePath),
"Dep file rule key should NOT change when only dep-file-covered but unused inputs change.");
testDepFileRuleKey(
ruleFinder,
pathResolver,
ImmutableList.of(usedSourcePath, unusedSourcePath, interestingSourcePath),
ImmutableMap.of( // before
usedAbsolutePath, HashCode.fromInt(100),
unusedAbsolutePath, HashCode.fromInt(200),
interestingAbsolutePath, HashCode.fromInt(400)),
ImmutableMap.of( // after
usedAbsolutePath, HashCode.fromInt(105),
unusedAbsolutePath, HashCode.fromInt(200),
interestingAbsolutePath, HashCode.fromInt(400)),
(SourcePath path) -> true, // all inputs are covered by dep file
ImmutableSet.of(interestingSourcePath),
ImmutableList.of(usedDepFileEntry),
false, // rule key should change
ImmutableSet.of(usedSourcePath),
"Dep file rule key should change when a dep-file-covered and used input changes.");
testDepFileRuleKey(
ruleFinder,
pathResolver,
ImmutableList.of(
usedSourcePath, unusedSourcePath, noncoveredSourcePath, interestingSourcePath),
ImmutableMap.of( // before
usedAbsolutePath, HashCode.fromInt(100),
unusedAbsolutePath, HashCode.fromInt(200),
noncoveredAbsolutePath, HashCode.fromInt(300),
interestingAbsolutePath, HashCode.fromInt(400)),
ImmutableMap.of( // after
usedAbsolutePath, HashCode.fromInt(105),
unusedAbsolutePath, HashCode.fromInt(200),
noncoveredAbsolutePath, HashCode.fromInt(300),
interestingAbsolutePath, HashCode.fromInt(400)),
ImmutableSet.of(usedSourcePath, unusedSourcePath, interestingSourcePath)::contains,
ImmutableSet.of(interestingSourcePath),
ImmutableList.of(usedDepFileEntry),
false, // rule key should change
ImmutableSet.of(usedSourcePath),
"Dep file rule key should change when a dep-file-covered and used input changes.");
testDepFileRuleKey(
ruleFinder,
pathResolver,
ImmutableList.of(
usedSourcePath, unusedSourcePath, noncoveredSourcePath, interestingSourcePath),
ImmutableMap.of( // before
usedAbsolutePath, HashCode.fromInt(100),
unusedAbsolutePath, HashCode.fromInt(200),
noncoveredAbsolutePath, HashCode.fromInt(300),
interestingAbsolutePath, HashCode.fromInt(400)),
ImmutableMap.of( // after
usedAbsolutePath, HashCode.fromInt(100),
unusedAbsolutePath, HashCode.fromInt(200),
noncoveredAbsolutePath, HashCode.fromInt(305),
interestingAbsolutePath, HashCode.fromInt(400)),
ImmutableSet.of(usedSourcePath, unusedSourcePath, interestingSourcePath)::contains,
ImmutableSet.of(interestingSourcePath),
ImmutableList.of(usedDepFileEntry),
false, // rule key should change
ImmutableSet.of(usedSourcePath),
"Dep file rule key should change when a not dep-file-covered input changes.");
testDepFileRuleKey(
ruleFinder,
pathResolver,
ImmutableList.of(
usedSourcePath, unusedSourcePath, noncoveredSourcePath, interestingSourcePath),
ImmutableMap.of( // before
usedAbsolutePath, HashCode.fromInt(100),
unusedAbsolutePath, HashCode.fromInt(200),
interestingAbsolutePath, HashCode.fromInt(400)),
ImmutableMap.of( // after
usedAbsolutePath, HashCode.fromInt(100),
unusedAbsolutePath, HashCode.fromInt(200),
interestingAbsolutePath, HashCode.fromInt(405)),
(SourcePath path) -> true, // all inputs are covered by dep file
ImmutableSet.of(interestingSourcePath),
ImmutableList.of(usedDepFileEntry),
true, // rule key should change
ImmutableSet.of(usedSourcePath),
"Dep file rule key should NOT change when an interesting, but unused input changes.");
testDepFileRuleKey(
ruleFinder,
pathResolver,
ImmutableList.of(
usedSourcePath, unusedSourcePath, noncoveredSourcePath, interestingSourcePath),
ImmutableMap.of( // before
usedAbsolutePath, HashCode.fromInt(100),
unusedAbsolutePath, HashCode.fromInt(200),
noncoveredAbsolutePath, HashCode.fromInt(300),
interestingAbsolutePath, HashCode.fromInt(400)),
ImmutableMap.of( // after
usedAbsolutePath, HashCode.fromInt(100),
unusedAbsolutePath, HashCode.fromInt(200),
noncoveredAbsolutePath, HashCode.fromInt(300),
interestingAbsolutePath, HashCode.fromInt(405)),
ImmutableSet.of(usedSourcePath, unusedSourcePath, interestingSourcePath)::contains,
ImmutableSet.of(interestingSourcePath),
ImmutableList.of(usedDepFileEntry),
true, // rule key should change
ImmutableSet.of(usedSourcePath),
"Dep file rule key should NOT change when an interesting, but unused input changes.");
testDepFileRuleKey(
ruleFinder,
pathResolver,
ImmutableList.of(usedSourcePath, interestingSourcePath),
ImmutableList.of(usedSourcePath, unusedSourcePath, interestingSourcePath),
ImmutableMap.of( // before
usedAbsolutePath, HashCode.fromInt(100),
interestingAbsolutePath, HashCode.fromInt(400)),
ImmutableMap.of( // after
usedAbsolutePath, HashCode.fromInt(100),
unusedAbsolutePath, HashCode.fromInt(205),
interestingAbsolutePath, HashCode.fromInt(400)),
(SourcePath path) -> true, // all inputs are covered by dep file
ImmutableSet.of(interestingSourcePath),
ImmutableList.of(usedDepFileEntry),
true, // rule key should remain same
ImmutableSet.of(usedSourcePath),
"Dep file rule key should NOT change when a covered but unused input is added/removed.");
testDepFileRuleKey(
ruleFinder,
pathResolver,
ImmutableList.of(usedSourcePath, unusedSourcePath, interestingSourcePath),
ImmutableList.of(
usedSourcePath, unusedSourcePath, noncoveredSourcePath, interestingSourcePath),
ImmutableMap.of( // before
usedAbsolutePath, HashCode.fromInt(100),
unusedAbsolutePath, HashCode.fromInt(200),
interestingAbsolutePath, HashCode.fromInt(400)),
ImmutableMap.of( // after
usedAbsolutePath, HashCode.fromInt(100),
unusedAbsolutePath, HashCode.fromInt(200),
noncoveredAbsolutePath, HashCode.fromInt(305),
interestingAbsolutePath, HashCode.fromInt(400)),
ImmutableSet.of(usedSourcePath, unusedSourcePath, interestingSourcePath)::contains,
ImmutableSet.of(interestingSourcePath),
ImmutableList.of(usedDepFileEntry),
false, // rule key should change
ImmutableSet.of(usedSourcePath),
"Dep file rule key should change when a not dep-file-covered input is added/removed.");
testDepFileRuleKey(
ruleFinder,
pathResolver,
ImmutableList.of(usedSourcePath, unusedSourcePath, noncoveredSourcePath),
ImmutableList.of(
usedSourcePath, unusedSourcePath, noncoveredSourcePath, interestingSourcePath),
ImmutableMap.of( // before
usedAbsolutePath, HashCode.fromInt(100),
unusedAbsolutePath, HashCode.fromInt(200),
noncoveredAbsolutePath, HashCode.fromInt(300)),
ImmutableMap.of( // after
usedAbsolutePath, HashCode.fromInt(100),
unusedAbsolutePath, HashCode.fromInt(200),
noncoveredAbsolutePath, HashCode.fromInt(305),
interestingAbsolutePath, HashCode.fromInt(400)),
ImmutableSet.of(usedSourcePath, unusedSourcePath, interestingSourcePath)::contains,
ImmutableSet.of(interestingSourcePath),
ImmutableList.of(usedDepFileEntry),
false, // rule key should change
ImmutableSet.of(usedSourcePath),
"Dep file rule key should change when an interesting input is added/removed.");
try {
testDepFileRuleKey(
ruleFinder,
pathResolver,
ImmutableList.of(
usedSourcePath, unusedSourcePath, noncoveredSourcePath, interestingSourcePath),
ImmutableMap.of( // before
usedAbsolutePath, HashCode.fromInt(100),
unusedAbsolutePath, HashCode.fromInt(200),
noncoveredAbsolutePath, HashCode.fromInt(300),
interestingAbsolutePath, HashCode.fromInt(400)),
ImmutableMap.of( // after
usedAbsolutePath, HashCode.fromInt(100),
unusedAbsolutePath, HashCode.fromInt(200),
noncoveredAbsolutePath, HashCode.fromInt(300),
interestingAbsolutePath, HashCode.fromInt(400)),
ImmutableSet.of(unusedSourcePath)::contains, // "used" is not accounted as covered
ImmutableSet.of(interestingSourcePath),
ImmutableList.of(usedDepFileEntry),
true, // rule key should not change
ImmutableSet.of(usedSourcePath),
"should throw");
Assert.fail("Unaccounted inputs should cause an exception.");
} catch (NoSuchFileException e) { // NOPMD
// expected
}
}
/** Tests SourcePaths both directly, and when wrapped with a RuleKeyAppendable. */
private void testDepFileRuleKey(
SourcePathRuleFinder ruleFinder,
SourcePathResolver pathResolver,
Object fieldValue,
ImmutableMap<Path, HashCode> hashesBefore,
ImmutableMap<Path, HashCode> hashesAfter,
Predicate<SourcePath> coveredInputPaths,
ImmutableSet<SourcePath> interestingInputPaths,
ImmutableList<DependencyFileEntry> depFileEntries,
boolean expectSameKeys,
ImmutableSet<SourcePath> expectedDepFileInputsAfter,
String failureMessage)
throws Exception {
testDepFileRuleKey(
ruleFinder,
pathResolver,
fieldValue, // same field value before and after
fieldValue, // same field value before and after
hashesBefore,
hashesAfter,
coveredInputPaths,
interestingInputPaths,
depFileEntries,
expectSameKeys,
expectedDepFileInputsAfter,
failureMessage);
}
private void testDepFileRuleKey(
SourcePathRuleFinder ruleFinder,
SourcePathResolver pathResolver,
Object fieldValueBefore,
Object fieldValueAfter,
ImmutableMap<Path, HashCode> hashesBefore,
ImmutableMap<Path, HashCode> hashesAfter,
Predicate<SourcePath> coveredInputPaths,
ImmutableSet<SourcePath> interestingInputPaths,
ImmutableList<DependencyFileEntry> depFileEntries,
boolean expectSameKeys,
ImmutableSet<SourcePath> expectedDepFileInputsAfter,
String failureMessage)
throws Exception {
testDepFileRuleKeyImpl(
ruleFinder,
pathResolver,
fieldValueBefore,
fieldValueAfter,
hashesBefore,
hashesAfter,
coveredInputPaths,
interestingInputPaths,
depFileEntries,
expectSameKeys,
expectedDepFileInputsAfter,
failureMessage);
// make sure the behavior is same if wrapped with RuleKeyAppendable
testDepFileRuleKeyImpl(
ruleFinder,
pathResolver,
new RuleKeyAppendableWrapped(fieldValueBefore),
new RuleKeyAppendableWrapped(fieldValueAfter),
hashesBefore,
hashesAfter,
coveredInputPaths,
interestingInputPaths,
depFileEntries,
expectSameKeys,
expectedDepFileInputsAfter,
failureMessage);
}
private void testDepFileRuleKeyImpl(
SourcePathRuleFinder ruleFinder,
SourcePathResolver pathResolver,
Object fieldValueBefore,
Object fieldValueAfter,
ImmutableMap<Path, HashCode> hashesBefore,
ImmutableMap<Path, HashCode> hashesAfter,
Predicate<SourcePath> coveredInputPaths,
ImmutableSet<SourcePath> interestingInputPaths,
ImmutableList<DependencyFileEntry> depFileEntries,
boolean expectSameKeys,
ImmutableSet<SourcePath> expectedDepFileInputsAfter,
String failureMessage)
throws Exception {
RuleKeyFieldLoader fieldLoader = new RuleKeyFieldLoader(0);
FakeDepFileBuildRule rule1 =
new FakeDepFileBuildRule("//:rule") {
@AddToRuleKey final Object myField = fieldValueBefore;
};
rule1.setCoveredByDepFilePredicate(coveredInputPaths);
rule1.setExistenceOfInterestPredicate(interestingInputPaths);
FakeFileHashCache hashCache = new FakeFileHashCache(hashesBefore, true, ImmutableMap.of());
RuleKeyAndInputs res1 =
new DefaultDependencyFileRuleKeyFactory(fieldLoader, hashCache, pathResolver, ruleFinder)
.build(rule1, depFileEntries);
FakeDepFileBuildRule rule2 =
new FakeDepFileBuildRule("//:rule") {
@AddToRuleKey final Object myField = fieldValueAfter;
};
rule2.setCoveredByDepFilePredicate(coveredInputPaths);
rule2.setExistenceOfInterestPredicate(interestingInputPaths);
hashCache = new FakeFileHashCache(hashesAfter, true, ImmutableMap.of());
RuleKeyAndInputs res2 =
new DefaultDependencyFileRuleKeyFactory(fieldLoader, hashCache, pathResolver, ruleFinder)
.build(rule2, depFileEntries);
if (expectSameKeys) {
assertThat(failureMessage, res2.getRuleKey(), Matchers.equalTo(res1.getRuleKey()));
} else {
assertThat(
failureMessage, res2.getRuleKey(), Matchers.not(Matchers.equalTo(res1.getRuleKey())));
}
assertThat(res2.getInputs(), Matchers.equalTo(expectedDepFileInputsAfter));
}
/** Tests all types of changes (or the lack of it): used, unused, noncovered. */
private void testManifestKeyWhenInputContentsChanges(
SourcePathRuleFinder ruleFinder,
SourcePathResolver pathResolver,
SourcePath coveredSourcePath,
SourcePath noncoveredSourcePath,
SourcePath interestingSourcePath,
Path coveredAbsolutePath,
Path noncoveredAbsolutePath,
Path interestingAbsolutePath)
throws Exception {
testManifestKey(
ruleFinder,
pathResolver,
ImmutableList.of(coveredSourcePath, interestingSourcePath),
ImmutableMap.of( // before
coveredAbsolutePath, HashCode.fromInt(100),
interestingAbsolutePath, HashCode.fromInt(400)),
ImmutableMap.of( // after
coveredAbsolutePath, HashCode.fromInt(100),
interestingAbsolutePath, HashCode.fromInt(400)),
(SourcePath path) -> true, // all inputs are covered by dep file
ImmutableSet.of(interestingSourcePath),
true, // rule key should remain same
ImmutableSet.of(coveredSourcePath, interestingSourcePath),
"Manifest key should NOT change when none of the inputs changes.");
testManifestKey(
ruleFinder,
pathResolver,
ImmutableList.of(coveredSourcePath, noncoveredSourcePath, interestingSourcePath),
ImmutableMap.of( // before
coveredAbsolutePath, HashCode.fromInt(100),
noncoveredAbsolutePath, HashCode.fromInt(300),
interestingAbsolutePath, HashCode.fromInt(400)),
ImmutableMap.of( // after
coveredAbsolutePath, HashCode.fromInt(100),
noncoveredAbsolutePath, HashCode.fromInt(300),
interestingAbsolutePath, HashCode.fromInt(400)),
ImmutableSet.of(coveredSourcePath, interestingSourcePath)::contains,
ImmutableSet.of(interestingSourcePath),
true, // rule key should remain same
ImmutableSet.of(coveredSourcePath, interestingSourcePath),
"Manifest key should NOT change when none of the inputs changes.");
testManifestKey(
ruleFinder,
pathResolver,
ImmutableList.of(coveredSourcePath, interestingSourcePath),
ImmutableMap.of( // before
coveredAbsolutePath, HashCode.fromInt(100),
interestingAbsolutePath, HashCode.fromInt(400)),
ImmutableMap.of( // after
coveredAbsolutePath, HashCode.fromInt(105),
interestingAbsolutePath, HashCode.fromInt(400)),
(SourcePath path) -> true, // all inputs are covered by dep file
ImmutableSet.of(interestingSourcePath),
true, // rule key should remain same
ImmutableSet.of(coveredSourcePath, interestingSourcePath),
"Manifest key should NOT change when only dep-file-covered inputs change.");
testManifestKey(
ruleFinder,
pathResolver,
ImmutableList.of(coveredSourcePath, noncoveredSourcePath, interestingSourcePath),
ImmutableMap.of( // before
coveredAbsolutePath, HashCode.fromInt(100),
noncoveredAbsolutePath, HashCode.fromInt(300),
interestingAbsolutePath, HashCode.fromInt(400)),
ImmutableMap.of( // after
coveredAbsolutePath, HashCode.fromInt(105),
noncoveredAbsolutePath, HashCode.fromInt(300),
interestingAbsolutePath, HashCode.fromInt(400)),
ImmutableSet.of(coveredSourcePath, interestingSourcePath)::contains,
ImmutableSet.of(interestingSourcePath),
true, // rule key should remain same
ImmutableSet.of(coveredSourcePath, interestingSourcePath),
"Manifest key should NOT change when only dep-file-covered but unused inputs change.");
testManifestKey(
ruleFinder,
pathResolver,
ImmutableList.of(coveredSourcePath, noncoveredSourcePath, interestingSourcePath),
ImmutableMap.of( // before
coveredAbsolutePath, HashCode.fromInt(100),
noncoveredAbsolutePath, HashCode.fromInt(300),
interestingAbsolutePath, HashCode.fromInt(400)),
ImmutableMap.of( // after
coveredAbsolutePath, HashCode.fromInt(100),
noncoveredAbsolutePath, HashCode.fromInt(305),
interestingAbsolutePath, HashCode.fromInt(400)),
ImmutableSet.of(coveredSourcePath, interestingSourcePath)::contains,
ImmutableSet.of(interestingSourcePath),
false, // rule key should change
ImmutableSet.of(coveredSourcePath, interestingSourcePath),
"Manifest key should change when a not dep-file-covered input changes.");
testManifestKey(
ruleFinder,
pathResolver,
ImmutableList.of(coveredSourcePath, interestingSourcePath),
ImmutableMap.of( // before
coveredAbsolutePath, HashCode.fromInt(100),
interestingAbsolutePath, HashCode.fromInt(400)),
ImmutableMap.of( // after
coveredAbsolutePath, HashCode.fromInt(100),
interestingAbsolutePath, HashCode.fromInt(405)),
(SourcePath path) -> true, // all files covered
ImmutableSet.of(interestingSourcePath),
true, // rule key should change
ImmutableSet.of(coveredSourcePath, interestingSourcePath),
"Manifest key should NOT change when an interesting, but covered input changes.");
testManifestKey(
ruleFinder,
pathResolver,
ImmutableList.of(coveredSourcePath, noncoveredSourcePath, interestingSourcePath),
ImmutableMap.of( // before
coveredAbsolutePath, HashCode.fromInt(100),
noncoveredAbsolutePath, HashCode.fromInt(300),
interestingAbsolutePath, HashCode.fromInt(400)),
ImmutableMap.of( // after
coveredAbsolutePath, HashCode.fromInt(100),
noncoveredAbsolutePath, HashCode.fromInt(300),
interestingAbsolutePath, HashCode.fromInt(405)),
ImmutableSet.of(coveredSourcePath, interestingSourcePath)::contains,
ImmutableSet.of(interestingSourcePath),
true, // rule key should change
ImmutableSet.of(coveredSourcePath, interestingSourcePath),
"Manifest key should NOT change when an interesting, but covered input changes.");
testManifestKey(
ruleFinder,
pathResolver,
ImmutableList.of(noncoveredSourcePath, interestingSourcePath),
ImmutableList.of(coveredSourcePath, noncoveredSourcePath, interestingSourcePath),
ImmutableMap.of( // before
noncoveredAbsolutePath, HashCode.fromInt(300),
interestingAbsolutePath, HashCode.fromInt(400)),
ImmutableMap.of( // after
coveredAbsolutePath, HashCode.fromInt(105),
noncoveredAbsolutePath, HashCode.fromInt(300),
interestingAbsolutePath, HashCode.fromInt(400)),
ImmutableSet.of(coveredSourcePath, interestingSourcePath)::contains,
ImmutableSet.of(interestingSourcePath),
true, // rule key should remain same
ImmutableSet.of(coveredSourcePath, interestingSourcePath),
"Manifest key should NOT change when only dep-file-covered inputs are added/removed.");
testManifestKey(
ruleFinder,
pathResolver,
ImmutableList.of(coveredSourcePath, interestingSourcePath),
ImmutableList.of(coveredSourcePath, noncoveredSourcePath, interestingSourcePath),
ImmutableMap.of( // before
coveredAbsolutePath, HashCode.fromInt(100),
interestingAbsolutePath, HashCode.fromInt(400)),
ImmutableMap.of( // after
coveredAbsolutePath, HashCode.fromInt(100),
noncoveredAbsolutePath, HashCode.fromInt(300),
interestingAbsolutePath, HashCode.fromInt(400)),
ImmutableSet.of(coveredSourcePath, interestingSourcePath)::contains,
ImmutableSet.of(interestingSourcePath),
false, // rule key should change
ImmutableSet.of(coveredSourcePath, interestingSourcePath),
"Manifest key should change when a not dep-file-covered input is added/removed.");
testManifestKey(
ruleFinder,
pathResolver,
ImmutableList.of(coveredSourcePath, noncoveredSourcePath),
ImmutableList.of(coveredSourcePath, noncoveredSourcePath, interestingSourcePath),
ImmutableMap.of( // before
coveredAbsolutePath, HashCode.fromInt(100),
noncoveredAbsolutePath, HashCode.fromInt(300)),
ImmutableMap.of( // after
coveredAbsolutePath, HashCode.fromInt(100),
noncoveredAbsolutePath, HashCode.fromInt(300),
interestingAbsolutePath, HashCode.fromInt(400)),
ImmutableSet.of(coveredSourcePath, interestingSourcePath)::contains,
ImmutableSet.of(interestingSourcePath),
false, // rule key should change
ImmutableSet.of(coveredSourcePath, interestingSourcePath),
"Manifest key should change when a mandatory input is added/removed.");
}
/** Tests SourcePaths both directly, and when wrapped with a RuleKeyAppendable. */
private void testManifestKey(
SourcePathRuleFinder ruleFinder,
SourcePathResolver pathResolver,
Object fieldValue,
ImmutableMap<Path, HashCode> hashesBefore,
ImmutableMap<Path, HashCode> hashesAfter,
Predicate<SourcePath> coveredInputPaths,
ImmutableSet<SourcePath> interestingInputPaths,
boolean expectSameKeys,
ImmutableSet<SourcePath> expectedDepFileInputsAfter,
String failureMessage)
throws Exception {
testManifestKey(
ruleFinder,
pathResolver,
fieldValue, // same field value before and after
fieldValue, // same field value before and after
hashesBefore,
hashesAfter,
coveredInputPaths,
interestingInputPaths,
expectSameKeys,
expectedDepFileInputsAfter,
failureMessage);
}
private void testManifestKey(
SourcePathRuleFinder ruleFinder,
SourcePathResolver pathResolver,
Object fieldValueBefore,
Object fieldValueAfter,
ImmutableMap<Path, HashCode> hashesBefore,
ImmutableMap<Path, HashCode> hashesAfter,
Predicate<SourcePath> coveredInputPaths,
ImmutableSet<SourcePath> interestingInputPaths,
boolean expectSameKeys,
ImmutableSet<SourcePath> expectedDepFileInputsAfter,
String failureMessage)
throws Exception {
testManifestKeyImpl(
ruleFinder,
pathResolver,
fieldValueBefore,
fieldValueAfter,
hashesBefore,
hashesAfter,
coveredInputPaths,
interestingInputPaths,
expectSameKeys,
expectedDepFileInputsAfter,
failureMessage);
// make sure the behavior is same if wrapped with RuleKeyAppendable
testManifestKeyImpl(
ruleFinder,
pathResolver,
new RuleKeyAppendableWrapped(fieldValueBefore),
new RuleKeyAppendableWrapped(fieldValueAfter),
hashesBefore,
hashesAfter,
coveredInputPaths,
interestingInputPaths,
expectSameKeys,
expectedDepFileInputsAfter,
failureMessage);
}
private void testManifestKeyImpl(
SourcePathRuleFinder ruleFinder,
SourcePathResolver pathResolver,
Object fieldValueBefore,
Object fieldValueAfter,
ImmutableMap<Path, HashCode> hashesBefore,
ImmutableMap<Path, HashCode> hashesAfter,
Predicate<SourcePath> coveredInputPaths,
ImmutableSet<SourcePath> interestingInputPaths,
boolean expectSameKeys,
ImmutableSet<SourcePath> expectedDepFileInputsAfter,
String failureMessage)
throws Exception {
RuleKeyFieldLoader fieldLoader = new RuleKeyFieldLoader(0);
FakeDepFileBuildRule rule1 =
new FakeDepFileBuildRule("//:rule") {
@AddToRuleKey final Object myField = fieldValueBefore;
};
rule1.setCoveredByDepFilePredicate(coveredInputPaths);
rule1.setExistenceOfInterestPredicate(interestingInputPaths);
FakeFileHashCache hashCache = new FakeFileHashCache(hashesBefore, true, ImmutableMap.of());
RuleKeyAndInputs res1 =
new DefaultDependencyFileRuleKeyFactory(fieldLoader, hashCache, pathResolver, ruleFinder)
.buildManifestKey(rule1);
FakeDepFileBuildRule rule2 =
new FakeDepFileBuildRule("//:rule") {
@AddToRuleKey final Object myField = fieldValueAfter;
};
rule2.setCoveredByDepFilePredicate(coveredInputPaths);
rule2.setExistenceOfInterestPredicate(interestingInputPaths);
hashCache = new FakeFileHashCache(hashesAfter, true, ImmutableMap.of());
RuleKeyAndInputs res2 =
new DefaultDependencyFileRuleKeyFactory(fieldLoader, hashCache, pathResolver, ruleFinder)
.buildManifestKey(rule2);
if (expectSameKeys) {
assertThat(failureMessage, res2.getRuleKey(), Matchers.equalTo(res1.getRuleKey()));
} else {
assertThat(
failureMessage, res2.getRuleKey(), Matchers.not(Matchers.equalTo(res1.getRuleKey())));
}
assertThat(res2.getInputs(), Matchers.equalTo(expectedDepFileInputsAfter));
}
@Test
public void testKeysGetHashed() throws Exception {
ProjectFilesystem filesystem = new FakeProjectFilesystem();
RuleKeyFieldLoader fieldLoader = new RuleKeyFieldLoader(0);
BuildRuleResolver ruleResolver = newRuleResolver();
SourcePathRuleFinder ruleFinder = new SourcePathRuleFinder(ruleResolver);
SourcePathResolver pathResolver = new SourcePathResolver(ruleFinder);
SourcePath unusedSourcePath = new PathSourcePath(filesystem, Paths.get("input0"));
SourcePath sourcePath = new PathSourcePath(filesystem, Paths.get("input"));
DependencyFileEntry dependencyFileEntry =
DependencyFileEntry.fromSourcePath(sourcePath, pathResolver);
ImmutableMap<Path, HashCode> hashes =
ImmutableMap.of(pathResolver.getAbsolutePath(sourcePath), HashCode.fromInt(42));
Predicate<SourcePath> coveredPredicate =
ImmutableSet.of(sourcePath, unusedSourcePath)::contains;
FakeDepFileBuildRule rule1 =
new FakeDepFileBuildRule("//:rule") {
@AddToRuleKey final Object myField1 = sourcePath;
@AddToRuleKey final Object myField2 = unusedSourcePath;
};
rule1.setCoveredByDepFilePredicate(coveredPredicate);
FakeFileHashCache hashCache = new FakeFileHashCache(hashes, true, ImmutableMap.of());
RuleKeyAndInputs res1 =
new DefaultDependencyFileRuleKeyFactory(fieldLoader, hashCache, pathResolver, ruleFinder)
.build(rule1, ImmutableList.of(dependencyFileEntry));
FakeDepFileBuildRule rule2 =
new FakeDepFileBuildRule("//:rule") {
@AddToRuleKey final Object myField1 = unusedSourcePath;
@AddToRuleKey final Object myField2 = sourcePath;
};
rule2.setCoveredByDepFilePredicate(coveredPredicate);
hashCache = new FakeFileHashCache(hashes, true, ImmutableMap.of());
RuleKeyAndInputs res2 =
new DefaultDependencyFileRuleKeyFactory(fieldLoader, hashCache, pathResolver, ruleFinder)
.build(rule2, ImmutableList.of(dependencyFileEntry));
assertThat(res2.getRuleKey(), Matchers.not(Matchers.equalTo(res1.getRuleKey())));
assertThat(res2.getInputs(), Matchers.equalTo(ImmutableSet.of(sourcePath)));
}
private static class RuleKeyAppendableWrapped implements RuleKeyAppendable {
private final Object field;
public RuleKeyAppendableWrapped(Object field) {
this.field = field;
}
@Override
public void appendToRuleKey(RuleKeyObjectSink sink) {
sink.setReflectively("field", field);
}
}
private BuildRuleResolver newRuleResolver() {
return new BuildRuleResolver(TargetGraph.EMPTY, new DefaultTargetNodeToBuildRuleTransformer());
}
}