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