1// Copyright 2014 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 <string>
6
7#include "base/logging.h"
8#include "content/browser/indexed_db/leveldb/leveldb_transaction.h"
9#include "content/browser/indexed_db/mock_browsertest_indexed_db_class_factory.h"
10#include "third_party/leveldatabase/src/include/leveldb/status.h"
11
12namespace {
13
14class FunctionTracer {
15 public:
16  FunctionTracer(const std::string& class_name,
17                 const std::string& method_name,
18                 int instance_num)
19      : class_name_(class_name),
20        method_name_(method_name),
21        instance_count_(instance_num),
22        current_call_num_(0) {}
23
24  void log_call() {
25    current_call_num_++;
26    VLOG(0) << class_name_ << '[' << instance_count_ << "]::" << method_name_
27            << "()[" << current_call_num_ << ']';
28  }
29
30 private:
31  std::string class_name_;
32  std::string method_name_;
33  int instance_count_;
34  int current_call_num_;
35};
36
37}  // namespace
38
39namespace content {
40
41class LevelDBTestTansaction : public LevelDBTransaction {
42 public:
43  LevelDBTestTansaction(LevelDBDatabase* db,
44                        FailMethod fail_method,
45                        int fail_on_call_num)
46      : LevelDBTransaction(db),
47        fail_method_(fail_method),
48        fail_on_call_num_(fail_on_call_num),
49        current_call_num_(0) {
50    DCHECK(fail_method != FAIL_METHOD_NOTHING);
51    DCHECK_GT(fail_on_call_num, 0);
52  }
53
54  virtual leveldb::Status Get(const base::StringPiece& key,
55                              std::string* value,
56                              bool* found) OVERRIDE {
57    if (fail_method_ != FAIL_METHOD_GET ||
58        ++current_call_num_ != fail_on_call_num_)
59      return LevelDBTransaction::Get(key, value, found);
60
61    *found = false;
62    return leveldb::Status::Corruption("Corrupted for the test");
63  }
64
65  virtual leveldb::Status Commit() OVERRIDE {
66    if (fail_method_ != FAIL_METHOD_COMMIT ||
67        ++current_call_num_ != fail_on_call_num_)
68      return LevelDBTransaction::Commit();
69
70    return leveldb::Status::Corruption("Corrupted for the test");
71  }
72
73 private:
74  virtual ~LevelDBTestTansaction() {}
75
76  FailMethod fail_method_;
77  int fail_on_call_num_;
78  int current_call_num_;
79};
80
81class LevelDBTraceTansaction : public LevelDBTransaction {
82 public:
83  LevelDBTraceTansaction(LevelDBDatabase* db, int tx_num)
84      : LevelDBTransaction(db),
85        commit_tracer_(s_class_name, "Commit", tx_num),
86        get_tracer_(s_class_name, "Get", tx_num) {}
87
88  virtual leveldb::Status Get(const base::StringPiece& key,
89                              std::string* value,
90                              bool* found) OVERRIDE {
91    get_tracer_.log_call();
92    return LevelDBTransaction::Get(key, value, found);
93  }
94
95  virtual leveldb::Status Commit() OVERRIDE {
96    commit_tracer_.log_call();
97    return LevelDBTransaction::Commit();
98  }
99
100 private:
101  virtual ~LevelDBTraceTansaction() {}
102
103  const static std::string s_class_name;
104
105  FunctionTracer commit_tracer_;
106  FunctionTracer get_tracer_;
107};
108
109const std::string LevelDBTraceTansaction::s_class_name = "LevelDBTransaction";
110
111MockBrowserTestIndexedDBClassFactory::MockBrowserTestIndexedDBClassFactory()
112    : failure_class_(FAIL_CLASS_NOTHING),
113      failure_method_(FAIL_METHOD_NOTHING),
114      only_trace_calls_(false) {
115}
116
117MockBrowserTestIndexedDBClassFactory::~MockBrowserTestIndexedDBClassFactory() {
118}
119
120LevelDBTransaction*
121MockBrowserTestIndexedDBClassFactory::CreateLevelDBTransaction(
122    LevelDBDatabase* db) {
123  instance_count_[FAIL_CLASS_LEVELDB_TRANSACTION] =
124      instance_count_[FAIL_CLASS_LEVELDB_TRANSACTION] + 1;
125  if (only_trace_calls_) {
126    return new LevelDBTraceTansaction(
127        db, instance_count_[FAIL_CLASS_LEVELDB_TRANSACTION]);
128  } else {
129    if (failure_class_ == FAIL_CLASS_LEVELDB_TRANSACTION &&
130        instance_count_[FAIL_CLASS_LEVELDB_TRANSACTION] ==
131            fail_on_instance_num_[FAIL_CLASS_LEVELDB_TRANSACTION]) {
132      return new LevelDBTestTansaction(
133          db,
134          failure_method_,
135          fail_on_call_num_[FAIL_CLASS_LEVELDB_TRANSACTION]);
136    } else {
137      return IndexedDBClassFactory::CreateLevelDBTransaction(db);
138    }
139  }
140}
141
142void MockBrowserTestIndexedDBClassFactory::FailOperation(
143    FailClass failure_class,
144    FailMethod failure_method,
145    int fail_on_instance_num,
146    int fail_on_call_num) {
147  VLOG(0) << "FailOperation: class=" << failure_class
148          << ", method=" << failure_method
149          << ", instanceNum=" << fail_on_instance_num
150          << ", callNum=" << fail_on_call_num;
151  DCHECK(failure_class != FAIL_CLASS_NOTHING);
152  DCHECK(failure_method != FAIL_METHOD_NOTHING);
153  failure_class_ = failure_class;
154  failure_method_ = failure_method;
155  fail_on_instance_num_[failure_class_] = fail_on_instance_num;
156  fail_on_call_num_[failure_class_] = fail_on_call_num;
157  instance_count_.clear();
158}
159
160void MockBrowserTestIndexedDBClassFactory::Reset() {
161  failure_class_ = FAIL_CLASS_NOTHING;
162  failure_method_ = FAIL_METHOD_NOTHING;
163  instance_count_.clear();
164  fail_on_instance_num_.clear();
165  fail_on_call_num_.clear();
166}
167
168}  // namespace content
169