Update To 11.40.268.0
[platform/framework/web/crosswalk.git] / src / content / browser / dom_storage / dom_storage_area_unittest.cc
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 "base/bind.h"
6 #include "base/files/file_util.h"
7 #include "base/files/scoped_temp_dir.h"
8 #include "base/message_loop/message_loop.h"
9 #include "base/message_loop/message_loop_proxy.h"
10 #include "base/strings/utf_string_conversions.h"
11 #include "base/threading/sequenced_worker_pool.h"
12 #include "base/time/time.h"
13 #include "content/browser/dom_storage/dom_storage_area.h"
14 #include "content/browser/dom_storage/dom_storage_database.h"
15 #include "content/browser/dom_storage/dom_storage_database_adapter.h"
16 #include "content/browser/dom_storage/dom_storage_task_runner.h"
17 #include "content/browser/dom_storage/local_storage_database_adapter.h"
18 #include "content/common/dom_storage/dom_storage_types.h"
19 #include "testing/gtest/include/gtest/gtest.h"
20
21 using base::ASCIIToUTF16;
22
23 namespace content {
24
25 class DOMStorageAreaTest : public testing::Test {
26  public:
27   DOMStorageAreaTest()
28     : kOrigin(GURL("http://dom_storage/")),
29       kKey(ASCIIToUTF16("key")),
30       kValue(ASCIIToUTF16("value")),
31       kKey2(ASCIIToUTF16("key2")),
32       kValue2(ASCIIToUTF16("value2")) {
33   }
34
35   const GURL kOrigin;
36   const base::string16 kKey;
37   const base::string16 kValue;
38   const base::string16 kKey2;
39   const base::string16 kValue2;
40
41   // Method used in the CommitTasks test case.
42   void InjectedCommitSequencingTask(DOMStorageArea* area) {
43     // At this point the OnCommitTimer has run.
44     // Verify that it put a commit in flight.
45     EXPECT_EQ(1, area->commit_batches_in_flight_);
46     EXPECT_FALSE(area->commit_batch_.get());
47     EXPECT_TRUE(area->HasUncommittedChanges());
48     // Make additional change and verify that a new commit batch
49     // is created for that change.
50     base::NullableString16 old_value;
51     EXPECT_TRUE(area->SetItem(kKey2, kValue2, &old_value));
52     EXPECT_TRUE(area->commit_batch_.get());
53     EXPECT_EQ(1, area->commit_batches_in_flight_);
54     EXPECT_TRUE(area->HasUncommittedChanges());
55   }
56
57   // Class used in the CommitChangesAtShutdown test case.
58   class VerifyChangesCommittedDatabase : public DOMStorageDatabase {
59    public:
60     VerifyChangesCommittedDatabase() {}
61     ~VerifyChangesCommittedDatabase() override {
62       const base::string16 kKey(ASCIIToUTF16("key"));
63       const base::string16 kValue(ASCIIToUTF16("value"));
64       DOMStorageValuesMap values;
65       ReadAllValues(&values);
66       EXPECT_EQ(1u, values.size());
67       EXPECT_EQ(kValue, values[kKey].string());
68     }
69   };
70
71  private:
72   base::MessageLoop message_loop_;
73 };
74
75 TEST_F(DOMStorageAreaTest, DOMStorageAreaBasics) {
76   scoped_refptr<DOMStorageArea> area(
77       new DOMStorageArea(1, std::string(), kOrigin, NULL, NULL));
78   base::string16 old_value;
79   base::NullableString16 old_nullable_value;
80   scoped_refptr<DOMStorageArea> copy;
81
82   // We don't focus on the underlying DOMStorageMap functionality
83   // since that's covered by seperate unit tests.
84   EXPECT_EQ(kOrigin, area->origin());
85   EXPECT_EQ(1, area->namespace_id());
86   EXPECT_EQ(0u, area->Length());
87   EXPECT_TRUE(area->SetItem(kKey, kValue, &old_nullable_value));
88   EXPECT_TRUE(area->SetItem(kKey2, kValue2, &old_nullable_value));
89   EXPECT_FALSE(area->HasUncommittedChanges());
90
91   // Verify that a copy shares the same map.
92   copy = area->ShallowCopy(2, std::string());
93   EXPECT_EQ(kOrigin, copy->origin());
94   EXPECT_EQ(2, copy->namespace_id());
95   EXPECT_EQ(area->Length(), copy->Length());
96   EXPECT_EQ(area->GetItem(kKey).string(), copy->GetItem(kKey).string());
97   EXPECT_EQ(area->Key(0).string(), copy->Key(0).string());
98   EXPECT_EQ(copy->map_.get(), area->map_.get());
99
100   // But will deep copy-on-write as needed.
101   EXPECT_TRUE(area->RemoveItem(kKey, &old_value));
102   EXPECT_NE(copy->map_.get(), area->map_.get());
103   copy = area->ShallowCopy(2, std::string());
104   EXPECT_EQ(copy->map_.get(), area->map_.get());
105   EXPECT_TRUE(area->SetItem(kKey, kValue, &old_nullable_value));
106   EXPECT_NE(copy->map_.get(), area->map_.get());
107   copy = area->ShallowCopy(2, std::string());
108   EXPECT_EQ(copy->map_.get(), area->map_.get());
109   EXPECT_NE(0u, area->Length());
110   EXPECT_TRUE(area->Clear());
111   EXPECT_EQ(0u, area->Length());
112   EXPECT_NE(copy->map_.get(), area->map_.get());
113
114   // Verify that once Shutdown(), behaves that way.
115   area->Shutdown();
116   EXPECT_TRUE(area->is_shutdown_);
117   EXPECT_FALSE(area->map_.get());
118   EXPECT_EQ(0u, area->Length());
119   EXPECT_TRUE(area->Key(0).is_null());
120   EXPECT_TRUE(area->GetItem(kKey).is_null());
121   EXPECT_FALSE(area->SetItem(kKey, kValue, &old_nullable_value));
122   EXPECT_FALSE(area->RemoveItem(kKey, &old_value));
123   EXPECT_FALSE(area->Clear());
124 }
125
126 TEST_F(DOMStorageAreaTest, BackingDatabaseOpened) {
127   const int64 kSessionStorageNamespaceId = kLocalStorageNamespaceId + 1;
128   base::ScopedTempDir temp_dir;
129   ASSERT_TRUE(temp_dir.CreateUniqueTempDir());
130   const base::FilePath kExpectedOriginFilePath = temp_dir.path().Append(
131       DOMStorageArea::DatabaseFileNameFromOrigin(kOrigin));
132
133   // No directory, backing should be null.
134   {
135     scoped_refptr<DOMStorageArea> area(
136         new DOMStorageArea(kOrigin, base::FilePath(), NULL));
137     EXPECT_EQ(NULL, area->backing_.get());
138     EXPECT_TRUE(area->is_initial_import_done_);
139     EXPECT_FALSE(base::PathExists(kExpectedOriginFilePath));
140   }
141
142   // Valid directory and origin but no session storage backing. Backing should
143   // be null.
144   {
145     scoped_refptr<DOMStorageArea> area(
146         new DOMStorageArea(kSessionStorageNamespaceId, std::string(), kOrigin,
147                            NULL, NULL));
148     EXPECT_EQ(NULL, area->backing_.get());
149     EXPECT_TRUE(area->is_initial_import_done_);
150
151     base::NullableString16 old_value;
152     EXPECT_TRUE(area->SetItem(kKey, kValue, &old_value));
153     ASSERT_TRUE(old_value.is_null());
154
155     // Check that saving a value has still left us without a backing database.
156     EXPECT_EQ(NULL, area->backing_.get());
157     EXPECT_FALSE(base::PathExists(kExpectedOriginFilePath));
158   }
159
160   // This should set up a DOMStorageArea that is correctly backed to disk.
161   {
162     scoped_refptr<DOMStorageArea> area(new DOMStorageArea(
163         kOrigin,
164         temp_dir.path(),
165         new MockDOMStorageTaskRunner(base::MessageLoopProxy::current().get())));
166
167     EXPECT_TRUE(area->backing_.get());
168     DOMStorageDatabase* database = static_cast<LocalStorageDatabaseAdapter*>(
169         area->backing_.get())->db_.get();
170     EXPECT_FALSE(database->IsOpen());
171     EXPECT_FALSE(area->is_initial_import_done_);
172
173     // Inject an in-memory db to speed up the test.
174     // We will verify that something is written into the database but not
175     // that a file is written to disk - DOMStorageDatabase unit tests cover
176     // that.
177     area->backing_.reset(new LocalStorageDatabaseAdapter());
178
179     // Need to write something to ensure that the database is created.
180     base::NullableString16 old_value;
181     EXPECT_TRUE(area->SetItem(kKey, kValue, &old_value));
182     ASSERT_TRUE(old_value.is_null());
183     EXPECT_TRUE(area->is_initial_import_done_);
184     EXPECT_TRUE(area->commit_batch_.get());
185     EXPECT_EQ(0, area->commit_batches_in_flight_);
186
187     base::MessageLoop::current()->RunUntilIdle();
188
189     EXPECT_FALSE(area->commit_batch_.get());
190     EXPECT_EQ(0, area->commit_batches_in_flight_);
191     database = static_cast<LocalStorageDatabaseAdapter*>(
192         area->backing_.get())->db_.get();
193     EXPECT_TRUE(database->IsOpen());
194     EXPECT_EQ(1u, area->Length());
195     EXPECT_EQ(kValue, area->GetItem(kKey).string());
196
197     // Verify the content made it to the in memory database.
198     DOMStorageValuesMap values;
199     area->backing_->ReadAllValues(&values);
200     EXPECT_EQ(1u, values.size());
201     EXPECT_EQ(kValue, values[kKey].string());
202   }
203 }
204
205 TEST_F(DOMStorageAreaTest, CommitTasks) {
206   base::ScopedTempDir temp_dir;
207   ASSERT_TRUE(temp_dir.CreateUniqueTempDir());
208
209   scoped_refptr<DOMStorageArea> area(new DOMStorageArea(
210       kOrigin,
211       temp_dir.path(),
212       new MockDOMStorageTaskRunner(base::MessageLoopProxy::current().get())));
213   // Inject an in-memory db to speed up the test.
214   area->backing_.reset(new LocalStorageDatabaseAdapter());
215
216   // Unrelated to commits, but while we're here, see that querying Length()
217   // causes the backing database to be opened and presumably read from.
218   EXPECT_FALSE(area->is_initial_import_done_);
219   EXPECT_EQ(0u, area->Length());
220   EXPECT_TRUE(area->is_initial_import_done_);
221
222   DOMStorageValuesMap values;
223   base::NullableString16 old_value;
224
225   // See that changes are batched up.
226   EXPECT_FALSE(area->commit_batch_.get());
227   EXPECT_TRUE(area->SetItem(kKey, kValue, &old_value));
228   EXPECT_TRUE(area->HasUncommittedChanges());
229   EXPECT_TRUE(area->commit_batch_.get());
230   EXPECT_FALSE(area->commit_batch_->clear_all_first);
231   EXPECT_EQ(1u, area->commit_batch_->changed_values.size());
232   EXPECT_TRUE(area->SetItem(kKey2, kValue2, &old_value));
233   EXPECT_TRUE(area->commit_batch_.get());
234   EXPECT_FALSE(area->commit_batch_->clear_all_first);
235   EXPECT_EQ(2u, area->commit_batch_->changed_values.size());
236   base::MessageLoop::current()->RunUntilIdle();
237   EXPECT_FALSE(area->HasUncommittedChanges());
238   EXPECT_FALSE(area->commit_batch_.get());
239   EXPECT_EQ(0, area->commit_batches_in_flight_);
240   // Verify the changes made it to the database.
241   values.clear();
242   area->backing_->ReadAllValues(&values);
243   EXPECT_EQ(2u, values.size());
244   EXPECT_EQ(kValue, values[kKey].string());
245   EXPECT_EQ(kValue2, values[kKey2].string());
246
247   // See that clear is handled properly.
248   EXPECT_TRUE(area->Clear());
249   EXPECT_TRUE(area->commit_batch_.get());
250   EXPECT_TRUE(area->commit_batch_->clear_all_first);
251   EXPECT_TRUE(area->commit_batch_->changed_values.empty());
252   base::MessageLoop::current()->RunUntilIdle();
253   EXPECT_FALSE(area->commit_batch_.get());
254   EXPECT_EQ(0, area->commit_batches_in_flight_);
255   // Verify the changes made it to the database.
256   values.clear();
257   area->backing_->ReadAllValues(&values);
258   EXPECT_TRUE(values.empty());
259
260   // See that if changes accrue while a commit is "in flight"
261   // those will also get committed.
262   EXPECT_TRUE(area->SetItem(kKey, kValue, &old_value));
263   EXPECT_TRUE(area->HasUncommittedChanges());
264   // At this point the OnCommitTimer task has been posted. We inject
265   // another task in the queue that will execute after the timer task,
266   // but before the CommitChanges task. From within our injected task,
267   // we'll make an additional SetItem() call.
268   base::MessageLoop::current()->PostTask(
269       FROM_HERE,
270       base::Bind(&DOMStorageAreaTest::InjectedCommitSequencingTask,
271                  base::Unretained(this),
272                  area));
273   base::MessageLoop::current()->RunUntilIdle();
274   EXPECT_TRUE(area->HasOneRef());
275   EXPECT_FALSE(area->HasUncommittedChanges());
276   // Verify the changes made it to the database.
277   values.clear();
278   area->backing_->ReadAllValues(&values);
279   EXPECT_EQ(2u, values.size());
280   EXPECT_EQ(kValue, values[kKey].string());
281   EXPECT_EQ(kValue2, values[kKey2].string());
282 }
283
284 TEST_F(DOMStorageAreaTest, CommitChangesAtShutdown) {
285   base::ScopedTempDir temp_dir;
286   ASSERT_TRUE(temp_dir.CreateUniqueTempDir());
287   scoped_refptr<DOMStorageArea> area(new DOMStorageArea(
288       kOrigin,
289       temp_dir.path(),
290       new MockDOMStorageTaskRunner(base::MessageLoopProxy::current().get())));
291
292   // Inject an in-memory db to speed up the test and also to verify
293   // the final changes are commited in it's dtor.
294   static_cast<LocalStorageDatabaseAdapter*>(area->backing_.get())->db_.reset(
295       new VerifyChangesCommittedDatabase());
296
297   DOMStorageValuesMap values;
298   base::NullableString16 old_value;
299   EXPECT_TRUE(area->SetItem(kKey, kValue, &old_value));
300   EXPECT_TRUE(area->HasUncommittedChanges());
301   area->backing_->ReadAllValues(&values);
302   EXPECT_TRUE(values.empty());  // not committed yet
303   area->Shutdown();
304   base::MessageLoop::current()->RunUntilIdle();
305   EXPECT_TRUE(area->HasOneRef());
306   EXPECT_FALSE(area->backing_.get());
307   // The VerifyChangesCommittedDatabase destructor verifies values
308   // were committed.
309 }
310
311 TEST_F(DOMStorageAreaTest, DeleteOrigin) {
312   base::ScopedTempDir temp_dir;
313   ASSERT_TRUE(temp_dir.CreateUniqueTempDir());
314   scoped_refptr<DOMStorageArea> area(new DOMStorageArea(
315       kOrigin,
316       temp_dir.path(),
317       new MockDOMStorageTaskRunner(base::MessageLoopProxy::current().get())));
318
319   // This test puts files on disk.
320   base::FilePath db_file_path = static_cast<LocalStorageDatabaseAdapter*>(
321       area->backing_.get())->db_->file_path();
322   base::FilePath db_journal_file_path =
323       DOMStorageDatabase::GetJournalFilePath(db_file_path);
324
325   // Nothing bad should happen when invoked w/o any files on disk.
326   area->DeleteOrigin();
327   EXPECT_FALSE(base::PathExists(db_file_path));
328
329   // Commit something in the database and then delete.
330   base::NullableString16 old_value;
331   area->SetItem(kKey, kValue, &old_value);
332   base::MessageLoop::current()->RunUntilIdle();
333   EXPECT_TRUE(base::PathExists(db_file_path));
334   area->DeleteOrigin();
335   EXPECT_EQ(0u, area->Length());
336   EXPECT_FALSE(base::PathExists(db_file_path));
337   EXPECT_FALSE(base::PathExists(db_journal_file_path));
338
339   // Put some uncommitted changes to a non-existing database in
340   // and then delete. No file ever gets created in this case.
341   area->SetItem(kKey, kValue, &old_value);
342   EXPECT_TRUE(area->HasUncommittedChanges());
343   EXPECT_EQ(1u, area->Length());
344   area->DeleteOrigin();
345   EXPECT_TRUE(area->HasUncommittedChanges());
346   EXPECT_EQ(0u, area->Length());
347   base::MessageLoop::current()->RunUntilIdle();
348   EXPECT_FALSE(area->HasUncommittedChanges());
349   EXPECT_FALSE(base::PathExists(db_file_path));
350
351   // Put some uncommitted changes to a an existing database in
352   // and then delete.
353   area->SetItem(kKey, kValue, &old_value);
354   base::MessageLoop::current()->RunUntilIdle();
355   EXPECT_TRUE(base::PathExists(db_file_path));
356   area->SetItem(kKey2, kValue2, &old_value);
357   EXPECT_TRUE(area->HasUncommittedChanges());
358   EXPECT_EQ(2u, area->Length());
359   area->DeleteOrigin();
360   EXPECT_TRUE(area->HasUncommittedChanges());
361   EXPECT_EQ(0u, area->Length());
362   base::MessageLoop::current()->RunUntilIdle();
363   EXPECT_FALSE(area->HasUncommittedChanges());
364   // Since the area had uncommitted changes at the time delete
365   // was called, the file will linger until the shutdown time.
366   EXPECT_TRUE(base::PathExists(db_file_path));
367   area->Shutdown();
368   base::MessageLoop::current()->RunUntilIdle();
369   EXPECT_FALSE(base::PathExists(db_file_path));
370 }
371
372 TEST_F(DOMStorageAreaTest, PurgeMemory) {
373   base::ScopedTempDir temp_dir;
374   ASSERT_TRUE(temp_dir.CreateUniqueTempDir());
375   scoped_refptr<DOMStorageArea> area(new DOMStorageArea(
376       kOrigin,
377       temp_dir.path(),
378       new MockDOMStorageTaskRunner(base::MessageLoopProxy::current().get())));
379
380   // Inject an in-memory db to speed up the test.
381   area->backing_.reset(new LocalStorageDatabaseAdapter());
382
383   // Unowned ptrs we use to verify that 'purge' has happened.
384   DOMStorageDatabase* original_backing =
385       static_cast<LocalStorageDatabaseAdapter*>(
386           area->backing_.get())->db_.get();
387   DOMStorageMap* original_map = area->map_.get();
388
389   // Should do no harm when called on a newly constructed object.
390   EXPECT_FALSE(area->is_initial_import_done_);
391   area->PurgeMemory();
392   EXPECT_FALSE(area->is_initial_import_done_);
393   DOMStorageDatabase* new_backing = static_cast<LocalStorageDatabaseAdapter*>(
394       area->backing_.get())->db_.get();
395   EXPECT_EQ(original_backing, new_backing);
396   EXPECT_EQ(original_map, area->map_.get());
397
398   // Should not do anything when commits are pending.
399   base::NullableString16 old_value;
400   area->SetItem(kKey, kValue, &old_value);
401   EXPECT_TRUE(area->is_initial_import_done_);
402   EXPECT_TRUE(area->HasUncommittedChanges());
403   area->PurgeMemory();
404   EXPECT_TRUE(area->is_initial_import_done_);
405   EXPECT_TRUE(area->HasUncommittedChanges());
406   new_backing = static_cast<LocalStorageDatabaseAdapter*>(
407       area->backing_.get())->db_.get();
408   EXPECT_EQ(original_backing, new_backing);
409   EXPECT_EQ(original_map, area->map_.get());
410
411   // Commit the changes from above,
412   base::MessageLoop::current()->RunUntilIdle();
413   EXPECT_FALSE(area->HasUncommittedChanges());
414   new_backing = static_cast<LocalStorageDatabaseAdapter*>(
415       area->backing_.get())->db_.get();
416   EXPECT_EQ(original_backing, new_backing);
417   EXPECT_EQ(original_map, area->map_.get());
418
419   // Should drop caches and reset database connections
420   // when invoked on an area that's loaded up primed.
421   area->PurgeMemory();
422   EXPECT_FALSE(area->is_initial_import_done_);
423   new_backing = static_cast<LocalStorageDatabaseAdapter*>(
424       area->backing_.get())->db_.get();
425   EXPECT_NE(original_backing, new_backing);
426   EXPECT_NE(original_map, area->map_.get());
427 }
428
429 TEST_F(DOMStorageAreaTest, DatabaseFileNames) {
430   struct {
431     const char* origin;
432     const char* file_name;
433     const char* journal_file_name;
434   } kCases[] = {
435     { "https://www.google.com/",
436       "https_www.google.com_0.localstorage",
437       "https_www.google.com_0.localstorage-journal" },
438     { "http://www.google.com:8080/",
439       "http_www.google.com_8080.localstorage",
440       "http_www.google.com_8080.localstorage-journal" },
441     { "file:///",
442       "file__0.localstorage",
443       "file__0.localstorage-journal" },
444   };
445
446   for (size_t i = 0; i < arraysize(kCases); ++i) {
447     GURL origin = GURL(kCases[i].origin).GetOrigin();
448     base::FilePath file_name =
449         base::FilePath().AppendASCII(kCases[i].file_name);
450     base::FilePath journal_file_name =
451         base::FilePath().AppendASCII(kCases[i].journal_file_name);
452
453     EXPECT_EQ(file_name,
454               DOMStorageArea::DatabaseFileNameFromOrigin(origin));
455     EXPECT_EQ(origin,
456               DOMStorageArea::OriginFromDatabaseFileName(file_name));
457     EXPECT_EQ(journal_file_name,
458               DOMStorageDatabase::GetJournalFilePath(file_name));
459   }
460
461   // Also test some DOMStorageDatabase::GetJournalFilePath cases here.
462   base::FilePath parent = base::FilePath().AppendASCII("a").AppendASCII("b");
463   EXPECT_EQ(
464       parent.AppendASCII("file-journal"),
465       DOMStorageDatabase::GetJournalFilePath(parent.AppendASCII("file")));
466   EXPECT_EQ(
467       base::FilePath().AppendASCII("-journal"),
468       DOMStorageDatabase::GetJournalFilePath(base::FilePath()));
469   EXPECT_EQ(
470       base::FilePath().AppendASCII(".extensiononly-journal"),
471       DOMStorageDatabase::GetJournalFilePath(
472           base::FilePath().AppendASCII(".extensiononly")));
473 }
474
475 }  // namespace content