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 "leveldb/env.h"
6
7 #include <algorithm>
8
9 #include "gtest/gtest.h"
10 #include "port/port.h"
11 #include "port/thread_annotations.h"
12 #include "util/mutexlock.h"
13 #include "util/testutil.h"
14
15 namespace leveldb {
16
17 static const int kDelayMicros = 100000;
18
19 class EnvTest : public testing::Test {
20 public:
EnvTest()21 EnvTest() : env_(Env::Default()) {}
22
23 Env* env_;
24 };
25
TEST_F(EnvTest,ReadWrite)26 TEST_F(EnvTest, ReadWrite) {
27 Random rnd(test::RandomSeed());
28
29 // Get file to use for testing.
30 std::string test_dir;
31 ASSERT_LEVELDB_OK(env_->GetTestDirectory(&test_dir));
32 std::string test_file_name = test_dir + "/open_on_read.txt";
33 WritableFile* writable_file;
34 ASSERT_LEVELDB_OK(env_->NewWritableFile(test_file_name, &writable_file));
35
36 // Fill a file with data generated via a sequence of randomly sized writes.
37 static const size_t kDataSize = 10 * 1048576;
38 std::string data;
39 while (data.size() < kDataSize) {
40 int len = rnd.Skewed(18); // Up to 2^18 - 1, but typically much smaller
41 std::string r;
42 test::RandomString(&rnd, len, &r);
43 ASSERT_LEVELDB_OK(writable_file->Append(r));
44 data += r;
45 if (rnd.OneIn(10)) {
46 ASSERT_LEVELDB_OK(writable_file->Flush());
47 }
48 }
49 ASSERT_LEVELDB_OK(writable_file->Sync());
50 ASSERT_LEVELDB_OK(writable_file->Close());
51 delete writable_file;
52
53 // Read all data using a sequence of randomly sized reads.
54 SequentialFile* sequential_file;
55 ASSERT_LEVELDB_OK(env_->NewSequentialFile(test_file_name, &sequential_file));
56 std::string read_result;
57 std::string scratch;
58 while (read_result.size() < data.size()) {
59 int len = std::min<int>(rnd.Skewed(18), data.size() - read_result.size());
60 scratch.resize(std::max(len, 1)); // at least 1 so &scratch[0] is legal
61 Slice read;
62 ASSERT_LEVELDB_OK(sequential_file->Read(len, &read, &scratch[0]));
63 if (len > 0) {
64 ASSERT_GT(read.size(), 0);
65 }
66 ASSERT_LE(read.size(), len);
67 read_result.append(read.data(), read.size());
68 }
69 ASSERT_EQ(read_result, data);
70 delete sequential_file;
71 }
72
TEST_F(EnvTest,RunImmediately)73 TEST_F(EnvTest, RunImmediately) {
74 struct RunState {
75 port::Mutex mu;
76 port::CondVar cvar{&mu};
77 bool called = false;
78
79 static void Run(void* arg) {
80 RunState* state = reinterpret_cast<RunState*>(arg);
81 MutexLock l(&state->mu);
82 ASSERT_EQ(state->called, false);
83 state->called = true;
84 state->cvar.Signal();
85 }
86 };
87
88 RunState state;
89 env_->Schedule(&RunState::Run, &state);
90
91 MutexLock l(&state.mu);
92 while (!state.called) {
93 state.cvar.Wait();
94 }
95 }
96
TEST_F(EnvTest,RunMany)97 TEST_F(EnvTest, RunMany) {
98 struct RunState {
99 port::Mutex mu;
100 port::CondVar cvar{&mu};
101 int last_id = 0;
102 };
103
104 struct Callback {
105 RunState* state_; // Pointer to shared state.
106 const int id_; // Order# for the execution of this callback.
107
108 Callback(RunState* s, int id) : state_(s), id_(id) {}
109
110 static void Run(void* arg) {
111 Callback* callback = reinterpret_cast<Callback*>(arg);
112 RunState* state = callback->state_;
113
114 MutexLock l(&state->mu);
115 ASSERT_EQ(state->last_id, callback->id_ - 1);
116 state->last_id = callback->id_;
117 state->cvar.Signal();
118 }
119 };
120
121 RunState state;
122 Callback callback1(&state, 1);
123 Callback callback2(&state, 2);
124 Callback callback3(&state, 3);
125 Callback callback4(&state, 4);
126 env_->Schedule(&Callback::Run, &callback1);
127 env_->Schedule(&Callback::Run, &callback2);
128 env_->Schedule(&Callback::Run, &callback3);
129 env_->Schedule(&Callback::Run, &callback4);
130
131 MutexLock l(&state.mu);
132 while (state.last_id != 4) {
133 state.cvar.Wait();
134 }
135 }
136
137 struct State {
138 port::Mutex mu;
139 port::CondVar cvar{&mu};
140
141 int val GUARDED_BY(mu);
142 int num_running GUARDED_BY(mu);
143
Stateleveldb::State144 State(int val, int num_running) : val(val), num_running(num_running) {}
145 };
146
ThreadBody(void * arg)147 static void ThreadBody(void* arg) {
148 State* s = reinterpret_cast<State*>(arg);
149 s->mu.Lock();
150 s->val += 1;
151 s->num_running -= 1;
152 s->cvar.Signal();
153 s->mu.Unlock();
154 }
155
TEST_F(EnvTest,StartThread)156 TEST_F(EnvTest, StartThread) {
157 State state(0, 3);
158 for (int i = 0; i < 3; i++) {
159 env_->StartThread(&ThreadBody, &state);
160 }
161
162 MutexLock l(&state.mu);
163 while (state.num_running != 0) {
164 state.cvar.Wait();
165 }
166 ASSERT_EQ(state.val, 3);
167 }
168
TEST_F(EnvTest,TestOpenNonExistentFile)169 TEST_F(EnvTest, TestOpenNonExistentFile) {
170 // Write some test data to a single file that will be opened |n| times.
171 std::string test_dir;
172 ASSERT_LEVELDB_OK(env_->GetTestDirectory(&test_dir));
173
174 std::string non_existent_file = test_dir + "/non_existent_file";
175 ASSERT_TRUE(!env_->FileExists(non_existent_file));
176
177 RandomAccessFile* random_access_file;
178 Status status =
179 env_->NewRandomAccessFile(non_existent_file, &random_access_file);
180 ASSERT_TRUE(status.IsNotFound());
181
182 SequentialFile* sequential_file;
183 status = env_->NewSequentialFile(non_existent_file, &sequential_file);
184 ASSERT_TRUE(status.IsNotFound());
185 }
186
TEST_F(EnvTest,ReopenWritableFile)187 TEST_F(EnvTest, ReopenWritableFile) {
188 std::string test_dir;
189 ASSERT_LEVELDB_OK(env_->GetTestDirectory(&test_dir));
190 std::string test_file_name = test_dir + "/reopen_writable_file.txt";
191 env_->RemoveFile(test_file_name);
192
193 WritableFile* writable_file;
194 ASSERT_LEVELDB_OK(env_->NewWritableFile(test_file_name, &writable_file));
195 std::string data("hello world!");
196 ASSERT_LEVELDB_OK(writable_file->Append(data));
197 ASSERT_LEVELDB_OK(writable_file->Close());
198 delete writable_file;
199
200 ASSERT_LEVELDB_OK(env_->NewWritableFile(test_file_name, &writable_file));
201 data = "42";
202 ASSERT_LEVELDB_OK(writable_file->Append(data));
203 ASSERT_LEVELDB_OK(writable_file->Close());
204 delete writable_file;
205
206 ASSERT_LEVELDB_OK(ReadFileToString(env_, test_file_name, &data));
207 ASSERT_EQ(std::string("42"), data);
208 env_->RemoveFile(test_file_name);
209 }
210
TEST_F(EnvTest,ReopenAppendableFile)211 TEST_F(EnvTest, ReopenAppendableFile) {
212 std::string test_dir;
213 ASSERT_LEVELDB_OK(env_->GetTestDirectory(&test_dir));
214 std::string test_file_name = test_dir + "/reopen_appendable_file.txt";
215 env_->RemoveFile(test_file_name);
216
217 WritableFile* appendable_file;
218 ASSERT_LEVELDB_OK(env_->NewAppendableFile(test_file_name, &appendable_file));
219 std::string data("hello world!");
220 ASSERT_LEVELDB_OK(appendable_file->Append(data));
221 ASSERT_LEVELDB_OK(appendable_file->Close());
222 delete appendable_file;
223
224 ASSERT_LEVELDB_OK(env_->NewAppendableFile(test_file_name, &appendable_file));
225 data = "42";
226 ASSERT_LEVELDB_OK(appendable_file->Append(data));
227 ASSERT_LEVELDB_OK(appendable_file->Close());
228 delete appendable_file;
229
230 ASSERT_LEVELDB_OK(ReadFileToString(env_, test_file_name, &data));
231 ASSERT_EQ(std::string("hello world!42"), data);
232 env_->RemoveFile(test_file_name);
233 }
234
235 } // namespace leveldb
236
237