1#!/usr/bin/perl -w
2#
3# A script to test and demonstrate the XS version of Template::Stash.
4#
5# Sadly, it looks like getrusage() may not return reasonable RSS values
6# on Linux and many other operating systems. It works great on FreeBSD
7# though.
8#
9# $Id$
10#
11
12# paths to Template version we want to test
13use lib qw( ./lib
14            ../lib
15	    ./blib/arch/auto/Template/Stash/XS
16	    ../blib/arch/auto/Template/Stash/XS'
17);
18
19use strict;
20use Template;
21use Template::Stash;
22use Template::Stash::XS;
23use BSD::Resource;
24use CGI;
25
26# test package
27package Foo;
28
29sub new 	{ return bless {}, $_[0]; }
30sub new_av      { return bless [ 1,2,3 ], $_[0]; }
31sub new_sv      { return bless "hello", $_[0]; }
32sub bar 	{ return 1001; }
33sub baz 	{ return "This is baz method from Foo."; }
34sub more 	{ return "I got " . $_[1] . " for more"; }
35sub newnew 	{ return new Foo; }
36sub err		{ return ( undef, "This is the ''error message''\n" ); }
37
38# main
39package main;
40
41# test data
42my $params = {
43    nodef     => undef,
44    zero      => 0,
45    one       => 1,
46    a_0	      => [],
47    a_1	      => [1],
48    a_2	      => [2,1],
49    bar	      => { baz => { boz => { bean => 20 } } },
50    i	      => 0,
51    string    => 'The quick brown fox jumped over the lazy dog',
52    spaced    => "This  is  a   test       string with   s  p  a  c  e s",
53    hash      => { a => 'b', c => 'd' },
54    metavars  => [ qw( foo bar baz qux wiz waz woz ) ],
55    people    => [ { id => 'tom',   name => 'Tomas' },
56	 	   { id => 'dick',  name => 'Richard' },
57		   { id => 'harry', name => 'Harold' },
58		   { id => 'wes',   name => 'Wesley' },
59		   { id => 'andy',  name => 'Andrew' },
60		   { id => 'jen',   name => 'Jennifer' },
61		   { id => 'larry', name => 'Larry' } ],
62    primes    => [ 6899, 13, 11, 69931, 17, 19, 682547, 2, 3, 5, 7 ],
63    phones    => { 3141 => 'Alpha', 5131 => 'Beta', 4131 => 'Gamma' },
64    groceries => { qw( a 1 b 2 c 3 d 4 e 5 f 6 g 7 h 8 i 9 j 10),
65		   'Flour' => 3, 'Milk' => 1,    'Peanut Butter' => 21,
66		   'Eggs'  => 5, 'Celery' => 15, 'Apples' => 12  },
67    stuff     => [ { a => 'apple',   b => 'banana',  c => 'carrot'    },
68    		   { a => 'apache',  b => 'bsd',     c => 'commodore' },
69    		   { a => 'asada',   b => 'beef',    c => 'carne'  } ],
70    method    => new Foo,
71    quux      => [ new Foo, new_av Foo, new_av Foo ],
72    cgi       => CGI->new('mode=submit&debug=1'),
73    ref       => { a => sub { return "a sub [@_]" },
74	 	   j => { k => 3, l => 5, m => { n => sub { "nsub [@_]" } } },
75	           z => sub { return "z called ".&{shift()}(10, 20, 30); } },
76};
77
78$params->{r}->{e}->{c}->{u}->{r}->{s}->{e} = $params;
79$params->{recurse} = $params;
80
81
82print "XS Module: " , Template::Stash::XS::cvsid() , "\n";
83
84# setup template object, etc.
85my $TMPDIR = $ENV{'TMPDIR'} || '.';
86my $o;
87
88my $fast_tt = new Template ({
89	STASH => new Template::Stash::XS,
90	ABSOLUTE => 1,
91	RELATIVE => 1,
92	COMPILE_DIR => $TMPDIR,
93        COMPILE_EXT => '.ttc2',
94	CACHE_SIZE => 64,
95	OUTPUT => \$o });
96
97my $slow_tt = new Template ({
98	STASH => new Template::Stash,
99	ABSOLUTE => 1,
100	RELATIVE => 1,
101	COMPILE_DIR => $TMPDIR,
102        COMPILE_EXT => '.ttc2',
103	CACHE_SIZE => 64,
104	OUTPUT => \$o });
105
106$Template::Filters::FILTERS->{ baz } = [];	# avoid silly warning
107$Template::Filters::FILTERS->{ baz } = [
108    sub {
109        my $context = shift;
110        my $word = shift || 'baz';
111        return sub {
112            my $text = shift;
113            $text =~ s/^/$word: /gm;
114            return $text;
115        };
116    }, 1 ];
117
118my $template;
119my $expected;
120
121if (@ARGV) {
122  $template = shift @ARGV;
123
124} else {
125  $template = "$TMPDIR/testing.tmpl";
126  unlink $template;
127  open FH, ">$template";
128  while(<DATA>) { last if /^__END__/; print FH; } close FH;
129}
130
131# verify that we get expected results
132{
133  $slow_tt->process($template, $params) or die "SLOW TT: " . $slow_tt->error;
134  print " Template: ", $template, "\n";
135  print "   Length: ", length($o)," bytes\n";
136  $expected = $o;
137  $o = '';
138
139  $fast_tt->process($template, $params) or die "FAST TT: " . $fast_tt->error;
140  if($expected ne $o) {
141    print "   GOT: $o\n";
142    print "=" x 60, "\n";
143    print "WANTED: $expected\n";
144    die "unexpected output from fast_tt->process\n";
145  } else {
146    print "   Status: XS output OK\n\n";
147  }
148}
149
150# here comes the big test
151for my $loops (10, 100, 1000) {
152
153  print STDERR "=" x 70, "\n\n";
154
155  print STDERR "Evaluating template with original stash $loops times...";
156
157  my $slow_stat = get_usage();
158  # Do the slow test
159  for (1..$loops) {
160    $o = '';
161    $slow_tt->process($template, $params) or die $Template::ERROR;
162    die "''$o'' ne ''$expected''" unless $o eq $expected;
163  }
164  my $slow_result = get_usage($slow_stat);
165  print STDERR "done.\n";
166
167  printf(STDERR
168	"Usr: %.2fs, Sys: %.2fs, Total: %.2fs, RSS: %dKB (%dKB change)\n",
169	@$slow_result);
170
171  print STDERR "\nEvaluating template with new XS stash $loops times...";
172
173  my $fast_stat = get_usage();
174  # Do the fast test
175  for (1..$loops) {
176    $o = '';
177    $fast_tt->process($template, $params) or die $Template::ERROR;
178    die "''$o'' ne ''$expected''" unless $o eq $expected;
179  }
180  my $fast_result = get_usage($fast_stat);
181
182  print STDERR "done.\n";
183
184  printf(STDERR
185	"Usr: %.2fs, Sys: %.2fs, Total: %.2fs, RSS: %dKB (%dKB change)\n",
186	@$fast_result);
187
188  printf(STDERR
189	"\n\tImprovement: %.2fX\n\n", $slow_result->[2] / $fast_result->[2]);
190
191}
192
193# If it's been enabled...
194# print Template::Stash::XS::performance(1);
195
196unlink "$TMPDIR/testing.tmpl";
197exit(0);
198
199
200# returns arrayref with user, system and total time
201# optionally subtracts given arrayref.
202sub get_usage {
203  my $a_ref = shift;
204
205  my ($usertime, $systemtime, $maxrss) = getrusage(RUSAGE_SELF);
206  my $maxrss_delta = $maxrss;
207
208  if (defined($a_ref)) {
209    $usertime     -= $a_ref->[0];
210    $systemtime   -= $a_ref->[1];
211    $maxrss_delta -= $a_ref->[3];
212  } else {
213    $maxrss_delta = 0;
214  }
215
216  return [ $usertime, $systemtime, $usertime + $systemtime,
217	   $maxrss, $maxrss_delta ];
218}
219
220
221__DATA__
222
223< here is the template >
224
225defined tests:
2260) [% totallynotdef.defined ? 'fail' : 'okay' %]
2271) [% totallynotdef.defined ? 'fail' : 'okay' %]
2282) [% zero.defined ? 'okay' : 'fail' %]
2293) [% one.defined ? 'okay' : 'fail' %]
2304) [% nodef ? 'fail' : 'okay' %]
231
232foreach:
2330) size: [% primes.size %] ([% primes.nsort.first %]..[% primes.nsort.last %])
2341) forward: [% FOREACH p = primes %] [% p %] [% END %]
2352) reverse: [% FOREACH p = primes.reverse %] [% p %] [% END %]
2363) hash: [% FOREACH p = stuff %] [% p.a %] [% p.b %] [% p.c %] [% END %]
2374) hash sort keys: [% FOREACH p = phones.sort %] [% p %] [% END %]
2385) [% FOREACH people.sort('id') -%] [% name +%] [% END %]
2396) reverse 0. [% a_0.reverse.join(",") %]
2407) reverse 1. [% a_1.reverse.join(",") %]
2418) reverse 2. [% a_2.reverse.join(",") %]
242
243first and last:
2440) [% string %] [% metavars.first %] [% metavars.last %]
2451) [% r.e.c.u.r.s.e.primes.nsort.first %]...[% recurse.primes.nsort.last %]
2462) [% r.e.c.u.r.s.e.primes.sort.first %]...[% recurse.primes.sort.last %]
247
248string split.join:
2490) [% string.length %]
2501) [% string.split.join('_') %]
2512) [% spaced.split.join('_') %]
252
253hash: (each, keys, values) join:
2540) [% hash.each.join(', ') %]
2551) [% hash.keys.join(', ') %]
2562) [% hash.values.join(', ') %]
257
258first, last, size:
2590) [% metavars.first %]
2601) [% metavars.last %]
2612) [% metavars.size %]
2623) [% metavars.max %]
263
264joins:
2650) [% metavars.join %]
2661) [% metavars.join(', ') %]
267
268assign and repeat:
2690) [% string = 'foo' %] [% string.repeat(3) %]
270
271more foreach, sort, etc:
2720) [% FOREACH person = people.sort('id') -%] [% person.name +%] [% END %]
2731) [% FOREACH person = people.sort('name') -%] [% person.name +%] [% END %]
2742) [% FOREACH n = phones.sort -%] [% phones.$n %] is [% n %], [% END %]
2753) [% FOREACH n = groceries.nsort.reverse -%] I got [% groceries.$n %] kilos of [% n %]! [% END %]
2764) [% FOREACH item = [ 'foo', 'bar', 'baz' ] -%]
277           [%- "<ul>\n" IF loop.first %]
278           <li>[% loop.count %]/[% loop.size %]: [% item %]
279           [%- "</ul>\n" IF loop.last %]
280   [% END %]
281
282commify:
2830) [% FOREACH item = people %][%item.name%][%UNLESS loop.last%],[%END%][%END%]
2841) [% FOREACH item = people; item.name; ',' UNLESS loop.last; END; %]
285
286methods:
2870) [% method.bar %]
2881) [% method.baz %]
2892) [% method.bad %]
2903) [% method.newnew.bar %]
2914) [% method.more("stuff") %]
2925) [% quux.first.bar %] -- [% quux.last.more("junk") %]
2936) [% x = quux.1; x.0 %]
294
295lots o' dots:
2960) [% bar.baz.boz.bean %]
2971) [% a.b.c.d.e.f = [ 1 2 3 4 ] %]
2982) [% a.b.c.d.e.f.join(",") %]
299
300include/block: [% BLOCK testblock %]bar=[% bar %]foo=[% foo %][% END %]
3010) [% INCLUDE testblock bar=2, foo='cat sat on mat' %]
302
303process/block: [% BLOCK testproc %]one=[% one %]zero=[% zero %]foo=[% foo %][% END %]
3040) [% PROCESS testproc bar=2, foo='matt sat on cat' %]
305
306slices:
3070) [% items = [ 'foo', 'bar', 'baz' ];
308   take  = [ 0, 2 ];
309   slice = items.$take;
310   slice.join(', '); -%]
311
3121) [% items = { foo = 'one', bar = 'two', baz = 'three' };
313   take  = [ 'foo', 'baz' ];
314   slice = items.$take;
315   slice.join(', '); %]
316
317cgi:
3180) [% cgi.param('mode') %]
3191) [% cgi.start_form %]
320   [% cgi.popup_menu(name   =>   'items',
321                     values => [ 'foo' 'bar' 'baz' ]) %]
322   [% cgi.end_form %]
323
324if/else:
3250) [% IF one %] okay [% END %]
3261) [% IF one %] okay [% ELSE %] fail [% END %]
3272) [% IF one and string %] okay [% ELSE %] fail [% END %]
3283) [% IF one && string %] okay [% ELSE %] fail [% END %]
3294) [% IF false || one %] okay [% ELSE %] fail [% END %]
3305) [% IF zero && one %] fail [% ELSE %] okay [% END %]
3316) [% " okay" UNLESS zero %]
3327) [% IF recurse.one %] okay [% ELSE %] fail [% END %]
3338) [% IF r.e.c.u.r.s.e.one %] okay [% ELSE %] fail [% END %]
3349) [% IF r.e.c.u.r.s.e.one.defined %] okay [% ELSE %] fail [% END %]
335
336ref:
3370) a: [% ref.a %] a(5): [% ref.a(5) %] a(5,10): [% ref.a(5,10) %]
338
339assignments:
340[% ten    = 10
341   twenty = 20
342   thirty = twenty + ten
343   forty  = 2 * twenty
344   fifty  = 100 div 2
345   six    = twenty mod 7 -%]
3460) 10=[%ten%] 20=[%twenty%] 30=[%thirty%] 40=[%forty%] 50=[%fifty%] 6=[%six%]
347
348[%- DEFAULT
349    seventy = 70
350    fifty   = -5 -%]
3511) 50=[%fifty%] 70=[%seventy%]
352
353[%- foo = { bar = 'Baz' } -%]
3542) foo=[% foo.bar %]
355
3563) [% META title   = 'The Cat in the Hat'
357           author  = 'Dr. Seuss'
358           version = 1.23 -%][% template.title %]
359   [% template.author %]
360
361errors, try, catch, etc:
362
3630) [% TRY %]
364     ...blah...blah...
365     [% CALL somecode %]
366     ...etc...
367     [% INCLUDE someblock %]
368     ...and so on...
369  [% CATCH %]
370     An error of type [% error.type %] occurred!
371  [% END %]
372
3731) [% TRY -%]
374   [% INCLUDE missingfile -%]
375   [% CATCH file ; "File Error! $error.info"; END %]
376
3772) [% TRY -%]
378   This gets printed
379   [% THROW food 'carrots' %]
380   This doesn't
381   [% CATCH food %]
382   culinary delights: [% error.info %]
383   [% END %]
384
3853) [% TRY -%][% method.err %][% CATCH %]error type=[% error.type %]
386   info=[% error.info %][% END %]
387
388views & filters
3890) [% VIEW my.foo quux=33; END -%] [% my.foo.quux %]
3901) [% FILTER baz('quux') -%] the itsy bitsy spider [% END %]
3912) [% FILTER baz -%] the itsy bitsy spider [% END %]
392
393more tests (crashme, etc.)
3940) [% x = [1]; x.y.z = 2; %]
3950) [% x = [1,2]; x.a.b.c = 3; %]
396
397--------------------
398end of template test ... have a pleasant day
399--------------------
400
401__END__
402
403This stuff leaks HUGE amounts of memory:
404
405ref:
406[% b = \ref.a -%]
407b: [% b %] b(5): [% b(5) %] b(5,10): [% b(5,10) %]
408[% c = \ref.a(10,20) -%]
409c: [% c %] c(30): [% c(30) %] c(30,40): [% c(30,40) %]
410[% f = \ref.j.k -%]
411f: [% f %]
412[% f = \ref.j.m.n -%]
413f: [% f %] f(11): [% f(11) %]
414
415
416