/Users/deen/code/yugabyte-db/src/yb/util/memenv/memenv-test.cc
Line | Count | Source (jump to first uncovered line) |
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 | | // The following only applies to changes made to this file as part of YugaByte development. |
6 | | // |
7 | | // Portions Copyright (c) YugaByte, Inc. |
8 | | // |
9 | | // Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file except |
10 | | // in compliance with the License. You may obtain a copy of the License at |
11 | | // |
12 | | // http://www.apache.org/licenses/LICENSE-2.0 |
13 | | // |
14 | | // Unless required by applicable law or agreed to in writing, software distributed under the License |
15 | | // is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express |
16 | | // or implied. See the License for the specific language governing permissions and limitations |
17 | | // under the License. |
18 | | // |
19 | | // Modified for yb: |
20 | | // - use gtest |
21 | | |
22 | | #include <memory> |
23 | | #include <unordered_set> |
24 | | #include <vector> |
25 | | |
26 | | #include <glog/logging.h> |
27 | | #include <gtest/gtest.h> |
28 | | |
29 | | #include "yb/gutil/map-util.h" |
30 | | |
31 | | #include "yb/util/env.h" |
32 | | #include "yb/util/env_util.h" |
33 | | #include "yb/util/memenv/memenv.h" |
34 | | #include "yb/util/result.h" |
35 | | #include "yb/util/test_macros.h" |
36 | | |
37 | | using std::shared_ptr; |
38 | | using std::string; |
39 | | using std::unordered_set; |
40 | | using std::vector; |
41 | | |
42 | | namespace yb { |
43 | | |
44 | | class MemEnvTest : public ::testing::Test { |
45 | | public: |
46 | | Env* env_; |
47 | | |
48 | | MemEnvTest() |
49 | 9 | : env_(NewMemEnv(Env::Default())) { |
50 | 9 | } |
51 | 9 | ~MemEnvTest() { |
52 | 9 | delete env_; |
53 | 9 | } |
54 | | }; |
55 | | |
56 | 1 | TEST_F(MemEnvTest, Basics) { |
57 | 1 | uint64_t file_size; |
58 | 1 | std::unique_ptr<WritableFile> writable_file; |
59 | 1 | vector<string> children; |
60 | | |
61 | | // Create the directory. |
62 | 1 | ASSERT_FALSE(env_->FileExists("/dir")); |
63 | 1 | ASSERT_OK(env_->CreateDir("/dir")); |
64 | 1 | ASSERT_TRUE(env_->FileExists("/dir")); |
65 | | |
66 | | // Check that the directory is empty. |
67 | 1 | ASSERT_TRUE(!env_->FileExists("/dir/non_existent")); |
68 | 1 | ASSERT_FALSE(env_->GetFileSize("/dir/non_existent").ok()); |
69 | 1 | ASSERT_OK(env_->GetChildren("/dir", &children)); |
70 | 1 | ASSERT_EQ(0, children.size()); |
71 | | |
72 | | // Create a file. |
73 | 1 | ASSERT_OK(env_->NewWritableFile("/dir/f", &writable_file)); |
74 | 1 | writable_file.reset(); |
75 | | |
76 | | // Check that the file exists. |
77 | 1 | ASSERT_TRUE(env_->FileExists("/dir/f")); |
78 | 1 | file_size = ASSERT_RESULT(env_->GetFileSize("/dir/f")); |
79 | 1 | ASSERT_EQ(0, file_size); |
80 | 1 | ASSERT_OK(env_->GetChildren("/dir", &children)); |
81 | 1 | ASSERT_EQ(1, children.size()); |
82 | 1 | ASSERT_EQ("f", children[0]); |
83 | | |
84 | | // Write to the file. |
85 | 1 | ASSERT_OK(env_->NewWritableFile("/dir/f", &writable_file)); |
86 | 1 | ASSERT_OK(writable_file->Append("abc")); |
87 | 1 | writable_file.reset(); |
88 | | |
89 | | // Check for expected size. |
90 | 1 | file_size = ASSERT_RESULT(env_->GetFileSize("/dir/f")); |
91 | 1 | ASSERT_EQ(3, file_size); |
92 | | |
93 | | // Check that renaming works. |
94 | 1 | ASSERT_TRUE(!env_->RenameFile("/dir/non_existent", "/dir/g").ok()); |
95 | 1 | ASSERT_OK(env_->RenameFile("/dir/f", "/dir/g")); |
96 | 1 | ASSERT_TRUE(!env_->FileExists("/dir/f")); |
97 | 1 | ASSERT_TRUE(env_->FileExists("/dir/g")); |
98 | 1 | file_size = ASSERT_RESULT(env_->GetFileSize("/dir/g")); |
99 | 1 | ASSERT_EQ(3, file_size); |
100 | | |
101 | | // Check that opening non-existent file fails. |
102 | 1 | std::unique_ptr<SequentialFile> seq_file; |
103 | 1 | std::unique_ptr<RandomAccessFile> rand_file; |
104 | 1 | ASSERT_TRUE(!env_->NewSequentialFile("/dir/non_existent", &seq_file).ok()); |
105 | 1 | ASSERT_TRUE(!seq_file); |
106 | 1 | ASSERT_TRUE(!env_->NewRandomAccessFile("/dir/non_existent", &rand_file).ok()); |
107 | 1 | ASSERT_TRUE(!rand_file); |
108 | | |
109 | | // Check that deleting works. |
110 | 1 | ASSERT_TRUE(!env_->DeleteFile("/dir/non_existent").ok()); |
111 | 1 | ASSERT_OK(env_->DeleteFile("/dir/g")); |
112 | 1 | ASSERT_TRUE(!env_->FileExists("/dir/g")); |
113 | 1 | ASSERT_OK(env_->GetChildren("/dir", &children)); |
114 | 1 | ASSERT_EQ(0, children.size()); |
115 | 1 | ASSERT_OK(env_->DeleteDir("/dir")); |
116 | 1 | ASSERT_FALSE(env_->FileExists("/dir")); |
117 | 1 | } |
118 | | |
119 | 1 | TEST_F(MemEnvTest, ReadWrite) { |
120 | 1 | Slice result; |
121 | 1 | uint8_t scratch[100]; |
122 | | |
123 | 1 | ASSERT_OK(env_->CreateDir("/dir")); |
124 | | |
125 | 1 | { |
126 | 1 | std::unique_ptr<WritableFile> writable_file; |
127 | 1 | ASSERT_OK(env_->NewWritableFile("/dir/f", &writable_file)); |
128 | 1 | ASSERT_OK(writable_file->Append("hello ")); |
129 | 1 | ASSERT_OK(writable_file->Append("world")); |
130 | 1 | } |
131 | | |
132 | 1 | { |
133 | | // Read sequentially. |
134 | 1 | std::unique_ptr<SequentialFile> seq_file; |
135 | 1 | ASSERT_OK(env_->NewSequentialFile("/dir/f", &seq_file)); |
136 | 1 | ASSERT_OK(seq_file->Read(5, &result, scratch)); // Read "hello". |
137 | 1 | ASSERT_EQ(0, result.compare("hello")); |
138 | 1 | ASSERT_OK(seq_file->Skip(1)); |
139 | 1 | ASSERT_OK(seq_file->Read(1000, &result, scratch)); // Read "world". |
140 | 1 | ASSERT_EQ(0, result.compare("world")); |
141 | 1 | ASSERT_OK(seq_file->Read(1000, &result, scratch)); // Try reading past EOF. |
142 | 1 | ASSERT_EQ(0, result.size()); |
143 | 1 | ASSERT_OK(seq_file->Skip(100)); // Try to skip past end of file. |
144 | 1 | ASSERT_OK(seq_file->Read(1000, &result, scratch)); |
145 | 1 | ASSERT_EQ(0, result.size()); |
146 | 1 | } |
147 | | |
148 | 1 | { |
149 | | // Random reads. |
150 | 1 | std::unique_ptr<RandomAccessFile> rand_file; |
151 | 1 | ASSERT_OK(env_->NewRandomAccessFile("/dir/f", &rand_file)); |
152 | 1 | ASSERT_OK(rand_file->Read(6, 5, &result, scratch)); // Read "world". |
153 | 1 | ASSERT_EQ(0, result.compare("world")); |
154 | 1 | ASSERT_OK(rand_file->Read(0, 5, &result, scratch)); // Read "hello". |
155 | 1 | ASSERT_EQ(0, result.compare("hello")); |
156 | 1 | ASSERT_OK(rand_file->Read(10, 100, &result, scratch)); // Read "d". |
157 | 1 | ASSERT_EQ(0, result.compare("d")); |
158 | | |
159 | | // Too high offset. |
160 | 1 | ASSERT_TRUE(!rand_file->Read(1000, 5, &result, scratch).ok()); |
161 | 1 | } |
162 | 1 | } |
163 | | |
164 | 1 | TEST_F(MemEnvTest, Locks) { |
165 | 1 | FileLock* lock; |
166 | | |
167 | | // These are no-ops, but we test they return success. |
168 | 1 | ASSERT_OK(env_->LockFile("some file", &lock, false /* recursive_lock_ok */)); |
169 | 1 | ASSERT_OK(env_->UnlockFile(lock)); |
170 | 1 | } |
171 | | |
172 | 1 | TEST_F(MemEnvTest, Misc) { |
173 | 1 | string test_dir; |
174 | 1 | ASSERT_OK(env_->GetTestDirectory(&test_dir)); |
175 | 1 | ASSERT_TRUE(!test_dir.empty()); |
176 | | |
177 | 1 | std::unique_ptr<WritableFile> writable_file; |
178 | 1 | ASSERT_OK(env_->NewWritableFile("/a/b", &writable_file)); |
179 | | |
180 | | // These are no-ops, but we test they return success. |
181 | 1 | ASSERT_OK(writable_file->Sync()); |
182 | 1 | ASSERT_OK(writable_file->Flush(WritableFile::FLUSH_SYNC)); |
183 | 1 | ASSERT_OK(writable_file->Flush(WritableFile::FLUSH_ASYNC)); |
184 | 1 | ASSERT_OK(writable_file->Close()); |
185 | 1 | } |
186 | | |
187 | 1 | TEST_F(MemEnvTest, LargeWrite) { |
188 | 1 | const size_t kWriteSize = 300 * 1024; |
189 | 1 | std::unique_ptr<uint8_t[]> scratch(new uint8_t[kWriteSize * 2]); |
190 | | |
191 | 1 | string write_data; |
192 | 307k | for (size_t i = 0; i < kWriteSize; ++i) { |
193 | 307k | write_data.append(1, static_cast<char>(i)); |
194 | 307k | } |
195 | | |
196 | 1 | std::unique_ptr<WritableFile> writable_file; |
197 | 1 | ASSERT_OK(env_->NewWritableFile("/dir/f", &writable_file)); |
198 | 1 | ASSERT_OK(writable_file->Append("foo")); |
199 | 1 | ASSERT_OK(writable_file->Append(write_data)); |
200 | 1 | writable_file.reset(); |
201 | | |
202 | 1 | std::unique_ptr<SequentialFile> seq_file; |
203 | 1 | Slice result; |
204 | 1 | ASSERT_OK(env_->NewSequentialFile("/dir/f", &seq_file)); |
205 | 1 | ASSERT_OK(seq_file->Read(3, &result, scratch.get())); // Read "foo". |
206 | 1 | ASSERT_EQ(0, result.compare("foo")); |
207 | | |
208 | 1 | size_t read = 0; |
209 | 1 | string read_data; |
210 | 2 | while (read < kWriteSize) { |
211 | 1 | ASSERT_OK(seq_file->Read(kWriteSize - read, &result, scratch.get())); |
212 | 1 | read_data.append(reinterpret_cast<const char *>(result.data()), |
213 | 1 | result.size()); |
214 | 1 | read += result.size(); |
215 | 1 | } |
216 | 1 | ASSERT_TRUE(write_data == read_data); |
217 | 1 | } |
218 | | |
219 | 1 | TEST_F(MemEnvTest, Overwrite) { |
220 | | // File does not exist, create it. |
221 | 1 | shared_ptr<WritableFile> writer; |
222 | 1 | ASSERT_OK(env_util::OpenFileForWrite(env_, "some file", &writer)); |
223 | | |
224 | | // File exists, overwrite it. |
225 | 1 | ASSERT_OK(env_util::OpenFileForWrite(env_, "some file", &writer)); |
226 | | |
227 | | // File exists, try to overwrite (and fail). |
228 | 1 | WritableFileOptions opts; |
229 | 1 | opts.mode = Env::CREATE_NON_EXISTING; |
230 | 1 | Status s = env_util::OpenFileForWrite(opts, |
231 | 1 | env_, "some file", &writer); |
232 | 1 | ASSERT_TRUE(s.IsAlreadyPresent()); |
233 | 1 | } |
234 | | |
235 | 1 | TEST_F(MemEnvTest, Reopen) { |
236 | 1 | string first = "The quick brown fox"; |
237 | 1 | string second = "jumps over the lazy dog"; |
238 | | |
239 | | // Create the file and write to it. |
240 | 1 | shared_ptr<WritableFile> writer; |
241 | 1 | ASSERT_OK(env_util::OpenFileForWrite(env_, "some file", &writer)); |
242 | 1 | ASSERT_OK(writer->Append(first)); |
243 | 1 | ASSERT_EQ(first.length(), writer->Size()); |
244 | 1 | ASSERT_OK(writer->Close()); |
245 | | |
246 | | // Reopen it and append to it. |
247 | 1 | WritableFileOptions reopen_opts; |
248 | 1 | reopen_opts.mode = Env::OPEN_EXISTING; |
249 | 1 | ASSERT_OK(env_util::OpenFileForWrite(reopen_opts, |
250 | 1 | env_, "some file", &writer)); |
251 | 1 | ASSERT_EQ(first.length(), writer->Size()); |
252 | 1 | ASSERT_OK(writer->Append(second)); |
253 | 1 | ASSERT_EQ(first.length() + second.length(), writer->Size()); |
254 | 1 | ASSERT_OK(writer->Close()); |
255 | | |
256 | | // Check that the file has both strings. |
257 | 1 | shared_ptr<RandomAccessFile> reader; |
258 | 1 | ASSERT_OK(env_util::OpenFileForRandom(env_, "some file", &reader)); |
259 | 1 | uint64_t size = ASSERT_RESULT(reader->Size()); |
260 | 1 | ASSERT_EQ(first.length() + second.length(), size); |
261 | 1 | Slice s; |
262 | 1 | std::vector<uint8_t> scratch(size); |
263 | 1 | ASSERT_OK(env_util::ReadFully(reader.get(), 0, size, &s, scratch.data())); |
264 | 1 | ASSERT_EQ(first + second, s.ToString()); |
265 | 1 | } |
266 | | |
267 | 1 | TEST_F(MemEnvTest, TempFile) { |
268 | 1 | string tmpl = "foo.XXXXXX"; |
269 | 1 | string bad_tmpl = "foo.YYY"; |
270 | | |
271 | 1 | string path; |
272 | 1 | std::unique_ptr<WritableFile> file; |
273 | | |
274 | | // Ensure we don't accept a bad template. |
275 | 1 | Status s = env_->NewTempWritableFile(WritableFileOptions(), bad_tmpl, &path, &file); |
276 | 2 | ASSERT_TRUE(s.IsInvalidArgument()) << "Should not accept bad template: " << s.ToString(); |
277 | 1 | ASSERT_STR_CONTAINS(s.ToString(), "must end with the string XXXXXX"); |
278 | | |
279 | | // Create multiple temp files, ensure no collisions. |
280 | 1 | unordered_set<string> paths; |
281 | 11 | for (int i = 0; i < 10; i++) { |
282 | 10 | ASSERT_OK(env_->NewTempWritableFile(WritableFileOptions(), tmpl, &path, &file)); |
283 | 0 | VLOG(1) << "Created temporary file at path " << path; |
284 | 20 | ASSERT_EQ(path.length(), tmpl.length()) << "Template and final path should have same length"; |
285 | 20 | ASSERT_NE(path, tmpl) << "Template and final path should differ"; |
286 | 10 | ASSERT_OK(file->Append("Hello, tempfile.\n")); |
287 | 10 | ASSERT_OK(file->Close()); |
288 | 20 | ASSERT_FALSE(ContainsKey(paths, path)) << "Created " << path << " twice!"; |
289 | 10 | InsertOrDie(&paths, path); // Will crash if we have a duplicate. |
290 | 10 | } |
291 | | |
292 | | // Delete the files we created. |
293 | 10 | for (const string& p : paths) { |
294 | 10 | ASSERT_OK(env_->DeleteFile(p)); |
295 | 10 | } |
296 | 1 | } |
297 | | |
298 | 1 | TEST_F(MemEnvTest, TestRWFile) { |
299 | | // Create the file. |
300 | 1 | std::unique_ptr<RWFile> file; |
301 | 1 | ASSERT_OK(env_->NewRWFile("foo", &file)); |
302 | | |
303 | | // Append to it. |
304 | 1 | string kTestData = "abcdefghijklmno"; |
305 | 1 | ASSERT_OK(file->Write(0, kTestData)); |
306 | | |
307 | | // Read from it. |
308 | 1 | Slice result; |
309 | 1 | std::unique_ptr<uint8_t[]> scratch(new uint8_t[kTestData.length()]); |
310 | 1 | ASSERT_OK(file->Read(0, kTestData.length(), &result, scratch.get())); |
311 | 1 | ASSERT_EQ(result, kTestData); |
312 | | |
313 | | // Try to rewrite; it shouldn't work. |
314 | 1 | ASSERT_TRUE(file->Write(0, kTestData).IsNotSupported()); |
315 | | |
316 | | // Make sure we can't overwrite it. |
317 | 1 | RWFileOptions opts; |
318 | 1 | opts.mode = Env::CREATE_NON_EXISTING; |
319 | 1 | ASSERT_TRUE(env_->NewRWFile(opts, "foo", &file).IsAlreadyPresent()); |
320 | | |
321 | | // Reopen it without truncating the existing data. |
322 | 1 | opts.mode = Env::OPEN_EXISTING; |
323 | 1 | ASSERT_OK(env_->NewRWFile(opts, "foo", &file)); |
324 | 1 | ASSERT_OK(file->Read(0, kTestData.length(), &result, scratch.get())); |
325 | 1 | ASSERT_EQ(result, kTestData); |
326 | 1 | } |
327 | | |
328 | | } // namespace yb |