summaryrefslogtreecommitdiff
path: root/ext/Socket/t/socketpair.t
blob: 997628c3bd72424e611aca7792ba6bd468e397b9 (plain)
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
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
#!./perl -w

my $child;
my $can_fork;
my $has_perlio;

BEGIN {
    require Config; import Config;
    $can_fork = $Config{'d_fork'} || $Config{'d_pseudofork'};

    if ($^O eq "hpux" or $Config{'extensions'} !~ /\bSocket\b/ &&
        !(($^O eq 'VMS') && $Config{d_socket})) {
	print "1..0\n";
	exit 0;
    }
}

{
    # This was in the BEGIN block, but since Test::More 0.47 added support to
    # detect forking, we don't need to fork before Test::More initialises.

    # Too many things in this test will hang forever if something is wrong,
    # so we need a self destruct timer. And IO can hang despite an alarm.

    if( $can_fork) {
      my $parent = $$;
      $child = fork;
      die "Fork failed" unless defined $child;
      if (!$child) {
        $SIG{INT} = sub {exit 0}; # You have 60 seconds. Your time starts now.
        my $must_finish_by = time + 60;
        my $remaining;
        while (($remaining = $must_finish_by - time) > 0) {
          sleep $remaining;
        }
        warn "Something unexpectedly hung during testing";
        kill "INT", $parent or die "Kill failed: $!";
        exit 1;
      }
    }
    unless ($has_perlio = find PerlIO::Layer 'perlio') {
	print <<EOF;
# Since you don't have perlio you might get failures with UTF-8 locales.
EOF
    }
}

use Socket;
use Test::More;
use strict;
use warnings;
use Errno;

my $skip_reason;

if( !$Config{d_alarm} ) {
  plan skip_all => "alarm() not implemented on this platform";
} elsif( !$can_fork ) {
  plan skip_all => "fork() not implemented on this platform";
} else {
  # This should fail but not die if there is real socketpair
  eval {socketpair LEFT, RIGHT, -1, -1, -1};
  if ($@ =~ /^Unsupported socket function "socketpair" called/ ||
      $! =~ /^The operation requested is not supported./) { # Stratus VOS
    plan skip_all => 'No socketpair (real or emulated)';
  } else {
    eval {AF_UNIX};
    if ($@ =~ /^Your vendor has not defined Socket macro AF_UNIX/) {
      plan skip_all => 'No AF_UNIX';
    } else {
      plan tests => 45;
    }
  }
}

# But we'll install an alarm handler in case any of the races below fail.
$SIG{ALRM} = sub {die "Unexpected alarm during testing"};

ok (socketpair (LEFT, RIGHT, AF_UNIX, SOCK_STREAM, PF_UNSPEC),
    "socketpair (LEFT, RIGHT, AF_UNIX, SOCK_STREAM, PF_UNSPEC)")
  or print "# \$\! = $!\n";

if ($has_perlio) {
    binmode(LEFT,  ":bytes");
    binmode(RIGHT, ":bytes");
}

my @left = ("hello ", "world\n");
my @right = ("perl ", "rules!"); # Not like I'm trying to bias any survey here.

foreach (@left) {
  # is (syswrite (LEFT, $_), length $_, "write " . _qq ($_) . " to left");
  is (syswrite (LEFT, $_), length $_, "syswrite to left");
}
foreach (@right) {
  # is (syswrite (RIGHT, $_), length $_, "write " . _qq ($_) . " to right");
  is (syswrite (RIGHT, $_), length $_, "syswrite to right");
}

# stream socket, so our writes will become joined:
my ($buffer, $expect);
$expect = join '', @right;
undef $buffer;
is (read (LEFT, $buffer, length $expect), length $expect, "read on left");
is ($buffer, $expect, "content what we expected?");
$expect = join '', @left;
undef $buffer;
is (read (RIGHT, $buffer, length $expect), length $expect, "read on right");
is ($buffer, $expect, "content what we expected?");

ok (shutdown(LEFT, SHUT_WR), "shutdown left for writing");
# This will hang forever if eof is buggy, and alarm doesn't interrupt system
# Calls. Hence the child process minder.
SKIP: {
  skip "SCO Unixware / OSR have a bug with shutdown",2 if $^O =~ /^(?:svr|sco)/;
  local $SIG{ALRM} = sub { warn "EOF on right took over 3 seconds" };
  local $TODO = "Known problems with unix sockets on $^O"
      if $^O eq 'hpux'   || $^O eq 'super-ux';
  alarm 3;
  $! = 0;
  ok (eof RIGHT, "right is at EOF");
  local $TODO = "Known problems with unix sockets on $^O"
      if $^O eq 'unicos' || $^O eq 'unicosmk';
  is ($!, '', 'and $! should report no error');
  alarm 60;
}

