8c6ff4587c5208dac97f2e54b9ee0cc13d2c2ebd
[bse.git] / t / 020-templater / 040-original.t
1 #!perl -w
2 # Basic tests for Squirrel::Template
3 use strict;
4 use Test::More tests => 209;
5 use HTML::Entities;
6
7 sub template_test($$$$;$$);
8
9 my $gotmodule = require_ok('Squirrel::Template');
10
11 my %extra_opts;
12
13 SKIP: {
14   skip "couldn't load module", 15 unless $gotmodule;
15
16   my $flag = 0;
17   my $str = "ABC";
18   my $str2 = "DEF";
19   my ($repeat_limit, $repeat_value);
20
21   my %acts =
22     (
23      ifEq => \&tag_ifeq,
24      iterate_repeat_reset =>
25      [ \&iter_repeat_reset, \$repeat_limit, \$repeat_value ],
26      iterate_repeat =>
27      [ \&iter_repeat, \$repeat_limit, \$repeat_value ],
28      repeat => \$repeat_value,
29      strref => \$str,
30      str => $str,
31      str2 => $str2,
32      with_upper => \&tag_with_upper,
33      cat => \&tag_cat,
34      ifFalse => 0,
35      dead => sub { die "foo\n" },
36      noimpl => sub { die "ENOIMPL\n" },
37     );
38   my %vars =
39     (
40      a =>
41      {
42       b =>
43       {
44        c => "CEE"
45       }
46      },
47      str => $str,
48      somelist => [ 'a' .. 'f' ],
49      somehash => { qw(a 11 b 12 c 14 e 8) },
50      num1 => 101,
51      num2 => 202,
52      testclass => Squirrel::Template::Expr::WrapClass->new("TestClass"),
53      error =>
54      {
55       noimpl => sub { die "ENOIMPL\n" },
56      },
57      callback1 => sub {
58        my ($cb, $templater, @args) = @_;
59
60        for my $foo ("a" .. "e") {
61          $templater->set_var(foo => $foo);
62          $cb->();
63        }
64      },
65      somecode1 => sub { return "FOO" },
66      somecode2 => sub { return [ @_ ] },
67      dumper => sub {
68        use Data::Dumper;
69        Dumper($_[0]);
70      },
71     );
72   template_test("<:str:>", "ABC", "simple", \%acts);
73   template_test("<:strref:>", "ABC", "scalar ref", \%acts);
74   $str = "DEF";
75   template_test("<:strref:>", "DEF", "scalar ref2", \%acts);
76   template_test(<<TEMPLATE, "12345", "iterate", \%acts, "in");
77 <:iterator begin repeat 1 5:><:repeat:><:iterator end repeat:>
78 TEMPLATE
79   template_test(<<TEMPLATE, "1|2|3|4|5", "iterate sep", \%acts, "in");
80 <:iterator begin repeat 1 5:><:repeat:><:
81 iterator separator repeat:>|<:iterator end repeat:>
82 TEMPLATE
83   template_test('<:ifEq [str] "ABC":>YES<:or:>NO<:eif:>', "YES", 
84                 "cond1", \%acts);
85   template_test('<:if Eq [str] "ABC":>YES<:or Eq:>NO<:eif Eq:>', "YES", 
86                 "cond2", \%acts);
87   template_test("<:dead:>", "* foo\n *", "dead", \%acts);
88   template_test("<:noimpl:>", "<:noimpl:>", "noimpl", \%acts);
89   template_test("<:unknown:>", "<:unknown:>", "unknown tag", \%acts);
90   template_test("<:ifDead:><:str:><:or:><:str2:><:eif:>",
91                 "* foo\n *<:ifDead:>ABC<:or:>DEF<:eif:>", "ifDead", \%acts);
92   template_test("<:ifNoimpl:><:str:><:or:><:str2:><:eif:>",
93                 "<:ifNoimpl:>ABC<:or:>DEF<:eif:>", "ifNoimpl", \%acts);
94
95   template_test("<:if!False:>FOO<:eif:>", "FOO", "if!False", \%acts);
96   template_test("<:if !False:>FOO<:eif:>", "FOO", "if !False", \%acts);
97   template_test("<:if!Str:>FOO<:eif:>", "", "if!Str", \%acts);
98   template_test("<:if!Dead:><:str:><:eif:>",
99                 "* foo\n *<:if!Dead:>ABC<:eif:>", "if!Dead", \%acts);
100   template_test("<:if!Noimpl:><:str:><:eif:>",
101                 "<:if!Noimpl:>ABC<:eif:>", "if!Noimpl", \%acts);
102
103   template_test(<<TEMPLATE, <<OUTPUT, "wrap", \%acts, "in");
104 <:wrap wraptest.tmpl title=>[cat "foo " [str]], menu => 1, showtitle => "abc" :>Alpha
105 <:param menu:>
106 <:param showtitle:>
107 <:= params.showtitle :>
108 TEMPLATE
109 <title>foo ABC</title>
110 Alpha
111 1
112 abc
113 abc
114 OUTPUT
115
116   template_test(<<TEMPLATE, <<OUTPUT, "wrap more", \%acts, "both");
117 Before
118 <:wrap wraptest.tmpl title=>[cat "foo " [str]], menu => 1, showtitle => "abc" -:>
119 Alpha
120 <:param menu:>
121 <:param showtitle:>
122 <:-endwrap-:>
123 After
124 TEMPLATE
125 Before
126 <title>foo ABC</title>
127 Alpha
128 1
129 abc
130 After
131 OUTPUT
132
133   template_test(<<TEMPLATE, <<OUTPUT, "wrap with too much parameter text", \%acts, "in");
134 <:wrap wraptest.tmpl title=>[cat "foo " [str]], menu => 1, showtitle => "abc" junk :>Alpha
135 <:param menu:>
136 <:param showtitle:>
137 TEMPLATE
138 * WARNING: Extra data after parameters ' junk' *<title>foo ABC</title>
139 Alpha
140 1
141 abc
142 OUTPUT
143
144   template_test(<<TEMPLATE, <<OUTPUT, "wrap recursive", \%acts, "both");
145 <:wrap wrapself.tmpl title=>[cat "foo " [str]], menu => 1, showtitle => "abc" :>Alpha
146 <:param menu:>
147 <:param showtitle:>
148 TEMPLATE
149 * Error starting wrap: Too many levels of wrap for 'wrapself.tmpl' *<title>foo ABC</title>
150 <title>foo ABC</title>
151 <title>foo ABC</title>
152 <title>foo ABC</title>
153 <title>foo ABC</title>
154 <title>foo ABC</title>
155 <title>foo ABC</title>
156 <title>foo ABC</title>
157 <title>foo ABC</title>
158 <title>foo ABC</title>
159 Alpha
160 1
161 abc
162 OUTPUT
163
164   template_test(<<TEMPLATE, <<OUTPUT, "wrap unknown", \%acts, "both");
165 <:wrap unknown.tmpl:>
166 Body
167 TEMPLATE
168 * Loading wrap: File unknown.tmpl not found *
169 OUTPUT
170
171   template_test(<<TEMPLATE, <<OUTPUT, "unwrapped wrap here", \%acts, "both");
172 before
173 <:wrap here:>
174 after
175 TEMPLATE
176 before
177 * wrap here without being wrapped *
178 after
179 OUTPUT
180
181   template_test(<<TEMPLATE, <<OUTPUT, ".wrap simple", \%acts, "in", \%vars);
182 <:.wrap "wraptest.tmpl", "title": "foo " _ str, "menu": 1, "showtitle":"abc" :>Alpha
183 <:param menu:>
184 <:param showtitle:>
185 <:= params.showtitle :>
186 TEMPLATE
187 <title>foo ABC</title>
188 Alpha
189 1
190 abc
191 abc
192 OUTPUT
193
194   template_test(<<TEMPLATE, <<OUTPUT, ".wrap unknown", \%acts, "both");
195 <:.wrap "unknown.tmpl":>
196 Body
197 TEMPLATE
198 * Loading wrap: File unknown.tmpl not found *
199 OUTPUT
200
201   template_test(<<TEMPLATE, <<OUTPUT, ".wrap recursive", \%acts, "both", \%vars);
202 <:.wrap "wrapself.tmpl", "title":"foo " _ str, "menu":1, "showtitle":"abc" :>Alpha
203 <:= params.menu :>
204 <:= params.showtitle :>
205 TEMPLATE
206 * Error starting wrap: Too many levels of wrap for 'wrapself.tmpl' *<title>foo ABC</title>
207 <title>foo ABC</title>
208 <title>foo ABC</title>
209 <title>foo ABC</title>
210 <title>foo ABC</title>
211 <title>foo ABC</title>
212 <title>foo ABC</title>
213 <title>foo ABC</title>
214 <title>foo ABC</title>
215 <title>foo ABC</title>
216 Alpha
217 1
218 abc
219 OUTPUT
220
221   template_test(<<TEMPLATE, <<OUTPUT, ".wrap more", \%acts, "both", \%vars);
222 Before
223 <:.wrap "wraptest.tmpl", "title":"foo " _ str, "menu":1, "showtitle":"abc" -:>
224 Alpha
225 <:= params.menu:>
226 <:= params.showtitle:>
227 <:-.end wrap-:>
228 After
229 TEMPLATE
230 Before
231 <title>foo ABC</title>
232 Alpha
233 1
234 abc
235 After
236 OUTPUT
237
238   # undefined iterator - replacement should happen on the inside
239   template_test(<<TEMPLATE, <<OUTPUT, "undefined iterator", \%acts);
240 <:iterator begin unknown:>
241 <:if Eq "1" "1":>TRUE<:or:>FALSE<:eif:>
242 <:iterator separator unknown:>
243 <:if Eq "1" "0":>TRUE<:or:>FALSE<:eif:>
244 <:iterator end unknown:>
245 TEMPLATE
246 <:iterator begin unknown:>
247 TRUE
248 <:iterator separator unknown:>
249 FALSE
250 <:iterator end unknown:>
251 OUTPUT
252
253   template_test(<<TEMPLATE, <<OUTPUT, "multi wrap", \%acts, "in");
254 <:wrap wrapinner.tmpl title => "ABC":>
255 Test
256 TEMPLATE
257 <title>ABC</title>
258
259 <head1>ABC</head1>
260
261 Test
262 OUTPUT
263
264   my $switch = <<IN;
265 <:switch:>ignored<:case Eq [strref] "ABC":>ONE<:case Eq [strref] "XYZ":>TWO<:
266 case default:>DEF<:endswitch:>
267 IN
268   $str = "ABC";
269   template_test($switch, "ONE", "switch1", \%acts, "both");
270   $str = "XYZ";
271   template_test($switch, "TWO", "switch2", \%acts, "both");
272   $str = "DEF";
273   template_test($switch, "DEF", "switch def", \%acts, "both");
274
275   my $switch2 = <<IN;
276 <:switch:><:case Eq [strref] "ABC":>ONE<:case Eq [strref] "XYZ":>TWO<:
277 case default:>DEF<:endswitch:>
278 IN
279   $str = "ABC";
280   template_test($switch2, "ONE", "switch without ignored", \%acts, "both");
281
282   template_test(<<IN, <<OUT, "unimplemented switch (by die)", \%acts, "both");
283 <foo><:strref bar |h:></foo><:switch:><:case Eq [strref] "XYZ":>FAIL<:case Eq [unknown] "ABC":><:endswitch:>
284 IN
285 <foo>ABC</foo><:switch:><:case Eq [unknown] "ABC":><:endswitch:>
286 OUT
287
288   template_test(<<IN, <<OUT, "unimplemented switch (by missing)", \%acts, "both");
289 <foo><:strref bar |h:></foo><:switch:><:case Eq [strref] "XYZ":>FAIL<:case Unknown:><:str:><:case Eq [unknown] "ABC":><:str2:><:endswitch:>
290 IN
291 <foo>ABC</foo><:switch:><:case Unknown:>ABC<:case Eq [unknown] "ABC":>DEF<:endswitch:>
292 OUT
293
294   template_test(<<IN, <<OUT, "switch with die in case and unknown", \%acts, "both");
295 <:switch:><:case Eq [strref] "XYZ":>FAIL<:case Dead:><:str:><:case Eq [unknown] "ABC":><:str2:><:endswitch:>
296 IN
297 * foo
298  *<:switch:><:case Eq [unknown] "ABC":>DEF<:endswitch:>
299 OUT
300
301   template_test(<<IN, <<OUT, "switch with die no matches", \%acts, "both");
302 <:switch:><:case Eq [strref] "XYZ":>FAIL<:case Dead:><:str:><:case False:><:str2:><:endswitch:>
303 IN
304 * foo
305  *
306 OUT
307
308   template_test(<<IN, <<OUT, "switch with case !", \%acts, "both");
309 <:switch:><:case !Str:>NOT STR<:case !False:>FALSE<:endswitch:>
310 IN
311 FALSE
312 OUT
313
314   template_test("<:with begin upper:>Alpha<:with end upper:>", "ALPHA", "with", \%acts);
315
316   template_test("<:with begin unknown:>Alpha<:str:><:with end unknown:>", <<EOS, "with", \%acts, "out");
317 <:with begin unknown:>AlphaABC<:with end unknown:>
318 EOS
319
320   template_test("<:include doesnt/exist optional:>", "", "optional include", \%acts);
321   template_test("<:include doesnt/exist:>", "* cannot find include doesnt/exist in path *", "failed include", \%acts);
322   template_test("x<:include included.include:>z", "xyz", "include", \%acts);
323
324   template_test <<IN, <<OUT, "nested in undefined if", \%acts;
325 <:if Unknown:><:if Eq "1" "1":>Equal<:or Eq:>Not Equal<:eif Eq:><:or Unknown:>false unknown<:eif Unknown:>
326 IN
327 <:if Unknown:>Equal<:or Unknown:>false unknown<:eif Unknown:>
328 OUT
329   template_test <<IN, <<OUT, "nested in undefined switch case", \%acts;
330 <:switch:>
331 <:case ifUnknown:><:if Eq 1 1:>Equal<:or Eq:>Unequal<:eif Eq:>
332 <:endswitch:>
333 IN
334 <:switch:><:case ifUnknown:>Equal
335 <:endswitch:>
336 OUT
337
338   { # using - for removing whitespace
339     template_test(<<IN, <<OUT, "space value", \%acts, "both");
340 <foo>
341 <:-str-:>
342 </foo>
343 <foo>
344 <:str-:>
345 </foo>
346 <foo>
347 <:str:>
348 </foo>
349 IN
350 <foo>ABC</foo>
351 <foo>
352 ABC</foo>
353 <foo>
354 ABC
355 </foo>
356 OUT
357
358     template_test(<<IN, <<OUT, "space simple cond", \%acts, "both");
359 <foo>
360 <:-ifStr:>TRUE<:or-:><:eif-:>
361 </foo>
362 <foo2>
363 <:-ifStr-:>
364 TRUE
365 <:-or:><:eif-:>
366 </foo2>
367 <foo3>
368 <:-ifStr-:>
369 TRUE
370 <:-or-:>
371 <:-eif-:>
372 </foo3>
373 <foo4>
374 <:-ifFalse-:>TRUE<:-or-:>FALSE<:-eif-:>
375 </foo4>
376 <foo5>
377 <:-ifFalse-:>
378 TRUE
379 <:-or-:>
380 FALSE
381 <:-eif-:>
382 </foo5>
383 <foo6>
384 <:ifFalse:>
385 TRUE
386 <:or:>
387 FALSE
388 <:eif:>
389 </foo6>
390 IN
391 <foo>TRUE</foo>
392 <foo2>TRUE</foo2>
393 <foo3>TRUE</foo3>
394 <foo4>FALSE</foo4>
395 <foo5>FALSE</foo5>
396 <foo6>
397
398 FALSE
399
400 </foo6>
401 OUT
402
403     template_test(<<IN, <<OUT, "space iterator", \%acts, "both");
404 <foo>
405 <:-iterator begin repeat 1 5 -:>
406 <:-repeat-:>
407 <:-iterator end repeat -:>
408 </foo>
409 <foo2>
410 <:-iterator begin repeat 1 5 -:>
411 <:-repeat-:>
412 <:-iterator separator repeat -:>
413 ,
414 <:-iterator end repeat -:>
415 </foo2>
416 IN
417 <foo>12345</foo>
418 <foo2>1,2,3,4,5</foo2>
419 OUT
420
421     template_test(<<IN, <<OUT, "space switch", \%acts, "both");
422 <foo>
423 <:- switch:>
424
425  <:- case default:>FOO
426 <:- endswitch:>
427 </foo>
428 IN
429 <foo>FOO
430 </foo>
431 OUT
432
433     template_test(<<IN, <<OUT, "while", \%acts);
434 <:.set work = [ "a" .. "z" ] -:>
435 <:.while work.size and work[0] ne "d" -:>
436 <:= work.shift :>
437 <:.end while -:>
438 IN
439 a
440 b
441 c
442 OUT
443
444     template_test(<<IN, <<OUT, "space complex", \%acts, "both");
445 <div class="window">
446   <h1><:str:></h1>
447   <ul class="children list">
448     <:iterator begin repeat 1 2:>
449     <:- switch:>
450     <:- case False:>
451     <li class="error message"><:repeat:></li>
452     <:case str:>
453   </ul>
454   <h2><:repeat:></h2>
455   <ul class="children list">
456     <:- case default:>
457     <li><:repeat:></li>
458     <:- endswitch:>
459     <:iterator end repeat:>
460   </ul>
461 </div>
462 IN
463 <div class="window">
464   <h1>ABC</h1>
465   <ul class="children list">
466     
467   </ul>
468   <h2>1</h2>
469   <ul class="children list">
470     
471   </ul>
472   <h2>2</h2>
473   <ul class="children list">
474     
475   </ul>
476 </div>
477 OUT
478   }
479
480   template_test("<:= unknown :>", "<:= unknown :>", "unknown", \%acts, "", \%vars);
481   template_test(<<TEMPLATE, "2", "multi-statement", \%acts, "", \%vars);
482 <:.set foo = [] :><:% foo.push(1); foo.push(2) :><:= foo.size() -:>
483 TEMPLATE
484
485   template_test(<<TEMPLATE, "2", "multi-statement no ws", \%acts, "", \%vars);
486 <:.set foo=[]:><:%foo.push(1);foo.push(2):><:= foo.size() -:>
487 TEMPLATE
488
489   template_test("<:= str :>", "ABC", "simple exp", \%acts, "", \%vars);
490   template_test("<:=str:>", "ABC", "simple exp no ws", \%acts, "", \%vars);
491   template_test("<:= a.b.c :>", "CEE", "hash methods", \%acts, "", \%vars);
492   template_test(<<IN, <<OUT, "simple set", \%acts, "both", \%vars);
493 <:.set d = "test" -:><:= d :>
494 IN
495 test
496 OUT
497   my @expr_tests =
498     (
499      [ 'num1 + num2', 303 ],
500      [ 'num1 - num2', -101 ],
501      [ 'num1 + num2 * 2', 505 ],
502      [ 'num2 mod 5', '2' ],
503      [ 'num1 / 5', '20.2' ],
504      [ 'num1 div 5', 20 ],
505      [ '+num1', 101 ],
506      [ '-(num1 + num2)', -303 ],
507      [ '"hello " _ str', 'hello ABC' ],
508      [ 'num1 < num2', 1 ],
509      [ 'num1 < 101', '' ],
510      [ 'num1 < 100', '' ],
511      [ 'num1 > num2', '' ],
512      [ 'num2 > num1', 1 ],
513      [ 'num1 > 101', '' ],
514      [ 'num1 == 101.0', '1' ],
515      [ 'num1 == 101', '1' ],
516      [ 'num1 == 100', '' ],
517      [ 'num1 != 101', '' ],
518      [ 'num1 != "101.0"', '' ],
519      [ 'num1 != 100', 1 ],
520      [ 'num1 >= 101', 1 ],
521      [ 'num1 >= 100', 1 ],
522      [ 'num1 >= 102', '' ],
523      [ 'num1 <= 101', 1 ],
524      [ 'num1 <= 100', '' ],
525      [ 'num1 <= 102', '1' ],
526      [ 'num1 <=> 102', '-1' ],
527      [ 'num1 <=> 101', '0' ],
528      [ 'str eq "ABC"', '1' ],
529      [ 'str eq "AB"', '' ],
530      [ 'str ne "AB"', '1' ],
531      [ 'str ne "ABC"', '' ],
532      [ 'str cmp "ABC"', 0 ],
533      [ 'str cmp "AB"', 1 ],
534      [ 'str.lower', 'abc' ],
535      [ 'somelist.size', 6 ],
536      [ '[ 4, 2, 3 ].first', 4 ],
537      [ '[ 1, 4, 9 ].join(",")', "1,4,9" ],
538      [ '[ "xx", "aa" .. "ad", "zz" ].join(" ")', "xx aa ab ac ad zz" ],
539      [ '1 ? "TRUE" : "FALSE"', 'TRUE' ],
540      [ '0 ? "TRUE" : "FALSE"', 'FALSE' ],
541      [ '[ 1 .. 4 ][2]', 3 ],
542      [ 'somelist[2]', "c" ],
543      [ 'somehash["b"]', "12" ],
544      [ 'not 1', '' ],
545      [ 'not 1 or 1', 1 ],
546      [ 'not 1 and 1', "" ],
547      [ '"xabcy" =~ /abc/', 1 ],
548      [ '"xabcy" !~ /abc/', "" ],
549      [ '[ "abc" =~ /(.)(.)/ ][1]', "b" ],
550      [ '"xabcy" !~ /abg/', 1 ],
551      [ '{ "a": 11, "b": 12, "c": 20 }["b"]', 12 ],
552      [ '[ 1, 2, 3 ][1]', 2 ],
553      [ 'testclass.foo', "[TestClass.foo]" ],
554      [ '@undef.defined', "" ],
555      [ '(@{: "abc" })()', "abc" ],
556      [ '(@{a,b: a+b})(12, 13)', '25' ],
557      [ '(@{a,b: a; b; a-b })(10, 5)', '5' ],
558
559      # WrapScalar
560      [ '"foo".length', 3 ],
561      [ '"foo".length(1)', "* scalar.length takes no parameters *" ],
562      [ '"foo".upper', "FOO" ],
563      [ '"foo".upper(1)', "* scalar.upper takes no parameters *" ],
564      [ '"Foo".lower', "foo" ],
565      [ '"Foo".lower(1)', "* scalar.lower takes no parameters *" ],
566      [ '"foo".defined', '1' ],
567      [ '"foo".defined(1)', '* scalar.defined takes no parameters *' ],
568      [ '"foo".trim', "foo" ],
569      [ '" a b ".trim', "a b" ],
570      [ '" a b ".trim(1)', "* scalar.trim takes no parameters *" ],
571      [ '"a b".split.join("|")', "a|b" ],
572      [ '"a,b,c".split(",").join("|")', 'a|b|c' ],
573      [ '"a,b,c".split(",",2).join("|")', 'a|b,c' ],
574      [ '(10.1).format("%.2f")', "10.10" ],
575      [ '(10.1).format("%.2f", 1)', "* scalar.format takes one parameter *" ],
576      [ '"str".evaltag', 'ABC' ],
577      [ '"cat [str] [str2]".evaltag', 'ABCDEF' ],
578      [ '"abc*".quotemeta', "abc\\*" ],
579      [ '"abc".quotemeta(1)', "* scalar.quotemeta takes no parameters *" ],
580      [ '"abcdef".contains("cde")', 1 ],
581      [ '"abcdef".contains("cdf")', "" ],
582      [ '"abcdef".contains("cdf",1)', "* scalar.contains requires one parameter *" ],
583      [ '"abcdefabcdef".index("cde")', 2 ],
584      [ '"abcdefabcdef".index("cdf")', -1 ],
585      [ '"abcdefabcdef".index("cde", 5)', 8 ],
586      [ '"abc".index("ab",1,2)', '* scalar.index requires one or two parameters *' ],
587      [ '"abcdefabcdef".rindex("cde")', 8 ],
588      [ '"abcdefabcdef".rindex("cde", 7)', 2 ],
589      [ '"abcdefabcdef".rindex("cde", 7, 3)', '* scalar.rindex requires one or two parameters *' ],
590      [ "(65).chr", "A" ],
591      [ "(10.1).int", 10 ],
592      [ "(10).int", 10 ],
593      [ "(10).rand < 10 and (10).rand >= 0", 1 ],
594      [ "(-10).abs", "10" ],
595      [ '(10).floor', 10 ],
596      [ '(10.1).floor', 10 ],
597      [ '(-10.1).floor', -11 ],
598      [ '(10).ceil', 10 ],
599      [ '(10.1).ceil', 11 ],
600      [ '(-10.1).ceil', -10 ],
601      [ '"test".is_list', 0 ],
602      [ '"test".is_hash', 0 ],
603      [ '"abc".replace(/(.)(.)(.)/, "$3$2$1")', "cba" ],
604      [ '"abc def ghi".replace(/(?:^|\b)([a-z])/, @{m: m.text.upper }, 1)',
605        'Abc Def Ghi' ],
606      [ '"a&b".escape("html")', 'a&amp;b' ],
607      [ '"abc".match(/b/).start', "1" ],
608      [ '"abc".match(/b/).end', "2" ],
609      [ '"abc".match(/b/).length', "1" ],
610      [ '"abc".match(/b/).text', "b" ],
611      [ '"abc".match(/(b)/).subexpr[0].start', "1" ],
612      [ '"abc".match(/(b)/).subexpr[0].end', "2" ],
613      [ '"abc".match(/(b)/).subexpr[0].length', "1" ],
614      [ '"abc".match(/(b)/).subexpr[0].text', "b" ],
615      [ '"abc".match(/(?<foo>b)/).named["foo"].text', "b" ],
616      [ '"abc".match(/(?<foo>b)/).named["bar"]', "" ],
617      [ '"abcd".substring(1)', "bcd" ],
618      [ '"abcd".substring(1,2)', "bc" ],
619      [ '"abcd".substring(1,-2)', "b" ],
620
621      # WrapArray
622      [ '[ [ 1, 2 ], 3 ].expand.join(",")', "1,2,3" ],
623      [ '[ 1, 2 ].is_list', 1 ],
624      [ '[ 1, 2 ].is_hash', 0 ],
625      [ '[ 1 .. 5 ].shuffle.size', 5 ],
626      [ '([ "a", 1, "b", "2" ].as_hash)["a"]', 1 ],
627      [ '[ 1 .. 5].grep(@{a: a mod 2 == 0 }).join(",")', '2,4' ],
628      [ '[ { a: 3 }, { a: 1 }, { a: 2 } ].sort(@{a,b: b.a <=> a.a }).map(@{a: a.a}).join("")', '321' ],
629      [ '[ "A" .. "Z" ].slice([ 0 .. 5 ]).join("")', 'ABCDEF' ],
630      [ '[ "A" .. "Z" ].slice(0, 1, -2, -1).join("")', 'ABYZ' ],
631      [ '[ "A" .. "Z" ].splice(0, 5).join("")', 'ABCDE' ],
632
633      # WrapHash
634      [ '{ "foo": 1 }.is_list', 0 ],
635      [ '{ "foo": 1 }.is_hash', 1 ],
636      [ '{ foo: 1, bar: 1 }.extend({ bar:2 })["bar"]', 2 ],
637     );
638   for my $test (@expr_tests) {
639     my ($expr, $result) = @$test;
640
641     template_test("<:= $expr :>", $result, "expr: $expr", \%acts, "", \%vars);
642   }
643
644   template_test(<<IN, "", "define no use", \%acts, "both", \%vars);
645 <:-.define foo:>
646 <:.end-:>
647 <:-.define bar:>
648 <:.end define-:>
649 IN
650   template_test(<<IN, "avaluebvalue", "define with call", \%acts, "both", \%vars);
651 <:-.define foo:>
652 <:-= avar -:>
653 <:.end-:>
654 <:.call "foo", avar:"avalue"-:>
655 <:.call "foo",
656   avar:"bvalue"-:>
657 IN
658   template_test(<<IN, "2716", "define defaults with call", \%acts, "both", \%vars);
659 <:-.define foo; abc:1, def:abc+5 :>
660 <:-= abc -:><:= def -:>
661 <:.end-:>
662 <:.call "foo", "abc":"2"-:>
663 <:.call "foo" -:>
664 IN
665   template_test(<<IN, "other value", "external call", \%acts, "", \%vars);
666 <:.call "called.tmpl", "avar":"other value"-:>
667 IN
668   template_test(<<IN, "This was preloaded", "call preloaded", \%acts, "both", \%vars);
669 <:.call "preloaded"-:>
670 IN
671   template_test(<<IN, <<OUT, "simple .for", \%acts, "", \%vars);
672 <:.for x in [ "a" .. "d" ] -:>
673 Value: <:= x :> Index: <:= loop.index :> Count: <:= loop.count:> Prev: <:= loop.prev :> Next: <:= loop.next :> Even: <:= loop.even :> Odd: <:= loop.odd :> Parity: <:= loop.parity :> is_first: <:= loop.is_first :> is_last: <:= loop.is_last :> Current by index: <:= loop.current :>-
674 <:.end-:>
675 IN
676 Value: a Index: 0 Count: 1 Prev:  Next: b Even:  Odd: 1 Parity: odd is_first: 1 is_last:  Current by index: a-
677 Value: b Index: 1 Count: 2 Prev: a Next: c Even: 1 Odd:  Parity: even is_first:  is_last:  Current by index: b-
678 Value: c Index: 2 Count: 3 Prev: b Next: d Even:  Odd: 1 Parity: odd is_first:  is_last:  Current by index: c-
679 Value: d Index: 3 Count: 4 Prev: c Next:  Even: 1 Odd:  Parity: even is_first:  is_last: 1 Current by index: d-
680 OUT
681   template_test(<<IN, <<OUT, "simple .if", \%acts, "", \%vars);
682 <:.if "a" eq "b" :>FAIL<:.else:>SUCCESS<:.end:>
683 <:.if "a" eq "a" :>SUCCESS<:.else:>FAIL<:.end:>
684 <:.if "a" eq "c" :>FAIL1<:.elsif "a" eq "a":>SUCCESS<:.else:>FAIL2<:.end:>
685 IN
686 SUCCESS
687 SUCCESS
688 SUCCESS
689 OUT
690   template_test(<<IN, <<OUT, "unknown .if", \%acts, "", \%vars);
691 <:.if unknown:>TRUE<:.end:>
692 <:.if "a" eq "a":>TRUE<:.elsif unknown:>TRUE<:.end:>
693 <:.if "a" eq "b" :>TRUE<:.elsif unknown:>TRUE<:.end:>
694 <:.if "a" ne "a" :>TRUE<:.elsif 0:>ELIF<:.elsif unknown:>TRUE<:.end:>
695 IN
696 <:.if unknown:>TRUE<:.end:>
697 TRUE
698 <:.if 0 :><:.elsif unknown:>TRUE<:.end:>
699 <:.if 0 :><:.elsif unknown:>TRUE<:.end:>
700 OUT
701
702   template_test(<<IN, <<OUT, "stack overflow on .call", \%acts, "", \%vars);
703 <:.define foo:>
704 <:-.call "foo"-:>
705 <:.end:>
706 <:-.call "foo"-:>
707 IN
708 Error opening scope for call: Too many scope levels
709 Backtrace:
710   .call 'foo' from test:1
711   .call 'foo' from test:1
712   .call 'foo' from test:1
713   .call 'foo' from test:1
714   .call 'foo' from test:1
715   .call 'foo' from test:1
716   .call 'foo' from test:1
717   .call 'foo' from test:1
718   .call 'foo' from test:1
719   .call 'foo' from test:1
720   .call 'foo' from test:1
721   .call 'foo' from test:1
722   .call 'foo' from test:1
723   .call 'foo' from test:1
724   .call 'foo' from test:1
725   .call 'foo' from test:1
726   .call 'foo' from test:1
727   .call 'foo' from test:1
728   .call 'foo' from test:1
729   .call 'foo' from test:1
730   .call 'foo' from test:1
731   .call 'foo' from test:1
732   .call 'foo' from test:1
733   .call 'foo' from test:1
734   .call 'foo' from test:1
735   .call 'foo' from test:1
736   .call 'foo' from test:1
737   .call 'foo' from test:1
738   .call 'foo' from test:1
739   .call 'foo' from test:1
740   .call 'foo' from test:1
741   .call 'foo' from test:1
742   .call 'foo' from test:1
743   .call 'foo' from test:1
744   .call 'foo' from test:1
745   .call 'foo' from test:1
746   .call 'foo' from test:1
747   .call 'foo' from test:1
748   .call 'foo' from test:1
749   .call 'foo' from test:1
750   .call 'foo' from test:1
751   .call 'foo' from test:1
752   .call 'foo' from test:1
753   .call 'foo' from test:1
754   .call 'foo' from test:1
755   .call 'foo' from test:1
756   .call 'foo' from test:1
757   .call 'foo' from test:3
758 OUT
759
760   template_test(<<IN, <<OUT, "evaltags", \%acts, "", \%vars);
761 <:= "str".evaltag :>
762 <:= "cat [str] [str2]".evaltag :>
763 IN
764 ABC
765 ABCDEF
766 OUT
767
768 template_test(<<IN, <<OUT, "hash methods", \%acts, "", \%vars);
769 <:.set foo = { "abc": 1, "def":2 } -:>
770 <:% foo.set("ghi", 3) -:>
771 ghi: <:= foo.ghi :>
772 keys: <:= foo.keys.sort.join(",") :>
773 size: <:= foo.size :>
774 values: <:= foo.values.sort.join(",") :>
775 <:.for i in foo.list -:>
776 <:= i.key _ "=" _ i.value :>
777 <:.end for-:>
778 IN
779 ghi: 3
780 keys: abc,def,ghi
781 size: 3
782 values: 1,2,3
783 abc=1
784 def=2
785 ghi=3
786 OUT
787
788 template_test(<<IN, <<OUT, "array methods", \%acts, "", \%vars);
789 <:.set foo = [ 1, 2, 4 ] -:>
790 <:% foo.set(2, 3) -:>
791 2: <:= foo[2] :>
792 <:.set foo = [ "A" .. "J" ] -:>
793 <:= foo.splice(8, 2, [ "Y", "Z" ]).join("") :>
794 <:= foo.join("") :>
795 <:.set foo = [ "A" .. "J" ] -:>
796 <:= foo.splice(5).join("") :>
797 <:= foo.join("") :>
798 IN
799 2: 3
800 IJ
801 ABCDEFGHYZ
802 FGHIJ
803 ABCDE
804 OUT
805
806   template_test(<<IN, <<OUT, "set undef", \%acts, "", \%vars);
807 <:.set foo = unknown :>
808 <:.set bar = error.noimpl :>
809 IN
810 <:.set foo = unknown :>
811 <:.set bar = error.noimpl :>
812 OUT
813
814   template_test(<<IN, <<OUT, "iterateover", \%acts, "", \%vars);
815 <:.iterateover callback1-:>
816 <:= foo -:>
817 <:.end:>
818 IN
819 abcde
820 OUT
821
822   template_test(<<'IN', <<OUT, "globals default variable", \%acts, "", \%vars);
823 <:.set globals.foo = "test" -:>
824 <:= globals.foo :>
825 <:.set name="foo" -:>
826 <:= testclass.$name :>
827 <:= testclass.$name() :>
828 <:= testclass.$name(1) :>
829 IN
830 test
831 [TestClass.foo]
832 [TestClass.foo]
833 [TestClass.foo]
834 OUT
835
836   template_test(<<'IN', <<OUT, "function calls", \%acts, "", \%vars);
837 <:= somecode1() :>
838 <:= somecode2().join(",") :>
839 <:= somecode2("a", "b").join(",") :>
840 IN
841 FOO
842
843 a,b
844 OUT
845
846   {
847     local $extra_opts{error_not_defined} = 1;
848     template_test(<<'IN', <<'OUT', "error !defined: expr", \%acts, "", \%vars);
849 <:= unknown_var :>
850 IN
851 * Variable 'unknown_var' not set
852  *
853 OUT
854     template_test(<<'IN', <<'OUT', "error !defined: .if", \%acts, "", \%vars);
855 <:.if unknown_var -:>
856 a
857 <:-.else -:>
858 b
859 <:-.end if:>
860 IN
861 * Variable 'unknown_var' not set
862  *
863 OUT
864     template_test(<<'IN', <<'OUT', "error !defined: .set", \%acts, "", \%vars);
865 <:.set foo = unknown_var :>
866 IN
867 * Variable 'unknown_var' not set
868  *
869 OUT
870   }
871 }
872
873 sub template_test ($$$$;$$) {
874   my ($in, $out, $desc, $acts, $stripnl, $vars) = @_;
875
876   $stripnl ||= 'none';
877   $in =~ s/\n$// if $stripnl eq 'in' || $stripnl eq 'both';
878   $out =~ s/\n$// if $stripnl eq 'out' || $stripnl eq 'both';
879
880   my $templater = Squirrel::Template->new
881     (
882      template_dir=>'t/templates',
883      preload => "preload.tmpl",
884      formats =>
885      {
886       html => sub {
887         encode_entities($_[0], '&<>');
888       }
889      },
890      %extra_opts,
891     );
892
893   my $result = $templater->replace_template($in, $acts, undef, "test", $vars);
894
895   is($result, $out, $desc);
896 }
897
898 sub iter_repeat_reset {
899   my ($rlimit, $rvalue, $args) = @_;
900
901   ($$rvalue, $$rlimit) = split ' ', $args;
902   --$$rvalue;
903 }
904
905 sub iter_repeat {
906   my ($rlimit, $rvalue) = @_;
907
908   ++$$rvalue <= $$rlimit;
909 }
910
911 sub tag_ifeq {
912   my ($args, $acts, $func, $templater) = @_;
913
914   my @args = get_expr($args, $acts, $templater);
915
916   @args >= 2
917     or die "ifEq takes 2 arguments";
918
919   $args[0] eq $args[1];
920 }
921
922 sub get_expr {
923   my ($origargs, $acts, $templater) = @_;
924
925   my @values;
926   my $args = $origargs;
927   while ($args) {
928     if ($args =~ s/\s*\[([^\[\]]+)\]\s*//) {
929       my $expr = $1;
930       my ($func, $funcargs) = split ' ', $expr, 2;
931       exists $acts->{$func} or die "ENOIMPL\n";
932       push @values, scalar $templater->perform($acts, $func, $funcargs, $expr);
933     }
934     elsif ($args =~ s/\s*\"((?:[^\"\\]|\\[\"\\]|\"\")*)\"\s*//) {
935       my $str = $1;
936       $str =~ s/(?:\\([\"\\])|\"(\"))/$1 || $2/eg;
937       push @values, $str;
938     }
939     elsif ($args =~ s/\s*(\S+)\s*//) {
940       push @values, $1;
941     }
942     else {
943       print "Arg parse failure with '$origargs' at '$args'\n";
944       exit;
945     }
946   }
947   
948   @values;
949 }
950
951 sub tag_with_upper {
952   my ($args, $text) = @_;
953
954   return uc($text);
955 }
956
957 sub tag_cat {
958   my ($args, $acts, $func, $templater) = @_;
959
960   return join "", $templater->get_parms($args, $acts);
961 }
962
963 package TestClass;
964
965 sub foo {
966   return "[TestClass.foo]";
967 }