/*
* Copyright 2012-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.artifact_cache;
import static java.nio.charset.StandardCharsets.UTF_8;
import static org.junit.Assert.assertEquals;
import static org.junit.Assert.assertFalse;
import static org.junit.Assert.assertSame;
import static org.junit.Assert.assertThat;
import static org.junit.Assert.assertTrue;
import com.facebook.buck.io.BorrowablePath;
import com.facebook.buck.io.LazyPath;
import com.facebook.buck.io.ProjectFilesystem;
import com.facebook.buck.model.BuildTargetFactory;
import com.facebook.buck.rules.AddToRuleKey;
import com.facebook.buck.rules.BuildRule;
import com.facebook.buck.rules.BuildRuleResolver;
import com.facebook.buck.rules.DefaultTargetNodeToBuildRuleTransformer;
import com.facebook.buck.rules.FakeBuildRule;
import com.facebook.buck.rules.PathSourcePath;
import com.facebook.buck.rules.RuleKey;
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.rules.keys.DefaultRuleKeyFactory;
import com.facebook.buck.testutil.FakeFileHashCache;
import com.facebook.buck.testutil.FakeProjectFilesystem;
import com.facebook.buck.testutil.integration.TemporaryPaths;
import com.facebook.buck.util.DirectoryCleaner;
import com.facebook.buck.util.MoreCollectors;
import com.facebook.buck.util.cache.FileHashCache;
import com.facebook.buck.util.cache.NullFileHashCache;
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.io.IOException;
import java.nio.file.Files;
import java.nio.file.Path;
import java.nio.file.Paths;
import java.nio.file.attribute.FileTime;
import java.util.List;
import java.util.Optional;
import org.hamcrest.Matchers;
import org.junit.After;
import org.junit.Rule;
import org.junit.Test;
public class DirArtifactCacheTest {
@Rule public TemporaryPaths tmpDir = new TemporaryPaths();
private FileHashCache fileHashCache = new NullFileHashCache();
private DirArtifactCache dirArtifactCache;
@After
public void tearDown() {
if (dirArtifactCache != null) {
dirArtifactCache.close();
}
}
@Test
public void testCacheCreation() throws InterruptedException, IOException {
Path cacheDir = tmpDir.newFolder();
dirArtifactCache =
new DirArtifactCache(
"dir",
new ProjectFilesystem(cacheDir),
Paths.get("."),
CacheReadMode.READWRITE,
/* maxCacheSizeBytes */ Optional.of(0L));
}
@Test
public void testCacheFetchMiss() throws InterruptedException, IOException {
Path cacheDir = tmpDir.newFolder();
Path fileX = tmpDir.newFile("x");
fileHashCache = new FakeFileHashCache(ImmutableMap.of(fileX, HashCode.fromInt(0)));
dirArtifactCache =
new DirArtifactCache(
"dir",
new ProjectFilesystem(cacheDir),
Paths.get("."),
CacheReadMode.READWRITE,
/* maxCacheSizeBytes */ Optional.of(0L));
Files.write(fileX, "x".getBytes(UTF_8));
BuildRule inputRuleX = new BuildRuleForTest(fileX);
BuildRuleResolver ruleResolver =
new BuildRuleResolver(TargetGraph.EMPTY, new DefaultTargetNodeToBuildRuleTransformer());
ruleResolver.addToIndex(inputRuleX);
SourcePathRuleFinder ruleFinder = new SourcePathRuleFinder(ruleResolver);
SourcePathResolver resolver = new SourcePathResolver(ruleFinder);
RuleKey ruleKeyX =
new DefaultRuleKeyFactory(0, fileHashCache, resolver, ruleFinder).build(inputRuleX);
assertEquals(
CacheResultType.MISS,
dirArtifactCache.fetch(ruleKeyX, LazyPath.ofInstance(fileX)).getType());
}
@Test
public void testCacheStoreAndFetchHit() throws InterruptedException, IOException {
Path cacheDir = tmpDir.newFolder();
Path fileX = tmpDir.newFile("x");
fileHashCache = new FakeFileHashCache(ImmutableMap.of(fileX, HashCode.fromInt(0)));
dirArtifactCache =
new DirArtifactCache(
"dir",
new ProjectFilesystem(cacheDir),
Paths.get("."),
CacheReadMode.READWRITE,
/* maxCacheSizeBytes */ Optional.empty());
Files.write(fileX, "x".getBytes(UTF_8));
BuildRule inputRuleX = new BuildRuleForTest(fileX);
BuildRuleResolver ruleResolver =
new BuildRuleResolver(TargetGraph.EMPTY, new DefaultTargetNodeToBuildRuleTransformer());
ruleResolver.addToIndex(inputRuleX);
SourcePathRuleFinder ruleFinder = new SourcePathRuleFinder(ruleResolver);
SourcePathResolver resolver = new SourcePathResolver(ruleFinder);
RuleKey ruleKeyX =
new DefaultRuleKeyFactory(0, fileHashCache, resolver, ruleFinder).build(inputRuleX);
dirArtifactCache.store(
ArtifactInfo.builder().addRuleKeys(ruleKeyX).build(),
BorrowablePath.notBorrowablePath(fileX));
// Test that artifact overwrite works.
assertEquals(
CacheResultType.HIT,
dirArtifactCache.fetch(ruleKeyX, LazyPath.ofInstance(fileX)).getType());
assertEquals(inputRuleX, new BuildRuleForTest(fileX));
// Test that artifact creation works.
Files.delete(fileX);
assertEquals(
CacheResultType.HIT,
dirArtifactCache.fetch(ruleKeyX, LazyPath.ofInstance(fileX)).getType());
assertEquals(inputRuleX, new BuildRuleForTest(fileX));
}
@Test
public void testCacheStoreOverwrite() throws InterruptedException, IOException {
Path cacheDir = tmpDir.newFolder();
Path fileX = tmpDir.newFile("x");
fileHashCache = new FakeFileHashCache(ImmutableMap.of(fileX, HashCode.fromInt(0)));
dirArtifactCache =
new DirArtifactCache(
"dir",
new ProjectFilesystem(cacheDir),
Paths.get("."),
CacheReadMode.READWRITE,
/* maxCacheSizeBytes */ Optional.empty());
Files.write(fileX, "x".getBytes(UTF_8));
BuildRule inputRuleX = new BuildRuleForTest(fileX);
BuildRuleResolver ruleResolver =
new BuildRuleResolver(TargetGraph.EMPTY, new DefaultTargetNodeToBuildRuleTransformer());
ruleResolver.addToIndex(inputRuleX);
SourcePathRuleFinder ruleFinder = new SourcePathRuleFinder(ruleResolver);
SourcePathResolver resolver = new SourcePathResolver(ruleFinder);
RuleKey ruleKeyX =
new DefaultRuleKeyFactory(0, fileHashCache, resolver, ruleFinder).build(inputRuleX);
dirArtifactCache.store(
ArtifactInfo.builder().addRuleKeys(ruleKeyX).build(),
BorrowablePath.notBorrowablePath(fileX));
// Overwrite.
dirArtifactCache.store(
ArtifactInfo.builder().addRuleKeys(ruleKeyX).build(),
BorrowablePath.notBorrowablePath(fileX));
assertEquals(
CacheResultType.HIT,
dirArtifactCache.fetch(ruleKeyX, LazyPath.ofInstance(fileX)).getType());
assertEquals(inputRuleX, new BuildRuleForTest(fileX));
}
@Test
public void testCacheStoresAndFetchHits() throws InterruptedException, IOException {
Path cacheDir = tmpDir.newFolder();
Path fileX = tmpDir.newFile("x");
Path fileY = tmpDir.newFile("y");
Path fileZ = tmpDir.newFile("z");
fileHashCache =
new FakeFileHashCache(
ImmutableMap.of(
fileX, HashCode.fromInt(0),
fileY, HashCode.fromInt(1),
fileZ, HashCode.fromInt(2)));
dirArtifactCache =
new DirArtifactCache(
"dir",
new ProjectFilesystem(cacheDir),
Paths.get("."),
CacheReadMode.READWRITE,
/* maxCacheSizeBytes */ Optional.empty());
Files.write(fileX, "x".getBytes(UTF_8));
Files.write(fileY, "y".getBytes(UTF_8));
Files.write(fileZ, "x".getBytes(UTF_8));
BuildRule inputRuleX = new BuildRuleForTest(fileX);
BuildRule inputRuleY = new BuildRuleForTest(fileY);
BuildRule inputRuleZ = new BuildRuleForTest(fileZ);
assertFalse(inputRuleX.equals(inputRuleY));
assertFalse(inputRuleX.equals(inputRuleZ));
assertFalse(inputRuleY.equals(inputRuleZ));
BuildRuleResolver ruleResolver =
new BuildRuleResolver(TargetGraph.EMPTY, new DefaultTargetNodeToBuildRuleTransformer());
ruleResolver.addToIndex(inputRuleX);
ruleResolver.addToIndex(inputRuleY);
ruleResolver.addToIndex(inputRuleZ);
SourcePathRuleFinder ruleFinder = new SourcePathRuleFinder(ruleResolver);
SourcePathResolver resolver = new SourcePathResolver(ruleFinder);
DefaultRuleKeyFactory fakeRuleKeyFactory =
new DefaultRuleKeyFactory(0, fileHashCache, resolver, ruleFinder);
RuleKey ruleKeyX = fakeRuleKeyFactory.build(inputRuleX);
RuleKey ruleKeyY = fakeRuleKeyFactory.build(inputRuleY);
RuleKey ruleKeyZ = fakeRuleKeyFactory.build(inputRuleZ);
assertEquals(
CacheResultType.MISS,
dirArtifactCache.fetch(ruleKeyX, LazyPath.ofInstance(fileX)).getType());
assertEquals(
CacheResultType.MISS,
dirArtifactCache.fetch(ruleKeyY, LazyPath.ofInstance(fileY)).getType());
assertEquals(
CacheResultType.MISS,
dirArtifactCache.fetch(ruleKeyZ, LazyPath.ofInstance(fileZ)).getType());
dirArtifactCache.store(
ArtifactInfo.builder().addRuleKeys(ruleKeyX).build(),
BorrowablePath.notBorrowablePath(fileX));
dirArtifactCache.store(
ArtifactInfo.builder().addRuleKeys(ruleKeyY).build(),
BorrowablePath.notBorrowablePath(fileY));
dirArtifactCache.store(
ArtifactInfo.builder().addRuleKeys(ruleKeyZ).build(),
BorrowablePath.notBorrowablePath(fileZ));
Files.delete(fileX);
Files.delete(fileY);
Files.delete(fileZ);
assertEquals(
CacheResultType.HIT,
dirArtifactCache.fetch(ruleKeyX, LazyPath.ofInstance(fileX)).getType());
assertEquals(
CacheResultType.HIT,
dirArtifactCache.fetch(ruleKeyY, LazyPath.ofInstance(fileY)).getType());
assertEquals(
CacheResultType.HIT,
dirArtifactCache.fetch(ruleKeyZ, LazyPath.ofInstance(fileZ)).getType());
assertEquals(inputRuleX, new BuildRuleForTest(fileX));
assertEquals(inputRuleY, new BuildRuleForTest(fileY));
assertEquals(inputRuleZ, new BuildRuleForTest(fileZ));
ImmutableList<Path> cachedFiles = ImmutableList.copyOf(dirArtifactCache.getAllFilesInCache());
assertEquals(6, cachedFiles.size());
ImmutableSet<String> filenames =
cachedFiles
.stream()
.map(input -> input.getFileName().toString())
.collect(MoreCollectors.toImmutableSet());
for (RuleKey ruleKey : ImmutableSet.of(ruleKeyX, ruleKeyY, ruleKeyZ)) {
assertThat(filenames, Matchers.hasItem(ruleKey.toString()));
assertThat(filenames, Matchers.hasItem(ruleKey.toString() + ".metadata"));
}
}
@Test
public void testCacheStoresAndBorrowsPaths() throws InterruptedException, IOException {
Path cacheDir = tmpDir.newFolder();
Path fileX = tmpDir.newFile("x");
Path fileY = tmpDir.newFile("y");
fileHashCache =
new FakeFileHashCache(
ImmutableMap.of(
fileX, HashCode.fromInt(0),
fileY, HashCode.fromInt(1)));
dirArtifactCache =
new DirArtifactCache(
"dir",
new ProjectFilesystem(cacheDir),
Paths.get("."),
CacheReadMode.READWRITE,
/* maxCacheSizeBytes */ Optional.empty());
Files.write(fileX, "x".getBytes(UTF_8));
Files.write(fileY, "y".getBytes(UTF_8));
BuildRule inputRuleX = new BuildRuleForTest(fileX);
BuildRule inputRuleY = new BuildRuleForTest(fileY);
assertFalse(inputRuleX.equals(inputRuleY));
BuildRuleResolver ruleResolver =
new BuildRuleResolver(TargetGraph.EMPTY, new DefaultTargetNodeToBuildRuleTransformer());
ruleResolver.addToIndex(inputRuleX);
ruleResolver.addToIndex(inputRuleY);
SourcePathRuleFinder ruleFinder = new SourcePathRuleFinder(ruleResolver);
SourcePathResolver resolver = new SourcePathResolver(ruleFinder);
DefaultRuleKeyFactory fakeRuleKeyFactory =
new DefaultRuleKeyFactory(0, fileHashCache, resolver, ruleFinder);
RuleKey ruleKeyX = fakeRuleKeyFactory.build(inputRuleX);
RuleKey ruleKeyY = fakeRuleKeyFactory.build(inputRuleY);
dirArtifactCache.store(
ArtifactInfo.builder().addRuleKeys(ruleKeyX).build(), BorrowablePath.borrowablePath(fileX));
dirArtifactCache.store(
ArtifactInfo.builder().addRuleKeys(ruleKeyY).build(),
BorrowablePath.notBorrowablePath(fileY));
assertThat(Files.exists(fileX), Matchers.is(false));
assertThat(Files.exists(fileY), Matchers.is(true));
}
@Test
public void testNoStoreMisses() throws InterruptedException, IOException {
Path cacheDir = tmpDir.newFolder();
Path fileX = tmpDir.newFile("x");
Path fileY = tmpDir.newFile("y");
Path fileZ = tmpDir.newFile("z");
fileHashCache =
new FakeFileHashCache(
ImmutableMap.of(
fileX, HashCode.fromInt(0),
fileY, HashCode.fromInt(1),
fileZ, HashCode.fromInt(2)));
dirArtifactCache =
new DirArtifactCache(
"dir",
new ProjectFilesystem(cacheDir),
Paths.get("."),
CacheReadMode.READONLY,
/* maxCacheSizeBytes */ Optional.of(0L));
Files.write(fileX, "x".getBytes(UTF_8));
Files.write(fileY, "y".getBytes(UTF_8));
Files.write(fileZ, "z".getBytes(UTF_8));
BuildRule inputRuleX = new BuildRuleForTest(fileX);
BuildRule inputRuleY = new BuildRuleForTest(fileY);
BuildRule inputRuleZ = new BuildRuleForTest(fileZ);
assertFalse(inputRuleX.equals(inputRuleY));
assertFalse(inputRuleX.equals(inputRuleZ));
assertFalse(inputRuleY.equals(inputRuleZ));
BuildRuleResolver ruleResolver =
new BuildRuleResolver(TargetGraph.EMPTY, new DefaultTargetNodeToBuildRuleTransformer());
ruleResolver.addToIndex(inputRuleX);
ruleResolver.addToIndex(inputRuleY);
ruleResolver.addToIndex(inputRuleZ);
SourcePathRuleFinder ruleFinder = new SourcePathRuleFinder(ruleResolver);
SourcePathResolver resolver = new SourcePathResolver(ruleFinder);
DefaultRuleKeyFactory fakeRuleKeyFactory =
new DefaultRuleKeyFactory(0, fileHashCache, resolver, ruleFinder);
RuleKey ruleKeyX = fakeRuleKeyFactory.build(inputRuleX);
RuleKey ruleKeyY = fakeRuleKeyFactory.build(inputRuleY);
RuleKey ruleKeyZ = fakeRuleKeyFactory.build(inputRuleZ);
assertEquals(
CacheResultType.MISS,
dirArtifactCache.fetch(ruleKeyX, LazyPath.ofInstance(fileX)).getType());
assertEquals(
CacheResultType.MISS,
dirArtifactCache.fetch(ruleKeyY, LazyPath.ofInstance(fileY)).getType());
assertEquals(
CacheResultType.MISS,
dirArtifactCache.fetch(ruleKeyZ, LazyPath.ofInstance(fileZ)).getType());
dirArtifactCache.store(
ArtifactInfo.builder().addRuleKeys(ruleKeyX).build(),
BorrowablePath.notBorrowablePath(fileX));
dirArtifactCache.store(
ArtifactInfo.builder().addRuleKeys(ruleKeyY).build(),
BorrowablePath.notBorrowablePath(fileY));
dirArtifactCache.store(
ArtifactInfo.builder().addRuleKeys(ruleKeyZ).build(),
BorrowablePath.notBorrowablePath(fileZ));
Files.delete(fileX);
Files.delete(fileY);
Files.delete(fileZ);
assertEquals(
CacheResultType.MISS,
dirArtifactCache.fetch(ruleKeyX, LazyPath.ofInstance(fileX)).getType());
assertEquals(
CacheResultType.MISS,
dirArtifactCache.fetch(ruleKeyY, LazyPath.ofInstance(fileY)).getType());
assertEquals(
CacheResultType.MISS,
dirArtifactCache.fetch(ruleKeyZ, LazyPath.ofInstance(fileZ)).getType());
assertEquals(inputRuleX, new BuildRuleForTest(fileX));
assertEquals(inputRuleY, new BuildRuleForTest(fileY));
assertEquals(inputRuleZ, new BuildRuleForTest(fileZ));
assertEquals(0, cacheDir.toFile().listFiles().length);
}
@Test
public void testDeleteNothing() throws InterruptedException, IOException {
Path cacheDir = tmpDir.newFolder();
Path fileX = cacheDir.resolve("x");
Path fileY = cacheDir.resolve("y");
Path fileZ = cacheDir.resolve("z");
dirArtifactCache =
new DirArtifactCache(
"dir",
new ProjectFilesystem(tmpDir.getRoot()),
Paths.get("."),
CacheReadMode.READWRITE,
/* maxCacheSizeBytes */ Optional.of(1024L));
Files.write(fileX, "x".getBytes(UTF_8));
Files.write(fileY, "y".getBytes(UTF_8));
Files.write(fileZ, "z".getBytes(UTF_8));
assertEquals(3, cacheDir.toFile().listFiles().length);
dirArtifactCache.deleteOldFiles();
assertEquals(3, cacheDir.toFile().listFiles().length);
}
@Test
public void testDeleteNothingAbsentLimit() throws InterruptedException, IOException {
Path cacheDir = tmpDir.newFolder();
Path fileX = cacheDir.resolve("x");
Path fileY = cacheDir.resolve("y");
Path fileZ = cacheDir.resolve("z");
dirArtifactCache =
new DirArtifactCache(
"dir",
new ProjectFilesystem(tmpDir.getRoot()),
Paths.get("."),
CacheReadMode.READWRITE,
/* maxCacheSizeBytes */ Optional.empty());
Files.write(fileX, "x".getBytes(UTF_8));
Files.write(fileY, "y".getBytes(UTF_8));
Files.write(fileZ, "z".getBytes(UTF_8));
assertEquals(3, cacheDir.toFile().listFiles().length);
dirArtifactCache.deleteOldFiles();
assertEquals(3, cacheDir.toFile().listFiles().length);
}
@Test
public void testDeleteSome() throws InterruptedException, IOException {
Path cacheDir = tmpDir.newFolder();
Path fileW = cacheDir.resolve("11").resolve("11").resolve("w");
Path fileX = cacheDir.resolve("22").resolve("22").resolve("x");
Path fileY = cacheDir.resolve("33").resolve("33").resolve("y");
Path fileZ = cacheDir.resolve("44").resolve("44").resolve("z");
Files.createDirectories(fileW.getParent());
Files.createDirectories(fileX.getParent());
Files.createDirectories(fileY.getParent());
Files.createDirectories(fileZ.getParent());
dirArtifactCache =
new DirArtifactCache(
"dir",
new ProjectFilesystem(cacheDir),
Paths.get("."),
CacheReadMode.READWRITE,
/* maxCacheSizeBytes */ Optional.of(3L));
Files.write(fileW, "w".getBytes(UTF_8));
Files.write(fileX, "x".getBytes(UTF_8));
Files.write(fileY, "y".getBytes(UTF_8));
Files.write(fileZ, "z".getBytes(UTF_8));
Files.setAttribute(fileW, "lastAccessTime", FileTime.fromMillis(9000));
Files.setAttribute(fileX, "lastAccessTime", FileTime.fromMillis(0));
Files.setAttribute(fileY, "lastAccessTime", FileTime.fromMillis(1000));
Files.setAttribute(fileZ, "lastAccessTime", FileTime.fromMillis(2000));
// On some filesystems, setting creationTime silently fails. So don't test that here.
assertEquals(4, cacheDir.toFile().listFiles().length);
dirArtifactCache.deleteOldFiles();
List<Path> filesInCache = dirArtifactCache.getAllFilesInCache();
assertEquals(ImmutableSet.of(fileZ, fileW), ImmutableSet.copyOf(filesInCache));
}
private DirectoryCleaner.PathStats fakePathStats(long creationTime, long lastAccessTime) {
return new DirectoryCleaner.PathStats(null, 0, creationTime, lastAccessTime);
}
@Test
public void testDirectoryCleanerPathSelector() throws InterruptedException, IOException {
Path cacheDir = tmpDir.newFolder();
dirArtifactCache =
new DirArtifactCache(
"dir",
new ProjectFilesystem(cacheDir),
Paths.get("."),
CacheReadMode.READWRITE,
/* maxCacheSizeBytes */ Optional.of(3L));
DirectoryCleaner.PathSelector pathSelector = dirArtifactCache.getDirectoryCleanerPathSelector();
assertTrue(pathSelector.comparePaths(fakePathStats(0, 0), fakePathStats(0, 10)) < 0);
assertTrue(pathSelector.comparePaths(fakePathStats(0, 10), fakePathStats(10, 10)) < 0);
assertSame(pathSelector.comparePaths(fakePathStats(20, 10), fakePathStats(20, 10)), 0);
assertTrue(pathSelector.comparePaths(fakePathStats(0, 20), fakePathStats(10, 10)) > 0);
assertTrue(pathSelector.comparePaths(fakePathStats(20, 10), fakePathStats(10, 10)) > 0);
}
@Test
public void testDeleteAfterStoreIfFull() throws InterruptedException, IOException {
Path cacheDir = tmpDir.newFolder();
Path fileX = tmpDir.newFile("x");
Path fileY = tmpDir.newFile("y");
Path fileZ = tmpDir.newFile("z");
fileHashCache =
new FakeFileHashCache(
ImmutableMap.of(
fileX, HashCode.fromInt(0),
fileY, HashCode.fromInt(1),
fileZ, HashCode.fromInt(2)));
// The reason max size is 9 bytes is because a 1-byte entry actually takes 6 bytes to store.
// If the cache trims the size down to 2/3 (6 bytes) every time it hits the max it means after
// every store only the most recent artifact should be left.
dirArtifactCache =
new DirArtifactCache(
"dir",
new ProjectFilesystem(cacheDir),
cacheDir,
CacheReadMode.READWRITE,
/* maxCacheSizeBytes */ Optional.of(9L));
Files.write(fileX, "x".getBytes(UTF_8));
Files.write(fileY, "y".getBytes(UTF_8));
Files.write(fileZ, "z".getBytes(UTF_8));
BuildRule inputRuleX = new BuildRuleForTest(fileX);
BuildRule inputRuleY = new BuildRuleForTest(fileY);
BuildRule inputRuleZ = new BuildRuleForTest(fileZ);
BuildRuleResolver ruleResolver =
new BuildRuleResolver(TargetGraph.EMPTY, new DefaultTargetNodeToBuildRuleTransformer());
ruleResolver.addToIndex(inputRuleX);
ruleResolver.addToIndex(inputRuleY);
ruleResolver.addToIndex(inputRuleZ);
SourcePathRuleFinder ruleFinder = new SourcePathRuleFinder(ruleResolver);
SourcePathResolver resolver = new SourcePathResolver(ruleFinder);
DefaultRuleKeyFactory fakeRuleKeyFactory =
new DefaultRuleKeyFactory(0, fileHashCache, resolver, ruleFinder);
RuleKey ruleKeyX = fakeRuleKeyFactory.build(inputRuleX);
RuleKey ruleKeyY = fakeRuleKeyFactory.build(inputRuleY);
RuleKey ruleKeyZ = fakeRuleKeyFactory.build(inputRuleZ);
dirArtifactCache.store(
ArtifactInfo.builder().addRuleKeys(ruleKeyX).build(),
BorrowablePath.notBorrowablePath(fileX));
assertEquals(
CacheResultType.HIT,
dirArtifactCache.fetch(ruleKeyX, LazyPath.ofInstance(fileX)).getType());
Files.setAttribute(
dirArtifactCache.getPathForRuleKey(ruleKeyX, Optional.empty()),
"lastAccessTime",
FileTime.fromMillis(0));
Files.setAttribute(
dirArtifactCache.getPathForRuleKey(ruleKeyX, Optional.of(".metadata")),
"lastAccessTime",
FileTime.fromMillis(0));
dirArtifactCache.store(
ArtifactInfo.builder().addRuleKeys(ruleKeyY).build(),
BorrowablePath.notBorrowablePath(fileY));
assertEquals(
CacheResultType.MISS,
dirArtifactCache.fetch(ruleKeyX, LazyPath.ofInstance(fileX)).getType());
assertEquals(
CacheResultType.HIT,
dirArtifactCache.fetch(ruleKeyY, LazyPath.ofInstance(fileY)).getType());
Files.setAttribute(
dirArtifactCache.getPathForRuleKey(ruleKeyY, Optional.empty()),
"lastAccessTime",
FileTime.fromMillis(1000));
Files.setAttribute(
dirArtifactCache.getPathForRuleKey(ruleKeyY, Optional.of(".metadata")),
"lastAccessTime",
FileTime.fromMillis(1000));
dirArtifactCache.store(
ArtifactInfo.builder().addRuleKeys(ruleKeyZ).build(),
BorrowablePath.notBorrowablePath(fileZ));
assertEquals(
CacheResultType.MISS,
dirArtifactCache.fetch(ruleKeyX, LazyPath.ofInstance(fileX)).getType());
assertEquals(
CacheResultType.MISS,
dirArtifactCache.fetch(ruleKeyY, LazyPath.ofInstance(fileY)).getType());
assertEquals(
CacheResultType.HIT,
dirArtifactCache.fetch(ruleKeyZ, LazyPath.ofInstance(fileZ)).getType());
}
@Test
public void testCacheStoreMultipleKeys() throws InterruptedException, IOException {
Path cacheDir = tmpDir.newFolder();
Path fileX = tmpDir.newFile("x");
fileHashCache = new FakeFileHashCache(ImmutableMap.of(fileX, HashCode.fromInt(0)));
dirArtifactCache =
new DirArtifactCache(
"dir",
new ProjectFilesystem(cacheDir),
Paths.get("."),
CacheReadMode.READWRITE,
/* maxCacheSizeBytes */ Optional.empty());
Files.write(fileX, "x".getBytes(UTF_8));
RuleKey ruleKey1 = new RuleKey("aaaa");
RuleKey ruleKey2 = new RuleKey("bbbb");
dirArtifactCache.store(
ArtifactInfo.builder().addRuleKeys(ruleKey1, ruleKey2).build(),
BorrowablePath.notBorrowablePath(fileX));
// Test that artifact is available via both keys.
assertEquals(
CacheResultType.HIT,
dirArtifactCache.fetch(ruleKey1, LazyPath.ofInstance(fileX)).getType());
assertEquals(
CacheResultType.HIT,
dirArtifactCache.fetch(ruleKey2, LazyPath.ofInstance(fileX)).getType());
}
@Test
public void testCacheStoreAndFetchMetadata() throws IOException {
FakeProjectFilesystem filesystem = new FakeProjectFilesystem();
DirArtifactCache cache =
new DirArtifactCache(
"dir",
filesystem,
Paths.get("cache"),
CacheReadMode.READWRITE,
/* maxCacheSizeBytes */ Optional.empty());
RuleKey ruleKey = new RuleKey("0000");
ImmutableMap<String, String> metadata = ImmutableMap.of("some", "metadata");
// Create a dummy data file.
Path data = Paths.get("data");
filesystem.touch(data);
// Store the artifact with metadata then re-fetch.
cache.store(
ArtifactInfo.builder().addRuleKeys(ruleKey).setMetadata(metadata).build(),
BorrowablePath.notBorrowablePath(data));
CacheResult result = cache.fetch(ruleKey, LazyPath.ofInstance(Paths.get("out-data")));
// Verify that the metadata is correct.
assertThat(result.getType(), Matchers.equalTo(CacheResultType.HIT));
assertThat(result.getMetadata(), Matchers.equalTo(metadata));
assertThat(result.getArtifactSizeBytes(), Matchers.equalTo(filesystem.getFileSize(data)));
cache.close();
}
@Test
public void testFolderLevelsForRuleKeys() throws IOException {
DirArtifactCache cache =
new DirArtifactCache(
"dir",
new FakeProjectFilesystem(),
Paths.get("cache"),
CacheReadMode.READONLY,
/* maxCacheSizeBytes */ Optional.empty());
Path result = cache.getPathForRuleKey(new RuleKey("aabb0123123234e324"), Optional.empty());
assertThat(result.endsWith(Paths.get("aa/bb/aabb0123123234e324")), Matchers.equalTo(true));
result = cache.getPathForRuleKey(new RuleKey("aabb0123123234e324"), Optional.of(".ext"));
assertThat(result.endsWith(Paths.get("aa/bb/aabb0123123234e324.ext")), Matchers.equalTo(true));
cache.close();
}
private static class BuildRuleForTest extends FakeBuildRule {
@SuppressWarnings("PMD.UnusedPrivateField")
@AddToRuleKey
private final SourcePath file;
private BuildRuleForTest(Path file) {
super(
BuildTargetFactory.newInstance("//foo:" + file.getFileName().toString()),
new SourcePathResolver(
new SourcePathRuleFinder(
new BuildRuleResolver(
TargetGraph.EMPTY, new DefaultTargetNodeToBuildRuleTransformer()))));
// TODO(15468825) - PathSourcePath should be relative!11!!11!1!!!
this.file = new PathSourcePath(new FakeProjectFilesystem(), file);
}
}
}