1package MemcachedTest; 2use strict; 3use IO::Socket::INET; 4use Exporter 'import'; 5use FindBin qw($Bin); 6use Carp qw(croak); 7use vars qw(@EXPORT); 8 9@EXPORT = qw(new_memcached sleep mem_get_is mem_stats); 10 11sub sleep { 12 my $n = shift; 13 select undef, undef, undef, $n; 14} 15 16sub mem_stats { 17 my ($sock, $type) = @_; 18 $type = $type ? " $type" : ""; 19 print $sock "stats$type\r\n"; 20 my $stats = {}; 21 while (<$sock>) { 22 last if /^(\.|END)/; 23 /^STAT (\S+) (\d+)/; 24 #print " slabs: $_"; 25 $stats->{$1} = $2; 26 } 27 return $stats; 28} 29 30sub mem_get_is { 31 # works on single-line values only. no newlines in value. 32 my ($sock_opts, $key, $val, $msg) = @_; 33 my $opts = ref $sock_opts eq "HASH" ? $sock_opts : {}; 34 my $sock = ref $sock_opts eq "HASH" ? $opts->{sock} : $sock_opts; 35 36 my $expect_flags = $opts->{flags} || 0; 37 my $dval = defined $val ? "'$val'" : "<undef>"; 38 $msg ||= "$key == $dval"; 39 40 print $sock "get $key\r\n"; 41 if (! defined $val) { 42 my $line = scalar <$sock>; 43 if ($line =~ /^VALUE/) { 44 $line .= scalar(<$sock>) . scalar(<$sock>); 45 } 46 Test::More::is($line, "END\r\n", $msg); 47 } else { 48 my $len = length($val); 49 my $body = scalar(<$sock>); 50 my $expected = "VALUE $key $expect_flags $len\r\n$val\r\nEND\r\n"; 51 if (!$body || $body =~ /^END/) { 52 Test::More::is($body, $expected, $msg); 53 return; 54 } 55 $body .= scalar(<$sock>) . scalar(<$sock>); 56 Test::More::is($body, $expected, $msg); 57 } 58} 59 60sub free_port { 61 my $sock; 62 my $port; 63 while (!$sock) { 64 $port = int(rand(20000)) + 30000; 65 $sock = IO::Socket::INET->new(LocalAddr => '127.0.0.1', 66 LocalPort => $port, 67 Proto => 'tcp', 68 ReuseAddr => 1); 69 } 70 return $port; 71} 72 73sub new_memcached { 74 my $args = shift || ""; 75 my $port = free_port(); 76 $args .= " -p $port"; 77 if ($< == 0) { 78 $args .= " -u root"; 79 } 80 my $childpid = fork(); 81 82 my $exe = "$Bin/../memcached"; 83 croak("memcached binary doesn't exist. Haven't run 'make' ?\n") unless -e $exe; 84 croak("memcached binary not executable\n") unless -x _; 85 86 unless ($childpid) { 87 exec "$exe $args"; 88 exit; # never gets here. 89 } 90 91 for (1..20) { 92 my $conn = IO::Socket::INET->new(PeerAddr => "127.0.0.1:$port"); 93 if ($conn) { 94 return Memcached::Handle->new(pid => $childpid, 95 conn => $conn, 96 port => $port); 97 } 98 select undef, undef, undef, 0.10; 99 } 100 croak("Failed to startup/connect to memcached server."); 101 102} 103 104############################################################################ 105package Memcached::Handle; 106sub new { 107 my ($class, %params) = @_; 108 return bless \%params, $class; 109} 110 111sub DESTROY { 112 my $self = shift; 113 kill 9, $self->{pid}; 114} 115 116sub port { $_[0]{port} } 117 118sub sock { 119 my $self = shift; 120 return $self->{conn} if $self->{conn} && getpeername($self->{conn}); 121 return $self->new_sock; 122} 123 124sub new_sock { 125 my $self = shift; 126 return IO::Socket::INET->new(PeerAddr => "127.0.0.1:$self->{port}"); 127} 128 1291; 130