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 40using base::ASCIIToUTF16; 41using storage::QuotaManager; 42using storage::DatabaseUtil; 43 44namespace content { 45 46// This browser test is aimed towards exercising the IndexedDB bindings and 47// the actual implementation that lives in the browser side. 48class 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 173IN_PROC_BROWSER_TEST_F(IndexedDBBrowserTest, CursorTest) { 174 SimpleTest(GetTestUrl("indexeddb", "cursor_test.html")); 175} 176 177IN_PROC_BROWSER_TEST_F(IndexedDBBrowserTest, CursorTestIncognito) { 178 SimpleTest(GetTestUrl("indexeddb", "cursor_test.html"), 179 true /* incognito */); 180} 181 182IN_PROC_BROWSER_TEST_F(IndexedDBBrowserTest, CursorPrefetch) { 183 SimpleTest(GetTestUrl("indexeddb", "cursor_prefetch.html")); 184} 185 186IN_PROC_BROWSER_TEST_F(IndexedDBBrowserTest, IndexTest) { 187 SimpleTest(GetTestUrl("indexeddb", "index_test.html")); 188} 189 190IN_PROC_BROWSER_TEST_F(IndexedDBBrowserTest, KeyPathTest) { 191 SimpleTest(GetTestUrl("indexeddb", "key_path_test.html")); 192} 193 194IN_PROC_BROWSER_TEST_F(IndexedDBBrowserTest, TransactionGetTest) { 195 SimpleTest(GetTestUrl("indexeddb", "transaction_get_test.html")); 196} 197 198IN_PROC_BROWSER_TEST_F(IndexedDBBrowserTest, KeyTypesTest) { 199 SimpleTest(GetTestUrl("indexeddb", "key_types_test.html")); 200} 201 202IN_PROC_BROWSER_TEST_F(IndexedDBBrowserTest, ObjectStoreTest) { 203 SimpleTest(GetTestUrl("indexeddb", "object_store_test.html")); 204} 205 206IN_PROC_BROWSER_TEST_F(IndexedDBBrowserTest, DatabaseTest) { 207 SimpleTest(GetTestUrl("indexeddb", "database_test.html")); 208} 209 210IN_PROC_BROWSER_TEST_F(IndexedDBBrowserTest, TransactionTest) { 211 SimpleTest(GetTestUrl("indexeddb", "transaction_test.html")); 212} 213 214IN_PROC_BROWSER_TEST_F(IndexedDBBrowserTest, CallbackAccounting) { 215 SimpleTest(GetTestUrl("indexeddb", "callback_accounting.html")); 216} 217 218IN_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 224IN_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 231IN_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 238IN_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 245class 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 258IN_PROC_BROWSER_TEST_F(IndexedDBBrowserTestWithLowQuota, QuotaTest) { 259 SimpleTest(GetTestUrl("indexeddb", "quota_test.html")); 260} 261 262class 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 274IN_PROC_BROWSER_TEST_F(IndexedDBBrowserTestWithGCExposed, 275 DatabaseCallbacksTest) { 276 SimpleTest(GetTestUrl("indexeddb", "database_callbacks_first.html")); 277} 278 279static 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 297class 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 318class IndexedDBBrowserTestWithVersion0Schema : public 319 IndexedDBBrowserTestWithPreexistingLevelDB { 320 virtual std::string EnclosingLevelDBDir() OVERRIDE { 321 return "migration_from_0"; 322 } 323}; 324 325IN_PROC_BROWSER_TEST_F(IndexedDBBrowserTestWithVersion0Schema, MigrationTest) { 326 SimpleTest(GetTestUrl("indexeddb", "migration_test.html")); 327} 328 329class IndexedDBBrowserTestWithVersion123456Schema : public 330 IndexedDBBrowserTestWithPreexistingLevelDB { 331 virtual std::string EnclosingLevelDBDir() OVERRIDE { 332 return "schema_version_123456"; 333 } 334}; 335 336IN_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 345class IndexedDBBrowserTestWithVersion987654SSVData : public 346 IndexedDBBrowserTestWithPreexistingLevelDB { 347 virtual std::string EnclosingLevelDBDir() OVERRIDE { 348 return "ssv_version_987654"; 349 } 350}; 351 352IN_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 361class IndexedDBBrowserTestWithCorruptLevelDB : public 362 IndexedDBBrowserTestWithPreexistingLevelDB { 363 virtual std::string EnclosingLevelDBDir() OVERRIDE { 364 return "corrupt_leveldb"; 365 } 366}; 367 368IN_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 377class IndexedDBBrowserTestWithMissingSSTFile : public 378 IndexedDBBrowserTestWithPreexistingLevelDB { 379 virtual std::string EnclosingLevelDBDir() OVERRIDE { 380 return "missing_sst"; 381 } 382}; 383 384IN_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 393IN_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 405IN_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 414namespace { 415 416static 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 435static 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 472const std::string s_corrupt_db_test_prefix = "/corrupt/test/"; 473 474static 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 589class IndexedDBBrowserCorruptionTest 590 : public IndexedDBBrowserTest, 591 public ::testing::WithParamInterface<const char*> {}; 592 593IN_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 613INSTANTIATE_TEST_CASE_P(IndexedDBBrowserCorruptionTestInstantiation, 614 IndexedDBBrowserCorruptionTest, 615 ::testing::Values("failGetBlobJournal", 616 "get", 617 "failWebkitGetDatabaseNames", 618 "iterate", 619 "failTransactionCommit", 620 "clearObjectStore")); 621 622IN_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. 651IN_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. 657IN_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 664IN_PROC_BROWSER_TEST_F(IndexedDBBrowserTest, 665 PRE_PRE_VersionChangeCrashResilience) { 666 NavigateAndWaitForTitle(shell(), "version_change_crash.html", "#part1", 667 "pass - part1 - complete"); 668} 669 670IN_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 680IN_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. 686IN_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. 708IN_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 724class IndexedDBBrowserTestSingleProcess : public IndexedDBBrowserTest { 725 public: 726 virtual void SetUpCommandLine(CommandLine* command_line) OVERRIDE { 727 command_line->AppendSwitch(switches::kSingleProcess); 728 } 729}; 730 731IN_PROC_BROWSER_TEST_F(IndexedDBBrowserTestSingleProcess, 732 RenderThreadShutdownTest) { 733 SimpleTest(GetTestUrl("indexeddb", "shutdown_with_requests.html")); 734} 735 736} // namespace content 737