aboutsummaryrefslogtreecommitdiffhomepage
path: root/src/test/java/com/google/devtools/build/lib/actions/DigestUtilsTest.java
diff options
context:
space:
mode:
Diffstat (limited to 'src/test/java/com/google/devtools/build/lib/actions/DigestUtilsTest.java')
-rw-r--r--src/test/java/com/google/devtools/build/lib/actions/DigestUtilsTest.java144
1 files changed, 136 insertions, 8 deletions
diff --git a/src/test/java/com/google/devtools/build/lib/actions/DigestUtilsTest.java b/src/test/java/com/google/devtools/build/lib/actions/DigestUtilsTest.java
index 70d2f40b22..b37eb4c830 100644
--- a/src/test/java/com/google/devtools/build/lib/actions/DigestUtilsTest.java
+++ b/src/test/java/com/google/devtools/build/lib/actions/DigestUtilsTest.java
@@ -16,10 +16,13 @@ package com.google.devtools.build.lib.actions;
import static org.junit.Assert.assertArrayEquals;
import static org.junit.Assert.assertEquals;
+import static org.junit.Assert.assertNotNull;
import static org.junit.Assert.assertNotSame;
import static org.junit.Assert.assertTrue;
+import static org.junit.Assert.fail;
import com.google.common.base.Strings;
+import com.google.common.cache.CacheStats;
import com.google.devtools.build.lib.actions.cache.DigestUtils;
import com.google.devtools.build.lib.testutil.TestThread;
import com.google.devtools.build.lib.testutil.TestUtils;
@@ -29,15 +32,16 @@ import com.google.devtools.build.lib.vfs.FileSystem.HashFunction;
import com.google.devtools.build.lib.vfs.FileSystemUtils;
import com.google.devtools.build.lib.vfs.Path;
import com.google.devtools.build.lib.vfs.inmemoryfs.InMemoryFileSystem;
-
-import org.junit.Test;
-import org.junit.runner.RunWith;
-import org.junit.runners.JUnit4;
-
import java.io.IOException;
import java.util.Arrays;
import java.util.concurrent.CountDownLatch;
import java.util.concurrent.TimeUnit;
+import java.util.concurrent.atomic.AtomicInteger;
+import javax.annotation.CheckReturnValue;
+import org.junit.After;
+import org.junit.Test;
+import org.junit.runner.RunWith;
+import org.junit.runners.JUnit4;
/**
* Tests for DigestUtils.
@@ -45,6 +49,11 @@ import java.util.concurrent.TimeUnit;
@RunWith(JUnit4.class)
public class DigestUtilsTest {
+ @After
+ public void tearDown() {
+ DigestUtils.configureCache(0);
+ }
+
private static void assertDigestCalculationConcurrency(boolean expectConcurrent,
final boolean fastDigest, final int fileSize1, final int fileSize2,
HashFunction hf) throws Exception {
@@ -135,8 +144,7 @@ public class DigestUtilsTest {
}
}
- @Test
- public void testRecoverFromMalformedDigest() throws Exception {
+ public void assertRecoverFromMalformedDigest(HashFunction... hashFunctions) throws Exception {
final byte[] malformed = {0, 0, 0};
FileSystem myFS = new InMemoryFileSystem(BlazeClock.instance()) {
@Override
@@ -147,7 +155,7 @@ public class DigestUtilsTest {
};
Path path = myFS.getPath("/file");
FileSystemUtils.writeContentAsLatin1(path, "a");
- for (HashFunction hf : Arrays.asList(HashFunction.MD5, HashFunction.SHA1)) {
+ for (HashFunction hf : hashFunctions) {
FileSystem.setDigestFunctionForTesting(hf);
byte[] result = DigestUtils.getDigestOrFail(path, 1);
assertArrayEquals(path.getDigest(), result);
@@ -155,4 +163,124 @@ public class DigestUtilsTest {
assertTrue(path.isValidDigest(result));
}
}
+
+ @Test
+ public void testRecoverFromMalformedDigestWithoutCache() throws Exception {
+ try {
+ DigestUtils.getCacheStats();
+ fail("Digests cache should remain disabled until configureCache is called");
+ } catch (NullPointerException expected) {
+ }
+ assertRecoverFromMalformedDigest(HashFunction.MD5, HashFunction.SHA1);
+ try {
+ DigestUtils.getCacheStats();
+ fail("Digests cache was unexpectedly enabled through the test");
+ } catch (NullPointerException expected) {
+ }
+ }
+
+ @Test
+ public void testRecoverFromMalformedDigestWithCache() throws Exception {
+ DigestUtils.configureCache(10);
+ assertNotNull(DigestUtils.getCacheStats()); // Ensure the cache is enabled.
+
+ // When using the cache, we cannot run our test using different hash functions because the
+ // hash function is not part of the cache key. This is intentional: the hash function is
+ // essentially final and can only be changed for tests. Therefore, just test the same hash
+ // function twice to further exercise the cache code.
+ assertRecoverFromMalformedDigest(HashFunction.MD5, HashFunction.MD5);
+
+ assertNotNull(DigestUtils.getCacheStats()); // Ensure the cache remains enabled.
+ }
+
+ /** Helper class to assert the cache statistics. */
+ private static class CacheStatsChecker {
+ /** Cache statistics, grabbed at construction time. */
+ private final CacheStats stats;
+
+ private int expectedEvictionCount;
+ private int expectedHitCount;
+ private int expectedMissCount;
+
+ CacheStatsChecker() {
+ this.stats = DigestUtils.getCacheStats();
+ }
+
+ @CheckReturnValue
+ CacheStatsChecker evictionCount(int count) {
+ expectedEvictionCount = count;
+ return this;
+ }
+
+ @CheckReturnValue
+ CacheStatsChecker hitCount(int count) {
+ expectedHitCount = count;
+ return this;
+ }
+
+ @CheckReturnValue
+ CacheStatsChecker missCount(int count) {
+ expectedMissCount = count;
+ return this;
+ }
+
+ void check() throws Exception {
+ assertEquals(expectedEvictionCount, stats.evictionCount());
+ assertEquals(expectedHitCount, stats.hitCount());
+ assertEquals(expectedMissCount, stats.missCount());
+ }
+ }
+
+ @Test
+ public void testCache() throws Exception {
+ final AtomicInteger getFastDigestCounter = new AtomicInteger(0);
+ final AtomicInteger getDigestCounter = new AtomicInteger(0);
+
+ FileSystem tracingFileSystem =
+ new InMemoryFileSystem(BlazeClock.instance()) {
+ @Override
+ protected byte[] getFastDigest(Path path, HashFunction hashFunction) throws IOException {
+ getFastDigestCounter.incrementAndGet();
+ return null;
+ }
+
+ @Override
+ protected byte[] getDigest(Path path) throws IOException {
+ getDigestCounter.incrementAndGet();
+ return super.getDigest(path);
+ }
+ };
+
+ DigestUtils.configureCache(2);
+
+ final Path file1 = tracingFileSystem.getPath("/1.txt");
+ final Path file2 = tracingFileSystem.getPath("/2.txt");
+ final Path file3 = tracingFileSystem.getPath("/3.txt");
+ FileSystemUtils.writeContentAsLatin1(file1, "some contents");
+ FileSystemUtils.writeContentAsLatin1(file2, "some other contents");
+ FileSystemUtils.writeContentAsLatin1(file3, "and something else");
+
+ byte[] digest1 = DigestUtils.getDigestOrFail(file1, file1.getFileSize());
+ assertEquals(1, getFastDigestCounter.get());
+ assertEquals(1, getDigestCounter.get());
+ new CacheStatsChecker().evictionCount(0).hitCount(0).missCount(1).check();
+
+ byte[] digest2 = DigestUtils.getDigestOrFail(file1, file1.getFileSize());
+ assertEquals(2, getFastDigestCounter.get());
+ assertEquals(1, getDigestCounter.get());
+ new CacheStatsChecker().evictionCount(0).hitCount(1).missCount(1).check();
+
+ assertArrayEquals(digest1, digest2);
+
+ // Evict the digest for the previous file.
+ DigestUtils.getDigestOrFail(file2, file2.getFileSize());
+ DigestUtils.getDigestOrFail(file3, file3.getFileSize());
+ new CacheStatsChecker().evictionCount(1).hitCount(1).missCount(3).check();
+
+ // And now try to recompute it.
+ byte[] digest3 = DigestUtils.getDigestOrFail(file1, file1.getFileSize());
+ new CacheStatsChecker().evictionCount(2).hitCount(1).missCount(4).check();
+
+ assertArrayEquals(digest1, digest3);
+ }
}