File Coverage

blib/lib/Protocol/Redis/Test.pm
Criterion Covered Total %
statement 311 330 94.2
branch 6 10 60.0
condition 10 15 66.6
subroutine 33 35 94.2
pod 1 1 100.0
total 361 391 92.3


line stmt bran cond sub pod time code
1             package Protocol::Redis::Test;
2              
3 1     1   138479 use strict;
  1         3  
  1         40  
4 1     1   5 use warnings;
  1         2  
  1         173  
5              
6             require Exporter;
7              
8             our @ISA = qw(Exporter);
9             our @EXPORT = qw(protocol_redis_ok);
10              
11 1     1   9 use Test::More;
  1         2  
  1         6  
12             require Carp;
13              
14             sub protocol_redis_ok {
15 3     3 1 226701 my ($redis_class, $api_version) = @_;
16              
17             subtest 'Protocol::Redis tests' => sub {
18 3     3   3776 plan tests => 3;
19              
20 1     1   1140 use_ok $redis_class;
  1     1   4  
  1     1   3  
  1         27  
  1         291  
  1         2  
  1         2  
  1         12  
  1         268  
  1         2  
  1         1  
  1         13  
  3         2664  
21              
22 3         1573 _test_unknown_version($redis_class);
23              
24 3 100 100     1452 if ($api_version == 1 or $api_version == 2) {
    50          
25 2         8 _apiv1_ok($redis_class);
26             }
27             elsif ($api_version == 3) {
28 1         3 _apiv3_ok($redis_class);
29             }
30             else {
31 0         0 Carp::croak(qq/Unknown Protocol::Redis API version $api_version/);
32             }
33 3         29 };
34             }
35              
36             sub _apiv1_ok {
37 2     2   5 my $redis_class = shift;
38              
39             subtest 'Protocol::Redis APIv1 ok' => sub {
40 2     2   2492 plan tests => 45;
41              
42 2         1803 _test_version_1($redis_class);
43             }
44 2         23 }
45              
46             sub _apiv3_ok {
47 1     1   2 my $redis_class = shift;
48              
49             subtest 'Protocol::Redis APIv3 ok' => sub {
50 1     1   2251 plan tests => 151;
51              
52 1         717 _test_version_3($redis_class);
53             }
54 1         7 }
55              
56             sub _test_version_1 {
57 2     2   4 my $redis_class = shift;
58              
59 2         13 my $redis = new_ok $redis_class, [api => 1];
60              
61 2         1048 can_ok $redis, 'parse', 'api', 'on_message', 'encode';
62              
63 2         1175 is $redis->api, 1, '$redis->api';
64              
65             # Parsing method tests
66 2         882 $redis->on_message(undef);
67 2         9 _parse_string_ok($redis);
68 2         2008 _parse_blob_ok($redis);
69 2         1351 _parse_array_ok($redis);
70 2         1479 _parse_null_bulk_ok($redis);
71              
72             # on_message works
73 2         725 _on_message_ok($redis);
74              
75             # Encoding method tests
76 2         7 _encode_ok($redis);
77 2         865 _encode_v1_ok($redis);
78             }
79              
80             sub _test_version_3 {
81 1     1   3 my $redis_class = shift;
82              
83 1         8 my $redis = new_ok $redis_class, [api => 3];
84              
85 1         412 can_ok $redis, 'parse', 'api', 'on_message', 'encode';
86              
87 1         452 is $redis->api, 3, '$redis->api';
88              
89             # Parsing method tests
90 1         350 $redis->on_message(undef);
91              
92             # simple string, error string, number
93 1         5 _parse_string_ok($redis);
94 1         736 _parse_null_ok($redis);
95 1         467 _parse_boolean_ok($redis);
96 1         502 _parse_double_ok($redis);
97 1         370 _parse_big_number_ok($redis);
98 1         1370 _parse_blob_ok($redis, '$'); # blob string
99 1         927 _parse_blob_ok($redis, '!'); # blob error
100 1         951 _parse_verbatim_ok($redis); # verbatim string
101 1         881 _parse_array_ok($redis, '*'); # array
102 1         1195 _parse_array_ok($redis, '~'); # set
103 1         1287 _parse_array_ok($redis, '>'); # push
104 1         1331 _parse_map_ok($redis);
105 1         1815 _parse_attribute_ok($redis);
106 1         2070 _parse_streamed_string_ok($redis);
107 1         1069 _parse_streamed_aggregate_ok($redis);
108              
109             # on_message works
110 1         4713 _on_message_ok($redis);
111              
112             # Encoding method tests
113 1         5 _encode_ok($redis);
114 1         601 _encode_v3_ok($redis);
115             }
116              
117             sub _parse_string_ok {
118 3     3   5 my $redis = shift;
119              
120             # Simple test
121 3         13 $redis->parse("+test\r\n");
122              
123 3         36 is_deeply $redis->get_message,
124             {type => '+', data => 'test'},
125             'simple message';
126              
127 3         2119 is_deeply $redis->get_message, undef, 'queue is empty';
128              
129 3         1548 $redis->parse(":1\r\n");
130              
131 3         11 is_deeply $redis->get_message, {type => ':', data => '1'},
132             'simple number';
133              
134             # Chunked message
135 3         1922 $redis->parse('-tes');
136 3         9 $redis->parse("t2\r\n");
137 3         8 is_deeply $redis->get_message,
138             {type => '-', data => 'test2'},
139             'chunked string';
140              
141             # Two chunked messages together
142 3         1950 $redis->parse("+test");
143 3         8 $redis->parse("1\r\n-test");
144 3         9 $redis->parse("2\r\n");
145 3         8 is_deeply
146             [$redis->get_message, $redis->get_message],
147             [{type => '+', data => 'test1'}, {type => '-', data => 'test2'}],
148             'first stick message';
149              
150             # Pipelined
151 3         3039 $redis->parse("+OK\r\n-ERROR\r\n");
152 3         10 is_deeply
153             [$redis->get_message, $redis->get_message],
154             [{type => '+', data => 'OK'}, {type => '-', data => 'ERROR'}],
155             'pipelined status messages';
156             }
157              
158             sub _parse_blob_ok {
159 4     4   16 my $redis = shift;
160 4   100     25 my $type = shift || '$';
161              
162             # Bulk message
163 4         25 $redis->parse("${type}4\r\ntest\r\n");
164 4         16 is_deeply $redis->get_message,
165             {type => $type, data => 'test'},
166             "simple $type blob message";
167              
168 4         5083 $redis->parse("${type}5\r\ntes");
169 4         20 $redis->parse("t2\r\n");
170 4         16 is_deeply $redis->get_message,
171             {type => $type, data => 'test2'},
172             "chunked $type blob message";
173              
174             # Two chunked bulk messages
175 4         4592 $redis->parse(join("\r\n", "${type}4", 'test', '+OK'));
176 4         65 $redis->parse("\r\n");
177 4         17 is_deeply $redis->get_message,
178             {type => $type, data => 'test'}, "two chunked $type blob messages";
179 4         3405 is_deeply $redis->get_message, {type => '+', data => 'OK'};
180              
181             # Pipelined bulk message
182 4         2998 $redis->parse(join("\r\n", ("${type}3", 'ok1'), ("${type}3", 'ok2'), ''));
183 4         17 is_deeply [$redis->get_message, $redis->get_message],
184             [{type => $type, data => 'ok1'}, {type => $type, data => 'ok2'}],
185             "pipelined $type blob message";
186              
187             # Binary test
188 4         4368 $redis->parse(join("\r\n", "${type}4", pack('C4', 0, 1, 2, 3), ''));
189              
190 4         15 is_deeply [unpack('C4', $redis->get_message->{data})],
191             [0, 1, 2, 3],
192             'binary data';
193              
194             # Blob message with newlines
195 4         3072 $redis->parse("${type}8\r\none\r\ntwo\r\n");
196              
197 4         12 is_deeply $redis->get_message,
198             {type => $type, data => "one\r\ntwo"},
199             "$type blob message with newlines";
200              
201             # Empty blob message
202 4         2980 $redis->parse("${type}0\r\n\r\n");
203              
204 4         15 is_deeply $redis->get_message,
205             {type => $type, data => ''},
206             "$type empty blob message";
207             }
208              
209             sub _parse_verbatim_ok {
210 1     1   3 my $redis = shift;
211              
212             # Verbatim message
213 1         8 $redis->parse("=8\r\ntxt:test\r\n");
214 1         4 is_deeply $redis->get_message,
215             {type => '=', data => 'test', format => 'txt'},
216             "simple verbatim string message";
217              
218 1         926 $redis->parse("=9\r\ntxt:tes");
219 1         6 $redis->parse("t2\r\n");
220 1         5 is_deeply $redis->get_message,
221             {type => '=', data => 'test2', format => 'txt'},
222             "chunked verbatim string message";
223              
224             # Two chunked verbatim messages
225 1         963 $redis->parse(join("\r\n", '=8', 'txt:test', '=6', 'mkd:OK'));
226 1         6 $redis->parse("\r\n");
227 1         7 is_deeply $redis->get_message,
228             {type => '=', data => 'test', format => 'txt'},
229             "two chunked verbatim string messages";
230 1         995 is_deeply $redis->get_message,
231             {type => '=', data => 'OK', format => 'mkd'};
232              
233             # Pipelined verbatim message
234 1         913 $redis->parse(join("\r\n", ('=7', 'txt:ok1'), ('=7', 'txt:ok2'), ''));
235 1         4 is_deeply [$redis->get_message, $redis->get_message], [
236             {type => '=', data => 'ok1', format => 'txt'},
237             {type => '=', data => 'ok2', format => 'txt'}
238             ],
239             "pipelined verbatim string message";
240              
241             # Binary test
242 1         1290 $redis->parse(join("\r\n", '=8', 'txt:' . pack('C4', 0, 1, 2, 3), ''));
243              
244 1         6 is_deeply [unpack('C4', $redis->get_message->{data})],
245             [0, 1, 2, 3],
246             'binary data';
247              
248             # Blob message with newlines
249 1         867 $redis->parse("=12\r\ntxt:one\r\ntwo\r\n");
250              
251 1         5 is_deeply $redis->get_message,
252             {type => '=', data => "one\r\ntwo", format => 'txt'},
253             "verbatim string message with newlines";
254              
255             # Empty blob message
256 1         843 $redis->parse("=4\r\ntxt:\r\n");
257              
258 1         5 is_deeply $redis->get_message,
259             {type => '=', data => '', format => 'txt'},
260             "empty verbatim string message";
261             }
262              
263             sub _parse_array_ok {
264 5     5   15 my $redis = shift;
265 5   100     82 my $type = shift || '*';
266              
267             # Array message
268 5         35 $redis->parse("${type}1\r\n\$4\r\ntest\r\n");
269              
270 5         18 is_deeply $redis->get_message,
271             {type => $type, data => [{type => '$', data => 'test'}]},
272             "simple $type array message";
273              
274             # Array message with multiple arguments
275 5         5586 $redis->parse("${type}3\r\n\$5\r\ntest1\r\n");
276 5         21 $redis->parse("\$5\r\ntest2\r\n");
277 5         19 $redis->parse("\$5\r\ntest3\r\n");
278              
279 5         23 is_deeply $redis->get_message, {
280             type => $type,
281             data => [
282             {type => '$', data => 'test1'},
283             {type => '$', data => 'test2'},
284             {type => '$', data => 'test3'}
285             ]
286             },
287             "multi argument $type array message";
288              
289             # Nested array
290 5         7573 $redis->parse("${type}2\r\n${type}2\r\n+test1\r\n+test2\r\n+test3\r\n");
291              
292 5         24 is_deeply $redis->get_message, {
293             type => $type,
294             data => [{
295             type => $type,
296             data => [
297             {type => '+', data => 'test1'},
298             {type => '+', data => 'test2'},
299             ]
300             },
301             {type => '+', data => 'test3'},
302             ]
303             },
304             "nested $type array message";
305              
306 5         9359 $redis->parse("${type}0\r\n");
307 5         21 is_deeply $redis->get_message,
308             {type => $type, data => []},
309             "$type array empty result";
310              
311             # Does it work?
312 5         4508 $redis->parse("\$4\r\ntest\r\n");
313 5         20 is_deeply $redis->get_message,
314             {type => '$', data => 'test'},
315             'everything still works';
316              
317             # Array message with status items
318 5         4169 $redis->parse(join("\r\n", ("${type}2", '+OK', '$4', 'test'), ''));
319 5         25 is_deeply $redis->get_message, {
320             type => $type,
321             data => [{type => '+', data => 'OK'}, {type => '$', data => 'test'}]
322             };
323              
324             # splitted array
325 5         6863 $redis->parse(join("\r\n", ("${type}1", '$4', 'test'), '+OK'));
326 5         20 $redis->parse("\r\n");
327              
328 5         17 is_deeply $redis->get_message,
329             {type => $type, data => [{type => '$', data => 'test'}]};
330 5         5441 is_deeply $redis->get_message, {type => '+', data => 'OK'};
331              
332             # Another splitted array message
333 5         3883 $redis->parse("${type}4\r\n\$0\r\n\r\n\$0\r\n");
334 5         19 $redis->parse("\r\n\$5\r\ntest2\r\n");
335 5         18 $redis->parse("\$5\r\ntest3\r");
336 5         18 $redis->parse("\n");
337 5         17 is_deeply $redis->get_message, {
338             type => $type,
339             data => [
340             {type => '$', data => ''},
341             {type => '$', data => ''},
342             {type => '$', data => 'test2'},
343             {type => '$', data => 'test3'}
344             ]
345             };
346              
347             # Complex string
348 5         8550 $redis->parse("${type}4\r\n");
349 5         27 $redis->parse("\$5\r\ntest1\r\n\$0\r\n\r\n:42\r\n+test3\r\n\$5\r\n123");
350 5         24 $redis->parse("45\r\n");
351 5         21 is_deeply $redis->get_message, {
352             type => $type,
353             data => [
354             {type => '$', data => 'test1'},
355             {type => '$', data => ''},
356             {type => ':', data => 42},
357             {type => '+', data => 'test3'}
358             ]
359             };
360 5         8926 is_deeply $redis->get_message, {
361             type => '$',
362             data => '12345',
363             };
364              
365             # pipelined array
366 5         3965 $redis->parse(
367             join("\r\n",
368             ("${type}2", '$3', 'ok1', '$3', 'ok2'),
369             ("${type}1", '$3', 'ok3'),
370             '')
371             );
372              
373 5         17 is_deeply $redis->get_message, {
374             type => $type,
375             data => [{type => '$', data => 'ok1'}, {type => '$', data => 'ok2'}]
376             };
377 5         6722 is_deeply $redis->get_message,
378             {type => $type, data => [{type => '$', data => 'ok3'}]};
379             }
380              
381             sub _parse_null_bulk_ok {
382 2     2   4 my $redis = shift;
383              
384             # Nil bulk message
385 2         9 $redis->parse("\$-1\r\n");
386              
387 2         5 my $message = $redis->get_message;
388 2   33     32 ok defined($message) && !defined($message->{data}), 'nil bulk message';
389              
390 2         655 $redis->parse("*-1\r\n");
391 2         5 $message = $redis->get_message;
392 2   33     13 ok defined($message) && !defined($message->{data}),
393             'multi-bulk nil result';
394             }
395              
396             sub _parse_null_ok {
397 1     1   2 my $redis = shift;
398              
399             # Null message
400 1         7 $redis->parse("_\r\n");
401              
402 1         3 is_deeply $redis->get_message,
403             {type => '_', data => undef},
404             'null message';
405             }
406              
407             sub _parse_boolean_ok {
408 1     1   2 my $redis = shift;
409              
410             # Boolean true message
411 1         5 $redis->parse("#t\r\n");
412              
413 1         3 is_deeply $redis->get_message,
414             {type => '#', data => !!1},
415             'boolean true message';
416              
417             # Boolean false message
418 1         542 $redis->parse("#f\r\n");
419              
420 1         3 is_deeply $redis->get_message,
421             {type => '#', data => !!0},
422             'boolean false message';
423             }
424              
425             sub _parse_double_ok {
426 1     1   2 my $redis = shift;
427              
428             # Double message
429 1         4 $redis->parse(",1.23\r\n");
430              
431 1         3 is_deeply $redis->get_message,
432             {type => ',', data => '1.23'},
433             'double message';
434              
435             # Integer double message
436 1         536 $redis->parse(",42\r\n");
437              
438 1         2 is_deeply $redis->get_message,
439             {type => ',', data => '42'},
440             'integer double message';
441              
442             # Exponent double messages
443 1         469 foreach my $double (qw(1e3 1.23e4 -4.2E-20 0.01e+7)) {
444 4         1440 $redis->parse(",$double\r\n");
445 4         8 is_deeply $redis->get_message,
446             {type => ',', data => $double},
447             "received $double as double with exponent";
448             }
449              
450             # Infinity message
451 1         443 $redis->parse(",inf\r\n");
452 1         3 my $message = $redis->get_message;
453 1         6 is $message->{type}, ',', 'double message type';
454 1         355 cmp_ok $message->{data}, '==', $message->{data} + 1, 'received infinity';
455 1         346 cmp_ok $message->{data}, '>', 0, 'received positive infinity';
456              
457             # Negative infinity message
458 1         341 $redis->parse(",-inf\r\n");
459 1         3 $message = $redis->get_message;
460 1         5 is $message->{type}, ',', 'double message type';
461 1         337 cmp_ok $message->{data}, '==', $message->{data} - 1, 'received infinity';
462 1         357 cmp_ok $message->{data}, '<', 0, 'received negative infinity';
463              
464             # NaN message
465 1         333 $redis->parse(",nan\r\n");
466 1         4 $message = $redis->get_message;
467 1         4 is $message->{type}, ',', 'double message type';
468 1         323 cmp_ok $message->{data}, '!=', $message->{data}, 'received NaN';
469              
470             # libc NaN compatibility
471 1         321 foreach my $nan (qw(-nan NaN NAN nan(chars))) {
472 4         1150 $redis->parse(",$nan\r\n");
473 4         11 my $message = $redis->get_message;
474 4         14 is $message->{type}, ',', 'double message type';
475             cmp_ok $message->{data}, '!=', $message->{data},
476 4         1521 "interpreted $nan as NaN double";
477             }
478             }
479              
480             sub _parse_big_number_ok {
481 1     1   2 my $redis = shift;
482              
483             # Big number message
484 1         5 $redis->parse("(3492890328409238509324850943850943825024385\r\n");
485 1         7 my $message = $redis->get_message;
486 1         11 is $message->{type}, '(', 'big number message type';
487 1         798 is $message->{data}, '3492890328409238509324850943850943825024385',
488             'big number data';
489              
490             # Negative big number message
491 1         833 $redis->parse("(-3492890328409238509324850943850943825024385\r\n");
492 1         6 $message = $redis->get_message;
493 1         9 is $message->{type}, '(', 'big number message type';
494 1         662 is $message->{data}, '-3492890328409238509324850943850943825024385',
495             'negative big number data';
496              
497             # Simple big number message
498 1         699 $redis->parse("(0\r\n");
499              
500 1         5 is_deeply $redis->get_message,
501             {type => '(', data => '0'},
502             'simple big number';
503             }
504              
505             sub _parse_map_ok {
506 1     1   3 my $redis = shift;
507              
508             # Map message
509 1         8 $redis->parse("%1\r\n+one\r\n+two\r\n");
510              
511 1         5 is_deeply $redis->get_message,
512             {type => '%', data => {one => {type => '+', data => 'two'}}},
513             'simple map message';
514              
515             # Empty map message
516 1         1415 $redis->parse("%0\r\n");
517              
518 1         6 is_deeply $redis->get_message,
519             {type => '%', data => {}},
520             'empty map message';
521              
522             # Complex map message
523 1         1151 $redis->parse("%3\r\n\$5\r\ntest1\r\n");
524 1         5 $redis->parse(":1\r\n+test2\r\n,0.01\r\n:3\r\n_\r\n");
525              
526 1         6 is_deeply $redis->get_message, {
527             type => '%',
528             data => {
529             test1 => {type => ':', data => '1'},
530             test2 => {type => ',', data => '0.01'},
531             '3' => {type => '_', data => undef},
532             }
533             },
534             'complex map message';
535             }
536              
537             sub _parse_set_ok {
538 0     0   0 my $redis = shift;
539              
540             # Set message
541 0         0 $redis->parse("~5\r\n+test1\r\n\$5\r\ntest2\r\n:42\r\n#t\r\n_\r\n");
542              
543 0         0 is_deeply $redis->get_message, {
544             type => '~',
545             data => [
546             {type => '+', data => 'test1'},
547             {type => '$', data => 'test2'},
548             {type => ':', data => '42'},
549             {type => '#', data => !!1},
550             {type => '_', data => undef},
551             ]
552             },
553             'set message';
554              
555             # Empty set message
556 0         0 $redis->parse("~0\r\n");
557              
558 0         0 is_deeply $redis->get_message,
559             {type => '~', data => []},
560             'empty set message';
561              
562             # Set message with duplicates
563 0         0 $redis->parse("~3\r\n+test1\r\n+test2\r\n+test1\r\n");
564              
565 0         0 is_deeply $redis->get_message, {
566             type => '~',
567             data => [
568             {type => '+', data => 'test1'},
569             {type => '+', data => 'test2'},
570             {type => '+', data => 'test1'},
571             ]
572             },
573             'set message with duplicates';
574              
575             # Set message with non-duplicate but equal strings
576 0         0 $redis->parse("~8\r\n+test1\r\n\$5\r\ntest1\r\n");
577 0         0 $redis->parse(":1\r\n,1\r\n#t\r\n");
578 0         0 $redis->parse("_\r\n+\r\n\$0\r\n\r\n");
579              
580 0         0 is_deeply $redis->get_message, {
581             type => '~',
582             data => [
583             {type => '+', data => 'test1'},
584             {type => '$', data => 'test1'},
585             {type => ':', data => '1'},
586             {type => ',', data => '1'},
587             {type => '#', data => !!1},
588             {type => '_', data => undef},
589             {type => '+', data => ''},
590             {type => '$', data => ''},
591             ]
592             },
593             'set message with non-duplicate but equal strings';
594             }
595              
596             sub _parse_attribute_ok {
597 1     1   4 my $redis = shift;
598              
599             # Message with attributes
600 1         6 $redis->parse("|1\r\n+total\r\n:5\r\n*2\r\n");
601 1         5 $redis->parse("\$5\r\ntest1\r\n\$5\r\ntest2\r\n");
602              
603 1         6 is_deeply $redis->get_message, {
604             type => '*',
605             data =>
606             [{type => '$', data => 'test1'}, {type => '$', data => 'test2'},],
607             attributes => {
608             total => {type => ':', data => '5'},
609             }
610             },
611             'message with attributes';
612              
613             # Multiple attributes
614 1         2009 $redis->parse("|3\r\n+x\r\n,0.5\r\n+y\r\n,-3.4\r\n+z\r\n:42\r\n+OK\r\n");
615              
616 1         7 is_deeply $redis->get_message, {
617             type => '+',
618             data => 'OK',
619             attributes => {
620             x => {type => ',', data => '0.5'},
621             y => {type => ',', data => '-3.4'},
622             z => {type => ':', data => '42'},
623             }
624             },
625             'message with multiple attributes';
626              
627             # Empty attributes
628 1         1879 $redis->parse("|0\r\n-ERR no response\r\n");
629              
630 1         6 is_deeply $redis->get_message,
631             {type => '-', data => 'ERR no response', attributes => {}},
632             'message with empty attributes';
633              
634             # Embedded attributes
635 1         1142 $redis->parse("*2\r\n|2\r\n+min\r\n:0\r\n+max\r\n:10\r\n:5\r\n");
636 1         6 $redis->parse("|2\r\n+min\r\n:4\r\n+max\r\n:8\r\n:7\r\n");
637              
638 1         6 is_deeply $redis->get_message, {
639             type => '*',
640             data => [{
641             type => ':',
642             data => '5',
643             attributes => {
644             min => {type => ':', data => '0'},
645             max => {type => ':', data => '10'},
646             }
647             }, {
648             type => ':',
649             data => '7',
650             attributes => {
651             min => {type => ':', data => '4'},
652             max => {type => ':', data => '8'},
653             }
654             },
655             ]
656             },
657             'message with embedded attributes';
658              
659             # Aggregate attributes
660 1         3248 $redis->parse("|1\r\n+array\r\n*2\r\n+test1\r\n+test2\r\n+test\r\n");
661              
662 1         7 is_deeply $redis->get_message, {
663             type => '+',
664             data => 'test',
665             attributes => {
666             array => {
667             type => '*',
668             data => [
669             {type => '+', data => 'test1'},
670             {type => '+', data => 'test2'},
671             ]
672             }
673             }
674             },
675             'message with aggregate attribute values';
676             }
677              
678             sub _parse_push_ok {
679 0     0   0 my $redis = shift;
680              
681             # Simple push message
682 0         0 $redis->parse(">2\r\n\$5\r\ntest1\r\n:42\r\n");
683              
684 0         0 is_deeply $redis->get_message, {
685             type => '>',
686             data => [{type => '$', data => 'test1'}, {type => ':', data => '42'},]
687             },
688             'simple push message';
689              
690             # Empty push message
691 0         0 $redis->parse(">0\r\n");
692              
693 0         0 is_deeply $redis->get_message,
694             {type => '>', data => []},
695             'empty push message';
696             }
697              
698             sub _parse_streamed_string_ok {
699 1     1   4 my $redis = shift;
700              
701             # Simple streamed string
702 1         8 $redis->parse("\$?\r\n;4\r\ntest\r\n;2\r\nxy\r\n;0\r\n");
703              
704 1         5 is_deeply $redis->get_message,
705             {type => '$', data => 'testxy'},
706             'simple streamed string';
707              
708             # Empty streamed string
709 1         987 $redis->parse("\$?\r\n;0\r\n");
710              
711 1         5 is_deeply $redis->get_message,
712             {type => '$', data => ''},
713             'empty streamed string';
714              
715             # Charwise streamed string
716 1         1009 $redis->parse("\$?\r\n");
717 1         10 $redis->parse(";1\r\n$_\r\n") for 'a' .. 'z';
718 1         6 $redis->parse(";0\r\n");
719              
720 1         5 is_deeply $redis->get_message,
721             {type => '$', data => join('', 'a' .. 'z')},
722             'string streamed by character';
723             }
724              
725             sub _parse_streamed_aggregate_ok {
726 1     1   4 my $redis = shift;
727 1   50     10 my $type = shift || '*';
728              
729             # Simple streamed aggregate
730 1         9 $redis->parse("${type}?\r\n+test1\r\n+test2\r\n.\r\n");
731              
732 1 50       6 if ($type eq '%') {
733 0         0 is_deeply $redis->get_message, {
734             type => $type,
735             data => {
736             test1 => {type => '+', data => 'test2'},
737             }
738             },
739             "simple $type streamed aggregate message";
740             }
741             else {
742 1         6 is_deeply $redis->get_message, {
743             type => $type,
744             data => [
745             {type => '+', data => 'test1'},
746             {type => '+', data => 'test2'},
747             ]
748             },
749             "simple $type streamed aggregate message";
750             }
751              
752             # Empty streamed aggregate
753 1         1843 $redis->parse("${type}?\r\n.\r\n");
754              
755 1 50       7 is_deeply $redis->get_message,
756             {type => $type, data => ($type eq '%' ? {} : [])},
757             "empty $type streamed aggregate message";
758              
759             # Complex streamed aggregate
760 1         1141 $redis->parse("${type}?\r\n");
761 1         5 $redis->parse("\$?\r\n;4\r\ntest\r\n;1\r\n1\r\n;0\r\n");
762 1         11 $redis->parse("\$5\r\ntest$_\r\n") for 2 .. 9;
763 1         7 $redis->parse("*?\r\n:10\r\n,11\r\n.\r\n");
764 1         8 $redis->parse(".\r\n");
765              
766 1 50       6 if ($type eq '%') {
767 0         0 is_deeply $redis->get_message, {
768             type => $type,
769             data => {
770             test1 => {type => '$', data => 'test2'},
771             test3 => {type => '$', data => 'test4'},
772             test5 => {type => '$', data => 'test6'},
773             test7 => {type => '$', data => 'test8'},
774             test9 => {
775             type => '*',
776             data => [
777             {type => ':', data => '10'},
778             {type => ',', data => '11'},
779             ]
780             },
781             }
782             },
783             "complex $type streamed aggregate message";
784             }
785             else {
786             is_deeply $redis->get_message, {
787             type => $type,
788             data => [
789             {type => '$', data => 'test1'},
790 1         5 (map { +{type => '$', data => "test$_"} } 2 .. 9), {
  8         74  
791             type => '*',
792             data => [
793             {type => ':', data => '10'},
794             {type => ',', data => '11'},
795             ]
796             },
797             ]
798             },
799             "complex $type streamed aggregate message";
800             }
801             }
802              
803             sub _on_message_ok {
804 3     3   8 my $redis = shift;
805              
806             # Parsing with cb
807 3         9 my $r = [];
808             $redis->on_message(
809             sub {
810 12     12   22 my ($redis, $message) = @_;
811              
812 12         29 push @$r, $message;
813             }
814 3         34 );
815              
816 3         14 $redis->parse("+foo\r\n");
817 3         10 $redis->parse("\$3\r\nbar\r\n");
818              
819 3         25 is_deeply $r,
820             [{type => '+', data => 'foo'}, {type => '$', data => 'bar'}],
821             'parsing with callback';
822              
823 3         2897 $r = [];
824 3         20 $redis->parse(join("\r\n", ('+foo'), ('$3', 'bar'), ''));
825              
826 3         23 is_deeply $r,
827             [{type => '+', data => 'foo'}, {type => '$', data => 'bar'}],
828             'pipelined parsing with callback';
829              
830 3         3064 $redis->on_message(undef);
831             }
832              
833             sub _encode_ok {
834 3     3   7 my $redis = shift;
835              
836             # Encode message
837 3         22 is $redis->encode({type => '+', data => 'OK'}), "+OK\r\n",
838             'encode status';
839 3         1401 is $redis->encode({type => '-', data => 'ERROR'}), "-ERROR\r\n",
840             'encode error';
841 3         1309 is $redis->encode({type => ':', data => '5'}), ":5\r\n", 'encode integer';
842              
843             # Encode bulk message
844 3         1441 is $redis->encode({type => '$', data => 'test'}), "\$4\r\ntest\r\n",
845             'encode bulk';
846 3         1529 is $redis->encode({type => '$', data => "\0\r\n"}), "\$3\r\n\0\r\n\r\n",
847             'encode binary bulk';
848              
849             # Encode multi-bulk
850 3         1286 is $redis->encode({type => '*', data => [{type => '$', data => 'test'}]}),
851             join("\r\n", ('*1', '$4', 'test'), ''),
852             'encode multi-bulk';
853              
854 3         1323 is $redis->encode({
855             type => '*',
856             data => [
857             {type => '$', data => 'test1'}, {type => '$', data => 'test2'}
858             ]
859             }
860             ),
861             join("\r\n", ('*2', '$5', 'test1', '$5', 'test2'), ''),
862             'encode multi-bulk';
863              
864 3         1372 is $redis->encode({type => '*', data => []}), "\*0\r\n",
865             'encode empty multi-bulk';
866             }
867              
868             sub _encode_v1_ok {
869 2     2   4 my $redis = shift;
870              
871 2         11 is $redis->encode({type => '$', data => undef}), "\$-1\r\n",
872             'encode nil bulk';
873              
874 2         1033 is $redis->encode({type => '*', data => undef}), "\*-1\r\n",
875             'encode nil multi-bulk';
876              
877 2         734 is $redis->encode({
878             type => '*',
879             data => [
880             {type => '$', data => 'foo'},
881             {type => '$', data => undef},
882             {type => '$', data => 'bar'}
883             ]
884             }
885             ),
886             join("\r\n", ('*3', '$3', 'foo', '$-1', '$3', 'bar'), ''),
887             'encode multi-bulk with nil element';
888             }
889              
890             sub _encode_v3_ok {
891 1     1   3 my $redis = shift;
892              
893             # Encode simple RESP3 types
894 1         10 is $redis->encode({type => '_', data => undef}), "_\r\n", 'encode null';
895              
896 1         758 is $redis->encode({type => '#', data => 1}), "#t\r\n",
897             'encode boolean true';
898 1         584 is $redis->encode({type => '#', data => 0}), "#f\r\n",
899             'encode boolean false';
900              
901 1         551 is $redis->encode({type => ',', data => '1.3'}), ",1.3\r\n",
902             'encode double';
903 1         674 is $redis->encode({type => ',', data => '-1.2e-5'}), ",-1.2e-5\r\n",
904             'encode negative double with exponent';
905 1         600 is $redis->encode({type => ',', data => '10'}), ",10\r\n",
906             'encode integer as double';
907 1         484 is $redis->encode({type => ',', data => '0'}), ",0\r\n",
908             'encode zero as double';
909 1         527 is $redis->encode({type => ',', data => 9**9**9}), ",inf\r\n",
910             'encode inf';
911 1         494 is $redis->encode({type => ',', data => -9**9**9}), ",-inf\r\n",
912             'encode negative inf';
913 1         483 is $redis->encode({type => ',', data => -sin 9**9**9}), ",nan\r\n",
914             'encode nan';
915              
916 1         491 is $redis->encode({
917             type => '(',
918             data => '3492890328409238509324850943850943825024385'
919             }
920             ),
921             "(3492890328409238509324850943850943825024385\r\n",
922             'encode big number';
923 1         501 require Math::BigInt;
924 1         14 is $redis->encode({
925             type => '(',
926             data => Math::BigInt->new(
927             '-3492890328409238509324850943850943825024385')
928             }
929             ),
930             "(-3492890328409238509324850943850943825024385\r\n",
931             'encode bigint as big number';
932 1         500 is $redis->encode({type => '(', data => '0'}), "(0\r\n",
933             'encode zero as big number';
934              
935             # Encode blob RESP3 types
936 1         488 is $redis->encode({type => '!', data => 'SYNTAX'}),
937             "!6\r\nSYNTAX\r\n", 'encode blob error';
938 1         605 is $redis->encode({type => '!', data => "\0\r\n"}),
939             "!3\r\n\0\r\n\r\n", 'encode binary blob error';
940              
941 1         457 is $redis->encode({type => '=', data => '"foo"'}),
942             "=9\r\ntxt:\"foo\"\r\n", 'encode verbatim string';
943 1         423 is $redis->encode({type => '=', data => '"bar"', format => 'mkd'}),
944             "=9\r\nmkd:\"bar\"\r\n", 'encode verbatim string with custom format';
945 1         436 is $redis->encode({type => '=', data => "\0\r\n"}),
946             "=7\r\ntxt:\0\r\n\r\n", 'encode binary verbatim string';
947              
948             # Encode aggregate RESP3 types
949 1         445 is $redis->encode(
950             {type => '%', data => {foo => {type => '+', data => 'bar'}}}),
951             join("\r\n", '%1', '$3', 'foo', '+bar', ''),
952             'encode map';
953 1         525 is $redis->encode({
954             type => '~',
955             data => [{type => ':', data => 5}, {type => '+', data => 'test'}]
956             }
957             ),
958             join("\r\n", '~2', ':5', '+test', ''),
959             'encode set';
960 1         486 is $redis->encode({
961             type => '>',
962             data =>
963             [{type => '+', data => 'test'}, {type => ',', data => '4.2'}]
964             }
965             ),
966             join("\r\n", '>2', '+test', ',4.2', ''),
967             'encode push';
968              
969             # Encode attributes
970 1         627 is $redis->encode({
971             type => '+',
972             data => 'test',
973             attributes => {foo => {type => '+', data => 'bar'}}
974             }
975             ),
976             join("\r\n", '|1', '$3', 'foo', '+bar', '+test', ''),
977             'encode simple string with attributes';
978 1         613 is $redis->encode({
979             type => '*',
980             data => [{type => '_', data => undef}],
981             attributes => {test => {type => '#', data => 1}}
982             }
983             ),
984             join("\r\n", '|1', '$4', 'test', '#t', '*1', '_', ''),
985             'encode array with attributes';
986 1         625 is $redis->encode({
987             type => '~',
988             data => [{
989             type => ',',
990             data => '-5.5',
991             attributes => {precision => {type => ':', data => 1}}
992             }
993             ]
994             }
995             ),
996             join("\r\n", '~1', '|1', '$9', 'precision', ':1', ',-5.5', ''),
997             'encode set with embedded attributes';
998 1         557 is $redis->encode({
999             type => '+',
1000             data => 'test',
1001             attributes => {
1002             array => {
1003             type => '*',
1004             data =>
1005             [{type => ':', data => 1}, {type => ':', data => 2}]
1006             }
1007             }
1008             }
1009             ),
1010             join("\r\n", '|1', '$5', 'array', '*2', ':1', ':2', '+test', ''),
1011             'encode array attributes';
1012             }
1013              
1014             sub _test_unknown_version {
1015 3     3   7 my $redis_class = shift;
1016              
1017 3         6 eval { $redis_class->new(api => 0); };
  3         17  
1018 3         24 ok($@, 'unknown version raises an exception');
1019             }
1020              
1021             1;
1022             __END__