diff options
author | Anonymous <no-reply@google.com> | 2017-08-08 12:42:44 -0700 |
---|---|---|
committer | Jeff Davidson <jpd@google.com> | 2017-08-08 12:51:12 -0700 |
commit | cc4c86984e7c6d0de90ea4362506fc5ca857bc68 (patch) | |
tree | 840682988d908a367eadc4d2231bde1e43134333 /src/test/java/com/android/volley/toolbox | |
parent | 3a1232594eb627e9e59b22406816648a55a3f2ae (diff) | |
download | volley-cc4c86984e7c6d0de90ea4362506fc5ca857bc68.tar.gz |
Import of Volley from GitHub to AOSP. - 27f4207774f7a0b59168f5b3b53d8c0c3841de64 Merge pull request #71 from google/fix-visibility by Jeff Davidson <jpd236@cornell.edu>android-o-iot-preview-5o-iot-preview-5
- e1d75bc49fff2f43b4304688f22ccb57f0c2782c Merge pull request #66 from joebowbeer/dbc_overflow by Jeff Davidson <jpd236@cornell.edu>
- d40b75f52f0f8e3467d63294142610a040ff8cd5 Merge pull request #64 from xyhuangjinfu/master by Jeff Davidson <jpd236@cornell.edu>
- aafd2e5d1279d68cd2c39b02a915e501974daf2f Merge pull request #52 from joebowbeer/dbc by Jeff Davidson <jpd236@cornell.edu>
- baefc4e4f7ad42ce84972716486928ab77b413fc Merge pull request #23 from google/javadoc-fixes by Jeff Davidson <jpd236@cornell.edu>
- dea97629925acea8942c06a7ff52938eba5dc78c Merge pull request #39 from google/publish by Jeff Davidson <jpd236@cornell.edu>
- f52e1e0d4a18c8e3e7004a3d60c8cbec9a04f3d4 Merge pull request #38 from SamuelYvon/master by Jeff Davidson <jpd236@cornell.edu>
- f0ab9a527d3a525325139026a7da0318b506abb4 Merge pull request #41 from LappleApple/patch-1 by Jeff Davidson <jpd236@cornell.edu>
- a444f81d099179ab7c5fa5f460262a206125cd37 Merge pull request #18 from Ericliu001/master by Jeff Davidson <jpd236@cornell.edu>
GitOrigin-RevId: 27f4207774f7a0b59168f5b3b53d8c0c3841de64
Change-Id: Idf47e2b8caff4c0d4a3f00a5d8c22878e36c59ba
Diffstat (limited to 'src/test/java/com/android/volley/toolbox')
-rw-r--r-- | src/test/java/com/android/volley/toolbox/DiskBasedCacheTest.java | 431 |
1 files changed, 391 insertions, 40 deletions
diff --git a/src/test/java/com/android/volley/toolbox/DiskBasedCacheTest.java b/src/test/java/com/android/volley/toolbox/DiskBasedCacheTest.java index 0a8be77..3d8d1f1 100644 --- a/src/test/java/com/android/volley/toolbox/DiskBasedCacheTest.java +++ b/src/test/java/com/android/volley/toolbox/DiskBasedCacheTest.java @@ -18,46 +18,371 @@ package com.android.volley.toolbox; import com.android.volley.Cache; import com.android.volley.toolbox.DiskBasedCache.CacheHeader; +import com.android.volley.toolbox.DiskBasedCache.CountingInputStream; + +import org.junit.After; +import org.junit.Before; +import org.junit.Rule; import org.junit.Test; +import org.junit.rules.ExpectedException; +import org.junit.rules.TemporaryFolder; +import org.junit.runner.RunWith; +import org.robolectric.RobolectricTestRunner; +import org.robolectric.annotation.Config; import java.io.ByteArrayInputStream; import java.io.ByteArrayOutputStream; +import java.io.EOFException; import java.io.File; +import java.io.FileOutputStream; +import java.io.IOException; +import java.io.InputStream; +import java.io.OutputStream; import java.util.HashMap; import java.util.Map; +import java.util.Random; -import static org.junit.Assert.*; +import static org.hamcrest.Matchers.arrayWithSize; +import static org.hamcrest.Matchers.emptyArray; +import static org.hamcrest.Matchers.equalTo; +import static org.hamcrest.Matchers.is; +import static org.hamcrest.Matchers.nullValue; +import static org.junit.Assert.assertEquals; +import static org.junit.Assert.assertNotNull; +import static org.junit.Assert.assertThat; +import static org.mockito.ArgumentMatchers.any; +import static org.mockito.ArgumentMatchers.anyInt; +import static org.mockito.Mockito.atLeastOnce; +import static org.mockito.Mockito.doReturn; +import static org.mockito.Mockito.doThrow; +import static org.mockito.Mockito.spy; +import static org.mockito.Mockito.verify; +@RunWith(RobolectricTestRunner.class) +@Config(manifest="src/main/AndroidManifest.xml", sdk=16) public class DiskBasedCacheTest { - // Simple end-to-end serialize/deserialize test. - @Test public void cacheHeaderSerialization() throws Exception { - Cache.Entry e = new Cache.Entry(); - e.data = new byte[8]; - e.serverDate = 1234567L; - e.lastModified = 13572468L; - e.ttl = 9876543L; - e.softTtl = 8765432L; - e.etag = "etag"; - e.responseHeaders = new HashMap<String, String>(); - e.responseHeaders.put("fruit", "banana"); - - CacheHeader first = new CacheHeader("my-magical-key", e); - ByteArrayOutputStream baos = new ByteArrayOutputStream(); - first.writeHeader(baos); - ByteArrayInputStream bais = new ByteArrayInputStream(baos.toByteArray()); - CacheHeader second = CacheHeader.readHeader(bais); + private static final int MAX_SIZE = 1024 * 1024; + + private Cache cache; + + @Rule + public TemporaryFolder temporaryFolder = new TemporaryFolder(); + + @Rule + public ExpectedException exception = ExpectedException.none(); + + @Before + public void setup() throws IOException { + // Initialize empty cache + cache = new DiskBasedCache(temporaryFolder.getRoot(), MAX_SIZE); + cache.initialize(); + } + + @After + public void teardown() { + cache = null; + } + + @Test + public void testEmptyInitialize() { + assertThat(cache.get("key"), is(nullValue())); + } + + @Test + public void testPutGetZeroBytes() { + Cache.Entry entry = new Cache.Entry(); + entry.data = new byte[0]; + entry.serverDate = 1234567L; + entry.lastModified = 13572468L; + entry.ttl = 9876543L; + entry.softTtl = 8765432L; + entry.etag = "etag"; + entry.responseHeaders = new HashMap<>(); + entry.responseHeaders.put("fruit", "banana"); + entry.responseHeaders.put("color", "yellow"); + cache.put("my-magical-key", entry); + + assertThatEntriesAreEqual(cache.get("my-magical-key"), entry); + assertThat(cache.get("unknown-key"), is(nullValue())); + } + + @Test + public void testPutRemoveGet() { + Cache.Entry entry = randomData(511); + cache.put("key", entry); + + assertThatEntriesAreEqual(cache.get("key"), entry); + + cache.remove("key"); + assertThat(cache.get("key"), is(nullValue())); + assertThat(listCachedFiles(), is(emptyArray())); + } + + @Test + public void testPutClearGet() { + Cache.Entry entry = randomData(511); + cache.put("key", entry); + + assertThatEntriesAreEqual(cache.get("key"), entry); + + cache.clear(); + assertThat(cache.get("key"), is(nullValue())); + assertThat(listCachedFiles(), is(emptyArray())); + } + + @Test + public void testReinitialize() { + Cache.Entry entry = randomData(1023); + cache.put("key", entry); + + Cache copy = new DiskBasedCache(temporaryFolder.getRoot(), MAX_SIZE); + copy.initialize(); + + assertThatEntriesAreEqual(copy.get("key"), entry); + } + + @Test + public void testInvalidate() { + Cache.Entry entry = randomData(32); + entry.softTtl = 8765432L; + entry.ttl = 9876543L; + cache.put("key", entry); + + cache.invalidate("key", false); + entry.softTtl = 0; // expired + assertThatEntriesAreEqual(cache.get("key"), entry); + } + + @Test + public void testInvalidateFullExpire() { + Cache.Entry entry = randomData(32); + entry.softTtl = 8765432L; + entry.ttl = 9876543L; + cache.put("key", entry); + + cache.invalidate("key", true); + entry.softTtl = 0; // expired + entry.ttl = 0; // expired + assertThatEntriesAreEqual(cache.get("key"), entry); + } + + @Test + public void testTrim() { + Cache.Entry entry = randomData(2 * MAX_SIZE); + cache.put("oversize", entry); + + assertThatEntriesAreEqual(cache.get("oversize"), entry); + + entry = randomData(1024); + cache.put("kilobyte", entry); + + assertThat(cache.get("oversize"), is(nullValue())); + assertThatEntriesAreEqual(cache.get("kilobyte"), entry); + + Cache.Entry entry2 = randomData(1024); + cache.put("kilobyte2", entry2); + Cache.Entry entry3 = randomData(1024); + cache.put("kilobyte3", entry3); + + assertThatEntriesAreEqual(cache.get("kilobyte"), entry); + assertThatEntriesAreEqual(cache.get("kilobyte2"), entry2); + assertThatEntriesAreEqual(cache.get("kilobyte3"), entry3); + + entry = randomData(MAX_SIZE); + cache.put("max", entry); + + assertThat(cache.get("kilobyte"), is(nullValue())); + assertThat(cache.get("kilobyte2"), is(nullValue())); + assertThat(cache.get("kilobyte3"), is(nullValue())); + assertThatEntriesAreEqual(cache.get("max"), entry); + } + + @Test + @SuppressWarnings("TryFinallyCanBeTryWithResources") + public void testGetBadMagic() throws IOException { + // Cache something + Cache.Entry entry = randomData(1023); + cache.put("key", entry); + assertThatEntriesAreEqual(cache.get("key"), entry); + + // Overwrite the magic header + File cacheFolder = temporaryFolder.getRoot(); + File file = cacheFolder.listFiles()[0]; + FileOutputStream fos = new FileOutputStream(file); + try { + DiskBasedCache.writeInt(fos, 0); // overwrite magic + } finally { + //noinspection ThrowFromFinallyBlock + fos.close(); + } + + assertThat(cache.get("key"), is(nullValue())); + assertThat(listCachedFiles(), is(emptyArray())); + } + + @Test + @SuppressWarnings("TryFinallyCanBeTryWithResources") + public void testGetWrongKey() throws IOException { + // Cache something + Cache.Entry entry = randomData(1023); + cache.put("key", entry); + assertThatEntriesAreEqual(cache.get("key"), entry); + + // Access the cached file + File cacheFolder = temporaryFolder.getRoot(); + File file = cacheFolder.listFiles()[0]; + FileOutputStream fos = new FileOutputStream(file); + try { + // Overwrite with a different key + CacheHeader wrongHeader = new CacheHeader("bad", entry); + wrongHeader.writeHeader(fos); + } finally { + //noinspection ThrowFromFinallyBlock + fos.close(); + } + + // key is gone, but file is still there + assertThat(cache.get("key"), is(nullValue())); + assertThat(listCachedFiles(), is(arrayWithSize(1))); + + // Note: file is now a zombie because its key does not map to its name + } + + @Test + public void testStreamToBytesNegativeLength() throws IOException { + byte[] data = new byte[1]; + CountingInputStream cis = + new CountingInputStream(new ByteArrayInputStream(data), data.length); + exception.expect(IOException.class); + DiskBasedCache.streamToBytes(cis, -1); + } - assertEquals(first.key, second.key); - assertEquals(first.serverDate, second.serverDate); - assertEquals(first.lastModified, second.lastModified); - assertEquals(first.ttl, second.ttl); - assertEquals(first.softTtl, second.softTtl); - assertEquals(first.etag, second.etag); - assertEquals(first.responseHeaders, second.responseHeaders); + @Test + public void testStreamToBytesExcessiveLength() throws IOException { + byte[] data = new byte[1]; + CountingInputStream cis = + new CountingInputStream(new ByteArrayInputStream(data), data.length); + exception.expect(IOException.class); + DiskBasedCache.streamToBytes(cis, 2); + } + + @Test + public void testStreamToBytesOverflow() throws IOException { + byte[] data = new byte[0]; + CountingInputStream cis = + new CountingInputStream(new ByteArrayInputStream(data), 0x100000000L); + exception.expect(IOException.class); + DiskBasedCache.streamToBytes(cis, 0x100000000L); // int value is 0 } - @Test public void serializeInt() throws Exception { + @Test + public void testFileIsDeletedWhenWriteHeaderFails() throws IOException { + // Create DataOutputStream that throws IOException + OutputStream mockedOutputStream = spy(OutputStream.class); + doThrow(IOException.class).when(mockedOutputStream).write(anyInt()); + + // Create read-only copy that fails to write anything + DiskBasedCache readonly = spy((DiskBasedCache) cache); + doReturn(mockedOutputStream).when(readonly).createOutputStream(any(File.class)); + + // Attempt to write + readonly.put("key", randomData(1111)); + + // write is called at least once because each linked stream flushes when closed + verify(mockedOutputStream, atLeastOnce()).write(anyInt()); + assertThat(readonly.get("key"), is(nullValue())); + assertThat(listCachedFiles(), is(emptyArray())); + + // Note: original cache will try (without success) to read from file + assertThat(cache.get("key"), is(nullValue())); + } + + @Test + public void testIOExceptionInInitialize() throws IOException { + // Cache a few kilobytes + cache.put("kilobyte", randomData(1024)); + cache.put("kilobyte2", randomData(1024)); + cache.put("kilobyte3", randomData(1024)); + + // Create DataInputStream that throws IOException + InputStream mockedInputStream = spy(InputStream.class); + //noinspection ResultOfMethodCallIgnored + doThrow(IOException.class).when(mockedInputStream).read(); + + // Create broken cache that fails to read anything + DiskBasedCache broken = + spy(new DiskBasedCache(temporaryFolder.getRoot())); + doReturn(mockedInputStream).when(broken).createInputStream(any(File.class)); + + // Attempt to initialize + broken.initialize(); + + // Everything is gone + assertThat(broken.get("kilobyte"), is(nullValue())); + assertThat(broken.get("kilobyte2"), is(nullValue())); + assertThat(broken.get("kilobyte3"), is(nullValue())); + assertThat(listCachedFiles(), is(emptyArray())); + + // Verify that original cache can cope with missing files + assertThat(cache.get("kilobyte"), is(nullValue())); + assertThat(cache.get("kilobyte2"), is(nullValue())); + assertThat(cache.get("kilobyte3"), is(nullValue())); + } + + @Test + public void testManyResponseHeaders() { + Cache.Entry entry = new Cache.Entry(); + entry.data = new byte[0]; + entry.responseHeaders = new HashMap<>(); + for (int i = 0; i < 0xFFFF; i++) { + entry.responseHeaders.put(Integer.toString(i), ""); + } + cache.put("key", entry); + } + + @Test + @SuppressWarnings("TryFinallyCanBeTryWithResources") + public void testCountingInputStreamByteCount() throws IOException { + // Write some bytes + ByteArrayOutputStream out = new ByteArrayOutputStream(); + //noinspection ThrowFromFinallyBlock + try { + DiskBasedCache.writeInt(out, 1); + DiskBasedCache.writeLong(out, -1L); + DiskBasedCache.writeString(out, "hamburger"); + } finally { + //noinspection ThrowFromFinallyBlock + out.close(); + } + long bytesWritten = out.size(); + + // Read the bytes and compare the counts + CountingInputStream cis = + new CountingInputStream(new ByteArrayInputStream(out.toByteArray()), bytesWritten); + try { + assertThat(cis.bytesRemaining(), is(bytesWritten)); + assertThat(cis.bytesRead(), is(0L)); + assertThat(DiskBasedCache.readInt(cis), is(1)); + assertThat(DiskBasedCache.readLong(cis), is(-1L)); + assertThat(DiskBasedCache.readString(cis), is("hamburger")); + assertThat(cis.bytesRead(), is(bytesWritten)); + assertThat(cis.bytesRemaining(), is(0L)); + } finally { + //noinspection ThrowFromFinallyBlock + cis.close(); + } + } + + /* Serialization tests */ + + @Test public void testEmptyReadThrowsEOF() throws IOException { + ByteArrayInputStream empty = new ByteArrayInputStream(new byte[]{}); + exception.expect(EOFException.class); + DiskBasedCache.readInt(empty); + } + + @Test public void serializeInt() throws IOException { ByteArrayOutputStream baos = new ByteArrayOutputStream(); DiskBasedCache.writeInt(baos, 0); DiskBasedCache.writeInt(baos, 19791214); @@ -96,33 +421,35 @@ public class DiskBasedCacheTest { DiskBasedCache.writeString(baos, ""); DiskBasedCache.writeString(baos, "This is a string."); DiskBasedCache.writeString(baos, "ファイカス"); - ByteArrayInputStream bais = new ByteArrayInputStream(baos.toByteArray()); - assertEquals(DiskBasedCache.readString(bais), ""); - assertEquals(DiskBasedCache.readString(bais), "This is a string."); - assertEquals(DiskBasedCache.readString(bais), "ファイカス"); + CountingInputStream cis = + new CountingInputStream(new ByteArrayInputStream(baos.toByteArray()), baos.size()); + assertEquals(DiskBasedCache.readString(cis), ""); + assertEquals(DiskBasedCache.readString(cis), "This is a string."); + assertEquals(DiskBasedCache.readString(cis), "ファイカス"); } @Test public void serializeMap() throws Exception { ByteArrayOutputStream baos = new ByteArrayOutputStream(); - Map<String, String> empty = new HashMap<String, String>(); + Map<String, String> empty = new HashMap<>(); DiskBasedCache.writeStringStringMap(empty, baos); DiskBasedCache.writeStringStringMap(null, baos); - Map<String, String> twoThings = new HashMap<String, String>(); + Map<String, String> twoThings = new HashMap<>(); twoThings.put("first", "thing"); twoThings.put("second", "item"); DiskBasedCache.writeStringStringMap(twoThings, baos); - Map<String, String> emptyKey = new HashMap<String, String>(); + Map<String, String> emptyKey = new HashMap<>(); emptyKey.put("", "value"); DiskBasedCache.writeStringStringMap(emptyKey, baos); - Map<String, String> emptyValue = new HashMap<String, String>(); + Map<String, String> emptyValue = new HashMap<>(); emptyValue.put("key", ""); DiskBasedCache.writeStringStringMap(emptyValue, baos); - ByteArrayInputStream bais = new ByteArrayInputStream(baos.toByteArray()); - assertEquals(DiskBasedCache.readStringStringMap(bais), empty); - assertEquals(DiskBasedCache.readStringStringMap(bais), empty); // null reads back empty - assertEquals(DiskBasedCache.readStringStringMap(bais), twoThings); - assertEquals(DiskBasedCache.readStringStringMap(bais), emptyKey); - assertEquals(DiskBasedCache.readStringStringMap(bais), emptyValue); + CountingInputStream cis = + new CountingInputStream(new ByteArrayInputStream(baos.toByteArray()), baos.size()); + assertEquals(DiskBasedCache.readStringStringMap(cis), empty); + assertEquals(DiskBasedCache.readStringStringMap(cis), empty); // null reads back empty + assertEquals(DiskBasedCache.readStringStringMap(cis), twoThings); + assertEquals(DiskBasedCache.readStringStringMap(cis), emptyKey); + assertEquals(DiskBasedCache.readStringStringMap(cis), emptyValue); } @Test @@ -133,4 +460,28 @@ public class DiskBasedCacheTest { assertNotNull(DiskBasedCache.class.getMethod("getFileForKey", String.class)); } + + /* Test helpers */ + + private void assertThatEntriesAreEqual(Cache.Entry actual, Cache.Entry expected) { + assertThat(actual.data, is(equalTo(expected.data))); + assertThat(actual.etag, is(equalTo(expected.etag))); + assertThat(actual.lastModified, is(equalTo(expected.lastModified))); + assertThat(actual.responseHeaders, is(equalTo(expected.responseHeaders))); + assertThat(actual.serverDate, is(equalTo(expected.serverDate))); + assertThat(actual.softTtl, is(equalTo(expected.softTtl))); + assertThat(actual.ttl, is(equalTo(expected.ttl))); + } + + private Cache.Entry randomData(int length) { + Cache.Entry entry = new Cache.Entry(); + byte[] data = new byte[length]; + new Random(42).nextBytes(data); // explicit seed for reproducible results + entry.data = data; + return entry; + } + + private File[] listCachedFiles() { + return temporaryFolder.getRoot().listFiles(); + } } |