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. 4 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/database/database_util.h" 38 #include "storage/browser/quota/quota_manager.h" 39 40 using base::ASCIIToUTF16; 41 using storage::QuotaManager; 42 using storage::DatabaseUtil; 43 44 namespace content { 45 46 // This browser test is aimed towards exercising the IndexedDB bindings and 47 // the actual implementation that lives in the browser side. 48 class IndexedDBBrowserTest : public ContentBrowserTest { 49 public: 50 IndexedDBBrowserTest() : disk_usage_(-1) {} 51 52 virtual void SetUp() OVERRIDE { 53 GetTestClassFactory()->Reset(); 54 IndexedDBClassFactory::SetIndexedDBClassFactoryGetter(GetIDBClassFactory); 55 ContentBrowserTest::SetUp(); 56 } 57 58 virtual void TearDown() OVERRIDE { 59 IndexedDBClassFactory::SetIndexedDBClassFactoryGetter(NULL); 60 ContentBrowserTest::TearDown(); 61 } 62 63 void FailOperation(FailClass failure_class, 64 FailMethod failure_method, 65 int fail_on_instance_num, 66 int fail_on_call_num) { 67 GetTestClassFactory()->FailOperation( 68 failure_class, failure_method, fail_on_instance_num, fail_on_call_num); 69 } 70 71 void SimpleTest(const GURL& test_url, bool incognito = false) { 72 // The test page will perform tests on IndexedDB, then navigate to either 73 // a #pass or #fail ref. 74 Shell* the_browser = incognito ? CreateOffTheRecordBrowser() : shell(); 75 76 VLOG(0) << "Navigating to URL and blocking."; 77 NavigateToURLBlockUntilNavigationsComplete(the_browser, test_url, 2); 78 VLOG(0) << "Navigation done."; 79 std::string result = 80 the_browser->web_contents()->GetLastCommittedURL().ref(); 81 if (result != "pass") { 82 std::string js_result; 83 ASSERT_TRUE(ExecuteScriptAndExtractString( 84 the_browser->web_contents(), 85 "window.domAutomationController.send(getLog())", 86 &js_result)); 87 FAIL() << "Failed: " << js_result; 88 } 89 } 90 91 void NavigateAndWaitForTitle(Shell* shell, 92 const char* filename, 93 const char* hash, 94 const char* expected_string) { 95 GURL url = GetTestUrl("indexeddb", filename); 96 if (hash) 97 url = GURL(url.spec() + hash); 98 99 base::string16 expected_title16(ASCIIToUTF16(expected_string)); 100 TitleWatcher title_watcher(shell->web_contents(), expected_title16); 101 NavigateToURL(shell, url); 102 EXPECT_EQ(expected_title16, title_watcher.WaitAndGetTitle()); 103 } 104 105 IndexedDBContextImpl* GetContext() { 106 StoragePartition* partition = 107 BrowserContext::GetDefaultStoragePartition( 108 shell()->web_contents()->GetBrowserContext()); 109 return static_cast<IndexedDBContextImpl*>(partition->GetIndexedDBContext()); 110 } 111 112 void SetQuota(int quotaKilobytes) { 113 const int kTemporaryStorageQuotaSize = quotaKilobytes 114 * 1024 * QuotaManager::kPerHostTemporaryPortion; 115 SetTempQuota(kTemporaryStorageQuotaSize, 116 BrowserContext::GetDefaultStoragePartition( 117 shell()->web_contents()->GetBrowserContext())->GetQuotaManager()); 118 } 119 120 static void SetTempQuota(int64 bytes, scoped_refptr<QuotaManager> qm) { 121 if (!BrowserThread::CurrentlyOn(BrowserThread::IO)) { 122 BrowserThread::PostTask( 123 BrowserThread::IO, FROM_HERE, 124 base::Bind(&IndexedDBBrowserTest::SetTempQuota, bytes, qm)); 125 return; 126 } 127 DCHECK(BrowserThread::CurrentlyOn(BrowserThread::IO)); 128 qm->SetTemporaryGlobalOverrideQuota(bytes, storage::QuotaCallback()); 129 // Don't return until the quota has been set. 130 scoped_refptr<base::ThreadTestHelper> helper(new base::ThreadTestHelper( 131 BrowserThread::GetMessageLoopProxyForThread(BrowserThread::DB))); 132 ASSERT_TRUE(helper->Run()); 133 } 134 135 virtual int64 RequestDiskUsage() { 136 PostTaskAndReplyWithResult( 137 GetContext()->TaskRunner(), 138 FROM_HERE, 139 base::Bind(&IndexedDBContext::GetOriginDiskUsage, 140 GetContext(), 141 GURL("file:///")), 142 base::Bind(&IndexedDBBrowserTest::DidGetDiskUsage, this)); 143 scoped_refptr<base::ThreadTestHelper> helper(new base::ThreadTestHelper( 144 BrowserMainLoop::GetInstance()->indexed_db_thread()-> 145 message_loop_proxy())); 146 EXPECT_TRUE(helper->Run()); 147 // Wait for DidGetDiskUsage to be called. 148 base::MessageLoop::current()->RunUntilIdle(); 149 return disk_usage_; 150 } 151 152 private: 153 static MockBrowserTestIndexedDBClassFactory* GetTestClassFactory() { 154 static ::base::LazyInstance<MockBrowserTestIndexedDBClassFactory>::Leaky 155 s_factory = LAZY_INSTANCE_INITIALIZER; 156 return s_factory.Pointer(); 157 } 158 159 static IndexedDBClassFactory* GetIDBClassFactory() { 160 return GetTestClassFactory(); 161 } 162 163 virtual void DidGetDiskUsage(int64 bytes) { 164 EXPECT_GT(bytes, 0); 165 disk_usage_ = bytes; 166 } 167 168 int64 disk_usage_; 169 170 DISALLOW_COPY_AND_ASSIGN(IndexedDBBrowserTest); 171 }; 172 173 IN_PROC_BROWSER_TEST_F(IndexedDBBrowserTest, CursorTest) { 174 SimpleTest(GetTestUrl("indexeddb", "cursor_test.html")); 175 } 176 177 IN_PROC_BROWSER_TEST_F(IndexedDBBrowserTest, CursorTestIncognito) { 178 SimpleTest(GetTestUrl("indexeddb", "cursor_test.html"), 179 true /* incognito */); 180 } 181 182 IN_PROC_BROWSER_TEST_F(IndexedDBBrowserTest, CursorPrefetch) { 183 SimpleTest(GetTestUrl("indexeddb", "cursor_prefetch.html")); 184 } 185 186 IN_PROC_BROWSER_TEST_F(IndexedDBBrowserTest, IndexTest) { 187 SimpleTest(GetTestUrl("indexeddb", "index_test.html")); 188 } 189 190 IN_PROC_BROWSER_TEST_F(IndexedDBBrowserTest, KeyPathTest) { 191 SimpleTest(GetTestUrl("indexeddb", "key_path_test.html")); 192 } 193 194 IN_PROC_BROWSER_TEST_F(IndexedDBBrowserTest, TransactionGetTest) { 195 SimpleTest(GetTestUrl("indexeddb", "transaction_get_test.html")); 196 } 197 198 IN_PROC_BROWSER_TEST_F(IndexedDBBrowserTest, KeyTypesTest) { 199 SimpleTest(GetTestUrl("indexeddb", "key_types_test.html")); 200 } 201 202 IN_PROC_BROWSER_TEST_F(IndexedDBBrowserTest, ObjectStoreTest) { 203 SimpleTest(GetTestUrl("indexeddb", "object_store_test.html")); 204 } 205 206 IN_PROC_BROWSER_TEST_F(IndexedDBBrowserTest, DatabaseTest) { 207 SimpleTest(GetTestUrl("indexeddb", "database_test.html")); 208 } 209 210 IN_PROC_BROWSER_TEST_F(IndexedDBBrowserTest, TransactionTest) { 211 SimpleTest(GetTestUrl("indexeddb", "transaction_test.html")); 212 } 213 214 IN_PROC_BROWSER_TEST_F(IndexedDBBrowserTest, CallbackAccounting) { 215 SimpleTest(GetTestUrl("indexeddb", "callback_accounting.html")); 216 } 217 218 IN_PROC_BROWSER_TEST_F(IndexedDBBrowserTest, DoesntHangTest) { 219 SimpleTest(GetTestUrl("indexeddb", "transaction_run_forever.html")); 220 CrashTab(shell()->web_contents()); 221 SimpleTest(GetTestUrl("indexeddb", "transaction_not_blocked.html")); 222 } 223 224 IN_PROC_BROWSER_TEST_F(IndexedDBBrowserTest, Bug84933Test) { 225 const GURL url = GetTestUrl("indexeddb", "bug_84933.html"); 226 227 // Just navigate to the URL. Test will crash if it fails. 228 NavigateToURLBlockUntilNavigationsComplete(shell(), url, 1); 229 } 230 231 IN_PROC_BROWSER_TEST_F(IndexedDBBrowserTest, Bug106883Test) { 232 const GURL url = GetTestUrl("indexeddb", "bug_106883.html"); 233 234 // Just navigate to the URL. Test will crash if it fails. 235 NavigateToURLBlockUntilNavigationsComplete(shell(), url, 1); 236 } 237 238 IN_PROC_BROWSER_TEST_F(IndexedDBBrowserTest, Bug109187Test) { 239 const GURL url = GetTestUrl("indexeddb", "bug_109187.html"); 240 241 // Just navigate to the URL. Test will crash if it fails. 242 NavigateToURLBlockUntilNavigationsComplete(shell(), url, 1); 243 } 244 245 class IndexedDBBrowserTestWithLowQuota : public IndexedDBBrowserTest { 246 public: 247 IndexedDBBrowserTestWithLowQuota() {} 248 249 virtual void SetUpOnMainThread() OVERRIDE { 250 const int kInitialQuotaKilobytes = 5000; 251 SetQuota(kInitialQuotaKilobytes); 252 } 253 254 private: 255 DISALLOW_COPY_AND_ASSIGN(IndexedDBBrowserTestWithLowQuota); 256 }; 257 258 IN_PROC_BROWSER_TEST_F(IndexedDBBrowserTestWithLowQuota, QuotaTest) { 259 SimpleTest(GetTestUrl("indexeddb", "quota_test.html")); 260 } 261 262 class IndexedDBBrowserTestWithGCExposed : public IndexedDBBrowserTest { 263 public: 264 IndexedDBBrowserTestWithGCExposed() {} 265 266 virtual void SetUpCommandLine(CommandLine* command_line) OVERRIDE { 267 command_line->AppendSwitchASCII(switches::kJavaScriptFlags, "--expose-gc"); 268 } 269 270 private: 271 DISALLOW_COPY_AND_ASSIGN(IndexedDBBrowserTestWithGCExposed); 272 }; 273 274 IN_PROC_BROWSER_TEST_F(IndexedDBBrowserTestWithGCExposed, 275 DatabaseCallbacksTest) { 276 SimpleTest(GetTestUrl("indexeddb", "database_callbacks_first.html")); 277 } 278 279 static void CopyLevelDBToProfile(Shell* shell, 280 scoped_refptr<IndexedDBContextImpl> context, 281 const std::string& test_directory) { 282 DCHECK(context->TaskRunner()->RunsTasksOnCurrentThread()); 283 base::FilePath leveldb_dir(FILE_PATH_LITERAL("file__0.indexeddb.leveldb")); 284 base::FilePath test_data_dir = 285 GetTestFilePath("indexeddb", test_directory.c_str()).Append(leveldb_dir); 286 base::FilePath dest = context->data_path().Append(leveldb_dir); 287 // If we don't create the destination directory first, the contents of the 288 // leveldb directory are copied directly into profile/IndexedDB instead of 289 // profile/IndexedDB/file__0.xxx/ 290 ASSERT_TRUE(base::CreateDirectory(dest)); 291 const bool kRecursive = true; 292 ASSERT_TRUE(base::CopyDirectory(test_data_dir, 293 context->data_path(), 294 kRecursive)); 295 } 296 297 class IndexedDBBrowserTestWithPreexistingLevelDB : public IndexedDBBrowserTest { 298 public: 299 IndexedDBBrowserTestWithPreexistingLevelDB() {} 300 virtual void SetUpOnMainThread() OVERRIDE { 301 scoped_refptr<IndexedDBContextImpl> context = GetContext(); 302 context->TaskRunner()->PostTask( 303 FROM_HERE, 304 base::Bind( 305 &CopyLevelDBToProfile, shell(), context, EnclosingLevelDBDir())); 306 scoped_refptr<base::ThreadTestHelper> helper(new base::ThreadTestHelper( 307 BrowserMainLoop::GetInstance()->indexed_db_thread()-> 308 message_loop_proxy())); 309 ASSERT_TRUE(helper->Run()); 310 } 311 312 virtual std::string EnclosingLevelDBDir() = 0; 313 314 private: 315 DISALLOW_COPY_AND_ASSIGN(IndexedDBBrowserTestWithPreexistingLevelDB); 316 }; 317 318 class IndexedDBBrowserTestWithVersion0Schema : public 319 IndexedDBBrowserTestWithPreexistingLevelDB { 320 virtual std::string EnclosingLevelDBDir() OVERRIDE { 321 return "migration_from_0"; 322 } 323 }; 324 325 IN_PROC_BROWSER_TEST_F(IndexedDBBrowserTestWithVersion0Schema, MigrationTest) { 326 SimpleTest(GetTestUrl("indexeddb", "migration_test.html")); 327 } 328 329 class IndexedDBBrowserTestWithVersion123456Schema : public 330 IndexedDBBrowserTestWithPreexistingLevelDB { 331 virtual std::string EnclosingLevelDBDir() OVERRIDE { 332 return "schema_version_123456"; 333 } 334 }; 335 336 IN_PROC_BROWSER_TEST_F(IndexedDBBrowserTestWithVersion123456Schema, 337 DestroyTest) { 338 int64 original_size = RequestDiskUsage(); 339 EXPECT_GT(original_size, 0); 340 SimpleTest(GetTestUrl("indexeddb", "open_bad_db.html")); 341 int64 new_size = RequestDiskUsage(); 342 EXPECT_NE(original_size, new_size); 343 } 344 345 class IndexedDBBrowserTestWithVersion987654SSVData : public 346 IndexedDBBrowserTestWithPreexistingLevelDB { 347 virtual std::string EnclosingLevelDBDir() OVERRIDE { 348 return "ssv_version_987654"; 349 } 350 }; 351 352 IN_PROC_BROWSER_TEST_F(IndexedDBBrowserTestWithVersion987654SSVData, 353 DestroyTest) { 354 int64 original_size = RequestDiskUsage(); 355 EXPECT_GT(original_size, 0); 356 SimpleTest(GetTestUrl("indexeddb", "open_bad_db.html")); 357 int64 new_size = RequestDiskUsage(); 358 EXPECT_NE(original_size, new_size); 359 } 360 361 class IndexedDBBrowserTestWithCorruptLevelDB : public 362 IndexedDBBrowserTestWithPreexistingLevelDB { 363 virtual std::string EnclosingLevelDBDir() OVERRIDE { 364 return "corrupt_leveldb"; 365 } 366 }; 367 368 IN_PROC_BROWSER_TEST_F(IndexedDBBrowserTestWithCorruptLevelDB, 369 DestroyTest) { 370 int64 original_size = RequestDiskUsage(); 371 EXPECT_GT(original_size, 0); 372 SimpleTest(GetTestUrl("indexeddb", "open_bad_db.html")); 373 int64 new_size = RequestDiskUsage(); 374 EXPECT_NE(original_size, new_size); 375 } 376 377 class IndexedDBBrowserTestWithMissingSSTFile : public 378 IndexedDBBrowserTestWithPreexistingLevelDB { 379 virtual std::string EnclosingLevelDBDir() OVERRIDE { 380 return "missing_sst"; 381 } 382 }; 383 384 IN_PROC_BROWSER_TEST_F(IndexedDBBrowserTestWithMissingSSTFile, 385 DestroyTest) { 386 int64 original_size = RequestDiskUsage(); 387 EXPECT_GT(original_size, 0); 388 SimpleTest(GetTestUrl("indexeddb", "open_missing_table.html")); 389 int64 new_size = RequestDiskUsage(); 390 EXPECT_NE(original_size, new_size); 391 } 392 393 IN_PROC_BROWSER_TEST_F(IndexedDBBrowserTest, LevelDBLogFileTest) { 394 // Any page that opens an IndexedDB will work here. 395 SimpleTest(GetTestUrl("indexeddb", "database_test.html")); 396 base::FilePath leveldb_dir(FILE_PATH_LITERAL("file__0.indexeddb.leveldb")); 397 base::FilePath log_file(FILE_PATH_LITERAL("LOG")); 398 base::FilePath log_file_path = 399 GetContext()->data_path().Append(leveldb_dir).Append(log_file); 400 int64 size; 401 EXPECT_TRUE(base::GetFileSize(log_file_path, &size)); 402 EXPECT_GT(size, 0); 403 } 404 405 IN_PROC_BROWSER_TEST_F(IndexedDBBrowserTest, CanDeleteWhenOverQuotaTest) { 406 SimpleTest(GetTestUrl("indexeddb", "fill_up_5k.html")); 407 int64 size = RequestDiskUsage(); 408 const int kQuotaKilobytes = 2; 409 EXPECT_GT(size, kQuotaKilobytes * 1024); 410 SetQuota(kQuotaKilobytes); 411 SimpleTest(GetTestUrl("indexeddb", "delete_over_quota.html")); 412 } 413 414 namespace { 415 416 static void CompactIndexedDBBackingStore( 417 scoped_refptr<IndexedDBContextImpl> context, 418 const GURL& origin_url) { 419 IndexedDBFactory* factory = context->GetIDBFactory(); 420 421 std::pair<IndexedDBFactory::OriginDBMapIterator, 422 IndexedDBFactory::OriginDBMapIterator> range = 423 factory->GetOpenDatabasesForOrigin(origin_url); 424 425 if (range.first == range.second) // If no open db's for this origin 426 return; 427 428 // Compact the first db's backing store since all the db's are in the same 429 // backing store. 430 IndexedDBDatabase* db = range.first->second; 431 IndexedDBBackingStore* backing_store = db->backing_store(); 432 backing_store->Compact(); 433 } 434 435 static void CorruptIndexedDBDatabase( 436 IndexedDBContextImpl* context, 437 const GURL& origin_url, 438 base::WaitableEvent* signal_when_finished) { 439 440 CompactIndexedDBBackingStore(context, origin_url); 441 442 int numFiles = 0; 443 int numErrors = 0; 444 base::FilePath idb_data_path = context->GetFilePath(origin_url); 445 const bool recursive = false; 446 base::FileEnumerator enumerator( 447 idb_data_path, recursive, base::FileEnumerator::FILES); 448 for (base::FilePath idb_file = enumerator.Next(); !idb_file.empty(); 449 idb_file = enumerator.Next()) { 450 int64 size(0); 451 GetFileSize(idb_file, &size); 452 453 if (idb_file.Extension() == FILE_PATH_LITERAL(".ldb")) { 454 numFiles++; 455 base::File file(idb_file, 456 base::File::FLAG_WRITE | base::File::FLAG_OPEN_TRUNCATED); 457 if (file.IsValid()) { 458 // Was opened truncated, expand back to the original 459 // file size and fill with zeros (corrupting the file). 460 file.SetLength(size); 461 } else { 462 numErrors++; 463 } 464 } 465 } 466 467 VLOG(0) << "There were " << numFiles << " in " << idb_data_path.value() 468 << " with " << numErrors << " errors"; 469 signal_when_finished->Signal(); 470 } 471 472 const std::string s_corrupt_db_test_prefix = "/corrupt/test/"; 473 474 static scoped_ptr<net::test_server::HttpResponse> CorruptDBRequestHandler( 475 IndexedDBContextImpl* context, 476 const GURL& origin_url, 477 const std::string& path, 478 IndexedDBBrowserTest* test, 479 const net::test_server::HttpRequest& request) { 480 std::string request_path; 481 if (path.find(s_corrupt_db_test_prefix) != std::string::npos) 482 request_path = request.relative_url.substr(s_corrupt_db_test_prefix.size()); 483 else 484 return scoped_ptr<net::test_server::HttpResponse>(); 485 486 // Remove the query string if present. 487 std::string request_query; 488 size_t query_pos = request_path.find('?'); 489 if (query_pos != std::string::npos) { 490 request_query = request_path.substr(query_pos + 1); 491 request_path = request_path.substr(0, query_pos); 492 } 493 494 if (request_path == "corruptdb" && !request_query.empty()) { 495 VLOG(0) << "Requested to corrupt IndexedDB: " << request_query; 496 base::WaitableEvent signal_when_finished(false, false); 497 context->TaskRunner()->PostTask(FROM_HERE, 498 base::Bind(&CorruptIndexedDBDatabase, 499 base::ConstRef(context), 500 origin_url, 501 &signal_when_finished)); 502 signal_when_finished.Wait(); 503 504 scoped_ptr<net::test_server::BasicHttpResponse> http_response( 505 new net::test_server::BasicHttpResponse); 506 http_response->set_code(net::HTTP_OK); 507 return http_response.PassAs<net::test_server::HttpResponse>(); 508 } else if (request_path == "fail" && !request_query.empty()) { 509 FailClass failure_class = FAIL_CLASS_NOTHING; 510 FailMethod failure_method = FAIL_METHOD_NOTHING; 511 int instance_num = 1; 512 int call_num = 1; 513 std::string fail_class; 514 std::string fail_method; 515 516 url::Component query(0, request_query.length()), key_pos, value_pos; 517 while (url::ExtractQueryKeyValue( 518 request_query.c_str(), &query, &key_pos, &value_pos)) { 519 std::string escaped_key(request_query.substr(key_pos.begin, key_pos.len)); 520 std::string escaped_value( 521 request_query.substr(value_pos.begin, value_pos.len)); 522 523 std::string key = net::UnescapeURLComponent( 524 escaped_key, 525 net::UnescapeRule::NORMAL | net::UnescapeRule::SPACES | 526 net::UnescapeRule::URL_SPECIAL_CHARS); 527 528 std::string value = net::UnescapeURLComponent( 529 escaped_value, 530 net::UnescapeRule::NORMAL | net::UnescapeRule::SPACES | 531 net::UnescapeRule::URL_SPECIAL_CHARS); 532 533 if (key == "method") 534 fail_method = value; 535 else if (key == "class") 536 fail_class = value; 537 else if (key == "instNum") 538 instance_num = atoi(value.c_str()); 539 else if (key == "callNum") 540 call_num = atoi(value.c_str()); 541 else 542 NOTREACHED() << "Unknown param: \"" << key << "\""; 543 } 544 545 if (fail_class == "LevelDBTransaction") { 546 failure_class = FAIL_CLASS_LEVELDB_TRANSACTION; 547 if (fail_method == "Get") 548 failure_method = FAIL_METHOD_GET; 549 else if (fail_method == "Commit") 550 failure_method = FAIL_METHOD_COMMIT; 551 else 552 NOTREACHED() << "Unknown method: \"" << fail_method << "\""; 553 } else if (fail_class == "LevelDBIterator") { 554 failure_class = FAIL_CLASS_LEVELDB_ITERATOR; 555 if (fail_method == "Seek") 556 failure_method = FAIL_METHOD_SEEK; 557 else 558 NOTREACHED() << "Unknown method: \"" << fail_method << "\""; 559 } else { 560 NOTREACHED() << "Unknown class: \"" << fail_class << "\""; 561 } 562 563 DCHECK_GE(instance_num, 1); 564 DCHECK_GE(call_num, 1); 565 566 test->FailOperation(failure_class, failure_method, instance_num, call_num); 567 568 scoped_ptr<net::test_server::BasicHttpResponse> http_response( 569 new net::test_server::BasicHttpResponse); 570 http_response->set_code(net::HTTP_OK); 571 return http_response.PassAs<net::test_server::HttpResponse>(); 572 } 573 574 // A request for a test resource 575 base::FilePath resourcePath = 576 content::GetTestFilePath("indexeddb", request_path.c_str()); 577 scoped_ptr<net::test_server::BasicHttpResponse> http_response( 578 new net::test_server::BasicHttpResponse); 579 http_response->set_code(net::HTTP_OK); 580 std::string file_contents; 581 if (!base::ReadFileToString(resourcePath, &file_contents)) 582 return scoped_ptr<net::test_server::HttpResponse>(); 583 http_response->set_content(file_contents); 584 return http_response.PassAs<net::test_server::HttpResponse>(); 585 } 586 587 } // namespace 588 589 class IndexedDBBrowserCorruptionTest 590 : public IndexedDBBrowserTest, 591 public ::testing::WithParamInterface<const char*> {}; 592 593 IN_PROC_BROWSER_TEST_P(IndexedDBBrowserCorruptionTest, 594 OperationOnCorruptedOpenDatabase) { 595 ASSERT_TRUE(embedded_test_server()->Started() || 596 embedded_test_server()->InitializeAndWaitUntilReady()); 597 const GURL& origin_url = embedded_test_server()->base_url(); 598 embedded_test_server()->RegisterRequestHandler( 599 base::Bind(&CorruptDBRequestHandler, 600 base::Unretained(GetContext()), 601 origin_url, 602 s_corrupt_db_test_prefix, 603 this)); 604 605 std::string test_file = s_corrupt_db_test_prefix + 606 "corrupted_open_db_detection.html#" + GetParam(); 607 SimpleTest(embedded_test_server()->GetURL(test_file)); 608 609 test_file = s_corrupt_db_test_prefix + "corrupted_open_db_recovery.html"; 610 SimpleTest(embedded_test_server()->GetURL(test_file)); 611 } 612 613 INSTANTIATE_TEST_CASE_P(IndexedDBBrowserCorruptionTestInstantiation, 614 IndexedDBBrowserCorruptionTest, 615 ::testing::Values("failGetBlobJournal", 616 "get", 617 "failWebkitGetDatabaseNames", 618 "iterate", 619 "failTransactionCommit", 620 "clearObjectStore")); 621 622 IN_PROC_BROWSER_TEST_F(IndexedDBBrowserTest, 623 DeleteCompactsBackingStore) { 624 const GURL test_url = GetTestUrl("indexeddb", "delete_compact.html"); 625 SimpleTest(GURL(test_url.spec() + "#fill")); 626 int64 after_filling = RequestDiskUsage(); 627 EXPECT_GT(after_filling, 0); 628 629 SimpleTest(GURL(test_url.spec() + "#purge")); 630 int64 after_deleting = RequestDiskUsage(); 631 EXPECT_LT(after_deleting, after_filling); 632 633 // The above tests verify basic assertions - that filling writes data and 634 // deleting reduces the amount stored. 635 636 // The below tests make assumptions about implementation specifics, such as 637 // data compression, compaction efficiency, and the maximum amount of 638 // metadata and log data remains after a deletion. It is possible that 639 // changes to the implementation may require these constants to be tweaked. 640 641 const int kTestFillBytes = 1024 * 1024 * 5; // 5MB 642 EXPECT_GT(after_filling, kTestFillBytes); 643 644 const int kTestCompactBytes = 1024 * 10; // 10kB 645 EXPECT_LT(after_deleting, kTestCompactBytes); 646 } 647 648 // Complex multi-step (converted from pyauto) tests begin here. 649 650 // Verify null key path persists after restarting browser. 651 IN_PROC_BROWSER_TEST_F(IndexedDBBrowserTest, PRE_NullKeyPathPersistence) { 652 NavigateAndWaitForTitle(shell(), "bug_90635.html", "#part1", 653 "pass - first run"); 654 } 655 656 // Verify null key path persists after restarting browser. 657 IN_PROC_BROWSER_TEST_F(IndexedDBBrowserTest, NullKeyPathPersistence) { 658 NavigateAndWaitForTitle(shell(), "bug_90635.html", "#part2", 659 "pass - second run"); 660 } 661 662 // Verify that a VERSION_CHANGE transaction is rolled back after a 663 // renderer/browser crash 664 IN_PROC_BROWSER_TEST_F(IndexedDBBrowserTest, 665 PRE_PRE_VersionChangeCrashResilience) { 666 NavigateAndWaitForTitle(shell(), "version_change_crash.html", "#part1", 667 "pass - part1 - complete"); 668 } 669 670 IN_PROC_BROWSER_TEST_F(IndexedDBBrowserTest, PRE_VersionChangeCrashResilience) { 671 NavigateAndWaitForTitle(shell(), "version_change_crash.html", "#part2", 672 "pass - part2 - crash me"); 673 // If we actually crash here then googletest will not run the next step 674 // (VersionChangeCrashResilience) as an optimization. googletest's 675 // ASSERT_DEATH/EXIT fails to work properly (on Windows) due to how we 676 // implement the PRE_* test mechanism. 677 exit(0); 678 } 679 680 IN_PROC_BROWSER_TEST_F(IndexedDBBrowserTest, VersionChangeCrashResilience) { 681 NavigateAndWaitForTitle(shell(), "version_change_crash.html", "#part3", 682 "pass - part3 - rolled back"); 683 } 684 685 // Verify that open DB connections are closed when a tab is destroyed. 686 IN_PROC_BROWSER_TEST_F(IndexedDBBrowserTest, ConnectionsClosedOnTabClose) { 687 NavigateAndWaitForTitle(shell(), "version_change_blocked.html", "#tab1", 688 "setVersion(2) complete"); 689 690 // Start on a different URL to force a new renderer process. 691 Shell* new_shell = CreateBrowser(); 692 NavigateToURL(new_shell, GURL(url::kAboutBlankURL)); 693 NavigateAndWaitForTitle(new_shell, "version_change_blocked.html", "#tab2", 694 "setVersion(3) blocked"); 695 696 base::string16 expected_title16(ASCIIToUTF16("setVersion(3) complete")); 697 TitleWatcher title_watcher(new_shell->web_contents(), expected_title16); 698 699 base::KillProcess( 700 shell()->web_contents()->GetRenderProcessHost()->GetHandle(), 0, true); 701 shell()->Close(); 702 703 EXPECT_EQ(expected_title16, title_watcher.WaitAndGetTitle()); 704 } 705 706 // Verify that a "close" event is fired at database connections when 707 // the backing store is deleted. 708 IN_PROC_BROWSER_TEST_F(IndexedDBBrowserTest, ForceCloseEventTest) { 709 NavigateAndWaitForTitle(shell(), "force_close_event.html", NULL, 710 "connection ready"); 711 712 GetContext()->TaskRunner()->PostTask( 713 FROM_HERE, 714 base::Bind(&IndexedDBContextImpl::DeleteForOrigin, 715 GetContext(), 716 GURL("file:///"))); 717 718 base::string16 expected_title16(ASCIIToUTF16("connection closed")); 719 TitleWatcher title_watcher(shell()->web_contents(), expected_title16); 720 title_watcher.AlsoWaitForTitle(ASCIIToUTF16("connection closed with error")); 721 EXPECT_EQ(expected_title16, title_watcher.WaitAndGetTitle()); 722 } 723 724 class IndexedDBBrowserTestSingleProcess : public IndexedDBBrowserTest { 725 public: 726 virtual void SetUpCommandLine(CommandLine* command_line) OVERRIDE { 727 command_line->AppendSwitch(switches::kSingleProcess); 728 } 729 }; 730 731 IN_PROC_BROWSER_TEST_F(IndexedDBBrowserTestSingleProcess, 732 RenderThreadShutdownTest) { 733 SimpleTest(GetTestUrl("indexeddb", "shutdown_with_requests.html")); 734 } 735 736 } // namespace content 737