appcache_service_unittest.cc revision 0529e5d033099cbfc42635f6f6183833b09dff6e
1// Copyright 2014 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 <string>
6
7#include "base/bind.h"
8#include "base/bind_helpers.h"
9#include "base/pickle.h"
10#include "base/run_loop.h"
11#include "content/browser/appcache/mock_appcache_storage.h"
12#include "net/base/completion_callback.h"
13#include "net/base/io_buffer.h"
14#include "net/http/http_response_headers.h"
15#include "testing/gtest/include/gtest/gtest.h"
16#include "webkit/browser/appcache/appcache_response.h"
17#include "webkit/browser/appcache/appcache_service.h"
18
19using appcache::AppCache;
20using appcache::AppCacheEntry;
21using appcache::AppCacheGroup;
22using appcache::AppCacheInfo;
23using appcache::AppCacheInfoCollection;
24using appcache::AppCacheInfoVector;
25using appcache::AppCacheResponseReader;
26using appcache::AppCacheService;
27using appcache::HttpResponseInfoIOBuffer;
28
29namespace content {
30namespace {
31
32const int64 kMockGroupId = 1;
33const int64 kMockCacheId = 1;
34const int64 kMockResponseId = 1;
35const int64 kMissingCacheId = 5;
36const int64 kMissingResponseId = 5;
37const char kMockHeaders[] =
38    "HTTP/1.0 200 OK\0Content-Length: 5\0\0";
39const char kMockBody[] = "Hello";
40const int kMockBodySize = 5;
41
42class MockResponseReader : public AppCacheResponseReader {
43 public:
44  MockResponseReader(int64 response_id,
45                     net::HttpResponseInfo* info, int info_size,
46                     const char* data, int data_size)
47      : AppCacheResponseReader(response_id, 0, NULL),
48        info_(info), info_size_(info_size),
49        data_(data), data_size_(data_size) {
50  }
51  virtual void ReadInfo(HttpResponseInfoIOBuffer* info_buf,
52                        const net::CompletionCallback& callback) OVERRIDE {
53    info_buffer_ = info_buf;
54    callback_ = callback;  // Cleared on completion.
55
56    int rv = info_.get() ? info_size_ : net::ERR_FAILED;
57    info_buffer_->http_info.reset(info_.release());
58    info_buffer_->response_data_size = data_size_;
59    ScheduleUserCallback(rv);
60  }
61  virtual void ReadData(net::IOBuffer* buf, int buf_len,
62                        const net::CompletionCallback& callback) OVERRIDE {
63    buffer_ = buf;
64    buffer_len_ = buf_len;
65    callback_ = callback;  // Cleared on completion.
66
67    if (!data_) {
68      ScheduleUserCallback(net::ERR_CACHE_READ_FAILURE);
69      return;
70    }
71    DCHECK(buf_len >= data_size_);
72    memcpy(buf->data(), data_, data_size_);
73    ScheduleUserCallback(data_size_);
74    data_size_ = 0;
75  }
76
77 private:
78  void ScheduleUserCallback(int result) {
79    base::MessageLoop::current()->PostTask(FROM_HERE,
80        base::Bind(&MockResponseReader::InvokeUserCompletionCallback,
81                   weak_factory_.GetWeakPtr(), result));
82  }
83
84  scoped_ptr<net::HttpResponseInfo> info_;
85  int info_size_;
86  const char* data_;
87  int data_size_;
88};
89
90}  // namespace
91
92
93class AppCacheServiceTest : public testing::Test {
94 public:
95  AppCacheServiceTest()
96      : kOrigin("http://hello/"),
97        kManifestUrl(kOrigin.Resolve("manifest")),
98        service_(new AppCacheService(NULL)),
99        delete_result_(net::OK), delete_completion_count_(0),
100        deletion_callback_(
101            base::Bind(&AppCacheServiceTest::OnDeleteAppCachesComplete,
102                       base::Unretained(this))) {
103    // Setup to use mock storage.
104    service_->storage_.reset(new MockAppCacheStorage(service_.get()));
105  }
106
107  void OnDeleteAppCachesComplete(int result) {
108    delete_result_ = result;
109    ++delete_completion_count_;
110  }
111
112  MockAppCacheStorage* mock_storage() {
113    return static_cast<MockAppCacheStorage*>(service_->storage());
114  }
115
116  void ResetStorage() {
117    service_->storage_.reset(new MockAppCacheStorage(service_.get()));
118  }
119
120  bool IsGroupStored(const GURL& manifest_url) {
121    return mock_storage()->IsGroupForManifestStored(manifest_url);
122  }
123
124  int CountPendingHelpers() {
125    return service_->pending_helpers_.size();
126  }
127
128  void SetupMockGroup() {
129    scoped_ptr<net::HttpResponseInfo> info(MakeMockResponseInfo());
130    const int kMockInfoSize = GetResponseInfoSize(info.get());
131
132    // Create a mock group, cache, and entry and stuff them into mock storage.
133    scoped_refptr<AppCacheGroup> group(
134        new AppCacheGroup(service_->storage(), kManifestUrl, kMockGroupId));
135    scoped_refptr<AppCache> cache(
136        new AppCache(service_->storage(), kMockCacheId));
137    cache->AddEntry(
138        kManifestUrl,
139        AppCacheEntry(AppCacheEntry::MANIFEST, kMockResponseId,
140                      kMockInfoSize + kMockBodySize));
141    cache->set_complete(true);
142    group->AddCache(cache.get());
143    mock_storage()->AddStoredGroup(group.get());
144    mock_storage()->AddStoredCache(cache.get());
145  }
146
147  void SetupMockReader(
148      bool valid_info, bool valid_data, bool valid_size) {
149    net::HttpResponseInfo* info = valid_info ? MakeMockResponseInfo() : NULL;
150    int info_size = info ? GetResponseInfoSize(info) : 0;
151    const char* data = valid_data ? kMockBody : NULL;
152    int data_size = valid_size ? kMockBodySize : 3;
153    mock_storage()->SimulateResponseReader(
154        new MockResponseReader(kMockResponseId, info, info_size,
155                               data, data_size));
156  }
157
158  net::HttpResponseInfo* MakeMockResponseInfo() {
159    net::HttpResponseInfo* info = new net::HttpResponseInfo;
160    info->request_time = base::Time::Now();
161    info->response_time = base::Time::Now();
162    info->was_cached = false;
163    info->headers = new net::HttpResponseHeaders(
164        std::string(kMockHeaders, arraysize(kMockHeaders)));
165    return info;
166  }
167
168  int GetResponseInfoSize(const net::HttpResponseInfo* info) {
169    Pickle pickle;
170    return PickleResponseInfo(&pickle, info);
171  }
172
173  int PickleResponseInfo(Pickle* pickle, const net::HttpResponseInfo* info) {
174    const bool kSkipTransientHeaders = true;
175    const bool kTruncated = false;
176    info->Persist(pickle, kSkipTransientHeaders, kTruncated);
177    return pickle->size();
178  }
179
180  const GURL kOrigin;
181  const GURL kManifestUrl;
182
183  scoped_ptr<AppCacheService> service_;
184  int delete_result_;
185  int delete_completion_count_;
186  net::CompletionCallback deletion_callback_;
187
188 private:
189  base::MessageLoop message_loop_;
190};
191
192TEST_F(AppCacheServiceTest, DeleteAppCachesForOrigin) {
193  // Without giving mock storage simiulated info, should fail.
194  service_->DeleteAppCachesForOrigin(kOrigin, deletion_callback_);
195  EXPECT_EQ(0, delete_completion_count_);
196  base::RunLoop().RunUntilIdle();
197  EXPECT_EQ(1, delete_completion_count_);
198  EXPECT_EQ(net::ERR_FAILED, delete_result_);
199  delete_completion_count_ = 0;
200
201  // Should succeed given an empty info collection.
202  mock_storage()->SimulateGetAllInfo(new AppCacheInfoCollection);
203  service_->DeleteAppCachesForOrigin(kOrigin, deletion_callback_);
204  EXPECT_EQ(0, delete_completion_count_);
205  base::RunLoop().RunUntilIdle();
206  EXPECT_EQ(1, delete_completion_count_);
207  EXPECT_EQ(net::OK, delete_result_);
208  delete_completion_count_ = 0;
209
210  scoped_refptr<AppCacheInfoCollection> info(new AppCacheInfoCollection);
211
212  // Should succeed given a non-empty info collection.
213  AppCacheInfo mock_manifest_1;
214  AppCacheInfo mock_manifest_2;
215  AppCacheInfo mock_manifest_3;
216  mock_manifest_1.manifest_url = kOrigin.Resolve("manifest1");
217  mock_manifest_2.manifest_url = kOrigin.Resolve("manifest2");
218  mock_manifest_3.manifest_url = kOrigin.Resolve("manifest3");
219  AppCacheInfoVector info_vector;
220  info_vector.push_back(mock_manifest_1);
221  info_vector.push_back(mock_manifest_2);
222  info_vector.push_back(mock_manifest_3);
223  info->infos_by_origin[kOrigin] = info_vector;
224  mock_storage()->SimulateGetAllInfo(info.get());
225  service_->DeleteAppCachesForOrigin(kOrigin, deletion_callback_);
226  EXPECT_EQ(0, delete_completion_count_);
227  base::RunLoop().RunUntilIdle();
228  EXPECT_EQ(1, delete_completion_count_);
229  EXPECT_EQ(net::OK, delete_result_);
230  delete_completion_count_ = 0;
231
232  // Should fail if storage fails to delete.
233  info->infos_by_origin[kOrigin] = info_vector;
234  mock_storage()->SimulateGetAllInfo(info.get());
235  mock_storage()->SimulateMakeGroupObsoleteFailure();
236  service_->DeleteAppCachesForOrigin(kOrigin, deletion_callback_);
237  EXPECT_EQ(0, delete_completion_count_);
238  base::RunLoop().RunUntilIdle();
239  EXPECT_EQ(1, delete_completion_count_);
240  EXPECT_EQ(net::ERR_FAILED, delete_result_);
241  delete_completion_count_ = 0;
242
243  // Should complete with abort error if the service is deleted
244  // prior to a delete completion.
245  service_->DeleteAppCachesForOrigin(kOrigin, deletion_callback_);
246  EXPECT_EQ(0, delete_completion_count_);
247  service_.reset();  // kill it
248  EXPECT_EQ(1, delete_completion_count_);
249  EXPECT_EQ(net::ERR_ABORTED, delete_result_);
250  delete_completion_count_ = 0;
251
252  // Let any tasks lingering from the sudden deletion run and verify
253  // no other completion calls occur.
254  base::RunLoop().RunUntilIdle();
255  EXPECT_EQ(0, delete_completion_count_);
256}
257
258TEST_F(AppCacheServiceTest, CheckAppCacheResponse) {
259  // Check a non-existing manifest.
260  EXPECT_FALSE(IsGroupStored(kManifestUrl));
261  service_->CheckAppCacheResponse(kManifestUrl, 1, 1);
262  base::RunLoop().RunUntilIdle();
263  EXPECT_EQ(0, CountPendingHelpers());
264  EXPECT_FALSE(IsGroupStored(kManifestUrl));
265  ResetStorage();
266
267  // Check a response that looks good.
268  // Nothing should be deleted.
269  SetupMockGroup();
270  EXPECT_TRUE(IsGroupStored(kManifestUrl));
271  SetupMockReader(true, true, true);
272  service_->CheckAppCacheResponse(kManifestUrl, kMockCacheId, kMockResponseId);
273  base::RunLoop().RunUntilIdle();
274  EXPECT_EQ(0, CountPendingHelpers());
275  EXPECT_TRUE(IsGroupStored(kManifestUrl));
276  ResetStorage();
277
278  // Check a response for which there is no cache entry.
279  // The group should get deleted.
280  SetupMockGroup();
281  service_->CheckAppCacheResponse(kManifestUrl, kMockCacheId,
282                                  kMissingResponseId);
283  base::RunLoop().RunUntilIdle();
284  EXPECT_EQ(0, CountPendingHelpers());
285  EXPECT_FALSE(IsGroupStored(kManifestUrl));
286  ResetStorage();
287
288  // Check a response for which there is no manifest entry in a newer version
289  // of the cache. Nothing should get deleted in this case.
290  SetupMockGroup();
291  service_->CheckAppCacheResponse(kManifestUrl, kMissingCacheId,
292                                  kMissingResponseId);
293  base::RunLoop().RunUntilIdle();
294  EXPECT_EQ(0, CountPendingHelpers());
295  EXPECT_TRUE(IsGroupStored(kManifestUrl));
296  ResetStorage();
297
298  // Check a response with bad headers.
299  SetupMockGroup();
300  service_->CheckAppCacheResponse(kManifestUrl, kMockCacheId, kMockResponseId);
301  SetupMockReader(false, true, true);
302  base::RunLoop().RunUntilIdle();
303  EXPECT_EQ(0, CountPendingHelpers());
304  EXPECT_FALSE(IsGroupStored(kManifestUrl));
305  ResetStorage();
306
307  // Check a response with bad data.
308  SetupMockGroup();
309  service_->CheckAppCacheResponse(kManifestUrl, kMockCacheId, kMockResponseId);
310  SetupMockReader(true, false, true);
311  base::RunLoop().RunUntilIdle();
312  EXPECT_EQ(0, CountPendingHelpers());
313  EXPECT_FALSE(IsGroupStored(kManifestUrl));
314  ResetStorage();
315
316  // Check a response with truncated data.
317  SetupMockGroup();
318  service_->CheckAppCacheResponse(kManifestUrl, kMockCacheId, kMockResponseId);
319  SetupMockReader(true, true, false);
320  base::RunLoop().RunUntilIdle();
321  EXPECT_EQ(0, CountPendingHelpers());
322  EXPECT_FALSE(IsGroupStored(kManifestUrl));
323  ResetStorage();
324
325  service_.reset();  // Clean up.
326  base::RunLoop().RunUntilIdle();
327}
328
329// Just tests the backoff scheduling function, not the actual reinit function.
330TEST_F(AppCacheServiceTest, ScheduleReinitialize) {
331  const base::TimeDelta kNoDelay;
332  const base::TimeDelta kOneSecond(base::TimeDelta::FromSeconds(1));
333  const base::TimeDelta k30Seconds(base::TimeDelta::FromSeconds(30));
334  const base::TimeDelta kOneHour(base::TimeDelta::FromHours(1));
335
336  // Do things get initialized as expected?
337  scoped_ptr<AppCacheService> service(new AppCacheService(NULL));
338  EXPECT_TRUE(service->last_reinit_time_.is_null());
339  EXPECT_FALSE(service->reinit_timer_.IsRunning());
340  EXPECT_EQ(kNoDelay, service->next_reinit_delay_);
341
342  // Do we see artifacts of the timer pending and such?
343  service->ScheduleReinitialize();
344  EXPECT_TRUE(service->reinit_timer_.IsRunning());
345  EXPECT_EQ(kNoDelay, service->reinit_timer_.GetCurrentDelay());
346  EXPECT_EQ(k30Seconds, service->next_reinit_delay_);
347
348  // Nothing should change if already scheduled
349  service->ScheduleReinitialize();
350  EXPECT_TRUE(service->reinit_timer_.IsRunning());
351  EXPECT_EQ(kNoDelay, service->reinit_timer_.GetCurrentDelay());
352  EXPECT_EQ(k30Seconds, service->next_reinit_delay_);
353
354  // Does the delay increase as expected?
355  service->reinit_timer_.Stop();
356  service->last_reinit_time_ = base::Time::Now() - kOneSecond;
357  service->ScheduleReinitialize();
358  EXPECT_TRUE(service->reinit_timer_.IsRunning());
359  EXPECT_EQ(k30Seconds, service->reinit_timer_.GetCurrentDelay());
360  EXPECT_EQ(k30Seconds + k30Seconds, service->next_reinit_delay_);
361
362  // Does the delay reset as expected?
363  service->reinit_timer_.Stop();
364  service->last_reinit_time_ = base::Time::Now() -
365                               base::TimeDelta::FromHours(2);
366  service->ScheduleReinitialize();
367  EXPECT_TRUE(service->reinit_timer_.IsRunning());
368  EXPECT_EQ(kNoDelay, service->reinit_timer_.GetCurrentDelay());
369  EXPECT_EQ(k30Seconds, service->next_reinit_delay_);
370
371  // Does the delay max out as expected?
372  service->reinit_timer_.Stop();
373  service->last_reinit_time_ = base::Time::Now() - kOneSecond;
374  service->next_reinit_delay_ = kOneHour;
375  service->ScheduleReinitialize();
376  EXPECT_TRUE(service->reinit_timer_.IsRunning());
377  EXPECT_EQ(kOneHour, service->reinit_timer_.GetCurrentDelay());
378  EXPECT_EQ(kOneHour, service->next_reinit_delay_);
379
380  // Fine to delete while pending.
381  service.reset(NULL);
382}
383
384
385
386}  // namespace content
387