summaryrefslogtreecommitdiff
path: root/lib/open.t
blob: 3a5565d86a020c40006a782c1b6ce76de3d48692 (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
#!./perl

BEGIN {
	chdir 't' if -d 't';
	@INC = '../lib';
	require Config; import Config;
	require './test.pl';
}

plan 23;

# open::import expects 'open' as its first argument, but it clashes with open()
sub import {
	open::import( 'open', @_ );
}

# can't use require_ok() here, with a name like 'open'
ok( require 'open.pm', 'requiring open' );

# this should fail
eval { import() };
like( $@, qr/needs explicit list of PerlIO layers/,
	'import should fail without args' );

# prevent it from loading I18N::Langinfo, so we can test encoding failures
my $warn;
local $SIG{__WARN__} = sub {
	$warn .= shift;
};

# and it shouldn't be able to find this layer
$warn = '';
eval q{ no warnings 'layer'; use open IN => ':macguffin' ; };
is( $warn, '',
	'should not warn about unknown layer with bad layer provided' );

$warn = '';
eval q{ use warnings 'layer'; use open IN => ':macguffin' ; };
like( $warn, qr/Unknown PerlIO layer/,
	'should warn about unknown layer with bad layer provided' );

# open :locale logic changed since open 1.04, new logic
# difficult to test portably.

# see if it sets the magic variables appropriately
import( 'IN', ':crlf' );
is( $^H{'open_IN'}, 'crlf', 'should have set crlf layer' );

# it should reset them appropriately, too
import( 'IN', ':raw' );
is( $^H{'open_IN'}, 'raw', 'should have reset to raw layer' );

# it dies if you don't set IN, OUT, or IO
eval { import( 'sideways', ':raw' ) };
like( $@, qr/Unknown PerlIO layer class/, 'should croak with unknown class' );

# but it handles them all so well together
import( 'IO', ':raw :crlf' );
is( ${^OPEN}, ":raw :crlf\0:raw :crlf",
	'should set multi types, multi layer' );
is( $^H{'open_IO'}, 'crlf', 'should record last layer set in %^H' );

SKIP: {
    skip("no perlio, no :utf8", 12) unless (find PerlIO::Layer 'perlio');

    eval <<EOE;
    use open ':utf8';
    open(O, ">utf8");
    print O chr(0x100);
    close O;
    open(I, "<utf8");
    is(ord(<I>), 0x100, ":utf8 single wide character round-trip");
    close I;
EOE

    open F, ">a";
    @a = map { chr(1 << ($_ << 2)) } 0..5; # 0x1, 0x10, .., 0x100000
    unshift @a, chr(0); # ... and a null byte in front just for fun
    print F @a;
    close F;

    sub systell {
        use Fcntl 'SEEK_CUR';
        sysseek($_[0], 0, SEEK_CUR);
    }

    require bytes; # not use

    my $ok;

    open F, "<:utf8", "a";
    $ok = $a = 0;
    for (@a) {
        unless (
		($c = sysread(F, $b, 1)) == 1  &&
		length($b)               == 1  &&
		ord($b)                  == ord($_) &&
		systell(F)               == ($a += bytes::length($b))
		) {
	    print '# ord($_)           == ', ord($_), "\n";
	    print '# ord($b)           == ', ord($b), "\n";
	    print '# length($b)        == ', length($b), "\n";
	    print '# bytes::length($b) == ', bytes::length($b), "\n";
	    print '# systell(F)        == ', systell(F), "\n";
	    print '# $a                == ', $a, "\n";
	    print '# $c                == ', $c, "\n";
	    last;
	}
	$ok++;
    }
    close F;
    ok($ok == @a,
       "on :utf8 streams sysread() should work on characters, not bytes");

    sub diagnostics {
	print '# ord($_)           == ', ord($_), "\n";
	print '# bytes::length($_) == ', bytes::length($_), "\n";
	print '# systell(G)        == ', systell(G), "\n";
	print '# $a                == ', $a, "\n";
	print '# $c                == ', $c, "\n";
    }


    my %actions = (
		   syswrite => sub { syswrite G, shift; },
		   'syswrite len' => sub { syswrite G, shift, 1; },
		   'syswrite len pad' => sub {
		       my $temp = shift() . "\243";
		       syswrite G, $temp, 1; },
		   'syswrite off' => sub { 
		       my $temp = "\351" . shift();
		       syswrite G, $temp, 1, 1; },
		   'syswrite off pad' => sub { 
		       my $temp = "\351" . shift() . "\243";
		       syswrite G, $temp, 1, 1; },
		  );

    foreach my $key (sort keys %actions) {
	# syswrite() on should work on characters, not bytes
	open G, ">:utf8", "b";

	print "# $key\n";
	$ok = $a = 0;
	for (@a) {
	    unless (
		    ($c = $actions{$key}($_)) == 1 &&
		    systell(G)                == ($a += bytes::length($_))
		   ) {
		diagnostics();
		last;
	    }
	    $ok++;
	}
	close G;
	ok($ok == @a,
	   "on :utf8 streams syswrite() should work on characters, not bytes");

	open G, "<:utf8", "b";
	$ok = $a = 0;
	for (@a) {
	    unless (
		    ($c = sysread(G, $b, 1)) == 1 &&
		    length($b)               == 1 &&
		    ord($b)                  == ord($_) &&
		    systell(G)               == ($a += bytes::length($_))
		   ) {
		print '# ord($_)           == ', ord($_), "\n";
		print '# ord($b)           == ', ord($b), "\n";
		print '# length($b)        == ', length($b), "\n";
		print '# bytes::length($b) == ', bytes::length($b), "\n";
		print '# systell(G)        == ', systell(G), "\n";
		print '# $a                == ', $a, "\n";
		print '# $c                == ', $c, "\n";
		last;
	    }
	    $ok++;
	}
	close G;
	ok($ok == @a,
	   "checking syswrite() output on :utf8 streams by reading it back in");
    }
}
SKIP: {
    skip("no perlio", 2) unless (find PerlIO::Layer 'perlio');
    skip("no Encode", 2) unless $Config{extensions} =~ m{\bEncode\b};

    eval q[use Encode::Alias;use open ":std", ":locale"];
    is($@, '', 'can use :std and :locale');
}

{
    local $ENV{PERL_UNICODE};
    delete $ENV{PERL_UNICODE};
    is runperl(
         progs => [
            'use open q\:encoding(UTF-8)\, q-:std-;',
            'use open q\:encoding(UTF-8)\;',
            'if(($_ = <STDIN>) eq qq-\x{100}\n-) { print qq-stdin ok\n- }',
            'else { print qq-got -, join(q q q, map ord, split//), "\n" }',
            'print STDOUT qq-\x{ff}\n-;',
            'print STDERR qq-\x{ff}\n-;',
         ],
         stdin => "\xc4\x80\n",
         stderr => 1,
       ),
       "stdin ok\n\xc3\xbf\n\xc3\xbf\n",
       "use open without :std does not affect standard handles",
    ;
}

END {
    1 while unlink "utf8";
    1 while unlink "a";
    1 while unlink "b";
}

# the test cases beyond __DATA__ need to be executed separately

__DATA__
$ENV{LC_ALL} = 'nonexistent.euc';
eval { open::_get_locale_encoding() };
like( $@, qr/too ambiguous/, 'should die with ambiguous locale encoding' );
%%%
# the special :locale layer
$ENV{LC_ALL} = $ENV{LANG} = 'ru_RU.KOI8-R';
# the :locale will probe the locale environment variables like LANG
use open OUT => ':locale';
open(O, ">koi8");
print O chr(0x430); # Unicode CYRILLIC SMALL LETTER A = KOI8-R 0xc1
close O;
open(I, "<koi8");
printf "%#x\n", ord(<I>), "\n"; # this should print 0xc1
close I;
%%%