1#!./perl
2
3BEGIN {
4	require Config;
5	if (($Config::Config{'extensions'} !~ m!\bList/Util\b!) ){
6	    print "1..0 # Skip -- Perl configured without List::Util module\n";
7	    exit 0;
8	}
9
10	# `make test` in the CPAN version of this module runs us with -w, but
11	# Dumpvalue.pm relies on all sorts of things that can cause warnings. I
12	# don't think that's worth fixing, so we just turn off all warnings
13	# during testing.
14	$^W = 0;
15}
16
17use lib ("./t/lib");
18use TieOut;
19use Test::More tests => 88;
20
21use_ok( 'Dumpvalue' );
22
23my $d;
24ok( $d = Dumpvalue->new(), 'create a new Dumpvalue object' );
25
26$d->set( globPrint => 1, dumpReused => 1 );
27is( $d->{globPrint}, 1, 'set an option correctly' );
28is( $d->get('globPrint'), 1, 'get an option correctly' );
29is_deeply( [ $d->get('globPrint', 'dumpReused') ],
30    [ 1, 1 ],
31    'get multiple options'
32);
33
34# check to see if unctrl works
35is( ref( Dumpvalue::unctrl(*FOO) ), 'GLOB', 'unctrl should not modify GLOB' );
36is( Dumpvalue::unctrl('donotchange'), 'donotchange', "unctrl shouldn't modify");
37like( Dumpvalue::unctrl("bo\007nd"), qr/bo\^.nd/, 'unctrl should escape' );
38
39# check to see if stringify works
40is( $d->stringify(), 'undef', 'stringify handles undef okay' );
41
42# the default is 1, but we want two single quotes
43$d->{printUndef} = 0;
44is( $d->stringify(), "''", 'stringify skips undef when asked nicely' );
45
46is( $d->stringify(*FOO), *FOO . "", 'stringify stringifies globs alright' );
47
48# check for double-quotes if there's an unprintable character
49$d->{tick} = 'auto';
50like( $d->stringify("hi\005"), qr/^"hi/, 'added double-quotes when necessary' );
51
52# if no unprintable character, escape ticks or backslashes
53is( $d->stringify('hi'), "'hi'", 'used single-quotes when appropriate' );
54
55# if 'unctrl' is set
56$d->{unctrl} = 'unctrl';
57like( $d->stringify('double and whack:\ "'), qr!\\ \"!, 'escaped with unctrl' );
58like( $d->stringify("a\005"), qr/^"a\^/, 'escaped ASCII value in unctrl' );
59like( $d->stringify("b\xb6"), qr!^'b.'$!, 'no high-bit escape value in unctrl');
60
61$d->{quoteHighBit} = 1;
62like( $d->stringify("b\266"), qr!^'b\\266!, 'high-bit now escaped in unctrl');
63
64# if 'quote' is set
65$d->{unctrl} = 'quote';
66is( $d->stringify('5@ $1'), "'5\@ \$1'", 'quoted $ and @ fine' );
67is( $d->stringify("5@\e\$1"), '"5\@\e\$1"', 'quoted $ and @ and \e fine' );
68like( $d->stringify("\037"), qr/^"\\c/, 'escaped ASCII value okay' );
69
70# add ticks, if necessary
71is( $d->stringify("no ticks", 1), 'no ticks', 'avoid ticks if asked' );
72
73my $out = tie *OUT, 'TieOut';
74select(OUT);
75
76# test DumpElem, it does its magic with veryCompact set
77$d->{veryCompact} = 1;
78$d->DumpElem([1, 2, 3]);
79is( $out->read, "0..2  1 2 3\n", 'DumpElem worked on array ref');
80$d->DumpElem({ one => 1, two => 2 });
81is( $out->read, "'one' => 1, 'two' => 2\n", 'DumpElem worked on hash ref' );
82$d->DumpElem('hi');
83is( $out->read, "'hi'\n", 'DumpElem worked on simple scalar' );
84$d->{veryCompact} = 0;
85$d->DumpElem([]);
86like( $out->read, qr/ARRAY/, 'DumpElem okay with reference and no veryCompact');
87
88# should compact simple arrays just fine
89$d->{veryCompact} = 1;
90$d->DumpElem([1, 2, 3]);
91is( $out->read, "0..2  1 2 3\n", 'dumped array fine' );
92$d->{arrayDepth} = 2;
93$d->DumpElem([1, 2, 3]);
94is( $out->read, "0..2  1 2 ...\n", 'dumped limited array fine' );
95
96# should compact simple hashes just fine
97$d->DumpElem({ a => 1, b => 2, c => 3 });
98is( $out->read, "'a' => 1, 'b' => 2, 'c' => 3\n", 'dumped hash fine' );
99$d->{hashDepth} = 2;
100$d->DumpElem({ a => 1, b => 2, c => 3 });
101is( $out->read, "'a' => 1, 'b' => 2 ...\n", 'dumped limited hash fine' );
102
103# should just stringify what it is
104$d->{veryCompact} = 0;
105$d->DumpElem([]);
106like( $out->read, qr/ARRAY.+empty array/s, 'stringified empty array ref' );
107$d->DumpElem({});
108like( $out->read, qr/HASH.+empty hash/s, 'stringified empty hash ref' );
109$d->DumpElem(1);
110is( $out->read, "1\n", 'stringified simple scalar' );
111
112# test unwrap
113$DB::signal = $d->{stopDbSignal} = 1;
114is( $d->unwrap(), undef, 'unwrap returns if DB signal is set' );
115undef $DB::signal;
116
117my $foo = 7;
118$d->{dumpReused} = 0;
119$d->unwrap(\$foo);
120is( $out->read, "-> 7\n", 'unwrap worked on scalar' );
121$d->unwrap(\$foo);
122is( $out->read, "-> REUSED_ADDRESS\n", 'unwrap worked on scalar' );
123$d->unwrap({ one => 1 });
124
125# leaving this at zero may cause some subsequent tests to fail
126# if they reuse an address creating an anonymous variable
127$d->{dumpReused} = 1;
128is( $out->read, "'one' => 1\n", 'unwrap worked on hash' );
129$d->unwrap([ 2, 3 ]);
130is( $out->read, "0  2\n1  3\n", 'unwrap worked on array' );
131$d->unwrap(*FOO);
132is( $out->read, '', 'unwrap ignored glob on first try');
133$d->unwrap(*FOO);
134is( $out->read, "*DUMPED_GLOB*\n", 'unwrap worked on glob');
135$d->unwrap(qr/foo(.+)/);
136
137my $modifiers = (qr// =~ /\Q(?^/) ? '^' : '-xism';
138is( $out->read, "-> qr/(?${modifiers}:foo(.+))/\n", 'unwrap worked on Regexp' );
139
140$d->unwrap( sub {} );
141like( $out->read, qr/^-> &CODE/, 'unwrap worked on sub ref' );
142
143# test matchvar
144# test to see if first arg 'eq' second
145ok( Dumpvalue::matchvar(1, 1), 'matchvar matched numbers fine' );
146ok( Dumpvalue::matchvar('hi', 'hi'), 'matchvar matched strings fine' );
147ok( !Dumpvalue::matchvar('hello', 1), 'matchvar caught failed match fine' );
148
149# test compactDump, which doesn't do much
150is( $d->compactDump(3), 3, 'set compactDump to 3' );
151is( $d->compactDump(1), 479, 'compactDump reset to 6*80-1 when less than 2' );
152
153# test veryCompact, which does slightly more, setting compactDump sometimes
154$d->{compactDump} = 0;
155is( $d->veryCompact(1), 1, 'set veryCompact successfully' );
156ok( $d->compactDump(), 'and it set compactDump as well' );
157
158# test set_unctrl
159$d->set_unctrl('impossible value');
160like( $out->read, qr/^Unknown value/, 'set_unctrl caught bad value' );
161is( $d->set_unctrl('quote'), 'quote', 'set quote fine' );
162is( $d->set_unctrl(), 'quote', 'retrieved quote fine' );
163
164# test set_quote
165$d->set_quote('"');
166is( $d->{tick}, '"', 'set_quote set tick right' );
167is( $d->{unctrl}, 'quote', 'set unctrl right too' );
168$d->set_quote('auto');
169is( $d->{tick}, 'auto', 'set_quote set auto right' );
170$d->set_quote('foo');
171is( $d->{tick}, "'", 'default value set to " correctly' );
172
173# test dumpglob
174# should do nothing if debugger signal flag is raised
175$d->{stopDbSignal} = $DB::signal = 1;
176is( $d->dumpglob(*DB::signal), undef, 'returned early with DB signal set' );
177undef $DB::signal;
178
179# test dumping "normal" variables, this is a nasty glob trick
180$foo = 1;
181$d->dumpglob( '', 2, 'foo', local *foo = \$foo );
182is( $out->read, "  \$foo = 1\n", 'dumped glob for $foo correctly' );
183our @bar = (1, 2);
184
185# the key name is a little different here
186$d->dumpglob( '', 0, 'boo', *bar );
187is( $out->read, "\@boo = (\n   0..1  1 2\n)\n", 'dumped glob for @bar fine' );
188
189our %baz = ( one => 1, two => 2 );
190$d->dumpglob( '', 0, 'baz', *baz );
191is( $out->read, "\%baz = (\n   'one' => 1, 'two' => 2\n)\n",
192	'dumped glob for %baz fine' );
193
194SKIP: {
195	skip( "Couldn't open $0 for reading", 1 ) unless open(FILE, '<', $0);
196	my $fileno = fileno(FILE);
197	$d->dumpglob( '', 0, 'FILE', *FILE );
198	is( $out->read, "FileHandle(FILE) => fileno($fileno)\n",
199		'dumped filehandle from glob fine' );
200}
201
202$d->dumpglob( '', 0, 'read', *TieOut::read );
203is( $out->read, '', 'no sub dumped without $all set' );
204$d->dumpglob( '', 0, 'read', \&TieOut::read, 1 );
205is( $out->read, "&read in ???\n", 'sub dumped when requested' );
206
207# see if it dumps DB-like values correctly
208$d->{dumpDBFiles} = 1;
209$d->dumpglob( '', 0, '_<foo', *foo );
210is( $out->read, "\$_<foo = 1\n", 'dumped glob for $_<foo correctly (DB)' );
211
212# test CvGV name
213SKIP: {
214	if (" $Config::Config{'extensions'} " !~ m[ Devel/Peek ]) {
215	    skip( 'no Devel::Peek', 2 );
216	}
217	use_ok( 'Devel::Peek' );
218	is( $d->CvGV_name(\&TieOut::read), 'TieOut::read', 'CvGV_name found sub' );
219}
220
221# test dumpsub
222$d->dumpsub( '', 'TieOut::read' );
223like( $out->read, qr/&TieOut::read in/, 'dumpsub found sub fine' );
224
225# test findsubs
226is( $d->findsubs(), undef, 'findsubs returns nothing without %DB::sub' );
227{
228    no warnings 'once';
229    $DB::sub{'TieOut::read'} = 'TieOut';
230    is( $d->findsubs( \&TieOut::read ), 'TieOut::read', 'findsubs reported sub' );
231}
232
233# now that it's capable of finding the package...
234$d->dumpsub( '', 'TieOut::read' );
235is( $out->read, "&TieOut::read in TieOut\n", 'dumpsub found sub fine again' );
236
237# this should print just a usage message
238$d->{usageOnly} = 1;
239$d->dumpvars( 'Fake', 'veryfake' );
240like( $out->read, qr/^String space:/, 'printed usage message fine' );
241delete $d->{usageOnly};
242
243# this should report @INC and %INC
244$d->dumpvars( 'main', 'INC' );
245like( $out->read, qr/\@INC =/, 'dumped variables from a package' );
246
247# this should report nothing
248$DB::signal = 1;
249$d->dumpvars( 'main', 'INC' );
250is( $out->read, '', 'no dump when $DB::signal is set' );
251undef $DB::signal;
252
253is( $d->scalarUsage('12345'), 5, 'scalarUsage reports length correctly' );
254is( $d->arrayUsage( [1, 2, 3], 'a' ), 3, 'arrayUsage reports correct lengths' );
255is( $out->read, "\@a = 3 items (data: 3 bytes)\n", 'arrayUsage message okay' );
256is( $d->hashUsage({ one => 1 }, 'b'), 4, 'hashUsage reports correct lengths' );
257is( $out->read, "\%b = 1 item (keys: 3; values: 1; total: 4 bytes)\n",
258	'hashUsage message okay' );
259is( $d->hashUsage({ one => [ 1, 2, 3 ]}, 'c'), 6, 'complex hash okay' );
260is( $out->read, "\%c = 1 item (keys: 3; values: 3; total: 6 bytes)\n",
261	'hashUsage complex message okay' );
262
263our $folly = 'one';
264our @folly = ('two');
265our %folly = ( three => '123' );
266is( $d->globUsage(\*folly, 'folly'), 14, 'globUsage reports length correctly' );
267like( $out->read, qr/\@folly =.+\%folly =/s, 'globValue message okay' );
268
269# and now, the real show
270$d->dumpValue(undef);
271is( $out->read, "undef\n", 'dumpValue caught undef value okay' );
272$d->dumpValue($folly);
273is( $out->read, "'one'\n", 'dumpValue worked' );
274$d->dumpValue(@folly);
275is( $out->read, "'two'\n", 'dumpValue worked on array' );
276$d->dumpValue(\$folly);
277is( $out->read, "-> 'one'\n", 'dumpValue worked on scalar ref' );
278
279# dumpValues (the rest of these should be caught by unwrap)
280$d->dumpValues(undef);
281is( $out->read, "undef\n", 'dumpValues caught undef value fine' );
282$d->dumpValues(\@folly);
283is( $out->read, "0  0..0  'two'\n", 'dumpValues worked on array ref' );
284$d->dumpValues('one', 'two');
285is( $out->read, "0..1  'one' 'two'\n", 'dumpValues worked on multiple values' );
286
287