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.java145
1 files changed, 145 insertions, 0 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
new file mode 100644
index 0000000000..2ed24a6376
--- /dev/null
+++ b/src/test/java/com/google/devtools/build/lib/actions/DigestUtilsTest.java
@@ -0,0 +1,145 @@
+// Copyright 2015 Google Inc. All rights reserved.
+//
+// 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.google.devtools.build.lib.actions;
+
+
+import static org.junit.Assert.assertArrayEquals;
+import static org.junit.Assert.assertEquals;
+import static org.junit.Assert.assertNotSame;
+import static org.junit.Assert.assertTrue;
+
+import com.google.common.base.Strings;
+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;
+import com.google.devtools.build.lib.util.BlazeClock;
+import com.google.devtools.build.lib.vfs.FileSystem;
+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.concurrent.CountDownLatch;
+import java.util.concurrent.TimeUnit;
+
+/**
+ * Tests for DigestUtils.
+ */
+@RunWith(JUnit4.class)
+public class DigestUtilsTest {
+
+ private static void assertMd5CalculationConcurrency(boolean expectConcurrent,
+ final boolean fastDigest, final int fileSize1, final int fileSize2) throws Exception {
+ final CountDownLatch barrierLatch = new CountDownLatch(2); // Used to block test threads.
+ final CountDownLatch readyLatch = new CountDownLatch(1); // Used to block main thread.
+
+ FileSystem myfs = new InMemoryFileSystem(BlazeClock.instance()) {
+ @Override
+ protected byte[] getMD5Digest(Path path) throws IOException {
+ try {
+ barrierLatch.countDown();
+ readyLatch.countDown();
+ // Either both threads will be inside getMD5Digest at the same time or they
+ // both will be blocked.
+ barrierLatch.await();
+ } catch (Exception e) {
+ throw new IOException(e);
+ }
+ return super.getMD5Digest(path);
+ }
+
+ @Override
+ protected String getFastDigestFunctionType(Path path) {
+ return "MD5";
+ }
+
+ @Override
+ protected byte[] getFastDigest(Path path) throws IOException {
+ return fastDigest ? super.getMD5Digest(path) : null;
+ }
+ };
+
+ final Path myFile1 = myfs.getPath("/f1.dat");
+ final Path myFile2 = myfs.getPath("/f2.dat");
+ FileSystemUtils.writeContentAsLatin1(myFile1, Strings.repeat("a", fileSize1));
+ FileSystemUtils.writeContentAsLatin1(myFile2, Strings.repeat("b", fileSize2));
+
+ TestThread thread1 = new TestThread () {
+ @Override public void runTest() throws Exception {
+ DigestUtils.getDigestOrFail(myFile1, fileSize1);
+ }
+ };
+
+ TestThread thread2 = new TestThread () {
+ @Override public void runTest() throws Exception {
+ DigestUtils.getDigestOrFail(myFile2, fileSize2);
+ }
+ };
+
+ thread1.start();
+ thread2.start();
+ if (!expectConcurrent) { // Synchronized case.
+ // Wait until at least one thread reached getMD5Digest().
+ assertTrue(readyLatch.await(TestUtils.WAIT_TIMEOUT_SECONDS, TimeUnit.SECONDS));
+ // Only 1 thread should be inside getMD5Digest().
+ assertEquals(1, barrierLatch.getCount());
+ barrierLatch.countDown(); // Release barrier latch, allowing both threads to proceed.
+ }
+ // Test successful execution within 5 seconds.
+ thread1.joinAndAssertState(TestUtils.WAIT_TIMEOUT_MILLISECONDS);
+ thread2.joinAndAssertState(TestUtils.WAIT_TIMEOUT_MILLISECONDS);
+ }
+
+ /**
+ * Ensures that MD5 calculation is synchronized for files
+ * greater than 4096 bytes if MD5 is not available cheaply,
+ * so machines with rotating drives don't become unusable.
+ */
+ @Test
+ public void testMd5CalculationConcurrency() throws Exception {
+ assertMd5CalculationConcurrency(true, true, 4096, 4096);
+ assertMd5CalculationConcurrency(true, true, 4097, 4097);
+ assertMd5CalculationConcurrency(true, false, 4096, 4096);
+ assertMd5CalculationConcurrency(false, false, 4097, 4097);
+ assertMd5CalculationConcurrency(true, false, 1024, 4097);
+ assertMd5CalculationConcurrency(true, false, 1024, 1024);
+ }
+
+ @Test
+ public void testRecoverFromMalformedDigest() throws Exception {
+ final byte[] malformed = {0, 0, 0};
+ FileSystem myFS = new InMemoryFileSystem(BlazeClock.instance()) {
+ @Override
+ protected String getFastDigestFunctionType(Path path) {
+ return "MD5";
+ }
+
+ @Override
+ protected byte[] getFastDigest(Path path) throws IOException {
+ // MD5 digests are supposed to be 16 bytes.
+ return malformed;
+ }
+ };
+ Path path = myFS.getPath("/file");
+ FileSystemUtils.writeContentAsLatin1(path, "a");
+ byte[] result = DigestUtils.getDigestOrFail(path, 1);
+ assertArrayEquals(path.getMD5Digest(), result);
+ assertNotSame(malformed, result);
+ assertEquals(16, result.length);
+ }
+}