1// Copyright (c) 2012 The Chromium Authors. All rights reserved.
2// Use of this source code is governed by a BSD-style license that can be
3// found in the LICENSE file.
4
5#include "net/disk_cache/disk_cache_test_base.h"
6
7#include "base/files/file_util.h"
8#include "base/path_service.h"
9#include "base/run_loop.h"
10#include "base/single_thread_task_runner.h"
11#include "base/thread_task_runner_handle.h"
12#include "base/threading/platform_thread.h"
13#include "net/base/io_buffer.h"
14#include "net/base/net_errors.h"
15#include "net/base/test_completion_callback.h"
16#include "net/disk_cache/blockfile/backend_impl.h"
17#include "net/disk_cache/cache_util.h"
18#include "net/disk_cache/disk_cache.h"
19#include "net/disk_cache/disk_cache_test_util.h"
20#include "net/disk_cache/memory/mem_backend_impl.h"
21#include "net/disk_cache/simple/simple_backend_impl.h"
22#include "net/disk_cache/simple/simple_index.h"
23
24DiskCacheTest::DiskCacheTest() {
25  CHECK(temp_dir_.CreateUniqueTempDir());
26  cache_path_ = temp_dir_.path();
27  if (!base::MessageLoop::current())
28    message_loop_.reset(new base::MessageLoopForIO());
29}
30
31DiskCacheTest::~DiskCacheTest() {
32}
33
34bool DiskCacheTest::CopyTestCache(const std::string& name) {
35  base::FilePath path;
36  PathService::Get(base::DIR_SOURCE_ROOT, &path);
37  path = path.AppendASCII("net");
38  path = path.AppendASCII("data");
39  path = path.AppendASCII("cache_tests");
40  path = path.AppendASCII(name);
41
42  if (!CleanupCacheDir())
43    return false;
44  return base::CopyDirectory(path, cache_path_, false);
45}
46
47bool DiskCacheTest::CleanupCacheDir() {
48  return DeleteCache(cache_path_);
49}
50
51void DiskCacheTest::TearDown() {
52  base::RunLoop().RunUntilIdle();
53}
54
55DiskCacheTestWithCache::TestIterator::TestIterator(
56    scoped_ptr<disk_cache::Backend::Iterator> iterator)
57    : iterator_(iterator.Pass()) {
58}
59
60DiskCacheTestWithCache::TestIterator::~TestIterator() {}
61
62int DiskCacheTestWithCache::TestIterator::OpenNextEntry(
63    disk_cache::Entry** next_entry) {
64  net::TestCompletionCallback cb;
65  int rv = iterator_->OpenNextEntry(next_entry, cb.callback());
66  return cb.GetResult(rv);
67}
68
69DiskCacheTestWithCache::DiskCacheTestWithCache()
70    : cache_impl_(NULL),
71      simple_cache_impl_(NULL),
72      mem_cache_(NULL),
73      mask_(0),
74      size_(0),
75      type_(net::DISK_CACHE),
76      memory_only_(false),
77      simple_cache_mode_(false),
78      simple_cache_wait_for_index_(true),
79      force_creation_(false),
80      new_eviction_(false),
81      first_cleanup_(true),
82      integrity_(true),
83      use_current_thread_(false),
84      cache_thread_("CacheThread") {
85}
86
87DiskCacheTestWithCache::~DiskCacheTestWithCache() {}
88
89void DiskCacheTestWithCache::InitCache() {
90  if (memory_only_)
91    InitMemoryCache();
92  else
93    InitDiskCache();
94
95  ASSERT_TRUE(NULL != cache_);
96  if (first_cleanup_)
97    ASSERT_EQ(0, cache_->GetEntryCount());
98}
99
100// We are expected to leak memory when simulating crashes.
101void DiskCacheTestWithCache::SimulateCrash() {
102  ASSERT_TRUE(!memory_only_);
103  net::TestCompletionCallback cb;
104  int rv = cache_impl_->FlushQueueForTest(cb.callback());
105  ASSERT_EQ(net::OK, cb.GetResult(rv));
106  cache_impl_->ClearRefCountForTest();
107
108  cache_.reset();
109  EXPECT_TRUE(CheckCacheIntegrity(cache_path_, new_eviction_, mask_));
110
111  CreateBackend(disk_cache::kNoRandom, &cache_thread_);
112}
113
114void DiskCacheTestWithCache::SetTestMode() {
115  ASSERT_TRUE(!memory_only_);
116  cache_impl_->SetUnitTestMode();
117}
118
119void DiskCacheTestWithCache::SetMaxSize(int size) {
120  size_ = size;
121  if (simple_cache_impl_)
122    EXPECT_TRUE(simple_cache_impl_->SetMaxSize(size));
123
124  if (cache_impl_)
125    EXPECT_TRUE(cache_impl_->SetMaxSize(size));
126
127  if (mem_cache_)
128    EXPECT_TRUE(mem_cache_->SetMaxSize(size));
129}
130
131int DiskCacheTestWithCache::OpenEntry(const std::string& key,
132                                      disk_cache::Entry** entry) {
133  net::TestCompletionCallback cb;
134  int rv = cache_->OpenEntry(key, entry, cb.callback());
135  return cb.GetResult(rv);
136}
137
138int DiskCacheTestWithCache::CreateEntry(const std::string& key,
139                                        disk_cache::Entry** entry) {
140  net::TestCompletionCallback cb;
141  int rv = cache_->CreateEntry(key, entry, cb.callback());
142  return cb.GetResult(rv);
143}
144
145int DiskCacheTestWithCache::DoomEntry(const std::string& key) {
146  net::TestCompletionCallback cb;
147  int rv = cache_->DoomEntry(key, cb.callback());
148  return cb.GetResult(rv);
149}
150
151int DiskCacheTestWithCache::DoomAllEntries() {
152  net::TestCompletionCallback cb;
153  int rv = cache_->DoomAllEntries(cb.callback());
154  return cb.GetResult(rv);
155}
156
157int DiskCacheTestWithCache::DoomEntriesBetween(const base::Time initial_time,
158                                               const base::Time end_time) {
159  net::TestCompletionCallback cb;
160  int rv = cache_->DoomEntriesBetween(initial_time, end_time, cb.callback());
161  return cb.GetResult(rv);
162}
163
164int DiskCacheTestWithCache::DoomEntriesSince(const base::Time initial_time) {
165  net::TestCompletionCallback cb;
166  int rv = cache_->DoomEntriesSince(initial_time, cb.callback());
167  return cb.GetResult(rv);
168}
169
170scoped_ptr<DiskCacheTestWithCache::TestIterator>
171    DiskCacheTestWithCache::CreateIterator() {
172  return scoped_ptr<TestIterator>(new TestIterator(cache_->CreateIterator()));
173}
174
175void DiskCacheTestWithCache::FlushQueueForTest() {
176  if (memory_only_ || !cache_impl_)
177    return;
178
179  net::TestCompletionCallback cb;
180  int rv = cache_impl_->FlushQueueForTest(cb.callback());
181  EXPECT_EQ(net::OK, cb.GetResult(rv));
182}
183
184void DiskCacheTestWithCache::RunTaskForTest(const base::Closure& closure) {
185  if (memory_only_ || !cache_impl_) {
186    closure.Run();
187    return;
188  }
189
190  net::TestCompletionCallback cb;
191  int rv = cache_impl_->RunTaskForTest(closure, cb.callback());
192  EXPECT_EQ(net::OK, cb.GetResult(rv));
193}
194
195int DiskCacheTestWithCache::ReadData(disk_cache::Entry* entry, int index,
196                                     int offset, net::IOBuffer* buf, int len) {
197  net::TestCompletionCallback cb;
198  int rv = entry->ReadData(index, offset, buf, len, cb.callback());
199  return cb.GetResult(rv);
200}
201
202int DiskCacheTestWithCache::WriteData(disk_cache::Entry* entry, int index,
203                                      int offset, net::IOBuffer* buf, int len,
204                                      bool truncate) {
205  net::TestCompletionCallback cb;
206  int rv = entry->WriteData(index, offset, buf, len, cb.callback(), truncate);
207  return cb.GetResult(rv);
208}
209
210int DiskCacheTestWithCache::ReadSparseData(disk_cache::Entry* entry,
211                                           int64 offset, net::IOBuffer* buf,
212                                           int len) {
213  net::TestCompletionCallback cb;
214  int rv = entry->ReadSparseData(offset, buf, len, cb.callback());
215  return cb.GetResult(rv);
216}
217
218int DiskCacheTestWithCache::WriteSparseData(disk_cache::Entry* entry,
219                                            int64 offset,
220                                            net::IOBuffer* buf, int len) {
221  net::TestCompletionCallback cb;
222  int rv = entry->WriteSparseData(offset, buf, len, cb.callback());
223  return cb.GetResult(rv);
224}
225
226void DiskCacheTestWithCache::TrimForTest(bool empty) {
227  RunTaskForTest(base::Bind(&disk_cache::BackendImpl::TrimForTest,
228                            base::Unretained(cache_impl_),
229                            empty));
230}
231
232void DiskCacheTestWithCache::TrimDeletedListForTest(bool empty) {
233  RunTaskForTest(base::Bind(&disk_cache::BackendImpl::TrimDeletedListForTest,
234                            base::Unretained(cache_impl_),
235                            empty));
236}
237
238void DiskCacheTestWithCache::AddDelay() {
239  if (simple_cache_mode_) {
240    // The simple cache uses second resolution for many timeouts, so it's safest
241    // to advance by at least whole seconds before falling back into the normal
242    // disk cache epsilon advance.
243    const base::Time initial_time = base::Time::Now();
244    do {
245      base::PlatformThread::YieldCurrentThread();
246    } while (base::Time::Now() -
247             initial_time < base::TimeDelta::FromSeconds(1));
248  }
249
250  base::Time initial = base::Time::Now();
251  while (base::Time::Now() <= initial) {
252    base::PlatformThread::Sleep(base::TimeDelta::FromMilliseconds(1));
253  };
254}
255
256void DiskCacheTestWithCache::TearDown() {
257  base::RunLoop().RunUntilIdle();
258  disk_cache::SimpleBackendImpl::FlushWorkerPoolForTesting();
259  base::RunLoop().RunUntilIdle();
260  cache_.reset();
261  if (cache_thread_.IsRunning())
262    cache_thread_.Stop();
263
264  if (!memory_only_ && !simple_cache_mode_ && integrity_) {
265    EXPECT_TRUE(CheckCacheIntegrity(cache_path_, new_eviction_, mask_));
266  }
267  base::RunLoop().RunUntilIdle();
268  disk_cache::SimpleBackendImpl::FlushWorkerPoolForTesting();
269  DiskCacheTest::TearDown();
270}
271
272void DiskCacheTestWithCache::InitMemoryCache() {
273  mem_cache_ = new disk_cache::MemBackendImpl(NULL);
274  cache_.reset(mem_cache_);
275  ASSERT_TRUE(cache_);
276
277  if (size_)
278    EXPECT_TRUE(mem_cache_->SetMaxSize(size_));
279
280  ASSERT_TRUE(mem_cache_->Init());
281}
282
283void DiskCacheTestWithCache::InitDiskCache() {
284  if (first_cleanup_)
285    ASSERT_TRUE(CleanupCacheDir());
286
287  if (!cache_thread_.IsRunning()) {
288    ASSERT_TRUE(cache_thread_.StartWithOptions(
289        base::Thread::Options(base::MessageLoop::TYPE_IO, 0)));
290  }
291  ASSERT_TRUE(cache_thread_.message_loop() != NULL);
292
293  CreateBackend(disk_cache::kNoRandom, &cache_thread_);
294}
295
296void DiskCacheTestWithCache::CreateBackend(uint32 flags, base::Thread* thread) {
297  scoped_refptr<base::SingleThreadTaskRunner> runner;
298  if (use_current_thread_)
299    runner = base::ThreadTaskRunnerHandle::Get();
300  else
301    runner = thread->task_runner();
302
303  if (simple_cache_mode_) {
304    net::TestCompletionCallback cb;
305    scoped_ptr<disk_cache::SimpleBackendImpl> simple_backend(
306        new disk_cache::SimpleBackendImpl(
307            cache_path_, size_, type_, runner, NULL));
308    int rv = simple_backend->Init(cb.callback());
309    ASSERT_EQ(net::OK, cb.GetResult(rv));
310    simple_cache_impl_ = simple_backend.get();
311    cache_ = simple_backend.PassAs<disk_cache::Backend>();
312    if (simple_cache_wait_for_index_) {
313      net::TestCompletionCallback wait_for_index_cb;
314      rv = simple_cache_impl_->index()->ExecuteWhenReady(
315          wait_for_index_cb.callback());
316      ASSERT_EQ(net::OK, wait_for_index_cb.GetResult(rv));
317    }
318    return;
319  }
320
321  if (mask_)
322    cache_impl_ = new disk_cache::BackendImpl(cache_path_, mask_, runner, NULL);
323  else
324    cache_impl_ = new disk_cache::BackendImpl(cache_path_, runner, NULL);
325  cache_.reset(cache_impl_);
326  ASSERT_TRUE(cache_);
327  if (size_)
328    EXPECT_TRUE(cache_impl_->SetMaxSize(size_));
329  if (new_eviction_)
330    cache_impl_->SetNewEviction();
331  cache_impl_->SetType(type_);
332  cache_impl_->SetFlags(flags);
333  net::TestCompletionCallback cb;
334  int rv = cache_impl_->Init(cb.callback());
335  ASSERT_EQ(net::OK, cb.GetResult(rv));
336}
337