summaryrefslogtreecommitdiffstats
path: root/content/browser/appcache/appcache_disk_cache_unittest.cc
blob: 5e1e083ebdc8cd33a6c213ed61c1aec5800abab5 (plain)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
// Copyright 2014 The Chromium Authors. All rights reserved.
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.

#include "base/bind.h"
#include "base/file_util.h"
#include "base/files/scoped_temp_dir.h"
#include "base/message_loop/message_loop_proxy.h"
#include "base/run_loop.h"
#include "net/base/io_buffer.h"
#include "net/base/net_errors.h"
#include "testing/gtest/include/gtest/gtest.h"
#include "webkit/browser/appcache/appcache_disk_cache.h"

using appcache::AppCacheDiskCache;

namespace content {

class AppCacheDiskCacheTest : public testing::Test {
 public:
  AppCacheDiskCacheTest() {}

  virtual void SetUp() OVERRIDE {
    // Use the current thread for the DiskCache's cache_thread.
    message_loop_.reset(new base::MessageLoopForIO());
    cache_thread_ = base::MessageLoopProxy::current();
    ASSERT_TRUE(directory_.CreateUniqueTempDir());
    completion_callback_ = base::Bind(
        &AppCacheDiskCacheTest::OnComplete,
        base::Unretained(this));
  }

  virtual void TearDown() OVERRIDE {
    base::RunLoop().RunUntilIdle();
    message_loop_.reset(NULL);
  }

  void FlushCacheTasks() {
    base::RunLoop().RunUntilIdle();
  }

  void OnComplete(int err) {
    completion_results_.push_back(err);
  }

  base::ScopedTempDir directory_;
  scoped_ptr<base::MessageLoop> message_loop_;
  scoped_refptr<base::MessageLoopProxy> cache_thread_;
  net::CompletionCallback completion_callback_;
  std::vector<int> completion_results_;

