]>
git.proxmox.com Git - ceph.git/blob - ceph/src/rocksdb/utilities/redis/redis_lists_test.cc
1 // Copyright (c) 2011-present, Facebook, Inc. All rights reserved.
2 // This source code is licensed under the BSD-style license found in the
3 // LICENSE file in the root directory of this source tree. An additional grant
4 // of patent rights can be found in the PATENTS file in the same directory.
6 * A test harness for the Redis API built on rocksdb.
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"
13 * TODO: Add LARGE random test cases to verify efficiency and scalability
15 * @author Deon Nicholas (dnicholas@fb.com)
23 #include "redis_lists.h"
24 #include "util/testharness.h"
25 #include "util/random.h"
27 using namespace rocksdb
;
31 class RedisListsTest
: public testing::Test
{
33 static const std::string kDefaultDbName
;
34 static Options options
;
37 options
.create_if_missing
= true;
41 const std::string
RedisListsTest::kDefaultDbName
=
42 test::TmpDir() + "/redis_lists_test";
43 Options
RedisListsTest::options
= Options();
45 // operator== and operator<< are defined below for vectors (lists)
46 // Needed for ASSERT_EQ
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
]);
58 // PushRight, Length, Index, Range
59 TEST_F(RedisListsTest
, SimpleTest
) {
60 RedisLists
redis(kDefaultDbName
, options
, true); // Destructive
62 std::string tempv
; // Used below for all Index(), PopRight(), PopLeft()
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);
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");
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
);
87 // PushLeft, Length, Index, Range
88 TEST_F(RedisListsTest
, SimpleTest2
) {
89 RedisLists
redis(kDefaultDbName
, options
, true); // Destructive
91 std::string tempv
; // Used below for all Index(), PopRight(), PopLeft()
94 ASSERT_EQ(redis
.PushLeft("k1", "v3"), 1);
95 ASSERT_EQ(redis
.PushLeft("k1", "v2"), 2);
96 ASSERT_EQ(redis
.PushLeft("k1", "v1"), 3);
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");
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
);
116 // Exhaustive test of the Index() function
117 TEST_F(RedisListsTest
, IndexTest
) {
118 RedisLists
redis(kDefaultDbName
, options
, true); // Destructive
120 std::string tempv
; // Used below for all Index(), PopRight(), PopLeft()
122 // Empty Index check (return empty and should not crash or edit tempv)
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");
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");
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");
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");
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
));
175 // Exhaustive test of the Range() function
176 TEST_F(RedisListsTest
, RangeTest
) {
177 RedisLists
redis(kDefaultDbName
, options
, true); // Destructive
179 std::string tempv
; // Used below for all Index(), PopRight(), PopLeft()
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");
189 // Sanity check (check the length; make sure it's 6)
190 ASSERT_EQ(redis
.Length("k1"), 6);
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");
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");
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");
214 res
= redis
.Range("k1", -1, 5);
215 ASSERT_EQ((int)res
.size(), 1);
216 ASSERT_EQ(res
[0], "v3");
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");
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");
231 res
= redis
.Range("k1", 7, 9);
232 ASSERT_EQ((int)res
.size(), 0);
234 res
= redis
.Range("k1", -8, -7);
235 ASSERT_EQ((int)res
.size(), 0);
237 res
= redis
.Range("k1", 3, 2);
238 ASSERT_EQ((int)res
.size(), 0);
240 res
= redis
.Range("k1", 5, -2);
241 ASSERT_EQ((int)res
.size(), 0);
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]);
253 res
= redis
.Range("k1", 0, -6);
254 ASSERT_EQ((int)res
.size(), 1);
255 ASSERT_EQ(res
[0], "v6");
258 // Exhaustive test for InsertBefore(), and InsertAfter()
259 TEST_F(RedisListsTest
, InsertTest
) {
260 RedisLists
redis(kDefaultDbName
, options
, true);
262 std::string tempv
; // Used below for all Index(), PopRight(), PopLeft()
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);
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);
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");
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");
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
);
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
);
307 // Test InsertBefore beginning
308 newLength
= redis
.InsertBefore("k1", "g", "begggggggggggggggg");
309 ASSERT_EQ(newLength
, 10);
310 ASSERT_EQ(redis
.Length("k1"), newLength
);
312 // Test InsertAfter end
313 newLength
= redis
.InsertAfter("k1", "a", "enddd");
314 ASSERT_EQ(newLength
, 11);
315 ASSERT_EQ(redis
.Length("k1"), newLength
);
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");
342 // Exhaustive test of Set function
343 TEST_F(RedisListsTest
, SetTest
) {
344 RedisLists
redis(kDefaultDbName
, options
, true);
346 std::string tempv
; // Used below for all Index(), PopRight(), PopLeft()
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);
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);
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"));
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");
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"));
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");
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);
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");
438 // Testing Insert, Push, and Set, in a mixed environment
439 TEST_F(RedisListsTest
, InsertPushSetTest
) {
440 RedisLists
redis(kDefaultDbName
, options
, true); // Destructive
442 std::string tempv
; // Used below for all Index(), PopRight(), PopLeft()
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)
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
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");
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");
490 // We currently have: [sad, nota, zz, happy, z, ba, a, aftera, x, newend]
491 // redis.Print("k1"); // manually check
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
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");
513 // We have: [sad, nota, zz, happy, z, beforeba2, ba2, a, aftera, x, newend]
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");
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);
531 TEST_F(RedisListsTest
, TrimPopTest
) {
532 RedisLists
redis(kDefaultDbName
, options
, true); // Destructive
534 std::string tempv
; // Used below for all Index(), PopRight(), PopLeft()
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");
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");
557 // Now have: [z, a, aftera, x]
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");
571 // Test out of bounds (empty) trim
572 ASSERT_TRUE(redis
.Trim("k1", 1, 0));
573 ASSERT_EQ(redis
.Length("k1"), 0);
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));
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);
600 // Testing Remove, RemoveFirst, RemoveLast
601 TEST_F(RedisListsTest
, RemoveTest
) {
602 RedisLists
redis(kDefaultDbName
, options
, true); // Destructive
604 std::string tempv
; // Used below for all Index(), PopRight(), PopLeft()
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");
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");
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);
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");
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");
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");
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);
681 numRemoved
= redis
.Remove("k1", 0, "newbegin"); // REMOVE 0 will remove all!
682 ASSERT_EQ(numRemoved
, 1);
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);
691 // Test Multiple keys and Persistence
692 TEST_F(RedisListsTest
, PersistenceMultiKeyTest
) {
693 std::string tempv
; // Used below for all Index(), PopRight(), PopLeft()
695 // Block one: populate a single key in the database
697 RedisLists
redis(kDefaultDbName
, options
, true); // Destructive
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");
710 ASSERT_TRUE(redis
.Index("k1", 3, &tempv
));
711 ASSERT_EQ(tempv
, "aftera");
714 // Block two: make sure changes were saved and add some other key
716 RedisLists
redis(kDefaultDbName
, options
, false); // Persistent, non-destructive
719 ASSERT_EQ(redis
.Length("k1"), 8);
720 ASSERT_TRUE(redis
.Index("k1", 3, &tempv
));
721 ASSERT_EQ(tempv
, "aftera");
723 redis
.PushRight("k2", "randomkey");
724 redis
.PushLeft("k2", "sas");
726 redis
.PopLeft("k1", &tempv
);
729 // Block three: Verify the changes from block 2
731 RedisLists
redis(kDefaultDbName
, options
, false); // Persistent, non-destructive
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");
743 /// THE manual REDIS TEST begins here
744 /// THIS WILL ONLY OCCUR IF YOU RUN: ./redis_test -m
747 void MakeUpper(std::string
* const s
) {
748 int len
= static_cast<int>(s
->length());
749 for (int i
= 0; i
< len
; ++i
) {
750 (*s
)[i
] = toupper((*s
)[i
]); // C-version defined in <ctype.h>
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
,
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"
773 if (command
== "LINSERT") {
774 std::string k
, t
, p
, v
;
775 std::cin
>> k
>> t
>> p
>> v
;
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
;
782 } else if (command
== "LPUSH") {
785 redis
.PushLeft(k
, v
);
786 } else if (command
== "RPUSH") {
789 redis
.PushRight(k
, v
);
790 } else if (command
== "LPOP") {
794 redis
.PopLeft(k
, &res
);
795 std::cout
<< res
<< std::endl
;
796 } else if (command
== "RPOP") {
800 redis
.PopRight(k
, &res
);
801 std::cout
<< res
<< std::endl
;
802 } else if (command
== "LREM") {
807 std::cin
>> k
>> amt
>> v
;
808 std::cout
<< redis
.Remove(k
, amt
, v
) << std::endl
;
809 } else if (command
== "LLEN") {
812 std::cout
<< redis
.Length(k
) << std::endl
;
813 } else if (command
== "LRANGE") {
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
);
821 std::cout
<< std::endl
;
822 } else if (command
== "LTRIM") {
825 std::cin
>> k
>> i
>> j
;
827 } else if (command
== "LSET") {
831 std::cin
>> k
>> idx
>> v
;
832 redis
.Set(k
, idx
, v
);
833 } else if (command
== "LINDEX") {
836 std::cin
>> k
>> idx
;
838 redis
.Index(k
, idx
, &res
);
839 std::cout
<< res
<< std::endl
;
840 } else if (command
== "PRINT") { // Added by Deon
844 } else if (command
== "QUIT") {
847 std::cout
<< "unknown command: " << command
<< std::endl
;
853 } // namespace rocksdb
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)
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) {
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
);
882 return RUN_ALL_TESTS();
889 int main(int argc
, char* argv
[]) {
890 fprintf(stderr
, "SKIPPED as redis is not supported in ROCKSDB_LITE\n");
894 #endif // !ROCKSDB_LITE