File: NetServerTest.pm

package info (click to toggle)
libnet-server-perl 2.006-1%2Bdeb7u1
  • links: PTS, VCS
  • area: main
  • in suites: wheezy
  • size: 896 kB
  • sloc: perl: 5,413; makefile: 2
file content (180 lines) | stat: -rw-r--r-- 6,003 bytes parent folder | download
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
package NetServerTest;

use strict;
use IO::Socket;
use Exporter;
@NetServerTest::ISA = qw(Exporter);
@NetServerTest::EXPORT_OK = qw(prepare_test client_connect ok is like use_ok skip diag);
my %env;
use constant debug => $ENV{'NS_DEBUG'} ? 1 : 0;

END {
    warn "# number of tests ran ".($env{'_ok_n'} || 0)." did not match number of specified tests ".($env{'_ok_N'} || 0)."\n"
        if ($env{'_ok_N'} || 0) ne ($env{'_ok_n'} || 0) && ($env{'_ok_pid'} || 0) == $$;
}

sub client_connect {
    shift if $_[0] && $_[0] eq __PACKAGE__;
    if ($env{'ipv'} && $env{'ipv'} ne 4) {
        require IO::Socket::INET6;
        return IO::Socket::INET6->new(@_);
    } else {
        return IO::Socket::INET->new(@_);
    }
}

# most of our tests need forking, a certain number of ports, and some pipes
sub prepare_test {
    my $args = shift || {};
    my $N = $args->{'n_tests'} || die "Missing n_tests";
    print "1..$N\n";
    %env = map {/NET_SERVER_TEST_(\w+)/; lc($1) => $ENV{$_}} grep {/^NET_SERVER_TEST_\w+$/} keys %ENV;
    $env{'_ok_N'} = $N;
    $env{'_ok_pid'} = $$;
    return if $args->{'plan_only'};

    $env{'_ok_n'} = 0;
    $env{'timeout'}  ||= 5;

    # allow for finding a hostname that we can use in our tests that appears to be valid
    if (!$env{'hostname'}) {
        eval { require Net::Server::Proto } || do { SKIP: { skip("Could not load Net::Server::Proto to lookup host: $@", $N - 1) }; exit; };
        foreach my $host (qw(localhost localhost6 localhost.localdomain * ::1)) { # try local bindings first to avoid opening external ports during testing
            my @info = eval { Net::Server::Proto->get_addr_info($host) };
            next if ! @info;
            @info = sort {$a->[2] <=> $b->[2]} @info; # try IPv4 first in the name of consistency, but let IPv6 work too
            $env{'hostname'} = $info[0]->[0];
            $env{'ipv'}      = $info[0]->[2];
            last;
        }
        die "Could not find a hostname to test connections with (tried localhost, *, ::1)" if ! $env{'hostname'};
    }

    warn "# Checking can_fork\n" if debug;
    ok(can_fork(), "Can fork on this platform") || do { SKIP: { skip("Fork doesn't work on this platform", $N - 1) }; exit; };
    warn "# Checked can_fork\n"  if debug;

    warn "# Getting ports\n"  if debug;
    my $ports = $env{'ports'} = get_ports($args);
    ok(scalar(@$ports), "Got needed ports (@$ports)") || do { SKIP: { skip("Couldn't get the needed ports for testing", $N - 2) }; exit };
    warn "# Got ports\n"  if debug;


    warn "# Checking pipe serialization\n" if debug;
    pipe(NST_READ, NST_WRITE);
    NST_READ->autoflush(1);
    NST_WRITE->autoflush(1);
    print NST_WRITE "22";
    is(read(NST_READ, my $buf, 2), 2, "Pipe works") || do { SKIP: { skip ("Couldn't use working pipe", $N - 3) }; exit };
    warn "# Checked pipe serialization\n" if debug;
    $env{'block_until_ready_to_test'} = sub { read(NST_READ, my $buf, 1) };
    $env{'signal_ready_to_test'}      = sub { print NST_WRITE "1"; NST_WRITE->flush; };

    return \%env;
}


sub can_fork {
    return eval {
        my $pid = fork;
        die "Trouble while forking" unless defined $pid; # can't fork
        exit unless $pid; # can fork, exit child
        1;
    };
}

sub get_ports {
    my $args = shift;
    my $start_port = $args->{'start_port'} || die "Missing start_port";
    my $n          = $args->{'n_ports'}    || die "Missing n_ports";
    my @ports;
    eval {
        local $SIG{'ALRM'} = sub { die };
        alarm $env{'timeout'};
        for my $port ($start_port .. $start_port + 99){
            my $serv = client_connect(
                LocalAddr => $env{'hostname'},
                LocalPort => $port,
                Timeout   => 2,
                Listen    => 1,
                ReuseAddr => 1, Reuse => 1,
            ) || do { warn "Couldn't open server socket on port $port: $!\n" if $env{'trace'}; next };
            my $client = client_connect(
                PeerAddr => $env{'hostname'},
                PeerPort => $port,
                Timeout  => 2,
            ) || do { warn "Couldn't open client socket on port $port: $!\n" if $env{'trace'}; next };
            my $sock = $serv->accept || do { warn "Didn't accept properly on server: $!" if $env{'trace'}; next };
            $sock->autoflush(1);
            print $sock "hi from server\n";
            $client->autoflush(1);
            print $client "hi from client\n";
            next if <$sock>   !~ /^hi from client/;
            next if <$client> !~ /^hi from server/;
            $client->close;
            $sock->close;
            push @ports, $port;
            last if @ports == $n;
        }
        alarm(0);
    };
    die "Number of ports didn't match (@ports) != $n ($@)" if @ports < $n;
    return \@ports;
}

###----------------------------------------------------------------###

sub ok {
    my ($ok, $msg, $level) = @_;
    my $n = ++$env{'_ok_n'};
    print "".($ok ? "" : "not ")."ok $n";
    print " - $msg" if defined $msg;
    print "\n" if $msg !~ /\n\Z/;
    if (! $ok) {
        my ($pkg, $file, $line) = caller($level || 0);
        print "#   failed at $file line $line\n";
    }
    return $ok;
}

sub is {
    my ($a, $b, $msg) = @_;
    if (! ok($a eq $b, $msg, 1)) {
        print "#        got: $a\n";
        print "#   expected: $b\n";
        return;
    }
    return 1;
}

sub like {
    my ($a, $b, $msg) = @_;
    if (! ok($a =~ $b, $msg, 1)) {
        print "#        got: $a\n";
        print "#   expected: $b\n";
        return;
    }
    return 1;
}

sub use_ok {
    my $pkg = shift;
    my $ok = eval("require $pkg") && eval {$pkg->import(@_);1};
    ok($ok, "use $pkg", 1) || do { print "#   failed to import $pkg: $@\n"; return 0 };
}

sub skip {
    my ($msg, $n) = @_;
    print "ok ".(++$env{'_ok_n'})." # skip $msg\n" for 1 .. $n;
    no warnings 'exiting';
    last SKIP;
}

sub diag {
    for my $line (@_) {
        chomp $line;
        print "# $line\n";
    }
}

1;