shlist

share and manage lists between multiple people
Log | Files | Refs

Test.pm (6312B)


      1 package SL::Test::Server;
      2 use strict;
      3 
      4 use IPC::Open3;
      5 
      6 sub new {
      7 	my $class = shift;
      8 
      9 	my $self = {};
     10 	bless ($self, $class);
     11 
     12 	my $perl_args = '';
     13 	if (defined $ARGV[0] && $ARGV[0] eq '-c') {
     14 		# Enable test coverage when -c is passed to the test
     15 		$perl_args = '-MDevel::Cover=-silent,1';
     16 	}
     17 
     18 	$ENV{PATH} = "/bin:/usr/bin";
     19 	delete @ENV{ 'IFS', 'CDPATH', 'ENV', 'BASH_ENV' };
     20 	my $pid = open3(undef, undef, \*CHLD_ERR, "perl $perl_args -T sl -t -p 4729");
     21 
     22 	$self->{pid} = $pid;
     23 	$self->{CHLD_ERR} = \*CHLD_ERR;
     24 	return $self;
     25 }
     26 
     27 sub readline {
     28 	my $self = shift;
     29 	return readline $self->{CHLD_ERR};
     30 }
     31 
     32 sub DESTROY {
     33 	my $self = shift;
     34 
     35 	kill 'TERM', $self->{pid};
     36 	waitpid( $self->{pid}, 0 );
     37 }
     38 
     39 1;
     40 
     41 package SL::Test::Client;
     42 use strict;
     43 use warnings;
     44 
     45 use IO::Socket::SSL;
     46 use JSON::XS;
     47 use String::Random;
     48 use Test;
     49 use Time::HiRes qw(usleep);
     50 
     51 require "msgs.pl";
     52 our (%msg_num, @msg_str);
     53 
     54 sub new {
     55 	my $class = shift;
     56 	my $dont_register = shift || 0;
     57 
     58 	my $self = {};
     59 	bless ($self, $class);
     60 
     61 	my $socket = undef;
     62 	while (! $socket) {
     63 		$socket = IO::Socket::SSL->new(
     64 			PeerHost => 'localhost',
     65 			PeerPort => 4729,
     66 			# certs are signed with absentmindedproductions.ca
     67 			SSL_verifycn_name => "absentmindedproductions.ca",
     68 		) or usleep(100 * 1000);
     69 	}
     70 	die "failed connect or ssl handshake: $!,$SSL_ERROR" unless ($socket);
     71 
     72 	$self->{sock} = $socket;
     73 	$self->{device_id} = undef;
     74 
     75 	if ($dont_register == 0) {
     76 		my $string_gen = String::Random->new;
     77 		$self->{phnum} = '403' . $string_gen->randpattern('nnnnnnn');
     78 
     79 		my $args = { phone_number => $self->{phnum}, os => 'unix' };
     80 		$self->{device_id} = $self->device_add($args);
     81 
     82 		$self->device_update( "token_$self->{phnum}" );
     83 	}
     84 
     85 	return $self;
     86 }
     87 
     88 sub device_add {
     89 	my ($self, $args, $status) = @_;
     90 	return $self->communicate('device_add', $status, $args);
     91 }
     92 
     93 sub device_update {
     94 	my ($self, $args, $status) = @_;
     95 	return $self->communicate('device_update', $status, $args);
     96 }
     97 
     98 sub list_add {
     99 	my ($self, $args, $status) = @_;
    100 	return $self->communicate('list_add', $status, $args);
    101 }
    102 
    103 sub list_update {
    104 	my ($self, $args, $status) = @_;
    105 	return $self->communicate('list_update', $status, $args);
    106 }
    107 
    108 sub list_join {
    109 	my ($self, $args, $status) = @_;
    110 	return $self->communicate('list_join', $status, $args);
    111 }
    112 
    113 sub list_leave {
    114 	my ($self, $args, $status) = @_;
    115 	return $self->communicate('list_leave', $status, $args);
    116 }
    117 
    118 sub friend_add {
    119 	my ($self, $args, $status) = @_;
    120 	return $self->communicate('friend_add', $status, $args);
    121 }
    122 
    123 sub friend_delete {
    124 	my ($self, $args, $status) = @_;
    125 	return $self->communicate('friend_delete', $status, $args);
    126 }
    127 
    128 sub lists_get {
    129 	my ($self, $status) = @_;
    130 	return $self->communicate('lists_get', $status);
    131 }
    132 
    133 sub lists_get_other {
    134 	my ($self, $status) = @_;
    135 	return $self->communicate('lists_get_other', $status);
    136 }
    137 
    138 sub communicate {
    139 	my ($self, $msg_type, $exp_status, $msg_data) = @_;
    140 
    141 	# If no expected status was passed in assume 'ok'
    142 	$exp_status = 'ok' if (! defined $exp_status);
    143 
    144 	# Only append "data" key if optional parameter $msg_data is defined
    145 	my $msg_args->{data} = $msg_data if (defined $msg_data);
    146 
    147 	# device_add is the only message type that does not require device_id as
    148 	# a mandatory argument
    149 	$msg_args->{device_id} = $self->{device_id} if ($msg_type ne 'device_add');
    150 
    151 	$self->send_msg($msg_type, $msg_args);
    152 	my $resp = $self->recv_msg($msg_type);
    153 
    154 	# Check that the response status matches the expected status
    155 	ok( $resp->{status}, $exp_status );
    156 
    157 	# Return the failure reason if the response status was error
    158 	return $resp->{reason} if ($resp->{status} eq 'err');
    159 
    160 	# Everything looks good, return the response data
    161 	return $resp->{data};
    162 }
    163 
    164 sub send_msg {
    165 	my ($self, $msg_type, $request) = @_;
    166 
    167 	# Request comes in as a hash ref, do this now to figure out length
    168 	my $payload = encode_json($request);
    169 
    170 	die "invalid message type $msg_type" unless (grep { $_ eq $msg_type } @msg_str);
    171 
    172 	my $version = 0;
    173 	my $payload_len = length($payload);
    174 	my $header = pack("nnn", $version, $msg_num{$msg_type}, $payload_len);
    175 
    176 	my $sent_bytes = 0;
    177 	$sent_bytes += $self->send_all($header, length($header));
    178 	$sent_bytes += $self->send_all($payload, $payload_len);
    179 
    180 	return $sent_bytes;
    181 }
    182 
    183 sub send_all {
    184 	my ($self, $bytes, $bytes_total) = @_;
    185 
    186 	my $bytes_written = $self->{sock}->syswrite($bytes);
    187 
    188 	die "write failed: $!" if (!defined $bytes_written);
    189 	die "wrote $bytes_written instead of $bytes_total bytes" if ($bytes_written != $bytes_total);
    190 
    191 	return $bytes_total;
    192 }
    193 
    194 sub recv_msg {
    195 	my ($self, $exp_msg_type) = @_;
    196 
    197 	# Read header
    198 	my $header = $self->read_all(6);
    199 	my ($version, $msg_type, $payload_size) = unpack("nnn", $header);
    200 
    201 	# Check some things
    202 	die "unsupported protocol version $version" if ($version != 0);
    203 	die "unknown message type $msg_type" if ($msg_type >= @msg_str);
    204 	die "0 byte payload" if ($payload_size == 0);
    205 	die "unexpected message type $msg_type" if ($msg_num{$exp_msg_type} != $msg_type);
    206 
    207 	# Read again for payload, $payload_size > 0
    208 	my $payload = $self->read_all($payload_size);
    209 
    210 	# This will die if $payload is not JSON
    211 	my $response = decode_json($payload);
    212 
    213 	# Don't accept messages without an object root (ie array roots)
    214 	if (ref($response) ne "HASH") {
    215 		die "server didn't send back object root element";
    216 	}
    217 
    218 	return $response;
    219 }
    220 
    221 sub read_all {
    222 	my ($self, $bytes_total) = @_;
    223 
    224 	my $data;
    225 	my $bytes_read = 0;
    226 	while ($bytes_total > 0) {
    227 		my $read = $self->{sock}->sysread($data, $bytes_total, $bytes_read);
    228 
    229 		die "read failed: $!" unless (defined $read);
    230 		die "read EOF on socket" if ($read == 0);
    231 
    232 		$bytes_total -= $read;
    233 		$bytes_read += $read;
    234 	}
    235 
    236 	return $data;
    237 }
    238 
    239 sub phnum {
    240 	my ($self) = @_;
    241 	return $self->{phnum};
    242 }
    243 
    244 sub device_id {
    245 	my ($self) = @_;
    246 	return $self->{device_id};
    247 }
    248 
    249 sub set_device_id {
    250 	my ($self, $new_id) = @_;
    251 	$self->{device_id} = $new_id;
    252 }
    253 
    254 sub msg_str {
    255 	return @msg_str;
    256 }
    257 
    258 1;
    259 
    260 package SL::Test::Notify;
    261 use strict;
    262 
    263 use IPC::Open2;
    264 
    265 sub new {
    266 	my $class = shift;
    267 
    268 	my $self = {};
    269 	bless ($self, $class);
    270 
    271 	my $pid = open2(\*CHLD_OUT, undef, "perl", "SL/testd.pl");
    272 
    273 	$self->{pid} = $pid;
    274 	$self->{chld_out} = \*CHLD_OUT;
    275 	return $self;
    276 }
    277 
    278 sub readline {
    279 	my $self = shift;
    280 	return readline $self->{chld_out};
    281 }
    282 
    283 sub DESTROY {
    284 	my $self = shift;
    285 
    286 	kill 'TERM', $self->{pid};
    287 	waitpid( $self->{pid}, 0 );
    288 }
    289 
    290 1;