YugabyteDB (2.13.0.0-b42, bfc6a6643e7399ac8a0e81d06a3ee6d6571b33ab)

Coverage Report

Created: 2022-03-09 17:30

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