| /* |
| * Copyright (C) 2011 The Android Open Source Project |
| * |
| * 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 libcore.io; |
| |
| import java.io.BufferedReader; |
| import java.io.File; |
| import java.io.FileReader; |
| import java.io.FileWriter; |
| import java.io.IOException; |
| import java.io.InputStream; |
| import java.io.OutputStream; |
| import java.io.Reader; |
| import java.io.StringWriter; |
| import java.io.Writer; |
| import java.util.ArrayList; |
| import java.util.Arrays; |
| import java.util.List; |
| import junit.framework.TestCase; |
| import static libcore.io.DiskLruCache.JOURNAL_FILE; |
| import static libcore.io.DiskLruCache.MAGIC; |
| import static libcore.io.DiskLruCache.VERSION_1; |
| import tests.io.MockOs; |
| |
| public final class DiskLruCacheTest extends TestCase { |
| private final int appVersion = 100; |
| private String javaTmpDir; |
| private File cacheDir; |
| private File journalFile; |
| private DiskLruCache cache; |
| private final MockOs mockOs = new MockOs(); |
| |
| @Override public void setUp() throws Exception { |
| super.setUp(); |
| javaTmpDir = System.getProperty("java.io.tmpdir"); |
| cacheDir = new File(javaTmpDir, "DiskLruCacheTest"); |
| cacheDir.mkdir(); |
| journalFile = new File(cacheDir, JOURNAL_FILE); |
| for (File file : cacheDir.listFiles()) { |
| file.delete(); |
| } |
| cache = DiskLruCache.open(cacheDir, appVersion, 2, Integer.MAX_VALUE); |
| mockOs.install(); |
| } |
| |
| @Override protected void tearDown() throws Exception { |
| mockOs.uninstall(); |
| cache.close(); |
| super.tearDown(); |
| } |
| |
| public void testEmptyCache() throws Exception { |
| cache.close(); |
| assertJournalEquals(); |
| } |
| |
| public void testWriteAndReadEntry() throws Exception { |
| DiskLruCache.Editor creator = cache.edit("k1"); |
| creator.set(0, "ABC"); |
| creator.set(1, "DE"); |
| assertNull(creator.getString(0)); |
| assertNull(creator.newInputStream(0)); |
| assertNull(creator.getString(1)); |
| assertNull(creator.newInputStream(1)); |
| creator.commit(); |
| |
| DiskLruCache.Snapshot snapshot = cache.get("k1"); |
| assertEquals("ABC", snapshot.getString(0)); |
| assertEquals("DE", snapshot.getString(1)); |
| } |
| |
| public void testReadAndWriteEntryAcrossCacheOpenAndClose() throws Exception { |
| DiskLruCache.Editor creator = cache.edit("k1"); |
| creator.set(0, "A"); |
| creator.set(1, "B"); |
| creator.commit(); |
| cache.close(); |
| |
| cache = DiskLruCache.open(cacheDir, appVersion, 2, Integer.MAX_VALUE); |
| DiskLruCache.Snapshot snapshot = cache.get("k1"); |
| assertEquals("A", snapshot.getString(0)); |
| assertEquals("B", snapshot.getString(1)); |
| snapshot.close(); |
| } |
| |
| public void testJournalWithEditAndPublish() throws Exception { |
| DiskLruCache.Editor creator = cache.edit("k1"); |
| assertJournalEquals("DIRTY k1"); // DIRTY must always be flushed |
| creator.set(0, "AB"); |
| creator.set(1, "C"); |
| creator.commit(); |
| cache.close(); |
| assertJournalEquals("DIRTY k1", "CLEAN k1 2 1"); |
| } |
| |
| public void testRevertedNewFileIsRemoveInJournal() throws Exception { |
| DiskLruCache.Editor creator = cache.edit("k1"); |
| assertJournalEquals("DIRTY k1"); // DIRTY must always be flushed |
| creator.set(0, "AB"); |
| creator.set(1, "C"); |
| creator.abort(); |
| cache.close(); |
| assertJournalEquals("DIRTY k1", "REMOVE k1"); |
| } |
| |
| public void testUnterminatedEditIsRevertedOnClose() throws Exception { |
| cache.edit("k1"); |
| cache.close(); |
| assertJournalEquals("DIRTY k1", "REMOVE k1"); |
| } |
| |
| public void testJournalDoesNotIncludeReadOfYetUnpublishedValue() throws Exception { |
| DiskLruCache.Editor creator = cache.edit("k1"); |
| assertNull(cache.get("k1")); |
| creator.set(0, "A"); |
| creator.set(1, "BC"); |
| creator.commit(); |
| cache.close(); |
| assertJournalEquals("DIRTY k1", "CLEAN k1 1 2"); |
| } |
| |
| public void testJournalWithEditAndPublishAndRead() throws Exception { |
| DiskLruCache.Editor k1Creator = cache.edit("k1"); |
| k1Creator.set(0, "AB"); |
| k1Creator.set(1, "C"); |
| k1Creator.commit(); |
| DiskLruCache.Editor k2Creator = cache.edit("k2"); |
| k2Creator.set(0, "DEF"); |
| k2Creator.set(1, "G"); |
| k2Creator.commit(); |
| DiskLruCache.Snapshot k1Snapshot = cache.get("k1"); |
| k1Snapshot.close(); |
| cache.close(); |
| assertJournalEquals("DIRTY k1", "CLEAN k1 2 1", |
| "DIRTY k2", "CLEAN k2 3 1", |
| "READ k1"); |
| } |
| |
| public void testCannotOperateOnEditAfterPublish() throws Exception { |
| DiskLruCache.Editor editor = cache.edit("k1"); |
| editor.set(0, "A"); |
| editor.set(1, "B"); |
| editor.commit(); |
| assertInoperable(editor); |
| } |
| |
| public void testCannotOperateOnEditAfterRevert() throws Exception { |
| DiskLruCache.Editor editor = cache.edit("k1"); |
| editor.set(0, "A"); |
| editor.set(1, "B"); |
| editor.abort(); |
| assertInoperable(editor); |
| } |
| |
| public void testExplicitRemoveAppliedToDiskImmediately() throws Exception { |
| DiskLruCache.Editor editor = cache.edit("k1"); |
| editor.set(0, "ABC"); |
| editor.set(1, "B"); |
| editor.commit(); |
| File k1 = getCleanFile("k1", 0); |
| assertEquals("ABC", readFile(k1)); |
| cache.remove("k1"); |
| assertFalse(k1.exists()); |
| } |
| |
| /** |
| * Each read sees a snapshot of the file at the time read was called. |
| * This means that two reads of the same key can see different data. |
| */ |
| public void testReadAndWriteOverlapsMaintainConsistency() throws Exception { |
| DiskLruCache.Editor v1Creator = cache.edit("k1"); |
| v1Creator.set(0, "AAaa"); |
| v1Creator.set(1, "BBbb"); |
| v1Creator.commit(); |
| |
| DiskLruCache.Snapshot snapshot1 = cache.get("k1"); |
| InputStream inV1 = snapshot1.getInputStream(0); |
| assertEquals('A', inV1.read()); |
| assertEquals('A', inV1.read()); |
| |
| DiskLruCache.Editor v1Updater = cache.edit("k1"); |
| v1Updater.set(0, "CCcc"); |
| v1Updater.set(1, "DDdd"); |
| v1Updater.commit(); |
| |
| DiskLruCache.Snapshot snapshot2 = cache.get("k1"); |
| assertEquals("CCcc", snapshot2.getString(0)); |
| assertEquals("DDdd", snapshot2.getString(1)); |
| snapshot2.close(); |
| |
| assertEquals('a', inV1.read()); |
| assertEquals('a', inV1.read()); |
| assertEquals("BBbb", snapshot1.getString(1)); |
| snapshot1.close(); |
| } |
| |
| public void testOpenWithDirtyKeyDeletesAllFilesForThatKey() throws Exception { |
| cache.close(); |
| File cleanFile0 = getCleanFile("k1", 0); |
| File cleanFile1 = getCleanFile("k1", 1); |
| File dirtyFile0 = getDirtyFile("k1", 0); |
| File dirtyFile1 = getDirtyFile("k1", 1); |
| writeFile(cleanFile0, "A"); |
| writeFile(cleanFile1, "B"); |
| writeFile(dirtyFile0, "C"); |
| writeFile(dirtyFile1, "D"); |
| createJournal("CLEAN k1 1 1", "DIRTY k1"); |
| cache = DiskLruCache.open(cacheDir, appVersion, 2, Integer.MAX_VALUE); |
| assertFalse(cleanFile0.exists()); |
| assertFalse(cleanFile1.exists()); |
| assertFalse(dirtyFile0.exists()); |
| assertFalse(dirtyFile1.exists()); |
| assertNull(cache.get("k1")); |
| } |
| |
| public void testOpenWithInvalidVersionClearsDirectory() throws Exception { |
| cache.close(); |
| generateSomeGarbageFiles(); |
| createJournalWithHeader(MAGIC, "0", "100", "2", ""); |
| cache = DiskLruCache.open(cacheDir, appVersion, 2, Integer.MAX_VALUE); |
| assertGarbageFilesAllDeleted(); |
| } |
| |
| public void testOpenWithInvalidAppVersionClearsDirectory() throws Exception { |
| cache.close(); |
| generateSomeGarbageFiles(); |
| createJournalWithHeader(MAGIC, "1", "101", "2", ""); |
| cache = DiskLruCache.open(cacheDir, appVersion, 2, Integer.MAX_VALUE); |
| assertGarbageFilesAllDeleted(); |
| } |
| |
| public void testOpenWithInvalidValueCountClearsDirectory() throws Exception { |
| cache.close(); |
| generateSomeGarbageFiles(); |
| createJournalWithHeader(MAGIC, "1", "100", "1", ""); |
| cache = DiskLruCache.open(cacheDir, appVersion, 2, Integer.MAX_VALUE); |
| assertGarbageFilesAllDeleted(); |
| } |
| |
| public void testOpenWithInvalidBlankLineClearsDirectory() throws Exception { |
| cache.close(); |
| generateSomeGarbageFiles(); |
| createJournalWithHeader(MAGIC, "1", "100", "2", "x"); |
| cache = DiskLruCache.open(cacheDir, appVersion, 2, Integer.MAX_VALUE); |
| assertGarbageFilesAllDeleted(); |
| } |
| |
| public void testOpenWithInvalidJournalLineClearsDirectory() throws Exception { |
| cache.close(); |
| generateSomeGarbageFiles(); |
| createJournal("CLEAN k1 1 1", "BOGUS"); |
| cache = DiskLruCache.open(cacheDir, appVersion, 2, Integer.MAX_VALUE); |
| assertGarbageFilesAllDeleted(); |
| assertNull(cache.get("k1")); |
| } |
| |
| public void testOpenWithInvalidFileSizeClearsDirectory() throws Exception { |
| cache.close(); |
| generateSomeGarbageFiles(); |
| createJournal("CLEAN k1 0000x001 1"); |
| cache = DiskLruCache.open(cacheDir, appVersion, 2, Integer.MAX_VALUE); |
| assertGarbageFilesAllDeleted(); |
| assertNull(cache.get("k1")); |
| } |
| |
| public void testOpenWithTruncatedLineDiscardsThatLine() throws Exception { |
| cache.close(); |
| writeFile(getCleanFile("k1", 0), "A"); |
| writeFile(getCleanFile("k1", 1), "B"); |
| Writer writer = new FileWriter(journalFile); |
| writer.write(MAGIC + "\n" + VERSION_1 + "\n100\n2\n\nCLEAN k1 1 1"); // no trailing newline |
| writer.close(); |
| cache = DiskLruCache.open(cacheDir, appVersion, 2, Integer.MAX_VALUE); |
| assertNull(cache.get("k1")); |
| } |
| |
| public void testOpenWithTooManyFileSizesClearsDirectory() throws Exception { |
| cache.close(); |
| generateSomeGarbageFiles(); |
| createJournal("CLEAN k1 1 1 1"); |
| cache = DiskLruCache.open(cacheDir, appVersion, 2, Integer.MAX_VALUE); |
| assertGarbageFilesAllDeleted(); |
| assertNull(cache.get("k1")); |
| } |
| |
| public void testKeyWithSpaceNotPermitted() throws Exception { |
| try { |
| cache.edit("my key"); |
| fail(); |
| } catch (IllegalArgumentException expected) { |
| } |
| } |
| |
| public void testKeyWithNewlineNotPermitted() throws Exception { |
| try { |
| cache.edit("my\nkey"); |
| fail(); |
| } catch (IllegalArgumentException expected) { |
| } |
| } |
| |
| public void testKeyWithCarriageReturnNotPermitted() throws Exception { |
| try { |
| cache.edit("my\rkey"); |
| fail(); |
| } catch (IllegalArgumentException expected) { |
| } |
| } |
| |
| public void testNullKeyThrows() throws Exception { |
| try { |
| cache.edit(null); |
| fail(); |
| } catch (NullPointerException expected) { |
| } |
| } |
| |
| public void testCreateNewEntryWithTooFewValuesFails() throws Exception { |
| DiskLruCache.Editor creator = cache.edit("k1"); |
| creator.set(1, "A"); |
| try { |
| creator.commit(); |
| fail(); |
| } catch (IllegalStateException expected) { |
| } |
| |
| assertFalse(getCleanFile("k1", 0).exists()); |
| assertFalse(getCleanFile("k1", 1).exists()); |
| assertFalse(getDirtyFile("k1", 0).exists()); |
| assertFalse(getDirtyFile("k1", 1).exists()); |
| assertNull(cache.get("k1")); |
| |
| DiskLruCache.Editor creator2 = cache.edit("k1"); |
| creator2.set(0, "B"); |
| creator2.set(1, "C"); |
| creator2.commit(); |
| } |
| |
| public void testCreateNewEntryWithMissingFileAborts() throws Exception { |
| DiskLruCache.Editor creator = cache.edit("k1"); |
| creator.set(0, "A"); |
| creator.set(1, "A"); |
| assertTrue(getDirtyFile("k1", 0).exists()); |
| assertTrue(getDirtyFile("k1", 1).exists()); |
| assertTrue(getDirtyFile("k1", 0).delete()); |
| assertFalse(getDirtyFile("k1", 0).exists()); |
| creator.commit(); // silently abort if file does not exist due to I/O issue |
| |
| assertFalse(getCleanFile("k1", 0).exists()); |
| assertFalse(getCleanFile("k1", 1).exists()); |
| assertFalse(getDirtyFile("k1", 0).exists()); |
| assertFalse(getDirtyFile("k1", 1).exists()); |
| assertNull(cache.get("k1")); |
| |
| DiskLruCache.Editor creator2 = cache.edit("k1"); |
| creator2.set(0, "B"); |
| creator2.set(1, "C"); |
| creator2.commit(); |
| } |
| |
| public void testRevertWithTooFewValues() throws Exception { |
| DiskLruCache.Editor creator = cache.edit("k1"); |
| creator.set(1, "A"); |
| creator.abort(); |
| assertFalse(getCleanFile("k1", 0).exists()); |
| assertFalse(getCleanFile("k1", 1).exists()); |
| assertFalse(getDirtyFile("k1", 0).exists()); |
| assertFalse(getDirtyFile("k1", 1).exists()); |
| assertNull(cache.get("k1")); |
| } |
| |
| public void testUpdateExistingEntryWithTooFewValuesReusesPreviousValues() throws Exception { |
| DiskLruCache.Editor creator = cache.edit("k1"); |
| creator.set(0, "A"); |
| creator.set(1, "B"); |
| creator.commit(); |
| |
| DiskLruCache.Editor updater = cache.edit("k1"); |
| updater.set(0, "C"); |
| updater.commit(); |
| |
| DiskLruCache.Snapshot snapshot = cache.get("k1"); |
| assertEquals("C", snapshot.getString(0)); |
| assertEquals("B", snapshot.getString(1)); |
| snapshot.close(); |
| } |
| |
| public void testEvictOnInsert() throws Exception { |
| cache.close(); |
| cache = DiskLruCache.open(cacheDir, appVersion, 2, 10); |
| |
| set("A", "a", "aaa"); // size 4 |
| set("B", "bb", "bbbb"); // size 6 |
| assertEquals(10, cache.size()); |
| |
| // cause the size to grow to 12 should evict 'A' |
| set("C", "c", "c"); |
| cache.flush(); |
| assertEquals(8, cache.size()); |
| assertAbsent("A"); |
| assertValue("B", "bb", "bbbb"); |
| assertValue("C", "c", "c"); |
| |
| // causing the size to grow to 10 should evict nothing |
| set("D", "d", "d"); |
| cache.flush(); |
| assertEquals(10, cache.size()); |
| assertAbsent("A"); |
| assertValue("B", "bb", "bbbb"); |
| assertValue("C", "c", "c"); |
| assertValue("D", "d", "d"); |
| |
| // causing the size to grow to 18 should evict 'B' and 'C' |
| set("E", "eeee", "eeee"); |
| cache.flush(); |
| assertEquals(10, cache.size()); |
| assertAbsent("A"); |
| assertAbsent("B"); |
| assertAbsent("C"); |
| assertValue("D", "d", "d"); |
| assertValue("E", "eeee", "eeee"); |
| } |
| |
| public void testEvictOnUpdate() throws Exception { |
| cache.close(); |
| cache = DiskLruCache.open(cacheDir, appVersion, 2, 10); |
| |
| set("A", "a", "aa"); // size 3 |
| set("B", "b", "bb"); // size 3 |
| set("C", "c", "cc"); // size 3 |
| assertEquals(9, cache.size()); |
| |
| // causing the size to grow to 11 should evict 'A' |
| set("B", "b", "bbbb"); |
| cache.flush(); |
| assertEquals(8, cache.size()); |
| assertAbsent("A"); |
| assertValue("B", "b", "bbbb"); |
| assertValue("C", "c", "cc"); |
| } |
| |
| public void testEvictionHonorsLruFromCurrentSession() throws Exception { |
| cache.close(); |
| cache = DiskLruCache.open(cacheDir, appVersion, 2, 10); |
| set("A", "a", "a"); |
| set("B", "b", "b"); |
| set("C", "c", "c"); |
| set("D", "d", "d"); |
| set("E", "e", "e"); |
| cache.get("B").close(); // 'B' is now least recently used |
| |
| // causing the size to grow to 12 should evict 'A' |
| set("F", "f", "f"); |
| // causing the size to grow to 12 should evict 'C' |
| set("G", "g", "g"); |
| cache.flush(); |
| assertEquals(10, cache.size()); |
| assertAbsent("A"); |
| assertValue("B", "b", "b"); |
| assertAbsent("C"); |
| assertValue("D", "d", "d"); |
| assertValue("E", "e", "e"); |
| assertValue("F", "f", "f"); |
| } |
| |
| public void testEvictionHonorsLruFromPreviousSession() throws Exception { |
| set("A", "a", "a"); |
| set("B", "b", "b"); |
| set("C", "c", "c"); |
| set("D", "d", "d"); |
| set("E", "e", "e"); |
| set("F", "f", "f"); |
| cache.get("B").close(); // 'B' is now least recently used |
| assertEquals(12, cache.size()); |
| cache.close(); |
| cache = DiskLruCache.open(cacheDir, appVersion, 2, 10); |
| |
| set("G", "g", "g"); |
| cache.flush(); |
| assertEquals(10, cache.size()); |
| assertAbsent("A"); |
| assertValue("B", "b", "b"); |
| assertAbsent("C"); |
| assertValue("D", "d", "d"); |
| assertValue("E", "e", "e"); |
| assertValue("F", "f", "f"); |
| assertValue("G", "g", "g"); |
| } |
| |
| public void testCacheSingleEntryOfSizeGreaterThanMaxSize() throws Exception { |
| cache.close(); |
| cache = DiskLruCache.open(cacheDir, appVersion, 2, 10); |
| set("A", "aaaaa", "aaaaaa"); // size=11 |
| cache.flush(); |
| assertAbsent("A"); |
| } |
| |
| public void testCacheSingleValueOfSizeGreaterThanMaxSize() throws Exception { |
| cache.close(); |
| cache = DiskLruCache.open(cacheDir, appVersion, 2, 10); |
| set("A", "aaaaaaaaaaa", "a"); // size=12 |
| cache.flush(); |
| assertAbsent("A"); |
| } |
| |
| public void testConstructorDoesNotAllowZeroCacheSize() throws Exception { |
| try { |
| DiskLruCache.open(cacheDir, appVersion, 2, 0); |
| fail(); |
| } catch (IllegalArgumentException expected) { |
| } |
| } |
| |
| public void testConstructorDoesNotAllowZeroValuesPerEntry() throws Exception { |
| try { |
| DiskLruCache.open(cacheDir, appVersion, 0, 10); |
| fail(); |
| } catch (IllegalArgumentException expected) { |
| } |
| } |
| |
| public void testRemoveAbsentElement() throws Exception { |
| cache.remove("A"); |
| } |
| |
| public void testReadingTheSameStreamMultipleTimes() throws Exception { |
| set("A", "a", "b"); |
| DiskLruCache.Snapshot snapshot = cache.get("A"); |
| assertSame(snapshot.getInputStream(0), snapshot.getInputStream(0)); |
| snapshot.close(); |
| } |
| |
| public void testRebuildJournalOnRepeatedReads() throws Exception { |
| set("A", "a", "a"); |
| set("B", "b", "b"); |
| long lastJournalLength = 0; |
| while (true) { |
| long journalLength = journalFile.length(); |
| assertValue("A", "a", "a"); |
| assertValue("B", "b", "b"); |
| if (journalLength < lastJournalLength) { |
| System.out.printf("Journal compacted from %s bytes to %s bytes\n", |
| lastJournalLength, journalLength); |
| break; // test passed! |
| } |
| lastJournalLength = journalLength; |
| } |
| } |
| |
| public void testRebuildJournalOnRepeatedEdits() throws Exception { |
| long lastJournalLength = 0; |
| while (true) { |
| long journalLength = journalFile.length(); |
| set("A", "a", "a"); |
| set("B", "b", "b"); |
| if (journalLength < lastJournalLength) { |
| System.out.printf("Journal compacted from %s bytes to %s bytes\n", |
| lastJournalLength, journalLength); |
| break; |
| } |
| lastJournalLength = journalLength; |
| } |
| |
| // sanity check that a rebuilt journal behaves normally |
| assertValue("A", "a", "a"); |
| assertValue("B", "b", "b"); |
| } |
| |
| /** @see <a href="https://github.com/JakeWharton/DiskLruCache/issues/28">Issue #28</a> */ |
| public void testRebuildJournalOnRepeatedReadsWithOpenAndClose() throws Exception { |
| set("a", "a", "a"); |
| set("b", "b", "b"); |
| long lastJournalLength = 0; |
| while (true) { |
| long journalLength = journalFile.length(); |
| assertValue("a", "a", "a"); |
| assertValue("b", "b", "b"); |
| cache.close(); |
| cache = DiskLruCache.open(cacheDir, appVersion, 2, Integer.MAX_VALUE); |
| if (journalLength < lastJournalLength) { |
| System.out.printf("Journal compacted from %s bytes to %s bytes\n", |
| lastJournalLength, journalLength); |
| break; // test passed! |
| } |
| lastJournalLength = journalLength; |
| } |
| } |
| |
| /** @see <a href="https://github.com/JakeWharton/DiskLruCache/issues/28">Issue #28</a> */ |
| public void testRebuildJournalOnRepeatedEditsWithOpenAndClose() throws Exception { |
| long lastJournalLength = 0; |
| while (true) { |
| long journalLength = journalFile.length(); |
| set("a", "a", "a"); |
| set("b", "b", "b"); |
| cache.close(); |
| cache = DiskLruCache.open(cacheDir, appVersion, 2, Integer.MAX_VALUE); |
| if (journalLength < lastJournalLength) { |
| System.out.printf("Journal compacted from %s bytes to %s bytes\n", |
| lastJournalLength, journalLength); |
| break; |
| } |
| lastJournalLength = journalLength; |
| } |
| } |
| |
| public void testOpenCreatesDirectoryIfNecessary() throws Exception { |
| cache.close(); |
| File dir = new File(javaTmpDir, "testOpenCreatesDirectoryIfNecessary"); |
| cache = DiskLruCache.open(dir, appVersion, 2, Integer.MAX_VALUE); |
| set("A", "a", "a"); |
| assertTrue(new File(dir, "A.0").exists()); |
| assertTrue(new File(dir, "A.1").exists()); |
| assertTrue(new File(dir, "journal").exists()); |
| } |
| |
| public void testFileDeletedExternally() throws Exception { |
| set("A", "a", "a"); |
| getCleanFile("A", 1).delete(); |
| assertNull(cache.get("A")); |
| } |
| |
| public void testFileBecomesInaccessibleDuringReadResultsInIoException() throws Exception { |
| set("A", "aaaaa", "a"); |
| DiskLruCache.Snapshot snapshot = cache.get("A"); |
| InputStream in = snapshot.getInputStream(0); |
| assertEquals('a', in.read()); |
| mockOs.enqueueFault("read"); |
| try { |
| in.read(); |
| fail(); |
| } catch (IOException expected) { |
| } |
| snapshot.close(); |
| } |
| |
| public void testFileBecomesInaccessibleDuringWriteIsSilentlyDiscarded() throws Exception { |
| set("A", "a", "a"); |
| DiskLruCache.Editor editor = cache.edit("A"); |
| OutputStream out0 = editor.newOutputStream(0); |
| out0.write('b'); |
| out0.close(); |
| OutputStream out1 = editor.newOutputStream(1); |
| out1.write('c'); |
| mockOs.enqueueFault("write"); |
| out1.write('c'); // this doesn't throw... |
| out1.close(); |
| editor.commit(); // ... but this will abort |
| assertAbsent("A"); |
| } |
| |
| public void testEditSameVersion() throws Exception { |
| set("A", "a", "a"); |
| DiskLruCache.Snapshot snapshot = cache.get("A"); |
| DiskLruCache.Editor editor = snapshot.edit(); |
| editor.set(1, "a2"); |
| editor.commit(); |
| assertValue("A", "a", "a2"); |
| } |
| |
| public void testEditSnapshotAfterChangeAborted() throws Exception { |
| set("A", "a", "a"); |
| DiskLruCache.Snapshot snapshot = cache.get("A"); |
| DiskLruCache.Editor toAbort = snapshot.edit(); |
| toAbort.set(0, "b"); |
| toAbort.abort(); |
| DiskLruCache.Editor editor = snapshot.edit(); |
| editor.set(1, "a2"); |
| editor.commit(); |
| assertValue("A", "a", "a2"); |
| } |
| |
| public void testEditSnapshotAfterChangeCommitted() throws Exception { |
| set("A", "a", "a"); |
| DiskLruCache.Snapshot snapshot = cache.get("A"); |
| DiskLruCache.Editor toAbort = snapshot.edit(); |
| toAbort.set(0, "b"); |
| toAbort.commit(); |
| assertNull(snapshot.edit()); |
| } |
| |
| public void testEditSinceEvicted() throws Exception { |
| cache.close(); |
| cache = DiskLruCache.open(cacheDir, appVersion, 2, 10); |
| set("A", "aa", "aaa"); // size 5 |
| DiskLruCache.Snapshot snapshot = cache.get("A"); |
| set("B", "bb", "bbb"); // size 5 |
| set("C", "cc", "ccc"); // size 5; will evict 'A' |
| cache.flush(); |
| assertNull(snapshot.edit()); |
| } |
| |
| public void testEditSinceEvictedAndRecreated() throws Exception { |
| cache.close(); |
| cache = DiskLruCache.open(cacheDir, appVersion, 2, 10); |
| set("A", "aa", "aaa"); // size 5 |
| DiskLruCache.Snapshot snapshot = cache.get("A"); |
| set("B", "bb", "bbb"); // size 5 |
| set("C", "cc", "ccc"); // size 5; will evict 'A' |
| set("A", "a", "aaaa"); // size 5; will evict 'B' |
| cache.flush(); |
| assertNull(snapshot.edit()); |
| } |
| |
| private void assertJournalEquals(String... expectedBodyLines) throws Exception { |
| List<String> expectedLines = new ArrayList<String>(); |
| expectedLines.add(MAGIC); |
| expectedLines.add(VERSION_1); |
| expectedLines.add("100"); |
| expectedLines.add("2"); |
| expectedLines.add(""); |
| expectedLines.addAll(Arrays.asList(expectedBodyLines)); |
| assertEquals(expectedLines, readJournalLines()); |
| } |
| |
| private void createJournal(String... bodyLines) throws Exception { |
| createJournalWithHeader(MAGIC, VERSION_1, "100", "2", "", bodyLines); |
| } |
| |
| private void createJournalWithHeader(String magic, String version, String appVersion, |
| String valueCount, String blank, String... bodyLines) throws Exception { |
| Writer writer = new FileWriter(journalFile); |
| writer.write(magic + "\n"); |
| writer.write(version + "\n"); |
| writer.write(appVersion + "\n"); |
| writer.write(valueCount + "\n"); |
| writer.write(blank + "\n"); |
| for (String line : bodyLines) { |
| writer.write(line); |
| writer.write('\n'); |
| } |
| writer.close(); |
| } |
| |
| private List<String> readJournalLines() throws Exception { |
| List<String> result = new ArrayList<String>(); |
| BufferedReader reader = new BufferedReader(new FileReader(journalFile)); |
| String line; |
| while ((line = reader.readLine()) != null) { |
| result.add(line); |
| } |
| reader.close(); |
| return result; |
| } |
| |
| private File getCleanFile(String key, int index) { |
| return new File(cacheDir, key + "." + index); |
| } |
| |
| private File getDirtyFile(String key, int index) { |
| return new File(cacheDir, key + "." + index + ".tmp"); |
| } |
| |
| private String readFile(File file) throws Exception { |
| Reader reader = new FileReader(file); |
| StringWriter writer = new StringWriter(); |
| char[] buffer = new char[1024]; |
| int count; |
| while ((count = reader.read(buffer)) != -1) { |
| writer.write(buffer, 0, count); |
| } |
| reader.close(); |
| return writer.toString(); |
| } |
| |
| public void writeFile(File file, String content) throws Exception { |
| FileWriter writer = new FileWriter(file); |
| writer.write(content); |
| writer.close(); |
| } |
| |
| private void assertInoperable(DiskLruCache.Editor editor) throws Exception { |
| try { |
| editor.getString(0); |
| fail(); |
| } catch (IllegalStateException expected) { |
| } |
| try { |
| editor.set(0, "A"); |
| fail(); |
| } catch (IllegalStateException expected) { |
| } |
| try { |
| editor.newInputStream(0); |
| fail(); |
| } catch (IllegalStateException expected) { |
| } |
| try { |
| editor.newOutputStream(0); |
| fail(); |
| } catch (IllegalStateException expected) { |
| } |
| try { |
| editor.commit(); |
| fail(); |
| } catch (IllegalStateException expected) { |
| } |
| try { |
| editor.abort(); |
| fail(); |
| } catch (IllegalStateException expected) { |
| } |
| } |
| |
| private void generateSomeGarbageFiles() throws Exception { |
| File dir1 = new File(cacheDir, "dir1"); |
| File dir2 = new File(dir1, "dir2"); |
| writeFile(getCleanFile("g1", 0), "A"); |
| writeFile(getCleanFile("g1", 1), "B"); |
| writeFile(getCleanFile("g2", 0), "C"); |
| writeFile(getCleanFile("g2", 1), "D"); |
| writeFile(getCleanFile("g2", 1), "D"); |
| writeFile(new File(cacheDir, "otherFile0"), "E"); |
| dir1.mkdir(); |
| dir2.mkdir(); |
| writeFile(new File(dir2, "otherFile1"), "F"); |
| } |
| |
| private void assertGarbageFilesAllDeleted() throws Exception { |
| assertFalse(getCleanFile("g1", 0).exists()); |
| assertFalse(getCleanFile("g1", 1).exists()); |
| assertFalse(getCleanFile("g2", 0).exists()); |
| assertFalse(getCleanFile("g2", 1).exists()); |
| assertFalse(new File(cacheDir, "otherFile0").exists()); |
| assertFalse(new File(cacheDir, "dir1").exists()); |
| } |
| |
| private void set(String key, String value0, String value1) throws Exception { |
| DiskLruCache.Editor editor = cache.edit(key); |
| editor.set(0, value0); |
| editor.set(1, value1); |
| editor.commit(); |
| } |
| |
| private void assertAbsent(String key) throws Exception { |
| DiskLruCache.Snapshot snapshot = cache.get(key); |
| if (snapshot != null) { |
| snapshot.close(); |
| fail(); |
| } |
| assertFalse(getCleanFile(key, 0).exists()); |
| assertFalse(getCleanFile(key, 1).exists()); |
| assertFalse(getDirtyFile(key, 0).exists()); |
| assertFalse(getDirtyFile(key, 1).exists()); |
| } |
| |
| private void assertValue(String key, String value0, String value1) throws Exception { |
| DiskLruCache.Snapshot snapshot = cache.get(key); |
| assertEquals(value0, snapshot.getString(0)); |
| assertEquals(value1, snapshot.getString(1)); |
| assertTrue(getCleanFile(key, 0).exists()); |
| assertTrue(getCleanFile(key, 1).exists()); |
| snapshot.close(); |
| } |
| } |