my $err = $!;
$SIG{PIPE} = 'IGNORE';
{
  local $SIG{ALRM}
    = sub { warn "syswrite to left didn't fail within 3 seconds" };
  alarm 3;
  # Split the system call from the is() - is() does IO so
  # (say) a flush may do a seek which on a pipe may disturb errno
  my $ans = syswrite (LEFT, "void");
  $err = $!;
  is ($ans, undef, "syswrite to shutdown left should fail");
  alarm 60;
}
{
  # This may need skipping on some OSes - restoring value saved above
  # should help
  $! = $err;
  ok (($!{EPIPE} or $!{ESHUTDOWN}), '$! should be EPIPE or ESHUTDOWN')
    or printf "\$\!=%d(%s)\n", $err, $err;
}

my @gripping = (chr 255, chr 127);
foreach (@gripping) {
  is (syswrite (RIGHT, $_), length $_, "syswrite to right");
}

ok (!eof LEFT, "left is not at EOF");

$expect = join '', @gripping;
undef $buffer;
is (read (LEFT, $buffer, length $expect), length $expect, "read on left");
is ($buffer, $expect, "content what we expected?");

ok (close LEFT, "close left");
ok (close RIGHT, "close right");


# And now datagrams
# I suspect we also need a self destruct time-bomb for these, as I don't see any
# guarantee that the stack won't drop a UDP packet, even if it is for localhost.

SKIP: {
  skip "No usable SOCK_DGRAM for socketpair", 24 if ($^O =~ /^(MSWin32|os2)\z/);
  local $TODO = "socketpair not supported on $^O" if $^O eq 'nto';

ok (socketpair (LEFT, RIGHT, AF_UNIX, SOCK_DGRAM, PF_UNSPEC),
    "socketpair (LEFT, RIGHT, AF_UNIX, SOCK_DGRAM, PF_UNSPEC)")
  or print "# \$\! = $!\n";

if ($has_perlio) {
    binmode(LEFT,  ":bytes");
    binmode(RIGHT, ":bytes");
}

foreach (@left) {
  # is (syswrite (LEFT, $_), length $_, "write " . _qq ($_) . " to left");
  is (syswrite (LEFT, $_), length $_, "syswrite to left");
}
foreach (@right) {
  # is (syswrite (RIGHT, $_), length $_, "write " . _qq ($_) . " to right");
  is (syswrite (RIGHT, $_), length $_, "syswrite to right");
}

# stream socket, so our writes will become joined:
my ($total);
$total = join '', @right;
foreach $expect (@right) {
  undef $buffer;
  is (sysread (LEFT, $buffer, length $total), length $expect, "read on left");
  is ($buffer, $expect, "content what we expected?");
}
$total = join '', @left;
foreach $expect (@left) {
  undef $buffer;
  is (sysread (RIGHT, $buffer, length $total), length $expect, "read on right");
  is ($buffer, $expect, "content what we expected?");
}

ok (shutdown(LEFT, 1), "shutdown left for writing");

# eof uses buffering. eof is indicated by a sysread of zero.
# but for a datagram socket there's no way it can know nothing will ever be
# sent
SKIP: {
  skip "$^O does length 0 udp reads", 2 if ($^O eq 'os390');

  my $alarmed = 0;
  local $SIG{ALRM} = sub { $alarmed = 1; };
  print "# Approximate forever as 3 seconds. Wait 'forever'...\n";
  alarm 3;
  undef $buffer;
  is (sysread (RIGHT, $buffer, 1), undef,
      "read on right should be interrupted");
  is ($alarmed, 1, "alarm should have fired");
}

alarm 30;

#ok (eof RIGHT, "right is at EOF");

foreach (@gripping) {
  is (syswrite (RIGHT, $_), length $_, "syswrite to right");
}

$total = join '', @gripping;
foreach $expect (@gripping) {
  undef $buffer;
  is (sysread (LEFT, $buffer, length $total), length $expect, "read on left");
  is ($buffer, $expect, "content what we expected?");
}

ok (close LEFT, "close left");
ok (close RIGHT, "close right");

} # end of DGRAM SKIP

kill "INT", $child or warn "Failed to kill child process $child: $!";
exit 0;