This is a live mirror of the Perl 5 development currently hosted at https://github.com/perl/perl5
[perl #72729] Test appending sv_gets for ascii/utf8 in target and handle
[perl5.git] / t / op / readline.t
1 #!./perl
2
3 BEGIN {
4     chdir 't';
5     @INC = '../lib';
6     require './test.pl';
7 }
8
9 plan tests => 25;
10
11 # [perl #19566]: sv_gets writes directly to its argument via
12 # TARG. Test that we respect SvREADONLY.
13 eval { for (\2) { $_ = <FH> } };
14 like($@, 'Modification of a read-only value attempted', '[perl #19566]');
15
16 # [perl #21628]
17 {
18   my $file = tempfile();
19   open A,'+>',$file; $a = 3;
20   is($a .= <A>, 3, '#21628 - $a .= <A> , A eof');
21   close A; $a = 4;
22   is($a .= <A>, 4, '#21628 - $a .= <A> , A closed');
23 }
24
25 # [perl #21614]: 82 is chosen to exceed the length for sv_grow in
26 # do_readline (80)
27 foreach my $k (1, 82) {
28   my $result
29     = runperl (stdin => '', stderr => 1,
30               prog => "\$x = q(k) x $k; \$a{\$x} = qw(v); \$_ = <> foreach keys %a; print qw(end)",
31               );
32   $result =~ s/\n\z// if $^O eq 'VMS';
33   is ($result, "end", '[perl #21614] for length ' . length('k' x $k));
34 }
35
36
37 foreach my $k (1, 21) {
38   my $result
39     = runperl (stdin => ' rules', stderr => 1,
40               prog => "\$x = q(perl) x $k; \$a{\$x} = q(v); foreach (keys %a) {\$_ .= <>; print}",
41               );
42   $result =~ s/\n\z// if $^O eq 'VMS';
43   is ($result, ('perl' x $k) . " rules", 'rcatline to shared sv for length ' . length('perl' x $k));
44 }
45
46 foreach my $l (1, 82) {
47   my $k = $l;
48   $k = 'k' x $k;
49   my $copy = $k;
50   $k = <DATA>;
51   is ($k, "moo\n", 'catline to COW sv for length ' . length $copy);
52 }
53
54
55 foreach my $l (1, 21) {
56   my $k = $l;
57   $k = 'perl' x $k;
58   my $perl = $k;
59   $k .= <DATA>;
60   is ($k, "$perl rules\n", 'rcatline to COW sv for length ' . length $perl);
61 }
62
63 use strict;
64 use File::Spec;
65
66 open F, File::Spec->curdir and sysread F, $_, 1;
67 my $err = $! + 0;
68 close F;
69
70 SKIP: {
71   skip "you can read directories as plain files", 2 unless( $err );
72
73   $!=0;
74   open F, File::Spec->curdir and $_=<F>;
75   ok( $!==$err && !defined($_) => 'readline( DIRECTORY )' );
76   close F;
77
78   $!=0;
79   { local $/;
80     open F, File::Spec->curdir and $_=<F>;
81     ok( $!==$err && !defined($_) => 'readline( DIRECTORY ) slurp mode' );
82     close F;
83   }
84 }
85
86 fresh_perl_is('BEGIN{<>}', '',
87               { switches => ['-w'], stdin => '', stderr => 1 },
88               'No ARGVOUT used only once warning');
89
90 fresh_perl_is('print readline', 'foo',
91               { switches => ['-w'], stdin => 'foo', stderr => 1 },
92               'readline() defaults to *ARGV');
93
94 # [perl #72720] Test that sv_gets clears any variables that should be
95 # empty so if the read() aborts with EINTER, the TARG is actually
96 # cleared.
97 sub test_eintr_readline {
98     my ( $fh, $timeout ) = @_;
99
100     # This variable, the TARG for the readline is the core of this
101     # test. The test is to see that after a my() and a failure in
102     # readline() has the variable revived old, "dead" values from the
103     # past or is it still undef like expected.
104     my $line;
105
106     # Do a readline into $line.
107     if ( $timeout ) {
108
109         # Do a SIGALARM aborted readline(). The underlying sv_gets()
110         # from sv.c will use the syscall read() while will exit early
111         # and return something like EINTR or ERESTARTSYS.
112         my $timed_out;
113         my $errno;
114         eval {
115             local $SIG{ALRM} = sub {
116                 $timed_out = 1;
117                 die 'abort this timeout';
118             };
119             alarm $timeout;
120             undef $!;
121             $line = readline $fh;
122             $errno = $!;
123             alarm 0;
124         };
125
126         # The code should have timed out.
127         if ( ! $timed_out ) {
128             warn $@
129                 ? "$@: $errno\n"
130                 : "Interrupted readline() test couldn't get interrupted: $errno";
131         }
132     }
133     else {
134         $line = readline $fh;
135     }
136     return $line;
137 }
138 SKIP: {
139
140     # Connect two handles together.
141     my ( $in, $out );
142     my $piped;
143     eval {
144         pipe $in, $out;
145         $piped = 1;
146     };
147     if ( ! $piped ) {
148         skip( 2, 'The pipe function is unimplemented' );
149     }
150
151     # Make the pipe autoflushing
152     {
153         my $old_fh = select $out;
154         $| = 1;
155         select $old_fh;
156     }
157
158     # Only one line is loaded into the pipe. It's written unbuffered
159     # so I'm confident it'll not be buffered.
160     syswrite $out, "once\n";
161
162     # Buggy perls will return the last thing successfully
163     # returned. Buggy perls will return "once\n" a second (and
164     # "infinitely" if we desired) as long as the internal read()
165     # syscall fails. In our case, it fails because the inner my($line)
166     # retains all its allocated space and buggy perl sets SvPOK to
167     # make the value valid but before it starts read().
168     my $once  = test_eintr_readline( $in, 0 );
169     my $twice = test_eintr_readline( $in, 1 );
170     is(   $once,  "once\n", "readline read first line ok" );
171     isnt( $twice, "once\n", "readline didn't re-return things when interrupted" );
172
173     TODO: {
174         local our $TODO = "bad readline returns '', not undef";
175         is( $twice, undef, "readline returned undef when interrupted" );
176     }
177 }
178
179 {
180     my $line = 'ascii';
181     my ( $in, $out );
182     pipe $in, $out;
183     binmode $in;
184     binmode $out;
185     syswrite $out, "...\n";
186     $line .= readline $in;
187
188     is( $line, "ascii...\n", 'Appending from ascii to ascii' );
189 }
190
191 {
192     my $line = "\x{2080} utf8";
193     my ( $in, $out );
194     pipe $in, $out;
195     binmode $out;
196     binmode $in;
197     syswrite $out, "...\n";
198     $line .= readline $in;
199
200     is( $line, "\x{2080} utf8...\n", 'Appending from ascii to utf8' );
201 }
202
203 {
204     my $line = 'ascii';
205     my ( $in, $out );
206     pipe $in, $out;
207     binmode $out, ':utf8';
208     binmode $in,  ':utf8';
209     syswrite $out, "...\n";
210     $line .= readline $in;
211
212     is( $line, "ascii...\n", 'Appending from utf8 to ascii' );
213 }
214
215 {
216     my $line = "\x{2080} utf8";;
217     my ( $in, $out );
218     pipe $in, $out;
219     binmode $out, ':utf8';
220     binmode $in,  ':utf8';
221     syswrite $out, "\x{2080}...\n";
222     $line .= readline $in;
223
224     is( $line, "\x{2080} utf8\x{2080}...\n", 'appending from utf to utf8' );
225 }
226
227 my $obj = bless [];
228 $obj .= <DATA>;
229 like($obj, qr/main=ARRAY.*world/, 'rcatline and refs');
230
231 # bug #38631
232 require Tie::Scalar;
233 tie our $one, 'Tie::StdScalar', "A: ";
234 tie our $two, 'Tie::StdScalar', "B: ";
235 my $junk = $one;
236 $one .= <DATA>;
237 $two .= <DATA>;
238 is( $one, "A: One\n", "rcatline works with tied scalars" );
239 is( $two, "B: Two\n", "rcatline works with tied scalars" );
240
241 __DATA__
242 moo
243 moo
244  rules
245  rules
246 world
247 One
248 Two