Rietveld Code Review Tool
Help | Bug tracker | Discussion group | Source code | Sign in
(1698)

Side by Side Diff: src/pkg/old/template/template_test.go

Issue 5979046: code review 5979046: old/regexp, old/template: delete. (Closed)
Patch Set: diff -r 6c742b3b04d1 https://go.googlecode.com/hg/ Created 11 years, 11 months ago
Left:
Right:
Use n/p to move between diff chunks; N/P to move between comments. Please Sign in to add in-line comments.
Jump to:
View unified diff | Download patch
« no previous file with comments | « src/pkg/old/template/parse.go ('k') | no next file » | no next file with comments »
Toggle Intra-line Diffs ('i') | Expand Comments ('e') | Collapse Comments ('c') | Show Comments Hide Comments ('s')
OLDNEW
(Empty)
1 // Copyright 2009 The Go Authors. All rights reserved.
2 // Use of this source code is governed by a BSD-style
3 // license that can be found in the LICENSE file.
4
5 package template
6
7 import (
8 "bytes"
9 "encoding/json"
10 "fmt"
11 "io"
12 "io/ioutil"
13 "os"
14 "strings"
15 "testing"
16 )
17
18 type Test struct {
19 in, out, err string
20 }
21
22 type T struct {
23 Item string
24 Value string
25 }
26
27 type U struct {
28 Mp map[string]int
29 }
30
31 type S struct {
32 Header string
33 HeaderPtr *string
34 Integer int
35 IntegerPtr *int
36 NilPtr *int
37 InnerT T
38 InnerPointerT *T
39 Data []T
40 Pdata []*T
41 Empty []*T
42 Emptystring string
43 Null []*T
44 Vec []interface{}
45 True bool
46 False bool
47 Mp map[string]string
48 JSON interface{}
49 Innermap U
50 Stringmap map[string]string
51 Ptrmap map[string]*string
52 Iface interface{}
53 Ifaceptr interface{}
54 }
55
56 func (s *S) PointerMethod() string { return "ptrmethod!" }
57
58 func (s S) ValueMethod() string { return "valmethod!" }
59
60 var t1 = T{"ItemNumber1", "ValueNumber1"}
61 var t2 = T{"ItemNumber2", "ValueNumber2"}
62
63 func uppercase(v interface{}) string {
64 s := v.(string)
65 t := ""
66 for i := 0; i < len(s); i++ {
67 c := s[i]
68 if 'a' <= c && c <= 'z' {
69 c = c + 'A' - 'a'
70 }
71 t += string(c)
72 }
73 return t
74 }
75
76 func plus1(v interface{}) string {
77 i := v.(int)
78 return fmt.Sprint(i + 1)
79 }
80
81 func writer(f func(interface{}) string) func(io.Writer, string, ...interface{}) {
82 return func(w io.Writer, format string, v ...interface{}) {
83 if len(v) != 1 {
84 panic("test writer expected one arg")
85 }
86 io.WriteString(w, f(v[0]))
87 }
88 }
89
90 func multiword(w io.Writer, format string, value ...interface{}) {
91 for _, v := range value {
92 fmt.Fprintf(w, "<%v>", v)
93 }
94 }
95
96 func printf(w io.Writer, format string, v ...interface{}) {
97 io.WriteString(w, fmt.Sprintf(v[0].(string), v[1:]...))
98 }
99
100 var formatters = FormatterMap{
101 "uppercase": writer(uppercase),
102 "+1": writer(plus1),
103 "multiword": multiword,
104 "printf": printf,
105 }
106
107 var tests = []*Test{
108 // Simple
109 {"", "", ""},
110 {"abc", "abc", ""},
111 {"abc\ndef\n", "abc\ndef\n", ""},
112 {" {.meta-left} \n", "{", ""},
113 {" {.meta-right} \n", "}", ""},
114 {" {.space} \n", " ", ""},
115 {" {.tab} \n", "\t", ""},
116 {" {#comment} \n", "", ""},
117 {"\tSome Text\t\n", "\tSome Text\t\n", ""},
118 {" {.meta-right} {.meta-right} {.meta-right} \n", " } } } \n", ""},
119
120 // Variables at top level
121 {
122 in: "{Header}={Integer}\n",
123
124 out: "Header=77\n",
125 },
126
127 {
128 in: "Pointers: {*HeaderPtr}={*IntegerPtr}\n",
129
130 out: "Pointers: Header=77\n",
131 },
132
133 {
134 in: "Stars but not pointers: {*Header}={*Integer}\n",
135
136 out: "Stars but not pointers: Header=77\n",
137 },
138
139 {
140 in: "nil pointer: {*NilPtr}={*Integer}\n",
141
142 out: "nil pointer: <nil>=77\n",
143 },
144
145 {
146 in: `{"Strings" ":"} {""} {"|"} {"\t\u0123 \x23\\"} {"\"}{\\"}`,
147
148 out: "Strings: | \t\u0123 \x23\\ \"}{\\",
149 },
150
151 {
152 in: "{`Raw strings` `:`} {``} {`|`} {`\\t\\u0123 \\x23\\`} {`}{\ \`}",
153
154 out: "Raw strings: | \\t\\u0123 \\x23\\ }{\\",
155 },
156
157 {
158 in: "Characters: {'a'} {'\\u0123'} {' '} {'{'} {'|'} {'}'}",
159
160 out: "Characters: 97 291 32 123 124 125",
161 },
162
163 {
164 in: "Integers: {1} {-2} {+42} {0777} {0x0a}",
165
166 out: "Integers: 1 -2 42 511 10",
167 },
168
169 {
170 in: "Floats: {.5} {-.5} {1.1} {-2.2} {+42.1} {1e10} {1.2e-3} {1. 2e3} {-1.2e3}",
171
172 out: "Floats: 0.5 -0.5 1.1 -2.2 42.1 1e+10 0.0012 1200 -1200",
173 },
174
175 // Method at top level
176 {
177 in: "ptrmethod={PointerMethod}\n",
178
179 out: "ptrmethod=ptrmethod!\n",
180 },
181
182 {
183 in: "valmethod={ValueMethod}\n",
184
185 out: "valmethod=valmethod!\n",
186 },
187
188 // Section
189 {
190 in: "{.section Data }\n" +
191 "some text for the section\n" +
192 "{.end}\n",
193
194 out: "some text for the section\n",
195 },
196 {
197 in: "{.section Data }\n" +
198 "{Header}={Integer}\n" +
199 "{.end}\n",
200
201 out: "Header=77\n",
202 },
203 {
204 in: "{.section Pdata }\n" +
205 "{Header}={Integer}\n" +
206 "{.end}\n",
207
208 out: "Header=77\n",
209 },
210 {
211 in: "{.section Pdata }\n" +
212 "data present\n" +
213 "{.or}\n" +
214 "data not present\n" +
215 "{.end}\n",
216
217 out: "data present\n",
218 },
219 {
220 in: "{.section Empty }\n" +
221 "data present\n" +
222 "{.or}\n" +
223 "data not present\n" +
224 "{.end}\n",
225
226 out: "data not present\n",
227 },
228 {
229 in: "{.section Null }\n" +
230 "data present\n" +
231 "{.or}\n" +
232 "data not present\n" +
233 "{.end}\n",
234
235 out: "data not present\n",
236 },
237 {
238 in: "{.section Pdata }\n" +
239 "{Header}={Integer}\n" +
240 "{.section @ }\n" +
241 "{Header}={Integer}\n" +
242 "{.end}\n" +
243 "{.end}\n",
244
245 out: "Header=77\n" +
246 "Header=77\n",
247 },
248
249 {
250 in: "{.section Data}{.end} {Header}\n",
251
252 out: " Header\n",
253 },
254
255 {
256 in: "{.section Integer}{@}{.end}",
257
258 out: "77",
259 },
260
261 // Repeated
262 {
263 in: "{.section Pdata }\n" +
264 "{.repeated section @ }\n" +
265 "{Item}={Value}\n" +
266 "{.end}\n" +
267 "{.end}\n",
268
269 out: "ItemNumber1=ValueNumber1\n" +
270 "ItemNumber2=ValueNumber2\n",
271 },
272 {
273 in: "{.section Pdata }\n" +
274 "{.repeated section @ }\n" +
275 "{Item}={Value}\n" +
276 "{.or}\n" +
277 "this should not appear\n" +
278 "{.end}\n" +
279 "{.end}\n",
280
281 out: "ItemNumber1=ValueNumber1\n" +
282 "ItemNumber2=ValueNumber2\n",
283 },
284 {
285 in: "{.section @ }\n" +
286 "{.repeated section Empty }\n" +
287 "{Item}={Value}\n" +
288 "{.or}\n" +
289 "this should appear: empty field\n" +
290 "{.end}\n" +
291 "{.end}\n",
292
293 out: "this should appear: empty field\n",
294 },
295 {
296 in: "{.repeated section Pdata }\n" +
297 "{Item}\n" +
298 "{.alternates with}\n" +
299 "is\nover\nmultiple\nlines\n" +
300 "{.end}\n",
301
302 out: "ItemNumber1\n" +
303 "is\nover\nmultiple\nlines\n" +
304 "ItemNumber2\n",
305 },
306 {
307 in: "{.repeated section Pdata }\n" +
308 "{Item}\n" +
309 "{.alternates with}\n" +
310 "is\nover\nmultiple\nlines\n" +
311 " {.end}\n",
312
313 out: "ItemNumber1\n" +
314 "is\nover\nmultiple\nlines\n" +
315 "ItemNumber2\n",
316 },
317 {
318 in: "{.section Pdata }\n" +
319 "{.repeated section @ }\n" +
320 "{Item}={Value}\n" +
321 "{.alternates with}DIVIDER\n" +
322 "{.or}\n" +
323 "this should not appear\n" +
324 "{.end}\n" +
325 "{.end}\n",
326
327 out: "ItemNumber1=ValueNumber1\n" +
328 "DIVIDER\n" +
329 "ItemNumber2=ValueNumber2\n",
330 },
331 {
332 in: "{.repeated section Vec }\n" +
333 "{@}\n" +
334 "{.end}\n",
335
336 out: "elt1\n" +
337 "elt2\n",
338 },
339 // Same but with a space before {.end}: was a bug.
340 {
341 in: "{.repeated section Vec }\n" +
342 "{@} {.end}\n",
343
344 out: "elt1 elt2 \n",
345 },
346 {
347 in: "{.repeated section Integer}{.end}",
348
349 err: "line 1: .repeated: cannot repeat Integer (type int)",
350 },
351
352 // Nested names
353 {
354 in: "{.section @ }\n" +
355 "{InnerT.Item}={InnerT.Value}\n" +
356 "{.end}",
357
358 out: "ItemNumber1=ValueNumber1\n",
359 },
360 {
361 in: "{.section @ }\n" +
362 "{InnerT.Item}={.section InnerT}{.section Value}{@}{.end }{.end}\n" +
363 "{.end}",
364
365 out: "ItemNumber1=ValueNumber1\n",
366 },
367
368 {
369 in: "{.section Emptystring}emptystring{.end}\n" +
370 "{.section Header}header{.end}\n",
371
372 out: "\nheader\n",
373 },
374
375 {
376 in: "{.section True}1{.or}2{.end}\n" +
377 "{.section False}3{.or}4{.end}\n",
378
379 out: "1\n4\n",
380 },
381
382 // Maps
383
384 {
385 in: "{Mp.mapkey}\n",
386
387 out: "Ahoy!\n",
388 },
389 {
390 in: "{Innermap.Mp.innerkey}\n",
391
392 out: "55\n",
393 },
394 {
395 in: "{.section Innermap}{.section Mp}{innerkey}{.end}{.end}\n",
396
397 out: "55\n",
398 },
399 {
400 in: "{.section JSON}{.repeated section maps}{a}{b}{.end}{.end}\n ",
401
402 out: "1234\n",
403 },
404 {
405 in: "{Stringmap.stringkey1}\n",
406
407 out: "stringresult\n",
408 },
409 {
410 in: "{.repeated section Stringmap}\n" +
411 "{@}\n" +
412 "{.end}",
413
414 out: "stringresult\n" +
415 "stringresult\n",
416 },
417 {
418 in: "{.repeated section Stringmap}\n" +
419 "\t{@}\n" +
420 "{.end}",
421
422 out: "\tstringresult\n" +
423 "\tstringresult\n",
424 },
425 {
426 in: "{*Ptrmap.stringkey1}\n",
427
428 out: "pointedToString\n",
429 },
430 {
431 in: "{.repeated section Ptrmap}\n" +
432 "{*@}\n" +
433 "{.end}",
434
435 out: "pointedToString\n" +
436 "pointedToString\n",
437 },
438
439 // Interface values
440
441 {
442 in: "{Iface}",
443
444 out: "[1 2 3]",
445 },
446 {
447 in: "{.repeated section Iface}{@}{.alternates with} {.end}",
448
449 out: "1 2 3",
450 },
451 {
452 in: "{.section Iface}{@}{.end}",
453
454 out: "[1 2 3]",
455 },
456 {
457 in: "{.section Ifaceptr}{Item} {Value}{.end}",
458
459 out: "Item Value",
460 },
461 }
462
463 func TestAll(t *testing.T) {
464 // Parse
465 testAll(t, func(test *Test) (*Template, error) { return Parse(test.in, f ormatters) })
466 // ParseFile
467 f, err := ioutil.TempFile("", "template-test")
468 if err != nil {
469 t.Fatal(err)
470 }
471 defer func() {
472 name := f.Name()
473 f.Close()
474 os.Remove(name)
475 }()
476 testAll(t, func(test *Test) (*Template, error) {
477 err := ioutil.WriteFile(f.Name(), []byte(test.in), 0600)
478 if err != nil {
479 t.Error("unexpected write error:", err)
480 return nil, err
481 }
482 return ParseFile(f.Name(), formatters)
483 })
484 // tmpl.ParseFile
485 testAll(t, func(test *Test) (*Template, error) {
486 err := ioutil.WriteFile(f.Name(), []byte(test.in), 0600)
487 if err != nil {
488 t.Error("unexpected write error:", err)
489 return nil, err
490 }
491 tmpl := New(formatters)
492 return tmpl, tmpl.ParseFile(f.Name())
493 })
494 }
495
496 func testAll(t *testing.T, parseFunc func(*Test) (*Template, error)) {
497 s := new(S)
498 // initialized by hand for clarity.
499 s.Header = "Header"
500 s.HeaderPtr = &s.Header
501 s.Integer = 77
502 s.IntegerPtr = &s.Integer
503 s.InnerT = t1
504 s.Data = []T{t1, t2}
505 s.Pdata = []*T{&t1, &t2}
506 s.Empty = []*T{}
507 s.Null = nil
508 s.Vec = []interface{}{"elt1", "elt2"}
509 s.True = true
510 s.False = false
511 s.Mp = make(map[string]string)
512 s.Mp["mapkey"] = "Ahoy!"
513 json.Unmarshal([]byte(`{"maps":[{"a":1,"b":2},{"a":3,"b":4}]}`), &s.JSON )
514 s.Innermap.Mp = make(map[string]int)
515 s.Innermap.Mp["innerkey"] = 55
516 s.Stringmap = make(map[string]string)
517 s.Stringmap["stringkey1"] = "stringresult" // the same value so repeated section is order-independent
518 s.Stringmap["stringkey2"] = "stringresult"
519 s.Ptrmap = make(map[string]*string)
520 x := "pointedToString"
521 s.Ptrmap["stringkey1"] = &x // the same value so repeated section is ord er-independent
522 s.Ptrmap["stringkey2"] = &x
523 s.Iface = []int{1, 2, 3}
524 s.Ifaceptr = &T{"Item", "Value"}
525
526 var buf bytes.Buffer
527 for _, test := range tests {
528 buf.Reset()
529 tmpl, err := parseFunc(test)
530 if err != nil {
531 t.Error("unexpected parse error: ", err)
532 continue
533 }
534 err = tmpl.Execute(&buf, s)
535 if test.err == "" {
536 if err != nil {
537 t.Error("unexpected execute error:", err)
538 }
539 } else {
540 if err == nil {
541 t.Errorf("expected execute error %q, got nil", t est.err)
542 } else if err.Error() != test.err {
543 t.Errorf("expected execute error %q, got %q", te st.err, err.Error())
544 }
545 }
546 if buf.String() != test.out {
547 t.Errorf("for %q: expected %q got %q", test.in, test.out , buf.String())
548 }
549 }
550 }
551
552 func TestMapDriverType(t *testing.T) {
553 mp := map[string]string{"footer": "Ahoy!"}
554 tmpl, err := Parse("template: {footer}", nil)
555 if err != nil {
556 t.Error("unexpected parse error:", err)
557 }
558 var b bytes.Buffer
559 err = tmpl.Execute(&b, mp)
560 if err != nil {
561 t.Error("unexpected execute error:", err)
562 }
563 s := b.String()
564 expect := "template: Ahoy!"
565 if s != expect {
566 t.Errorf("failed passing string as data: expected %q got %q", ex pect, s)
567 }
568 }
569
570 func TestMapNoEntry(t *testing.T) {
571 mp := make(map[string]int)
572 tmpl, err := Parse("template: {notthere}!", nil)
573 if err != nil {
574 t.Error("unexpected parse error:", err)
575 }
576 var b bytes.Buffer
577 err = tmpl.Execute(&b, mp)
578 if err != nil {
579 t.Error("unexpected execute error:", err)
580 }
581 s := b.String()
582 expect := "template: 0!"
583 if s != expect {
584 t.Errorf("failed passing string as data: expected %q got %q", ex pect, s)
585 }
586 }
587
588 func TestStringDriverType(t *testing.T) {
589 tmpl, err := Parse("template: {@}", nil)
590 if err != nil {
591 t.Error("unexpected parse error:", err)
592 }
593 var b bytes.Buffer
594 err = tmpl.Execute(&b, "hello")
595 if err != nil {
596 t.Error("unexpected execute error:", err)
597 }
598 s := b.String()
599 expect := "template: hello"
600 if s != expect {
601 t.Errorf("failed passing string as data: expected %q got %q", ex pect, s)
602 }
603 }
604
605 func TestTwice(t *testing.T) {
606 tmpl, err := Parse("template: {@}", nil)
607 if err != nil {
608 t.Error("unexpected parse error:", err)
609 }
610 var b bytes.Buffer
611 err = tmpl.Execute(&b, "hello")
612 if err != nil {
613 t.Error("unexpected parse error:", err)
614 }
615 s := b.String()
616 expect := "template: hello"
617 if s != expect {
618 t.Errorf("failed passing string as data: expected %q got %q", ex pect, s)
619 }
620 err = tmpl.Execute(&b, "hello")
621 if err != nil {
622 t.Error("unexpected parse error:", err)
623 }
624 s = b.String()
625 expect += expect
626 if s != expect {
627 t.Errorf("failed passing string as data: expected %q got %q", ex pect, s)
628 }
629 }
630
631 func TestCustomDelims(t *testing.T) {
632 // try various lengths. zero should catch error.
633 for i := 0; i < 7; i++ {
634 for j := 0; j < 7; j++ {
635 tmpl := New(nil)
636 // first two chars deliberately the same to test equal l eft and right delims
637 ldelim := "$!#$%^&"[0:i]
638 rdelim := "$*&^%$!"[0:j]
639 tmpl.SetDelims(ldelim, rdelim)
640 // if braces, this would be template: {@}{.meta-left}{.m eta-right}
641 text := "template: " +
642 ldelim + "@" + rdelim +
643 ldelim + ".meta-left" + rdelim +
644 ldelim + ".meta-right" + rdelim
645 err := tmpl.Parse(text)
646 if err != nil {
647 if i == 0 || j == 0 { // expected
648 continue
649 }
650 t.Error("unexpected parse error:", err)
651 } else if i == 0 || j == 0 {
652 t.Errorf("expected parse error for empty delimit er: %d %d %q %q", i, j, ldelim, rdelim)
653 continue
654 }
655 var b bytes.Buffer
656 err = tmpl.Execute(&b, "hello")
657 s := b.String()
658 if s != "template: hello"+ldelim+rdelim {
659 t.Errorf("failed delim check(%q %q) %q got %q", ldelim, rdelim, text, s)
660 }
661 }
662 }
663 }
664
665 // Test that a variable evaluates to the field itself and does not further indir ection
666 func TestVarIndirection(t *testing.T) {
667 s := new(S)
668 // initialized by hand for clarity.
669 s.InnerPointerT = &t1
670
671 var buf bytes.Buffer
672 input := "{.section @}{InnerPointerT}{.end}"
673 tmpl, err := Parse(input, nil)
674 if err != nil {
675 t.Fatal("unexpected parse error:", err)
676 }
677 err = tmpl.Execute(&buf, s)
678 if err != nil {
679 t.Fatal("unexpected execute error:", err)
680 }
681 expect := fmt.Sprintf("%v", &t1) // output should be hex address of t1
682 if buf.String() != expect {
683 t.Errorf("for %q: expected %q got %q", input, expect, buf.String ())
684 }
685 }
686
687 func TestHTMLFormatterWithByte(t *testing.T) {
688 s := "Test string."
689 b := []byte(s)
690 var buf bytes.Buffer
691 HTMLFormatter(&buf, "", b)
692 bs := buf.String()
693 if bs != s {
694 t.Errorf("munged []byte, expected: %s got: %s", s, bs)
695 }
696 }
697
698 type UF struct {
699 I int
700 s string
701 }
702
703 func TestReferenceToUnexported(t *testing.T) {
704 u := &UF{3, "hello"}
705 var buf bytes.Buffer
706 input := "{.section @}{I}{s}{.end}"
707 tmpl, err := Parse(input, nil)
708 if err != nil {
709 t.Fatal("unexpected parse error:", err)
710 }
711 err = tmpl.Execute(&buf, u)
712 if err == nil {
713 t.Fatal("expected execute error, got none")
714 }
715 if strings.Index(err.Error(), "not exported") < 0 {
716 t.Fatal("expected unexported error; got", err)
717 }
718 }
719
720 var formatterTests = []Test{
721 {
722 in: "{Header|uppercase}={Integer|+1}\n" +
723 "{Header|html}={Integer|str}\n",
724
725 out: "HEADER=78\n" +
726 "Header=77\n",
727 },
728
729 {
730 in: "{Header|uppercase}={Integer Header|multiword}\n" +
731 "{Header|html}={Header Integer|multiword}\n" +
732 "{Header|html}={Header Integer}\n",
733
734 out: "HEADER=<77><Header>\n" +
735 "Header=<Header><77>\n" +
736 "Header=Header77\n",
737 },
738 {
739 in: "{Raw}\n" +
740 "{Raw|html}\n",
741
742 out: "a <&> b\n" +
743 "a &lt;&amp;&gt; b\n",
744 },
745 {
746 in: "{Bytes}",
747 out: "hello",
748 },
749 {
750 in: "{Raw|uppercase|html|html}",
751 out: "A &amp;lt;&amp;amp;&amp;gt; B",
752 },
753 {
754 in: "{Header Integer|multiword|html}",
755 out: "&lt;Header&gt;&lt;77&gt;",
756 },
757 {
758 in: "{Integer|no_formatter|html}",
759 err: `unknown formatter: "no_formatter"`,
760 },
761 {
762 in: "{Integer|||||}", // empty string is a valid formatter
763 out: "77",
764 },
765 {
766 in: `{"%.02f 0x%02X" 1.1 10|printf}`,
767 out: "1.10 0x0A",
768 },
769 {
770 in: `{""|}{""||}{""|printf}`, // Issue #1896.
771 out: "",
772 },
773 }
774
775 func TestFormatters(t *testing.T) {
776 data := map[string]interface{}{
777 "Header": "Header",
778 "Integer": 77,
779 "Raw": "a <&> b",
780 "Bytes": []byte("hello"),
781 }
782 for _, c := range formatterTests {
783 tmpl, err := Parse(c.in, formatters)
784 if err != nil {
785 if c.err == "" {
786 t.Error("unexpected parse error:", err)
787 continue
788 }
789 if strings.Index(err.Error(), c.err) < 0 {
790 t.Errorf("unexpected error: expected %q, got %q" , c.err, err.Error())
791 continue
792 }
793 } else {
794 if c.err != "" {
795 t.Errorf("For %q, expected error, got none.", c. in)
796 continue
797 }
798 var buf bytes.Buffer
799 err = tmpl.Execute(&buf, data)
800 if err != nil {
801 t.Error("unexpected Execute error: ", err)
802 continue
803 }
804 actual := buf.String()
805 if actual != c.out {
806 t.Errorf("for %q: expected %q but got %q.", c.in , c.out, actual)
807 }
808 }
809 }
810 }
OLDNEW
« no previous file with comments | « src/pkg/old/template/parse.go ('k') | no next file » | no next file with comments »

Powered by Google App Engine
RSS Feeds Recent Issues | This issue
This is Rietveld f62528b