diff options
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.java | 145 |
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); + } +} |