1 | // Copyright (c) 2011 The LevelDB 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. See the AUTHORS file for names of contributors. |
4 | |
5 | #include "helpers/memenv/memenv.h" |
6 | |
7 | #include <string> |
8 | #include <vector> |
9 | |
10 | #include "gtest/gtest.h" |
11 | #include "db/db_impl.h" |
12 | #include "leveldb/db.h" |
13 | #include "leveldb/env.h" |
14 | #include "util/testutil.h" |
15 | |
16 | namespace leveldb { |
17 | |
18 | class MemEnvTest : public testing::Test { |
19 | public: |
20 | MemEnvTest() : env_(NewMemEnv(Env::Default())) {} |
21 | ~MemEnvTest() { delete env_; } |
22 | |
23 | Env* env_; |
24 | }; |
25 | |
26 | TEST_F(MemEnvTest, Basics) { |
27 | uint64_t file_size; |
28 | WritableFile* writable_file; |
29 | std::vector<std::string> children; |
30 | |
31 | ASSERT_LEVELDB_OK(env_->CreateDir("/dir" )); |
32 | |
33 | // Check that the directory is empty. |
34 | ASSERT_TRUE(!env_->FileExists("/dir/non_existent" )); |
35 | ASSERT_TRUE(!env_->GetFileSize("/dir/non_existent" , &file_size).ok()); |
36 | ASSERT_LEVELDB_OK(env_->GetChildren("/dir" , &children)); |
37 | ASSERT_EQ(0, children.size()); |
38 | |
39 | // Create a file. |
40 | ASSERT_LEVELDB_OK(env_->NewWritableFile("/dir/f" , &writable_file)); |
41 | ASSERT_LEVELDB_OK(env_->GetFileSize("/dir/f" , &file_size)); |
42 | ASSERT_EQ(0, file_size); |
43 | delete writable_file; |
44 | |
45 | // Check that the file exists. |
46 | ASSERT_TRUE(env_->FileExists("/dir/f" )); |
47 | ASSERT_LEVELDB_OK(env_->GetFileSize("/dir/f" , &file_size)); |
48 | ASSERT_EQ(0, file_size); |
49 | ASSERT_LEVELDB_OK(env_->GetChildren("/dir" , &children)); |
50 | ASSERT_EQ(1, children.size()); |
51 | ASSERT_EQ("f" , children[0]); |
52 | |
53 | // Write to the file. |
54 | ASSERT_LEVELDB_OK(env_->NewWritableFile("/dir/f" , &writable_file)); |
55 | ASSERT_LEVELDB_OK(writable_file->Append("abc" )); |
56 | delete writable_file; |
57 | |
58 | // Check that append works. |
59 | ASSERT_LEVELDB_OK(env_->NewAppendableFile("/dir/f" , &writable_file)); |
60 | ASSERT_LEVELDB_OK(env_->GetFileSize("/dir/f" , &file_size)); |
61 | ASSERT_EQ(3, file_size); |
62 | ASSERT_LEVELDB_OK(writable_file->Append("hello" )); |
63 | delete writable_file; |
64 | |
65 | // Check for expected size. |
66 | ASSERT_LEVELDB_OK(env_->GetFileSize("/dir/f" , &file_size)); |
67 | ASSERT_EQ(8, file_size); |
68 | |
69 | // Check that renaming works. |
70 | ASSERT_TRUE(!env_->RenameFile("/dir/non_existent" , "/dir/g" ).ok()); |
71 | ASSERT_LEVELDB_OK(env_->RenameFile("/dir/f" , "/dir/g" )); |
72 | ASSERT_TRUE(!env_->FileExists("/dir/f" )); |
73 | ASSERT_TRUE(env_->FileExists("/dir/g" )); |
74 | ASSERT_LEVELDB_OK(env_->GetFileSize("/dir/g" , &file_size)); |
75 | ASSERT_EQ(8, file_size); |
76 | |
77 | // Check that opening non-existent file fails. |
78 | SequentialFile* seq_file; |
79 | RandomAccessFile* rand_file; |
80 | ASSERT_TRUE(!env_->NewSequentialFile("/dir/non_existent" , &seq_file).ok()); |
81 | ASSERT_TRUE(!seq_file); |
82 | ASSERT_TRUE(!env_->NewRandomAccessFile("/dir/non_existent" , &rand_file).ok()); |
83 | ASSERT_TRUE(!rand_file); |
84 | |
85 | // Check that deleting works. |
86 | ASSERT_TRUE(!env_->RemoveFile("/dir/non_existent" ).ok()); |
87 | ASSERT_LEVELDB_OK(env_->RemoveFile("/dir/g" )); |
88 | ASSERT_TRUE(!env_->FileExists("/dir/g" )); |
89 | ASSERT_LEVELDB_OK(env_->GetChildren("/dir" , &children)); |
90 | ASSERT_EQ(0, children.size()); |
91 | ASSERT_LEVELDB_OK(env_->RemoveDir("/dir" )); |
92 | } |
93 | |
94 | TEST_F(MemEnvTest, ReadWrite) { |
95 | WritableFile* writable_file; |
96 | SequentialFile* seq_file; |
97 | RandomAccessFile* rand_file; |
98 | Slice result; |
99 | char scratch[100]; |
100 | |
101 | ASSERT_LEVELDB_OK(env_->CreateDir("/dir" )); |
102 | |
103 | ASSERT_LEVELDB_OK(env_->NewWritableFile("/dir/f" , &writable_file)); |
104 | ASSERT_LEVELDB_OK(writable_file->Append("hello " )); |
105 | ASSERT_LEVELDB_OK(writable_file->Append("world" )); |
106 | delete writable_file; |
107 | |
108 | // Read sequentially. |
109 | ASSERT_LEVELDB_OK(env_->NewSequentialFile("/dir/f" , &seq_file)); |
110 | ASSERT_LEVELDB_OK(seq_file->Read(5, &result, scratch)); // Read "hello". |
111 | ASSERT_EQ(0, result.compare("hello" )); |
112 | ASSERT_LEVELDB_OK(seq_file->Skip(1)); |
113 | ASSERT_LEVELDB_OK(seq_file->Read(1000, &result, scratch)); // Read "world". |
114 | ASSERT_EQ(0, result.compare("world" )); |
115 | ASSERT_LEVELDB_OK( |
116 | seq_file->Read(1000, &result, scratch)); // Try reading past EOF. |
117 | ASSERT_EQ(0, result.size()); |
118 | ASSERT_LEVELDB_OK(seq_file->Skip(100)); // Try to skip past end of file. |
119 | ASSERT_LEVELDB_OK(seq_file->Read(1000, &result, scratch)); |
120 | ASSERT_EQ(0, result.size()); |
121 | delete seq_file; |
122 | |
123 | // Random reads. |
124 | ASSERT_LEVELDB_OK(env_->NewRandomAccessFile("/dir/f" , &rand_file)); |
125 | ASSERT_LEVELDB_OK(rand_file->Read(6, 5, &result, scratch)); // Read "world". |
126 | ASSERT_EQ(0, result.compare("world" )); |
127 | ASSERT_LEVELDB_OK(rand_file->Read(0, 5, &result, scratch)); // Read "hello". |
128 | ASSERT_EQ(0, result.compare("hello" )); |
129 | ASSERT_LEVELDB_OK(rand_file->Read(10, 100, &result, scratch)); // Read "d". |
130 | ASSERT_EQ(0, result.compare("d" )); |
131 | |
132 | // Too high offset. |
133 | ASSERT_TRUE(!rand_file->Read(1000, 5, &result, scratch).ok()); |
134 | delete rand_file; |
135 | } |
136 | |
137 | TEST_F(MemEnvTest, Locks) { |
138 | FileLock* lock; |
139 | |
140 | // These are no-ops, but we test they return success. |
141 | ASSERT_LEVELDB_OK(env_->LockFile("some file" , &lock)); |
142 | ASSERT_LEVELDB_OK(env_->UnlockFile(lock)); |
143 | } |
144 | |
145 | TEST_F(MemEnvTest, Misc) { |
146 | std::string test_dir; |
147 | ASSERT_LEVELDB_OK(env_->GetTestDirectory(&test_dir)); |
148 | ASSERT_TRUE(!test_dir.empty()); |
149 | |
150 | WritableFile* writable_file; |
151 | ASSERT_LEVELDB_OK(env_->NewWritableFile("/a/b" , &writable_file)); |
152 | |
153 | // These are no-ops, but we test they return success. |
154 | ASSERT_LEVELDB_OK(writable_file->Sync()); |
155 | ASSERT_LEVELDB_OK(writable_file->Flush()); |
156 | ASSERT_LEVELDB_OK(writable_file->Close()); |
157 | delete writable_file; |
158 | } |
159 | |
160 | TEST_F(MemEnvTest, LargeWrite) { |
161 | const size_t kWriteSize = 300 * 1024; |
162 | char* scratch = new char[kWriteSize * 2]; |
163 | |
164 | std::string write_data; |
165 | for (size_t i = 0; i < kWriteSize; ++i) { |
166 | write_data.append(1, static_cast<char>(i)); |
167 | } |
168 | |
169 | WritableFile* writable_file; |
170 | ASSERT_LEVELDB_OK(env_->NewWritableFile("/dir/f" , &writable_file)); |
171 | ASSERT_LEVELDB_OK(writable_file->Append("foo" )); |
172 | ASSERT_LEVELDB_OK(writable_file->Append(write_data)); |
173 | delete writable_file; |
174 | |
175 | SequentialFile* seq_file; |
176 | Slice result; |
177 | ASSERT_LEVELDB_OK(env_->NewSequentialFile("/dir/f" , &seq_file)); |
178 | ASSERT_LEVELDB_OK(seq_file->Read(3, &result, scratch)); // Read "foo". |
179 | ASSERT_EQ(0, result.compare("foo" )); |
180 | |
181 | size_t read = 0; |
182 | std::string read_data; |
183 | while (read < kWriteSize) { |
184 | ASSERT_LEVELDB_OK(seq_file->Read(kWriteSize - read, &result, scratch)); |
185 | read_data.append(result.data(), result.size()); |
186 | read += result.size(); |
187 | } |
188 | ASSERT_TRUE(write_data == read_data); |
189 | delete seq_file; |
190 | delete[] scratch; |
191 | } |
192 | |
193 | TEST_F(MemEnvTest, OverwriteOpenFile) { |
194 | const char kWrite1Data[] = "Write #1 data" ; |
195 | const size_t kFileDataLen = sizeof(kWrite1Data) - 1; |
196 | const std::string kTestFileName = testing::TempDir() + "leveldb-TestFile.dat" ; |
197 | |
198 | ASSERT_LEVELDB_OK(WriteStringToFile(env_, kWrite1Data, kTestFileName)); |
199 | |
200 | RandomAccessFile* rand_file; |
201 | ASSERT_LEVELDB_OK(env_->NewRandomAccessFile(kTestFileName, &rand_file)); |
202 | |
203 | const char kWrite2Data[] = "Write #2 data" ; |
204 | ASSERT_LEVELDB_OK(WriteStringToFile(env_, kWrite2Data, kTestFileName)); |
205 | |
206 | // Verify that overwriting an open file will result in the new file data |
207 | // being read from files opened before the write. |
208 | Slice result; |
209 | char scratch[kFileDataLen]; |
210 | ASSERT_LEVELDB_OK(rand_file->Read(0, kFileDataLen, &result, scratch)); |
211 | ASSERT_EQ(0, result.compare(kWrite2Data)); |
212 | |
213 | delete rand_file; |
214 | } |
215 | |
216 | TEST_F(MemEnvTest, DBTest) { |
217 | Options options; |
218 | options.create_if_missing = true; |
219 | options.env = env_; |
220 | DB* db; |
221 | |
222 | const Slice keys[] = {Slice("aaa" ), Slice("bbb" ), Slice("ccc" )}; |
223 | const Slice vals[] = {Slice("foo" ), Slice("bar" ), Slice("baz" )}; |
224 | |
225 | ASSERT_LEVELDB_OK(DB::Open(options, "/dir/db" , &db)); |
226 | for (size_t i = 0; i < 3; ++i) { |
227 | ASSERT_LEVELDB_OK(db->Put(WriteOptions(), keys[i], vals[i])); |
228 | } |
229 | |
230 | for (size_t i = 0; i < 3; ++i) { |
231 | std::string res; |
232 | ASSERT_LEVELDB_OK(db->Get(ReadOptions(), keys[i], &res)); |
233 | ASSERT_TRUE(res == vals[i]); |
234 | } |
235 | |
236 | Iterator* iterator = db->NewIterator(ReadOptions()); |
237 | iterator->SeekToFirst(); |
238 | for (size_t i = 0; i < 3; ++i) { |
239 | ASSERT_TRUE(iterator->Valid()); |
240 | ASSERT_TRUE(keys[i] == iterator->key()); |
241 | ASSERT_TRUE(vals[i] == iterator->value()); |
242 | iterator->Next(); |
243 | } |
244 | ASSERT_TRUE(!iterator->Valid()); |
245 | delete iterator; |
246 | |
247 | DBImpl* dbi = reinterpret_cast<DBImpl*>(db); |
248 | ASSERT_LEVELDB_OK(dbi->TEST_CompactMemTable()); |
249 | |
250 | for (size_t i = 0; i < 3; ++i) { |
251 | std::string res; |
252 | ASSERT_LEVELDB_OK(db->Get(ReadOptions(), keys[i], &res)); |
253 | ASSERT_TRUE(res == vals[i]); |
254 | } |
255 | |
256 | delete db; |
257 | } |
258 | |
259 | } // namespace leveldb |
260 | |