Index: content/browser/indexed_db/indexed_db_browsertest.cc |
diff --git a/content/browser/indexed_db/indexed_db_browsertest.cc b/content/browser/indexed_db/indexed_db_browsertest.cc |
index 0d62a1336749cddc3cdb854f2c44e8292fe0cdc2..9d784a06a9551c7a500ee7b02e0389eb1d97dbd6 100644 |
--- a/content/browser/indexed_db/indexed_db_browsertest.cc |
+++ b/content/browser/indexed_db/indexed_db_browsertest.cc |
@@ -8,11 +8,14 @@ |
#include "base/files/file.h" |
#include "base/files/file_enumerator.h" |
#include "base/files/file_path.h" |
+#include "base/lazy_instance.h" |
#include "base/memory/ref_counted.h" |
#include "base/message_loop/message_loop.h" |
+#include "base/strings/string_split.h" |
#include "base/strings/utf_string_conversions.h" |
#include "base/test/thread_test_helper.h" |
#include "content/browser/browser_main_loop.h" |
+#include "content/browser/indexed_db/indexed_db_class_factory.h" |
#include "content/browser/indexed_db/indexed_db_context_impl.h" |
#include "content/browser/web_contents/web_contents_impl.h" |
#include "content/public/browser/browser_context.h" |
@@ -39,12 +42,193 @@ using webkit_database::DatabaseUtil; |
namespace content { |
+enum FailClass { |
+ FAIL_CLASS_NOTHING, |
+ FAIL_CLASS_LEVELDB_TRANSACTION, |
+}; |
+ |
+enum FailMethod { |
+ FAIL_METHOD_NOTHING, |
+ FAIL_METHOD_COMMIT, |
+ FAIL_METHOD_GET, |
+}; |
+ |
+class FunctionTracer { |
+ public: |
+ FunctionTracer(const std::string& class_name, |
+ const std::string& method_name, |
+ int instance_num) |
+ : class_name_(class_name), |
+ method_name_(method_name), |
+ instance_count_(instance_num), |
+ current_call_num_(0) {} |
+ |
+ void log_call() { |
+ current_call_num_++; |
+ VLOG(0) << class_name_ << '[' << instance_count_ << "]::" << method_name_ |
+ << "()[" << current_call_num_ << ']'; |
+ } |
+ |
+ private: |
+ std::string class_name_; |
+ std::string method_name_; |
+ int instance_count_; |
+ int current_call_num_; |
+}; |
+ |
+class LevelDBTestTansaction : public LevelDBTransaction { |
+ public: |
+ LevelDBTestTansaction(LevelDBDatabase* db, |
+ FailMethod fail_method, |
+ int fail_on_call_num) |
+ : LevelDBTransaction(db), |
+ fail_method_(fail_method), |
+ fail_on_call_num_(fail_on_call_num), |
+ current_call_num_(0) { |
+ DCHECK(fail_method != FAIL_METHOD_NOTHING); |
+ DCHECK_GT(fail_on_call_num, 0); |
+ } |
+ |
+ virtual leveldb::Status Get(const base::StringPiece& key, |
+ std::string* value, |
+ bool* found) OVERRIDE { |
+ if (fail_method_ != FAIL_METHOD_GET || |
+ ++current_call_num_ != fail_on_call_num_) |
+ return LevelDBTransaction::Get(key, value, found); |
+ |
+ *found = false; |
+ return leveldb::Status::Corruption("Corrupted for the test"); |
+ } |
+ |
+ virtual leveldb::Status Commit() OVERRIDE { |
+ if (fail_method_ != FAIL_METHOD_COMMIT || |
+ ++current_call_num_ != fail_on_call_num_) |
+ return LevelDBTransaction::Commit(); |
+ |
+ return leveldb::Status::Corruption("Corrupted for the test"); |
+ } |
+ |
+ private: |
+ virtual ~LevelDBTestTansaction() {} |
+ |
+ FailMethod fail_method_; |
+ int fail_on_call_num_; |
+ int current_call_num_; |
+}; |
+ |
+class LevelDBTraceTansaction : public LevelDBTransaction { |
+ public: |
+ LevelDBTraceTansaction(LevelDBDatabase* db, int tx_num) |
+ : LevelDBTransaction(db), |
+ commit_tracer_(s_class_name, "Commit", tx_num), |
+ get_tracer_(s_class_name, "Get", tx_num) {} |
+ |
+ virtual leveldb::Status Get(const base::StringPiece& key, |
+ std::string* value, |
+ bool* found) OVERRIDE { |
+ get_tracer_.log_call(); |
+ return LevelDBTransaction::Get(key, value, found); |
+ } |
+ |
+ virtual leveldb::Status Commit() OVERRIDE { |
+ commit_tracer_.log_call(); |
+ return LevelDBTransaction::Commit(); |
+ } |
+ |
+ private: |
+ virtual ~LevelDBTraceTansaction() {} |
+ |
+ const std::string s_class_name = "LevelDBTransaction"; |
+ |
+ FunctionTracer commit_tracer_; |
+ FunctionTracer get_tracer_; |
+}; |
+ |
+class IndexedDBBrowserTestClassFactory : public IndexedDBClassFactory { |
+ public: |
+ IndexedDBBrowserTestClassFactory() |
+ : failure_class_(FAIL_CLASS_NOTHING), |
+ failure_method_(FAIL_METHOD_NOTHING), |
+ only_trace_calls_(false) {} |
+ virtual LevelDBTransaction* CreateLevelDBTransaction( |
+ LevelDBDatabase* db) OVERRIDE { |
+ instance_count_[FAIL_CLASS_LEVELDB_TRANSACTION] = |
+ instance_count_[FAIL_CLASS_LEVELDB_TRANSACTION] + 1; |
+ if (only_trace_calls_) { |
+ return new LevelDBTraceTansaction( |
+ db, instance_count_[FAIL_CLASS_LEVELDB_TRANSACTION]); |
+ } else { |
+ if (failure_class_ == FAIL_CLASS_LEVELDB_TRANSACTION && |
+ instance_count_[FAIL_CLASS_LEVELDB_TRANSACTION] == |
+ fail_on_instance_num_[FAIL_CLASS_LEVELDB_TRANSACTION]) |
+ return new LevelDBTestTansaction( |
+ db, |
+ failure_method_, |
+ fail_on_call_num_[FAIL_CLASS_LEVELDB_TRANSACTION]); |
+ else |
+ return IndexedDBClassFactory::CreateLevelDBTransaction(db); |
+ } |
+ } |
+ |
+ void FailOperation(FailClass failure_class, |
+ FailMethod failure_method, |
+ int fail_on_instance_num, |
+ int fail_on_call_num) { |
+ VLOG(0) << "FailOperation: class=" << failure_class |
+ << ", method=" << failure_method |
+ << ", instanceNum=" << fail_on_instance_num |
+ << ", callNum=" << fail_on_call_num; |
+ DCHECK(failure_class != FAIL_CLASS_NOTHING); |
+ DCHECK(failure_method != FAIL_METHOD_NOTHING); |
+ failure_class_ = failure_class; |
+ failure_method_ = failure_method; |
+ fail_on_instance_num_[failure_class_] = fail_on_instance_num; |
+ fail_on_call_num_[failure_class_] = fail_on_call_num; |
+ instance_count_.clear(); |
+ } |
+ |
+ void Reset() { |
+ failure_class_ = FAIL_CLASS_NOTHING; |
+ failure_method_ = FAIL_METHOD_NOTHING; |
+ instance_count_.clear(); |
+ fail_on_instance_num_.clear(); |
+ fail_on_call_num_.clear(); |
+ } |
+ |
+ private: |
+ FailClass failure_class_; |
+ FailMethod failure_method_; |
+ std::map<FailClass, int> instance_count_; |
+ std::map<FailClass, int> fail_on_instance_num_; |
+ std::map<FailClass, int> fail_on_call_num_; |
+ bool only_trace_calls_; |
+}; |
+ |
// This browser test is aimed towards exercising the IndexedDB bindings and |
// the actual implementation that lives in the browser side. |
class IndexedDBBrowserTest : public ContentBrowserTest { |
public: |
IndexedDBBrowserTest() : disk_usage_(-1) {} |
+ virtual void SetUp() OVERRIDE { |
+ GetTestClassFactory()->Reset(); |
+ IndexedDBClassFactory::SetIndexedDBClassFactoryGetter(GetIDBClassFactory); |
ericu
2014/06/17 17:03:05
Can you pass in GetTestClassFactory here, or is a
cmumford
2014/06/17 21:53:18
Yes, I gave it a try, but compiler wasn't able to
ericu
2014/06/17 21:55:45
Yeah, let's stay away from that stuff. There be d
|
+ ContentBrowserTest::SetUp(); |
+ } |
+ |
+ virtual void TearDown() OVERRIDE { |
+ IndexedDBClassFactory::SetIndexedDBClassFactoryGetter(NULL); |
+ ContentBrowserTest::TearDown(); |
+ } |
+ |
+ void FailOperation(FailClass failure_class, |
+ FailMethod failure_method, |
+ int fail_on_instance_num, |
+ int fail_on_call_num) { |
+ GetTestClassFactory()->FailOperation( |
+ failure_class, failure_method, fail_on_instance_num, fail_on_call_num); |
+ } |
+ |
void SimpleTest(const GURL& test_url, bool incognito = false) { |
// The test page will perform tests on IndexedDB, then navigate to either |
// a #pass or #fail ref. |
@@ -127,6 +311,16 @@ class IndexedDBBrowserTest : public ContentBrowserTest { |
} |
private: |
+ static IndexedDBBrowserTestClassFactory* GetTestClassFactory() { |
+ static ::base::LazyInstance<IndexedDBBrowserTestClassFactory>::Leaky |
+ s_factory = LAZY_INSTANCE_INITIALIZER; |
+ return s_factory.Pointer(); |
+ } |
+ |
+ static IndexedDBClassFactory* GetIDBClassFactory() { |
+ return GetTestClassFactory(); |
+ } |
+ |
virtual void DidGetDiskUsage(int64 bytes) { |
EXPECT_GT(bytes, 0); |
disk_usage_ = bytes; |
@@ -442,8 +636,8 @@ static scoped_ptr<net::test_server::HttpResponse> CorruptDBRequestHandler( |
IndexedDBContextImpl* context, |
const GURL& origin_url, |
const std::string& path, |
+ IndexedDBBrowserTest* test, |
const net::test_server::HttpRequest& request) { |
- |
std::string request_path; |
if (path.find(s_corrupt_db_test_prefix) != std::string::npos) |
request_path = request.relative_url.substr(s_corrupt_db_test_prefix.size()); |
@@ -452,10 +646,12 @@ static scoped_ptr<net::test_server::HttpResponse> CorruptDBRequestHandler( |
// Remove the query string if present. |
std::string request_query; |
+ std::vector<std::pair<std::string, std::string> > query_params; |
size_t query_pos = request_path.find('?'); |
if (query_pos != std::string::npos) { |
request_query = request_path.substr(query_pos + 1); |
request_path = request_path.substr(0, query_pos); |
+ base::SplitStringIntoKeyValuePairs(request_query, '=', '&', &query_params); |
} |
if (request_path == "corruptdb" && !request_query.empty()) { |
@@ -472,6 +668,53 @@ static scoped_ptr<net::test_server::HttpResponse> CorruptDBRequestHandler( |
new net::test_server::BasicHttpResponse); |
http_response->set_code(net::HTTP_OK); |
return http_response.PassAs<net::test_server::HttpResponse>(); |
+ } else if (request_path == "fail" && !query_params.empty()) { |
+ FailClass failure_class = FAIL_CLASS_NOTHING; |
+ FailMethod failure_method = FAIL_METHOD_NOTHING; |
+ int instance_num = 1; |
+ int call_num = 1; |
+ std::string fail_class; |
+ std::string fail_method; |
+ |
+ for (std::vector<std::pair<std::string, std::string> >::iterator it = |
+ query_params.begin(); |
+ it != query_params.end(); |
+ it++) { |
+ if (it->first == "method") |
+ fail_method = it->second; |
+ else if (it->first == "class") |
+ fail_class = it->second; |
+ else if (it->first == "instNum") |
+ instance_num = atoi(it->second.c_str()); |
+ else if (it->first == "callNum") |
+ call_num = atoi(it->second.c_str()); |
+ else { |
+ VLOG(0) << "Unknown param: \"" << it->first << '"'; |
+ NOTREACHED(); |
+ } |
+ } |
+ |
+ if (fail_class == "LevelDBTransaction") { |
+ failure_class = FAIL_CLASS_LEVELDB_TRANSACTION; |
+ if (fail_method == "Get") |
+ failure_method = FAIL_METHOD_GET; |
+ else if (fail_method == "Commit") |
+ failure_method = FAIL_METHOD_COMMIT; |
+ else { |
+ VLOG(0) << "Unknown method: \"" << fail_method << '"'; |
ericu
2014/06/17 17:03:05
Nit: It's strange to use two different methods to
cmumford
2014/06/17 21:53:18
Done.
|
+ NOTREACHED(); |
+ } |
+ } |
+ |
+ DCHECK(instance_num >= 1); |
+ DCHECK(call_num >= 1); |
+ |
+ test->FailOperation(failure_class, failure_method, instance_num, call_num); |
+ |
+ scoped_ptr<net::test_server::BasicHttpResponse> http_response( |
+ new net::test_server::BasicHttpResponse); |
+ http_response->set_code(net::HTTP_OK); |
+ return http_response.PassAs<net::test_server::HttpResponse>(); |
} |
// A request for a test resource |
@@ -502,7 +745,8 @@ IN_PROC_BROWSER_TEST_P(IndexedDBBrowserCorruptionTest, |
base::Bind(&CorruptDBRequestHandler, |
base::ConstRef(GetContext()), |
origin_url, |
- s_corrupt_db_test_prefix)); |
+ s_corrupt_db_test_prefix, |
+ this)); |
std::string test_file = s_corrupt_db_test_prefix + |
"corrupted_open_db_detection.html#" + GetParam(); |