]>
Commit | Line | Data |
---|---|---|
7c673cae | 1 | // Copyright (c) 2011-present, Facebook, Inc. All rights reserved. |
11fdf7f2 TL |
2 | // This source code is licensed under both the GPLv2 (found in the |
3 | // COPYING file in the root directory) and Apache 2.0 License | |
4 | // (found in the LICENSE.Apache file in the root directory). | |
7c673cae FG |
5 | /** |
6 | * A test harness for the Redis API built on rocksdb. | |
7 | * | |
8 | * USAGE: Build with: "make redis_test" (in rocksdb directory). | |
9 | * Run unit tests with: "./redis_test" | |
10 | * Manual/Interactive user testing: "./redis_test -m" | |
11 | * Manual user testing + restart database: "./redis_test -m -d" | |
12 | * | |
13 | * TODO: Add LARGE random test cases to verify efficiency and scalability | |
14 | * | |
15 | * @author Deon Nicholas (dnicholas@fb.com) | |
16 | */ | |
17 | ||
18 | #ifndef ROCKSDB_LITE | |
19 | ||
20 | #include <iostream> | |
21 | #include <cctype> | |
22 | ||
23 | #include "redis_lists.h" | |
24 | #include "util/testharness.h" | |
25 | #include "util/random.h" | |
26 | ||
27 | using namespace rocksdb; | |
28 | ||
29 | namespace rocksdb { | |
30 | ||
31 | class RedisListsTest : public testing::Test { | |
32 | public: | |
33 | static const std::string kDefaultDbName; | |
34 | static Options options; | |
35 | ||
36 | RedisListsTest() { | |
37 | options.create_if_missing = true; | |
38 | } | |
39 | }; | |
40 | ||
41 | const std::string RedisListsTest::kDefaultDbName = | |
11fdf7f2 | 42 | test::PerThreadDBPath("redis_lists_test"); |
7c673cae FG |
43 | Options RedisListsTest::options = Options(); |
44 | ||
45 | // operator== and operator<< are defined below for vectors (lists) | |
46 | // Needed for ASSERT_EQ | |
47 | ||
48 | namespace { | |
49 | void AssertListEq(const std::vector<std::string>& result, | |
50 | const std::vector<std::string>& expected_result) { | |
51 | ASSERT_EQ(result.size(), expected_result.size()); | |
52 | for (size_t i = 0; i < result.size(); ++i) { | |
53 | ASSERT_EQ(result[i], expected_result[i]); | |
54 | } | |
55 | } | |
56 | } // namespace | |
57 | ||
58 | // PushRight, Length, Index, Range | |
59 | TEST_F(RedisListsTest, SimpleTest) { | |
60 | RedisLists redis(kDefaultDbName, options, true); // Destructive | |
61 | ||
62 | std::string tempv; // Used below for all Index(), PopRight(), PopLeft() | |
63 | ||
64 | // Simple PushRight (should return the new length each time) | |
65 | ASSERT_EQ(redis.PushRight("k1", "v1"), 1); | |
66 | ASSERT_EQ(redis.PushRight("k1", "v2"), 2); | |
67 | ASSERT_EQ(redis.PushRight("k1", "v3"), 3); | |
68 | ||
69 | // Check Length and Index() functions | |
70 | ASSERT_EQ(redis.Length("k1"), 3); // Check length | |
71 | ASSERT_TRUE(redis.Index("k1", 0, &tempv)); | |
72 | ASSERT_EQ(tempv, "v1"); // Check valid indices | |
73 | ASSERT_TRUE(redis.Index("k1", 1, &tempv)); | |
74 | ASSERT_EQ(tempv, "v2"); | |
75 | ASSERT_TRUE(redis.Index("k1", 2, &tempv)); | |
76 | ASSERT_EQ(tempv, "v3"); | |
77 | ||
78 | // Check range function and vectors | |
79 | std::vector<std::string> result = redis.Range("k1", 0, 2); // Get the list | |
80 | std::vector<std::string> expected_result(3); | |
81 | expected_result[0] = "v1"; | |
82 | expected_result[1] = "v2"; | |
83 | expected_result[2] = "v3"; | |
84 | AssertListEq(result, expected_result); | |
85 | } | |
86 | ||
87 | // PushLeft, Length, Index, Range | |
88 | TEST_F(RedisListsTest, SimpleTest2) { | |
89 | RedisLists redis(kDefaultDbName, options, true); // Destructive | |
90 | ||
91 | std::string tempv; // Used below for all Index(), PopRight(), PopLeft() | |
92 | ||
93 | // Simple PushRight | |
94 | ASSERT_EQ(redis.PushLeft("k1", "v3"), 1); | |
95 | ASSERT_EQ(redis.PushLeft("k1", "v2"), 2); | |
96 | ASSERT_EQ(redis.PushLeft("k1", "v1"), 3); | |
97 | ||
98 | // Check Length and Index() functions | |
99 | ASSERT_EQ(redis.Length("k1"), 3); // Check length | |
100 | ASSERT_TRUE(redis.Index("k1", 0, &tempv)); | |
101 | ASSERT_EQ(tempv, "v1"); // Check valid indices | |
102 | ASSERT_TRUE(redis.Index("k1", 1, &tempv)); | |
103 | ASSERT_EQ(tempv, "v2"); | |
104 | ASSERT_TRUE(redis.Index("k1", 2, &tempv)); | |
105 | ASSERT_EQ(tempv, "v3"); | |
106 | ||
107 | // Check range function and vectors | |
108 | std::vector<std::string> result = redis.Range("k1", 0, 2); // Get the list | |
109 | std::vector<std::string> expected_result(3); | |
110 | expected_result[0] = "v1"; | |
111 | expected_result[1] = "v2"; | |
112 | expected_result[2] = "v3"; | |
113 | AssertListEq(result, expected_result); | |
114 | } | |
115 | ||
116 | // Exhaustive test of the Index() function | |
117 | TEST_F(RedisListsTest, IndexTest) { | |
118 | RedisLists redis(kDefaultDbName, options, true); // Destructive | |
119 | ||
120 | std::string tempv; // Used below for all Index(), PopRight(), PopLeft() | |
121 | ||
122 | // Empty Index check (return empty and should not crash or edit tempv) | |
123 | tempv = "yo"; | |
124 | ASSERT_TRUE(!redis.Index("k1", 0, &tempv)); | |
125 | ASSERT_EQ(tempv, "yo"); | |
126 | ASSERT_TRUE(!redis.Index("fda", 3, &tempv)); | |
127 | ASSERT_EQ(tempv, "yo"); | |
128 | ASSERT_TRUE(!redis.Index("random", -12391, &tempv)); | |
129 | ASSERT_EQ(tempv, "yo"); | |
130 | ||
131 | // Simple Pushes (will yield: [v6, v4, v4, v1, v2, v3] | |
132 | redis.PushRight("k1", "v1"); | |
133 | redis.PushRight("k1", "v2"); | |
134 | redis.PushRight("k1", "v3"); | |
135 | redis.PushLeft("k1", "v4"); | |
136 | redis.PushLeft("k1", "v4"); | |
137 | redis.PushLeft("k1", "v6"); | |
138 | ||
139 | // Simple, non-negative indices | |
140 | ASSERT_TRUE(redis.Index("k1", 0, &tempv)); | |
141 | ASSERT_EQ(tempv, "v6"); | |
142 | ASSERT_TRUE(redis.Index("k1", 1, &tempv)); | |
143 | ASSERT_EQ(tempv, "v4"); | |
144 | ASSERT_TRUE(redis.Index("k1", 2, &tempv)); | |
145 | ASSERT_EQ(tempv, "v4"); | |
146 | ASSERT_TRUE(redis.Index("k1", 3, &tempv)); | |
147 | ASSERT_EQ(tempv, "v1"); | |
148 | ASSERT_TRUE(redis.Index("k1", 4, &tempv)); | |
149 | ASSERT_EQ(tempv, "v2"); | |
150 | ASSERT_TRUE(redis.Index("k1", 5, &tempv)); | |
151 | ASSERT_EQ(tempv, "v3"); | |
152 | ||
153 | // Negative indices | |
154 | ASSERT_TRUE(redis.Index("k1", -6, &tempv)); | |
155 | ASSERT_EQ(tempv, "v6"); | |
156 | ASSERT_TRUE(redis.Index("k1", -5, &tempv)); | |
157 | ASSERT_EQ(tempv, "v4"); | |
158 | ASSERT_TRUE(redis.Index("k1", -4, &tempv)); | |
159 | ASSERT_EQ(tempv, "v4"); | |
160 | ASSERT_TRUE(redis.Index("k1", -3, &tempv)); | |
161 | ASSERT_EQ(tempv, "v1"); | |
162 | ASSERT_TRUE(redis.Index("k1", -2, &tempv)); | |
163 | ASSERT_EQ(tempv, "v2"); | |
164 | ASSERT_TRUE(redis.Index("k1", -1, &tempv)); | |
165 | ASSERT_EQ(tempv, "v3"); | |
166 | ||
167 | // Out of bounds (return empty, no crash) | |
168 | ASSERT_TRUE(!redis.Index("k1", 6, &tempv)); | |
169 | ASSERT_TRUE(!redis.Index("k1", 123219, &tempv)); | |
170 | ASSERT_TRUE(!redis.Index("k1", -7, &tempv)); | |
171 | ASSERT_TRUE(!redis.Index("k1", -129, &tempv)); | |
172 | } | |
173 | ||
174 | ||
175 | // Exhaustive test of the Range() function | |
176 | TEST_F(RedisListsTest, RangeTest) { | |
177 | RedisLists redis(kDefaultDbName, options, true); // Destructive | |
178 | ||
179 | std::string tempv; // Used below for all Index(), PopRight(), PopLeft() | |
180 | ||
181 | // Simple Pushes (will yield: [v6, v4, v4, v1, v2, v3]) | |
182 | redis.PushRight("k1", "v1"); | |
183 | redis.PushRight("k1", "v2"); | |
184 | redis.PushRight("k1", "v3"); | |
185 | redis.PushLeft("k1", "v4"); | |
186 | redis.PushLeft("k1", "v4"); | |
187 | redis.PushLeft("k1", "v6"); | |
188 | ||
189 | // Sanity check (check the length; make sure it's 6) | |
190 | ASSERT_EQ(redis.Length("k1"), 6); | |
191 | ||
192 | // Simple range | |
193 | std::vector<std::string> res = redis.Range("k1", 1, 4); | |
194 | ASSERT_EQ((int)res.size(), 4); | |
195 | ASSERT_EQ(res[0], "v4"); | |
196 | ASSERT_EQ(res[1], "v4"); | |
197 | ASSERT_EQ(res[2], "v1"); | |
198 | ASSERT_EQ(res[3], "v2"); | |
199 | ||
200 | // Negative indices (i.e.: measured from the end) | |
201 | res = redis.Range("k1", 2, -1); | |
202 | ASSERT_EQ((int)res.size(), 4); | |
203 | ASSERT_EQ(res[0], "v4"); | |
204 | ASSERT_EQ(res[1], "v1"); | |
205 | ASSERT_EQ(res[2], "v2"); | |
206 | ASSERT_EQ(res[3], "v3"); | |
207 | ||
208 | res = redis.Range("k1", -6, -4); | |
209 | ASSERT_EQ((int)res.size(), 3); | |
210 | ASSERT_EQ(res[0], "v6"); | |
211 | ASSERT_EQ(res[1], "v4"); | |
212 | ASSERT_EQ(res[2], "v4"); | |
213 | ||
214 | res = redis.Range("k1", -1, 5); | |
215 | ASSERT_EQ((int)res.size(), 1); | |
216 | ASSERT_EQ(res[0], "v3"); | |
217 | ||
218 | // Partial / Broken indices | |
219 | res = redis.Range("k1", -3, 1000000); | |
220 | ASSERT_EQ((int)res.size(), 3); | |
221 | ASSERT_EQ(res[0], "v1"); | |
222 | ASSERT_EQ(res[1], "v2"); | |
223 | ASSERT_EQ(res[2], "v3"); | |
224 | ||
225 | res = redis.Range("k1", -1000000, 1); | |
226 | ASSERT_EQ((int)res.size(), 2); | |
227 | ASSERT_EQ(res[0], "v6"); | |
228 | ASSERT_EQ(res[1], "v4"); | |
229 | ||
230 | // Invalid indices | |
231 | res = redis.Range("k1", 7, 9); | |
232 | ASSERT_EQ((int)res.size(), 0); | |
233 | ||
234 | res = redis.Range("k1", -8, -7); | |
235 | ASSERT_EQ((int)res.size(), 0); | |
236 | ||
237 | res = redis.Range("k1", 3, 2); | |
238 | ASSERT_EQ((int)res.size(), 0); | |
239 | ||
240 | res = redis.Range("k1", 5, -2); | |
241 | ASSERT_EQ((int)res.size(), 0); | |
242 | ||
243 | // Range matches Index | |
244 | res = redis.Range("k1", -6, -4); | |
245 | ASSERT_TRUE(redis.Index("k1", -6, &tempv)); | |
246 | ASSERT_EQ(tempv, res[0]); | |
247 | ASSERT_TRUE(redis.Index("k1", -5, &tempv)); | |
248 | ASSERT_EQ(tempv, res[1]); | |
249 | ASSERT_TRUE(redis.Index("k1", -4, &tempv)); | |
250 | ASSERT_EQ(tempv, res[2]); | |
251 | ||
252 | // Last check | |
253 | res = redis.Range("k1", 0, -6); | |
254 | ASSERT_EQ((int)res.size(), 1); | |
255 | ASSERT_EQ(res[0], "v6"); | |
256 | } | |
257 | ||
258 | // Exhaustive test for InsertBefore(), and InsertAfter() | |
259 | TEST_F(RedisListsTest, InsertTest) { | |
260 | RedisLists redis(kDefaultDbName, options, true); | |
261 | ||
262 | std::string tempv; // Used below for all Index(), PopRight(), PopLeft() | |
263 | ||
264 | // Insert on empty list (return 0, and do not crash) | |
265 | ASSERT_EQ(redis.InsertBefore("k1", "non-exist", "a"), 0); | |
266 | ASSERT_EQ(redis.InsertAfter("k1", "other-non-exist", "c"), 0); | |
267 | ASSERT_EQ(redis.Length("k1"), 0); | |
268 | ||
269 | // Push some preliminary stuff [g, f, e, d, c, b, a] | |
270 | redis.PushLeft("k1", "a"); | |
271 | redis.PushLeft("k1", "b"); | |
272 | redis.PushLeft("k1", "c"); | |
273 | redis.PushLeft("k1", "d"); | |
274 | redis.PushLeft("k1", "e"); | |
275 | redis.PushLeft("k1", "f"); | |
276 | redis.PushLeft("k1", "g"); | |
277 | ASSERT_EQ(redis.Length("k1"), 7); | |
278 | ||
279 | // Test InsertBefore | |
280 | int newLength = redis.InsertBefore("k1", "e", "hello"); | |
281 | ASSERT_EQ(newLength, 8); | |
282 | ASSERT_EQ(redis.Length("k1"), newLength); | |
283 | ASSERT_TRUE(redis.Index("k1", 1, &tempv)); | |
284 | ASSERT_EQ(tempv, "f"); | |
285 | ASSERT_TRUE(redis.Index("k1", 3, &tempv)); | |
286 | ASSERT_EQ(tempv, "e"); | |
287 | ASSERT_TRUE(redis.Index("k1", 2, &tempv)); | |
288 | ASSERT_EQ(tempv, "hello"); | |
289 | ||
290 | // Test InsertAfter | |
291 | newLength = redis.InsertAfter("k1", "c", "bye"); | |
292 | ASSERT_EQ(newLength, 9); | |
293 | ASSERT_EQ(redis.Length("k1"), newLength); | |
294 | ASSERT_TRUE(redis.Index("k1", 6, &tempv)); | |
295 | ASSERT_EQ(tempv, "bye"); | |
296 | ||
297 | // Test bad value on InsertBefore | |
298 | newLength = redis.InsertBefore("k1", "yo", "x"); | |
299 | ASSERT_EQ(newLength, 9); | |
300 | ASSERT_EQ(redis.Length("k1"), newLength); | |
301 | ||
302 | // Test bad value on InsertAfter | |
303 | newLength = redis.InsertAfter("k1", "xxxx", "y"); | |
304 | ASSERT_EQ(newLength, 9); | |
305 | ASSERT_EQ(redis.Length("k1"), newLength); | |
306 | ||
307 | // Test InsertBefore beginning | |
308 | newLength = redis.InsertBefore("k1", "g", "begggggggggggggggg"); | |
309 | ASSERT_EQ(newLength, 10); | |
310 | ASSERT_EQ(redis.Length("k1"), newLength); | |
311 | ||
312 | // Test InsertAfter end | |
313 | newLength = redis.InsertAfter("k1", "a", "enddd"); | |
314 | ASSERT_EQ(newLength, 11); | |
315 | ASSERT_EQ(redis.Length("k1"), newLength); | |
316 | ||
317 | // Make sure nothing weird happened. | |
318 | ASSERT_TRUE(redis.Index("k1", 0, &tempv)); | |
319 | ASSERT_EQ(tempv, "begggggggggggggggg"); | |
320 | ASSERT_TRUE(redis.Index("k1", 1, &tempv)); | |
321 | ASSERT_EQ(tempv, "g"); | |
322 | ASSERT_TRUE(redis.Index("k1", 2, &tempv)); | |
323 | ASSERT_EQ(tempv, "f"); | |
324 | ASSERT_TRUE(redis.Index("k1", 3, &tempv)); | |
325 | ASSERT_EQ(tempv, "hello"); | |
326 | ASSERT_TRUE(redis.Index("k1", 4, &tempv)); | |
327 | ASSERT_EQ(tempv, "e"); | |
328 | ASSERT_TRUE(redis.Index("k1", 5, &tempv)); | |
329 | ASSERT_EQ(tempv, "d"); | |
330 | ASSERT_TRUE(redis.Index("k1", 6, &tempv)); | |
331 | ASSERT_EQ(tempv, "c"); | |
332 | ASSERT_TRUE(redis.Index("k1", 7, &tempv)); | |
333 | ASSERT_EQ(tempv, "bye"); | |
334 | ASSERT_TRUE(redis.Index("k1", 8, &tempv)); | |
335 | ASSERT_EQ(tempv, "b"); | |
336 | ASSERT_TRUE(redis.Index("k1", 9, &tempv)); | |
337 | ASSERT_EQ(tempv, "a"); | |
338 | ASSERT_TRUE(redis.Index("k1", 10, &tempv)); | |
339 | ASSERT_EQ(tempv, "enddd"); | |
340 | } | |
341 | ||
342 | // Exhaustive test of Set function | |
343 | TEST_F(RedisListsTest, SetTest) { | |
344 | RedisLists redis(kDefaultDbName, options, true); | |
345 | ||
346 | std::string tempv; // Used below for all Index(), PopRight(), PopLeft() | |
347 | ||
348 | // Set on empty list (return false, and do not crash) | |
349 | ASSERT_EQ(redis.Set("k1", 7, "a"), false); | |
350 | ASSERT_EQ(redis.Set("k1", 0, "a"), false); | |
351 | ASSERT_EQ(redis.Set("k1", -49, "cx"), false); | |
352 | ASSERT_EQ(redis.Length("k1"), 0); | |
353 | ||
354 | // Push some preliminary stuff [g, f, e, d, c, b, a] | |
355 | redis.PushLeft("k1", "a"); | |
356 | redis.PushLeft("k1", "b"); | |
357 | redis.PushLeft("k1", "c"); | |
358 | redis.PushLeft("k1", "d"); | |
359 | redis.PushLeft("k1", "e"); | |
360 | redis.PushLeft("k1", "f"); | |
361 | redis.PushLeft("k1", "g"); | |
362 | ASSERT_EQ(redis.Length("k1"), 7); | |
363 | ||
364 | // Test Regular Set | |
365 | ASSERT_TRUE(redis.Set("k1", 0, "0")); | |
366 | ASSERT_TRUE(redis.Set("k1", 3, "3")); | |
367 | ASSERT_TRUE(redis.Set("k1", 6, "6")); | |
368 | ASSERT_TRUE(redis.Set("k1", 2, "2")); | |
369 | ASSERT_TRUE(redis.Set("k1", 5, "5")); | |
370 | ASSERT_TRUE(redis.Set("k1", 1, "1")); | |
371 | ASSERT_TRUE(redis.Set("k1", 4, "4")); | |
372 | ||
373 | ASSERT_EQ(redis.Length("k1"), 7); // Size should not change | |
374 | ASSERT_TRUE(redis.Index("k1", 0, &tempv)); | |
375 | ASSERT_EQ(tempv, "0"); | |
376 | ASSERT_TRUE(redis.Index("k1", 1, &tempv)); | |
377 | ASSERT_EQ(tempv, "1"); | |
378 | ASSERT_TRUE(redis.Index("k1", 2, &tempv)); | |
379 | ASSERT_EQ(tempv, "2"); | |
380 | ASSERT_TRUE(redis.Index("k1", 3, &tempv)); | |
381 | ASSERT_EQ(tempv, "3"); | |
382 | ASSERT_TRUE(redis.Index("k1", 4, &tempv)); | |
383 | ASSERT_EQ(tempv, "4"); | |
384 | ASSERT_TRUE(redis.Index("k1", 5, &tempv)); | |
385 | ASSERT_EQ(tempv, "5"); | |
386 | ASSERT_TRUE(redis.Index("k1", 6, &tempv)); | |
387 | ASSERT_EQ(tempv, "6"); | |
388 | ||
389 | // Set with negative indices | |
390 | ASSERT_TRUE(redis.Set("k1", -7, "a")); | |
391 | ASSERT_TRUE(redis.Set("k1", -4, "d")); | |
392 | ASSERT_TRUE(redis.Set("k1", -1, "g")); | |
393 | ASSERT_TRUE(redis.Set("k1", -5, "c")); | |
394 | ASSERT_TRUE(redis.Set("k1", -2, "f")); | |
395 | ASSERT_TRUE(redis.Set("k1", -6, "b")); | |
396 | ASSERT_TRUE(redis.Set("k1", -3, "e")); | |
397 | ||
398 | ASSERT_EQ(redis.Length("k1"), 7); // Size should not change | |
399 | ASSERT_TRUE(redis.Index("k1", 0, &tempv)); | |
400 | ASSERT_EQ(tempv, "a"); | |
401 | ASSERT_TRUE(redis.Index("k1", 1, &tempv)); | |
402 | ASSERT_EQ(tempv, "b"); | |
403 | ASSERT_TRUE(redis.Index("k1", 2, &tempv)); | |
404 | ASSERT_EQ(tempv, "c"); | |
405 | ASSERT_TRUE(redis.Index("k1", 3, &tempv)); | |
406 | ASSERT_EQ(tempv, "d"); | |
407 | ASSERT_TRUE(redis.Index("k1", 4, &tempv)); | |
408 | ASSERT_EQ(tempv, "e"); | |
409 | ASSERT_TRUE(redis.Index("k1", 5, &tempv)); | |
410 | ASSERT_EQ(tempv, "f"); | |
411 | ASSERT_TRUE(redis.Index("k1", 6, &tempv)); | |
412 | ASSERT_EQ(tempv, "g"); | |
413 | ||
414 | // Bad indices (just out-of-bounds / off-by-one check) | |
415 | ASSERT_EQ(redis.Set("k1", -8, "off-by-one in negative index"), false); | |
416 | ASSERT_EQ(redis.Set("k1", 7, "off-by-one-error in positive index"), false); | |
417 | ASSERT_EQ(redis.Set("k1", 43892, "big random index should fail"), false); | |
418 | ASSERT_EQ(redis.Set("k1", -21391, "large negative index should fail"), false); | |
419 | ||
420 | // One last check (to make sure nothing weird happened) | |
421 | ASSERT_EQ(redis.Length("k1"), 7); // Size should not change | |
422 | ASSERT_TRUE(redis.Index("k1", 0, &tempv)); | |
423 | ASSERT_EQ(tempv, "a"); | |
424 | ASSERT_TRUE(redis.Index("k1", 1, &tempv)); | |
425 | ASSERT_EQ(tempv, "b"); | |
426 | ASSERT_TRUE(redis.Index("k1", 2, &tempv)); | |
427 | ASSERT_EQ(tempv, "c"); | |
428 | ASSERT_TRUE(redis.Index("k1", 3, &tempv)); | |
429 | ASSERT_EQ(tempv, "d"); | |
430 | ASSERT_TRUE(redis.Index("k1", 4, &tempv)); | |
431 | ASSERT_EQ(tempv, "e"); | |
432 | ASSERT_TRUE(redis.Index("k1", 5, &tempv)); | |
433 | ASSERT_EQ(tempv, "f"); | |
434 | ASSERT_TRUE(redis.Index("k1", 6, &tempv)); | |
435 | ASSERT_EQ(tempv, "g"); | |
436 | } | |
437 | ||
438 | // Testing Insert, Push, and Set, in a mixed environment | |
439 | TEST_F(RedisListsTest, InsertPushSetTest) { | |
440 | RedisLists redis(kDefaultDbName, options, true); // Destructive | |
441 | ||
442 | std::string tempv; // Used below for all Index(), PopRight(), PopLeft() | |
443 | ||
444 | // A series of pushes and insertions | |
445 | // Will result in [newbegin, z, a, aftera, x, newend] | |
446 | // Also, check the return value sometimes (should return length) | |
447 | int lengthCheck; | |
448 | lengthCheck = redis.PushLeft("k1", "a"); | |
449 | ASSERT_EQ(lengthCheck, 1); | |
450 | redis.PushLeft("k1", "z"); | |
451 | redis.PushRight("k1", "x"); | |
452 | lengthCheck = redis.InsertAfter("k1", "a", "aftera"); | |
453 | ASSERT_EQ(lengthCheck , 4); | |
454 | redis.InsertBefore("k1", "z", "newbegin"); // InsertBefore beginning of list | |
455 | redis.InsertAfter("k1", "x", "newend"); // InsertAfter end of list | |
456 | ||
457 | // Check | |
458 | std::vector<std::string> res = redis.Range("k1", 0, -1); // Get the list | |
459 | ASSERT_EQ((int)res.size(), 6); | |
460 | ASSERT_EQ(res[0], "newbegin"); | |
461 | ASSERT_EQ(res[5], "newend"); | |
462 | ASSERT_EQ(res[3], "aftera"); | |
463 | ||
464 | // Testing duplicate values/pivots (multiple occurrences of 'a') | |
465 | ASSERT_TRUE(redis.Set("k1", 0, "a")); // [a, z, a, aftera, x, newend] | |
466 | redis.InsertAfter("k1", "a", "happy"); // [a, happy, z, a, aftera, ...] | |
467 | ASSERT_TRUE(redis.Index("k1", 1, &tempv)); | |
468 | ASSERT_EQ(tempv, "happy"); | |
469 | redis.InsertBefore("k1", "a", "sad"); // [sad, a, happy, z, a, aftera, ...] | |
470 | ASSERT_TRUE(redis.Index("k1", 0, &tempv)); | |
471 | ASSERT_EQ(tempv, "sad"); | |
472 | ASSERT_TRUE(redis.Index("k1", 2, &tempv)); | |
473 | ASSERT_EQ(tempv, "happy"); | |
474 | ASSERT_TRUE(redis.Index("k1", 5, &tempv)); | |
475 | ASSERT_EQ(tempv, "aftera"); | |
476 | redis.InsertAfter("k1", "a", "zz"); // [sad, a, zz, happy, z, a, aftera, ...] | |
477 | ASSERT_TRUE(redis.Index("k1", 2, &tempv)); | |
478 | ASSERT_EQ(tempv, "zz"); | |
479 | ASSERT_TRUE(redis.Index("k1", 6, &tempv)); | |
480 | ASSERT_EQ(tempv, "aftera"); | |
481 | ASSERT_TRUE(redis.Set("k1", 1, "nota")); // [sad, nota, zz, happy, z, a, ...] | |
482 | redis.InsertBefore("k1", "a", "ba"); // [sad, nota, zz, happy, z, ba, a, ...] | |
483 | ASSERT_TRUE(redis.Index("k1", 4, &tempv)); | |
484 | ASSERT_EQ(tempv, "z"); | |
485 | ASSERT_TRUE(redis.Index("k1", 5, &tempv)); | |
486 | ASSERT_EQ(tempv, "ba"); | |
487 | ASSERT_TRUE(redis.Index("k1", 6, &tempv)); | |
488 | ASSERT_EQ(tempv, "a"); | |
489 | ||
490 | // We currently have: [sad, nota, zz, happy, z, ba, a, aftera, x, newend] | |
491 | // redis.Print("k1"); // manually check | |
492 | ||
493 | // Test Inserting before/after non-existent values | |
494 | lengthCheck = redis.Length("k1"); // Ensure that the length doesn't change | |
495 | ASSERT_EQ(lengthCheck, 10); | |
496 | ASSERT_EQ(redis.InsertBefore("k1", "non-exist", "randval"), lengthCheck); | |
497 | ASSERT_EQ(redis.InsertAfter("k1", "nothing", "a"), lengthCheck); | |
498 | ASSERT_EQ(redis.InsertAfter("randKey", "randVal", "ranValue"), 0); // Empty | |
499 | ASSERT_EQ(redis.Length("k1"), lengthCheck); // The length should not change | |
500 | ||
501 | // Simply Test the Set() function | |
502 | redis.Set("k1", 5, "ba2"); | |
503 | redis.InsertBefore("k1", "ba2", "beforeba2"); | |
504 | ASSERT_TRUE(redis.Index("k1", 4, &tempv)); | |
505 | ASSERT_EQ(tempv, "z"); | |
506 | ASSERT_TRUE(redis.Index("k1", 5, &tempv)); | |
507 | ASSERT_EQ(tempv, "beforeba2"); | |
508 | ASSERT_TRUE(redis.Index("k1", 6, &tempv)); | |
509 | ASSERT_EQ(tempv, "ba2"); | |
510 | ASSERT_TRUE(redis.Index("k1", 7, &tempv)); | |
511 | ASSERT_EQ(tempv, "a"); | |
512 | ||
513 | // We have: [sad, nota, zz, happy, z, beforeba2, ba2, a, aftera, x, newend] | |
514 | ||
515 | // Set() with negative indices | |
516 | redis.Set("k1", -1, "endprank"); | |
517 | ASSERT_TRUE(!redis.Index("k1", 11, &tempv)); | |
518 | ASSERT_TRUE(redis.Index("k1", 10, &tempv)); | |
519 | ASSERT_EQ(tempv, "endprank"); // Ensure Set worked correctly | |
520 | redis.Set("k1", -11, "t"); | |
521 | ASSERT_TRUE(redis.Index("k1", 0, &tempv)); | |
522 | ASSERT_EQ(tempv, "t"); | |
523 | ||
524 | // Test out of bounds Set | |
525 | ASSERT_EQ(redis.Set("k1", -12, "ssd"), false); | |
526 | ASSERT_EQ(redis.Set("k1", 11, "sasd"), false); | |
527 | ASSERT_EQ(redis.Set("k1", 1200, "big"), false); | |
528 | } | |
529 | ||
530 | // Testing Trim, Pop | |
531 | TEST_F(RedisListsTest, TrimPopTest) { | |
532 | RedisLists redis(kDefaultDbName, options, true); // Destructive | |
533 | ||
534 | std::string tempv; // Used below for all Index(), PopRight(), PopLeft() | |
535 | ||
536 | // A series of pushes and insertions | |
537 | // Will result in [newbegin, z, a, aftera, x, newend] | |
538 | redis.PushLeft("k1", "a"); | |
539 | redis.PushLeft("k1", "z"); | |
540 | redis.PushRight("k1", "x"); | |
541 | redis.InsertBefore("k1", "z", "newbegin"); // InsertBefore start of list | |
542 | redis.InsertAfter("k1", "x", "newend"); // InsertAfter end of list | |
543 | redis.InsertAfter("k1", "a", "aftera"); | |
544 | ||
545 | // Simple PopLeft/Right test | |
546 | ASSERT_TRUE(redis.PopLeft("k1", &tempv)); | |
547 | ASSERT_EQ(tempv, "newbegin"); | |
548 | ASSERT_EQ(redis.Length("k1"), 5); | |
549 | ASSERT_TRUE(redis.Index("k1", 0, &tempv)); | |
550 | ASSERT_EQ(tempv, "z"); | |
551 | ASSERT_TRUE(redis.PopRight("k1", &tempv)); | |
552 | ASSERT_EQ(tempv, "newend"); | |
553 | ASSERT_EQ(redis.Length("k1"), 4); | |
554 | ASSERT_TRUE(redis.Index("k1", -1, &tempv)); | |
555 | ASSERT_EQ(tempv, "x"); | |
556 | ||
557 | // Now have: [z, a, aftera, x] | |
558 | ||
559 | // Test Trim | |
560 | ASSERT_TRUE(redis.Trim("k1", 0, -1)); // [z, a, aftera, x] (do nothing) | |
561 | ASSERT_EQ(redis.Length("k1"), 4); | |
562 | ASSERT_TRUE(redis.Trim("k1", 0, 2)); // [z, a, aftera] | |
563 | ASSERT_EQ(redis.Length("k1"), 3); | |
564 | ASSERT_TRUE(redis.Index("k1", -1, &tempv)); | |
565 | ASSERT_EQ(tempv, "aftera"); | |
566 | ASSERT_TRUE(redis.Trim("k1", 1, 1)); // [a] | |
567 | ASSERT_EQ(redis.Length("k1"), 1); | |
568 | ASSERT_TRUE(redis.Index("k1", 0, &tempv)); | |
569 | ASSERT_EQ(tempv, "a"); | |
570 | ||
571 | // Test out of bounds (empty) trim | |
572 | ASSERT_TRUE(redis.Trim("k1", 1, 0)); | |
573 | ASSERT_EQ(redis.Length("k1"), 0); | |
574 | ||
575 | // Popping with empty list (return empty without error) | |
576 | ASSERT_TRUE(!redis.PopLeft("k1", &tempv)); | |
577 | ASSERT_TRUE(!redis.PopRight("k1", &tempv)); | |
578 | ASSERT_TRUE(redis.Trim("k1", 0, 5)); | |
579 | ||
580 | // Exhaustive Trim test (negative and invalid indices) | |
581 | // Will start in [newbegin, z, a, aftera, x, newend] | |
582 | redis.PushLeft("k1", "a"); | |
583 | redis.PushLeft("k1", "z"); | |
584 | redis.PushRight("k1", "x"); | |
585 | redis.InsertBefore("k1", "z", "newbegin"); // InsertBefore start of list | |
586 | redis.InsertAfter("k1", "x", "newend"); // InsertAfter end of list | |
587 | redis.InsertAfter("k1", "a", "aftera"); | |
588 | ASSERT_TRUE(redis.Trim("k1", -6, -1)); // Should do nothing | |
589 | ASSERT_EQ(redis.Length("k1"), 6); | |
590 | ASSERT_TRUE(redis.Trim("k1", 1, -2)); | |
591 | ASSERT_TRUE(redis.Index("k1", 0, &tempv)); | |
592 | ASSERT_EQ(tempv, "z"); | |
593 | ASSERT_TRUE(redis.Index("k1", 3, &tempv)); | |
594 | ASSERT_EQ(tempv, "x"); | |
595 | ASSERT_EQ(redis.Length("k1"), 4); | |
596 | ASSERT_TRUE(redis.Trim("k1", -3, -2)); | |
597 | ASSERT_EQ(redis.Length("k1"), 2); | |
598 | } | |
599 | ||
600 | // Testing Remove, RemoveFirst, RemoveLast | |
601 | TEST_F(RedisListsTest, RemoveTest) { | |
602 | RedisLists redis(kDefaultDbName, options, true); // Destructive | |
603 | ||
604 | std::string tempv; // Used below for all Index(), PopRight(), PopLeft() | |
605 | ||
606 | // A series of pushes and insertions | |
607 | // Will result in [newbegin, z, a, aftera, x, newend, a, a] | |
608 | redis.PushLeft("k1", "a"); | |
609 | redis.PushLeft("k1", "z"); | |
610 | redis.PushRight("k1", "x"); | |
611 | redis.InsertBefore("k1", "z", "newbegin"); // InsertBefore start of list | |
612 | redis.InsertAfter("k1", "x", "newend"); // InsertAfter end of list | |
613 | redis.InsertAfter("k1", "a", "aftera"); | |
614 | redis.PushRight("k1", "a"); | |
615 | redis.PushRight("k1", "a"); | |
616 | ||
617 | // Verify | |
618 | ASSERT_TRUE(redis.Index("k1", 0, &tempv)); | |
619 | ASSERT_EQ(tempv, "newbegin"); | |
620 | ASSERT_TRUE(redis.Index("k1", -1, &tempv)); | |
621 | ASSERT_EQ(tempv, "a"); | |
622 | ||
623 | // Check RemoveFirst (Remove the first two 'a') | |
624 | // Results in [newbegin, z, aftera, x, newend, a] | |
625 | int numRemoved = redis.Remove("k1", 2, "a"); | |
626 | ASSERT_EQ(numRemoved, 2); | |
627 | ASSERT_TRUE(redis.Index("k1", 0, &tempv)); | |
628 | ASSERT_EQ(tempv, "newbegin"); | |
629 | ASSERT_TRUE(redis.Index("k1", 1, &tempv)); | |
630 | ASSERT_EQ(tempv, "z"); | |
631 | ASSERT_TRUE(redis.Index("k1", 4, &tempv)); | |
632 | ASSERT_EQ(tempv, "newend"); | |
633 | ASSERT_TRUE(redis.Index("k1", 5, &tempv)); | |
634 | ASSERT_EQ(tempv, "a"); | |
635 | ASSERT_EQ(redis.Length("k1"), 6); | |
636 | ||
637 | // Repopulate some stuff | |
638 | // Results in: [x, x, x, x, x, newbegin, z, x, aftera, x, newend, a, x] | |
639 | redis.PushLeft("k1", "x"); | |
640 | redis.PushLeft("k1", "x"); | |
641 | redis.PushLeft("k1", "x"); | |
642 | redis.PushLeft("k1", "x"); | |
643 | redis.PushLeft("k1", "x"); | |
644 | redis.PushRight("k1", "x"); | |
645 | redis.InsertAfter("k1", "z", "x"); | |
646 | ||
647 | // Test removal from end | |
648 | numRemoved = redis.Remove("k1", -2, "x"); | |
649 | ASSERT_EQ(numRemoved, 2); | |
650 | ASSERT_TRUE(redis.Index("k1", 8, &tempv)); | |
651 | ASSERT_EQ(tempv, "aftera"); | |
652 | ASSERT_TRUE(redis.Index("k1", 9, &tempv)); | |
653 | ASSERT_EQ(tempv, "newend"); | |
654 | ASSERT_TRUE(redis.Index("k1", 10, &tempv)); | |
655 | ASSERT_EQ(tempv, "a"); | |
656 | ASSERT_TRUE(!redis.Index("k1", 11, &tempv)); | |
657 | numRemoved = redis.Remove("k1", -2, "x"); | |
658 | ASSERT_EQ(numRemoved, 2); | |
659 | ASSERT_TRUE(redis.Index("k1", 4, &tempv)); | |
660 | ASSERT_EQ(tempv, "newbegin"); | |
661 | ASSERT_TRUE(redis.Index("k1", 6, &tempv)); | |
662 | ASSERT_EQ(tempv, "aftera"); | |
663 | ||
664 | // We now have: [x, x, x, x, newbegin, z, aftera, newend, a] | |
665 | ASSERT_EQ(redis.Length("k1"), 9); | |
666 | ASSERT_TRUE(redis.Index("k1", -1, &tempv)); | |
667 | ASSERT_EQ(tempv, "a"); | |
668 | ASSERT_TRUE(redis.Index("k1", 0, &tempv)); | |
669 | ASSERT_EQ(tempv, "x"); | |
670 | ||
671 | // Test over-shooting (removing more than there exists) | |
672 | numRemoved = redis.Remove("k1", -9000, "x"); | |
673 | ASSERT_EQ(numRemoved , 4); // Only really removed 4 | |
674 | ASSERT_EQ(redis.Length("k1"), 5); | |
675 | ASSERT_TRUE(redis.Index("k1", 0, &tempv)); | |
676 | ASSERT_EQ(tempv, "newbegin"); | |
677 | numRemoved = redis.Remove("k1", 1, "x"); | |
678 | ASSERT_EQ(numRemoved, 0); | |
679 | ||
680 | // Try removing ALL! | |
681 | numRemoved = redis.Remove("k1", 0, "newbegin"); // REMOVE 0 will remove all! | |
682 | ASSERT_EQ(numRemoved, 1); | |
683 | ||
684 | // Removal from an empty-list | |
685 | ASSERT_TRUE(redis.Trim("k1", 1, 0)); | |
686 | numRemoved = redis.Remove("k1", 1, "z"); | |
687 | ASSERT_EQ(numRemoved, 0); | |
688 | } | |
689 | ||
690 | ||
691 | // Test Multiple keys and Persistence | |
692 | TEST_F(RedisListsTest, PersistenceMultiKeyTest) { | |
693 | std::string tempv; // Used below for all Index(), PopRight(), PopLeft() | |
694 | ||
695 | // Block one: populate a single key in the database | |
696 | { | |
697 | RedisLists redis(kDefaultDbName, options, true); // Destructive | |
698 | ||
699 | // A series of pushes and insertions | |
700 | // Will result in [newbegin, z, a, aftera, x, newend, a, a] | |
701 | redis.PushLeft("k1", "a"); | |
702 | redis.PushLeft("k1", "z"); | |
703 | redis.PushRight("k1", "x"); | |
704 | redis.InsertBefore("k1", "z", "newbegin"); // InsertBefore start of list | |
705 | redis.InsertAfter("k1", "x", "newend"); // InsertAfter end of list | |
706 | redis.InsertAfter("k1", "a", "aftera"); | |
707 | redis.PushRight("k1", "a"); | |
708 | redis.PushRight("k1", "a"); | |
709 | ||
710 | ASSERT_TRUE(redis.Index("k1", 3, &tempv)); | |
711 | ASSERT_EQ(tempv, "aftera"); | |
712 | } | |
713 | ||
714 | // Block two: make sure changes were saved and add some other key | |
715 | { | |
716 | RedisLists redis(kDefaultDbName, options, false); // Persistent, non-destructive | |
717 | ||
718 | // Check | |
719 | ASSERT_EQ(redis.Length("k1"), 8); | |
720 | ASSERT_TRUE(redis.Index("k1", 3, &tempv)); | |
721 | ASSERT_EQ(tempv, "aftera"); | |
722 | ||
723 | redis.PushRight("k2", "randomkey"); | |
724 | redis.PushLeft("k2", "sas"); | |
725 | ||
726 | redis.PopLeft("k1", &tempv); | |
727 | } | |
728 | ||
729 | // Block three: Verify the changes from block 2 | |
730 | { | |
731 | RedisLists redis(kDefaultDbName, options, false); // Persistent, non-destructive | |
732 | ||
733 | // Check | |
734 | ASSERT_EQ(redis.Length("k1"), 7); | |
735 | ASSERT_EQ(redis.Length("k2"), 2); | |
736 | ASSERT_TRUE(redis.Index("k1", 0, &tempv)); | |
737 | ASSERT_EQ(tempv, "z"); | |
738 | ASSERT_TRUE(redis.Index("k2", -2, &tempv)); | |
739 | ASSERT_EQ(tempv, "sas"); | |
740 | } | |
741 | } | |
742 | ||
743 | /// THE manual REDIS TEST begins here | |
744 | /// THIS WILL ONLY OCCUR IF YOU RUN: ./redis_test -m | |
745 | ||
746 | namespace { | |
747 | void MakeUpper(std::string* const s) { | |
748 | int len = static_cast<int>(s->length()); | |
749 | for (int i = 0; i < len; ++i) { | |
11fdf7f2 | 750 | (*s)[i] = static_cast<char>(toupper((*s)[i])); // C-version defined in <ctype.h> |
7c673cae FG |
751 | } |
752 | } | |
753 | ||
754 | /// Allows the user to enter in REDIS commands into the command-line. | |
755 | /// This is useful for manual / interacticve testing / debugging. | |
756 | /// Use destructive=true to clean the database before use. | |
757 | /// Use destructive=false to remember the previous state (i.e.: persistent) | |
758 | /// Should be called from main function. | |
759 | int manual_redis_test(bool destructive){ | |
760 | RedisLists redis(RedisListsTest::kDefaultDbName, | |
761 | RedisListsTest::options, | |
762 | destructive); | |
763 | ||
764 | // TODO: Right now, please use spaces to separate each word. | |
765 | // In actual redis, you can use quotes to specify compound values | |
766 | // Example: RPUSH mylist "this is a compound value" | |
767 | ||
768 | std::string command; | |
769 | while(true) { | |
770 | std::cin >> command; | |
771 | MakeUpper(&command); | |
772 | ||
773 | if (command == "LINSERT") { | |
774 | std::string k, t, p, v; | |
775 | std::cin >> k >> t >> p >> v; | |
776 | MakeUpper(&t); | |
777 | if (t=="BEFORE") { | |
778 | std::cout << redis.InsertBefore(k, p, v) << std::endl; | |
779 | } else if (t=="AFTER") { | |
780 | std::cout << redis.InsertAfter(k, p, v) << std::endl; | |
781 | } | |
782 | } else if (command == "LPUSH") { | |
783 | std::string k, v; | |
784 | std::cin >> k >> v; | |
785 | redis.PushLeft(k, v); | |
786 | } else if (command == "RPUSH") { | |
787 | std::string k, v; | |
788 | std::cin >> k >> v; | |
789 | redis.PushRight(k, v); | |
790 | } else if (command == "LPOP") { | |
791 | std::string k; | |
792 | std::cin >> k; | |
793 | std::string res; | |
794 | redis.PopLeft(k, &res); | |
795 | std::cout << res << std::endl; | |
796 | } else if (command == "RPOP") { | |
797 | std::string k; | |
798 | std::cin >> k; | |
799 | std::string res; | |
800 | redis.PopRight(k, &res); | |
801 | std::cout << res << std::endl; | |
802 | } else if (command == "LREM") { | |
803 | std::string k; | |
804 | int amt; | |
805 | std::string v; | |
806 | ||
807 | std::cin >> k >> amt >> v; | |
808 | std::cout << redis.Remove(k, amt, v) << std::endl; | |
809 | } else if (command == "LLEN") { | |
810 | std::string k; | |
811 | std::cin >> k; | |
812 | std::cout << redis.Length(k) << std::endl; | |
813 | } else if (command == "LRANGE") { | |
814 | std::string k; | |
815 | int i, j; | |
816 | std::cin >> k >> i >> j; | |
817 | std::vector<std::string> res = redis.Range(k, i, j); | |
818 | for (auto it = res.begin(); it != res.end(); ++it) { | |
819 | std::cout << " " << (*it); | |
820 | } | |
821 | std::cout << std::endl; | |
822 | } else if (command == "LTRIM") { | |
823 | std::string k; | |
824 | int i, j; | |
825 | std::cin >> k >> i >> j; | |
826 | redis.Trim(k, i, j); | |
827 | } else if (command == "LSET") { | |
828 | std::string k; | |
829 | int idx; | |
830 | std::string v; | |
831 | std::cin >> k >> idx >> v; | |
832 | redis.Set(k, idx, v); | |
833 | } else if (command == "LINDEX") { | |
834 | std::string k; | |
835 | int idx; | |
836 | std::cin >> k >> idx; | |
837 | std::string res; | |
838 | redis.Index(k, idx, &res); | |
839 | std::cout << res << std::endl; | |
840 | } else if (command == "PRINT") { // Added by Deon | |
841 | std::string k; | |
842 | std::cin >> k; | |
843 | redis.Print(k); | |
844 | } else if (command == "QUIT") { | |
845 | return 0; | |
846 | } else { | |
847 | std::cout << "unknown command: " << command << std::endl; | |
848 | } | |
849 | } | |
850 | } | |
851 | } // namespace | |
852 | ||
853 | } // namespace rocksdb | |
854 | ||
855 | ||
856 | // USAGE: "./redis_test" for default (unit tests) | |
857 | // "./redis_test -m" for manual testing (redis command api) | |
858 | // "./redis_test -m -d" for destructive manual test (erase db before use) | |
859 | ||
860 | ||
861 | namespace { | |
862 | // Check for "want" argument in the argument list | |
863 | bool found_arg(int argc, char* argv[], const char* want){ | |
864 | for(int i=1; i<argc; ++i){ | |
865 | if (strcmp(argv[i], want) == 0) { | |
866 | return true; | |
867 | } | |
868 | } | |
869 | return false; | |
870 | } | |
871 | } // namespace | |
872 | ||
873 | // Will run unit tests. | |
874 | // However, if -m is specified, it will do user manual/interactive testing | |
875 | // -m -d is manual and destructive (will clear the database before use) | |
876 | int main(int argc, char* argv[]) { | |
877 | ::testing::InitGoogleTest(&argc, argv); | |
878 | if (found_arg(argc, argv, "-m")) { | |
879 | bool destructive = found_arg(argc, argv, "-d"); | |
880 | return rocksdb::manual_redis_test(destructive); | |
881 | } else { | |
882 | return RUN_ALL_TESTS(); | |
883 | } | |
884 | } | |
885 | ||
886 | #else | |
887 | #include <stdio.h> | |
888 | ||
11fdf7f2 | 889 | int main(int /*argc*/, char** /*argv*/) { |
7c673cae FG |
890 | fprintf(stderr, "SKIPPED as redis is not supported in ROCKSDB_LITE\n"); |
891 | return 0; | |
892 | } | |
893 | ||
894 | #endif // !ROCKSDB_LITE |