1e78f117bcbd6b57d783737107f445ef75ecb474aNeil Fuller/*
2e78f117bcbd6b57d783737107f445ef75ecb474aNeil Fuller * Copyright (C) 2011 The Android Open Source Project
3e78f117bcbd6b57d783737107f445ef75ecb474aNeil Fuller *
4e78f117bcbd6b57d783737107f445ef75ecb474aNeil Fuller * Licensed under the Apache License, Version 2.0 (the "License");
5e78f117bcbd6b57d783737107f445ef75ecb474aNeil Fuller * you may not use this file except in compliance with the License.
6e78f117bcbd6b57d783737107f445ef75ecb474aNeil Fuller * You may obtain a copy of the License at
7e78f117bcbd6b57d783737107f445ef75ecb474aNeil Fuller *
8e78f117bcbd6b57d783737107f445ef75ecb474aNeil Fuller *      http://www.apache.org/licenses/LICENSE-2.0
9e78f117bcbd6b57d783737107f445ef75ecb474aNeil Fuller *
10e78f117bcbd6b57d783737107f445ef75ecb474aNeil Fuller * Unless required by applicable law or agreed to in writing, software
11e78f117bcbd6b57d783737107f445ef75ecb474aNeil Fuller * distributed under the License is distributed on an "AS IS" BASIS,
12e78f117bcbd6b57d783737107f445ef75ecb474aNeil Fuller * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13e78f117bcbd6b57d783737107f445ef75ecb474aNeil Fuller * See the License for the specific language governing permissions and
14e78f117bcbd6b57d783737107f445ef75ecb474aNeil Fuller * limitations under the License.
15e78f117bcbd6b57d783737107f445ef75ecb474aNeil Fuller */
16e78f117bcbd6b57d783737107f445ef75ecb474aNeil Fullerpackage com.squareup.okhttp.internal;
17e78f117bcbd6b57d783737107f445ef75ecb474aNeil Fuller
18a2cab72aa5ff730ba2ae987b45398faafffeb505Neil Fullerimport com.squareup.okhttp.internal.io.FileSystem;
19e78f117bcbd6b57d783737107f445ef75ecb474aNeil Fullerimport java.io.File;
20e78f117bcbd6b57d783737107f445ef75ecb474aNeil Fullerimport java.io.IOException;
21e78f117bcbd6b57d783737107f445ef75ecb474aNeil Fullerimport java.util.ArrayDeque;
22e78f117bcbd6b57d783737107f445ef75ecb474aNeil Fullerimport java.util.ArrayList;
23e78f117bcbd6b57d783737107f445ef75ecb474aNeil Fullerimport java.util.Arrays;
24e78f117bcbd6b57d783737107f445ef75ecb474aNeil Fullerimport java.util.Deque;
25e78f117bcbd6b57d783737107f445ef75ecb474aNeil Fullerimport java.util.Iterator;
26e78f117bcbd6b57d783737107f445ef75ecb474aNeil Fullerimport java.util.List;
27e78f117bcbd6b57d783737107f445ef75ecb474aNeil Fullerimport java.util.NoSuchElementException;
28e78f117bcbd6b57d783737107f445ef75ecb474aNeil Fullerimport java.util.concurrent.Executor;
29e78f117bcbd6b57d783737107f445ef75ecb474aNeil Fullerimport okio.BufferedSink;
30e78f117bcbd6b57d783737107f445ef75ecb474aNeil Fullerimport okio.BufferedSource;
31e78f117bcbd6b57d783737107f445ef75ecb474aNeil Fullerimport okio.Okio;
32e78f117bcbd6b57d783737107f445ef75ecb474aNeil Fullerimport okio.Source;
33e78f117bcbd6b57d783737107f445ef75ecb474aNeil Fullerimport org.junit.After;
34e78f117bcbd6b57d783737107f445ef75ecb474aNeil Fullerimport org.junit.Before;
35e78f117bcbd6b57d783737107f445ef75ecb474aNeil Fullerimport org.junit.Rule;
36e78f117bcbd6b57d783737107f445ef75ecb474aNeil Fullerimport org.junit.Test;
37e78f117bcbd6b57d783737107f445ef75ecb474aNeil Fullerimport org.junit.rules.TemporaryFolder;
38e78f117bcbd6b57d783737107f445ef75ecb474aNeil Fullerimport org.junit.rules.Timeout;
39e78f117bcbd6b57d783737107f445ef75ecb474aNeil Fuller
40e78f117bcbd6b57d783737107f445ef75ecb474aNeil Fullerimport static com.squareup.okhttp.internal.DiskLruCache.JOURNAL_FILE;
41e78f117bcbd6b57d783737107f445ef75ecb474aNeil Fullerimport static com.squareup.okhttp.internal.DiskLruCache.JOURNAL_FILE_BACKUP;
42e78f117bcbd6b57d783737107f445ef75ecb474aNeil Fullerimport static com.squareup.okhttp.internal.DiskLruCache.MAGIC;
43e78f117bcbd6b57d783737107f445ef75ecb474aNeil Fullerimport static com.squareup.okhttp.internal.DiskLruCache.VERSION_1;
44e78f117bcbd6b57d783737107f445ef75ecb474aNeil Fullerimport static org.junit.Assert.assertEquals;
45e78f117bcbd6b57d783737107f445ef75ecb474aNeil Fullerimport static org.junit.Assert.assertFalse;
46e78f117bcbd6b57d783737107f445ef75ecb474aNeil Fullerimport static org.junit.Assert.assertNull;
47e78f117bcbd6b57d783737107f445ef75ecb474aNeil Fullerimport static org.junit.Assert.assertSame;
48e78f117bcbd6b57d783737107f445ef75ecb474aNeil Fullerimport static org.junit.Assert.assertTrue;
49e78f117bcbd6b57d783737107f445ef75ecb474aNeil Fullerimport static org.junit.Assert.fail;
50e78f117bcbd6b57d783737107f445ef75ecb474aNeil Fuller
51e78f117bcbd6b57d783737107f445ef75ecb474aNeil Fullerpublic final class DiskLruCacheTest {
52e78f117bcbd6b57d783737107f445ef75ecb474aNeil Fuller  @Rule public final TemporaryFolder tempDir = new TemporaryFolder();
53e78f117bcbd6b57d783737107f445ef75ecb474aNeil Fuller  @Rule public final Timeout timeout = new Timeout(30 * 1000);
54e78f117bcbd6b57d783737107f445ef75ecb474aNeil Fuller
55a2cab72aa5ff730ba2ae987b45398faafffeb505Neil Fuller  private final FaultyFileSystem fileSystem = new FaultyFileSystem(FileSystem.SYSTEM);
56e78f117bcbd6b57d783737107f445ef75ecb474aNeil Fuller  private final int appVersion = 100;
57e78f117bcbd6b57d783737107f445ef75ecb474aNeil Fuller  private File cacheDir;
58e78f117bcbd6b57d783737107f445ef75ecb474aNeil Fuller  private File journalFile;
59e78f117bcbd6b57d783737107f445ef75ecb474aNeil Fuller  private File journalBkpFile;
60e78f117bcbd6b57d783737107f445ef75ecb474aNeil Fuller  private final TestExecutor executor = new TestExecutor();
61e78f117bcbd6b57d783737107f445ef75ecb474aNeil Fuller
62e78f117bcbd6b57d783737107f445ef75ecb474aNeil Fuller  private DiskLruCache cache;
63e78f117bcbd6b57d783737107f445ef75ecb474aNeil Fuller  private final Deque<DiskLruCache> toClose = new ArrayDeque<>();
64e78f117bcbd6b57d783737107f445ef75ecb474aNeil Fuller
65e78f117bcbd6b57d783737107f445ef75ecb474aNeil Fuller  private void createNewCache() throws IOException {
66e78f117bcbd6b57d783737107f445ef75ecb474aNeil Fuller    createNewCacheWithSize(Integer.MAX_VALUE);
67e78f117bcbd6b57d783737107f445ef75ecb474aNeil Fuller  }
68e78f117bcbd6b57d783737107f445ef75ecb474aNeil Fuller
69e78f117bcbd6b57d783737107f445ef75ecb474aNeil Fuller  private void createNewCacheWithSize(int maxSize) throws IOException {
70a2cab72aa5ff730ba2ae987b45398faafffeb505Neil Fuller    cache = new DiskLruCache(fileSystem, cacheDir, appVersion, 2, maxSize, executor);
71e78f117bcbd6b57d783737107f445ef75ecb474aNeil Fuller    synchronized (cache) {
72e78f117bcbd6b57d783737107f445ef75ecb474aNeil Fuller      cache.initialize();
73e78f117bcbd6b57d783737107f445ef75ecb474aNeil Fuller    }
74e78f117bcbd6b57d783737107f445ef75ecb474aNeil Fuller    toClose.add(cache);
75e78f117bcbd6b57d783737107f445ef75ecb474aNeil Fuller  }
76e78f117bcbd6b57d783737107f445ef75ecb474aNeil Fuller
77e78f117bcbd6b57d783737107f445ef75ecb474aNeil Fuller  @Before public void setUp() throws Exception {
78e78f117bcbd6b57d783737107f445ef75ecb474aNeil Fuller    cacheDir = tempDir.getRoot();
79e78f117bcbd6b57d783737107f445ef75ecb474aNeil Fuller    journalFile = new File(cacheDir, JOURNAL_FILE);
80e78f117bcbd6b57d783737107f445ef75ecb474aNeil Fuller    journalBkpFile = new File(cacheDir, JOURNAL_FILE_BACKUP);
81e78f117bcbd6b57d783737107f445ef75ecb474aNeil Fuller    createNewCache();
82e78f117bcbd6b57d783737107f445ef75ecb474aNeil Fuller  }
83e78f117bcbd6b57d783737107f445ef75ecb474aNeil Fuller
84e78f117bcbd6b57d783737107f445ef75ecb474aNeil Fuller  @After public void tearDown() throws Exception {
85e78f117bcbd6b57d783737107f445ef75ecb474aNeil Fuller    while (!toClose.isEmpty()) {
86e78f117bcbd6b57d783737107f445ef75ecb474aNeil Fuller      toClose.pop().close();
87e78f117bcbd6b57d783737107f445ef75ecb474aNeil Fuller    }
88e78f117bcbd6b57d783737107f445ef75ecb474aNeil Fuller  }
89e78f117bcbd6b57d783737107f445ef75ecb474aNeil Fuller
90e78f117bcbd6b57d783737107f445ef75ecb474aNeil Fuller  @Test public void emptyCache() throws Exception {
91e78f117bcbd6b57d783737107f445ef75ecb474aNeil Fuller    cache.close();
92e78f117bcbd6b57d783737107f445ef75ecb474aNeil Fuller    assertJournalEquals();
93e78f117bcbd6b57d783737107f445ef75ecb474aNeil Fuller  }
94e78f117bcbd6b57d783737107f445ef75ecb474aNeil Fuller
95e78f117bcbd6b57d783737107f445ef75ecb474aNeil Fuller  @Test public void validateKey() throws Exception {
96e78f117bcbd6b57d783737107f445ef75ecb474aNeil Fuller    String key = null;
97e78f117bcbd6b57d783737107f445ef75ecb474aNeil Fuller    try {
98e78f117bcbd6b57d783737107f445ef75ecb474aNeil Fuller      key = "has_space ";
99e78f117bcbd6b57d783737107f445ef75ecb474aNeil Fuller      cache.edit(key);
100e78f117bcbd6b57d783737107f445ef75ecb474aNeil Fuller      fail("Exepcting an IllegalArgumentException as the key was invalid.");
101e78f117bcbd6b57d783737107f445ef75ecb474aNeil Fuller    } catch (IllegalArgumentException iae) {
102e78f117bcbd6b57d783737107f445ef75ecb474aNeil Fuller      assertEquals("keys must match regex [a-z0-9_-]{1,120}: \"" + key + "\"", iae.getMessage());
103e78f117bcbd6b57d783737107f445ef75ecb474aNeil Fuller    }
104e78f117bcbd6b57d783737107f445ef75ecb474aNeil Fuller    try {
105e78f117bcbd6b57d783737107f445ef75ecb474aNeil Fuller      key = "has_CR\r";
106e78f117bcbd6b57d783737107f445ef75ecb474aNeil Fuller      cache.edit(key);
107e78f117bcbd6b57d783737107f445ef75ecb474aNeil Fuller      fail("Exepcting an IllegalArgumentException as the key was invalid.");
108e78f117bcbd6b57d783737107f445ef75ecb474aNeil Fuller    } catch (IllegalArgumentException iae) {
109e78f117bcbd6b57d783737107f445ef75ecb474aNeil Fuller      assertEquals("keys must match regex [a-z0-9_-]{1,120}: \"" + key + "\"", iae.getMessage());
110e78f117bcbd6b57d783737107f445ef75ecb474aNeil Fuller    }
111e78f117bcbd6b57d783737107f445ef75ecb474aNeil Fuller    try {
112e78f117bcbd6b57d783737107f445ef75ecb474aNeil Fuller      key = "has_LF\n";
113e78f117bcbd6b57d783737107f445ef75ecb474aNeil Fuller      cache.edit(key);
114e78f117bcbd6b57d783737107f445ef75ecb474aNeil Fuller      fail("Exepcting an IllegalArgumentException as the key was invalid.");
115e78f117bcbd6b57d783737107f445ef75ecb474aNeil Fuller    } catch (IllegalArgumentException iae) {
116e78f117bcbd6b57d783737107f445ef75ecb474aNeil Fuller      assertEquals("keys must match regex [a-z0-9_-]{1,120}: \"" + key + "\"", iae.getMessage());
117e78f117bcbd6b57d783737107f445ef75ecb474aNeil Fuller    }
118e78f117bcbd6b57d783737107f445ef75ecb474aNeil Fuller    try {
119e78f117bcbd6b57d783737107f445ef75ecb474aNeil Fuller      key = "has_invalid/";
120e78f117bcbd6b57d783737107f445ef75ecb474aNeil Fuller      cache.edit(key);
121e78f117bcbd6b57d783737107f445ef75ecb474aNeil Fuller      fail("Exepcting an IllegalArgumentException as the key was invalid.");
122e78f117bcbd6b57d783737107f445ef75ecb474aNeil Fuller    } catch (IllegalArgumentException iae) {
123e78f117bcbd6b57d783737107f445ef75ecb474aNeil Fuller      assertEquals("keys must match regex [a-z0-9_-]{1,120}: \"" + key + "\"", iae.getMessage());
124e78f117bcbd6b57d783737107f445ef75ecb474aNeil Fuller    }
125e78f117bcbd6b57d783737107f445ef75ecb474aNeil Fuller    try {
126e78f117bcbd6b57d783737107f445ef75ecb474aNeil Fuller      key = "has_invalid\u2603";
127e78f117bcbd6b57d783737107f445ef75ecb474aNeil Fuller      cache.edit(key);
128e78f117bcbd6b57d783737107f445ef75ecb474aNeil Fuller      fail("Exepcting an IllegalArgumentException as the key was invalid.");
129e78f117bcbd6b57d783737107f445ef75ecb474aNeil Fuller    } catch (IllegalArgumentException iae) {
130e78f117bcbd6b57d783737107f445ef75ecb474aNeil Fuller      assertEquals("keys must match regex [a-z0-9_-]{1,120}: \"" + key + "\"", iae.getMessage());
131e78f117bcbd6b57d783737107f445ef75ecb474aNeil Fuller    }
132e78f117bcbd6b57d783737107f445ef75ecb474aNeil Fuller    try {
133e78f117bcbd6b57d783737107f445ef75ecb474aNeil Fuller      key = "this_is_way_too_long_this_is_way_too_long_this_is_way_too_long_"
134e78f117bcbd6b57d783737107f445ef75ecb474aNeil Fuller          + "this_is_way_too_long_this_is_way_too_long_this_is_way_too_long";
135e78f117bcbd6b57d783737107f445ef75ecb474aNeil Fuller      cache.edit(key);
136e78f117bcbd6b57d783737107f445ef75ecb474aNeil Fuller      fail("Exepcting an IllegalArgumentException as the key was too long.");
137e78f117bcbd6b57d783737107f445ef75ecb474aNeil Fuller    } catch (IllegalArgumentException iae) {
138e78f117bcbd6b57d783737107f445ef75ecb474aNeil Fuller      assertEquals("keys must match regex [a-z0-9_-]{1,120}: \"" + key + "\"", iae.getMessage());
139e78f117bcbd6b57d783737107f445ef75ecb474aNeil Fuller    }
140e78f117bcbd6b57d783737107f445ef75ecb474aNeil Fuller
141e78f117bcbd6b57d783737107f445ef75ecb474aNeil Fuller    // Test valid cases.
142e78f117bcbd6b57d783737107f445ef75ecb474aNeil Fuller
143e78f117bcbd6b57d783737107f445ef75ecb474aNeil Fuller    // Exactly 120.
144e78f117bcbd6b57d783737107f445ef75ecb474aNeil Fuller    key = "0123456789012345678901234567890123456789012345678901234567890123456789"
145e78f117bcbd6b57d783737107f445ef75ecb474aNeil Fuller        + "01234567890123456789012345678901234567890123456789";
146e78f117bcbd6b57d783737107f445ef75ecb474aNeil Fuller    cache.edit(key).abort();
147e78f117bcbd6b57d783737107f445ef75ecb474aNeil Fuller    // Contains all valid characters.
148e78f117bcbd6b57d783737107f445ef75ecb474aNeil Fuller    key = "abcdefghijklmnopqrstuvwxyz_0123456789";
149e78f117bcbd6b57d783737107f445ef75ecb474aNeil Fuller    cache.edit(key).abort();
150e78f117bcbd6b57d783737107f445ef75ecb474aNeil Fuller    // Contains dash.
151e78f117bcbd6b57d783737107f445ef75ecb474aNeil Fuller    key = "-20384573948576";
152e78f117bcbd6b57d783737107f445ef75ecb474aNeil Fuller    cache.edit(key).abort();
153e78f117bcbd6b57d783737107f445ef75ecb474aNeil Fuller  }
154e78f117bcbd6b57d783737107f445ef75ecb474aNeil Fuller
155e78f117bcbd6b57d783737107f445ef75ecb474aNeil Fuller  @Test public void writeAndReadEntry() throws Exception {
156e78f117bcbd6b57d783737107f445ef75ecb474aNeil Fuller    DiskLruCache.Editor creator = cache.edit("k1");
157e78f117bcbd6b57d783737107f445ef75ecb474aNeil Fuller    setString(creator, 0, "ABC");
158e78f117bcbd6b57d783737107f445ef75ecb474aNeil Fuller    setString(creator, 1, "DE");
159e78f117bcbd6b57d783737107f445ef75ecb474aNeil Fuller    assertNull(creator.newSource(0));
160e78f117bcbd6b57d783737107f445ef75ecb474aNeil Fuller    assertNull(creator.newSource(1));
161e78f117bcbd6b57d783737107f445ef75ecb474aNeil Fuller    creator.commit();
162e78f117bcbd6b57d783737107f445ef75ecb474aNeil Fuller
163e78f117bcbd6b57d783737107f445ef75ecb474aNeil Fuller    DiskLruCache.Snapshot snapshot = cache.get("k1");
164e78f117bcbd6b57d783737107f445ef75ecb474aNeil Fuller    assertSnapshotValue(snapshot, 0, "ABC");
165e78f117bcbd6b57d783737107f445ef75ecb474aNeil Fuller    assertSnapshotValue(snapshot, 1, "DE");
166e78f117bcbd6b57d783737107f445ef75ecb474aNeil Fuller  }
167e78f117bcbd6b57d783737107f445ef75ecb474aNeil Fuller
168e78f117bcbd6b57d783737107f445ef75ecb474aNeil Fuller  @Test public void readAndWriteEntryAcrossCacheOpenAndClose() throws Exception {
169e78f117bcbd6b57d783737107f445ef75ecb474aNeil Fuller    DiskLruCache.Editor creator = cache.edit("k1");
170e78f117bcbd6b57d783737107f445ef75ecb474aNeil Fuller    setString(creator, 0, "A");
171e78f117bcbd6b57d783737107f445ef75ecb474aNeil Fuller    setString(creator, 1, "B");
172e78f117bcbd6b57d783737107f445ef75ecb474aNeil Fuller    creator.commit();
173e78f117bcbd6b57d783737107f445ef75ecb474aNeil Fuller    cache.close();
174e78f117bcbd6b57d783737107f445ef75ecb474aNeil Fuller
175e78f117bcbd6b57d783737107f445ef75ecb474aNeil Fuller    createNewCache();
176e78f117bcbd6b57d783737107f445ef75ecb474aNeil Fuller    DiskLruCache.Snapshot snapshot = cache.get("k1");
177e78f117bcbd6b57d783737107f445ef75ecb474aNeil Fuller    assertSnapshotValue(snapshot, 0, "A");
178e78f117bcbd6b57d783737107f445ef75ecb474aNeil Fuller    assertSnapshotValue(snapshot, 1, "B");
179e78f117bcbd6b57d783737107f445ef75ecb474aNeil Fuller    snapshot.close();
180e78f117bcbd6b57d783737107f445ef75ecb474aNeil Fuller  }
181e78f117bcbd6b57d783737107f445ef75ecb474aNeil Fuller
182e78f117bcbd6b57d783737107f445ef75ecb474aNeil Fuller  @Test public void readAndWriteEntryWithoutProperClose() throws Exception {
183e78f117bcbd6b57d783737107f445ef75ecb474aNeil Fuller    DiskLruCache.Editor creator = cache.edit("k1");
184e78f117bcbd6b57d783737107f445ef75ecb474aNeil Fuller    setString(creator, 0, "A");
185e78f117bcbd6b57d783737107f445ef75ecb474aNeil Fuller    setString(creator, 1, "B");
186e78f117bcbd6b57d783737107f445ef75ecb474aNeil Fuller    creator.commit();
187e78f117bcbd6b57d783737107f445ef75ecb474aNeil Fuller
188e78f117bcbd6b57d783737107f445ef75ecb474aNeil Fuller    // Simulate a dirty close of 'cache' by opening the cache directory again.
189e78f117bcbd6b57d783737107f445ef75ecb474aNeil Fuller    createNewCache();
190e78f117bcbd6b57d783737107f445ef75ecb474aNeil Fuller    DiskLruCache.Snapshot snapshot = cache.get("k1");
191e78f117bcbd6b57d783737107f445ef75ecb474aNeil Fuller    assertSnapshotValue(snapshot, 0, "A");
192e78f117bcbd6b57d783737107f445ef75ecb474aNeil Fuller    assertSnapshotValue(snapshot, 1, "B");
193e78f117bcbd6b57d783737107f445ef75ecb474aNeil Fuller    snapshot.close();
194e78f117bcbd6b57d783737107f445ef75ecb474aNeil Fuller  }
195e78f117bcbd6b57d783737107f445ef75ecb474aNeil Fuller
196e78f117bcbd6b57d783737107f445ef75ecb474aNeil Fuller  @Test public void journalWithEditAndPublish() throws Exception {
197e78f117bcbd6b57d783737107f445ef75ecb474aNeil Fuller    DiskLruCache.Editor creator = cache.edit("k1");
198e78f117bcbd6b57d783737107f445ef75ecb474aNeil Fuller    assertJournalEquals("DIRTY k1"); // DIRTY must always be flushed.
199e78f117bcbd6b57d783737107f445ef75ecb474aNeil Fuller    setString(creator, 0, "AB");
200e78f117bcbd6b57d783737107f445ef75ecb474aNeil Fuller    setString(creator, 1, "C");
201e78f117bcbd6b57d783737107f445ef75ecb474aNeil Fuller    creator.commit();
202e78f117bcbd6b57d783737107f445ef75ecb474aNeil Fuller    cache.close();
203e78f117bcbd6b57d783737107f445ef75ecb474aNeil Fuller    assertJournalEquals("DIRTY k1", "CLEAN k1 2 1");
204e78f117bcbd6b57d783737107f445ef75ecb474aNeil Fuller  }
205e78f117bcbd6b57d783737107f445ef75ecb474aNeil Fuller
206e78f117bcbd6b57d783737107f445ef75ecb474aNeil Fuller  @Test public void revertedNewFileIsRemoveInJournal() throws Exception {
207e78f117bcbd6b57d783737107f445ef75ecb474aNeil Fuller    DiskLruCache.Editor creator = cache.edit("k1");
208e78f117bcbd6b57d783737107f445ef75ecb474aNeil Fuller    assertJournalEquals("DIRTY k1"); // DIRTY must always be flushed.
209e78f117bcbd6b57d783737107f445ef75ecb474aNeil Fuller    setString(creator, 0, "AB");
210e78f117bcbd6b57d783737107f445ef75ecb474aNeil Fuller    setString(creator, 1, "C");
211e78f117bcbd6b57d783737107f445ef75ecb474aNeil Fuller    creator.abort();
212e78f117bcbd6b57d783737107f445ef75ecb474aNeil Fuller    cache.close();
213e78f117bcbd6b57d783737107f445ef75ecb474aNeil Fuller    assertJournalEquals("DIRTY k1", "REMOVE k1");
214e78f117bcbd6b57d783737107f445ef75ecb474aNeil Fuller  }
215e78f117bcbd6b57d783737107f445ef75ecb474aNeil Fuller
216e78f117bcbd6b57d783737107f445ef75ecb474aNeil Fuller  @Test public void unterminatedEditIsRevertedOnClose() throws Exception {
217e78f117bcbd6b57d783737107f445ef75ecb474aNeil Fuller    cache.edit("k1");
218e78f117bcbd6b57d783737107f445ef75ecb474aNeil Fuller    cache.close();
219e78f117bcbd6b57d783737107f445ef75ecb474aNeil Fuller    assertJournalEquals("DIRTY k1", "REMOVE k1");
220e78f117bcbd6b57d783737107f445ef75ecb474aNeil Fuller  }
221e78f117bcbd6b57d783737107f445ef75ecb474aNeil Fuller
222e78f117bcbd6b57d783737107f445ef75ecb474aNeil Fuller  @Test public void journalDoesNotIncludeReadOfYetUnpublishedValue() throws Exception {
223e78f117bcbd6b57d783737107f445ef75ecb474aNeil Fuller    DiskLruCache.Editor creator = cache.edit("k1");
224e78f117bcbd6b57d783737107f445ef75ecb474aNeil Fuller    assertNull(cache.get("k1"));
225e78f117bcbd6b57d783737107f445ef75ecb474aNeil Fuller    setString(creator, 0, "A");
226e78f117bcbd6b57d783737107f445ef75ecb474aNeil Fuller    setString(creator, 1, "BC");
227e78f117bcbd6b57d783737107f445ef75ecb474aNeil Fuller    creator.commit();
228e78f117bcbd6b57d783737107f445ef75ecb474aNeil Fuller    cache.close();
229e78f117bcbd6b57d783737107f445ef75ecb474aNeil Fuller    assertJournalEquals("DIRTY k1", "CLEAN k1 1 2");
230e78f117bcbd6b57d783737107f445ef75ecb474aNeil Fuller  }
231e78f117bcbd6b57d783737107f445ef75ecb474aNeil Fuller
232e78f117bcbd6b57d783737107f445ef75ecb474aNeil Fuller  @Test public void journalWithEditAndPublishAndRead() throws Exception {
233e78f117bcbd6b57d783737107f445ef75ecb474aNeil Fuller    DiskLruCache.Editor k1Creator = cache.edit("k1");
234e78f117bcbd6b57d783737107f445ef75ecb474aNeil Fuller    setString(k1Creator, 0, "AB");
235e78f117bcbd6b57d783737107f445ef75ecb474aNeil Fuller    setString(k1Creator, 1, "C");
236e78f117bcbd6b57d783737107f445ef75ecb474aNeil Fuller    k1Creator.commit();
237e78f117bcbd6b57d783737107f445ef75ecb474aNeil Fuller    DiskLruCache.Editor k2Creator = cache.edit("k2");
238e78f117bcbd6b57d783737107f445ef75ecb474aNeil Fuller    setString(k2Creator, 0, "DEF");
239e78f117bcbd6b57d783737107f445ef75ecb474aNeil Fuller    setString(k2Creator, 1, "G");
240e78f117bcbd6b57d783737107f445ef75ecb474aNeil Fuller    k2Creator.commit();
241e78f117bcbd6b57d783737107f445ef75ecb474aNeil Fuller    DiskLruCache.Snapshot k1Snapshot = cache.get("k1");
242e78f117bcbd6b57d783737107f445ef75ecb474aNeil Fuller    k1Snapshot.close();
243e78f117bcbd6b57d783737107f445ef75ecb474aNeil Fuller    cache.close();
244e78f117bcbd6b57d783737107f445ef75ecb474aNeil Fuller    assertJournalEquals("DIRTY k1", "CLEAN k1 2 1", "DIRTY k2", "CLEAN k2 3 1", "READ k1");
245e78f117bcbd6b57d783737107f445ef75ecb474aNeil Fuller  }
246e78f117bcbd6b57d783737107f445ef75ecb474aNeil Fuller
247e78f117bcbd6b57d783737107f445ef75ecb474aNeil Fuller  @Test public void cannotOperateOnEditAfterPublish() throws Exception {
248e78f117bcbd6b57d783737107f445ef75ecb474aNeil Fuller    DiskLruCache.Editor editor = cache.edit("k1");
249e78f117bcbd6b57d783737107f445ef75ecb474aNeil Fuller    setString(editor, 0, "A");
250e78f117bcbd6b57d783737107f445ef75ecb474aNeil Fuller    setString(editor, 1, "B");
251e78f117bcbd6b57d783737107f445ef75ecb474aNeil Fuller    editor.commit();
252e78f117bcbd6b57d783737107f445ef75ecb474aNeil Fuller    assertInoperable(editor);
253e78f117bcbd6b57d783737107f445ef75ecb474aNeil Fuller  }
254e78f117bcbd6b57d783737107f445ef75ecb474aNeil Fuller
255e78f117bcbd6b57d783737107f445ef75ecb474aNeil Fuller  @Test public void cannotOperateOnEditAfterRevert() throws Exception {
256e78f117bcbd6b57d783737107f445ef75ecb474aNeil Fuller    DiskLruCache.Editor editor = cache.edit("k1");
257e78f117bcbd6b57d783737107f445ef75ecb474aNeil Fuller    setString(editor, 0, "A");
258e78f117bcbd6b57d783737107f445ef75ecb474aNeil Fuller    setString(editor, 1, "B");
259e78f117bcbd6b57d783737107f445ef75ecb474aNeil Fuller    editor.abort();
260e78f117bcbd6b57d783737107f445ef75ecb474aNeil Fuller    assertInoperable(editor);
261e78f117bcbd6b57d783737107f445ef75ecb474aNeil Fuller  }
262e78f117bcbd6b57d783737107f445ef75ecb474aNeil Fuller
263e78f117bcbd6b57d783737107f445ef75ecb474aNeil Fuller  @Test public void explicitRemoveAppliedToDiskImmediately() throws Exception {
264e78f117bcbd6b57d783737107f445ef75ecb474aNeil Fuller    DiskLruCache.Editor editor = cache.edit("k1");
265e78f117bcbd6b57d783737107f445ef75ecb474aNeil Fuller    setString(editor, 0, "ABC");
266e78f117bcbd6b57d783737107f445ef75ecb474aNeil Fuller    setString(editor, 1, "B");
267e78f117bcbd6b57d783737107f445ef75ecb474aNeil Fuller    editor.commit();
268e78f117bcbd6b57d783737107f445ef75ecb474aNeil Fuller    File k1 = getCleanFile("k1", 0);
269e78f117bcbd6b57d783737107f445ef75ecb474aNeil Fuller    assertEquals("ABC", readFile(k1));
270e78f117bcbd6b57d783737107f445ef75ecb474aNeil Fuller    cache.remove("k1");
271a2cab72aa5ff730ba2ae987b45398faafffeb505Neil Fuller    assertFalse(fileSystem.exists(k1));
272e78f117bcbd6b57d783737107f445ef75ecb474aNeil Fuller  }
273e78f117bcbd6b57d783737107f445ef75ecb474aNeil Fuller
274e78f117bcbd6b57d783737107f445ef75ecb474aNeil Fuller  @Test public void removePreventsActiveEditFromStoringAValue() throws Exception {
275e78f117bcbd6b57d783737107f445ef75ecb474aNeil Fuller    set("a", "a", "a");
276e78f117bcbd6b57d783737107f445ef75ecb474aNeil Fuller    DiskLruCache.Editor a = cache.edit("a");
277e78f117bcbd6b57d783737107f445ef75ecb474aNeil Fuller    setString(a, 0, "a1");
278e78f117bcbd6b57d783737107f445ef75ecb474aNeil Fuller    assertTrue(cache.remove("a"));
279e78f117bcbd6b57d783737107f445ef75ecb474aNeil Fuller    setString(a, 1, "a2");
280e78f117bcbd6b57d783737107f445ef75ecb474aNeil Fuller    a.commit();
281e78f117bcbd6b57d783737107f445ef75ecb474aNeil Fuller    assertAbsent("a");
282e78f117bcbd6b57d783737107f445ef75ecb474aNeil Fuller  }
283e78f117bcbd6b57d783737107f445ef75ecb474aNeil Fuller
284e78f117bcbd6b57d783737107f445ef75ecb474aNeil Fuller  /**
285e78f117bcbd6b57d783737107f445ef75ecb474aNeil Fuller   * Each read sees a snapshot of the file at the time read was called.
286e78f117bcbd6b57d783737107f445ef75ecb474aNeil Fuller   * This means that two reads of the same key can see different data.
287e78f117bcbd6b57d783737107f445ef75ecb474aNeil Fuller   */
288e78f117bcbd6b57d783737107f445ef75ecb474aNeil Fuller  @Test public void readAndWriteOverlapsMaintainConsistency() throws Exception {
289e78f117bcbd6b57d783737107f445ef75ecb474aNeil Fuller    DiskLruCache.Editor v1Creator = cache.edit("k1");
290e78f117bcbd6b57d783737107f445ef75ecb474aNeil Fuller    setString(v1Creator, 0, "AAaa");
291e78f117bcbd6b57d783737107f445ef75ecb474aNeil Fuller    setString(v1Creator, 1, "BBbb");
292e78f117bcbd6b57d783737107f445ef75ecb474aNeil Fuller    v1Creator.commit();
293e78f117bcbd6b57d783737107f445ef75ecb474aNeil Fuller
294e78f117bcbd6b57d783737107f445ef75ecb474aNeil Fuller    DiskLruCache.Snapshot snapshot1 = cache.get("k1");
295e78f117bcbd6b57d783737107f445ef75ecb474aNeil Fuller    BufferedSource inV1 = Okio.buffer(snapshot1.getSource(0));
296e78f117bcbd6b57d783737107f445ef75ecb474aNeil Fuller    assertEquals('A', inV1.readByte());
297e78f117bcbd6b57d783737107f445ef75ecb474aNeil Fuller    assertEquals('A', inV1.readByte());
298e78f117bcbd6b57d783737107f445ef75ecb474aNeil Fuller
299e78f117bcbd6b57d783737107f445ef75ecb474aNeil Fuller    DiskLruCache.Editor v1Updater = cache.edit("k1");
300e78f117bcbd6b57d783737107f445ef75ecb474aNeil Fuller    setString(v1Updater, 0, "CCcc");
301e78f117bcbd6b57d783737107f445ef75ecb474aNeil Fuller    setString(v1Updater, 1, "DDdd");
302e78f117bcbd6b57d783737107f445ef75ecb474aNeil Fuller    v1Updater.commit();
303e78f117bcbd6b57d783737107f445ef75ecb474aNeil Fuller
304e78f117bcbd6b57d783737107f445ef75ecb474aNeil Fuller    DiskLruCache.Snapshot snapshot2 = cache.get("k1");
305e78f117bcbd6b57d783737107f445ef75ecb474aNeil Fuller    assertSnapshotValue(snapshot2, 0, "CCcc");
306e78f117bcbd6b57d783737107f445ef75ecb474aNeil Fuller    assertSnapshotValue(snapshot2, 1, "DDdd");
307e78f117bcbd6b57d783737107f445ef75ecb474aNeil Fuller    snapshot2.close();
308e78f117bcbd6b57d783737107f445ef75ecb474aNeil Fuller
309e78f117bcbd6b57d783737107f445ef75ecb474aNeil Fuller    assertEquals('a', inV1.readByte());
310e78f117bcbd6b57d783737107f445ef75ecb474aNeil Fuller    assertEquals('a', inV1.readByte());
311e78f117bcbd6b57d783737107f445ef75ecb474aNeil Fuller    assertSnapshotValue(snapshot1, 1, "BBbb");
312e78f117bcbd6b57d783737107f445ef75ecb474aNeil Fuller    snapshot1.close();
313e78f117bcbd6b57d783737107f445ef75ecb474aNeil Fuller  }
314e78f117bcbd6b57d783737107f445ef75ecb474aNeil Fuller
315e78f117bcbd6b57d783737107f445ef75ecb474aNeil Fuller  @Test public void openWithDirtyKeyDeletesAllFilesForThatKey() throws Exception {
316e78f117bcbd6b57d783737107f445ef75ecb474aNeil Fuller    cache.close();
317e78f117bcbd6b57d783737107f445ef75ecb474aNeil Fuller    File cleanFile0 = getCleanFile("k1", 0);
318e78f117bcbd6b57d783737107f445ef75ecb474aNeil Fuller    File cleanFile1 = getCleanFile("k1", 1);
319e78f117bcbd6b57d783737107f445ef75ecb474aNeil Fuller    File dirtyFile0 = getDirtyFile("k1", 0);
320e78f117bcbd6b57d783737107f445ef75ecb474aNeil Fuller    File dirtyFile1 = getDirtyFile("k1", 1);
321e78f117bcbd6b57d783737107f445ef75ecb474aNeil Fuller    writeFile(cleanFile0, "A");
322e78f117bcbd6b57d783737107f445ef75ecb474aNeil Fuller    writeFile(cleanFile1, "B");
323e78f117bcbd6b57d783737107f445ef75ecb474aNeil Fuller    writeFile(dirtyFile0, "C");
324e78f117bcbd6b57d783737107f445ef75ecb474aNeil Fuller    writeFile(dirtyFile1, "D");
325e78f117bcbd6b57d783737107f445ef75ecb474aNeil Fuller    createJournal("CLEAN k1 1 1", "DIRTY   k1");
326e78f117bcbd6b57d783737107f445ef75ecb474aNeil Fuller    createNewCache();
327a2cab72aa5ff730ba2ae987b45398faafffeb505Neil Fuller    assertFalse(fileSystem.exists(cleanFile0));
328a2cab72aa5ff730ba2ae987b45398faafffeb505Neil Fuller    assertFalse(fileSystem.exists(cleanFile1));
329a2cab72aa5ff730ba2ae987b45398faafffeb505Neil Fuller    assertFalse(fileSystem.exists(dirtyFile0));
330a2cab72aa5ff730ba2ae987b45398faafffeb505Neil Fuller    assertFalse(fileSystem.exists(dirtyFile1));
331e78f117bcbd6b57d783737107f445ef75ecb474aNeil Fuller    assertNull(cache.get("k1"));
332e78f117bcbd6b57d783737107f445ef75ecb474aNeil Fuller  }
333e78f117bcbd6b57d783737107f445ef75ecb474aNeil Fuller
334e78f117bcbd6b57d783737107f445ef75ecb474aNeil Fuller  @Test public void openWithInvalidVersionClearsDirectory() throws Exception {
335e78f117bcbd6b57d783737107f445ef75ecb474aNeil Fuller    cache.close();
336e78f117bcbd6b57d783737107f445ef75ecb474aNeil Fuller    generateSomeGarbageFiles();
337e78f117bcbd6b57d783737107f445ef75ecb474aNeil Fuller    createJournalWithHeader(MAGIC, "0", "100", "2", "");
338e78f117bcbd6b57d783737107f445ef75ecb474aNeil Fuller    createNewCache();
339e78f117bcbd6b57d783737107f445ef75ecb474aNeil Fuller    assertGarbageFilesAllDeleted();
340e78f117bcbd6b57d783737107f445ef75ecb474aNeil Fuller  }
341e78f117bcbd6b57d783737107f445ef75ecb474aNeil Fuller
342e78f117bcbd6b57d783737107f445ef75ecb474aNeil Fuller  @Test public void openWithInvalidAppVersionClearsDirectory() throws Exception {
343e78f117bcbd6b57d783737107f445ef75ecb474aNeil Fuller    cache.close();
344e78f117bcbd6b57d783737107f445ef75ecb474aNeil Fuller    generateSomeGarbageFiles();
345e78f117bcbd6b57d783737107f445ef75ecb474aNeil Fuller    createJournalWithHeader(MAGIC, "1", "101", "2", "");
346e78f117bcbd6b57d783737107f445ef75ecb474aNeil Fuller    createNewCache();
347e78f117bcbd6b57d783737107f445ef75ecb474aNeil Fuller    assertGarbageFilesAllDeleted();
348e78f117bcbd6b57d783737107f445ef75ecb474aNeil Fuller  }
349e78f117bcbd6b57d783737107f445ef75ecb474aNeil Fuller
350e78f117bcbd6b57d783737107f445ef75ecb474aNeil Fuller  @Test public void openWithInvalidValueCountClearsDirectory() throws Exception {
351e78f117bcbd6b57d783737107f445ef75ecb474aNeil Fuller    cache.close();
352e78f117bcbd6b57d783737107f445ef75ecb474aNeil Fuller    generateSomeGarbageFiles();
353e78f117bcbd6b57d783737107f445ef75ecb474aNeil Fuller    createJournalWithHeader(MAGIC, "1", "100", "1", "");
354e78f117bcbd6b57d783737107f445ef75ecb474aNeil Fuller    createNewCache();
355e78f117bcbd6b57d783737107f445ef75ecb474aNeil Fuller    assertGarbageFilesAllDeleted();
356e78f117bcbd6b57d783737107f445ef75ecb474aNeil Fuller  }
357e78f117bcbd6b57d783737107f445ef75ecb474aNeil Fuller
358e78f117bcbd6b57d783737107f445ef75ecb474aNeil Fuller  @Test public void openWithInvalidBlankLineClearsDirectory() throws Exception {
359e78f117bcbd6b57d783737107f445ef75ecb474aNeil Fuller    cache.close();
360e78f117bcbd6b57d783737107f445ef75ecb474aNeil Fuller    generateSomeGarbageFiles();
361e78f117bcbd6b57d783737107f445ef75ecb474aNeil Fuller    createJournalWithHeader(MAGIC, "1", "100", "2", "x");
362e78f117bcbd6b57d783737107f445ef75ecb474aNeil Fuller    createNewCache();
363e78f117bcbd6b57d783737107f445ef75ecb474aNeil Fuller    assertGarbageFilesAllDeleted();
364e78f117bcbd6b57d783737107f445ef75ecb474aNeil Fuller  }
365e78f117bcbd6b57d783737107f445ef75ecb474aNeil Fuller
366e78f117bcbd6b57d783737107f445ef75ecb474aNeil Fuller  @Test public void openWithInvalidJournalLineClearsDirectory() throws Exception {
367e78f117bcbd6b57d783737107f445ef75ecb474aNeil Fuller    cache.close();
368e78f117bcbd6b57d783737107f445ef75ecb474aNeil Fuller    generateSomeGarbageFiles();
369e78f117bcbd6b57d783737107f445ef75ecb474aNeil Fuller    createJournal("CLEAN k1 1 1", "BOGUS");
370e78f117bcbd6b57d783737107f445ef75ecb474aNeil Fuller    createNewCache();
371e78f117bcbd6b57d783737107f445ef75ecb474aNeil Fuller    assertGarbageFilesAllDeleted();
372e78f117bcbd6b57d783737107f445ef75ecb474aNeil Fuller    assertNull(cache.get("k1"));
373e78f117bcbd6b57d783737107f445ef75ecb474aNeil Fuller  }
374e78f117bcbd6b57d783737107f445ef75ecb474aNeil Fuller
375e78f117bcbd6b57d783737107f445ef75ecb474aNeil Fuller  @Test public void openWithInvalidFileSizeClearsDirectory() throws Exception {
376e78f117bcbd6b57d783737107f445ef75ecb474aNeil Fuller    cache.close();
377e78f117bcbd6b57d783737107f445ef75ecb474aNeil Fuller    generateSomeGarbageFiles();
378e78f117bcbd6b57d783737107f445ef75ecb474aNeil Fuller    createJournal("CLEAN k1 0000x001 1");
379e78f117bcbd6b57d783737107f445ef75ecb474aNeil Fuller    createNewCache();
380e78f117bcbd6b57d783737107f445ef75ecb474aNeil Fuller    assertGarbageFilesAllDeleted();
381e78f117bcbd6b57d783737107f445ef75ecb474aNeil Fuller    assertNull(cache.get("k1"));
382e78f117bcbd6b57d783737107f445ef75ecb474aNeil Fuller  }
383e78f117bcbd6b57d783737107f445ef75ecb474aNeil Fuller
384e78f117bcbd6b57d783737107f445ef75ecb474aNeil Fuller  @Test public void openWithTruncatedLineDiscardsThatLine() throws Exception {
385e78f117bcbd6b57d783737107f445ef75ecb474aNeil Fuller    cache.close();
386e78f117bcbd6b57d783737107f445ef75ecb474aNeil Fuller    writeFile(getCleanFile("k1", 0), "A");
387e78f117bcbd6b57d783737107f445ef75ecb474aNeil Fuller    writeFile(getCleanFile("k1", 1), "B");
388a2cab72aa5ff730ba2ae987b45398faafffeb505Neil Fuller
389a2cab72aa5ff730ba2ae987b45398faafffeb505Neil Fuller    BufferedSink sink = Okio.buffer(fileSystem.sink(journalFile));
390a2cab72aa5ff730ba2ae987b45398faafffeb505Neil Fuller    sink.writeUtf8(MAGIC + "\n" + VERSION_1 + "\n100\n2\n\nCLEAN k1 1 1"); // no trailing newline
391a2cab72aa5ff730ba2ae987b45398faafffeb505Neil Fuller    sink.close();
392e78f117bcbd6b57d783737107f445ef75ecb474aNeil Fuller    createNewCache();
393e78f117bcbd6b57d783737107f445ef75ecb474aNeil Fuller    assertNull(cache.get("k1"));
394e78f117bcbd6b57d783737107f445ef75ecb474aNeil Fuller
395e78f117bcbd6b57d783737107f445ef75ecb474aNeil Fuller    // The journal is not corrupt when editing after a truncated line.
396e78f117bcbd6b57d783737107f445ef75ecb474aNeil Fuller    set("k1", "C", "D");
397e78f117bcbd6b57d783737107f445ef75ecb474aNeil Fuller
398e78f117bcbd6b57d783737107f445ef75ecb474aNeil Fuller    cache.close();
399e78f117bcbd6b57d783737107f445ef75ecb474aNeil Fuller    createNewCache();
400e78f117bcbd6b57d783737107f445ef75ecb474aNeil Fuller    assertValue("k1", "C", "D");
401e78f117bcbd6b57d783737107f445ef75ecb474aNeil Fuller  }
402e78f117bcbd6b57d783737107f445ef75ecb474aNeil Fuller
403e78f117bcbd6b57d783737107f445ef75ecb474aNeil Fuller  @Test public void openWithTooManyFileSizesClearsDirectory() throws Exception {
404e78f117bcbd6b57d783737107f445ef75ecb474aNeil Fuller    cache.close();
405e78f117bcbd6b57d783737107f445ef75ecb474aNeil Fuller    generateSomeGarbageFiles();
406e78f117bcbd6b57d783737107f445ef75ecb474aNeil Fuller    createJournal("CLEAN k1 1 1 1");
407e78f117bcbd6b57d783737107f445ef75ecb474aNeil Fuller    createNewCache();
408e78f117bcbd6b57d783737107f445ef75ecb474aNeil Fuller    assertGarbageFilesAllDeleted();
409e78f117bcbd6b57d783737107f445ef75ecb474aNeil Fuller    assertNull(cache.get("k1"));
410e78f117bcbd6b57d783737107f445ef75ecb474aNeil Fuller  }
411e78f117bcbd6b57d783737107f445ef75ecb474aNeil Fuller
412e78f117bcbd6b57d783737107f445ef75ecb474aNeil Fuller  @Test public void keyWithSpaceNotPermitted() throws Exception {
413e78f117bcbd6b57d783737107f445ef75ecb474aNeil Fuller    try {
414e78f117bcbd6b57d783737107f445ef75ecb474aNeil Fuller      cache.edit("my key");
415e78f117bcbd6b57d783737107f445ef75ecb474aNeil Fuller      fail();
416e78f117bcbd6b57d783737107f445ef75ecb474aNeil Fuller    } catch (IllegalArgumentException expected) {
417e78f117bcbd6b57d783737107f445ef75ecb474aNeil Fuller    }
418e78f117bcbd6b57d783737107f445ef75ecb474aNeil Fuller  }
419e78f117bcbd6b57d783737107f445ef75ecb474aNeil Fuller
420e78f117bcbd6b57d783737107f445ef75ecb474aNeil Fuller  @Test public void keyWithNewlineNotPermitted() throws Exception {
421e78f117bcbd6b57d783737107f445ef75ecb474aNeil Fuller    try {
422e78f117bcbd6b57d783737107f445ef75ecb474aNeil Fuller      cache.edit("my\nkey");
423e78f117bcbd6b57d783737107f445ef75ecb474aNeil Fuller      fail();
424e78f117bcbd6b57d783737107f445ef75ecb474aNeil Fuller    } catch (IllegalArgumentException expected) {
425e78f117bcbd6b57d783737107f445ef75ecb474aNeil Fuller    }
426e78f117bcbd6b57d783737107f445ef75ecb474aNeil Fuller  }
427e78f117bcbd6b57d783737107f445ef75ecb474aNeil Fuller
428e78f117bcbd6b57d783737107f445ef75ecb474aNeil Fuller  @Test public void keyWithCarriageReturnNotPermitted() throws Exception {
429e78f117bcbd6b57d783737107f445ef75ecb474aNeil Fuller    try {
430e78f117bcbd6b57d783737107f445ef75ecb474aNeil Fuller      cache.edit("my\rkey");
431e78f117bcbd6b57d783737107f445ef75ecb474aNeil Fuller      fail();
432e78f117bcbd6b57d783737107f445ef75ecb474aNeil Fuller    } catch (IllegalArgumentException expected) {
433e78f117bcbd6b57d783737107f445ef75ecb474aNeil Fuller    }
434e78f117bcbd6b57d783737107f445ef75ecb474aNeil Fuller  }
435e78f117bcbd6b57d783737107f445ef75ecb474aNeil Fuller
436e78f117bcbd6b57d783737107f445ef75ecb474aNeil Fuller  @Test public void nullKeyThrows() throws Exception {
437e78f117bcbd6b57d783737107f445ef75ecb474aNeil Fuller    try {
438e78f117bcbd6b57d783737107f445ef75ecb474aNeil Fuller      cache.edit(null);
439e78f117bcbd6b57d783737107f445ef75ecb474aNeil Fuller      fail();
440e78f117bcbd6b57d783737107f445ef75ecb474aNeil Fuller    } catch (NullPointerException expected) {
441e78f117bcbd6b57d783737107f445ef75ecb474aNeil Fuller    }
442e78f117bcbd6b57d783737107f445ef75ecb474aNeil Fuller  }
443e78f117bcbd6b57d783737107f445ef75ecb474aNeil Fuller
444e78f117bcbd6b57d783737107f445ef75ecb474aNeil Fuller  @Test public void createNewEntryWithTooFewValuesFails() throws Exception {
445e78f117bcbd6b57d783737107f445ef75ecb474aNeil Fuller    DiskLruCache.Editor creator = cache.edit("k1");
446e78f117bcbd6b57d783737107f445ef75ecb474aNeil Fuller    setString(creator, 1, "A");
447e78f117bcbd6b57d783737107f445ef75ecb474aNeil Fuller    try {
448e78f117bcbd6b57d783737107f445ef75ecb474aNeil Fuller      creator.commit();
449e78f117bcbd6b57d783737107f445ef75ecb474aNeil Fuller      fail();
450e78f117bcbd6b57d783737107f445ef75ecb474aNeil Fuller    } catch (IllegalStateException expected) {
451e78f117bcbd6b57d783737107f445ef75ecb474aNeil Fuller    }
452e78f117bcbd6b57d783737107f445ef75ecb474aNeil Fuller
453a2cab72aa5ff730ba2ae987b45398faafffeb505Neil Fuller    assertFalse(fileSystem.exists(getCleanFile("k1", 0)));
454a2cab72aa5ff730ba2ae987b45398faafffeb505Neil Fuller    assertFalse(fileSystem.exists(getCleanFile("k1", 1)));
455a2cab72aa5ff730ba2ae987b45398faafffeb505Neil Fuller    assertFalse(fileSystem.exists(getDirtyFile("k1", 0)));
456a2cab72aa5ff730ba2ae987b45398faafffeb505Neil Fuller    assertFalse(fileSystem.exists(getDirtyFile("k1", 1)));
457e78f117bcbd6b57d783737107f445ef75ecb474aNeil Fuller    assertNull(cache.get("k1"));
458e78f117bcbd6b57d783737107f445ef75ecb474aNeil Fuller
459e78f117bcbd6b57d783737107f445ef75ecb474aNeil Fuller    DiskLruCache.Editor creator2 = cache.edit("k1");
460e78f117bcbd6b57d783737107f445ef75ecb474aNeil Fuller    setString(creator2, 0, "B");
461e78f117bcbd6b57d783737107f445ef75ecb474aNeil Fuller    setString(creator2, 1, "C");
462e78f117bcbd6b57d783737107f445ef75ecb474aNeil Fuller    creator2.commit();
463e78f117bcbd6b57d783737107f445ef75ecb474aNeil Fuller  }
464e78f117bcbd6b57d783737107f445ef75ecb474aNeil Fuller
465e78f117bcbd6b57d783737107f445ef75ecb474aNeil Fuller  @Test public void revertWithTooFewValues() throws Exception {
466e78f117bcbd6b57d783737107f445ef75ecb474aNeil Fuller    DiskLruCache.Editor creator = cache.edit("k1");
467e78f117bcbd6b57d783737107f445ef75ecb474aNeil Fuller    setString(creator, 1, "A");
468e78f117bcbd6b57d783737107f445ef75ecb474aNeil Fuller    creator.abort();
469a2cab72aa5ff730ba2ae987b45398faafffeb505Neil Fuller    assertFalse(fileSystem.exists(getCleanFile("k1", 0)));
470a2cab72aa5ff730ba2ae987b45398faafffeb505Neil Fuller    assertFalse(fileSystem.exists(getCleanFile("k1", 1)));
471a2cab72aa5ff730ba2ae987b45398faafffeb505Neil Fuller    assertFalse(fileSystem.exists(getDirtyFile("k1", 0)));
472a2cab72aa5ff730ba2ae987b45398faafffeb505Neil Fuller    assertFalse(fileSystem.exists(getDirtyFile("k1", 1)));
473e78f117bcbd6b57d783737107f445ef75ecb474aNeil Fuller    assertNull(cache.get("k1"));
474e78f117bcbd6b57d783737107f445ef75ecb474aNeil Fuller  }
475e78f117bcbd6b57d783737107f445ef75ecb474aNeil Fuller
476e78f117bcbd6b57d783737107f445ef75ecb474aNeil Fuller  @Test public void updateExistingEntryWithTooFewValuesReusesPreviousValues() throws Exception {
477e78f117bcbd6b57d783737107f445ef75ecb474aNeil Fuller    DiskLruCache.Editor creator = cache.edit("k1");
478e78f117bcbd6b57d783737107f445ef75ecb474aNeil Fuller    setString(creator, 0, "A");
479e78f117bcbd6b57d783737107f445ef75ecb474aNeil Fuller    setString(creator, 1, "B");
480e78f117bcbd6b57d783737107f445ef75ecb474aNeil Fuller    creator.commit();
481e78f117bcbd6b57d783737107f445ef75ecb474aNeil Fuller
482e78f117bcbd6b57d783737107f445ef75ecb474aNeil Fuller    DiskLruCache.Editor updater = cache.edit("k1");
483e78f117bcbd6b57d783737107f445ef75ecb474aNeil Fuller    setString(updater, 0, "C");
484e78f117bcbd6b57d783737107f445ef75ecb474aNeil Fuller    updater.commit();
485e78f117bcbd6b57d783737107f445ef75ecb474aNeil Fuller
486e78f117bcbd6b57d783737107f445ef75ecb474aNeil Fuller    DiskLruCache.Snapshot snapshot = cache.get("k1");
487e78f117bcbd6b57d783737107f445ef75ecb474aNeil Fuller    assertSnapshotValue(snapshot, 0, "C");
488e78f117bcbd6b57d783737107f445ef75ecb474aNeil Fuller    assertSnapshotValue(snapshot, 1, "B");
489e78f117bcbd6b57d783737107f445ef75ecb474aNeil Fuller    snapshot.close();
490e78f117bcbd6b57d783737107f445ef75ecb474aNeil Fuller  }
491e78f117bcbd6b57d783737107f445ef75ecb474aNeil Fuller
492e78f117bcbd6b57d783737107f445ef75ecb474aNeil Fuller  @Test public void growMaxSize() throws Exception {
493e78f117bcbd6b57d783737107f445ef75ecb474aNeil Fuller    cache.close();
494e78f117bcbd6b57d783737107f445ef75ecb474aNeil Fuller    createNewCacheWithSize(10);
495e78f117bcbd6b57d783737107f445ef75ecb474aNeil Fuller    set("a", "a", "aaa"); // size 4
496e78f117bcbd6b57d783737107f445ef75ecb474aNeil Fuller    set("b", "bb", "bbbb"); // size 6
497e78f117bcbd6b57d783737107f445ef75ecb474aNeil Fuller    cache.setMaxSize(20);
498e78f117bcbd6b57d783737107f445ef75ecb474aNeil Fuller    set("c", "c", "c"); // size 12
499e78f117bcbd6b57d783737107f445ef75ecb474aNeil Fuller    assertEquals(12, cache.size());
500e78f117bcbd6b57d783737107f445ef75ecb474aNeil Fuller  }
501e78f117bcbd6b57d783737107f445ef75ecb474aNeil Fuller
502e78f117bcbd6b57d783737107f445ef75ecb474aNeil Fuller  @Test public void shrinkMaxSizeEvicts() throws Exception {
503e78f117bcbd6b57d783737107f445ef75ecb474aNeil Fuller    cache.close();
504e78f117bcbd6b57d783737107f445ef75ecb474aNeil Fuller    createNewCacheWithSize(20);
505e78f117bcbd6b57d783737107f445ef75ecb474aNeil Fuller    set("a", "a", "aaa"); // size 4
506e78f117bcbd6b57d783737107f445ef75ecb474aNeil Fuller    set("b", "bb", "bbbb"); // size 6
507e78f117bcbd6b57d783737107f445ef75ecb474aNeil Fuller    set("c", "c", "c"); // size 12
508e78f117bcbd6b57d783737107f445ef75ecb474aNeil Fuller    cache.setMaxSize(10);
509e78f117bcbd6b57d783737107f445ef75ecb474aNeil Fuller    assertEquals(1, executor.jobs.size());
510e78f117bcbd6b57d783737107f445ef75ecb474aNeil Fuller  }
511e78f117bcbd6b57d783737107f445ef75ecb474aNeil Fuller
512e78f117bcbd6b57d783737107f445ef75ecb474aNeil Fuller  @Test public void evictOnInsert() throws Exception {
513e78f117bcbd6b57d783737107f445ef75ecb474aNeil Fuller    cache.close();
514e78f117bcbd6b57d783737107f445ef75ecb474aNeil Fuller    createNewCacheWithSize(10);
515e78f117bcbd6b57d783737107f445ef75ecb474aNeil Fuller
516e78f117bcbd6b57d783737107f445ef75ecb474aNeil Fuller    set("a", "a", "aaa"); // size 4
517e78f117bcbd6b57d783737107f445ef75ecb474aNeil Fuller    set("b", "bb", "bbbb"); // size 6
518e78f117bcbd6b57d783737107f445ef75ecb474aNeil Fuller    assertEquals(10, cache.size());
519e78f117bcbd6b57d783737107f445ef75ecb474aNeil Fuller
520e78f117bcbd6b57d783737107f445ef75ecb474aNeil Fuller    // Cause the size to grow to 12 should evict 'A'.
521e78f117bcbd6b57d783737107f445ef75ecb474aNeil Fuller    set("c", "c", "c");
522e78f117bcbd6b57d783737107f445ef75ecb474aNeil Fuller    cache.flush();
523e78f117bcbd6b57d783737107f445ef75ecb474aNeil Fuller    assertEquals(8, cache.size());
524e78f117bcbd6b57d783737107f445ef75ecb474aNeil Fuller    assertAbsent("a");
525e78f117bcbd6b57d783737107f445ef75ecb474aNeil Fuller    assertValue("b", "bb", "bbbb");
526e78f117bcbd6b57d783737107f445ef75ecb474aNeil Fuller    assertValue("c", "c", "c");
527e78f117bcbd6b57d783737107f445ef75ecb474aNeil Fuller
528e78f117bcbd6b57d783737107f445ef75ecb474aNeil Fuller    // Causing the size to grow to 10 should evict nothing.
529e78f117bcbd6b57d783737107f445ef75ecb474aNeil Fuller    set("d", "d", "d");
530e78f117bcbd6b57d783737107f445ef75ecb474aNeil Fuller    cache.flush();
531e78f117bcbd6b57d783737107f445ef75ecb474aNeil Fuller    assertEquals(10, cache.size());
532e78f117bcbd6b57d783737107f445ef75ecb474aNeil Fuller    assertAbsent("a");
533e78f117bcbd6b57d783737107f445ef75ecb474aNeil Fuller    assertValue("b", "bb", "bbbb");
534e78f117bcbd6b57d783737107f445ef75ecb474aNeil Fuller    assertValue("c", "c", "c");
535e78f117bcbd6b57d783737107f445ef75ecb474aNeil Fuller    assertValue("d", "d", "d");
536e78f117bcbd6b57d783737107f445ef75ecb474aNeil Fuller
537e78f117bcbd6b57d783737107f445ef75ecb474aNeil Fuller    // Causing the size to grow to 18 should evict 'B' and 'C'.
538e78f117bcbd6b57d783737107f445ef75ecb474aNeil Fuller    set("e", "eeee", "eeee");
539e78f117bcbd6b57d783737107f445ef75ecb474aNeil Fuller    cache.flush();
540e78f117bcbd6b57d783737107f445ef75ecb474aNeil Fuller    assertEquals(10, cache.size());
541e78f117bcbd6b57d783737107f445ef75ecb474aNeil Fuller    assertAbsent("a");
542e78f117bcbd6b57d783737107f445ef75ecb474aNeil Fuller    assertAbsent("b");
543e78f117bcbd6b57d783737107f445ef75ecb474aNeil Fuller    assertAbsent("c");
544e78f117bcbd6b57d783737107f445ef75ecb474aNeil Fuller    assertValue("d", "d", "d");
545e78f117bcbd6b57d783737107f445ef75ecb474aNeil Fuller    assertValue("e", "eeee", "eeee");
546e78f117bcbd6b57d783737107f445ef75ecb474aNeil Fuller  }
547e78f117bcbd6b57d783737107f445ef75ecb474aNeil Fuller
548e78f117bcbd6b57d783737107f445ef75ecb474aNeil Fuller  @Test public void evictOnUpdate() throws Exception {
549e78f117bcbd6b57d783737107f445ef75ecb474aNeil Fuller    cache.close();
550e78f117bcbd6b57d783737107f445ef75ecb474aNeil Fuller    createNewCacheWithSize(10);
551e78f117bcbd6b57d783737107f445ef75ecb474aNeil Fuller
552e78f117bcbd6b57d783737107f445ef75ecb474aNeil Fuller    set("a", "a", "aa"); // size 3
553e78f117bcbd6b57d783737107f445ef75ecb474aNeil Fuller    set("b", "b", "bb"); // size 3
554e78f117bcbd6b57d783737107f445ef75ecb474aNeil Fuller    set("c", "c", "cc"); // size 3
555e78f117bcbd6b57d783737107f445ef75ecb474aNeil Fuller    assertEquals(9, cache.size());
556e78f117bcbd6b57d783737107f445ef75ecb474aNeil Fuller
557e78f117bcbd6b57d783737107f445ef75ecb474aNeil Fuller    // Causing the size to grow to 11 should evict 'A'.
558e78f117bcbd6b57d783737107f445ef75ecb474aNeil Fuller    set("b", "b", "bbbb");
559e78f117bcbd6b57d783737107f445ef75ecb474aNeil Fuller    cache.flush();
560e78f117bcbd6b57d783737107f445ef75ecb474aNeil Fuller    assertEquals(8, cache.size());
561e78f117bcbd6b57d783737107f445ef75ecb474aNeil Fuller    assertAbsent("a");
562e78f117bcbd6b57d783737107f445ef75ecb474aNeil Fuller    assertValue("b", "b", "bbbb");
563e78f117bcbd6b57d783737107f445ef75ecb474aNeil Fuller    assertValue("c", "c", "cc");
564e78f117bcbd6b57d783737107f445ef75ecb474aNeil Fuller  }
565e78f117bcbd6b57d783737107f445ef75ecb474aNeil Fuller
566e78f117bcbd6b57d783737107f445ef75ecb474aNeil Fuller  @Test public void evictionHonorsLruFromCurrentSession() throws Exception {
567e78f117bcbd6b57d783737107f445ef75ecb474aNeil Fuller    cache.close();
568e78f117bcbd6b57d783737107f445ef75ecb474aNeil Fuller    createNewCacheWithSize(10);
569e78f117bcbd6b57d783737107f445ef75ecb474aNeil Fuller    set("a", "a", "a");
570e78f117bcbd6b57d783737107f445ef75ecb474aNeil Fuller    set("b", "b", "b");
571e78f117bcbd6b57d783737107f445ef75ecb474aNeil Fuller    set("c", "c", "c");
572e78f117bcbd6b57d783737107f445ef75ecb474aNeil Fuller    set("d", "d", "d");
573e78f117bcbd6b57d783737107f445ef75ecb474aNeil Fuller    set("e", "e", "e");
574e78f117bcbd6b57d783737107f445ef75ecb474aNeil Fuller    cache.get("b").close(); // 'B' is now least recently used.
575e78f117bcbd6b57d783737107f445ef75ecb474aNeil Fuller
576e78f117bcbd6b57d783737107f445ef75ecb474aNeil Fuller    // Causing the size to grow to 12 should evict 'A'.
577e78f117bcbd6b57d783737107f445ef75ecb474aNeil Fuller    set("f", "f", "f");
578e78f117bcbd6b57d783737107f445ef75ecb474aNeil Fuller    // Causing the size to grow to 12 should evict 'C'.
579e78f117bcbd6b57d783737107f445ef75ecb474aNeil Fuller    set("g", "g", "g");
580e78f117bcbd6b57d783737107f445ef75ecb474aNeil Fuller    cache.flush();
581e78f117bcbd6b57d783737107f445ef75ecb474aNeil Fuller    assertEquals(10, cache.size());
582e78f117bcbd6b57d783737107f445ef75ecb474aNeil Fuller    assertAbsent("a");
583e78f117bcbd6b57d783737107f445ef75ecb474aNeil Fuller    assertValue("b", "b", "b");
584e78f117bcbd6b57d783737107f445ef75ecb474aNeil Fuller    assertAbsent("c");
585e78f117bcbd6b57d783737107f445ef75ecb474aNeil Fuller    assertValue("d", "d", "d");
586e78f117bcbd6b57d783737107f445ef75ecb474aNeil Fuller    assertValue("e", "e", "e");
587e78f117bcbd6b57d783737107f445ef75ecb474aNeil Fuller    assertValue("f", "f", "f");
588e78f117bcbd6b57d783737107f445ef75ecb474aNeil Fuller  }
589e78f117bcbd6b57d783737107f445ef75ecb474aNeil Fuller
590e78f117bcbd6b57d783737107f445ef75ecb474aNeil Fuller  @Test public void evictionHonorsLruFromPreviousSession() throws Exception {
591e78f117bcbd6b57d783737107f445ef75ecb474aNeil Fuller    set("a", "a", "a");
592e78f117bcbd6b57d783737107f445ef75ecb474aNeil Fuller    set("b", "b", "b");
593e78f117bcbd6b57d783737107f445ef75ecb474aNeil Fuller    set("c", "c", "c");
594e78f117bcbd6b57d783737107f445ef75ecb474aNeil Fuller    set("d", "d", "d");
595e78f117bcbd6b57d783737107f445ef75ecb474aNeil Fuller    set("e", "e", "e");
596e78f117bcbd6b57d783737107f445ef75ecb474aNeil Fuller    set("f", "f", "f");
597e78f117bcbd6b57d783737107f445ef75ecb474aNeil Fuller    cache.get("b").close(); // 'B' is now least recently used.
598e78f117bcbd6b57d783737107f445ef75ecb474aNeil Fuller    assertEquals(12, cache.size());
599e78f117bcbd6b57d783737107f445ef75ecb474aNeil Fuller    cache.close();
600e78f117bcbd6b57d783737107f445ef75ecb474aNeil Fuller    createNewCacheWithSize(10);
601e78f117bcbd6b57d783737107f445ef75ecb474aNeil Fuller
602e78f117bcbd6b57d783737107f445ef75ecb474aNeil Fuller    set("g", "g", "g");
603e78f117bcbd6b57d783737107f445ef75ecb474aNeil Fuller    cache.flush();
604e78f117bcbd6b57d783737107f445ef75ecb474aNeil Fuller    assertEquals(10, cache.size());
605e78f117bcbd6b57d783737107f445ef75ecb474aNeil Fuller    assertAbsent("a");
606e78f117bcbd6b57d783737107f445ef75ecb474aNeil Fuller    assertValue("b", "b", "b");
607e78f117bcbd6b57d783737107f445ef75ecb474aNeil Fuller    assertAbsent("c");
608e78f117bcbd6b57d783737107f445ef75ecb474aNeil Fuller    assertValue("d", "d", "d");
609e78f117bcbd6b57d783737107f445ef75ecb474aNeil Fuller    assertValue("e", "e", "e");
610e78f117bcbd6b57d783737107f445ef75ecb474aNeil Fuller    assertValue("f", "f", "f");
611e78f117bcbd6b57d783737107f445ef75ecb474aNeil Fuller    assertValue("g", "g", "g");
612e78f117bcbd6b57d783737107f445ef75ecb474aNeil Fuller  }
613e78f117bcbd6b57d783737107f445ef75ecb474aNeil Fuller
614e78f117bcbd6b57d783737107f445ef75ecb474aNeil Fuller  @Test public void cacheSingleEntryOfSizeGreaterThanMaxSize() throws Exception {
615e78f117bcbd6b57d783737107f445ef75ecb474aNeil Fuller    cache.close();
616e78f117bcbd6b57d783737107f445ef75ecb474aNeil Fuller    createNewCacheWithSize(10);
617e78f117bcbd6b57d783737107f445ef75ecb474aNeil Fuller    set("a", "aaaaa", "aaaaaa"); // size=11
618e78f117bcbd6b57d783737107f445ef75ecb474aNeil Fuller    cache.flush();
619e78f117bcbd6b57d783737107f445ef75ecb474aNeil Fuller    assertAbsent("a");
620e78f117bcbd6b57d783737107f445ef75ecb474aNeil Fuller  }
621e78f117bcbd6b57d783737107f445ef75ecb474aNeil Fuller
622e78f117bcbd6b57d783737107f445ef75ecb474aNeil Fuller  @Test public void cacheSingleValueOfSizeGreaterThanMaxSize() throws Exception {
623e78f117bcbd6b57d783737107f445ef75ecb474aNeil Fuller    cache.close();
624e78f117bcbd6b57d783737107f445ef75ecb474aNeil Fuller    createNewCacheWithSize(10);
625e78f117bcbd6b57d783737107f445ef75ecb474aNeil Fuller    set("a", "aaaaaaaaaaa", "a"); // size=12
626e78f117bcbd6b57d783737107f445ef75ecb474aNeil Fuller    cache.flush();
627e78f117bcbd6b57d783737107f445ef75ecb474aNeil Fuller    assertAbsent("a");
628e78f117bcbd6b57d783737107f445ef75ecb474aNeil Fuller  }
629e78f117bcbd6b57d783737107f445ef75ecb474aNeil Fuller
630e78f117bcbd6b57d783737107f445ef75ecb474aNeil Fuller  @Test public void constructorDoesNotAllowZeroCacheSize() throws Exception {
631e78f117bcbd6b57d783737107f445ef75ecb474aNeil Fuller    try {
632a2cab72aa5ff730ba2ae987b45398faafffeb505Neil Fuller      DiskLruCache.create(fileSystem, cacheDir, appVersion, 2, 0);
633e78f117bcbd6b57d783737107f445ef75ecb474aNeil Fuller      fail();
634e78f117bcbd6b57d783737107f445ef75ecb474aNeil Fuller    } catch (IllegalArgumentException expected) {
635e78f117bcbd6b57d783737107f445ef75ecb474aNeil Fuller    }
636e78f117bcbd6b57d783737107f445ef75ecb474aNeil Fuller  }
637e78f117bcbd6b57d783737107f445ef75ecb474aNeil Fuller
638e78f117bcbd6b57d783737107f445ef75ecb474aNeil Fuller  @Test public void constructorDoesNotAllowZeroValuesPerEntry() throws Exception {
639e78f117bcbd6b57d783737107f445ef75ecb474aNeil Fuller    try {
640a2cab72aa5ff730ba2ae987b45398faafffeb505Neil Fuller      DiskLruCache.create(fileSystem, cacheDir, appVersion, 0, 10);
641e78f117bcbd6b57d783737107f445ef75ecb474aNeil Fuller      fail();
642e78f117bcbd6b57d783737107f445ef75ecb474aNeil Fuller    } catch (IllegalArgumentException expected) {
643e78f117bcbd6b57d783737107f445ef75ecb474aNeil Fuller    }
644e78f117bcbd6b57d783737107f445ef75ecb474aNeil Fuller  }
645e78f117bcbd6b57d783737107f445ef75ecb474aNeil Fuller
646e78f117bcbd6b57d783737107f445ef75ecb474aNeil Fuller  @Test public void removeAbsentElement() throws Exception {
647e78f117bcbd6b57d783737107f445ef75ecb474aNeil Fuller    cache.remove("a");
648e78f117bcbd6b57d783737107f445ef75ecb474aNeil Fuller  }
649e78f117bcbd6b57d783737107f445ef75ecb474aNeil Fuller
650e78f117bcbd6b57d783737107f445ef75ecb474aNeil Fuller  @Test public void readingTheSameStreamMultipleTimes() throws Exception {
651e78f117bcbd6b57d783737107f445ef75ecb474aNeil Fuller    set("a", "a", "b");
652e78f117bcbd6b57d783737107f445ef75ecb474aNeil Fuller    DiskLruCache.Snapshot snapshot = cache.get("a");
653e78f117bcbd6b57d783737107f445ef75ecb474aNeil Fuller    assertSame(snapshot.getSource(0), snapshot.getSource(0));
654e78f117bcbd6b57d783737107f445ef75ecb474aNeil Fuller    snapshot.close();
655e78f117bcbd6b57d783737107f445ef75ecb474aNeil Fuller  }
656e78f117bcbd6b57d783737107f445ef75ecb474aNeil Fuller
657e78f117bcbd6b57d783737107f445ef75ecb474aNeil Fuller  @Test public void rebuildJournalOnRepeatedReads() throws Exception {
658e78f117bcbd6b57d783737107f445ef75ecb474aNeil Fuller    set("a", "a", "a");
659e78f117bcbd6b57d783737107f445ef75ecb474aNeil Fuller    set("b", "b", "b");
660e78f117bcbd6b57d783737107f445ef75ecb474aNeil Fuller    while (executor.jobs.isEmpty()) {
661e78f117bcbd6b57d783737107f445ef75ecb474aNeil Fuller      assertValue("a", "a", "a");
662e78f117bcbd6b57d783737107f445ef75ecb474aNeil Fuller      assertValue("b", "b", "b");
663e78f117bcbd6b57d783737107f445ef75ecb474aNeil Fuller    }
664e78f117bcbd6b57d783737107f445ef75ecb474aNeil Fuller  }
665e78f117bcbd6b57d783737107f445ef75ecb474aNeil Fuller
666e78f117bcbd6b57d783737107f445ef75ecb474aNeil Fuller  @Test public void rebuildJournalOnRepeatedEdits() throws Exception {
667e78f117bcbd6b57d783737107f445ef75ecb474aNeil Fuller    while (executor.jobs.isEmpty()) {
668e78f117bcbd6b57d783737107f445ef75ecb474aNeil Fuller      set("a", "a", "a");
669e78f117bcbd6b57d783737107f445ef75ecb474aNeil Fuller      set("b", "b", "b");
670e78f117bcbd6b57d783737107f445ef75ecb474aNeil Fuller    }
671e78f117bcbd6b57d783737107f445ef75ecb474aNeil Fuller    executor.jobs.removeFirst().run();
672e78f117bcbd6b57d783737107f445ef75ecb474aNeil Fuller
673e78f117bcbd6b57d783737107f445ef75ecb474aNeil Fuller    // Sanity check that a rebuilt journal behaves normally.
674e78f117bcbd6b57d783737107f445ef75ecb474aNeil Fuller    assertValue("a", "a", "a");
675e78f117bcbd6b57d783737107f445ef75ecb474aNeil Fuller    assertValue("b", "b", "b");
676e78f117bcbd6b57d783737107f445ef75ecb474aNeil Fuller  }
677e78f117bcbd6b57d783737107f445ef75ecb474aNeil Fuller
678e78f117bcbd6b57d783737107f445ef75ecb474aNeil Fuller  /** @see <a href="https://github.com/JakeWharton/DiskLruCache/issues/28">Issue #28</a> */
679e78f117bcbd6b57d783737107f445ef75ecb474aNeil Fuller  @Test public void rebuildJournalOnRepeatedReadsWithOpenAndClose() throws Exception {
680e78f117bcbd6b57d783737107f445ef75ecb474aNeil Fuller    set("a", "a", "a");
681e78f117bcbd6b57d783737107f445ef75ecb474aNeil Fuller    set("b", "b", "b");
682e78f117bcbd6b57d783737107f445ef75ecb474aNeil Fuller    while (executor.jobs.isEmpty()) {
683e78f117bcbd6b57d783737107f445ef75ecb474aNeil Fuller      assertValue("a", "a", "a");
684e78f117bcbd6b57d783737107f445ef75ecb474aNeil Fuller      assertValue("b", "b", "b");
685e78f117bcbd6b57d783737107f445ef75ecb474aNeil Fuller      cache.close();
686e78f117bcbd6b57d783737107f445ef75ecb474aNeil Fuller      createNewCache();
687e78f117bcbd6b57d783737107f445ef75ecb474aNeil Fuller    }
688e78f117bcbd6b57d783737107f445ef75ecb474aNeil Fuller  }
689e78f117bcbd6b57d783737107f445ef75ecb474aNeil Fuller
690e78f117bcbd6b57d783737107f445ef75ecb474aNeil Fuller  /** @see <a href="https://github.com/JakeWharton/DiskLruCache/issues/28">Issue #28</a> */
691e78f117bcbd6b57d783737107f445ef75ecb474aNeil Fuller  @Test public void rebuildJournalOnRepeatedEditsWithOpenAndClose() throws Exception {
692e78f117bcbd6b57d783737107f445ef75ecb474aNeil Fuller    while (executor.jobs.isEmpty()) {
693e78f117bcbd6b57d783737107f445ef75ecb474aNeil Fuller      set("a", "a", "a");
694e78f117bcbd6b57d783737107f445ef75ecb474aNeil Fuller      set("b", "b", "b");
695e78f117bcbd6b57d783737107f445ef75ecb474aNeil Fuller      cache.close();
696e78f117bcbd6b57d783737107f445ef75ecb474aNeil Fuller      createNewCache();
697e78f117bcbd6b57d783737107f445ef75ecb474aNeil Fuller    }
698e78f117bcbd6b57d783737107f445ef75ecb474aNeil Fuller  }
699e78f117bcbd6b57d783737107f445ef75ecb474aNeil Fuller
700e78f117bcbd6b57d783737107f445ef75ecb474aNeil Fuller  @Test public void restoreBackupFile() throws Exception {
701e78f117bcbd6b57d783737107f445ef75ecb474aNeil Fuller    DiskLruCache.Editor creator = cache.edit("k1");
702e78f117bcbd6b57d783737107f445ef75ecb474aNeil Fuller    setString(creator, 0, "ABC");
703e78f117bcbd6b57d783737107f445ef75ecb474aNeil Fuller    setString(creator, 1, "DE");
704e78f117bcbd6b57d783737107f445ef75ecb474aNeil Fuller    creator.commit();
705e78f117bcbd6b57d783737107f445ef75ecb474aNeil Fuller    cache.close();
706e78f117bcbd6b57d783737107f445ef75ecb474aNeil Fuller
707a2cab72aa5ff730ba2ae987b45398faafffeb505Neil Fuller    fileSystem.rename(journalFile, journalBkpFile);
708a2cab72aa5ff730ba2ae987b45398faafffeb505Neil Fuller    assertFalse(fileSystem.exists(journalFile));
709e78f117bcbd6b57d783737107f445ef75ecb474aNeil Fuller
710e78f117bcbd6b57d783737107f445ef75ecb474aNeil Fuller    createNewCache();
711e78f117bcbd6b57d783737107f445ef75ecb474aNeil Fuller
712e78f117bcbd6b57d783737107f445ef75ecb474aNeil Fuller    DiskLruCache.Snapshot snapshot = cache.get("k1");
713e78f117bcbd6b57d783737107f445ef75ecb474aNeil Fuller    assertSnapshotValue(snapshot, 0, "ABC");
714e78f117bcbd6b57d783737107f445ef75ecb474aNeil Fuller    assertSnapshotValue(snapshot, 1, "DE");
715e78f117bcbd6b57d783737107f445ef75ecb474aNeil Fuller
716a2cab72aa5ff730ba2ae987b45398faafffeb505Neil Fuller    assertFalse(fileSystem.exists(journalBkpFile));
717a2cab72aa5ff730ba2ae987b45398faafffeb505Neil Fuller    assertTrue(fileSystem.exists(journalFile));
718e78f117bcbd6b57d783737107f445ef75ecb474aNeil Fuller  }
719e78f117bcbd6b57d783737107f445ef75ecb474aNeil Fuller
720e78f117bcbd6b57d783737107f445ef75ecb474aNeil Fuller  @Test public void journalFileIsPreferredOverBackupFile() throws Exception {
721e78f117bcbd6b57d783737107f445ef75ecb474aNeil Fuller    DiskLruCache.Editor creator = cache.edit("k1");
722e78f117bcbd6b57d783737107f445ef75ecb474aNeil Fuller    setString(creator, 0, "ABC");
723e78f117bcbd6b57d783737107f445ef75ecb474aNeil Fuller    setString(creator, 1, "DE");
724e78f117bcbd6b57d783737107f445ef75ecb474aNeil Fuller    creator.commit();
725e78f117bcbd6b57d783737107f445ef75ecb474aNeil Fuller    cache.flush();
726e78f117bcbd6b57d783737107f445ef75ecb474aNeil Fuller
727e78f117bcbd6b57d783737107f445ef75ecb474aNeil Fuller    copyFile(journalFile, journalBkpFile);
728e78f117bcbd6b57d783737107f445ef75ecb474aNeil Fuller
729e78f117bcbd6b57d783737107f445ef75ecb474aNeil Fuller    creator = cache.edit("k2");
730e78f117bcbd6b57d783737107f445ef75ecb474aNeil Fuller    setString(creator, 0, "F");
731e78f117bcbd6b57d783737107f445ef75ecb474aNeil Fuller    setString(creator, 1, "GH");
732e78f117bcbd6b57d783737107f445ef75ecb474aNeil Fuller    creator.commit();
733e78f117bcbd6b57d783737107f445ef75ecb474aNeil Fuller    cache.close();
734e78f117bcbd6b57d783737107f445ef75ecb474aNeil Fuller
735a2cab72aa5ff730ba2ae987b45398faafffeb505Neil Fuller    assertTrue(fileSystem.exists(journalFile));
736a2cab72aa5ff730ba2ae987b45398faafffeb505Neil Fuller    assertTrue(fileSystem.exists(journalBkpFile));
737e78f117bcbd6b57d783737107f445ef75ecb474aNeil Fuller
738e78f117bcbd6b57d783737107f445ef75ecb474aNeil Fuller    createNewCache();
739e78f117bcbd6b57d783737107f445ef75ecb474aNeil Fuller
740e78f117bcbd6b57d783737107f445ef75ecb474aNeil Fuller    DiskLruCache.Snapshot snapshotA = cache.get("k1");
741e78f117bcbd6b57d783737107f445ef75ecb474aNeil Fuller    assertSnapshotValue(snapshotA, 0, "ABC");
742e78f117bcbd6b57d783737107f445ef75ecb474aNeil Fuller    assertSnapshotValue(snapshotA, 1, "DE");
743e78f117bcbd6b57d783737107f445ef75ecb474aNeil Fuller
744e78f117bcbd6b57d783737107f445ef75ecb474aNeil Fuller    DiskLruCache.Snapshot snapshotB = cache.get("k2");
745e78f117bcbd6b57d783737107f445ef75ecb474aNeil Fuller    assertSnapshotValue(snapshotB, 0, "F");
746e78f117bcbd6b57d783737107f445ef75ecb474aNeil Fuller    assertSnapshotValue(snapshotB, 1, "GH");
747e78f117bcbd6b57d783737107f445ef75ecb474aNeil Fuller
748a2cab72aa5ff730ba2ae987b45398faafffeb505Neil Fuller    assertFalse(fileSystem.exists(journalBkpFile));
749a2cab72aa5ff730ba2ae987b45398faafffeb505Neil Fuller    assertTrue(fileSystem.exists(journalFile));
750e78f117bcbd6b57d783737107f445ef75ecb474aNeil Fuller  }
751e78f117bcbd6b57d783737107f445ef75ecb474aNeil Fuller
752e78f117bcbd6b57d783737107f445ef75ecb474aNeil Fuller  @Test public void openCreatesDirectoryIfNecessary() throws Exception {
753e78f117bcbd6b57d783737107f445ef75ecb474aNeil Fuller    cache.close();
754e78f117bcbd6b57d783737107f445ef75ecb474aNeil Fuller    File dir = tempDir.newFolder("testOpenCreatesDirectoryIfNecessary");
755a2cab72aa5ff730ba2ae987b45398faafffeb505Neil Fuller    cache = DiskLruCache.create(fileSystem, dir, appVersion, 2, Integer.MAX_VALUE);
756e78f117bcbd6b57d783737107f445ef75ecb474aNeil Fuller    set("a", "a", "a");
757a2cab72aa5ff730ba2ae987b45398faafffeb505Neil Fuller    assertTrue(fileSystem.exists(new File(dir, "a.0")));
758a2cab72aa5ff730ba2ae987b45398faafffeb505Neil Fuller    assertTrue(fileSystem.exists(new File(dir, "a.1")));
759a2cab72aa5ff730ba2ae987b45398faafffeb505Neil Fuller    assertTrue(fileSystem.exists(new File(dir, "journal")));
760e78f117bcbd6b57d783737107f445ef75ecb474aNeil Fuller  }
761e78f117bcbd6b57d783737107f445ef75ecb474aNeil Fuller
762e78f117bcbd6b57d783737107f445ef75ecb474aNeil Fuller  @Test public void fileDeletedExternally() throws Exception {
763e78f117bcbd6b57d783737107f445ef75ecb474aNeil Fuller    set("a", "a", "a");
764a2cab72aa5ff730ba2ae987b45398faafffeb505Neil Fuller    fileSystem.delete(getCleanFile("a", 1));
765e78f117bcbd6b57d783737107f445ef75ecb474aNeil Fuller    assertNull(cache.get("a"));
766e78f117bcbd6b57d783737107f445ef75ecb474aNeil Fuller  }
767e78f117bcbd6b57d783737107f445ef75ecb474aNeil Fuller
768e78f117bcbd6b57d783737107f445ef75ecb474aNeil Fuller  @Test public void editSameVersion() throws Exception {
769e78f117bcbd6b57d783737107f445ef75ecb474aNeil Fuller    set("a", "a", "a");
770e78f117bcbd6b57d783737107f445ef75ecb474aNeil Fuller    DiskLruCache.Snapshot snapshot = cache.get("a");
771e78f117bcbd6b57d783737107f445ef75ecb474aNeil Fuller    DiskLruCache.Editor editor = snapshot.edit();
772e78f117bcbd6b57d783737107f445ef75ecb474aNeil Fuller    setString(editor, 1, "a2");
773e78f117bcbd6b57d783737107f445ef75ecb474aNeil Fuller    editor.commit();
774e78f117bcbd6b57d783737107f445ef75ecb474aNeil Fuller    assertValue("a", "a", "a2");
775e78f117bcbd6b57d783737107f445ef75ecb474aNeil Fuller  }
776e78f117bcbd6b57d783737107f445ef75ecb474aNeil Fuller
777e78f117bcbd6b57d783737107f445ef75ecb474aNeil Fuller  @Test public void editSnapshotAfterChangeAborted() throws Exception {
778e78f117bcbd6b57d783737107f445ef75ecb474aNeil Fuller    set("a", "a", "a");
779e78f117bcbd6b57d783737107f445ef75ecb474aNeil Fuller    DiskLruCache.Snapshot snapshot = cache.get("a");
780e78f117bcbd6b57d783737107f445ef75ecb474aNeil Fuller    DiskLruCache.Editor toAbort = snapshot.edit();
781e78f117bcbd6b57d783737107f445ef75ecb474aNeil Fuller    setString(toAbort, 0, "b");
782e78f117bcbd6b57d783737107f445ef75ecb474aNeil Fuller    toAbort.abort();
783e78f117bcbd6b57d783737107f445ef75ecb474aNeil Fuller    DiskLruCache.Editor editor = snapshot.edit();
784e78f117bcbd6b57d783737107f445ef75ecb474aNeil Fuller    setString(editor, 1, "a2");
785e78f117bcbd6b57d783737107f445ef75ecb474aNeil Fuller    editor.commit();
786e78f117bcbd6b57d783737107f445ef75ecb474aNeil Fuller    assertValue("a", "a", "a2");
787e78f117bcbd6b57d783737107f445ef75ecb474aNeil Fuller  }
788e78f117bcbd6b57d783737107f445ef75ecb474aNeil Fuller
789e78f117bcbd6b57d783737107f445ef75ecb474aNeil Fuller  @Test public void editSnapshotAfterChangeCommitted() throws Exception {
790e78f117bcbd6b57d783737107f445ef75ecb474aNeil Fuller    set("a", "a", "a");
791e78f117bcbd6b57d783737107f445ef75ecb474aNeil Fuller    DiskLruCache.Snapshot snapshot = cache.get("a");
792e78f117bcbd6b57d783737107f445ef75ecb474aNeil Fuller    DiskLruCache.Editor toAbort = snapshot.edit();
793e78f117bcbd6b57d783737107f445ef75ecb474aNeil Fuller    setString(toAbort, 0, "b");
794e78f117bcbd6b57d783737107f445ef75ecb474aNeil Fuller    toAbort.commit();
795e78f117bcbd6b57d783737107f445ef75ecb474aNeil Fuller    assertNull(snapshot.edit());
796e78f117bcbd6b57d783737107f445ef75ecb474aNeil Fuller  }
797e78f117bcbd6b57d783737107f445ef75ecb474aNeil Fuller
798e78f117bcbd6b57d783737107f445ef75ecb474aNeil Fuller  @Test public void editSinceEvicted() throws Exception {
799e78f117bcbd6b57d783737107f445ef75ecb474aNeil Fuller    cache.close();
800e78f117bcbd6b57d783737107f445ef75ecb474aNeil Fuller    createNewCacheWithSize(10);
801e78f117bcbd6b57d783737107f445ef75ecb474aNeil Fuller    set("a", "aa", "aaa"); // size 5
802e78f117bcbd6b57d783737107f445ef75ecb474aNeil Fuller    DiskLruCache.Snapshot snapshot = cache.get("a");
803e78f117bcbd6b57d783737107f445ef75ecb474aNeil Fuller    set("b", "bb", "bbb"); // size 5
804e78f117bcbd6b57d783737107f445ef75ecb474aNeil Fuller    set("c", "cc", "ccc"); // size 5; will evict 'A'
805e78f117bcbd6b57d783737107f445ef75ecb474aNeil Fuller    cache.flush();
806e78f117bcbd6b57d783737107f445ef75ecb474aNeil Fuller    assertNull(snapshot.edit());
807e78f117bcbd6b57d783737107f445ef75ecb474aNeil Fuller  }
808e78f117bcbd6b57d783737107f445ef75ecb474aNeil Fuller
809e78f117bcbd6b57d783737107f445ef75ecb474aNeil Fuller  @Test public void editSinceEvictedAndRecreated() throws Exception {
810e78f117bcbd6b57d783737107f445ef75ecb474aNeil Fuller    cache.close();
811e78f117bcbd6b57d783737107f445ef75ecb474aNeil Fuller    createNewCacheWithSize(10);
812e78f117bcbd6b57d783737107f445ef75ecb474aNeil Fuller    set("a", "aa", "aaa"); // size 5
813e78f117bcbd6b57d783737107f445ef75ecb474aNeil Fuller    DiskLruCache.Snapshot snapshot = cache.get("a");
814e78f117bcbd6b57d783737107f445ef75ecb474aNeil Fuller    set("b", "bb", "bbb"); // size 5
815e78f117bcbd6b57d783737107f445ef75ecb474aNeil Fuller    set("c", "cc", "ccc"); // size 5; will evict 'A'
816e78f117bcbd6b57d783737107f445ef75ecb474aNeil Fuller    set("a", "a", "aaaa"); // size 5; will evict 'B'
817e78f117bcbd6b57d783737107f445ef75ecb474aNeil Fuller    cache.flush();
818e78f117bcbd6b57d783737107f445ef75ecb474aNeil Fuller    assertNull(snapshot.edit());
819e78f117bcbd6b57d783737107f445ef75ecb474aNeil Fuller  }
820e78f117bcbd6b57d783737107f445ef75ecb474aNeil Fuller
821e78f117bcbd6b57d783737107f445ef75ecb474aNeil Fuller  /** @see <a href="https://github.com/JakeWharton/DiskLruCache/issues/2">Issue #2</a> */
822e78f117bcbd6b57d783737107f445ef75ecb474aNeil Fuller  @Test public void aggressiveClearingHandlesWrite() throws Exception {
823a2cab72aa5ff730ba2ae987b45398faafffeb505Neil Fuller    fileSystem.deleteContents(tempDir.getRoot());
824e78f117bcbd6b57d783737107f445ef75ecb474aNeil Fuller    set("a", "a", "a");
825e78f117bcbd6b57d783737107f445ef75ecb474aNeil Fuller    assertValue("a", "a", "a");
826e78f117bcbd6b57d783737107f445ef75ecb474aNeil Fuller  }
827e78f117bcbd6b57d783737107f445ef75ecb474aNeil Fuller
828e78f117bcbd6b57d783737107f445ef75ecb474aNeil Fuller  /** @see <a href="https://github.com/JakeWharton/DiskLruCache/issues/2">Issue #2</a> */
829e78f117bcbd6b57d783737107f445ef75ecb474aNeil Fuller  @Test public void aggressiveClearingHandlesEdit() throws Exception {
830e78f117bcbd6b57d783737107f445ef75ecb474aNeil Fuller    set("a", "a", "a");
831e78f117bcbd6b57d783737107f445ef75ecb474aNeil Fuller    DiskLruCache.Editor a = cache.get("a").edit();
832a2cab72aa5ff730ba2ae987b45398faafffeb505Neil Fuller    fileSystem.deleteContents(tempDir.getRoot());
833e78f117bcbd6b57d783737107f445ef75ecb474aNeil Fuller    setString(a, 1, "a2");
834e78f117bcbd6b57d783737107f445ef75ecb474aNeil Fuller    a.commit();
835e78f117bcbd6b57d783737107f445ef75ecb474aNeil Fuller  }
836e78f117bcbd6b57d783737107f445ef75ecb474aNeil Fuller
837e78f117bcbd6b57d783737107f445ef75ecb474aNeil Fuller  @Test public void removeHandlesMissingFile() throws Exception {
838e78f117bcbd6b57d783737107f445ef75ecb474aNeil Fuller    set("a", "a", "a");
839e78f117bcbd6b57d783737107f445ef75ecb474aNeil Fuller    getCleanFile("a", 0).delete();
840e78f117bcbd6b57d783737107f445ef75ecb474aNeil Fuller    cache.remove("a");
841e78f117bcbd6b57d783737107f445ef75ecb474aNeil Fuller  }
842e78f117bcbd6b57d783737107f445ef75ecb474aNeil Fuller
843e78f117bcbd6b57d783737107f445ef75ecb474aNeil Fuller  /** @see <a href="https://github.com/JakeWharton/DiskLruCache/issues/2">Issue #2</a> */
844e78f117bcbd6b57d783737107f445ef75ecb474aNeil Fuller  @Test public void aggressiveClearingHandlesPartialEdit() throws Exception {
845e78f117bcbd6b57d783737107f445ef75ecb474aNeil Fuller    set("a", "a", "a");
846e78f117bcbd6b57d783737107f445ef75ecb474aNeil Fuller    set("b", "b", "b");
847e78f117bcbd6b57d783737107f445ef75ecb474aNeil Fuller    DiskLruCache.Editor a = cache.get("a").edit();
848e78f117bcbd6b57d783737107f445ef75ecb474aNeil Fuller    setString(a, 0, "a1");
849a2cab72aa5ff730ba2ae987b45398faafffeb505Neil Fuller    fileSystem.deleteContents(tempDir.getRoot());
850e78f117bcbd6b57d783737107f445ef75ecb474aNeil Fuller    setString(a, 1, "a2");
851e78f117bcbd6b57d783737107f445ef75ecb474aNeil Fuller    a.commit();
852e78f117bcbd6b57d783737107f445ef75ecb474aNeil Fuller    assertNull(cache.get("a"));
853e78f117bcbd6b57d783737107f445ef75ecb474aNeil Fuller  }
854e78f117bcbd6b57d783737107f445ef75ecb474aNeil Fuller
855e78f117bcbd6b57d783737107f445ef75ecb474aNeil Fuller  /** @see <a href="https://github.com/JakeWharton/DiskLruCache/issues/2">Issue #2</a> */
856e78f117bcbd6b57d783737107f445ef75ecb474aNeil Fuller  @Test public void aggressiveClearingHandlesRead() throws Exception {
857a2cab72aa5ff730ba2ae987b45398faafffeb505Neil Fuller    fileSystem.deleteContents(tempDir.getRoot());
858e78f117bcbd6b57d783737107f445ef75ecb474aNeil Fuller    assertNull(cache.get("a"));
859e78f117bcbd6b57d783737107f445ef75ecb474aNeil Fuller  }
860e78f117bcbd6b57d783737107f445ef75ecb474aNeil Fuller
861e78f117bcbd6b57d783737107f445ef75ecb474aNeil Fuller  /**
862e78f117bcbd6b57d783737107f445ef75ecb474aNeil Fuller   * We had a long-lived bug where {@link DiskLruCache#trimToSize} could
863e78f117bcbd6b57d783737107f445ef75ecb474aNeil Fuller   * infinite loop if entries being edited required deletion for the operation
864e78f117bcbd6b57d783737107f445ef75ecb474aNeil Fuller   * to complete.
865e78f117bcbd6b57d783737107f445ef75ecb474aNeil Fuller   */
866e78f117bcbd6b57d783737107f445ef75ecb474aNeil Fuller  @Test public void trimToSizeWithActiveEdit() throws Exception {
867e78f117bcbd6b57d783737107f445ef75ecb474aNeil Fuller    set("a", "a1234", "a1234");
868e78f117bcbd6b57d783737107f445ef75ecb474aNeil Fuller    DiskLruCache.Editor a = cache.edit("a");
869e78f117bcbd6b57d783737107f445ef75ecb474aNeil Fuller    setString(a, 0, "a123");
870e78f117bcbd6b57d783737107f445ef75ecb474aNeil Fuller
871e78f117bcbd6b57d783737107f445ef75ecb474aNeil Fuller    cache.setMaxSize(8); // Smaller than the sum of active edits!
872e78f117bcbd6b57d783737107f445ef75ecb474aNeil Fuller    cache.flush(); // Force trimToSize().
873e78f117bcbd6b57d783737107f445ef75ecb474aNeil Fuller    assertEquals(0, cache.size());
874e78f117bcbd6b57d783737107f445ef75ecb474aNeil Fuller    assertNull(cache.get("a"));
875e78f117bcbd6b57d783737107f445ef75ecb474aNeil Fuller
876e78f117bcbd6b57d783737107f445ef75ecb474aNeil Fuller    // After the edit is completed, its entry is still gone.
877e78f117bcbd6b57d783737107f445ef75ecb474aNeil Fuller    setString(a, 1, "a1");
878e78f117bcbd6b57d783737107f445ef75ecb474aNeil Fuller    a.commit();
879e78f117bcbd6b57d783737107f445ef75ecb474aNeil Fuller    assertAbsent("a");
880e78f117bcbd6b57d783737107f445ef75ecb474aNeil Fuller    assertEquals(0, cache.size());
881e78f117bcbd6b57d783737107f445ef75ecb474aNeil Fuller  }
882e78f117bcbd6b57d783737107f445ef75ecb474aNeil Fuller
883e78f117bcbd6b57d783737107f445ef75ecb474aNeil Fuller  @Test public void evictAll() throws Exception {
884e78f117bcbd6b57d783737107f445ef75ecb474aNeil Fuller    set("a", "a", "a");
885e78f117bcbd6b57d783737107f445ef75ecb474aNeil Fuller    set("b", "b", "b");
886e78f117bcbd6b57d783737107f445ef75ecb474aNeil Fuller    cache.evictAll();
887e78f117bcbd6b57d783737107f445ef75ecb474aNeil Fuller    assertEquals(0, cache.size());
888e78f117bcbd6b57d783737107f445ef75ecb474aNeil Fuller    assertAbsent("a");
889e78f117bcbd6b57d783737107f445ef75ecb474aNeil Fuller    assertAbsent("b");
890e78f117bcbd6b57d783737107f445ef75ecb474aNeil Fuller  }
891e78f117bcbd6b57d783737107f445ef75ecb474aNeil Fuller
892e78f117bcbd6b57d783737107f445ef75ecb474aNeil Fuller  @Test public void evictAllWithPartialCreate() throws Exception {
893e78f117bcbd6b57d783737107f445ef75ecb474aNeil Fuller    DiskLruCache.Editor a = cache.edit("a");
894e78f117bcbd6b57d783737107f445ef75ecb474aNeil Fuller    setString(a, 0, "a1");
895e78f117bcbd6b57d783737107f445ef75ecb474aNeil Fuller    setString(a, 1, "a2");
896e78f117bcbd6b57d783737107f445ef75ecb474aNeil Fuller    cache.evictAll();
897e78f117bcbd6b57d783737107f445ef75ecb474aNeil Fuller    assertEquals(0, cache.size());
898e78f117bcbd6b57d783737107f445ef75ecb474aNeil Fuller    a.commit();
899e78f117bcbd6b57d783737107f445ef75ecb474aNeil Fuller    assertAbsent("a");
900e78f117bcbd6b57d783737107f445ef75ecb474aNeil Fuller  }
901e78f117bcbd6b57d783737107f445ef75ecb474aNeil Fuller
902e78f117bcbd6b57d783737107f445ef75ecb474aNeil Fuller  @Test public void evictAllWithPartialEditDoesNotStoreAValue() throws Exception {
903e78f117bcbd6b57d783737107f445ef75ecb474aNeil Fuller    set("a", "a", "a");
904e78f117bcbd6b57d783737107f445ef75ecb474aNeil Fuller    DiskLruCache.Editor a = cache.edit("a");
905e78f117bcbd6b57d783737107f445ef75ecb474aNeil Fuller    setString(a, 0, "a1");
906e78f117bcbd6b57d783737107f445ef75ecb474aNeil Fuller    setString(a, 1, "a2");
907e78f117bcbd6b57d783737107f445ef75ecb474aNeil Fuller    cache.evictAll();
908e78f117bcbd6b57d783737107f445ef75ecb474aNeil Fuller    assertEquals(0, cache.size());
909e78f117bcbd6b57d783737107f445ef75ecb474aNeil Fuller    a.commit();
910e78f117bcbd6b57d783737107f445ef75ecb474aNeil Fuller    assertAbsent("a");
911e78f117bcbd6b57d783737107f445ef75ecb474aNeil Fuller  }
912e78f117bcbd6b57d783737107f445ef75ecb474aNeil Fuller
913e78f117bcbd6b57d783737107f445ef75ecb474aNeil Fuller  @Test public void evictAllDoesntInterruptPartialRead() throws Exception {
914e78f117bcbd6b57d783737107f445ef75ecb474aNeil Fuller    set("a", "a", "a");
915e78f117bcbd6b57d783737107f445ef75ecb474aNeil Fuller    DiskLruCache.Snapshot a = cache.get("a");
916e78f117bcbd6b57d783737107f445ef75ecb474aNeil Fuller    assertSnapshotValue(a, 0, "a");
917e78f117bcbd6b57d783737107f445ef75ecb474aNeil Fuller    cache.evictAll();
918e78f117bcbd6b57d783737107f445ef75ecb474aNeil Fuller    assertEquals(0, cache.size());
919e78f117bcbd6b57d783737107f445ef75ecb474aNeil Fuller    assertAbsent("a");
920e78f117bcbd6b57d783737107f445ef75ecb474aNeil Fuller    assertSnapshotValue(a, 1, "a");
921e78f117bcbd6b57d783737107f445ef75ecb474aNeil Fuller    a.close();
922e78f117bcbd6b57d783737107f445ef75ecb474aNeil Fuller  }
923e78f117bcbd6b57d783737107f445ef75ecb474aNeil Fuller
924e78f117bcbd6b57d783737107f445ef75ecb474aNeil Fuller  @Test public void editSnapshotAfterEvictAllReturnsNullDueToStaleValue() throws Exception {
925e78f117bcbd6b57d783737107f445ef75ecb474aNeil Fuller    set("a", "a", "a");
926e78f117bcbd6b57d783737107f445ef75ecb474aNeil Fuller    DiskLruCache.Snapshot a = cache.get("a");
927e78f117bcbd6b57d783737107f445ef75ecb474aNeil Fuller    cache.evictAll();
928e78f117bcbd6b57d783737107f445ef75ecb474aNeil Fuller    assertEquals(0, cache.size());
929e78f117bcbd6b57d783737107f445ef75ecb474aNeil Fuller    assertAbsent("a");
930e78f117bcbd6b57d783737107f445ef75ecb474aNeil Fuller    assertNull(a.edit());
931e78f117bcbd6b57d783737107f445ef75ecb474aNeil Fuller    a.close();
932e78f117bcbd6b57d783737107f445ef75ecb474aNeil Fuller  }
933e78f117bcbd6b57d783737107f445ef75ecb474aNeil Fuller
934e78f117bcbd6b57d783737107f445ef75ecb474aNeil Fuller  @Test public void iterator() throws Exception {
935e78f117bcbd6b57d783737107f445ef75ecb474aNeil Fuller    set("a", "a1", "a2");
936e78f117bcbd6b57d783737107f445ef75ecb474aNeil Fuller    set("b", "b1", "b2");
937e78f117bcbd6b57d783737107f445ef75ecb474aNeil Fuller    set("c", "c1", "c2");
938e78f117bcbd6b57d783737107f445ef75ecb474aNeil Fuller    Iterator<DiskLruCache.Snapshot> iterator = cache.snapshots();
939e78f117bcbd6b57d783737107f445ef75ecb474aNeil Fuller
940e78f117bcbd6b57d783737107f445ef75ecb474aNeil Fuller    assertTrue(iterator.hasNext());
941e78f117bcbd6b57d783737107f445ef75ecb474aNeil Fuller    DiskLruCache.Snapshot a = iterator.next();
942e78f117bcbd6b57d783737107f445ef75ecb474aNeil Fuller    assertEquals("a", a.key());
943e78f117bcbd6b57d783737107f445ef75ecb474aNeil Fuller    assertSnapshotValue(a, 0, "a1");
944e78f117bcbd6b57d783737107f445ef75ecb474aNeil Fuller    assertSnapshotValue(a, 1, "a2");
945e78f117bcbd6b57d783737107f445ef75ecb474aNeil Fuller    a.close();
946e78f117bcbd6b57d783737107f445ef75ecb474aNeil Fuller
947e78f117bcbd6b57d783737107f445ef75ecb474aNeil Fuller    assertTrue(iterator.hasNext());
948e78f117bcbd6b57d783737107f445ef75ecb474aNeil Fuller    DiskLruCache.Snapshot b = iterator.next();
949e78f117bcbd6b57d783737107f445ef75ecb474aNeil Fuller    assertEquals("b", b.key());
950e78f117bcbd6b57d783737107f445ef75ecb474aNeil Fuller    assertSnapshotValue(b, 0, "b1");
951e78f117bcbd6b57d783737107f445ef75ecb474aNeil Fuller    assertSnapshotValue(b, 1, "b2");
952e78f117bcbd6b57d783737107f445ef75ecb474aNeil Fuller    b.close();
953e78f117bcbd6b57d783737107f445ef75ecb474aNeil Fuller
954e78f117bcbd6b57d783737107f445ef75ecb474aNeil Fuller    assertTrue(iterator.hasNext());
955e78f117bcbd6b57d783737107f445ef75ecb474aNeil Fuller    DiskLruCache.Snapshot c = iterator.next();
956e78f117bcbd6b57d783737107f445ef75ecb474aNeil Fuller    assertEquals("c", c.key());
957e78f117bcbd6b57d783737107f445ef75ecb474aNeil Fuller    assertSnapshotValue(c, 0, "c1");
958e78f117bcbd6b57d783737107f445ef75ecb474aNeil Fuller    assertSnapshotValue(c, 1, "c2");
959e78f117bcbd6b57d783737107f445ef75ecb474aNeil Fuller    c.close();
960e78f117bcbd6b57d783737107f445ef75ecb474aNeil Fuller
961e78f117bcbd6b57d783737107f445ef75ecb474aNeil Fuller    assertFalse(iterator.hasNext());
962e78f117bcbd6b57d783737107f445ef75ecb474aNeil Fuller    try {
963e78f117bcbd6b57d783737107f445ef75ecb474aNeil Fuller      iterator.next();
964e78f117bcbd6b57d783737107f445ef75ecb474aNeil Fuller      fail();
965e78f117bcbd6b57d783737107f445ef75ecb474aNeil Fuller    } catch (NoSuchElementException expected) {
966e78f117bcbd6b57d783737107f445ef75ecb474aNeil Fuller    }
967e78f117bcbd6b57d783737107f445ef75ecb474aNeil Fuller  }
968e78f117bcbd6b57d783737107f445ef75ecb474aNeil Fuller
969e78f117bcbd6b57d783737107f445ef75ecb474aNeil Fuller  @Test public void iteratorElementsAddedDuringIterationAreOmitted() throws Exception {
970e78f117bcbd6b57d783737107f445ef75ecb474aNeil Fuller    set("a", "a1", "a2");
971e78f117bcbd6b57d783737107f445ef75ecb474aNeil Fuller    set("b", "b1", "b2");
972e78f117bcbd6b57d783737107f445ef75ecb474aNeil Fuller    Iterator<DiskLruCache.Snapshot> iterator = cache.snapshots();
973e78f117bcbd6b57d783737107f445ef75ecb474aNeil Fuller
974e78f117bcbd6b57d783737107f445ef75ecb474aNeil Fuller    DiskLruCache.Snapshot a = iterator.next();
975e78f117bcbd6b57d783737107f445ef75ecb474aNeil Fuller    assertEquals("a", a.key());
976e78f117bcbd6b57d783737107f445ef75ecb474aNeil Fuller    a.close();
977e78f117bcbd6b57d783737107f445ef75ecb474aNeil Fuller
978e78f117bcbd6b57d783737107f445ef75ecb474aNeil Fuller    set("c", "c1", "c2");
979e78f117bcbd6b57d783737107f445ef75ecb474aNeil Fuller
980e78f117bcbd6b57d783737107f445ef75ecb474aNeil Fuller    DiskLruCache.Snapshot b = iterator.next();
981e78f117bcbd6b57d783737107f445ef75ecb474aNeil Fuller    assertEquals("b", b.key());
982e78f117bcbd6b57d783737107f445ef75ecb474aNeil Fuller    b.close();
983e78f117bcbd6b57d783737107f445ef75ecb474aNeil Fuller
984e78f117bcbd6b57d783737107f445ef75ecb474aNeil Fuller    assertFalse(iterator.hasNext());
985e78f117bcbd6b57d783737107f445ef75ecb474aNeil Fuller  }
986e78f117bcbd6b57d783737107f445ef75ecb474aNeil Fuller
987e78f117bcbd6b57d783737107f445ef75ecb474aNeil Fuller  @Test public void iteratorElementsUpdatedDuringIterationAreUpdated() throws Exception {
988e78f117bcbd6b57d783737107f445ef75ecb474aNeil Fuller    set("a", "a1", "a2");
989e78f117bcbd6b57d783737107f445ef75ecb474aNeil Fuller    set("b", "b1", "b2");
990e78f117bcbd6b57d783737107f445ef75ecb474aNeil Fuller    Iterator<DiskLruCache.Snapshot> iterator = cache.snapshots();
991e78f117bcbd6b57d783737107f445ef75ecb474aNeil Fuller
992e78f117bcbd6b57d783737107f445ef75ecb474aNeil Fuller    DiskLruCache.Snapshot a = iterator.next();
993e78f117bcbd6b57d783737107f445ef75ecb474aNeil Fuller    assertEquals("a", a.key());
994e78f117bcbd6b57d783737107f445ef75ecb474aNeil Fuller    a.close();
995e78f117bcbd6b57d783737107f445ef75ecb474aNeil Fuller
996e78f117bcbd6b57d783737107f445ef75ecb474aNeil Fuller    set("b", "b3", "b4");
997e78f117bcbd6b57d783737107f445ef75ecb474aNeil Fuller
998e78f117bcbd6b57d783737107f445ef75ecb474aNeil Fuller    DiskLruCache.Snapshot b = iterator.next();
999e78f117bcbd6b57d783737107f445ef75ecb474aNeil Fuller    assertEquals("b", b.key());
1000e78f117bcbd6b57d783737107f445ef75ecb474aNeil Fuller    assertSnapshotValue(b, 0, "b3");
1001e78f117bcbd6b57d783737107f445ef75ecb474aNeil Fuller    assertSnapshotValue(b, 1, "b4");
1002e78f117bcbd6b57d783737107f445ef75ecb474aNeil Fuller    b.close();
1003e78f117bcbd6b57d783737107f445ef75ecb474aNeil Fuller  }
1004e78f117bcbd6b57d783737107f445ef75ecb474aNeil Fuller
1005e78f117bcbd6b57d783737107f445ef75ecb474aNeil Fuller  @Test public void iteratorElementsRemovedDuringIterationAreOmitted() throws Exception {
1006e78f117bcbd6b57d783737107f445ef75ecb474aNeil Fuller    set("a", "a1", "a2");
1007e78f117bcbd6b57d783737107f445ef75ecb474aNeil Fuller    set("b", "b1", "b2");
1008e78f117bcbd6b57d783737107f445ef75ecb474aNeil Fuller    Iterator<DiskLruCache.Snapshot> iterator = cache.snapshots();
1009e78f117bcbd6b57d783737107f445ef75ecb474aNeil Fuller
1010e78f117bcbd6b57d783737107f445ef75ecb474aNeil Fuller    cache.remove("b");
1011e78f117bcbd6b57d783737107f445ef75ecb474aNeil Fuller
1012e78f117bcbd6b57d783737107f445ef75ecb474aNeil Fuller    DiskLruCache.Snapshot a = iterator.next();
1013e78f117bcbd6b57d783737107f445ef75ecb474aNeil Fuller    assertEquals("a", a.key());
1014e78f117bcbd6b57d783737107f445ef75ecb474aNeil Fuller    a.close();
1015e78f117bcbd6b57d783737107f445ef75ecb474aNeil Fuller
1016e78f117bcbd6b57d783737107f445ef75ecb474aNeil Fuller    assertFalse(iterator.hasNext());
1017e78f117bcbd6b57d783737107f445ef75ecb474aNeil Fuller  }
1018e78f117bcbd6b57d783737107f445ef75ecb474aNeil Fuller
1019e78f117bcbd6b57d783737107f445ef75ecb474aNeil Fuller  @Test public void iteratorRemove() throws Exception {
1020e78f117bcbd6b57d783737107f445ef75ecb474aNeil Fuller    set("a", "a1", "a2");
1021e78f117bcbd6b57d783737107f445ef75ecb474aNeil Fuller    Iterator<DiskLruCache.Snapshot> iterator = cache.snapshots();
1022e78f117bcbd6b57d783737107f445ef75ecb474aNeil Fuller
1023e78f117bcbd6b57d783737107f445ef75ecb474aNeil Fuller    DiskLruCache.Snapshot a = iterator.next();
1024e78f117bcbd6b57d783737107f445ef75ecb474aNeil Fuller    a.close();
1025e78f117bcbd6b57d783737107f445ef75ecb474aNeil Fuller    iterator.remove();
1026e78f117bcbd6b57d783737107f445ef75ecb474aNeil Fuller
1027e78f117bcbd6b57d783737107f445ef75ecb474aNeil Fuller    assertEquals(null, cache.get("a"));
1028e78f117bcbd6b57d783737107f445ef75ecb474aNeil Fuller  }
1029e78f117bcbd6b57d783737107f445ef75ecb474aNeil Fuller
1030e78f117bcbd6b57d783737107f445ef75ecb474aNeil Fuller  @Test public void iteratorRemoveBeforeNext() throws Exception {
1031e78f117bcbd6b57d783737107f445ef75ecb474aNeil Fuller    set("a", "a1", "a2");
1032e78f117bcbd6b57d783737107f445ef75ecb474aNeil Fuller    Iterator<DiskLruCache.Snapshot> iterator = cache.snapshots();
1033e78f117bcbd6b57d783737107f445ef75ecb474aNeil Fuller    try {
1034e78f117bcbd6b57d783737107f445ef75ecb474aNeil Fuller      iterator.remove();
1035e78f117bcbd6b57d783737107f445ef75ecb474aNeil Fuller      fail();
1036e78f117bcbd6b57d783737107f445ef75ecb474aNeil Fuller    } catch (IllegalStateException expected) {
1037e78f117bcbd6b57d783737107f445ef75ecb474aNeil Fuller    }
1038e78f117bcbd6b57d783737107f445ef75ecb474aNeil Fuller  }
1039e78f117bcbd6b57d783737107f445ef75ecb474aNeil Fuller
1040e78f117bcbd6b57d783737107f445ef75ecb474aNeil Fuller  @Test public void iteratorRemoveOncePerCallToNext() throws Exception {
1041e78f117bcbd6b57d783737107f445ef75ecb474aNeil Fuller    set("a", "a1", "a2");
1042e78f117bcbd6b57d783737107f445ef75ecb474aNeil Fuller    Iterator<DiskLruCache.Snapshot> iterator = cache.snapshots();
1043e78f117bcbd6b57d783737107f445ef75ecb474aNeil Fuller
1044e78f117bcbd6b57d783737107f445ef75ecb474aNeil Fuller    DiskLruCache.Snapshot a = iterator.next();
1045e78f117bcbd6b57d783737107f445ef75ecb474aNeil Fuller    iterator.remove();
1046e78f117bcbd6b57d783737107f445ef75ecb474aNeil Fuller    a.close();
1047e78f117bcbd6b57d783737107f445ef75ecb474aNeil Fuller
1048e78f117bcbd6b57d783737107f445ef75ecb474aNeil Fuller    try {
1049e78f117bcbd6b57d783737107f445ef75ecb474aNeil Fuller      iterator.remove();
1050e78f117bcbd6b57d783737107f445ef75ecb474aNeil Fuller      fail();
1051e78f117bcbd6b57d783737107f445ef75ecb474aNeil Fuller    } catch (IllegalStateException expected) {
1052e78f117bcbd6b57d783737107f445ef75ecb474aNeil Fuller    }
1053e78f117bcbd6b57d783737107f445ef75ecb474aNeil Fuller  }
1054e78f117bcbd6b57d783737107f445ef75ecb474aNeil Fuller
1055e78f117bcbd6b57d783737107f445ef75ecb474aNeil Fuller  @Test public void cacheClosedTruncatesIterator() throws Exception {
1056e78f117bcbd6b57d783737107f445ef75ecb474aNeil Fuller    set("a", "a1", "a2");
1057e78f117bcbd6b57d783737107f445ef75ecb474aNeil Fuller    Iterator<DiskLruCache.Snapshot> iterator = cache.snapshots();
1058e78f117bcbd6b57d783737107f445ef75ecb474aNeil Fuller    cache.close();
1059e78f117bcbd6b57d783737107f445ef75ecb474aNeil Fuller    assertFalse(iterator.hasNext());
1060e78f117bcbd6b57d783737107f445ef75ecb474aNeil Fuller  }
1061e78f117bcbd6b57d783737107f445ef75ecb474aNeil Fuller
1062e78f117bcbd6b57d783737107f445ef75ecb474aNeil Fuller  @Test public void isClosed_uninitializedCache() throws Exception {
1063e78f117bcbd6b57d783737107f445ef75ecb474aNeil Fuller    // Create an uninitialized cache.
1064a2cab72aa5ff730ba2ae987b45398faafffeb505Neil Fuller    cache = new DiskLruCache(fileSystem, cacheDir, appVersion, 2, Integer.MAX_VALUE, executor);
1065e78f117bcbd6b57d783737107f445ef75ecb474aNeil Fuller    toClose.add(cache);
1066e78f117bcbd6b57d783737107f445ef75ecb474aNeil Fuller
1067e78f117bcbd6b57d783737107f445ef75ecb474aNeil Fuller    assertFalse(cache.isClosed());
1068e78f117bcbd6b57d783737107f445ef75ecb474aNeil Fuller    cache.close();
1069e78f117bcbd6b57d783737107f445ef75ecb474aNeil Fuller    assertTrue(cache.isClosed());
1070e78f117bcbd6b57d783737107f445ef75ecb474aNeil Fuller  }
1071e78f117bcbd6b57d783737107f445ef75ecb474aNeil Fuller
1072a2cab72aa5ff730ba2ae987b45398faafffeb505Neil Fuller  @Test public void journalWriteFailsDuringEdit() throws Exception {
1073a2cab72aa5ff730ba2ae987b45398faafffeb505Neil Fuller    set("a", "a", "a");
1074a2cab72aa5ff730ba2ae987b45398faafffeb505Neil Fuller    set("b", "b", "b");
1075a2cab72aa5ff730ba2ae987b45398faafffeb505Neil Fuller
1076a2cab72aa5ff730ba2ae987b45398faafffeb505Neil Fuller    // We can't begin the edit if writing 'DIRTY' fails.
1077a2cab72aa5ff730ba2ae987b45398faafffeb505Neil Fuller    fileSystem.setFaulty(journalFile, true);
1078a2cab72aa5ff730ba2ae987b45398faafffeb505Neil Fuller    assertNull(cache.edit("c"));
1079a2cab72aa5ff730ba2ae987b45398faafffeb505Neil Fuller
1080a2cab72aa5ff730ba2ae987b45398faafffeb505Neil Fuller    // Once the journal has a failure, subsequent writes aren't permitted.
1081a2cab72aa5ff730ba2ae987b45398faafffeb505Neil Fuller    fileSystem.setFaulty(journalFile, false);
1082a2cab72aa5ff730ba2ae987b45398faafffeb505Neil Fuller    assertNull(cache.edit("d"));
1083a2cab72aa5ff730ba2ae987b45398faafffeb505Neil Fuller
1084a2cab72aa5ff730ba2ae987b45398faafffeb505Neil Fuller    // Confirm that the fault didn't corrupt entries stored before the fault was introduced.
1085a2cab72aa5ff730ba2ae987b45398faafffeb505Neil Fuller    cache.close();
1086a2cab72aa5ff730ba2ae987b45398faafffeb505Neil Fuller    cache = new DiskLruCache(fileSystem, cacheDir, appVersion, 2, Integer.MAX_VALUE, executor);
1087a2cab72aa5ff730ba2ae987b45398faafffeb505Neil Fuller    assertValue("a", "a", "a");
1088a2cab72aa5ff730ba2ae987b45398faafffeb505Neil Fuller    assertValue("b", "b", "b");
1089a2cab72aa5ff730ba2ae987b45398faafffeb505Neil Fuller    assertAbsent("c");
1090a2cab72aa5ff730ba2ae987b45398faafffeb505Neil Fuller    assertAbsent("d");
1091a2cab72aa5ff730ba2ae987b45398faafffeb505Neil Fuller  }
1092a2cab72aa5ff730ba2ae987b45398faafffeb505Neil Fuller
1093a2cab72aa5ff730ba2ae987b45398faafffeb505Neil Fuller  /**
1094a2cab72aa5ff730ba2ae987b45398faafffeb505Neil Fuller   * We had a bug where the cache was left in an inconsistent state after a journal write failed.
1095a2cab72aa5ff730ba2ae987b45398faafffeb505Neil Fuller   * https://github.com/square/okhttp/issues/1211
1096a2cab72aa5ff730ba2ae987b45398faafffeb505Neil Fuller   */
1097a2cab72aa5ff730ba2ae987b45398faafffeb505Neil Fuller  @Test public void journalWriteFailsDuringEditorCommit() throws Exception {
1098a2cab72aa5ff730ba2ae987b45398faafffeb505Neil Fuller    set("a", "a", "a");
1099a2cab72aa5ff730ba2ae987b45398faafffeb505Neil Fuller    set("b", "b", "b");
1100a2cab72aa5ff730ba2ae987b45398faafffeb505Neil Fuller
1101a2cab72aa5ff730ba2ae987b45398faafffeb505Neil Fuller    // Create an entry that fails to write to the journal during commit.
1102a2cab72aa5ff730ba2ae987b45398faafffeb505Neil Fuller    DiskLruCache.Editor editor = cache.edit("c");
1103a2cab72aa5ff730ba2ae987b45398faafffeb505Neil Fuller    setString(editor, 0, "c");
1104a2cab72aa5ff730ba2ae987b45398faafffeb505Neil Fuller    setString(editor, 1, "c");
1105a2cab72aa5ff730ba2ae987b45398faafffeb505Neil Fuller    fileSystem.setFaulty(journalFile, true);
1106a2cab72aa5ff730ba2ae987b45398faafffeb505Neil Fuller    editor.commit();
1107a2cab72aa5ff730ba2ae987b45398faafffeb505Neil Fuller
1108a2cab72aa5ff730ba2ae987b45398faafffeb505Neil Fuller    // Once the journal has a failure, subsequent writes aren't permitted.
1109a2cab72aa5ff730ba2ae987b45398faafffeb505Neil Fuller    fileSystem.setFaulty(journalFile, false);
1110a2cab72aa5ff730ba2ae987b45398faafffeb505Neil Fuller    assertNull(cache.edit("d"));
1111a2cab72aa5ff730ba2ae987b45398faafffeb505Neil Fuller
1112a2cab72aa5ff730ba2ae987b45398faafffeb505Neil Fuller    // Confirm that the fault didn't corrupt entries stored before the fault was introduced.
1113a2cab72aa5ff730ba2ae987b45398faafffeb505Neil Fuller    cache.close();
1114a2cab72aa5ff730ba2ae987b45398faafffeb505Neil Fuller    cache = new DiskLruCache(fileSystem, cacheDir, appVersion, 2, Integer.MAX_VALUE, executor);
1115a2cab72aa5ff730ba2ae987b45398faafffeb505Neil Fuller    assertValue("a", "a", "a");
1116a2cab72aa5ff730ba2ae987b45398faafffeb505Neil Fuller    assertValue("b", "b", "b");
1117a2cab72aa5ff730ba2ae987b45398faafffeb505Neil Fuller    assertAbsent("c");
1118a2cab72aa5ff730ba2ae987b45398faafffeb505Neil Fuller    assertAbsent("d");
1119a2cab72aa5ff730ba2ae987b45398faafffeb505Neil Fuller  }
1120a2cab72aa5ff730ba2ae987b45398faafffeb505Neil Fuller
1121a2cab72aa5ff730ba2ae987b45398faafffeb505Neil Fuller  @Test public void journalWriteFailsDuringEditorAbort() throws Exception {
1122a2cab72aa5ff730ba2ae987b45398faafffeb505Neil Fuller    set("a", "a", "a");
1123a2cab72aa5ff730ba2ae987b45398faafffeb505Neil Fuller    set("b", "b", "b");
1124a2cab72aa5ff730ba2ae987b45398faafffeb505Neil Fuller
1125a2cab72aa5ff730ba2ae987b45398faafffeb505Neil Fuller    // Create an entry that fails to write to the journal during abort.
1126a2cab72aa5ff730ba2ae987b45398faafffeb505Neil Fuller    DiskLruCache.Editor editor = cache.edit("c");
1127a2cab72aa5ff730ba2ae987b45398faafffeb505Neil Fuller    setString(editor, 0, "c");
1128a2cab72aa5ff730ba2ae987b45398faafffeb505Neil Fuller    setString(editor, 1, "c");
1129a2cab72aa5ff730ba2ae987b45398faafffeb505Neil Fuller    fileSystem.setFaulty(journalFile, true);
1130a2cab72aa5ff730ba2ae987b45398faafffeb505Neil Fuller    editor.abort();
1131a2cab72aa5ff730ba2ae987b45398faafffeb505Neil Fuller
1132a2cab72aa5ff730ba2ae987b45398faafffeb505Neil Fuller    // Once the journal has a failure, subsequent writes aren't permitted.
1133a2cab72aa5ff730ba2ae987b45398faafffeb505Neil Fuller    fileSystem.setFaulty(journalFile, false);
1134a2cab72aa5ff730ba2ae987b45398faafffeb505Neil Fuller    assertNull(cache.edit("d"));
1135a2cab72aa5ff730ba2ae987b45398faafffeb505Neil Fuller
1136a2cab72aa5ff730ba2ae987b45398faafffeb505Neil Fuller    // Confirm that the fault didn't corrupt entries stored before the fault was introduced.
1137a2cab72aa5ff730ba2ae987b45398faafffeb505Neil Fuller    cache.close();
1138a2cab72aa5ff730ba2ae987b45398faafffeb505Neil Fuller    cache = new DiskLruCache(fileSystem, cacheDir, appVersion, 2, Integer.MAX_VALUE, executor);
1139a2cab72aa5ff730ba2ae987b45398faafffeb505Neil Fuller    assertValue("a", "a", "a");
1140a2cab72aa5ff730ba2ae987b45398faafffeb505Neil Fuller    assertValue("b", "b", "b");
1141a2cab72aa5ff730ba2ae987b45398faafffeb505Neil Fuller    assertAbsent("c");
1142a2cab72aa5ff730ba2ae987b45398faafffeb505Neil Fuller    assertAbsent("d");
1143a2cab72aa5ff730ba2ae987b45398faafffeb505Neil Fuller  }
1144a2cab72aa5ff730ba2ae987b45398faafffeb505Neil Fuller
1145a2cab72aa5ff730ba2ae987b45398faafffeb505Neil Fuller  @Test public void journalWriteFailsDuringRemove() throws Exception {
1146a2cab72aa5ff730ba2ae987b45398faafffeb505Neil Fuller    set("a", "a", "a");
1147a2cab72aa5ff730ba2ae987b45398faafffeb505Neil Fuller    set("b", "b", "b");
1148a2cab72aa5ff730ba2ae987b45398faafffeb505Neil Fuller
1149a2cab72aa5ff730ba2ae987b45398faafffeb505Neil Fuller    // Remove, but the journal write will fail.
1150a2cab72aa5ff730ba2ae987b45398faafffeb505Neil Fuller    fileSystem.setFaulty(journalFile, true);
1151a2cab72aa5ff730ba2ae987b45398faafffeb505Neil Fuller    assertTrue(cache.remove("a"));
1152a2cab72aa5ff730ba2ae987b45398faafffeb505Neil Fuller
1153a2cab72aa5ff730ba2ae987b45398faafffeb505Neil Fuller    // Confirm that the entry was still removed.
1154a2cab72aa5ff730ba2ae987b45398faafffeb505Neil Fuller    fileSystem.setFaulty(journalFile, false);
1155a2cab72aa5ff730ba2ae987b45398faafffeb505Neil Fuller    cache.close();
1156a2cab72aa5ff730ba2ae987b45398faafffeb505Neil Fuller    cache = new DiskLruCache(fileSystem, cacheDir, appVersion, 2, Integer.MAX_VALUE, executor);
1157a2cab72aa5ff730ba2ae987b45398faafffeb505Neil Fuller    assertAbsent("a");
1158a2cab72aa5ff730ba2ae987b45398faafffeb505Neil Fuller    assertValue("b", "b", "b");
1159a2cab72aa5ff730ba2ae987b45398faafffeb505Neil Fuller  }
1160a2cab72aa5ff730ba2ae987b45398faafffeb505Neil Fuller
1161757558d44f11770d9d7710f6811d6bd784fc98d7Tobias Thierer  @Test public void noSizeCorruptionAfterCreatorDetached() throws Exception {
1162757558d44f11770d9d7710f6811d6bd784fc98d7Tobias Thierer    // Create an editor for k1. Detach it by clearing the cache.
1163757558d44f11770d9d7710f6811d6bd784fc98d7Tobias Thierer    DiskLruCache.Editor editor = cache.edit("k1");
1164757558d44f11770d9d7710f6811d6bd784fc98d7Tobias Thierer    setString(editor, 0, "a");
1165757558d44f11770d9d7710f6811d6bd784fc98d7Tobias Thierer    setString(editor, 1, "a");
1166757558d44f11770d9d7710f6811d6bd784fc98d7Tobias Thierer    cache.evictAll();
1167757558d44f11770d9d7710f6811d6bd784fc98d7Tobias Thierer
1168757558d44f11770d9d7710f6811d6bd784fc98d7Tobias Thierer    // Create a new value in its place.
1169757558d44f11770d9d7710f6811d6bd784fc98d7Tobias Thierer    set("k1", "bb", "bb");
1170757558d44f11770d9d7710f6811d6bd784fc98d7Tobias Thierer    assertEquals(4, cache.size());
1171757558d44f11770d9d7710f6811d6bd784fc98d7Tobias Thierer
1172757558d44f11770d9d7710f6811d6bd784fc98d7Tobias Thierer    // Committing the detached editor should not change the cache's size.
1173757558d44f11770d9d7710f6811d6bd784fc98d7Tobias Thierer    editor.commit();
1174757558d44f11770d9d7710f6811d6bd784fc98d7Tobias Thierer    assertEquals(4, cache.size());
1175757558d44f11770d9d7710f6811d6bd784fc98d7Tobias Thierer    assertValue("k1", "bb", "bb");
1176757558d44f11770d9d7710f6811d6bd784fc98d7Tobias Thierer  }
1177757558d44f11770d9d7710f6811d6bd784fc98d7Tobias Thierer
1178757558d44f11770d9d7710f6811d6bd784fc98d7Tobias Thierer  @Test public void noSizeCorruptionAfterEditorDetached() throws Exception {
1179757558d44f11770d9d7710f6811d6bd784fc98d7Tobias Thierer    set("k1", "a", "a");
1180757558d44f11770d9d7710f6811d6bd784fc98d7Tobias Thierer
1181757558d44f11770d9d7710f6811d6bd784fc98d7Tobias Thierer    // Create an editor for k1. Detach it by clearing the cache.
1182757558d44f11770d9d7710f6811d6bd784fc98d7Tobias Thierer    DiskLruCache.Editor editor = cache.edit("k1");
1183757558d44f11770d9d7710f6811d6bd784fc98d7Tobias Thierer    setString(editor, 0, "bb");
1184757558d44f11770d9d7710f6811d6bd784fc98d7Tobias Thierer    setString(editor, 1, "bb");
1185757558d44f11770d9d7710f6811d6bd784fc98d7Tobias Thierer    cache.evictAll();
1186757558d44f11770d9d7710f6811d6bd784fc98d7Tobias Thierer
1187757558d44f11770d9d7710f6811d6bd784fc98d7Tobias Thierer    // Create a new value in its place.
1188757558d44f11770d9d7710f6811d6bd784fc98d7Tobias Thierer    set("k1", "ccc", "ccc");
1189757558d44f11770d9d7710f6811d6bd784fc98d7Tobias Thierer    assertEquals(6, cache.size());
1190757558d44f11770d9d7710f6811d6bd784fc98d7Tobias Thierer
1191757558d44f11770d9d7710f6811d6bd784fc98d7Tobias Thierer    // Committing the detached editor should not change the cache's size.
1192757558d44f11770d9d7710f6811d6bd784fc98d7Tobias Thierer    editor.commit();
1193757558d44f11770d9d7710f6811d6bd784fc98d7Tobias Thierer    assertEquals(6, cache.size());
1194757558d44f11770d9d7710f6811d6bd784fc98d7Tobias Thierer    assertValue("k1", "ccc", "ccc");
1195757558d44f11770d9d7710f6811d6bd784fc98d7Tobias Thierer  }
1196757558d44f11770d9d7710f6811d6bd784fc98d7Tobias Thierer
1197757558d44f11770d9d7710f6811d6bd784fc98d7Tobias Thierer  @Test public void noNewSourceAfterEditorDetached() throws Exception {
1198757558d44f11770d9d7710f6811d6bd784fc98d7Tobias Thierer    set("k1", "a", "a");
1199757558d44f11770d9d7710f6811d6bd784fc98d7Tobias Thierer
1200757558d44f11770d9d7710f6811d6bd784fc98d7Tobias Thierer    DiskLruCache.Editor editor = cache.edit("k1");
1201757558d44f11770d9d7710f6811d6bd784fc98d7Tobias Thierer    cache.evictAll();
1202757558d44f11770d9d7710f6811d6bd784fc98d7Tobias Thierer
1203757558d44f11770d9d7710f6811d6bd784fc98d7Tobias Thierer    assertNull(editor.newSource(0));
1204757558d44f11770d9d7710f6811d6bd784fc98d7Tobias Thierer  }
1205757558d44f11770d9d7710f6811d6bd784fc98d7Tobias Thierer
1206757558d44f11770d9d7710f6811d6bd784fc98d7Tobias Thierer  @Test public void editsDiscardedAfterEditorDetached() throws Exception {
1207757558d44f11770d9d7710f6811d6bd784fc98d7Tobias Thierer    set("k1", "a", "a");
1208757558d44f11770d9d7710f6811d6bd784fc98d7Tobias Thierer
1209757558d44f11770d9d7710f6811d6bd784fc98d7Tobias Thierer    // Create an editor, then detach it.
1210757558d44f11770d9d7710f6811d6bd784fc98d7Tobias Thierer    DiskLruCache.Editor editor = cache.edit("k1");
1211757558d44f11770d9d7710f6811d6bd784fc98d7Tobias Thierer    BufferedSink sink = Okio.buffer(editor.newSink(0));
1212757558d44f11770d9d7710f6811d6bd784fc98d7Tobias Thierer    cache.evictAll();
1213757558d44f11770d9d7710f6811d6bd784fc98d7Tobias Thierer
1214757558d44f11770d9d7710f6811d6bd784fc98d7Tobias Thierer    // Create another value in its place.
1215757558d44f11770d9d7710f6811d6bd784fc98d7Tobias Thierer    set("k1", "ccc", "ccc");
1216757558d44f11770d9d7710f6811d6bd784fc98d7Tobias Thierer
1217757558d44f11770d9d7710f6811d6bd784fc98d7Tobias Thierer    // Complete the original edit. It goes into a black hole.
1218757558d44f11770d9d7710f6811d6bd784fc98d7Tobias Thierer    sink.writeUtf8("bb");
1219757558d44f11770d9d7710f6811d6bd784fc98d7Tobias Thierer    sink.close();
1220757558d44f11770d9d7710f6811d6bd784fc98d7Tobias Thierer
1221757558d44f11770d9d7710f6811d6bd784fc98d7Tobias Thierer    assertValue("k1", "ccc", "ccc");
1222757558d44f11770d9d7710f6811d6bd784fc98d7Tobias Thierer  }
1223757558d44f11770d9d7710f6811d6bd784fc98d7Tobias Thierer
1224757558d44f11770d9d7710f6811d6bd784fc98d7Tobias Thierer  @Test public void abortAfterDetach() throws Exception {
1225757558d44f11770d9d7710f6811d6bd784fc98d7Tobias Thierer    set("k1", "a", "a");
1226757558d44f11770d9d7710f6811d6bd784fc98d7Tobias Thierer
1227757558d44f11770d9d7710f6811d6bd784fc98d7Tobias Thierer    DiskLruCache.Editor editor = cache.edit("k1");
1228757558d44f11770d9d7710f6811d6bd784fc98d7Tobias Thierer    cache.evictAll();
1229757558d44f11770d9d7710f6811d6bd784fc98d7Tobias Thierer
1230757558d44f11770d9d7710f6811d6bd784fc98d7Tobias Thierer    editor.abort();
1231757558d44f11770d9d7710f6811d6bd784fc98d7Tobias Thierer    assertEquals(0, cache.size());
1232757558d44f11770d9d7710f6811d6bd784fc98d7Tobias Thierer    assertAbsent("k1");
1233757558d44f11770d9d7710f6811d6bd784fc98d7Tobias Thierer  }
1234757558d44f11770d9d7710f6811d6bd784fc98d7Tobias Thierer
1235e78f117bcbd6b57d783737107f445ef75ecb474aNeil Fuller  private void assertJournalEquals(String... expectedBodyLines) throws Exception {
1236e78f117bcbd6b57d783737107f445ef75ecb474aNeil Fuller    List<String> expectedLines = new ArrayList<>();
1237e78f117bcbd6b57d783737107f445ef75ecb474aNeil Fuller    expectedLines.add(MAGIC);
1238e78f117bcbd6b57d783737107f445ef75ecb474aNeil Fuller    expectedLines.add(VERSION_1);
1239e78f117bcbd6b57d783737107f445ef75ecb474aNeil Fuller    expectedLines.add("100");
1240e78f117bcbd6b57d783737107f445ef75ecb474aNeil Fuller    expectedLines.add("2");
1241e78f117bcbd6b57d783737107f445ef75ecb474aNeil Fuller    expectedLines.add("");
1242e78f117bcbd6b57d783737107f445ef75ecb474aNeil Fuller    expectedLines.addAll(Arrays.asList(expectedBodyLines));
1243e78f117bcbd6b57d783737107f445ef75ecb474aNeil Fuller    assertEquals(expectedLines, readJournalLines());
1244e78f117bcbd6b57d783737107f445ef75ecb474aNeil Fuller  }
1245e78f117bcbd6b57d783737107f445ef75ecb474aNeil Fuller
1246e78f117bcbd6b57d783737107f445ef75ecb474aNeil Fuller  private void createJournal(String... bodyLines) throws Exception {
1247e78f117bcbd6b57d783737107f445ef75ecb474aNeil Fuller    createJournalWithHeader(MAGIC, VERSION_1, "100", "2", "", bodyLines);
1248e78f117bcbd6b57d783737107f445ef75ecb474aNeil Fuller  }
1249e78f117bcbd6b57d783737107f445ef75ecb474aNeil Fuller
1250e78f117bcbd6b57d783737107f445ef75ecb474aNeil Fuller  private void createJournalWithHeader(String magic, String version, String appVersion,
1251e78f117bcbd6b57d783737107f445ef75ecb474aNeil Fuller      String valueCount, String blank, String... bodyLines) throws Exception {
1252a2cab72aa5ff730ba2ae987b45398faafffeb505Neil Fuller    BufferedSink sink = Okio.buffer(fileSystem.sink(journalFile));
1253a2cab72aa5ff730ba2ae987b45398faafffeb505Neil Fuller    sink.writeUtf8(magic + "\n");
1254a2cab72aa5ff730ba2ae987b45398faafffeb505Neil Fuller    sink.writeUtf8(version + "\n");
1255a2cab72aa5ff730ba2ae987b45398faafffeb505Neil Fuller    sink.writeUtf8(appVersion + "\n");
1256a2cab72aa5ff730ba2ae987b45398faafffeb505Neil Fuller    sink.writeUtf8(valueCount + "\n");
1257a2cab72aa5ff730ba2ae987b45398faafffeb505Neil Fuller    sink.writeUtf8(blank + "\n");
1258e78f117bcbd6b57d783737107f445ef75ecb474aNeil Fuller    for (String line : bodyLines) {
1259a2cab72aa5ff730ba2ae987b45398faafffeb505Neil Fuller      sink.writeUtf8(line);
1260a2cab72aa5ff730ba2ae987b45398faafffeb505Neil Fuller      sink.writeUtf8("\n");
1261e78f117bcbd6b57d783737107f445ef75ecb474aNeil Fuller    }
1262a2cab72aa5ff730ba2ae987b45398faafffeb505Neil Fuller    sink.close();
1263e78f117bcbd6b57d783737107f445ef75ecb474aNeil Fuller  }
1264e78f117bcbd6b57d783737107f445ef75ecb474aNeil Fuller
1265e78f117bcbd6b57d783737107f445ef75ecb474aNeil Fuller  private List<String> readJournalLines() throws Exception {
1266e78f117bcbd6b57d783737107f445ef75ecb474aNeil Fuller    List<String> result = new ArrayList<>();
1267a2cab72aa5ff730ba2ae987b45398faafffeb505Neil Fuller    BufferedSource source = Okio.buffer(fileSystem.source(journalFile));
1268a2cab72aa5ff730ba2ae987b45398faafffeb505Neil Fuller    for (String line; (line = source.readUtf8Line()) != null; ) {
1269e78f117bcbd6b57d783737107f445ef75ecb474aNeil Fuller      result.add(line);
1270e78f117bcbd6b57d783737107f445ef75ecb474aNeil Fuller    }
1271a2cab72aa5ff730ba2ae987b45398faafffeb505Neil Fuller    source.close();
1272e78f117bcbd6b57d783737107f445ef75ecb474aNeil Fuller    return result;
1273e78f117bcbd6b57d783737107f445ef75ecb474aNeil Fuller  }
1274e78f117bcbd6b57d783737107f445ef75ecb474aNeil Fuller
1275e78f117bcbd6b57d783737107f445ef75ecb474aNeil Fuller  private File getCleanFile(String key, int index) {
1276e78f117bcbd6b57d783737107f445ef75ecb474aNeil Fuller    return new File(cacheDir, key + "." + index);
1277e78f117bcbd6b57d783737107f445ef75ecb474aNeil Fuller  }
1278e78f117bcbd6b57d783737107f445ef75ecb474aNeil Fuller
1279e78f117bcbd6b57d783737107f445ef75ecb474aNeil Fuller  private File getDirtyFile(String key, int index) {
1280e78f117bcbd6b57d783737107f445ef75ecb474aNeil Fuller    return new File(cacheDir, key + "." + index + ".tmp");
1281e78f117bcbd6b57d783737107f445ef75ecb474aNeil Fuller  }
1282e78f117bcbd6b57d783737107f445ef75ecb474aNeil Fuller
1283a2cab72aa5ff730ba2ae987b45398faafffeb505Neil Fuller  private String readFile(File file) throws Exception {
1284a2cab72aa5ff730ba2ae987b45398faafffeb505Neil Fuller    BufferedSource source = Okio.buffer(fileSystem.source(file));
1285a2cab72aa5ff730ba2ae987b45398faafffeb505Neil Fuller    String result = source.readUtf8();
1286a2cab72aa5ff730ba2ae987b45398faafffeb505Neil Fuller    source.close();
1287a2cab72aa5ff730ba2ae987b45398faafffeb505Neil Fuller    return result;
1288e78f117bcbd6b57d783737107f445ef75ecb474aNeil Fuller  }
1289e78f117bcbd6b57d783737107f445ef75ecb474aNeil Fuller
1290a2cab72aa5ff730ba2ae987b45398faafffeb505Neil Fuller  public void writeFile(File file, String content) throws Exception {
1291a2cab72aa5ff730ba2ae987b45398faafffeb505Neil Fuller    BufferedSink sink = Okio.buffer(fileSystem.sink(file));
1292a2cab72aa5ff730ba2ae987b45398faafffeb505Neil Fuller    sink.writeUtf8(content);
1293a2cab72aa5ff730ba2ae987b45398faafffeb505Neil Fuller    sink.close();
1294e78f117bcbd6b57d783737107f445ef75ecb474aNeil Fuller  }
1295e78f117bcbd6b57d783737107f445ef75ecb474aNeil Fuller
1296e78f117bcbd6b57d783737107f445ef75ecb474aNeil Fuller  private static void assertInoperable(DiskLruCache.Editor editor) throws Exception {
1297e78f117bcbd6b57d783737107f445ef75ecb474aNeil Fuller    try {
1298e78f117bcbd6b57d783737107f445ef75ecb474aNeil Fuller      setString(editor, 0, "A");
1299e78f117bcbd6b57d783737107f445ef75ecb474aNeil Fuller      fail();
1300e78f117bcbd6b57d783737107f445ef75ecb474aNeil Fuller    } catch (IllegalStateException expected) {
1301e78f117bcbd6b57d783737107f445ef75ecb474aNeil Fuller    }
1302e78f117bcbd6b57d783737107f445ef75ecb474aNeil Fuller    try {
1303e78f117bcbd6b57d783737107f445ef75ecb474aNeil Fuller      editor.newSource(0);
1304e78f117bcbd6b57d783737107f445ef75ecb474aNeil Fuller      fail();
1305e78f117bcbd6b57d783737107f445ef75ecb474aNeil Fuller    } catch (IllegalStateException expected) {
1306e78f117bcbd6b57d783737107f445ef75ecb474aNeil Fuller    }
1307e78f117bcbd6b57d783737107f445ef75ecb474aNeil Fuller    try {
1308e78f117bcbd6b57d783737107f445ef75ecb474aNeil Fuller      editor.newSink(0);
1309e78f117bcbd6b57d783737107f445ef75ecb474aNeil Fuller      fail();
1310e78f117bcbd6b57d783737107f445ef75ecb474aNeil Fuller    } catch (IllegalStateException expected) {
1311e78f117bcbd6b57d783737107f445ef75ecb474aNeil Fuller    }
1312e78f117bcbd6b57d783737107f445ef75ecb474aNeil Fuller    try {
1313e78f117bcbd6b57d783737107f445ef75ecb474aNeil Fuller      editor.commit();
1314e78f117bcbd6b57d783737107f445ef75ecb474aNeil Fuller      fail();
1315e78f117bcbd6b57d783737107f445ef75ecb474aNeil Fuller    } catch (IllegalStateException expected) {
1316e78f117bcbd6b57d783737107f445ef75ecb474aNeil Fuller    }
1317e78f117bcbd6b57d783737107f445ef75ecb474aNeil Fuller    try {
1318e78f117bcbd6b57d783737107f445ef75ecb474aNeil Fuller      editor.abort();
1319e78f117bcbd6b57d783737107f445ef75ecb474aNeil Fuller      fail();
1320e78f117bcbd6b57d783737107f445ef75ecb474aNeil Fuller    } catch (IllegalStateException expected) {
1321e78f117bcbd6b57d783737107f445ef75ecb474aNeil Fuller    }
1322e78f117bcbd6b57d783737107f445ef75ecb474aNeil Fuller  }
1323e78f117bcbd6b57d783737107f445ef75ecb474aNeil Fuller
1324e78f117bcbd6b57d783737107f445ef75ecb474aNeil Fuller  private void generateSomeGarbageFiles() throws Exception {
1325e78f117bcbd6b57d783737107f445ef75ecb474aNeil Fuller    File dir1 = new File(cacheDir, "dir1");
1326e78f117bcbd6b57d783737107f445ef75ecb474aNeil Fuller    File dir2 = new File(dir1, "dir2");
1327e78f117bcbd6b57d783737107f445ef75ecb474aNeil Fuller    writeFile(getCleanFile("g1", 0), "A");
1328e78f117bcbd6b57d783737107f445ef75ecb474aNeil Fuller    writeFile(getCleanFile("g1", 1), "B");
1329e78f117bcbd6b57d783737107f445ef75ecb474aNeil Fuller    writeFile(getCleanFile("g2", 0), "C");
1330e78f117bcbd6b57d783737107f445ef75ecb474aNeil Fuller    writeFile(getCleanFile("g2", 1), "D");
1331e78f117bcbd6b57d783737107f445ef75ecb474aNeil Fuller    writeFile(getCleanFile("g2", 1), "D");
1332e78f117bcbd6b57d783737107f445ef75ecb474aNeil Fuller    writeFile(new File(cacheDir, "otherFile0"), "E");
1333e78f117bcbd6b57d783737107f445ef75ecb474aNeil Fuller    writeFile(new File(dir2, "otherFile1"), "F");
1334e78f117bcbd6b57d783737107f445ef75ecb474aNeil Fuller  }
1335e78f117bcbd6b57d783737107f445ef75ecb474aNeil Fuller
1336e78f117bcbd6b57d783737107f445ef75ecb474aNeil Fuller  private void assertGarbageFilesAllDeleted() throws Exception {
1337a2cab72aa5ff730ba2ae987b45398faafffeb505Neil Fuller    assertFalse(fileSystem.exists(getCleanFile("g1", 0)));
1338a2cab72aa5ff730ba2ae987b45398faafffeb505Neil Fuller    assertFalse(fileSystem.exists(getCleanFile("g1", 1)));
1339a2cab72aa5ff730ba2ae987b45398faafffeb505Neil Fuller    assertFalse(fileSystem.exists(getCleanFile("g2", 0)));
1340a2cab72aa5ff730ba2ae987b45398faafffeb505Neil Fuller    assertFalse(fileSystem.exists(getCleanFile("g2", 1)));
1341a2cab72aa5ff730ba2ae987b45398faafffeb505Neil Fuller    assertFalse(fileSystem.exists(new File(cacheDir, "otherFile0")));
1342a2cab72aa5ff730ba2ae987b45398faafffeb505Neil Fuller    assertFalse(fileSystem.exists(new File(cacheDir, "dir1")));
1343e78f117bcbd6b57d783737107f445ef75ecb474aNeil Fuller  }
1344e78f117bcbd6b57d783737107f445ef75ecb474aNeil Fuller
1345e78f117bcbd6b57d783737107f445ef75ecb474aNeil Fuller  private void set(String key, String value0, String value1) throws Exception {
1346e78f117bcbd6b57d783737107f445ef75ecb474aNeil Fuller    DiskLruCache.Editor editor = cache.edit(key);
1347e78f117bcbd6b57d783737107f445ef75ecb474aNeil Fuller    setString(editor, 0, value0);
1348e78f117bcbd6b57d783737107f445ef75ecb474aNeil Fuller    setString(editor, 1, value1);
1349e78f117bcbd6b57d783737107f445ef75ecb474aNeil Fuller    editor.commit();
1350e78f117bcbd6b57d783737107f445ef75ecb474aNeil Fuller  }
1351e78f117bcbd6b57d783737107f445ef75ecb474aNeil Fuller
1352e78f117bcbd6b57d783737107f445ef75ecb474aNeil Fuller  public static void setString(DiskLruCache.Editor editor, int index, String value) throws IOException {
1353e78f117bcbd6b57d783737107f445ef75ecb474aNeil Fuller    BufferedSink writer = Okio.buffer(editor.newSink(index));
1354e78f117bcbd6b57d783737107f445ef75ecb474aNeil Fuller    writer.writeUtf8(value);
1355e78f117bcbd6b57d783737107f445ef75ecb474aNeil Fuller    writer.close();
1356e78f117bcbd6b57d783737107f445ef75ecb474aNeil Fuller  }
1357e78f117bcbd6b57d783737107f445ef75ecb474aNeil Fuller
1358e78f117bcbd6b57d783737107f445ef75ecb474aNeil Fuller  private void assertAbsent(String key) throws Exception {
1359e78f117bcbd6b57d783737107f445ef75ecb474aNeil Fuller    DiskLruCache.Snapshot snapshot = cache.get(key);
1360e78f117bcbd6b57d783737107f445ef75ecb474aNeil Fuller    if (snapshot != null) {
1361e78f117bcbd6b57d783737107f445ef75ecb474aNeil Fuller      snapshot.close();
1362e78f117bcbd6b57d783737107f445ef75ecb474aNeil Fuller      fail();
1363e78f117bcbd6b57d783737107f445ef75ecb474aNeil Fuller    }
1364a2cab72aa5ff730ba2ae987b45398faafffeb505Neil Fuller    assertFalse(fileSystem.exists(getCleanFile(key, 0)));
1365a2cab72aa5ff730ba2ae987b45398faafffeb505Neil Fuller    assertFalse(fileSystem.exists(getCleanFile(key, 1)));
1366a2cab72aa5ff730ba2ae987b45398faafffeb505Neil Fuller    assertFalse(fileSystem.exists(getDirtyFile(key, 0)));
1367a2cab72aa5ff730ba2ae987b45398faafffeb505Neil Fuller    assertFalse(fileSystem.exists(getDirtyFile(key, 1)));
1368e78f117bcbd6b57d783737107f445ef75ecb474aNeil Fuller  }
1369e78f117bcbd6b57d783737107f445ef75ecb474aNeil Fuller
1370e78f117bcbd6b57d783737107f445ef75ecb474aNeil Fuller  private void assertValue(String key, String value0, String value1) throws Exception {
1371e78f117bcbd6b57d783737107f445ef75ecb474aNeil Fuller    DiskLruCache.Snapshot snapshot = cache.get(key);
1372e78f117bcbd6b57d783737107f445ef75ecb474aNeil Fuller    assertSnapshotValue(snapshot, 0, value0);
1373e78f117bcbd6b57d783737107f445ef75ecb474aNeil Fuller    assertSnapshotValue(snapshot, 1, value1);
1374a2cab72aa5ff730ba2ae987b45398faafffeb505Neil Fuller    assertTrue(fileSystem.exists(getCleanFile(key, 0)));
1375a2cab72aa5ff730ba2ae987b45398faafffeb505Neil Fuller    assertTrue(fileSystem.exists(getCleanFile(key, 1)));
1376e78f117bcbd6b57d783737107f445ef75ecb474aNeil Fuller    snapshot.close();
1377e78f117bcbd6b57d783737107f445ef75ecb474aNeil Fuller  }
1378e78f117bcbd6b57d783737107f445ef75ecb474aNeil Fuller
1379e78f117bcbd6b57d783737107f445ef75ecb474aNeil Fuller  private void assertSnapshotValue(DiskLruCache.Snapshot snapshot, int index, String value)
1380e78f117bcbd6b57d783737107f445ef75ecb474aNeil Fuller      throws IOException {
1381e78f117bcbd6b57d783737107f445ef75ecb474aNeil Fuller    assertEquals(value, sourceAsString(snapshot.getSource(index)));
1382e78f117bcbd6b57d783737107f445ef75ecb474aNeil Fuller    assertEquals(value.length(), snapshot.getLength(index));
1383e78f117bcbd6b57d783737107f445ef75ecb474aNeil Fuller  }
1384e78f117bcbd6b57d783737107f445ef75ecb474aNeil Fuller
1385e78f117bcbd6b57d783737107f445ef75ecb474aNeil Fuller  private String sourceAsString(Source source) throws IOException {
1386e78f117bcbd6b57d783737107f445ef75ecb474aNeil Fuller    return source != null ? Okio.buffer(source).readUtf8() : null;
1387e78f117bcbd6b57d783737107f445ef75ecb474aNeil Fuller  }
1388e78f117bcbd6b57d783737107f445ef75ecb474aNeil Fuller
1389e78f117bcbd6b57d783737107f445ef75ecb474aNeil Fuller  private void copyFile(File from, File to) throws IOException {
1390a2cab72aa5ff730ba2ae987b45398faafffeb505Neil Fuller    Source source = fileSystem.source(from);
1391a2cab72aa5ff730ba2ae987b45398faafffeb505Neil Fuller    BufferedSink sink = Okio.buffer(fileSystem.sink(to));
1392e78f117bcbd6b57d783737107f445ef75ecb474aNeil Fuller    sink.writeAll(source);
1393e78f117bcbd6b57d783737107f445ef75ecb474aNeil Fuller    source.close();
1394e78f117bcbd6b57d783737107f445ef75ecb474aNeil Fuller    sink.close();
1395e78f117bcbd6b57d783737107f445ef75ecb474aNeil Fuller  }
1396e78f117bcbd6b57d783737107f445ef75ecb474aNeil Fuller
1397e78f117bcbd6b57d783737107f445ef75ecb474aNeil Fuller  private static class TestExecutor implements Executor {
1398e78f117bcbd6b57d783737107f445ef75ecb474aNeil Fuller    final Deque<Runnable> jobs = new ArrayDeque<>();
1399e78f117bcbd6b57d783737107f445ef75ecb474aNeil Fuller
1400e78f117bcbd6b57d783737107f445ef75ecb474aNeil Fuller    @Override public void execute(Runnable command) {
1401e78f117bcbd6b57d783737107f445ef75ecb474aNeil Fuller      jobs.addLast(command);
1402e78f117bcbd6b57d783737107f445ef75ecb474aNeil Fuller    }
1403e78f117bcbd6b57d783737107f445ef75ecb474aNeil Fuller  }
1404e78f117bcbd6b57d783737107f445ef75ecb474aNeil Fuller}
1405