]> git.ipfire.org Git - thirdparty/gcc.git/blob - libgo/go/template/template_test.go
Add Go frontend, libgo library, and Go testsuite.
[thirdparty/gcc.git] / libgo / go / template / template_test.go
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 "container/vector"
10 "fmt"
11 "io"
12 "io/ioutil"
13 "json"
14 "os"
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 integer int
34 raw string
35 innerT T
36 innerPointerT *T
37 data []T
38 pdata []*T
39 empty []*T
40 emptystring string
41 null []*T
42 vec *vector.Vector
43 true bool
44 false bool
45 mp map[string]string
46 json interface{}
47 innermap U
48 stringmap map[string]string
49 bytes []byte
50 iface interface{}
51 ifaceptr interface{}
52 }
53
54 func (s *S) pointerMethod() string { return "ptrmethod!" }
55
56 func (s S) valueMethod() string { return "valmethod!" }
57
58 var t1 = T{"ItemNumber1", "ValueNumber1"}
59 var t2 = T{"ItemNumber2", "ValueNumber2"}
60
61 func uppercase(v interface{}) string {
62 s := v.(string)
63 t := ""
64 for i := 0; i < len(s); i++ {
65 c := s[i]
66 if 'a' <= c && c <= 'z' {
67 c = c + 'A' - 'a'
68 }
69 t += string(c)
70 }
71 return t
72 }
73
74 func plus1(v interface{}) string {
75 i := v.(int)
76 return fmt.Sprint(i + 1)
77 }
78
79 func writer(f func(interface{}) string) func(io.Writer, interface{}, string) {
80 return func(w io.Writer, v interface{}, format string) {
81 io.WriteString(w, f(v))
82 }
83 }
84
85
86 var formatters = FormatterMap{
87 "uppercase": writer(uppercase),
88 "+1": writer(plus1),
89 }
90
91 var tests = []*Test{
92 // Simple
93 &Test{"", "", ""},
94 &Test{"abc", "abc", ""},
95 &Test{"abc\ndef\n", "abc\ndef\n", ""},
96 &Test{" {.meta-left} \n", "{", ""},
97 &Test{" {.meta-right} \n", "}", ""},
98 &Test{" {.space} \n", " ", ""},
99 &Test{" {.tab} \n", "\t", ""},
100 &Test{" {#comment} \n", "", ""},
101 &Test{"\tSome Text\t\n", "\tSome Text\t\n", ""},
102 &Test{" {.meta-right} {.meta-right} {.meta-right} \n", " } } } \n", ""},
103
104 // Variables at top level
105 &Test{
106 in: "{header}={integer}\n",
107
108 out: "Header=77\n",
109 },
110
111 // Method at top level
112 &Test{
113 in: "ptrmethod={pointerMethod}\n",
114
115 out: "ptrmethod=ptrmethod!\n",
116 },
117
118 &Test{
119 in: "valmethod={valueMethod}\n",
120
121 out: "valmethod=valmethod!\n",
122 },
123
124 // Section
125 &Test{
126 in: "{.section data }\n" +
127 "some text for the section\n" +
128 "{.end}\n",
129
130 out: "some text for the section\n",
131 },
132 &Test{
133 in: "{.section data }\n" +
134 "{header}={integer}\n" +
135 "{.end}\n",
136
137 out: "Header=77\n",
138 },
139 &Test{
140 in: "{.section pdata }\n" +
141 "{header}={integer}\n" +
142 "{.end}\n",
143
144 out: "Header=77\n",
145 },
146 &Test{
147 in: "{.section pdata }\n" +
148 "data present\n" +
149 "{.or}\n" +
150 "data not present\n" +
151 "{.end}\n",
152
153 out: "data present\n",
154 },
155 &Test{
156 in: "{.section empty }\n" +
157 "data present\n" +
158 "{.or}\n" +
159 "data not present\n" +
160 "{.end}\n",
161
162 out: "data not present\n",
163 },
164 &Test{
165 in: "{.section null }\n" +
166 "data present\n" +
167 "{.or}\n" +
168 "data not present\n" +
169 "{.end}\n",
170
171 out: "data not present\n",
172 },
173 &Test{
174 in: "{.section pdata }\n" +
175 "{header}={integer}\n" +
176 "{.section @ }\n" +
177 "{header}={integer}\n" +
178 "{.end}\n" +
179 "{.end}\n",
180
181 out: "Header=77\n" +
182 "Header=77\n",
183 },
184
185 &Test{
186 in: "{.section data}{.end} {header}\n",
187
188 out: " Header\n",
189 },
190
191 // Repeated
192 &Test{
193 in: "{.section pdata }\n" +
194 "{.repeated section @ }\n" +
195 "{item}={value}\n" +
196 "{.end}\n" +
197 "{.end}\n",
198
199 out: "ItemNumber1=ValueNumber1\n" +
200 "ItemNumber2=ValueNumber2\n",
201 },
202 &Test{
203 in: "{.section pdata }\n" +
204 "{.repeated section @ }\n" +
205 "{item}={value}\n" +
206 "{.or}\n" +
207 "this should not appear\n" +
208 "{.end}\n" +
209 "{.end}\n",
210
211 out: "ItemNumber1=ValueNumber1\n" +
212 "ItemNumber2=ValueNumber2\n",
213 },
214 &Test{
215 in: "{.section @ }\n" +
216 "{.repeated section empty }\n" +
217 "{item}={value}\n" +
218 "{.or}\n" +
219 "this should appear: empty field\n" +
220 "{.end}\n" +
221 "{.end}\n",
222
223 out: "this should appear: empty field\n",
224 },
225 &Test{
226 in: "{.repeated section pdata }\n" +
227 "{item}\n" +
228 "{.alternates with}\n" +
229 "is\nover\nmultiple\nlines\n" +
230 "{.end}\n",
231
232 out: "ItemNumber1\n" +
233 "is\nover\nmultiple\nlines\n" +
234 "ItemNumber2\n",
235 },
236 &Test{
237 in: "{.repeated section pdata }\n" +
238 "{item}\n" +
239 "{.alternates with}\n" +
240 "is\nover\nmultiple\nlines\n" +
241 " {.end}\n",
242
243 out: "ItemNumber1\n" +
244 "is\nover\nmultiple\nlines\n" +
245 "ItemNumber2\n",
246 },
247 &Test{
248 in: "{.section pdata }\n" +
249 "{.repeated section @ }\n" +
250 "{item}={value}\n" +
251 "{.alternates with}DIVIDER\n" +
252 "{.or}\n" +
253 "this should not appear\n" +
254 "{.end}\n" +
255 "{.end}\n",
256
257 out: "ItemNumber1=ValueNumber1\n" +
258 "DIVIDER\n" +
259 "ItemNumber2=ValueNumber2\n",
260 },
261 &Test{
262 in: "{.repeated section vec }\n" +
263 "{@}\n" +
264 "{.end}\n",
265
266 out: "elt1\n" +
267 "elt2\n",
268 },
269 // Same but with a space before {.end}: was a bug.
270 &Test{
271 in: "{.repeated section vec }\n" +
272 "{@} {.end}\n",
273
274 out: "elt1 elt2 \n",
275 },
276 &Test{
277 in: "{.repeated section integer}{.end}",
278
279 err: "line 1: .repeated: cannot repeat integer (type int)",
280 },
281
282 // Nested names
283 &Test{
284 in: "{.section @ }\n" +
285 "{innerT.item}={innerT.value}\n" +
286 "{.end}",
287
288 out: "ItemNumber1=ValueNumber1\n",
289 },
290 &Test{
291 in: "{.section @ }\n" +
292 "{innerT.item}={.section innerT}{.section value}{@}{.end}{.end}\n" +
293 "{.end}",
294
295 out: "ItemNumber1=ValueNumber1\n",
296 },
297
298
299 // Formatters
300 &Test{
301 in: "{.section pdata }\n" +
302 "{header|uppercase}={integer|+1}\n" +
303 "{header|html}={integer|str}\n" +
304 "{.end}\n",
305
306 out: "HEADER=78\n" +
307 "Header=77\n",
308 },
309
310 &Test{
311 in: "{raw}\n" +
312 "{raw|html}\n",
313
314 out: "&<>!@ #$%^\n" +
315 "&amp;&lt;&gt;!@ #$%^\n",
316 },
317
318 &Test{
319 in: "{.section emptystring}emptystring{.end}\n" +
320 "{.section header}header{.end}\n",
321
322 out: "\nheader\n",
323 },
324
325 &Test{
326 in: "{.section true}1{.or}2{.end}\n" +
327 "{.section false}3{.or}4{.end}\n",
328
329 out: "1\n4\n",
330 },
331
332 &Test{
333 in: "{bytes}",
334
335 out: "hello",
336 },
337
338 // Maps
339
340 &Test{
341 in: "{mp.mapkey}\n",
342
343 out: "Ahoy!\n",
344 },
345 &Test{
346 in: "{innermap.mp.innerkey}\n",
347
348 out: "55\n",
349 },
350 &Test{
351 in: "{.section innermap}{.section mp}{innerkey}{.end}{.end}\n",
352
353 out: "55\n",
354 },
355 &Test{
356 in: "{.section json}{.repeated section maps}{a}{b}{.end}{.end}\n",
357
358 out: "1234\n",
359 },
360 &Test{
361 in: "{stringmap.stringkey1}\n",
362
363 out: "stringresult\n",
364 },
365 &Test{
366 in: "{.repeated section stringmap}\n" +
367 "{@}\n" +
368 "{.end}",
369
370 out: "stringresult\n" +
371 "stringresult\n",
372 },
373 &Test{
374 in: "{.repeated section stringmap}\n" +
375 "\t{@}\n" +
376 "{.end}",
377
378 out: "\tstringresult\n" +
379 "\tstringresult\n",
380 },
381
382 // Interface values
383
384 &Test{
385 in: "{iface}",
386
387 out: "[1 2 3]",
388 },
389 &Test{
390 in: "{.repeated section iface}{@}{.alternates with} {.end}",
391
392 out: "1 2 3",
393 },
394 &Test{
395 in: "{.section iface}{@}{.end}",
396
397 out: "[1 2 3]",
398 },
399 &Test{
400 in: "{.section ifaceptr}{item} {value}{.end}",
401
402 out: "Item Value",
403 },
404 }
405
406 func TestAll(t *testing.T) {
407 // Parse
408 testAll(t, func(test *Test) (*Template, os.Error) { return Parse(test.in, formatters) })
409 // ParseFile
410 testAll(t, func(test *Test) (*Template, os.Error) {
411 err := ioutil.WriteFile("_test/test.tmpl", []byte(test.in), 0600)
412 if err != nil {
413 t.Error("unexpected write error:", err)
414 return nil, err
415 }
416 return ParseFile("_test/test.tmpl", formatters)
417 })
418 // tmpl.ParseFile
419 testAll(t, func(test *Test) (*Template, os.Error) {
420 err := ioutil.WriteFile("_test/test.tmpl", []byte(test.in), 0600)
421 if err != nil {
422 t.Error("unexpected write error:", err)
423 return nil, err
424 }
425 tmpl := New(formatters)
426 return tmpl, tmpl.ParseFile("_test/test.tmpl")
427 })
428 }
429
430 func testAll(t *testing.T, parseFunc func(*Test) (*Template, os.Error)) {
431 s := new(S)
432 // initialized by hand for clarity.
433 s.header = "Header"
434 s.integer = 77
435 s.raw = "&<>!@ #$%^"
436 s.innerT = t1
437 s.data = []T{t1, t2}
438 s.pdata = []*T{&t1, &t2}
439 s.empty = []*T{}
440 s.null = nil
441 s.vec = new(vector.Vector)
442 s.vec.Push("elt1")
443 s.vec.Push("elt2")
444 s.true = true
445 s.false = false
446 s.mp = make(map[string]string)
447 s.mp["mapkey"] = "Ahoy!"
448 json.Unmarshal([]byte(`{"maps":[{"a":1,"b":2},{"a":3,"b":4}]}`), &s.json)
449 s.innermap.mp = make(map[string]int)
450 s.innermap.mp["innerkey"] = 55
451 s.stringmap = make(map[string]string)
452 s.stringmap["stringkey1"] = "stringresult" // the same value so repeated section is order-independent
453 s.stringmap["stringkey2"] = "stringresult"
454 s.bytes = []byte("hello")
455 s.iface = []int{1, 2, 3}
456 s.ifaceptr = &T{"Item", "Value"}
457
458 var buf bytes.Buffer
459 for _, test := range tests {
460 buf.Reset()
461 tmpl, err := parseFunc(test)
462 if err != nil {
463 t.Error("unexpected parse error: ", err)
464 continue
465 }
466 err = tmpl.Execute(s, &buf)
467 if test.err == "" {
468 if err != nil {
469 t.Error("unexpected execute error:", err)
470 }
471 } else {
472 if err == nil {
473 t.Errorf("expected execute error %q, got nil", test.err)
474 } else if err.String() != test.err {
475 t.Errorf("expected execute error %q, got %q", test.err, err.String())
476 }
477 }
478 if buf.String() != test.out {
479 t.Errorf("for %q: expected %q got %q", test.in, test.out, buf.String())
480 }
481 }
482 }
483
484 func TestMapDriverType(t *testing.T) {
485 mp := map[string]string{"footer": "Ahoy!"}
486 tmpl, err := Parse("template: {footer}", nil)
487 if err != nil {
488 t.Error("unexpected parse error:", err)
489 }
490 var b bytes.Buffer
491 err = tmpl.Execute(mp, &b)
492 if err != nil {
493 t.Error("unexpected execute error:", err)
494 }
495 s := b.String()
496 expected := "template: Ahoy!"
497 if s != expected {
498 t.Errorf("failed passing string as data: expected %q got %q", "template: Ahoy!", s)
499 }
500 }
501
502 func TestStringDriverType(t *testing.T) {
503 tmpl, err := Parse("template: {@}", nil)
504 if err != nil {
505 t.Error("unexpected parse error:", err)
506 }
507 var b bytes.Buffer
508 err = tmpl.Execute("hello", &b)
509 if err != nil {
510 t.Error("unexpected execute error:", err)
511 }
512 s := b.String()
513 if s != "template: hello" {
514 t.Errorf("failed passing string as data: expected %q got %q", "template: hello", s)
515 }
516 }
517
518 func TestTwice(t *testing.T) {
519 tmpl, err := Parse("template: {@}", nil)
520 if err != nil {
521 t.Error("unexpected parse error:", err)
522 }
523 var b bytes.Buffer
524 err = tmpl.Execute("hello", &b)
525 if err != nil {
526 t.Error("unexpected parse error:", err)
527 }
528 s := b.String()
529 text := "template: hello"
530 if s != text {
531 t.Errorf("failed passing string as data: expected %q got %q", text, s)
532 }
533 err = tmpl.Execute("hello", &b)
534 if err != nil {
535 t.Error("unexpected parse error:", err)
536 }
537 s = b.String()
538 text += text
539 if s != text {
540 t.Errorf("failed passing string as data: expected %q got %q", text, s)
541 }
542 }
543
544 func TestCustomDelims(t *testing.T) {
545 // try various lengths. zero should catch error.
546 for i := 0; i < 7; i++ {
547 for j := 0; j < 7; j++ {
548 tmpl := New(nil)
549 // first two chars deliberately the same to test equal left and right delims
550 ldelim := "$!#$%^&"[0:i]
551 rdelim := "$*&^%$!"[0:j]
552 tmpl.SetDelims(ldelim, rdelim)
553 // if braces, this would be template: {@}{.meta-left}{.meta-right}
554 text := "template: " +
555 ldelim + "@" + rdelim +
556 ldelim + ".meta-left" + rdelim +
557 ldelim + ".meta-right" + rdelim
558 err := tmpl.Parse(text)
559 if err != nil {
560 if i == 0 || j == 0 { // expected
561 continue
562 }
563 t.Error("unexpected parse error:", err)
564 } else if i == 0 || j == 0 {
565 t.Errorf("expected parse error for empty delimiter: %d %d %q %q", i, j, ldelim, rdelim)
566 continue
567 }
568 var b bytes.Buffer
569 err = tmpl.Execute("hello", &b)
570 s := b.String()
571 if s != "template: hello"+ldelim+rdelim {
572 t.Errorf("failed delim check(%q %q) %q got %q", ldelim, rdelim, text, s)
573 }
574 }
575 }
576 }
577
578 // Test that a variable evaluates to the field itself and does not further indirection
579 func TestVarIndirection(t *testing.T) {
580 s := new(S)
581 // initialized by hand for clarity.
582 s.innerPointerT = &t1
583
584 var buf bytes.Buffer
585 input := "{.section @}{innerPointerT}{.end}"
586 tmpl, err := Parse(input, nil)
587 if err != nil {
588 t.Fatal("unexpected parse error:", err)
589 }
590 err = tmpl.Execute(s, &buf)
591 if err != nil {
592 t.Fatal("unexpected execute error:", err)
593 }
594 expect := fmt.Sprintf("%v", &t1) // output should be hex address of t1
595 if buf.String() != expect {
596 t.Errorf("for %q: expected %q got %q", input, expect, buf.String())
597 }
598 }
599
600 func TestHTMLFormatterWithByte(t *testing.T) {
601 s := "Test string."
602 b := []byte(s)
603 var buf bytes.Buffer
604 HTMLFormatter(&buf, b, "")
605 bs := buf.String()
606 if bs != s {
607 t.Errorf("munged []byte, expected: %s got: %s", s, bs)
608 }
609 }