1// Copyright (c) 2013 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 <cerrno>
6
7#include "base/files/file.h"
8#include "base/files/file_path.h"
9#include "base/files/scoped_temp_dir.h"
10#include "base/strings/string16.h"
11#include "base/strings/utf_string_conversions.h"
12#include "content/browser/indexed_db/indexed_db_backing_store.h"
13#include "content/browser/indexed_db/leveldb/leveldb_database.h"
14#include "content/browser/indexed_db/leveldb/mock_leveldb_factory.h"
15#include "testing/gmock/include/gmock/gmock.h"
16#include "testing/gtest/include/gtest/gtest.h"
17#include "third_party/leveldatabase/env_chromium.h"
18
19using base::StringPiece;
20using content::IndexedDBBackingStore;
21using content::LevelDBComparator;
22using content::LevelDBDatabase;
23using content::LevelDBFactory;
24using content::LevelDBSnapshot;
25using testing::_;
26using testing::Exactly;
27using testing::Invoke;
28
29namespace base {
30class TaskRunner;
31}
32
33namespace content {
34class IndexedDBFactory;
35}
36
37namespace net {
38class URLRequestContext;
39}
40
41namespace {
42
43class BustedLevelDBDatabase : public LevelDBDatabase {
44 public:
45  BustedLevelDBDatabase() {}
46  static scoped_ptr<LevelDBDatabase> Open(
47      const base::FilePath& file_name,
48      const LevelDBComparator* /*comparator*/) {
49    return scoped_ptr<LevelDBDatabase>(new BustedLevelDBDatabase);
50  }
51  virtual leveldb::Status Get(const base::StringPiece& key,
52                              std::string* value,
53                              bool* found,
54                              const LevelDBSnapshot* = 0) OVERRIDE {
55    return leveldb::Status::IOError("It's busted!");
56  }
57
58 private:
59  DISALLOW_COPY_AND_ASSIGN(BustedLevelDBDatabase);
60};
61
62class BustedLevelDBFactory : public LevelDBFactory {
63 public:
64  virtual leveldb::Status OpenLevelDB(
65      const base::FilePath& file_name,
66      const LevelDBComparator* comparator,
67      scoped_ptr<LevelDBDatabase>* db,
68      bool* is_disk_full = 0) OVERRIDE {
69    if (open_error_.ok())
70      *db = BustedLevelDBDatabase::Open(file_name, comparator);
71    return open_error_;
72  }
73  virtual leveldb::Status DestroyLevelDB(
74      const base::FilePath& file_name) OVERRIDE {
75    return leveldb::Status::IOError("error");
76  }
77  void SetOpenError(const leveldb::Status& open_error) {
78    open_error_ = open_error;
79  }
80
81 private:
82  leveldb::Status open_error_;
83};
84
85TEST(IndexedDBIOErrorTest, CleanUpTest) {
86  content::IndexedDBFactory* factory = NULL;
87  const GURL origin("http://localhost:81");
88  base::ScopedTempDir temp_directory;
89  ASSERT_TRUE(temp_directory.CreateUniqueTempDir());
90  const base::FilePath path = temp_directory.path();
91  net::URLRequestContext* request_context = NULL;
92
93  BustedLevelDBFactory busted_factory;
94  content::MockLevelDBFactory mock_leveldb_factory;
95  ON_CALL(mock_leveldb_factory, OpenLevelDB(_, _, _, _)).WillByDefault(
96      Invoke(&busted_factory, &BustedLevelDBFactory::OpenLevelDB));
97  ON_CALL(mock_leveldb_factory, DestroyLevelDB(_)).WillByDefault(
98      Invoke(&busted_factory, &BustedLevelDBFactory::DestroyLevelDB));
99
100  EXPECT_CALL(mock_leveldb_factory, OpenLevelDB(_, _, _, _)).Times(Exactly(1));
101  EXPECT_CALL(mock_leveldb_factory, DestroyLevelDB(_)).Times(Exactly(1));
102  blink::WebIDBDataLoss data_loss = blink::WebIDBDataLossNone;
103  std::string data_loss_message;
104  bool disk_full = false;
105  base::SequencedTaskRunner* task_runner = NULL;
106  bool clean_journal = false;
107  leveldb::Status s;
108  scoped_refptr<IndexedDBBackingStore> backing_store =
109      IndexedDBBackingStore::Open(factory,
110                                  origin,
111                                  path,
112                                  request_context,
113                                  &data_loss,
114                                  &data_loss_message,
115                                  &disk_full,
116                                  &mock_leveldb_factory,
117                                  task_runner,
118                                  clean_journal,
119                                  &s);
120}
121
122TEST(IndexedDBNonRecoverableIOErrorTest, NuancedCleanupTest) {
123  content::IndexedDBFactory* factory = NULL;
124  const GURL origin("http://localhost:81");
125  net::URLRequestContext* request_context = NULL;
126  base::ScopedTempDir temp_directory;
127  ASSERT_TRUE(temp_directory.CreateUniqueTempDir());
128  const base::FilePath path = temp_directory.path();
129  blink::WebIDBDataLoss data_loss =
130      blink::WebIDBDataLossNone;
131  std::string data_loss_reason;
132  bool disk_full = false;
133  base::SequencedTaskRunner* task_runner = NULL;
134  bool clean_journal = false;
135  leveldb::Status s;
136
137  BustedLevelDBFactory busted_factory;
138  content::MockLevelDBFactory mock_leveldb_factory;
139  ON_CALL(mock_leveldb_factory, OpenLevelDB(_, _, _, _)).WillByDefault(
140      Invoke(&busted_factory, &BustedLevelDBFactory::OpenLevelDB));
141  ON_CALL(mock_leveldb_factory, DestroyLevelDB(_)).WillByDefault(
142      Invoke(&busted_factory, &BustedLevelDBFactory::DestroyLevelDB));
143
144  EXPECT_CALL(mock_leveldb_factory, OpenLevelDB(_, _, _, _)).Times(Exactly(4));
145  EXPECT_CALL(mock_leveldb_factory, DestroyLevelDB(_)).Times(Exactly(0));
146
147  busted_factory.SetOpenError(MakeIOError(
148      "some filename", "some message", leveldb_env::kNewLogger, ENOSPC));
149  scoped_refptr<IndexedDBBackingStore> backing_store =
150      IndexedDBBackingStore::Open(factory,
151                                  origin,
152                                  path,
153                                  request_context,
154                                  &data_loss,
155                                  &data_loss_reason,
156                                  &disk_full,
157                                  &mock_leveldb_factory,
158                                  task_runner,
159                                  clean_journal,
160                                  &s);
161  ASSERT_TRUE(s.IsIOError());
162
163  busted_factory.SetOpenError(MakeIOError("some filename",
164                                          "some message",
165                                          leveldb_env::kNewLogger,
166                                          base::File::FILE_ERROR_NO_MEMORY));
167  scoped_refptr<IndexedDBBackingStore> backing_store2 =
168      IndexedDBBackingStore::Open(factory,
169                                  origin,
170                                  path,
171                                  request_context,
172                                  &data_loss,
173                                  &data_loss_reason,
174                                  &disk_full,
175                                  &mock_leveldb_factory,
176                                  task_runner,
177                                  clean_journal,
178                                  &s);
179  ASSERT_TRUE(s.IsIOError());
180
181  busted_factory.SetOpenError(MakeIOError(
182      "some filename", "some message", leveldb_env::kNewLogger, EIO));
183  scoped_refptr<IndexedDBBackingStore> backing_store3 =
184      IndexedDBBackingStore::Open(factory,
185                                  origin,
186                                  path,
187                                  request_context,
188                                  &data_loss,
189                                  &data_loss_reason,
190                                  &disk_full,
191                                  &mock_leveldb_factory,
192                                  task_runner,
193                                  clean_journal,
194                                  &s);
195  ASSERT_TRUE(s.IsIOError());
196
197  busted_factory.SetOpenError(MakeIOError("some filename",
198                                          "some message",
199                                          leveldb_env::kNewLogger,
200                                          base::File::FILE_ERROR_FAILED));
201  scoped_refptr<IndexedDBBackingStore> backing_store4 =
202      IndexedDBBackingStore::Open(factory,
203                                  origin,
204                                  path,
205                                  request_context,
206                                  &data_loss,
207                                  &data_loss_reason,
208                                  &disk_full,
209                                  &mock_leveldb_factory,
210                                  task_runner,
211                                  clean_journal,
212                                  &s);
213  ASSERT_TRUE(s.IsIOError());
214}
215
216}  // namespace
217