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..8210ab45fe627e0b01323dfdc887f2204d3f23b1 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,11 +42,192 @@ 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_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_; |
+}; |
+ |
+static ::base::LazyInstance<IndexedDBBrowserTestClassFactory>::Leaky s_factory = |
+ LAZY_INSTANCE_INITIALIZER; |
+ |
// 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) {} |
+ IndexedDBBrowserTest() |
+ : disk_usage_(-1), |
+ test_class_factory_(new IndexedDBBrowserTestClassFactory) {} |
ericu
2014/06/17 01:12:53
Is test_class_factory_ actually used somewhere? I
cmumford
2014/06/17 16:29:24
Embarrassingly no :-|
|
+ |
+ static IndexedDBClassFactory* GetIDBClassFactory() { |
+ return s_factory.Pointer(); |
+ } |
+ |
+ virtual void SetUp() OVERRIDE { |
+ s_factory.Get().Reset(); |
+ IndexedDBClassFactory::SetIndexedDBClassFactoryGetter(GetIDBClassFactory); |
+ ContentBrowserTest::SetUp(); |
+ } |
+ |
+ virtual void TearDown() OVERRIDE { |
+ IndexedDBClassFactory::SetIndexedDBClassFactoryGetter(NULL); |
ericu
2014/06/17 01:12:53
Given this cleanup, do we need s_factory to be a s
cmumford
2014/06/17 16:29:24
I was able to eliminate the use of s_factory in Co
|
+ ContentBrowserTest::TearDown(); |
+ } |
void SimpleTest(const GURL& test_url, bool incognito = false) { |
// The test page will perform tests on IndexedDB, then navigate to either |
@@ -133,6 +317,7 @@ class IndexedDBBrowserTest : public ContentBrowserTest { |
} |
int64 disk_usage_; |
+ IndexedDBBrowserTestClassFactory* test_class_factory_; |
DISALLOW_COPY_AND_ASSIGN(IndexedDBBrowserTest); |
}; |
@@ -452,10 +637,13 @@ 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 +660,55 @@ 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 == "num") |
ericu
2014/06/17 01:12:53
Should "num" be qualified, just as "callNum" is?
cmumford
2014/06/17 16:29:24
How about "instNum"?
ericu
2014/06/17 17:03:04
Sure.
|
+ 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; |
+ } |
ericu
2014/06/17 01:12:53
else...? It's undoubtedly a typo of some sort.
cmumford
2014/06/17 16:29:24
Done. A DCHECK would have caught the error, but a
|
+ |
+ DCHECK(instance_num >= 1); |
+ DCHECK(call_num >= 1); |
+ |
+ s_factory.Get().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>(); |
+ } else if (request_path == "nothing") { |
ericu
2014/06/17 01:12:53
Is this clause needed by anything, or do you have
cmumford
2014/06/17 16:29:24
Oops - shouldn't have been there. I was experiment
|
+ 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 |