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_iterator_impl.h"
9#include "content/browser/indexed_db/leveldb/leveldb_transaction.h"
10#include "content/browser/indexed_db/mock_browsertest_indexed_db_class_factory.h"
11#include "third_party/leveldatabase/src/include/leveldb/status.h"
12
13namespace {
14
15class FunctionTracer {
16 public:
17  FunctionTracer(const std::string& class_name,
18                 const std::string& method_name,
19                 int instance_num)
20      : class_name_(class_name),
21        method_name_(method_name),
22        instance_count_(instance_num),
23        current_call_num_(0) {}
24
25  void log_call() {
26    current_call_num_++;
27    VLOG(0) << class_name_ << '[' << instance_count_ << "]::" << method_name_
28            << "()[" << current_call_num_ << ']';
29  }
30
31 private:
32  std::string class_name_;
33  std::string method_name_;
34  int instance_count_;
35  int current_call_num_;
36};
37
38}  // namespace
39
40namespace content {
41
42class LevelDBTestTansaction : public LevelDBTransaction {
43 public:
44  LevelDBTestTansaction(LevelDBDatabase* db,
45                        FailMethod fail_method,
46                        int fail_on_call_num)
47      : LevelDBTransaction(db),
48        fail_method_(fail_method),
49        fail_on_call_num_(fail_on_call_num),
50        current_call_num_(0) {
51    DCHECK(fail_method != FAIL_METHOD_NOTHING);
52    DCHECK_GT(fail_on_call_num, 0);
53  }
54
55  virtual leveldb::Status Get(const base::StringPiece& key,
56                              std::string* value,
57                              bool* found) OVERRIDE {
58    if (fail_method_ != FAIL_METHOD_GET ||
59        ++current_call_num_ != fail_on_call_num_)
60      return LevelDBTransaction::Get(key, value, found);
61
62    *found = false;
63    return leveldb::Status::Corruption("Corrupted for the test");
64  }
65
66  virtual leveldb::Status Commit() OVERRIDE {
67    if (fail_method_ != FAIL_METHOD_COMMIT ||
68        ++current_call_num_ != fail_on_call_num_)
69      return LevelDBTransaction::Commit();
70
71    return leveldb::Status::Corruption("Corrupted for the test");
72  }
73
74 private:
75  virtual ~LevelDBTestTansaction() {}
76
77  FailMethod fail_method_;
78  int fail_on_call_num_;
79  int current_call_num_;
80};
81
82class LevelDBTraceTansaction : public LevelDBTransaction {
83 public:
84  LevelDBTraceTansaction(LevelDBDatabase* db, int tx_num)
85      : LevelDBTransaction(db),
86        commit_tracer_(s_class_name, "Commit", tx_num),
87        get_tracer_(s_class_name, "Get", tx_num) {}
88
89  virtual leveldb::Status Get(const base::StringPiece& key,
90                              std::string* value,
91                              bool* found) OVERRIDE {
92    get_tracer_.log_call();
93    return LevelDBTransaction::Get(key, value, found);
94  }
95
96  virtual leveldb::Status Commit() OVERRIDE {
97    commit_tracer_.log_call();
98    return LevelDBTransaction::Commit();
99  }
100
101 private:
102  static const std::string s_class_name;
103
104  virtual ~LevelDBTraceTansaction() {}
105
106  FunctionTracer commit_tracer_;
107  FunctionTracer get_tracer_;
108};
109
110const std::string LevelDBTraceTansaction::s_class_name = "LevelDBTransaction";
111
112class LevelDBTraceIteratorImpl : public LevelDBIteratorImpl {
113 public:
114  LevelDBTraceIteratorImpl(scoped_ptr<leveldb::Iterator> iterator, int inst_num)
115      : LevelDBIteratorImpl(iterator.Pass()),
116        is_valid_tracer_(s_class_name, "IsValid", inst_num),
117        seek_to_last_tracer_(s_class_name, "SeekToLast", inst_num),
118        seek_tracer_(s_class_name, "Seek", inst_num),
119        next_tracer_(s_class_name, "Next", inst_num),
120        prev_tracer_(s_class_name, "Prev", inst_num),
121        key_tracer_(s_class_name, "Key", inst_num),
122        value_tracer_(s_class_name, "Value", inst_num) {}
123  virtual ~LevelDBTraceIteratorImpl() {}
124
125 private:
126  static const std::string s_class_name;
127
128  virtual bool IsValid() const OVERRIDE {
129    is_valid_tracer_.log_call();
130    return LevelDBIteratorImpl::IsValid();
131  }
132  virtual leveldb::Status SeekToLast() OVERRIDE {
133    seek_to_last_tracer_.log_call();
134    return LevelDBIteratorImpl::SeekToLast();
135  }
136  virtual leveldb::Status Seek(const base::StringPiece& target) OVERRIDE {
137    seek_tracer_.log_call();
138    return LevelDBIteratorImpl::Seek(target);
139  }
140  virtual leveldb::Status Next() OVERRIDE {
141    next_tracer_.log_call();
142    return LevelDBIteratorImpl::Next();
143  }
144  virtual leveldb::Status Prev() OVERRIDE {
145    prev_tracer_.log_call();
146    return LevelDBIteratorImpl::Prev();
147  }
148  virtual base::StringPiece Key() const OVERRIDE {
149    key_tracer_.log_call();
150    return LevelDBIteratorImpl::Key();
151  }
152  virtual base::StringPiece Value() const OVERRIDE {
153    value_tracer_.log_call();
154    return LevelDBIteratorImpl::Value();
155  }
156
157  mutable FunctionTracer is_valid_tracer_;
158  mutable FunctionTracer seek_to_last_tracer_;
159  mutable FunctionTracer seek_tracer_;
160  mutable FunctionTracer next_tracer_;
161  mutable FunctionTracer prev_tracer_;
162  mutable FunctionTracer key_tracer_;
163  mutable FunctionTracer value_tracer_;
164};
165
166const std::string LevelDBTraceIteratorImpl::s_class_name = "LevelDBIterator";
167
168class LevelDBTestIteratorImpl : public content::LevelDBIteratorImpl {
169 public:
170  LevelDBTestIteratorImpl(scoped_ptr<leveldb::Iterator> iterator,
171                          FailMethod fail_method,
172                          int fail_on_call_num)
173      : LevelDBIteratorImpl(iterator.Pass()),
174        fail_method_(fail_method),
175        fail_on_call_num_(fail_on_call_num),
176        current_call_num_(0) {}
177  virtual ~LevelDBTestIteratorImpl() {}
178
179 private:
180  virtual leveldb::Status Seek(const base::StringPiece& target) OVERRIDE {
181    if (fail_method_ != FAIL_METHOD_SEEK ||
182        ++current_call_num_ != fail_on_call_num_)
183      return LevelDBIteratorImpl::Seek(target);
184    return leveldb::Status::Corruption("Corrupted for test");
185  }
186
187  FailMethod fail_method_;
188  int fail_on_call_num_;
189  int current_call_num_;
190};
191
192MockBrowserTestIndexedDBClassFactory::MockBrowserTestIndexedDBClassFactory()
193    : failure_class_(FAIL_CLASS_NOTHING),
194      failure_method_(FAIL_METHOD_NOTHING),
195      only_trace_calls_(false) {
196}
197
198MockBrowserTestIndexedDBClassFactory::~MockBrowserTestIndexedDBClassFactory() {
199}
200
201LevelDBTransaction*
202MockBrowserTestIndexedDBClassFactory::CreateLevelDBTransaction(
203    LevelDBDatabase* db) {
204  instance_count_[FAIL_CLASS_LEVELDB_TRANSACTION] =
205      instance_count_[FAIL_CLASS_LEVELDB_TRANSACTION] + 1;
206  if (only_trace_calls_) {
207    return new LevelDBTraceTansaction(
208        db, instance_count_[FAIL_CLASS_LEVELDB_TRANSACTION]);
209  } else {
210    if (failure_class_ == FAIL_CLASS_LEVELDB_TRANSACTION &&
211        instance_count_[FAIL_CLASS_LEVELDB_TRANSACTION] ==
212            fail_on_instance_num_[FAIL_CLASS_LEVELDB_TRANSACTION]) {
213      return new LevelDBTestTansaction(
214          db,
215          failure_method_,
216          fail_on_call_num_[FAIL_CLASS_LEVELDB_TRANSACTION]);
217    } else {
218      return IndexedDBClassFactory::CreateLevelDBTransaction(db);
219    }
220  }
221}
222
223LevelDBIteratorImpl* MockBrowserTestIndexedDBClassFactory::CreateIteratorImpl(
224    scoped_ptr<leveldb::Iterator> iterator) {
225  instance_count_[FAIL_CLASS_LEVELDB_ITERATOR] =
226      instance_count_[FAIL_CLASS_LEVELDB_ITERATOR] + 1;
227  if (only_trace_calls_) {
228    return new LevelDBTraceIteratorImpl(
229        iterator.Pass(), instance_count_[FAIL_CLASS_LEVELDB_ITERATOR]);
230  } else {
231    if (failure_class_ == FAIL_CLASS_LEVELDB_ITERATOR &&
232        instance_count_[FAIL_CLASS_LEVELDB_ITERATOR] ==
233            fail_on_instance_num_[FAIL_CLASS_LEVELDB_ITERATOR]) {
234      return new LevelDBTestIteratorImpl(
235          iterator.Pass(),
236          failure_method_,
237          fail_on_call_num_[FAIL_CLASS_LEVELDB_ITERATOR]);
238    } else {
239      return new LevelDBIteratorImpl(iterator.Pass());
240    }
241  }
242}
243
244void MockBrowserTestIndexedDBClassFactory::FailOperation(
245    FailClass failure_class,
246    FailMethod failure_method,
247    int fail_on_instance_num,
248    int fail_on_call_num) {
249  VLOG(0) << "FailOperation: class=" << failure_class
250          << ", method=" << failure_method
251          << ", instanceNum=" << fail_on_instance_num
252          << ", callNum=" << fail_on_call_num;
253  DCHECK(failure_class != FAIL_CLASS_NOTHING);
254  DCHECK(failure_method != FAIL_METHOD_NOTHING);
255  failure_class_ = failure_class;
256  failure_method_ = failure_method;
257  fail_on_instance_num_[failure_class_] = fail_on_instance_num;
258  fail_on_call_num_[failure_class_] = fail_on_call_num;
259  instance_count_.clear();
260}
261
262void MockBrowserTestIndexedDBClassFactory::Reset() {
263  failure_class_ = FAIL_CLASS_NOTHING;
264  failure_method_ = FAIL_METHOD_NOTHING;
265  instance_count_.clear();
266  fail_on_instance_num_.clear();
267  fail_on_call_num_.clear();
268}
269
270}  // namespace content
271