]> git.proxmox.com Git - ceph.git/blame - ceph/src/rocksdb/utilities/redis/redis_lists_test.cc
build: use dgit for download target
[ceph.git] / ceph / src / rocksdb / utilities / redis / redis_lists_test.cc
CommitLineData
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
27using namespace rocksdb;
28
29namespace rocksdb {
30
31class 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
41const std::string RedisListsTest::kDefaultDbName =
11fdf7f2 42 test::PerThreadDBPath("redis_lists_test");
7c673cae
FG
43Options RedisListsTest::options = Options();
44
45// operator== and operator<< are defined below for vectors (lists)
46// Needed for ASSERT_EQ
47
48namespace {
49void 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
59TEST_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
88TEST_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
117TEST_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
176TEST_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()
259TEST_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
343TEST_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
439TEST_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
531TEST_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
601TEST_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
692TEST_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
746namespace {
747void 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.
759int 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
861namespace {
862// Check for "want" argument in the argument list
863bool 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)
876int 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 889int 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