root/branches/binary/server/t/binary.t @ 764

Revision 764, 11.4 kB (checked in by dormando, 20 months ago)

Updating perl tests. Now works a little again. Fails 'Simple set/get'

  • Property svn:executable set to *
Line 
1#!/usr/bin/perl
2
3use strict;
4use warnings;
5
6use Test::More 'no_plan';
7
8# Based almost 100% off testClient.py which is Copyright (c) 2007  Dustin Sallings <dustin@spy.net>
9
10# Command constants
11use constant CMD_GET     => 0;
12use constant CMD_SET     => 1;
13use constant CMD_ADD     => 2;
14use constant CMD_REPLACE => 3;
15use constant CMD_DELETE  => 4;
16use constant CMD_INCR    => 5;
17use constant CMD_DECR    => 6;
18use constant CMD_QUIT    => 7;
19use constant CMD_FLUSH   => 8;
20use constant CMD_GETQ    => 9;
21use constant CMD_NOOP    => 10;
22use constant CMD_VERSION => 11;
23
24use constant CMD_GETS    => 50;
25use constant CMD_CAS     => 51;
26
27# Flags, expiration
28use constant SET_PKT_FMT => "NN";
29
30# Flags, expiration, id
31use constant CAS_PKT_FMT => "NNNN";
32
33# How long until the deletion takes effect.
34use constant DEL_PKT_FMT => "N";
35
36# amount, initial value, expiration
37use constant INCRDECR_PKT_FMT => "NNNNN";
38
39use constant REQ_MAGIC_BYTE => 0x80;
40use constant RES_MAGIC_BYTE => 0x80;
41
42use constant PKT_FMT => "CCSCxxxNN";
43
44#min recv packet size
45use constant MIN_RECV_PACKET => length(pack(PKT_FMT));
46
47my $mc = MC::Client->new;
48my $check = sub {
49        my ($key, $orig_flags, $orig_value) = @_;
50        my ($flags, $value) = $mc->get($key);
51        is($flags, $orig_flags, "Flags is set properly");
52        is($value, $orig_value, "Value is set properly");
53};
54
55my $set = sub {
56        my ($key, $exp, $orig_flags, $orig_value) = @_;
57        $mc->set($key, $exp, $orig_flags, $orig_value);
58        $check->($key, $orig_flags, $orig_value);
59};
60
61my $empty = sub {
62        my $key = shift;
63        my $rv =()= eval { $mc->get($key) };
64        is($rv, 0, "Didn't get a result from get");
65        ok($@->not_found, "We got a not found error when we expected one");
66};
67
68my $delete = sub {
69        my ($key, $when) = @_;
70        $mc->delete($key, $when);
71        $empty->($key);
72};
73
74diag "Flushing...";
75$mc->flush;
76
77{
78        diag "Test Version";
79        my $v = $mc->version;
80        ok(defined $v && length($v), "Proper version: $v");
81}
82
83diag "Noop";
84$mc->noop;
85       
86diag "Simple set/get";
87$set->('x', 5, 19, "somevalue");
88
89diag "Delete";
90$delete->('x');
91
92diag "Flush";
93$set->('x', 5, 19, "somevaluex");
94$set->('y', 5, 17, "somevaluey");
95$mc->flush;
96$empty->('x');
97$empty->('y');
98
99{
100        diag "Reservation delete";
101        $set->('y', 5, 19, "someothervalue");
102        $delete->('y', 1);
103        my $rv =()= eval { $mc->add('y', 5, 19, "yetanothervalue") };
104        is($rv, 0, "Add didn't return anything");
105        ok($@->exists, "We got an exists error like we expected");
106        sleep 2;
107        $mc->add('y', 5, 19, "wibblevalue");
108}
109
110{
111        diag "Add";
112        $empty->('i');
113        $mc->add('i', 5, 19, "ex");
114        $check->('i', 19, "ex");
115
116        my $rv =()= eval { $mc->add('i', 5, 19, "ex2") };
117        is($rv, 0, "Add didn't return anything");
118        ok($@->exists, "Expected exists error received");
119       
120        $check->('i', 19, "ex");
121}
122
123{
124        diag "Replace";
125        $empty->('j');
126
127        my $rv =()= eval { $mc->replace('j', 5, 19, "ex") };
128        is($rv, 0, "Replace didn't return anything");
129        ok($@->not_found, "Expected not_found error received");
130
131        $empty->('j');
132       
133        $mc->add('j', 5, 14, "ex2");
134        $check->('j', 14, "ex2");
135       
136        $mc->replace('j', 5, 24, "ex3");
137        $check->('j', 24, "ex3");
138}
139
140{
141        diag "MultiGet";
142        $mc->add('xx', 5, 1, "ex");
143        $mc->add('wye', 5, 2, "why");
144        my $rv = $mc->getMulti(qw(xx wye zed));
145
146        is_deeply([1, 'ex'], $rv->{xx}, "X is correct");
147        is_deeply([2, 'why'], $rv->{wye}, "Y is correct");
148        is(keys(%$rv), 2, "Got only two answers like we expect");
149}
150
151diag "Test increment";
152$mc->flush;
153is($mc->incr("x"), 0, "First incr call is zero");
154is($mc->incr("x"), 1, "Second incr call is one");
155is($mc->incr("x", 211), 212, "Adding 211 gives you 212");
156is($mc->incr("x", 2**33), 8589934804, "Blast the 32bit border");
157
158diag "Test decrement";
159$mc->flush;
160is($mc->incr("x", undef, 5), 5, "Initial value");
161is($mc->decr("x"), 4, "Decrease by one");
162is($mc->decr("x", 211), 0, "Floor is zero");
163
164<<EOT;
165    def testIncrDoesntExistNoCreate(self):
166        """Testing incr when a value doesn't exist (and not creating)."""
167        try:
168            self.mc.incr("x", exp=-1)
169            self.fail("Expected failure to increment non-existent key")
170        except MemcachedError, e:
171            self.assertEquals(memcacheConstants.ERR_NOT_FOUND, e.status)
172        self.assertNotExists("x")
173
174    def testIncrDoesntExistCreate(self):
175        """Testing incr when a value doesn't exist (and we make a new one)"""
176        self.assertNotExists("x")
177        self.assertEquals(19, self.mc.incr("x", init=19))
178
179    def testDecrDoesntExistNoCreate(self):
180        """Testing decr when a value doesn't exist (and not creating)."""
181        try:
182            self.mc.decr("x", exp=-1)
183            self.fail("Expected failiure to decrement non-existent key.")
184        except MemcachedError, e:
185            self.assertEquals(memcacheConstants.ERR_NOT_FOUND, e.status)
186        self.assertNotExists("x")
187
188    def testDecrDoesntExistCreate(self):
189        """Testing decr when a value doesn't exist (and we make a new one)"""
190        self.assertNotExists("x")
191        self.assertEquals(19, self.mc.decr("x", init=19))
192EOT
193
194{
195        diag "CAS";
196        $mc->flush;
197       
198        {
199                my $rv =()= eval { $mc->cas("x", 5, 19, 0x7FFFFFFFFF, "bad value") };
200                is($rv, 0, "Empty return on expected failure");
201                ok($@->not_found, "Error was 'not found' as expected");
202        }
203
204        $mc->add("x", 5, 19, "original value");
205
206        my ($flags, $i, $val) = $mc->gets("x");
207        is($val, "original value", "->gets returned proper value");
208       
209        {
210                my $rv =()= eval { $mc->cas("x", 5, 19, $i+1, "broken value") };
211                is($rv, 0, "Empty return on expected failure (1)");
212                ok($@->exists, "Expected error state of 'exists' (1)");
213        }
214
215        $mc->cas("x", 5, 19, $i, "new value");
216
217        my ($newflags, $newi, $newval) = $mc->gets("x");
218        is($newval, "new value", "CAS properly overwrote value");
219
220        {
221                my $rv =()= eval { $mc->cas("x", 5, 19, $i, "replay value") };
222                is($rv, 0, "Empty return on expected failure (2)");
223                ok($@->exists, "Expected error state of 'exists' (2)");
224        }
225
226        (undef, undef, my $newval2) = $mc->gets("x");
227        is($newval2, "new value", "CAS replay didn't overwrite value");
228}
229
230$mc->flush;
231$mc->close;
232
233
234package MC::Client;
235
236use strict;
237use warnings;
238
239use fields qw(socket);
240
241use IO::Socket::INET;
242
243sub new {
244        my $self = shift;
245
246        my $host = shift || '127.0.0.1';
247        my $port = shift || 11211;
248
249        my $sock = IO::Socket::INET->new(PeerHost => $host, PeerPort => $port);
250
251        unless ($sock) {
252                warn "Unable to contact memcached.";
253                return;
254        }
255
256        $self = fields::new($self);
257
258        $self->{socket} = $sock;
259
260        return $self;
261}
262
263sub close {
264        my $self = shift;
265        return $self->{socket}->close(@_);
266}
267
268sub _sendCmd {
269        my $self = shift;
270        die "Not enough args to _sendCmd" unless @_ >= 4;
271        my ($cmd, $key, $val, $opaque, $extraHeader) = @_;
272
273        $extraHeader = '' unless defined $extraHeader;
274
275        my $keylen = length($key);
276        my $vallen = length($val);
277        my $extralen = length($extraHeader);
278
279        my $msg = pack(::PKT_FMT, ::REQ_MAGIC_BYTE, $cmd, $keylen, $extralen,
280                    $keylen + $vallen + $extralen, $opaque);
281        return $self->{socket}->send($msg . $extraHeader . $key . $val);
282}
283
284sub _handleSingleResponse {
285        my $self = shift;
286        my $myopaque = shift;
287
288        $self->{socket}->recv(my $response, ::MIN_RECV_PACKET);
289
290        Test::More::is(length($response), ::MIN_RECV_PACKET, "Expected read length");
291
292        my ($magic, $cmd, $errcode, $extralen, $remaining,
293        $opaque) = unpack(::PKT_FMT, $response);
294
295        Test::More::is($magic, ::RES_MAGIC_BYTE, "Got proper magic");
296
297        return ($opaque, "")
298                if $remaining == 0;
299
300        $self->{socket}->recv(my $rv, $remaining);
301
302        if (defined $myopaque) {
303                Test::More::is($opaque, $myopaque, "Expected opaque");
304        } else {
305                Test::More::pass("Implicit pass since myopaque is undefined");
306        }
307
308        if ($errcode) {
309                die MC::Error->new($errcode, $rv);
310        }
311
312        return ($opaque, $rv);
313}
314
315sub _doCmd {
316        my $self = shift;
317        die unless @_ >= 3;
318        my ($cmd, $key, $val, $extraHeader) = @_;
319
320        $extraHeader = '' unless defined $extraHeader;
321
322        my $opaque = int(rand(2**32));
323
324        $self->_sendCmd($cmd, $key, $val, $opaque, $extraHeader);
325        (undef, my $rv) = $self->_handleSingleResponse($opaque);
326        return $rv;
327}
328
329sub __parseGet {
330        my $self = shift;
331        my $rv = shift; # currently contains 4 bytes of 'flag' followed by value
332        my $flag = substr $rv, 0, 4, ''; # Now $flag contains flags, $rv contains value
333        return unpack("N", $flag), $rv;
334}
335
336sub get {
337        my $self = shift;
338        my $key = shift;
339        my $parts = $self->_doCmd(::CMD_GET, $key, '');
340        return $self->__parseGet($parts);
341}
342
343sub _mutate {
344        my $self = shift;
345        my ($cmd, $key, $exp, $flags, $val) = @_;
346
347        return $self->_doCmd($cmd, $key, $val, pack(::SET_PKT_FMT, $flags, $exp));
348}
349
350sub set {
351        my $self = shift;
352        my ($key, $exp, $flags, $val) = @_;
353
354        return $self->_mutate(::CMD_SET, $key, $exp, $flags, $val);
355}
356
357sub __incrdecr {
358        my $self = shift;
359        my ($cmd, $key, $amt, $init, $exp) = @_;
360
361        my $amt_hi = int($amt / 2 ** 32);
362        my $amt_lo = int($amt % 2 ** 32);
363
364        my $init_hi = int($init / 2 ** 32);
365        my $init_lo = int($init % 2 ** 32);
366
367        return $self->_doCmd($cmd, $key, '', pack(::INCRDECR_PKT_FMT, $amt_hi, $amt_lo, $init_hi, $init_lo, $exp));
368}
369
370sub incr {
371        my $self = shift;
372        my ($key, $amt, $init, $exp) = @_;
373        $amt = 1 unless defined $amt;
374        $init = 0 unless defined $init;
375        $exp = 0 unless defined $exp;
376
377        return $self->__incrdecr(::CMD_INCR, $key, $amt, $init, $exp);
378}
379
380sub decr {
381        my $self = shift;
382        my ($key, $amt, $init, $exp) = @_;
383        $amt = 1 unless defined $amt;
384        $init = 0 unless defined $init;
385        $exp = 0 unless defined $exp;
386
387        return $self->__incrdecr(::CMD_INCR, $key, 0 - $amt, $init, $exp);
388}
389
390sub add {
391        my $self = shift;
392        my ($key, $exp, $flags, $val) = @_;
393        return $self->_mutate(::CMD_ADD, $key, $exp, $flags, $val);
394}
395sub replace {
396        my $self = shift;
397        my ($key, $exp, $flags, $val) = @_;
398        return $self->_mutate(::CMD_REPLACE, $key, $exp, $flags, $val);
399}
400
401sub getMulti {
402        my $self = shift;
403        my @keys = @_;
404
405        for (my $i = 0; $i < @keys; $i++) {
406                $self->_sendCmd(::CMD_GETQ, $keys[$i], '', $i);
407        }
408
409        my $terminal = @keys + 10;
410        $self->_sendCmd(::CMD_NOOP, '', '', $terminal);
411
412        my %return;
413
414        while (1) {
415                my ($opaque, $data) = $self->_handleSingleResponse;
416                last if $opaque == $terminal;
417
418                $return{$keys[$opaque]} = [$self->__parseGet($data)];
419        }
420        return %return if wantarray;
421        return \%return;
422}
423
424sub gets {
425        my $self = shift;
426        my $key = shift;
427
428        my $data = $self->_doCmd(::CMD_GETS, $key, '');
429        my $header = substr $data, 0, 12, '';
430        my ($flags, $ident_hi, $ident_lo) = unpack "NNN", $header;
431        my $ident = ($ident_hi * 2 ** 32) + $ident_lo;
432
433        return $flags, $ident, $data;
434}
435
436sub cas {
437        my $self = shift;
438        my ($key, $exp, $flags, $oldVal, $val) = @_;
439
440        my $oldVal_hi = int($oldVal / 2 ** 32);
441        my $oldVal_lo = int($oldVal % 2 ** 32);
442
443        return $self->_doCmd(::CMD_CAS, $key, $val, pack(::CAS_PKT_FMT, $flags, $exp, $oldVal_hi, $oldVal_lo));
444}
445
446sub noop {
447        my $self = shift;
448        return $self->_doCmd(::CMD_NOOP, '', '');
449}
450
451sub delete {
452        my $self = shift;
453        my ($key, $when) = @_;
454        $when = 0 unless defined $when;
455
456        return $self->_doCmd(::CMD_DELETE, $key, '', pack(::DEL_PKT_FMT, $when));
457}
458
459sub version {
460        my $self = shift;
461        return $self->_doCmd(::CMD_VERSION, '', '');
462}
463
464sub flush {
465        my $self = shift;
466        return $self->_doCmd(::CMD_FLUSH, '', '');
467}
468
469package MC::Error;
470
471use strict;
472use warnings;
473
474use constant ERR_UNKNOWN_CMD => 0x81;
475use constant ERR_NOT_FOUND   => 0x1;
476use constant ERR_EXISTS      => 0x2;
477
478use overload '""' => sub {
479        my $self = shift;
480
481        return "Memcache Error ($self->[0]): $self->[1]";
482};
483
484sub new {
485        my $class = shift;
486        my $error = [@_];
487       
488        my $self = bless $error, (ref $class || $class);
489
490        return $self;
491}
492
493sub not_found {
494        my $self = shift;
495
496        return $self->[0] == ERR_NOT_FOUND;
497}
498
499sub exists {
500        my $self = shift;
501
502        return $self->[0] == ERR_EXISTS;
503}
504
505# vim: filetype=perl
Note: See TracBrowser for help on using the browser.