1// Copyright 2013 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/files/scoped_temp_dir.h"
8#include "base/logging.h"
9#include "base/run_loop.h"
10#include "base/thread_task_runner_handle.h"
11#include "content/browser/browser_thread_impl.h"
12#include "content/browser/service_worker/service_worker_context_core.h"
13#include "content/browser/service_worker/service_worker_disk_cache.h"
14#include "content/browser/service_worker/service_worker_registration.h"
15#include "content/browser/service_worker/service_worker_storage.h"
16#include "content/browser/service_worker/service_worker_utils.h"
17#include "content/browser/service_worker/service_worker_version.h"
18#include "content/common/service_worker/service_worker_status_code.h"
19#include "content/public/test/test_browser_thread_bundle.h"
20#include "net/base/io_buffer.h"
21#include "net/base/net_errors.h"
22#include "net/base/test_completion_callback.h"
23#include "net/http/http_response_headers.h"
24#include "testing/gtest/include/gtest/gtest.h"
25
26using net::IOBuffer;
27using net::TestCompletionCallback;
28using net::WrappedIOBuffer;
29
30namespace content {
31
32namespace {
33
34typedef ServiceWorkerDatabase::RegistrationData RegistrationData;
35typedef ServiceWorkerDatabase::ResourceRecord ResourceRecord;
36
37void StatusCallback(bool* was_called,
38                    ServiceWorkerStatusCode* result,
39                    ServiceWorkerStatusCode status) {
40  *was_called = true;
41  *result = status;
42}
43
44ServiceWorkerStorage::StatusCallback MakeStatusCallback(
45    bool* was_called,
46    ServiceWorkerStatusCode* result) {
47  return base::Bind(&StatusCallback, was_called, result);
48}
49
50void FindCallback(
51    bool* was_called,
52    ServiceWorkerStatusCode* result,
53    scoped_refptr<ServiceWorkerRegistration>* found,
54    ServiceWorkerStatusCode status,
55    const scoped_refptr<ServiceWorkerRegistration>& registration) {
56  *was_called = true;
57  *result = status;
58  *found = registration;
59}
60
61ServiceWorkerStorage::FindRegistrationCallback MakeFindCallback(
62    bool* was_called,
63    ServiceWorkerStatusCode* result,
64    scoped_refptr<ServiceWorkerRegistration>* found) {
65  return base::Bind(&FindCallback, was_called, result, found);
66}
67
68void GetAllCallback(
69    bool* was_called,
70    std::vector<ServiceWorkerRegistrationInfo>* all_out,
71    const std::vector<ServiceWorkerRegistrationInfo>& all) {
72  *was_called = true;
73  *all_out = all;
74}
75
76ServiceWorkerStorage::GetAllRegistrationInfosCallback MakeGetAllCallback(
77    bool* was_called,
78    std::vector<ServiceWorkerRegistrationInfo>* all) {
79  return base::Bind(&GetAllCallback, was_called, all);
80}
81
82void OnIOComplete(int* rv_out, int rv) {
83  *rv_out = rv;
84}
85
86void OnCompareComplete(
87    ServiceWorkerStatusCode* status_out, bool* are_equal_out,
88    ServiceWorkerStatusCode status, bool are_equal) {
89  *status_out = status;
90  *are_equal_out = are_equal;
91}
92
93void WriteResponse(
94    ServiceWorkerStorage* storage, int64 id,
95    const std::string& headers,
96    IOBuffer* body, int length) {
97  scoped_ptr<ServiceWorkerResponseWriter> writer =
98      storage->CreateResponseWriter(id);
99
100  scoped_ptr<net::HttpResponseInfo> info(new net::HttpResponseInfo);
101  info->request_time = base::Time::Now();
102  info->response_time = base::Time::Now();
103  info->was_cached = false;
104  info->headers = new net::HttpResponseHeaders(headers);
105  scoped_refptr<HttpResponseInfoIOBuffer> info_buffer =
106      new HttpResponseInfoIOBuffer(info.release());
107
108  int rv = -1234;
109  writer->WriteInfo(info_buffer.get(), base::Bind(&OnIOComplete, &rv));
110  base::RunLoop().RunUntilIdle();
111  EXPECT_LT(0, rv);
112
113  rv = -1234;
114  writer->WriteData(body, length, base::Bind(&OnIOComplete, &rv));
115  base::RunLoop().RunUntilIdle();
116  EXPECT_EQ(length, rv);
117}
118
119void WriteStringResponse(
120    ServiceWorkerStorage* storage, int64 id,
121    const std::string& headers,
122    const std::string& body) {
123  scoped_refptr<IOBuffer> body_buffer(new WrappedIOBuffer(body.data()));
124  WriteResponse(storage, id, headers, body_buffer.get(), body.length());
125}
126
127void WriteBasicResponse(ServiceWorkerStorage* storage, int64 id) {
128  scoped_ptr<ServiceWorkerResponseWriter> writer =
129      storage->CreateResponseWriter(id);
130
131  const char kHttpHeaders[] = "HTTP/1.0 200 HONKYDORY\0Content-Length: 5\0\0";
132  const char kHttpBody[] = "Hello";
133  std::string headers(kHttpHeaders, arraysize(kHttpHeaders));
134  WriteStringResponse(storage, id, headers, std::string(kHttpBody));
135}
136
137bool VerifyBasicResponse(ServiceWorkerStorage* storage, int64 id,
138                         bool expected_positive_result) {
139  const std::string kExpectedHttpBody("Hello");
140  scoped_ptr<ServiceWorkerResponseReader> reader =
141      storage->CreateResponseReader(id);
142  scoped_refptr<HttpResponseInfoIOBuffer> info_buffer =
143      new HttpResponseInfoIOBuffer();
144  {
145    TestCompletionCallback cb;
146    reader->ReadInfo(info_buffer.get(), cb.callback());
147    int rv = cb.WaitForResult();
148    if (expected_positive_result)
149      EXPECT_LT(0, rv);
150    if (rv <= 0)
151      return false;
152  }
153
154  std::string received_body;
155  {
156    const int kBigEnough = 512;
157    scoped_refptr<net::IOBuffer> buffer = new IOBuffer(kBigEnough);
158    TestCompletionCallback cb;
159    reader->ReadData(buffer.get(), kBigEnough, cb.callback());
160    int rv = cb.WaitForResult();
161    EXPECT_EQ(static_cast<int>(kExpectedHttpBody.size()), rv);
162    if (rv <= 0)
163      return false;
164    received_body.assign(buffer->data(), rv);
165  }
166
167  bool status_match =
168      std::string("HONKYDORY") ==
169          info_buffer->http_info->headers->GetStatusText();
170  bool data_match = kExpectedHttpBody == received_body;
171
172  EXPECT_TRUE(status_match);
173  EXPECT_TRUE(data_match);
174  return status_match && data_match;
175}
176
177void WriteResponseOfSize(ServiceWorkerStorage* storage, int64 id,
178                         char val, int size) {
179  const char kHttpHeaders[] = "HTTP/1.0 200 HONKYDORY\00";
180  std::string headers(kHttpHeaders, arraysize(kHttpHeaders));
181  scoped_refptr<net::IOBuffer> buffer = new net::IOBuffer(size);
182  memset(buffer->data(), val, size);
183  WriteResponse(storage, id, headers, buffer.get(), size);
184}
185
186}  // namespace
187
188class ServiceWorkerStorageTest : public testing::Test {
189 public:
190  ServiceWorkerStorageTest()
191      : browser_thread_bundle_(TestBrowserThreadBundle::IO_MAINLOOP) {
192  }
193
194  virtual void SetUp() OVERRIDE {
195    context_.reset(
196        new ServiceWorkerContextCore(GetUserDataDirectory(),
197                                     base::ThreadTaskRunnerHandle::Get(),
198                                     base::ThreadTaskRunnerHandle::Get(),
199                                     base::ThreadTaskRunnerHandle::Get(),
200                                     NULL,
201                                     NULL,
202                                     NULL));
203    context_ptr_ = context_->AsWeakPtr();
204  }
205
206  virtual void TearDown() OVERRIDE {
207    context_.reset();
208  }
209
210  virtual base::FilePath GetUserDataDirectory() { return base::FilePath(); }
211
212  ServiceWorkerStorage* storage() { return context_->storage(); }
213
214  // A static class method for friendliness.
215  static void VerifyPurgeableListStatusCallback(
216      ServiceWorkerDatabase* database,
217      std::set<int64> *purgeable_ids,
218      bool* was_called,
219      ServiceWorkerStatusCode* result,
220      ServiceWorkerStatusCode status) {
221    *was_called = true;
222    *result = status;
223    EXPECT_EQ(ServiceWorkerDatabase::STATUS_OK,
224              database->GetPurgeableResourceIds(purgeable_ids));
225  }
226
227 protected:
228  ServiceWorkerStatusCode StoreRegistration(
229      scoped_refptr<ServiceWorkerRegistration> registration,
230      scoped_refptr<ServiceWorkerVersion> version) {
231    bool was_called = false;
232    ServiceWorkerStatusCode result = SERVICE_WORKER_ERROR_FAILED;
233    storage()->StoreRegistration(registration.get(),
234                                 version.get(),
235                                 MakeStatusCallback(&was_called, &result));
236    EXPECT_FALSE(was_called);  // always async
237    base::RunLoop().RunUntilIdle();
238    EXPECT_TRUE(was_called);
239    return result;
240  }
241
242  ServiceWorkerStatusCode DeleteRegistration(
243      int64 registration_id,
244      const GURL& origin) {
245    bool was_called = false;
246    ServiceWorkerStatusCode result = SERVICE_WORKER_ERROR_FAILED;
247    storage()->DeleteRegistration(
248        registration_id, origin, MakeStatusCallback(&was_called, &result));
249    EXPECT_FALSE(was_called);  // always async
250    base::RunLoop().RunUntilIdle();
251    EXPECT_TRUE(was_called);
252    return result;
253  }
254
255  void GetAllRegistrations(
256      std::vector<ServiceWorkerRegistrationInfo>* registrations) {
257    bool was_called = false;
258    storage()->GetAllRegistrations(
259        MakeGetAllCallback(&was_called, registrations));
260    EXPECT_FALSE(was_called);  // always async
261    base::RunLoop().RunUntilIdle();
262    EXPECT_TRUE(was_called);
263  }
264
265  ServiceWorkerStatusCode UpdateToActiveState(
266      scoped_refptr<ServiceWorkerRegistration> registration) {
267    bool was_called = false;
268    ServiceWorkerStatusCode result = SERVICE_WORKER_ERROR_FAILED;
269    storage()->UpdateToActiveState(registration.get(),
270                                   MakeStatusCallback(&was_called, &result));
271    EXPECT_FALSE(was_called);  // always async
272    base::RunLoop().RunUntilIdle();
273    EXPECT_TRUE(was_called);
274    return result;
275  }
276
277  void UpdateLastUpdateCheckTime(ServiceWorkerRegistration* registration) {
278    storage()->UpdateLastUpdateCheckTime(registration);
279    base::RunLoop().RunUntilIdle();
280  }
281
282  ServiceWorkerStatusCode FindRegistrationForDocument(
283      const GURL& document_url,
284      scoped_refptr<ServiceWorkerRegistration>* registration) {
285    bool was_called = false;
286    ServiceWorkerStatusCode result = SERVICE_WORKER_ERROR_FAILED;
287    storage()->FindRegistrationForDocument(
288        document_url, MakeFindCallback(&was_called, &result, registration));
289    base::RunLoop().RunUntilIdle();
290    EXPECT_TRUE(was_called);
291    return result;
292  }
293
294  ServiceWorkerStatusCode FindRegistrationForPattern(
295      const GURL& scope,
296      scoped_refptr<ServiceWorkerRegistration>* registration) {
297    bool was_called = false;
298    ServiceWorkerStatusCode result = SERVICE_WORKER_ERROR_FAILED;
299    storage()->FindRegistrationForPattern(
300        scope, MakeFindCallback(&was_called, &result, registration));
301    EXPECT_FALSE(was_called);  // always async
302    base::RunLoop().RunUntilIdle();
303    EXPECT_TRUE(was_called);
304    return result;
305  }
306
307  ServiceWorkerStatusCode FindRegistrationForId(
308      int64 registration_id,
309      const GURL& origin,
310      scoped_refptr<ServiceWorkerRegistration>* registration) {
311    bool was_called = false;
312    ServiceWorkerStatusCode result = SERVICE_WORKER_ERROR_FAILED;
313    storage()->FindRegistrationForId(
314        registration_id, origin,
315        MakeFindCallback(&was_called, &result, registration));
316    base::RunLoop().RunUntilIdle();
317    EXPECT_TRUE(was_called);
318    return result;
319  }
320
321  scoped_ptr<ServiceWorkerContextCore> context_;
322  base::WeakPtr<ServiceWorkerContextCore> context_ptr_;
323  TestBrowserThreadBundle browser_thread_bundle_;
324};
325
326TEST_F(ServiceWorkerStorageTest, StoreFindUpdateDeleteRegistration) {
327  const GURL kScope("http://www.test.not/scope/");
328  const GURL kScript("http://www.test.not/script.js");
329  const GURL kDocumentUrl("http://www.test.not/scope/document.html");
330  const int64 kRegistrationId = 0;
331  const int64 kVersionId = 0;
332  const base::Time kToday = base::Time::Now();
333  const base::Time kYesterday = kToday - base::TimeDelta::FromDays(1);
334
335  scoped_refptr<ServiceWorkerRegistration> found_registration;
336
337  // We shouldn't find anything without having stored anything.
338  EXPECT_EQ(SERVICE_WORKER_ERROR_NOT_FOUND,
339            FindRegistrationForDocument(kDocumentUrl, &found_registration));
340  EXPECT_FALSE(found_registration.get());
341
342  EXPECT_EQ(SERVICE_WORKER_ERROR_NOT_FOUND,
343            FindRegistrationForPattern(kScope, &found_registration));
344  EXPECT_FALSE(found_registration.get());
345
346  EXPECT_EQ(SERVICE_WORKER_ERROR_NOT_FOUND,
347            FindRegistrationForId(
348                kRegistrationId, kScope.GetOrigin(), &found_registration));
349  EXPECT_FALSE(found_registration.get());
350
351  // Store something.
352  scoped_refptr<ServiceWorkerRegistration> live_registration =
353      new ServiceWorkerRegistration(
354          kScope, kRegistrationId, context_ptr_);
355  scoped_refptr<ServiceWorkerVersion> live_version =
356      new ServiceWorkerVersion(
357          live_registration.get(), kScript, kVersionId, context_ptr_);
358  live_version->SetStatus(ServiceWorkerVersion::INSTALLED);
359  live_registration->SetWaitingVersion(live_version.get());
360  live_registration->set_last_update_check(kYesterday);
361  EXPECT_EQ(SERVICE_WORKER_OK,
362            StoreRegistration(live_registration, live_version));
363
364  // Now we should find it and get the live ptr back immediately.
365  EXPECT_EQ(SERVICE_WORKER_OK,
366            FindRegistrationForDocument(kDocumentUrl, &found_registration));
367  EXPECT_EQ(live_registration, found_registration);
368  found_registration = NULL;
369
370  // But FindRegistrationForPattern is always async.
371  EXPECT_EQ(SERVICE_WORKER_OK,
372            FindRegistrationForPattern(kScope, &found_registration));
373  EXPECT_EQ(live_registration, found_registration);
374  found_registration = NULL;
375
376  // Can be found by id too.
377  EXPECT_EQ(SERVICE_WORKER_OK,
378            FindRegistrationForId(
379                kRegistrationId, kScope.GetOrigin(), &found_registration));
380  ASSERT_TRUE(found_registration.get());
381  EXPECT_EQ(kRegistrationId, found_registration->id());
382  EXPECT_EQ(live_registration, found_registration);
383  found_registration = NULL;
384
385  // Drop the live registration, but keep the version live.
386  live_registration = NULL;
387
388  // Now FindRegistrationForDocument should be async.
389  EXPECT_EQ(SERVICE_WORKER_OK,
390            FindRegistrationForDocument(kDocumentUrl, &found_registration));
391  ASSERT_TRUE(found_registration.get());
392  EXPECT_EQ(kRegistrationId, found_registration->id());
393  EXPECT_TRUE(found_registration->HasOneRef());
394  EXPECT_EQ(live_version.get(), found_registration->waiting_version());
395  found_registration = NULL;
396
397  // Drop the live version too.
398  live_version = NULL;
399
400  // And FindRegistrationForPattern is always async.
401  EXPECT_EQ(SERVICE_WORKER_OK,
402            FindRegistrationForPattern(kScope, &found_registration));
403  ASSERT_TRUE(found_registration.get());
404  EXPECT_EQ(kRegistrationId, found_registration->id());
405  EXPECT_TRUE(found_registration->HasOneRef());
406  EXPECT_FALSE(found_registration->active_version());
407  ASSERT_TRUE(found_registration->waiting_version());
408  EXPECT_EQ(kYesterday, found_registration->last_update_check());
409  EXPECT_EQ(ServiceWorkerVersion::INSTALLED,
410            found_registration->waiting_version()->status());
411
412  // Update to active and update the last check time.
413  scoped_refptr<ServiceWorkerVersion> temp_version =
414      found_registration->waiting_version();
415  temp_version->SetStatus(ServiceWorkerVersion::ACTIVATED);
416  found_registration->SetActiveVersion(temp_version.get());
417  temp_version = NULL;
418  EXPECT_EQ(SERVICE_WORKER_OK, UpdateToActiveState(found_registration));
419  found_registration->set_last_update_check(kToday);
420  UpdateLastUpdateCheckTime(found_registration.get());
421
422  found_registration = NULL;
423
424  // Trying to update a unstored registration to active should fail.
425  scoped_refptr<ServiceWorkerRegistration> unstored_registration =
426      new ServiceWorkerRegistration(
427          kScope, kRegistrationId + 1, context_ptr_);
428  EXPECT_EQ(SERVICE_WORKER_ERROR_NOT_FOUND,
429            UpdateToActiveState(unstored_registration));
430  unstored_registration = NULL;
431
432  // The Find methods should return a registration with an active version
433  // and the expected update time.
434  EXPECT_EQ(SERVICE_WORKER_OK,
435            FindRegistrationForDocument(kDocumentUrl, &found_registration));
436  ASSERT_TRUE(found_registration.get());
437  EXPECT_EQ(kRegistrationId, found_registration->id());
438  EXPECT_TRUE(found_registration->HasOneRef());
439  EXPECT_FALSE(found_registration->waiting_version());
440  ASSERT_TRUE(found_registration->active_version());
441  EXPECT_EQ(ServiceWorkerVersion::ACTIVATED,
442            found_registration->active_version()->status());
443  EXPECT_EQ(kToday, found_registration->last_update_check());
444
445  // Delete from storage but with a instance still live.
446  EXPECT_TRUE(context_->GetLiveVersion(kRegistrationId));
447  EXPECT_EQ(SERVICE_WORKER_OK,
448            DeleteRegistration(kRegistrationId, kScope.GetOrigin()));
449  EXPECT_TRUE(context_->GetLiveVersion(kRegistrationId));
450
451  // Should no longer be found.
452  EXPECT_EQ(SERVICE_WORKER_ERROR_NOT_FOUND,
453            FindRegistrationForId(
454                kRegistrationId, kScope.GetOrigin(), &found_registration));
455  EXPECT_FALSE(found_registration.get());
456
457  // Deleting an unstored registration should succeed.
458  EXPECT_EQ(SERVICE_WORKER_OK,
459            DeleteRegistration(kRegistrationId + 1, kScope.GetOrigin()));
460}
461
462TEST_F(ServiceWorkerStorageTest, InstallingRegistrationsAreFindable) {
463  const GURL kScope("http://www.test.not/scope/");
464  const GURL kScript("http://www.test.not/script.js");
465  const GURL kDocumentUrl("http://www.test.not/scope/document.html");
466  const int64 kRegistrationId = 0;
467  const int64 kVersionId = 0;
468
469  scoped_refptr<ServiceWorkerRegistration> found_registration;
470
471  // Create an unstored registration.
472  scoped_refptr<ServiceWorkerRegistration> live_registration =
473      new ServiceWorkerRegistration(
474          kScope, kRegistrationId, context_ptr_);
475  scoped_refptr<ServiceWorkerVersion> live_version =
476      new ServiceWorkerVersion(
477          live_registration.get(), kScript, kVersionId, context_ptr_);
478  live_version->SetStatus(ServiceWorkerVersion::INSTALLING);
479  live_registration->SetWaitingVersion(live_version.get());
480
481  // Should not be findable, including by GetAllRegistrations.
482  EXPECT_EQ(SERVICE_WORKER_ERROR_NOT_FOUND,
483            FindRegistrationForId(
484                kRegistrationId, kScope.GetOrigin(), &found_registration));
485  EXPECT_FALSE(found_registration.get());
486
487  EXPECT_EQ(SERVICE_WORKER_ERROR_NOT_FOUND,
488            FindRegistrationForDocument(kDocumentUrl, &found_registration));
489  EXPECT_FALSE(found_registration.get());
490
491  EXPECT_EQ(SERVICE_WORKER_ERROR_NOT_FOUND,
492            FindRegistrationForPattern(kScope, &found_registration));
493  EXPECT_FALSE(found_registration.get());
494
495  std::vector<ServiceWorkerRegistrationInfo> all_registrations;
496  GetAllRegistrations(&all_registrations);
497  EXPECT_TRUE(all_registrations.empty());
498
499  // Notify storage of it being installed.
500  storage()->NotifyInstallingRegistration(live_registration.get());
501
502  // Now should be findable.
503  EXPECT_EQ(SERVICE_WORKER_OK,
504            FindRegistrationForId(
505                kRegistrationId, kScope.GetOrigin(), &found_registration));
506  EXPECT_EQ(live_registration, found_registration);
507  found_registration = NULL;
508
509  EXPECT_EQ(SERVICE_WORKER_OK,
510            FindRegistrationForDocument(kDocumentUrl, &found_registration));
511  EXPECT_EQ(live_registration, found_registration);
512  found_registration = NULL;
513
514  EXPECT_EQ(SERVICE_WORKER_OK,
515            FindRegistrationForPattern(kScope, &found_registration));
516  EXPECT_EQ(live_registration, found_registration);
517  found_registration = NULL;
518
519  GetAllRegistrations(&all_registrations);
520  EXPECT_EQ(1u, all_registrations.size());
521  all_registrations.clear();
522
523  // Notify storage of installation no longer happening.
524  storage()->NotifyDoneInstallingRegistration(
525      live_registration.get(), NULL, SERVICE_WORKER_OK);
526
527  // Once again, should not be findable.
528  EXPECT_EQ(SERVICE_WORKER_ERROR_NOT_FOUND,
529            FindRegistrationForId(
530                kRegistrationId, kScope.GetOrigin(), &found_registration));
531  EXPECT_FALSE(found_registration.get());
532
533  EXPECT_EQ(SERVICE_WORKER_ERROR_NOT_FOUND,
534            FindRegistrationForDocument(kDocumentUrl, &found_registration));
535  EXPECT_FALSE(found_registration.get());
536
537  EXPECT_EQ(SERVICE_WORKER_ERROR_NOT_FOUND,
538            FindRegistrationForPattern(kScope, &found_registration));
539  EXPECT_FALSE(found_registration.get());
540
541  GetAllRegistrations(&all_registrations);
542  EXPECT_TRUE(all_registrations.empty());
543}
544
545class ServiceWorkerResourceStorageTest : public ServiceWorkerStorageTest {
546 public:
547  virtual void SetUp() OVERRIDE {
548    ServiceWorkerStorageTest::SetUp();
549
550    storage()->LazyInitialize(base::Bind(&base::DoNothing));
551    base::RunLoop().RunUntilIdle();
552    scope_ = GURL("http://www.test.not/scope/");
553    script_ = GURL("http://www.test.not/script.js");
554    import_ = GURL("http://www.test.not/import.js");
555    document_url_ = GURL("http://www.test.not/scope/document.html");
556    registration_id_ = storage()->NewRegistrationId();
557    version_id_ = storage()->NewVersionId();
558    resource_id1_ = storage()->NewResourceId();
559    resource_id2_ = storage()->NewResourceId();
560
561    // Cons up a new registration+version with two script resources.
562    RegistrationData data;
563    data.registration_id = registration_id_;
564    data.scope = scope_;
565    data.script = script_;
566    data.version_id = version_id_;
567    data.is_active = false;
568    std::vector<ResourceRecord> resources;
569    resources.push_back(ResourceRecord(resource_id1_, script_));
570    resources.push_back(ResourceRecord(resource_id2_, import_));
571    registration_ = storage()->GetOrCreateRegistration(data, resources);
572    registration_->waiting_version()->SetStatus(ServiceWorkerVersion::NEW);
573
574    // Add the resources ids to the uncommitted list.
575    storage()->StoreUncommittedResponseId(resource_id1_);
576    storage()->StoreUncommittedResponseId(resource_id2_);
577    base::RunLoop().RunUntilIdle();
578    std::set<int64> verify_ids;
579    EXPECT_EQ(ServiceWorkerDatabase::STATUS_OK,
580              storage()->database_->GetUncommittedResourceIds(&verify_ids));
581    EXPECT_EQ(2u, verify_ids.size());
582
583    // And dump something in the disk cache for them.
584    WriteBasicResponse(storage(), resource_id1_);
585    WriteBasicResponse(storage(), resource_id2_);
586    EXPECT_TRUE(VerifyBasicResponse(storage(), resource_id1_, true));
587    EXPECT_TRUE(VerifyBasicResponse(storage(), resource_id2_, true));
588
589    // Storing the registration/version should take the resources ids out
590    // of the uncommitted list.
591    EXPECT_EQ(
592        SERVICE_WORKER_OK,
593        StoreRegistration(registration_, registration_->waiting_version()));
594    verify_ids.clear();
595    EXPECT_EQ(ServiceWorkerDatabase::STATUS_OK,
596              storage()->database_->GetUncommittedResourceIds(&verify_ids));
597    EXPECT_TRUE(verify_ids.empty());
598  }
599
600 protected:
601  GURL scope_;
602  GURL script_;
603  GURL import_;
604  GURL document_url_;
605  int64 registration_id_;
606  int64 version_id_;
607  int64 resource_id1_;
608  int64 resource_id2_;
609  scoped_refptr<ServiceWorkerRegistration> registration_;
610};
611
612class ServiceWorkerResourceStorageDiskTest
613    : public ServiceWorkerResourceStorageTest {
614 public:
615  virtual void SetUp() OVERRIDE {
616    ASSERT_TRUE(user_data_directory_.CreateUniqueTempDir());
617    ServiceWorkerResourceStorageTest::SetUp();
618  }
619
620  virtual base::FilePath GetUserDataDirectory() OVERRIDE {
621    return user_data_directory_.path();
622  }
623
624 protected:
625  base::ScopedTempDir user_data_directory_;
626};
627
628TEST_F(ServiceWorkerResourceStorageTest, DeleteRegistration_NoLiveVersion) {
629  bool was_called = false;
630  ServiceWorkerStatusCode result = SERVICE_WORKER_ERROR_FAILED;
631  std::set<int64> verify_ids;
632
633  registration_->SetWaitingVersion(NULL);
634  registration_ = NULL;
635
636  // Deleting the registration should result in the resources being added to the
637  // purgeable list and then doomed in the disk cache and removed from that
638  // list.
639  storage()->DeleteRegistration(
640      registration_id_,
641      scope_.GetOrigin(),
642      base::Bind(&VerifyPurgeableListStatusCallback,
643                 base::Unretained(storage()->database_.get()),
644                 &verify_ids,
645                 &was_called,
646                 &result));
647  base::RunLoop().RunUntilIdle();
648  ASSERT_TRUE(was_called);
649  EXPECT_EQ(SERVICE_WORKER_OK, result);
650  EXPECT_EQ(2u, verify_ids.size());
651  verify_ids.clear();
652  EXPECT_EQ(ServiceWorkerDatabase::STATUS_OK,
653            storage()->database_->GetPurgeableResourceIds(&verify_ids));
654  EXPECT_TRUE(verify_ids.empty());
655
656  EXPECT_FALSE(VerifyBasicResponse(storage(), resource_id1_, false));
657  EXPECT_FALSE(VerifyBasicResponse(storage(), resource_id2_, false));
658}
659
660TEST_F(ServiceWorkerResourceStorageTest, DeleteRegistration_WaitingVersion) {
661  bool was_called = false;
662  ServiceWorkerStatusCode result = SERVICE_WORKER_ERROR_FAILED;
663  std::set<int64> verify_ids;
664
665  // Deleting the registration should result in the resources being added to the
666  // purgeable list and then doomed in the disk cache and removed from that
667  // list.
668  storage()->DeleteRegistration(
669      registration_->id(),
670      scope_.GetOrigin(),
671      base::Bind(&VerifyPurgeableListStatusCallback,
672                 base::Unretained(storage()->database_.get()),
673                 &verify_ids,
674                 &was_called,
675                 &result));
676  base::RunLoop().RunUntilIdle();
677  ASSERT_TRUE(was_called);
678  EXPECT_EQ(SERVICE_WORKER_OK, result);
679  EXPECT_EQ(2u, verify_ids.size());
680  verify_ids.clear();
681  EXPECT_EQ(ServiceWorkerDatabase::STATUS_OK,
682            storage()->database_->GetPurgeableResourceIds(&verify_ids));
683  EXPECT_EQ(2u, verify_ids.size());
684
685  EXPECT_TRUE(VerifyBasicResponse(storage(), resource_id1_, false));
686  EXPECT_TRUE(VerifyBasicResponse(storage(), resource_id2_, false));
687
688  // Doom the version, now it happens.
689  registration_->waiting_version()->Doom();
690  base::RunLoop().RunUntilIdle();
691  EXPECT_EQ(SERVICE_WORKER_OK, result);
692  EXPECT_EQ(2u, verify_ids.size());
693  verify_ids.clear();
694  EXPECT_EQ(ServiceWorkerDatabase::STATUS_OK,
695            storage()->database_->GetPurgeableResourceIds(&verify_ids));
696  EXPECT_TRUE(verify_ids.empty());
697
698  EXPECT_FALSE(VerifyBasicResponse(storage(), resource_id1_, false));
699  EXPECT_FALSE(VerifyBasicResponse(storage(), resource_id2_, false));
700}
701
702TEST_F(ServiceWorkerResourceStorageTest, DeleteRegistration_ActiveVersion) {
703  // Promote the worker to active and add a controllee.
704  registration_->SetActiveVersion(registration_->waiting_version());
705  storage()->UpdateToActiveState(
706      registration_.get(), base::Bind(&ServiceWorkerUtils::NoOpStatusCallback));
707  scoped_ptr<ServiceWorkerProviderHost> host(
708      new ServiceWorkerProviderHost(33 /* dummy render process id */,
709                                    1 /* dummy provider_id */,
710                                    context_->AsWeakPtr(),
711                                    NULL));
712  registration_->active_version()->AddControllee(host.get());
713
714  bool was_called = false;
715  ServiceWorkerStatusCode result = SERVICE_WORKER_ERROR_FAILED;
716  std::set<int64> verify_ids;
717
718  // Deleting the registration should move the resources to the purgeable list
719  // but keep them available.
720  storage()->DeleteRegistration(
721      registration_->id(),
722      scope_.GetOrigin(),
723      base::Bind(&VerifyPurgeableListStatusCallback,
724                 base::Unretained(storage()->database_.get()),
725                 &verify_ids,
726                 &was_called,
727                 &result));
728  registration_->active_version()->Doom();
729  base::RunLoop().RunUntilIdle();
730  ASSERT_TRUE(was_called);
731  EXPECT_EQ(SERVICE_WORKER_OK, result);
732  EXPECT_EQ(2u, verify_ids.size());
733  verify_ids.clear();
734  EXPECT_EQ(ServiceWorkerDatabase::STATUS_OK,
735            storage()->database_->GetPurgeableResourceIds(&verify_ids));
736  EXPECT_EQ(2u, verify_ids.size());
737
738  EXPECT_TRUE(VerifyBasicResponse(storage(), resource_id1_, true));
739  EXPECT_TRUE(VerifyBasicResponse(storage(), resource_id2_, true));
740
741  // Removing the controllee should cause the resources to be deleted.
742  registration_->active_version()->RemoveControllee(host.get());
743  base::RunLoop().RunUntilIdle();
744  verify_ids.clear();
745  EXPECT_EQ(ServiceWorkerDatabase::STATUS_OK,
746            storage()->database_->GetPurgeableResourceIds(&verify_ids));
747  EXPECT_TRUE(verify_ids.empty());
748
749  EXPECT_FALSE(VerifyBasicResponse(storage(), resource_id1_, false));
750  EXPECT_FALSE(VerifyBasicResponse(storage(), resource_id2_, false));
751}
752
753// Android has flaky IO error: http://crbug.com/387045
754#if defined(OS_ANDROID)
755#define MAYBE_CleanupOnRestart DISABLED_CleanupOnRestart
756#else
757#define MAYBE_CleanupOnRestart CleanupOnRestart
758#endif
759TEST_F(ServiceWorkerResourceStorageDiskTest, MAYBE_CleanupOnRestart) {
760  // Promote the worker to active and add a controllee.
761  registration_->SetActiveVersion(registration_->waiting_version());
762  registration_->SetWaitingVersion(NULL);
763  storage()->UpdateToActiveState(
764      registration_.get(), base::Bind(&ServiceWorkerUtils::NoOpStatusCallback));
765  scoped_ptr<ServiceWorkerProviderHost> host(
766      new ServiceWorkerProviderHost(33 /* dummy render process id */,
767                                    1 /* dummy provider_id */,
768                                    context_->AsWeakPtr(),
769                                    NULL));
770  registration_->active_version()->AddControllee(host.get());
771
772  bool was_called = false;
773  ServiceWorkerStatusCode result = SERVICE_WORKER_ERROR_FAILED;
774  std::set<int64> verify_ids;
775
776  // Deleting the registration should move the resources to the purgeable list
777  // but keep them available.
778  storage()->DeleteRegistration(
779      registration_->id(),
780      scope_.GetOrigin(),
781      base::Bind(&VerifyPurgeableListStatusCallback,
782                 base::Unretained(storage()->database_.get()),
783                 &verify_ids,
784                 &was_called,
785                 &result));
786  base::RunLoop().RunUntilIdle();
787  ASSERT_TRUE(was_called);
788  EXPECT_EQ(SERVICE_WORKER_OK, result);
789  EXPECT_EQ(2u, verify_ids.size());
790  verify_ids.clear();
791  EXPECT_EQ(ServiceWorkerDatabase::STATUS_OK,
792            storage()->database_->GetPurgeableResourceIds(&verify_ids));
793  EXPECT_EQ(2u, verify_ids.size());
794
795  EXPECT_TRUE(VerifyBasicResponse(storage(), resource_id1_, true));
796  EXPECT_TRUE(VerifyBasicResponse(storage(), resource_id2_, true));
797
798  // Also add an uncommitted resource.
799  int64 kStaleUncommittedResourceId = storage()->NewResourceId();
800  storage()->StoreUncommittedResponseId(kStaleUncommittedResourceId);
801  base::RunLoop().RunUntilIdle();
802  verify_ids.clear();
803  EXPECT_EQ(ServiceWorkerDatabase::STATUS_OK,
804            storage()->database_->GetUncommittedResourceIds(&verify_ids));
805  EXPECT_EQ(1u, verify_ids.size());
806  WriteBasicResponse(storage(), kStaleUncommittedResourceId);
807  EXPECT_TRUE(
808      VerifyBasicResponse(storage(), kStaleUncommittedResourceId, true));
809
810  // Simulate browser shutdown. The purgeable and uncommitted resources are now
811  // stale.
812  context_.reset();
813  context_.reset(
814      new ServiceWorkerContextCore(GetUserDataDirectory(),
815                                   base::ThreadTaskRunnerHandle::Get(),
816                                   base::ThreadTaskRunnerHandle::Get(),
817                                   base::ThreadTaskRunnerHandle::Get(),
818                                   NULL,
819                                   NULL,
820                                   NULL));
821  storage()->LazyInitialize(base::Bind(&base::DoNothing));
822  base::RunLoop().RunUntilIdle();
823
824  // Store a new uncommitted resource. This triggers stale resource cleanup.
825  int64 kNewResourceId = storage()->NewResourceId();
826  WriteBasicResponse(storage(), kNewResourceId);
827  storage()->StoreUncommittedResponseId(kNewResourceId);
828  base::RunLoop().RunUntilIdle();
829
830  // The stale resources should be purged, but the new resource should persist.
831  verify_ids.clear();
832  EXPECT_EQ(ServiceWorkerDatabase::STATUS_OK,
833            storage()->database_->GetUncommittedResourceIds(&verify_ids));
834  ASSERT_EQ(1u, verify_ids.size());
835  EXPECT_EQ(kNewResourceId, *verify_ids.begin());
836
837  verify_ids.clear();
838  EXPECT_EQ(ServiceWorkerDatabase::STATUS_OK,
839            storage()->database_->GetPurgeableResourceIds(&verify_ids));
840  EXPECT_TRUE(verify_ids.empty());
841  EXPECT_FALSE(VerifyBasicResponse(storage(), resource_id1_, false));
842  EXPECT_FALSE(VerifyBasicResponse(storage(), resource_id2_, false));
843  EXPECT_FALSE(
844      VerifyBasicResponse(storage(), kStaleUncommittedResourceId, false));
845  EXPECT_TRUE(VerifyBasicResponse(storage(), kNewResourceId, true));
846}
847
848TEST_F(ServiceWorkerResourceStorageTest, UpdateRegistration) {
849  // Promote the worker to active worker and add a controllee.
850  registration_->SetActiveVersion(registration_->waiting_version());
851  storage()->UpdateToActiveState(
852      registration_.get(), base::Bind(&ServiceWorkerUtils::NoOpStatusCallback));
853  scoped_ptr<ServiceWorkerProviderHost> host(
854      new ServiceWorkerProviderHost(33 /* dummy render process id */,
855                                    1 /* dummy provider_id */,
856                                    context_->AsWeakPtr(),
857                                    NULL));
858  registration_->active_version()->AddControllee(host.get());
859
860  bool was_called = false;
861  ServiceWorkerStatusCode result = SERVICE_WORKER_ERROR_FAILED;
862  std::set<int64> verify_ids;
863
864  // Make an updated registration.
865  scoped_refptr<ServiceWorkerVersion> live_version = new ServiceWorkerVersion(
866      registration_.get(), script_, storage()->NewVersionId(), context_ptr_);
867  live_version->SetStatus(ServiceWorkerVersion::NEW);
868  registration_->SetWaitingVersion(live_version.get());
869
870  // Writing the registration should move the old version's resources to the
871  // purgeable list but keep them available.
872  storage()->StoreRegistration(
873      registration_.get(),
874      registration_->waiting_version(),
875      base::Bind(&VerifyPurgeableListStatusCallback,
876                 base::Unretained(storage()->database_.get()),
877                 &verify_ids,
878                 &was_called,
879                 &result));
880  registration_->active_version()->Doom();
881  base::RunLoop().RunUntilIdle();
882  ASSERT_TRUE(was_called);
883  EXPECT_EQ(SERVICE_WORKER_OK, result);
884  EXPECT_EQ(2u, verify_ids.size());
885  verify_ids.clear();
886  EXPECT_EQ(ServiceWorkerDatabase::STATUS_OK,
887            storage()->database_->GetPurgeableResourceIds(&verify_ids));
888  EXPECT_EQ(2u, verify_ids.size());
889
890  EXPECT_TRUE(VerifyBasicResponse(storage(), resource_id1_, false));
891  EXPECT_TRUE(VerifyBasicResponse(storage(), resource_id2_, false));
892
893  // Removing the controllee should cause the old version's resources to be
894  // deleted.
895  registration_->active_version()->RemoveControllee(host.get());
896  base::RunLoop().RunUntilIdle();
897  verify_ids.clear();
898  EXPECT_EQ(ServiceWorkerDatabase::STATUS_OK,
899            storage()->database_->GetPurgeableResourceIds(&verify_ids));
900  EXPECT_TRUE(verify_ids.empty());
901
902  EXPECT_FALSE(VerifyBasicResponse(storage(), resource_id1_, false));
903  EXPECT_FALSE(VerifyBasicResponse(storage(), resource_id2_, false));
904}
905
906TEST_F(ServiceWorkerStorageTest, FindRegistration_LongestScopeMatch) {
907  const GURL kDocumentUrl("http://www.example.com/scope/foo");
908  scoped_refptr<ServiceWorkerRegistration> found_registration;
909
910  // Registration for "/scope/".
911  const GURL kScope1("http://www.example.com/scope/");
912  const GURL kScript1("http://www.example.com/script1.js");
913  const int64 kRegistrationId1 = 1;
914  const int64 kVersionId1 = 1;
915  scoped_refptr<ServiceWorkerRegistration> live_registration1 =
916      new ServiceWorkerRegistration(
917          kScope1, kRegistrationId1, context_ptr_);
918  scoped_refptr<ServiceWorkerVersion> live_version1 =
919      new ServiceWorkerVersion(
920          live_registration1.get(), kScript1, kVersionId1, context_ptr_);
921  live_version1->SetStatus(ServiceWorkerVersion::INSTALLED);
922  live_registration1->SetWaitingVersion(live_version1.get());
923
924  // Registration for "/scope/foo".
925  const GURL kScope2("http://www.example.com/scope/foo");
926  const GURL kScript2("http://www.example.com/script2.js");
927  const int64 kRegistrationId2 = 2;
928  const int64 kVersionId2 = 2;
929  scoped_refptr<ServiceWorkerRegistration> live_registration2 =
930      new ServiceWorkerRegistration(
931          kScope2, kRegistrationId2, context_ptr_);
932  scoped_refptr<ServiceWorkerVersion> live_version2 =
933      new ServiceWorkerVersion(
934          live_registration2.get(), kScript2, kVersionId2, context_ptr_);
935  live_version2->SetStatus(ServiceWorkerVersion::INSTALLED);
936  live_registration2->SetWaitingVersion(live_version2.get());
937
938  // Registration for "/scope/foobar".
939  const GURL kScope3("http://www.example.com/scope/foobar");
940  const GURL kScript3("http://www.example.com/script3.js");
941  const int64 kRegistrationId3 = 3;
942  const int64 kVersionId3 = 3;
943  scoped_refptr<ServiceWorkerRegistration> live_registration3 =
944      new ServiceWorkerRegistration(
945          kScope3, kRegistrationId3, context_ptr_);
946  scoped_refptr<ServiceWorkerVersion> live_version3 =
947      new ServiceWorkerVersion(
948          live_registration3.get(), kScript3, kVersionId3, context_ptr_);
949  live_version3->SetStatus(ServiceWorkerVersion::INSTALLED);
950  live_registration3->SetWaitingVersion(live_version3.get());
951
952  // Notify storage of they being installed.
953  storage()->NotifyInstallingRegistration(live_registration1.get());
954  storage()->NotifyInstallingRegistration(live_registration2.get());
955  storage()->NotifyInstallingRegistration(live_registration3.get());
956
957  // Find a registration among installing ones.
958  EXPECT_EQ(SERVICE_WORKER_OK,
959            FindRegistrationForDocument(kDocumentUrl, &found_registration));
960  EXPECT_EQ(live_registration2, found_registration);
961  found_registration = NULL;
962
963  // Store registrations.
964  EXPECT_EQ(SERVICE_WORKER_OK,
965            StoreRegistration(live_registration1, live_version1));
966  EXPECT_EQ(SERVICE_WORKER_OK,
967            StoreRegistration(live_registration2, live_version2));
968  EXPECT_EQ(SERVICE_WORKER_OK,
969            StoreRegistration(live_registration3, live_version3));
970
971  // Notify storage of installations no longer happening.
972  storage()->NotifyDoneInstallingRegistration(
973      live_registration1.get(), NULL, SERVICE_WORKER_OK);
974  storage()->NotifyDoneInstallingRegistration(
975      live_registration2.get(), NULL, SERVICE_WORKER_OK);
976  storage()->NotifyDoneInstallingRegistration(
977      live_registration3.get(), NULL, SERVICE_WORKER_OK);
978
979  // Find a registration among installed ones.
980  EXPECT_EQ(SERVICE_WORKER_OK,
981            FindRegistrationForDocument(kDocumentUrl, &found_registration));
982  EXPECT_EQ(live_registration2, found_registration);
983}
984
985TEST_F(ServiceWorkerStorageTest, CompareResources) {
986  // Compare two small responses containing the same data.
987  WriteBasicResponse(storage(), 1);
988  WriteBasicResponse(storage(), 2);
989  ServiceWorkerStatusCode status = static_cast<ServiceWorkerStatusCode>(-1);
990  bool are_equal = false;
991  storage()->CompareScriptResources(
992      1, 2,
993      base::Bind(&OnCompareComplete, &status, &are_equal));
994  base::RunLoop().RunUntilIdle();
995  EXPECT_EQ(SERVICE_WORKER_OK, status);
996  EXPECT_TRUE(are_equal);
997
998  // Compare two small responses with different data.
999  const char kHttpHeaders[] = "HTTP/1.0 200 HONKYDORY\0\0";
1000  const char kHttpBody[] = "Goodbye";
1001  std::string headers(kHttpHeaders, arraysize(kHttpHeaders));
1002  WriteStringResponse(storage(), 3, headers, std::string(kHttpBody));
1003  status = static_cast<ServiceWorkerStatusCode>(-1);
1004  are_equal = true;
1005  storage()->CompareScriptResources(
1006      1, 3,
1007      base::Bind(&OnCompareComplete, &status, &are_equal));
1008  base::RunLoop().RunUntilIdle();
1009  EXPECT_EQ(SERVICE_WORKER_OK, status);
1010  EXPECT_FALSE(are_equal);
1011
1012  // Compare two large responses with the same data.
1013  const int k32K = 32 * 1024;
1014  WriteResponseOfSize(storage(), 4, 'a', k32K);
1015  WriteResponseOfSize(storage(), 5, 'a', k32K);
1016  status = static_cast<ServiceWorkerStatusCode>(-1);
1017  are_equal = false;
1018  storage()->CompareScriptResources(
1019      4, 5,
1020      base::Bind(&OnCompareComplete, &status, &are_equal));
1021  base::RunLoop().RunUntilIdle();
1022  EXPECT_EQ(SERVICE_WORKER_OK, status);
1023  EXPECT_TRUE(are_equal);
1024
1025  // Compare a large and small response.
1026  status = static_cast<ServiceWorkerStatusCode>(-1);
1027  are_equal = true;
1028  storage()->CompareScriptResources(
1029      1, 5,
1030      base::Bind(&OnCompareComplete, &status, &are_equal));
1031  base::RunLoop().RunUntilIdle();
1032  EXPECT_EQ(SERVICE_WORKER_OK, status);
1033  EXPECT_FALSE(are_equal);
1034
1035  // Compare two large responses with different data.
1036  WriteResponseOfSize(storage(), 6, 'b', k32K);
1037  status = static_cast<ServiceWorkerStatusCode>(-1);
1038  are_equal = true;
1039  storage()->CompareScriptResources(
1040      5, 6,
1041      base::Bind(&OnCompareComplete, &status, &are_equal));
1042  base::RunLoop().RunUntilIdle();
1043  EXPECT_EQ(SERVICE_WORKER_OK, status);
1044  EXPECT_FALSE(are_equal);
1045}
1046
1047}  // namespace content
1048