  static const int k10MBytes = 10 * 1024 * 1024;
};

TEST_F(AppCacheDiskCacheTest, DisablePriorToInitCompletion) {
  AppCacheDiskCache::Entry* entry = NULL;

  // Create an instance and start it initializing, queue up
  // one of each kind of "entry" function.
  scoped_ptr<AppCacheDiskCache> disk_cache(new AppCacheDiskCache);
  EXPECT_FALSE(disk_cache->is_disabled());
  disk_cache->InitWithDiskBackend(
      directory_.path(), k10MBytes, false, cache_thread_,
      completion_callback_);
  disk_cache->CreateEntry(1, &entry, completion_callback_);
  disk_cache->OpenEntry(2, &entry, completion_callback_);
  disk_cache->DoomEntry(3, completion_callback_);

  // Pull the plug on all that.
  EXPECT_FALSE(disk_cache->is_disabled());
  disk_cache->Disable();
  EXPECT_TRUE(disk_cache->is_disabled());

  FlushCacheTasks();

  EXPECT_EQ(NULL, entry);
  EXPECT_EQ(4u, completion_results_.size());
  for (std::vector<int>::const_iterator iter = completion_results_.begin();
       iter < completion_results_.end(); ++iter) {
    EXPECT_EQ(net::ERR_ABORTED, *iter);
  }

  // Ensure the directory can be deleted at this point.
  EXPECT_TRUE(base::DirectoryExists(directory_.path()));
  EXPECT_FALSE(base::IsDirectoryEmpty(directory_.path()));
  EXPECT_TRUE(base::DeleteFile(directory_.path(), true));
  EXPECT_FALSE(base::DirectoryExists(directory_.path()));
}

TEST_F(AppCacheDiskCacheTest, DisableAfterInitted) {
  // Create an instance and let it fully init.
  scoped_ptr<AppCacheDiskCache> disk_cache(new AppCacheDiskCache);
  EXPECT_FALSE(disk_cache->is_disabled());
  disk_cache->InitWithDiskBackend(
      directory_.path(), k10MBytes, false, cache_thread_,
      completion_callback_);
  FlushCacheTasks();
  EXPECT_EQ(1u, completion_results_.size());
  EXPECT_EQ(net::OK, completion_results_[0]);

  // Pull the plug
  disk_cache->Disable();
  FlushCacheTasks();

  // Ensure the directory can be deleted at this point.
  EXPECT_TRUE(base::DirectoryExists(directory_.path()));
  EXPECT_FALSE(base::IsDirectoryEmpty(directory_.path()));
  EXPECT_TRUE(base::DeleteFile(directory_.path(), true));
  EXPECT_FALSE(base::DirectoryExists(directory_.path()));

  // Methods should return immediately when disabled and not invoke
  // the callback at all.
  AppCacheDiskCache::Entry* entry = NULL;
  completion_results_.clear();
  EXPECT_EQ(net::ERR_ABORTED,
            disk_cache->CreateEntry(1, &entry, completion_callback_));
  EXPECT_EQ(net::ERR_ABORTED,
            disk_cache->OpenEntry(2, &entry, completion_callback_));
  EXPECT_EQ(net::ERR_ABORTED,
            disk_cache->DoomEntry(3, completion_callback_));
  FlushCacheTasks();
  EXPECT_TRUE(completion_results_.empty());
}

// Flaky on Android: http://crbug.com/339534
TEST_F(AppCacheDiskCacheTest, DISABLED_DisableWithEntriesOpen) {
  // Create an instance and let it fully init.
  scoped_ptr<AppCacheDiskCache> disk_cache(new AppCacheDiskCache);
  EXPECT_FALSE(disk_cache->is_disabled());
  disk_cache->InitWithDiskBackend(
      directory_.path(), k10MBytes, false, cache_thread_,
      completion_callback_);
  FlushCacheTasks();
  EXPECT_EQ(1u, completion_results_.size());
  EXPECT_EQ(net::OK, completion_results_[0]);

  // Note: We don't have detailed expectations of the DiskCache
  // operations because on android it's really SimpleCache which
  // does behave differently.
  //
  // What matters for the corruption handling and service reinitiazation
  // is that the directory can be deleted after the calling Disable() method,
  // and we do have expectations about that.

  // Create/open some entries.
  AppCacheDiskCache::Entry* entry1 = NULL;
  AppCacheDiskCache::Entry* entry2 = NULL;
  disk_cache->CreateEntry(1, &entry1, completion_callback_);
  disk_cache->CreateEntry(2, &entry2, completion_callback_);
  FlushCacheTasks();
  EXPECT_TRUE(entry1);
  EXPECT_TRUE(entry2);

  // Write something to one of the entries and flush it.
  const char* kData = "Hello";
  const int kDataLen = strlen(kData) + 1;
  scoped_refptr<net::IOBuffer> write_buf(new net::WrappedIOBuffer(kData));
  entry1->Write(0, 0, write_buf, kDataLen, completion_callback_);
  FlushCacheTasks();

  // Queue up a read and a write.
  scoped_refptr<net::IOBuffer> read_buf = new net::IOBuffer(kDataLen);
  entry1->Read(0, 0, read_buf.get(), kDataLen, completion_callback_);
  entry2->Write(0, 0, write_buf.get(), kDataLen, completion_callback_);

  // Pull the plug
  disk_cache->Disable();
  FlushCacheTasks();

  // Ensure the directory can be deleted at this point.
  EXPECT_TRUE(base::DirectoryExists(directory_.path()));
  EXPECT_FALSE(base::IsDirectoryEmpty(directory_.path()));
  EXPECT_TRUE(base::DeleteFile(directory_.path(), true));
  EXPECT_FALSE(base::DirectoryExists(directory_.path()));

  disk_cache.reset(NULL);

  // Also, new IO operations should fail immediately.
  EXPECT_EQ(
      net::ERR_ABORTED,
      entry1->Read(0, 0, read_buf.get(), kDataLen, completion_callback_));
  entry1->Close();
  entry2->Close();

  FlushCacheTasks();
}

}  // namespace content