Supervised user whitelists: Cleanup
[chromium-blink-merge.git] / content / browser / indexed_db / indexed_db_browsertest.cc
blob9ad5b4dad2af2bed36f863a435a8ad6ef282f61e
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.
5 #include "base/bind.h"
6 #include "base/command_line.h"
7 #include "base/files/file.h"
8 #include "base/files/file_enumerator.h"
9 #include "base/files/file_path.h"
10 #include "base/files/file_util.h"
11 #include "base/lazy_instance.h"
12 #include "base/memory/ref_counted.h"
13 #include "base/message_loop/message_loop.h"
14 #include "base/strings/utf_string_conversions.h"
15 #include "base/test/thread_test_helper.h"
16 #include "content/browser/browser_main_loop.h"
17 #include "content/browser/indexed_db/indexed_db_class_factory.h"
18 #include "content/browser/indexed_db/indexed_db_context_impl.h"
19 #include "content/browser/indexed_db/mock_browsertest_indexed_db_class_factory.h"
20 #include "content/browser/web_contents/web_contents_impl.h"
21 #include "content/public/browser/browser_context.h"
22 #include "content/public/browser/browser_thread.h"
23 #include "content/public/browser/render_process_host.h"
24 #include "content/public/browser/storage_partition.h"
25 #include "content/public/browser/web_contents.h"
26 #include "content/public/common/content_switches.h"
27 #include "content/public/common/url_constants.h"
28 #include "content/public/test/browser_test_utils.h"
29 #include "content/public/test/content_browser_test.h"
30 #include "content/public/test/content_browser_test_utils.h"
31 #include "content/shell/browser/shell.h"
32 #include "net/base/escape.h"
33 #include "net/base/net_errors.h"
34 #include "net/test/embedded_test_server/embedded_test_server.h"
35 #include "net/test/embedded_test_server/http_request.h"
36 #include "net/test/embedded_test_server/http_response.h"
37 #include "storage/browser/blob/blob_storage_context.h"
38 #include "storage/browser/database/database_util.h"
39 #include "storage/browser/quota/quota_manager.h"
41 using base::ASCIIToUTF16;
42 using storage::QuotaManager;
43 using storage::DatabaseUtil;
45 namespace content {
47 // This browser test is aimed towards exercising the IndexedDB bindings and
48 // the actual implementation that lives in the browser side.
49 class IndexedDBBrowserTest : public ContentBrowserTest {
50 public:
51 IndexedDBBrowserTest() : disk_usage_(-1) {}
53 void SetUp() override {
54 GetTestClassFactory()->Reset();
55 IndexedDBClassFactory::SetIndexedDBClassFactoryGetter(GetIDBClassFactory);
56 ContentBrowserTest::SetUp();
59 void TearDown() override {
60 IndexedDBClassFactory::SetIndexedDBClassFactoryGetter(NULL);
61 ContentBrowserTest::TearDown();
64 void FailOperation(FailClass failure_class,
65 FailMethod failure_method,
66 int fail_on_instance_num,
67 int fail_on_call_num) {
68 GetTestClassFactory()->FailOperation(
69 failure_class, failure_method, fail_on_instance_num, fail_on_call_num);
72 void SimpleTest(const GURL& test_url, bool incognito = false) {
73 // The test page will perform tests on IndexedDB, then navigate to either
74 // a #pass or #fail ref.
75 Shell* the_browser = incognito ? CreateOffTheRecordBrowser() : shell();
77 VLOG(0) << "Navigating to URL and blocking.";
78 NavigateToURLBlockUntilNavigationsComplete(the_browser, test_url, 2);
79 VLOG(0) << "Navigation done.";
80 std::string result =
81 the_browser->web_contents()->GetLastCommittedURL().ref();
82 if (result != "pass") {
83 std::string js_result;
84 ASSERT_TRUE(ExecuteScriptAndExtractString(
85 the_browser->web_contents(),
86 "window.domAutomationController.send(getLog())",
87 &js_result));
88 FAIL() << "Failed: " << js_result;
92 void NavigateAndWaitForTitle(Shell* shell,
93 const char* filename,
94 const char* hash,
95 const char* expected_string) {
96 GURL url = GetTestUrl("indexeddb", filename);
97 if (hash)
98 url = GURL(url.spec() + hash);
100 base::string16 expected_title16(ASCIIToUTF16(expected_string));
101 TitleWatcher title_watcher(shell->web_contents(), expected_title16);
102 NavigateToURL(shell, url);
103 EXPECT_EQ(expected_title16, title_watcher.WaitAndGetTitle());
106 IndexedDBContextImpl* GetContext() {
107 StoragePartition* partition =
108 BrowserContext::GetDefaultStoragePartition(
109 shell()->web_contents()->GetBrowserContext());
110 return static_cast<IndexedDBContextImpl*>(partition->GetIndexedDBContext());
113 void SetQuota(int quota_kilobytes) {
114 const int kTemporaryStorageQuotaSize =
115 quota_kilobytes * 1024 * QuotaManager::kPerHostTemporaryPortion;
116 SetTempQuota(kTemporaryStorageQuotaSize,
117 BrowserContext::GetDefaultStoragePartition(
118 shell()->web_contents()->GetBrowserContext())->GetQuotaManager());
121 static void SetTempQuota(int64 bytes, scoped_refptr<QuotaManager> qm) {
122 if (!BrowserThread::CurrentlyOn(BrowserThread::IO)) {
123 BrowserThread::PostTask(
124 BrowserThread::IO, FROM_HERE,
125 base::Bind(&IndexedDBBrowserTest::SetTempQuota, bytes, qm));
126 return;
128 DCHECK_CURRENTLY_ON(BrowserThread::IO);
129 qm->SetTemporaryGlobalOverrideQuota(bytes, storage::QuotaCallback());
130 // Don't return until the quota has been set.
131 scoped_refptr<base::ThreadTestHelper> helper(new base::ThreadTestHelper(
132 BrowserThread::GetMessageLoopProxyForThread(BrowserThread::DB)));
133 ASSERT_TRUE(helper->Run());
136 virtual int64 RequestDiskUsage() {
137 PostTaskAndReplyWithResult(
138 GetContext()->TaskRunner(),
139 FROM_HERE,
140 base::Bind(&IndexedDBContext::GetOriginDiskUsage,
141 GetContext(),
142 GURL("file:///")),
143 base::Bind(&IndexedDBBrowserTest::DidGetDiskUsage, this));
144 scoped_refptr<base::ThreadTestHelper> helper(new base::ThreadTestHelper(
145 BrowserMainLoop::GetInstance()->indexed_db_thread()->
146 message_loop_proxy()));
147 EXPECT_TRUE(helper->Run());
148 // Wait for DidGetDiskUsage to be called.
149 base::MessageLoop::current()->RunUntilIdle();
150 return disk_usage_;
153 virtual int RequestBlobFileCount() {
154 PostTaskAndReplyWithResult(
155 GetContext()->TaskRunner(), FROM_HERE,
156 base::Bind(&IndexedDBContextImpl::GetOriginBlobFileCount, GetContext(),
157 GURL("file:///")),
158 base::Bind(&IndexedDBBrowserTest::DidGetBlobFileCount, this));
159 scoped_refptr<base::ThreadTestHelper> helper(
160 new base::ThreadTestHelper(BrowserMainLoop::GetInstance()
161 ->indexed_db_thread()
162 ->message_loop_proxy()));
163 EXPECT_TRUE(helper->Run());
164 // Wait for DidGetBlobFileCount to be called.
165 base::MessageLoop::current()->RunUntilIdle();
166 return blob_file_count_;
169 private:
170 static MockBrowserTestIndexedDBClassFactory* GetTestClassFactory() {
171 static ::base::LazyInstance<MockBrowserTestIndexedDBClassFactory>::Leaky
172 s_factory = LAZY_INSTANCE_INITIALIZER;
173 return s_factory.Pointer();
176 static IndexedDBClassFactory* GetIDBClassFactory() {
177 return GetTestClassFactory();
180 virtual void DidGetDiskUsage(int64 bytes) {
181 disk_usage_ = bytes;
184 virtual void DidGetBlobFileCount(int count) { blob_file_count_ = count; }
186 int64 disk_usage_;
187 int blob_file_count_ = 0;
189 DISALLOW_COPY_AND_ASSIGN(IndexedDBBrowserTest);
192 IN_PROC_BROWSER_TEST_F(IndexedDBBrowserTest, CursorTest) {
193 SimpleTest(GetTestUrl("indexeddb", "cursor_test.html"));
196 IN_PROC_BROWSER_TEST_F(IndexedDBBrowserTest, CursorTestIncognito) {
197 SimpleTest(GetTestUrl("indexeddb", "cursor_test.html"),
198 true /* incognito */);
201 IN_PROC_BROWSER_TEST_F(IndexedDBBrowserTest, CursorPrefetch) {
202 SimpleTest(GetTestUrl("indexeddb", "cursor_prefetch.html"));
205 IN_PROC_BROWSER_TEST_F(IndexedDBBrowserTest, IndexTest) {
206 SimpleTest(GetTestUrl("indexeddb", "index_test.html"));
209 IN_PROC_BROWSER_TEST_F(IndexedDBBrowserTest, KeyPathTest) {
210 SimpleTest(GetTestUrl("indexeddb", "key_path_test.html"));
213 IN_PROC_BROWSER_TEST_F(IndexedDBBrowserTest, TransactionGetTest) {
214 SimpleTest(GetTestUrl("indexeddb", "transaction_get_test.html"));
217 IN_PROC_BROWSER_TEST_F(IndexedDBBrowserTest, KeyTypesTest) {
218 SimpleTest(GetTestUrl("indexeddb", "key_types_test.html"));
221 IN_PROC_BROWSER_TEST_F(IndexedDBBrowserTest, ObjectStoreTest) {
222 SimpleTest(GetTestUrl("indexeddb", "object_store_test.html"));
225 IN_PROC_BROWSER_TEST_F(IndexedDBBrowserTest, DatabaseTest) {
226 SimpleTest(GetTestUrl("indexeddb", "database_test.html"));
229 IN_PROC_BROWSER_TEST_F(IndexedDBBrowserTest, TransactionTest) {
230 SimpleTest(GetTestUrl("indexeddb", "transaction_test.html"));
233 IN_PROC_BROWSER_TEST_F(IndexedDBBrowserTest, CallbackAccounting) {
234 SimpleTest(GetTestUrl("indexeddb", "callback_accounting.html"));
237 IN_PROC_BROWSER_TEST_F(IndexedDBBrowserTest, DoesntHangTest) {
238 SimpleTest(GetTestUrl("indexeddb", "transaction_run_forever.html"));
239 CrashTab(shell()->web_contents());
240 SimpleTest(GetTestUrl("indexeddb", "transaction_not_blocked.html"));
243 IN_PROC_BROWSER_TEST_F(IndexedDBBrowserTest, Bug84933Test) {
244 const GURL url = GetTestUrl("indexeddb", "bug_84933.html");
246 // Just navigate to the URL. Test will crash if it fails.
247 NavigateToURLBlockUntilNavigationsComplete(shell(), url, 1);
250 IN_PROC_BROWSER_TEST_F(IndexedDBBrowserTest, Bug106883Test) {
251 const GURL url = GetTestUrl("indexeddb", "bug_106883.html");
253 // Just navigate to the URL. Test will crash if it fails.
254 NavigateToURLBlockUntilNavigationsComplete(shell(), url, 1);
257 IN_PROC_BROWSER_TEST_F(IndexedDBBrowserTest, Bug109187Test) {
258 const GURL url = GetTestUrl("indexeddb", "bug_109187.html");
260 // Just navigate to the URL. Test will crash if it fails.
261 NavigateToURLBlockUntilNavigationsComplete(shell(), url, 1);
264 class IndexedDBBrowserTestWithLowQuota : public IndexedDBBrowserTest {
265 public:
266 IndexedDBBrowserTestWithLowQuota() {}
268 void SetUpOnMainThread() override {
269 const int kInitialQuotaKilobytes = 5000;
270 SetQuota(kInitialQuotaKilobytes);
273 private:
274 DISALLOW_COPY_AND_ASSIGN(IndexedDBBrowserTestWithLowQuota);
277 IN_PROC_BROWSER_TEST_F(IndexedDBBrowserTestWithLowQuota, QuotaTest) {
278 SimpleTest(GetTestUrl("indexeddb", "quota_test.html"));
281 class IndexedDBBrowserTestWithGCExposed : public IndexedDBBrowserTest {
282 public:
283 IndexedDBBrowserTestWithGCExposed() {}
285 void SetUpCommandLine(base::CommandLine* command_line) override {
286 command_line->AppendSwitchASCII(switches::kJavaScriptFlags, "--expose-gc");
289 private:
290 DISALLOW_COPY_AND_ASSIGN(IndexedDBBrowserTestWithGCExposed);
293 IN_PROC_BROWSER_TEST_F(IndexedDBBrowserTestWithGCExposed,
294 DatabaseCallbacksTest) {
295 SimpleTest(GetTestUrl("indexeddb", "database_callbacks_first.html"));
298 static void CopyLevelDBToProfile(Shell* shell,
299 scoped_refptr<IndexedDBContextImpl> context,
300 const std::string& test_directory) {
301 DCHECK(context->TaskRunner()->RunsTasksOnCurrentThread());
302 base::FilePath leveldb_dir(FILE_PATH_LITERAL("file__0.indexeddb.leveldb"));
303 base::FilePath test_data_dir =
304 GetTestFilePath("indexeddb", test_directory.c_str()).Append(leveldb_dir);
305 base::FilePath dest = context->data_path().Append(leveldb_dir);
306 // If we don't create the destination directory first, the contents of the
307 // leveldb directory are copied directly into profile/IndexedDB instead of
308 // profile/IndexedDB/file__0.xxx/
309 ASSERT_TRUE(base::CreateDirectory(dest));
310 const bool kRecursive = true;
311 ASSERT_TRUE(base::CopyDirectory(test_data_dir,
312 context->data_path(),
313 kRecursive));
316 class IndexedDBBrowserTestWithPreexistingLevelDB : public IndexedDBBrowserTest {
317 public:
318 IndexedDBBrowserTestWithPreexistingLevelDB() {}
319 void SetUpOnMainThread() override {
320 scoped_refptr<IndexedDBContextImpl> context = GetContext();
321 context->TaskRunner()->PostTask(
322 FROM_HERE,
323 base::Bind(
324 &CopyLevelDBToProfile, shell(), context, EnclosingLevelDBDir()));
325 scoped_refptr<base::ThreadTestHelper> helper(new base::ThreadTestHelper(
326 BrowserMainLoop::GetInstance()->indexed_db_thread()->
327 message_loop_proxy()));
328 ASSERT_TRUE(helper->Run());
331 virtual std::string EnclosingLevelDBDir() = 0;
333 private:
334 DISALLOW_COPY_AND_ASSIGN(IndexedDBBrowserTestWithPreexistingLevelDB);
337 class IndexedDBBrowserTestWithVersion0Schema : public
338 IndexedDBBrowserTestWithPreexistingLevelDB {
339 std::string EnclosingLevelDBDir() override { return "migration_from_0"; }
342 IN_PROC_BROWSER_TEST_F(IndexedDBBrowserTestWithVersion0Schema, MigrationTest) {
343 SimpleTest(GetTestUrl("indexeddb", "migration_test.html"));
346 class IndexedDBBrowserTestWithVersion123456Schema : public
347 IndexedDBBrowserTestWithPreexistingLevelDB {
348 std::string EnclosingLevelDBDir() override { return "schema_version_123456"; }
351 IN_PROC_BROWSER_TEST_F(IndexedDBBrowserTestWithVersion123456Schema,
352 DestroyTest) {
353 int64 original_size = RequestDiskUsage();
354 EXPECT_GT(original_size, 0);
355 SimpleTest(GetTestUrl("indexeddb", "open_bad_db.html"));
356 int64 new_size = RequestDiskUsage();
357 EXPECT_GT(new_size, 0);
358 EXPECT_NE(original_size, new_size);
361 class IndexedDBBrowserTestWithVersion987654SSVData : public
362 IndexedDBBrowserTestWithPreexistingLevelDB {
363 std::string EnclosingLevelDBDir() override { return "ssv_version_987654"; }
366 IN_PROC_BROWSER_TEST_F(IndexedDBBrowserTestWithVersion987654SSVData,
367 DestroyTest) {
368 int64 original_size = RequestDiskUsage();
369 EXPECT_GT(original_size, 0);
370 SimpleTest(GetTestUrl("indexeddb", "open_bad_db.html"));
371 int64 new_size = RequestDiskUsage();
372 EXPECT_GT(new_size, 0);
373 EXPECT_NE(original_size, new_size);
376 class IndexedDBBrowserTestWithCorruptLevelDB : public
377 IndexedDBBrowserTestWithPreexistingLevelDB {
378 std::string EnclosingLevelDBDir() override { return "corrupt_leveldb"; }
381 IN_PROC_BROWSER_TEST_F(IndexedDBBrowserTestWithCorruptLevelDB,
382 DestroyTest) {
383 int64 original_size = RequestDiskUsage();
384 EXPECT_GT(original_size, 0);
385 SimpleTest(GetTestUrl("indexeddb", "open_bad_db.html"));
386 int64 new_size = RequestDiskUsage();
387 EXPECT_GT(new_size, 0);
388 EXPECT_NE(original_size, new_size);
391 class IndexedDBBrowserTestWithMissingSSTFile : public
392 IndexedDBBrowserTestWithPreexistingLevelDB {
393 std::string EnclosingLevelDBDir() override { return "missing_sst"; }
396 IN_PROC_BROWSER_TEST_F(IndexedDBBrowserTestWithMissingSSTFile,
397 DestroyTest) {
398 int64 original_size = RequestDiskUsage();
399 EXPECT_GT(original_size, 0);
400 SimpleTest(GetTestUrl("indexeddb", "open_missing_table.html"));
401 int64 new_size = RequestDiskUsage();
402 EXPECT_GT(new_size, 0);
403 EXPECT_NE(original_size, new_size);
406 IN_PROC_BROWSER_TEST_F(IndexedDBBrowserTest, LevelDBLogFileTest) {
407 // Any page that opens an IndexedDB will work here.
408 SimpleTest(GetTestUrl("indexeddb", "database_test.html"));
409 base::FilePath leveldb_dir(FILE_PATH_LITERAL("file__0.indexeddb.leveldb"));
410 base::FilePath log_file(FILE_PATH_LITERAL("LOG"));
411 base::FilePath log_file_path =
412 GetContext()->data_path().Append(leveldb_dir).Append(log_file);
413 int64 size;
414 EXPECT_TRUE(base::GetFileSize(log_file_path, &size));
415 EXPECT_GT(size, 0);
418 IN_PROC_BROWSER_TEST_F(IndexedDBBrowserTest, CanDeleteWhenOverQuotaTest) {
419 SimpleTest(GetTestUrl("indexeddb", "fill_up_5k.html"));
420 int64 size = RequestDiskUsage();
421 const int kQuotaKilobytes = 2;
422 EXPECT_GT(size, kQuotaKilobytes * 1024);
423 SetQuota(kQuotaKilobytes);
424 SimpleTest(GetTestUrl("indexeddb", "delete_over_quota.html"));
427 IN_PROC_BROWSER_TEST_F(IndexedDBBrowserTest, EmptyBlob) {
428 // First delete all IDB's for the test origin
429 GetContext()->TaskRunner()->PostTask(
430 FROM_HERE, base::Bind(&IndexedDBContextImpl::DeleteForOrigin,
431 GetContext(), GURL("file:///")));
432 EXPECT_EQ(0, RequestBlobFileCount()); // Start with no blob files.
433 const GURL test_url = GetTestUrl("indexeddb", "empty_blob.html");
434 // For some reason Android's futimes fails (EPERM) in this test. Do not assert
435 // file times on Android, but do so on other platforms. crbug.com/467247
436 // TODO(cmumford): Figure out why this is the case and fix if possible.
437 #if defined(OS_ANDROID)
438 SimpleTest(GURL(test_url.spec() + "#ignoreTimes"));
439 #else
440 SimpleTest(GURL(test_url.spec()));
441 #endif
442 // Test stores one blob and one file to disk, so expect two files.
443 EXPECT_EQ(2, RequestBlobFileCount());
446 IN_PROC_BROWSER_TEST_F(IndexedDBBrowserTestWithGCExposed, BlobDidAck) {
447 SimpleTest(GetTestUrl("indexeddb", "blob_did_ack.html"));
448 // Wait for idle so that the blob ack has time to be received/processed by
449 // the browser process.
450 base::MessageLoop::current()->RunUntilIdle();
451 content::ChromeBlobStorageContext* blob_context =
452 ChromeBlobStorageContext::GetFor(
453 shell()->web_contents()->GetBrowserContext());
454 EXPECT_EQ(0UL, blob_context->context()->blob_count());
457 IN_PROC_BROWSER_TEST_F(IndexedDBBrowserTestWithGCExposed, BlobDidAckPrefetch) {
458 SimpleTest(GetTestUrl("indexeddb", "blob_did_ack_prefetch.html"));
459 // Wait for idle so that the blob ack has time to be received/processed by
460 // the browser process.
461 base::MessageLoop::current()->RunUntilIdle();
462 content::ChromeBlobStorageContext* blob_context =
463 ChromeBlobStorageContext::GetFor(
464 shell()->web_contents()->GetBrowserContext());
465 EXPECT_EQ(0UL, blob_context->context()->blob_count());
468 IN_PROC_BROWSER_TEST_F(IndexedDBBrowserTest, BlobsCountAgainstQuota) {
469 SimpleTest(GetTestUrl("indexeddb", "blobs_use_quota.html"));
472 IN_PROC_BROWSER_TEST_F(IndexedDBBrowserTest, DeleteForOriginDeletesBlobs) {
473 SimpleTest(GetTestUrl("indexeddb", "write_20mb_blob.html"));
474 int64 size = RequestDiskUsage();
475 // This assertion assumes that we do not compress blobs.
476 EXPECT_GT(size, 20 << 20 /* 20 MB */);
477 GetContext()->TaskRunner()->PostTask(
478 FROM_HERE, base::Bind(&IndexedDBContextImpl::DeleteForOrigin,
479 GetContext(), GURL("file:///")));
480 scoped_refptr<base::ThreadTestHelper> helper(
481 new base::ThreadTestHelper(BrowserMainLoop::GetInstance()
482 ->indexed_db_thread()
483 ->message_loop_proxy()));
484 ASSERT_TRUE(helper->Run());
485 EXPECT_EQ(0, RequestDiskUsage());
488 IN_PROC_BROWSER_TEST_F(IndexedDBBrowserTest, DiskFullOnCommit) {
489 // Ignore several preceding transactions:
490 // * The test calls deleteDatabase() which opens the backing store:
491 // #1: IndexedDBBackingStore::OpenBackingStore
492 // => IndexedDBBackingStore::SetUpMetadata
493 // #2: IndexedDBBackingStore::OpenBackingStore
494 // => IndexedDBBackingStore::CleanUpBlobJournal (no-op)
495 // * The test calls open(), to create a new database:
496 // #3: IndexedDBFactoryImpl::Open
497 // => IndexedDBDatabase::Create
498 // => IndexedDBBackingStore::CreateIDBDatabaseMetaData
499 // #4: IndexedDBTransaction::Commit - initial "versionchange" transaction
500 // * Once the connection is opened, the test runs:
501 // #5: IndexedDBTransaction::Commit - the test's "readwrite" transaction)
502 const int instance_num = 5;
503 const int call_num = 1;
504 FailOperation(FAIL_CLASS_LEVELDB_TRANSACTION, FAIL_METHOD_COMMIT_DISK_FULL,
505 instance_num, call_num);
506 SimpleTest(GetTestUrl("indexeddb", "disk_full_on_commit.html"));
509 namespace {
511 static void CompactIndexedDBBackingStore(
512 scoped_refptr<IndexedDBContextImpl> context,
513 const GURL& origin_url) {
514 IndexedDBFactory* factory = context->GetIDBFactory();
516 std::pair<IndexedDBFactory::OriginDBMapIterator,
517 IndexedDBFactory::OriginDBMapIterator> range =
518 factory->GetOpenDatabasesForOrigin(origin_url);
520 if (range.first == range.second) // If no open db's for this origin
521 return;
523 // Compact the first db's backing store since all the db's are in the same
524 // backing store.
525 IndexedDBDatabase* db = range.first->second;
526 IndexedDBBackingStore* backing_store = db->backing_store();
527 backing_store->Compact();
530 static void CorruptIndexedDBDatabase(
531 IndexedDBContextImpl* context,
532 const GURL& origin_url,
533 base::WaitableEvent* signal_when_finished) {
535 CompactIndexedDBBackingStore(context, origin_url);
537 int num_files = 0;
538 int num_errors = 0;
539 const bool recursive = false;
540 for (const base::FilePath& idb_data_path :
541 context->GetStoragePaths(origin_url)) {
542 base::FileEnumerator enumerator(
543 idb_data_path, recursive, base::FileEnumerator::FILES);
544 for (base::FilePath idb_file = enumerator.Next(); !idb_file.empty();
545 idb_file = enumerator.Next()) {
546 int64 size(0);
547 GetFileSize(idb_file, &size);
549 if (idb_file.Extension() == FILE_PATH_LITERAL(".ldb")) {
550 num_files++;
551 base::File file(
552 idb_file, base::File::FLAG_WRITE | base::File::FLAG_OPEN_TRUNCATED);
553 if (file.IsValid()) {
554 // Was opened truncated, expand back to the original
555 // file size and fill with zeros (corrupting the file).
556 file.SetLength(size);
557 } else {
558 num_errors++;
562 VLOG(0) << "There were " << num_files << " in " << idb_data_path.value()
563 << " with " << num_errors << " errors";
566 signal_when_finished->Signal();
569 const std::string s_corrupt_db_test_prefix = "/corrupt/test/";
571 static scoped_ptr<net::test_server::HttpResponse> CorruptDBRequestHandler(
572 IndexedDBContextImpl* context,
573 const GURL& origin_url,
574 const std::string& path,
575 IndexedDBBrowserTest* test,
576 const net::test_server::HttpRequest& request) {
577 std::string request_path;
578 if (path.find(s_corrupt_db_test_prefix) != std::string::npos)
579 request_path = request.relative_url.substr(s_corrupt_db_test_prefix.size());
580 else
581 return scoped_ptr<net::test_server::HttpResponse>();
583 // Remove the query string if present.
584 std::string request_query;
585 size_t query_pos = request_path.find('?');
586 if (query_pos != std::string::npos) {
587 request_query = request_path.substr(query_pos + 1);
588 request_path = request_path.substr(0, query_pos);
591 if (request_path == "corruptdb" && !request_query.empty()) {
592 VLOG(0) << "Requested to corrupt IndexedDB: " << request_query;
593 base::WaitableEvent signal_when_finished(false, false);
594 context->TaskRunner()->PostTask(FROM_HERE,
595 base::Bind(&CorruptIndexedDBDatabase,
596 base::ConstRef(context),
597 origin_url,
598 &signal_when_finished));
599 signal_when_finished.Wait();
601 scoped_ptr<net::test_server::BasicHttpResponse> http_response(
602 new net::test_server::BasicHttpResponse);
603 http_response->set_code(net::HTTP_OK);
604 return http_response.Pass();
605 } else if (request_path == "fail" && !request_query.empty()) {
606 FailClass failure_class = FAIL_CLASS_NOTHING;
607 FailMethod failure_method = FAIL_METHOD_NOTHING;
608 int instance_num = 1;
609 int call_num = 1;
610 std::string fail_class;
611 std::string fail_method;
613 url::Component query(0, request_query.length()), key_pos, value_pos;
614 while (url::ExtractQueryKeyValue(
615 request_query.c_str(), &query, &key_pos, &value_pos)) {
616 std::string escaped_key(request_query.substr(key_pos.begin, key_pos.len));
617 std::string escaped_value(
618 request_query.substr(value_pos.begin, value_pos.len));
620 std::string key = net::UnescapeURLComponent(
621 escaped_key,
622 net::UnescapeRule::NORMAL | net::UnescapeRule::SPACES |
623 net::UnescapeRule::URL_SPECIAL_CHARS);
625 std::string value = net::UnescapeURLComponent(
626 escaped_value,
627 net::UnescapeRule::NORMAL | net::UnescapeRule::SPACES |
628 net::UnescapeRule::URL_SPECIAL_CHARS);
630 if (key == "method")
631 fail_method = value;
632 else if (key == "class")
633 fail_class = value;
634 else if (key == "instNum")
635 instance_num = atoi(value.c_str());
636 else if (key == "callNum")
637 call_num = atoi(value.c_str());
638 else
639 NOTREACHED() << "Unknown param: \"" << key << "\"";
642 if (fail_class == "LevelDBTransaction") {
643 failure_class = FAIL_CLASS_LEVELDB_TRANSACTION;
644 if (fail_method == "Get")
645 failure_method = FAIL_METHOD_GET;
646 else if (fail_method == "Commit")
647 failure_method = FAIL_METHOD_COMMIT;
648 else
649 NOTREACHED() << "Unknown method: \"" << fail_method << "\"";
650 } else if (fail_class == "LevelDBIterator") {
651 failure_class = FAIL_CLASS_LEVELDB_ITERATOR;
652 if (fail_method == "Seek")
653 failure_method = FAIL_METHOD_SEEK;
654 else
655 NOTREACHED() << "Unknown method: \"" << fail_method << "\"";
656 } else {
657 NOTREACHED() << "Unknown class: \"" << fail_class << "\"";
660 DCHECK_GE(instance_num, 1);
661 DCHECK_GE(call_num, 1);
663 test->FailOperation(failure_class, failure_method, instance_num, call_num);
665 scoped_ptr<net::test_server::BasicHttpResponse> http_response(
666 new net::test_server::BasicHttpResponse);
667 http_response->set_code(net::HTTP_OK);
668 return http_response.Pass();
671 // A request for a test resource
672 base::FilePath resource_path =
673 content::GetTestFilePath("indexeddb", request_path.c_str());
674 scoped_ptr<net::test_server::BasicHttpResponse> http_response(
675 new net::test_server::BasicHttpResponse);
676 http_response->set_code(net::HTTP_OK);
677 std::string file_contents;
678 if (!base::ReadFileToString(resource_path, &file_contents))
679 return scoped_ptr<net::test_server::HttpResponse>();
680 http_response->set_content(file_contents);
681 return http_response.Pass();
684 } // namespace
686 class IndexedDBBrowserCorruptionTest
687 : public IndexedDBBrowserTest,
688 public ::testing::WithParamInterface<const char*> {};
690 IN_PROC_BROWSER_TEST_P(IndexedDBBrowserCorruptionTest,
691 OperationOnCorruptedOpenDatabase) {
692 ASSERT_TRUE(embedded_test_server()->Started() ||
693 embedded_test_server()->InitializeAndWaitUntilReady());
694 const GURL& origin_url = embedded_test_server()->base_url();
695 embedded_test_server()->RegisterRequestHandler(
696 base::Bind(&CorruptDBRequestHandler,
697 base::Unretained(GetContext()),
698 origin_url,
699 s_corrupt_db_test_prefix,
700 this));
702 std::string test_file = s_corrupt_db_test_prefix +
703 "corrupted_open_db_detection.html#" + GetParam();
704 SimpleTest(embedded_test_server()->GetURL(test_file));
706 test_file = s_corrupt_db_test_prefix + "corrupted_open_db_recovery.html";
707 SimpleTest(embedded_test_server()->GetURL(test_file));
710 INSTANTIATE_TEST_CASE_P(IndexedDBBrowserCorruptionTestInstantiation,
711 IndexedDBBrowserCorruptionTest,
712 ::testing::Values("failGetBlobJournal",
713 "get",
714 "failWebkitGetDatabaseNames",
715 "iterate",
716 "failTransactionCommit",
717 "clearObjectStore"));
719 IN_PROC_BROWSER_TEST_F(IndexedDBBrowserTest,
720 DeleteCompactsBackingStore) {
721 const GURL test_url = GetTestUrl("indexeddb", "delete_compact.html");
722 SimpleTest(GURL(test_url.spec() + "#fill"));
723 int64 after_filling = RequestDiskUsage();
724 EXPECT_GT(after_filling, 0);
726 SimpleTest(GURL(test_url.spec() + "#purge"));
727 int64 after_deleting = RequestDiskUsage();
728 EXPECT_LT(after_deleting, after_filling);
730 // The above tests verify basic assertions - that filling writes data and
731 // deleting reduces the amount stored.
733 // The below tests make assumptions about implementation specifics, such as
734 // data compression, compaction efficiency, and the maximum amount of
735 // metadata and log data remains after a deletion. It is possible that
736 // changes to the implementation may require these constants to be tweaked.
738 const int kTestFillBytes = 1024 * 1024 * 5; // 5MB
739 EXPECT_GT(after_filling, kTestFillBytes);
741 const int kTestCompactBytes = 1024 * 10; // 10kB
742 EXPECT_LT(after_deleting, kTestCompactBytes);
745 // Complex multi-step (converted from pyauto) tests begin here.
747 // Verify null key path persists after restarting browser.
748 IN_PROC_BROWSER_TEST_F(IndexedDBBrowserTest, PRE_NullKeyPathPersistence) {
749 NavigateAndWaitForTitle(shell(), "bug_90635.html", "#part1",
750 "pass - first run");
753 // Verify null key path persists after restarting browser.
754 IN_PROC_BROWSER_TEST_F(IndexedDBBrowserTest, NullKeyPathPersistence) {
755 NavigateAndWaitForTitle(shell(), "bug_90635.html", "#part2",
756 "pass - second run");
759 // Verify that a VERSION_CHANGE transaction is rolled back after a
760 // renderer/browser crash
761 IN_PROC_BROWSER_TEST_F(IndexedDBBrowserTest,
762 PRE_PRE_VersionChangeCrashResilience) {
763 NavigateAndWaitForTitle(shell(), "version_change_crash.html", "#part1",
764 "pass - part1 - complete");
767 IN_PROC_BROWSER_TEST_F(IndexedDBBrowserTest, PRE_VersionChangeCrashResilience) {
768 NavigateAndWaitForTitle(shell(), "version_change_crash.html", "#part2",
769 "pass - part2 - crash me");
770 // If we actually crash here then googletest will not run the next step
771 // (VersionChangeCrashResilience) as an optimization. googletest's
772 // ASSERT_DEATH/EXIT fails to work properly (on Windows) due to how we
773 // implement the PRE_* test mechanism.
774 exit(0);
777 IN_PROC_BROWSER_TEST_F(IndexedDBBrowserTest, VersionChangeCrashResilience) {
778 NavigateAndWaitForTitle(shell(), "version_change_crash.html", "#part3",
779 "pass - part3 - rolled back");
782 // crbug.com/427529
783 // Disable this test for ASAN on Android because it takes too long to run.
784 #if defined(ANDROID) && defined(ADDRESS_SANITIZER)
785 #define MAYBE_ConnectionsClosedOnTabClose DISABLED_ConnectionsClosedOnTabClose
786 #else
787 #define MAYBE_ConnectionsClosedOnTabClose ConnectionsClosedOnTabClose
788 #endif
789 // Verify that open DB connections are closed when a tab is destroyed.
790 IN_PROC_BROWSER_TEST_F(
791 IndexedDBBrowserTest, MAYBE_ConnectionsClosedOnTabClose) {
792 NavigateAndWaitForTitle(shell(), "version_change_blocked.html", "#tab1",
793 "setVersion(2) complete");
795 // Start on a different URL to force a new renderer process.
796 Shell* new_shell = CreateBrowser();
797 NavigateToURL(new_shell, GURL(url::kAboutBlankURL));
798 NavigateAndWaitForTitle(new_shell, "version_change_blocked.html", "#tab2",
799 "setVersion(3) blocked");
801 base::string16 expected_title16(ASCIIToUTF16("setVersion(3) complete"));
802 TitleWatcher title_watcher(new_shell->web_contents(), expected_title16);
804 shell()->web_contents()->GetRenderProcessHost()->Shutdown(0, true);
805 shell()->Close();
807 EXPECT_EQ(expected_title16, title_watcher.WaitAndGetTitle());
810 // Verify that a "close" event is fired at database connections when
811 // the backing store is deleted.
812 IN_PROC_BROWSER_TEST_F(IndexedDBBrowserTest, ForceCloseEventTest) {
813 NavigateAndWaitForTitle(shell(), "force_close_event.html", NULL,
814 "connection ready");
816 GetContext()->TaskRunner()->PostTask(
817 FROM_HERE,
818 base::Bind(&IndexedDBContextImpl::DeleteForOrigin,
819 GetContext(),
820 GURL("file:///")));
822 base::string16 expected_title16(ASCIIToUTF16("connection closed"));
823 TitleWatcher title_watcher(shell()->web_contents(), expected_title16);
824 title_watcher.AlsoWaitForTitle(ASCIIToUTF16("connection closed with error"));
825 EXPECT_EQ(expected_title16, title_watcher.WaitAndGetTitle());
828 class IndexedDBBrowserTestSingleProcess : public IndexedDBBrowserTest {
829 public:
830 void SetUpCommandLine(base::CommandLine* command_line) override {
831 command_line->AppendSwitch(switches::kSingleProcess);
835 IN_PROC_BROWSER_TEST_F(IndexedDBBrowserTestSingleProcess,
836 RenderThreadShutdownTest) {
837 SimpleTest(GetTestUrl("indexeddb", "shutdown_with_requests.html"));
840 } // namespace content