/* * 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 com.facebook.buck.rules.BuildableProperties.Kind.LIBRARY; import static org.junit.Assert.assertEquals; import static org.junit.Assert.assertNotEquals; 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.model.Either; import com.facebook.buck.rules.AddToRuleKey; import com.facebook.buck.rules.BuildContext; import com.facebook.buck.rules.BuildRule; import com.facebook.buck.rules.BuildRuleResolver; import com.facebook.buck.rules.BuildRuleType; import com.facebook.buck.rules.BuildableContext; import com.facebook.buck.rules.BuildableProperties; import com.facebook.buck.rules.DefaultTargetNodeToBuildRuleTransformer; import com.facebook.buck.rules.FakeBuildRuleParamsBuilder; import com.facebook.buck.rules.NoopBuildRule; import com.facebook.buck.rules.PathSourcePath; import com.facebook.buck.rules.RuleKey; 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.SourceRoot; import com.facebook.buck.rules.TargetGraph; import com.facebook.buck.step.Step; import com.facebook.buck.testutil.FakeProjectFilesystem; import com.facebook.buck.util.cache.DefaultFileHashCache; import com.facebook.buck.util.cache.FileHashCache; import com.facebook.buck.util.cache.NullFileHashCache; import com.facebook.buck.util.cache.StackedFileHashCache; import com.facebook.buck.util.sha1.Sha1HashCode; import com.google.common.base.Objects; import com.google.common.base.Preconditions; import com.google.common.base.Suppliers; import com.google.common.cache.CacheStats; import com.google.common.collect.ImmutableList; import com.google.common.collect.ImmutableMap; import com.google.common.collect.ImmutableSet; import com.google.common.collect.ImmutableSortedSet; import com.google.common.collect.MapMaker; import com.google.common.hash.HashCode; import java.io.IOException; import java.nio.file.Path; import java.nio.file.Paths; import java.util.Arrays; import java.util.Map; import java.util.Optional; import java.util.function.Function; import java.util.regex.Pattern; import javax.annotation.Nullable; import org.hamcrest.Matchers; import org.junit.Test; // There are tons of unused fields in this class. @SuppressWarnings("unused") public class DefaultRuleKeyFactoryTest { @Test public void shouldNotAddUnannotatedFieldsToRuleKey() { BuildTarget target = BuildTargetFactory.newInstance("//cheese:peas"); SourcePathRuleFinder ruleFinder = new SourcePathRuleFinder( new BuildRuleResolver( TargetGraph.EMPTY, new DefaultTargetNodeToBuildRuleTransformer())); SourcePathResolver pathResolver = new SourcePathResolver(ruleFinder); BuildRule rule = new EmptyRule(target); DefaultRuleKeyFactory factory = new DefaultRuleKeyFactory(0, new NullFileHashCache(), pathResolver, ruleFinder); RuleKey expected = factory.build(rule); class UndecoratedFields extends EmptyRule { private String field = "cake-walk"; public UndecoratedFields(BuildTarget target) { super(target); } } RuleKey seen = factory.build(new UndecoratedFields(target)); assertEquals(expected, seen); } @Test public void shouldAddASingleAnnotatedFieldToRuleKey() { BuildTarget target = BuildTargetFactory.newInstance("//cheese:peas"); SourcePathRuleFinder ruleFinder = new SourcePathRuleFinder( new BuildRuleResolver( TargetGraph.EMPTY, new DefaultTargetNodeToBuildRuleTransformer())); SourcePathResolver pathResolver = new SourcePathResolver(ruleFinder); BuildRule rule = new EmptyRule(target); DefaultRuleKeyFactory factory = new DefaultRuleKeyFactory(0, new NullFileHashCache(), pathResolver, ruleFinder); DefaultRuleKeyFactory.Builder<HashCode> builder = factory.newBuilderForTesting(rule); builder.setReflectively("field", "cake-walk"); RuleKey expected = builder.build(RuleKey::new); class DecoratedFields extends EmptyRule { @AddToRuleKey private String field = "cake-walk"; public DecoratedFields(BuildTarget target) { super(target); } } RuleKey seen = factory.build(new DecoratedFields(target)); assertEquals(expected, seen); } @Test public void shouldAllowAFieldToBeStringified() { BuildTarget target = BuildTargetFactory.newInstance("//cheese:peas"); SourcePathRuleFinder ruleFinder = new SourcePathRuleFinder( new BuildRuleResolver( TargetGraph.EMPTY, new DefaultTargetNodeToBuildRuleTransformer())); SourcePathResolver pathResolver = new SourcePathResolver(ruleFinder); BuildRule rule = new EmptyRule(target); DefaultRuleKeyFactory factory = new DefaultRuleKeyFactory(0, new NullFileHashCache(), pathResolver, ruleFinder); DefaultRuleKeyFactory.Builder<HashCode> builder = factory.newBuilderForTesting(rule); builder.setReflectively("field", "sausages"); RuleKey expected = builder.build(RuleKey::new); class Stringifiable { @Override public String toString() { return "sausages"; } } class StringifiedField extends EmptyRule { @AddToRuleKey(stringify = true) private Stringifiable field = new Stringifiable(); public StringifiedField(BuildTarget target) { super(target); } } RuleKey seen = factory.build(new StringifiedField(target)); assertEquals(expected, seen); } @Test public void shouldAllowRuleKeyAppendablesToAppendToRuleKey() { BuildTarget target = BuildTargetFactory.newInstance("//cheese:peas"); SourcePathRuleFinder ruleFinder = new SourcePathRuleFinder( new BuildRuleResolver( TargetGraph.EMPTY, new DefaultTargetNodeToBuildRuleTransformer())); SourcePathResolver pathResolver = new SourcePathResolver(ruleFinder); BuildRule rule = new EmptyRule(target); FileHashCache fileHashCache = new NullFileHashCache(); DefaultRuleKeyFactory factory = new DefaultRuleKeyFactory(0, fileHashCache, pathResolver, ruleFinder); RuleKey subKey = new UncachedRuleKeyBuilder(ruleFinder, pathResolver, fileHashCache, factory) .setReflectively("cheese", "brie") .build(RuleKey::new); DefaultRuleKeyFactory.Builder<HashCode> builder = factory.newBuilderForTesting(rule); try (RuleKeyScopedHasher.Scope keyScope = builder.getScopedHasher().keyScope("field")) { try (RuleKeyScopedHasher.Scope appendableScope = builder.getScopedHasher().wrapperScope(RuleKeyHasher.Wrapper.APPENDABLE)) { builder.getScopedHasher().getHasher().putRuleKey(subKey); } } RuleKey expected = builder.build(RuleKey::new); class AppendingField extends EmptyRule { @AddToRuleKey private Appender field = new Appender(); public AppendingField(BuildTarget target) { super(target); } } RuleKey seen = factory.build(new AppendingField(target)); assertEquals(expected, seen); } @Test public void annotatedAppendableBuildRulesIncludeTheirRuleKey() { BuildTarget target = BuildTargetFactory.newInstance("//cheese:peas"); BuildTarget depTarget = BuildTargetFactory.newInstance("//cheese:more-peas"); SourcePathRuleFinder ruleFinder = new SourcePathRuleFinder( new BuildRuleResolver( TargetGraph.EMPTY, new DefaultTargetNodeToBuildRuleTransformer())); SourcePathResolver pathResolver = new SourcePathResolver(ruleFinder); BuildRule rule = new EmptyRule(target); FileHashCache fileHashCache = new NullFileHashCache(); DefaultRuleKeyFactory factory = new DefaultRuleKeyFactory(0, fileHashCache, pathResolver, ruleFinder); class AppendableRule extends EmptyRule { public AppendableRule(BuildTarget target) { super(target); } @Override public void appendToRuleKey(RuleKeyObjectSink sink) { sink.setReflectively("cheese", "brie"); } } AppendableRule appendableRule = new AppendableRule(depTarget); RuleKey appendableSubKey = new UncachedRuleKeyBuilder(ruleFinder, pathResolver, fileHashCache, factory) .setReflectively("cheese", "brie") .build(RuleKey::new); RuleKey ruleSubKey = factory.build(appendableRule); DefaultRuleKeyFactory.Builder<HashCode> builder = factory.newBuilderForTesting(rule); try (RuleKeyScopedHasher.Scope keyScope = builder.getScopedHasher().keyScope("field")) { try (RuleKeyScopedHasher.Scope appendableScope = builder.getScopedHasher().wrapperScope(RuleKeyHasher.Wrapper.BUILD_RULE)) { builder.getScopedHasher().getHasher().putRuleKey(ruleSubKey); } } RuleKey expected = builder.build(RuleKey::new); class RuleContainingAppendableRule extends EmptyRule { @AddToRuleKey private final AppendableRule field; public RuleContainingAppendableRule(BuildTarget target, AppendableRule appendableRule) { super(target); this.field = appendableRule; } } RuleKey seen = factory.build(new RuleContainingAppendableRule(target, appendableRule)); assertEquals(expected, seen); } @Test public void stringifiedRuleKeyAppendablesGetAddedToRuleKeyAsStrings() { BuildTarget target = BuildTargetFactory.newInstance("//cheese:peas"); SourcePathRuleFinder ruleFinder = new SourcePathRuleFinder( new BuildRuleResolver( TargetGraph.EMPTY, new DefaultTargetNodeToBuildRuleTransformer())); SourcePathResolver pathResolver = new SourcePathResolver(ruleFinder); BuildRule rule = new EmptyRule(target); DefaultRuleKeyFactory factory = new DefaultRuleKeyFactory(0, new NullFileHashCache(), pathResolver, ruleFinder); DefaultRuleKeyFactory.Builder<HashCode> builder = factory.newBuilderForTesting(rule); builder.setReflectively("field", "cheddar"); RuleKey expected = builder.build(RuleKey::new); class AppendingField extends EmptyRule { @AddToRuleKey(stringify = true) private Appender field = new Appender(); public AppendingField(BuildTarget target) { super(target); } } RuleKey seen = factory.build(new AppendingField(target)); assertEquals(expected, seen); } @Test public void fieldsAreAddedInAlphabeticalOrder() { BuildTarget target = BuildTargetFactory.newInstance("//cheese:peas"); SourcePathRuleFinder ruleFinder = new SourcePathRuleFinder( new BuildRuleResolver( TargetGraph.EMPTY, new DefaultTargetNodeToBuildRuleTransformer())); SourcePathResolver pathResolver = new SourcePathResolver(ruleFinder); BuildRule rule = new EmptyRule(target); DefaultRuleKeyFactory factory = new DefaultRuleKeyFactory(0, new NullFileHashCache(), pathResolver, ruleFinder); DefaultRuleKeyFactory.Builder<HashCode> builder = factory.newBuilderForTesting(rule); builder.setReflectively("alpha", "stilton"); builder.setReflectively("beta", 1); builder.setReflectively("gamma", "stinking bishop"); RuleKey expected = builder.build(RuleKey::new); class UnsortedFields extends EmptyRule { @AddToRuleKey private String gamma = "stinking bishop"; @AddToRuleKey private int beta = 1; @AddToRuleKey private String alpha = "stilton"; public UnsortedFields(BuildTarget target) { super(target); } } RuleKey seen = factory.build(new UnsortedFields(target)); assertEquals(expected, seen); } @Test public void fieldsFromParentClassesShouldBeAddedAndFieldsRetainOverallAlphabeticalOrdering() { BuildTarget topLevelTarget = BuildTargetFactory.newInstance("//cheese:peas"); SourcePathRuleFinder ruleFinder = new SourcePathRuleFinder( new BuildRuleResolver( TargetGraph.EMPTY, new DefaultTargetNodeToBuildRuleTransformer())); SourcePathResolver pathResolver = new SourcePathResolver(ruleFinder); BuildRule rule = new EmptyRule(topLevelTarget); DefaultRuleKeyFactory factory = new DefaultRuleKeyFactory(0, new NullFileHashCache(), pathResolver, ruleFinder); DefaultRuleKeyFactory.Builder<HashCode> builder = factory.newBuilderForTesting(rule); builder.setReflectively("exoticCheese", "bavarian smoked"); builder.setReflectively("target", topLevelTarget); RuleKey expected = builder.build(RuleKey::new); class Parent extends EmptyRule { @AddToRuleKey private BuildTarget target; public Parent(BuildTarget target) { super(target); this.target = target; } } class Child extends Parent { @AddToRuleKey private String exoticCheese = "bavarian smoked"; public Child(BuildTarget target) { super(target); } } RuleKey seen = factory.build(new Child(topLevelTarget)); assertEquals(expected, seen); } @Test public void fieldsFromParentClassesAreAlsoAdded() { BuildTarget target = BuildTargetFactory.newInstance("//cheese:peas"); SourcePathRuleFinder ruleFinder = new SourcePathRuleFinder( new BuildRuleResolver( TargetGraph.EMPTY, new DefaultTargetNodeToBuildRuleTransformer())); SourcePathResolver pathResolver = new SourcePathResolver(ruleFinder); BuildRule rule = new EmptyRule(target); DefaultRuleKeyFactory factory = new DefaultRuleKeyFactory(0, new NullFileHashCache(), pathResolver, ruleFinder); DefaultRuleKeyFactory.Builder<HashCode> builder = factory.newBuilderForTesting(rule); builder.setReflectively("key", "child"); builder.setReflectively("key", "parent"); RuleKey expected = builder.build(RuleKey::new); class Parent extends EmptyRule { @AddToRuleKey private String key = "parent"; public Parent(BuildTarget target) { super(target); } } class Child extends Parent { @AddToRuleKey private String key = "child"; public Child(BuildTarget target) { super(target); } } RuleKey seen = factory.build(new Child(target)); assertEquals(expected, seen); } @Test public void testBothKeysAndValuesGetHashed() { assertKeysGetHashed(null); assertBothKeysAndValuesGetHashed(true, false); assertBothKeysAndValuesGetHashed((double) 123, (double) 42); assertBothKeysAndValuesGetHashed((float) 123, (float) 42); assertBothKeysAndValuesGetHashed(123, 42); // (int) assertBothKeysAndValuesGetHashed((long) 123, (long) 42); assertBothKeysAndValuesGetHashed((short) 123, (short) 42); assertBothKeysAndValuesGetHashed((byte) 123, (byte) 42); assertBothKeysAndValuesGetHashed(new byte[] {1, 2, 3}, new byte[] {4, 2}); assertBothKeysAndValuesGetHashed(DummyEnum.BLACK, DummyEnum.WHITE); assertBothKeysAndValuesGetHashed("abc", "def"); assertBothKeysAndValuesGetHashed(Pattern.compile("regex1"), Pattern.compile("regex2")); assertBothKeysAndValuesGetHashed( Sha1HashCode.of("a002b39af204cdfaa5fdb67816b13867c32ac52c"), Sha1HashCode.of("b67816b13867c32ac52ca002b39af204cdfaa5fd")); assertBothKeysAndValuesGetHashed( new RuleKey("a002b39af204cdfaa5fdb67816b13867c32ac52c"), new RuleKey("b67816b13867c32ac52ca002b39af204cdfaa5fd")); assertBothKeysAndValuesGetHashed(BuildRuleType.of("rule_type"), BuildRuleType.of("type2")); assertBothKeysAndValuesGetHashed( BuildTargetFactory.newInstance(Paths.get("/root"), "//example/base:one"), BuildTargetFactory.newInstance(Paths.get("/root"), "//example/base:two")); assertBothKeysAndValuesGetHashed(new SourceRoot("root1"), new SourceRoot("root2")); // wrapper types assertBothKeysAndValuesGetHashed(Optional.of("abc"), Optional.of("def")); assertBothKeysAndValuesGetHashed(Either.ofLeft("abc"), Either.ofLeft("def")); assertBothKeysAndValuesGetHashed(Either.ofRight("def"), Either.ofRight("ghi")); assertBothKeysAndValuesGetHashed(Suppliers.ofInstance("abc"), Suppliers.ofInstance("def")); // iterables & maps assertBothKeysAndValuesGetHashed(Arrays.asList(1, 2, 3), Arrays.asList(4, 2)); assertBothKeysAndValuesGetHashed(ImmutableList.of("abc", "xy"), ImmutableList.of("xy", "abc")); assertBothKeysAndValuesGetHashed(ImmutableMap.of("key", "v1"), ImmutableMap.of("key", "v2")); // nested assertBothKeysAndValuesGetHashed( ImmutableMap.of("key", Optional.of(ImmutableList.of(1, 2, 3))), ImmutableMap.of("key", Optional.of(ImmutableList.of(1, 2, 4)))); } @Test public void testFactoryReportsInputsAndDependenciesToCacheForBuildRule() throws IOException { SourcePathRuleFinder ruleFinder = new SourcePathRuleFinder( new BuildRuleResolver( TargetGraph.EMPTY, new DefaultTargetNodeToBuildRuleTransformer())); SourcePathResolver pathResolver = new SourcePathResolver(ruleFinder); NoopRuleKeyCache<RuleKey> noopRuleKeyCache = new NoopRuleKeyCache<>(); ProjectFilesystem filesystem = new FakeProjectFilesystem(); DefaultRuleKeyFactory factory = new DefaultRuleKeyFactory( new RuleKeyFieldLoader(0), new StackedFileHashCache( ImmutableList.of(DefaultFileHashCache.createDefaultFileHashCache(filesystem))), pathResolver, ruleFinder, noopRuleKeyCache); // Create a sample input. PathSourcePath input = new PathSourcePath(filesystem, filesystem.getPath("input")); filesystem.touch(input.getRelativePath()); // Create a sample dep rule. BuildRule dep = new EmptyRule(BuildTargetFactory.newInstance("//:dep")); // Create a sample rule key appendable. RuleKeyAppendable appendable = sink -> {}; // Create a dummy build rule that uses the input. BuildRule rule = new NoopBuildRule( new FakeBuildRuleParamsBuilder("//:target") .setProjectFilesystem(filesystem) .setDeclaredDeps(ImmutableSortedSet.of(dep)) .build()) { @AddToRuleKey private final SourcePath inputField = input; @AddToRuleKey private final RuleKeyAppendable appendableField = appendable; }; // Build the rule key. factory.build(rule); // Verify the input was properly reported to the rule key cache. RuleKeyResult<RuleKey> result = noopRuleKeyCache.results.get(rule); assertThat(result, Matchers.notNullValue()); assertThat( result.inputs, Matchers.containsInAnyOrder(RuleKeyInput.of(filesystem, input.getRelativePath()))); assertThat(result.deps, Matchers.containsInAnyOrder(dep, appendable)); } @Test public void testFactoryReportsInputsAndDependenciesToCacheForRuleKeyAppendable() throws IOException { SourcePathRuleFinder ruleFinder = new SourcePathRuleFinder( new BuildRuleResolver( TargetGraph.EMPTY, new DefaultTargetNodeToBuildRuleTransformer())); SourcePathResolver pathResolver = new SourcePathResolver(ruleFinder); NoopRuleKeyCache<RuleKey> noopRuleKeyCache = new NoopRuleKeyCache<>(); ProjectFilesystem filesystem = new FakeProjectFilesystem(); DefaultRuleKeyFactory factory = new DefaultRuleKeyFactory( new RuleKeyFieldLoader(0), new StackedFileHashCache( ImmutableList.of(DefaultFileHashCache.createDefaultFileHashCache(filesystem))), pathResolver, ruleFinder, noopRuleKeyCache); // Create a sample input. PathSourcePath input = new PathSourcePath(filesystem, filesystem.getPath("input")); filesystem.touch(input.getRelativePath()); // Create a sample dep rule. BuildRule dep = new EmptyRule(BuildTargetFactory.newInstance("//:dep")); // Create a sample dep appendable. RuleKeyAppendable depAppendable = sink -> {}; // Create a sample rule key appendable. RuleKeyAppendable appendable = sink -> { sink.setReflectively("input", input); sink.setReflectively("dep", dep); sink.setReflectively("depAppendable", depAppendable); }; // Create a dummy build rule that uses the input. BuildRule rule = new NoopBuildRule( new FakeBuildRuleParamsBuilder("//:target").setProjectFilesystem(filesystem).build()) { @AddToRuleKey private final RuleKeyAppendable appendableField = appendable; }; // Build the rule key. factory.build(rule); // Verify the input was properly reported to the rule key cache. RuleKeyResult<RuleKey> result = noopRuleKeyCache.results.get(appendable); assertThat(result, Matchers.notNullValue()); assertThat( result.inputs, Matchers.containsInAnyOrder(RuleKeyInput.of(filesystem, input.getRelativePath()))); assertThat(result.deps, Matchers.containsInAnyOrder(dep, depAppendable)); } private void assertBothKeysAndValuesGetHashed(@Nullable Object val1, @Nullable Object val2) { assertKeysGetHashed(val1); assertValuesGetHashed(val1, val2); } private void assertKeysGetHashed(@Nullable Object val) { BuildTarget target = BuildTargetFactory.newInstance("//cheese:peas"); SourcePathRuleFinder ruleFinder = new SourcePathRuleFinder( new BuildRuleResolver( TargetGraph.EMPTY, new DefaultTargetNodeToBuildRuleTransformer())); SourcePathResolver pathResolver = new SourcePathResolver(ruleFinder); BuildRule rule = new EmptyRule(target); DefaultRuleKeyFactory factory = new DefaultRuleKeyFactory(0, new NullFileHashCache(), pathResolver, ruleFinder); RuleKey key1 = factory.newBuilderForTesting(rule).setReflectively("key1", val).build(RuleKey::new); RuleKey key1again = factory.newBuilderForTesting(rule).setReflectively("key1", val).build(RuleKey::new); RuleKey key2 = factory.newBuilderForTesting(rule).setReflectively("key2", val).build(RuleKey::new); assertEquals("Rule keys should be same! " + val, key1, key1again); assertNotEquals("Rule keys should be different! " + val, key1, key2); } private void assertValuesGetHashed(@Nullable Object val1, @Nullable Object val2) { Preconditions.checkArgument(!Objects.equal(val1, val2), "Values should be different!"); BuildTarget target = BuildTargetFactory.newInstance("//cheese:peas"); SourcePathRuleFinder ruleFinder = new SourcePathRuleFinder( new BuildRuleResolver( TargetGraph.EMPTY, new DefaultTargetNodeToBuildRuleTransformer())); SourcePathResolver pathResolver = new SourcePathResolver(ruleFinder); BuildRule rule = new EmptyRule(target); DefaultRuleKeyFactory factory = new DefaultRuleKeyFactory(0, new NullFileHashCache(), pathResolver, ruleFinder); RuleKey key1 = factory.newBuilderForTesting(rule).setReflectively("key", val1).build(RuleKey::new); RuleKey key1again = factory.newBuilderForTesting(rule).setReflectively("key", val1).build(RuleKey::new); RuleKey key2 = factory.newBuilderForTesting(rule).setReflectively("key", val2).build(RuleKey::new); assertEquals("Rule keys should be same! " + val1, key1, key1again); assertNotEquals("Rule keys should be different! " + val1 + " != " + val2, key1, key2); } private static class Appender implements RuleKeyAppendable { @Override public void appendToRuleKey(RuleKeyObjectSink sink) { sink.setReflectively("cheese", "brie"); } @Override public String toString() { return "cheddar"; } } /** A hollow shell of a build rule containing absolutely no marked up fields. */ private static class EmptyRule implements BuildRule { private final BuildTarget target; public EmptyRule(BuildTarget target) { this.target = target; } @Override public BuildTarget getBuildTarget() { return target; } @Override public String getType() { return "empty"; } @Override public BuildableProperties getProperties() { return new BuildableProperties(LIBRARY); } @Override public ImmutableSortedSet<BuildRule> getBuildDeps() { return ImmutableSortedSet.of(); } @Override public ProjectFilesystem getProjectFilesystem() { return new FakeProjectFilesystem(); } @Override public ImmutableList<Step> getBuildSteps( BuildContext context, BuildableContext buildableContext) { throw new UnsupportedOperationException("getBuildSteps"); } @Nullable @Override public SourcePath getSourcePathToOutput() { return null; } @Override public boolean isCacheable() { return true; } } private enum DummyEnum { BLACK, WHITE, } private static class NoopRuleKeyCache<V> implements RuleKeyCache<V> { private final Map<Object, RuleKeyResult<V>> results = new MapMaker().weakKeys().makeMap(); @Override public V get(BuildRule rule, Function<? super BuildRule, RuleKeyResult<V>> create) { RuleKeyResult<V> result = create.apply(rule); results.put(rule, result); return result.result; } @Override public V get( RuleKeyAppendable appendable, Function<? super RuleKeyAppendable, RuleKeyResult<V>> create) { RuleKeyResult<V> result = create.apply(appendable); results.put(appendable, result); return result.result; } public boolean isCached(BuildRule rule) { throw new UnsupportedOperationException(); } public boolean isCached(RuleKeyAppendable appendable) { throw new UnsupportedOperationException(); } @Override public void invalidateInputs(Iterable<RuleKeyInput> inputs) { throw new UnsupportedOperationException(); } @Override public void invalidateInputsMatchingRelativePath(Path path) { throw new UnsupportedOperationException(); } @Override public void invalidateAllExceptFilesystems(ImmutableSet<ProjectFilesystem> filesystems) { throw new UnsupportedOperationException(); } @Override public void invalidateFilesystem(ProjectFilesystem filesystem) { throw new UnsupportedOperationException(); } @Override public void invalidateAll() { throw new UnsupportedOperationException(); } @Override public CacheStats getStats() { throw new UnsupportedOperationException(); } } }