github.com/insionng/yougam@v0.0.0-20170714101924-2bc18d833463/libraries/flosch/pongo2.v3/pongo2_template_test.go (about) 1 package pongo2 2 3 import ( 4 "bytes" 5 "fmt" 6 "io/ioutil" 7 "path/filepath" 8 "regexp" 9 "strings" 10 "testing" 11 "time" 12 ) 13 14 var admin_list = []string{"user2"} 15 16 var time1 = time.Date(2014, 06, 10, 15, 30, 15, 0, time.UTC) 17 var time2 = time.Date(2011, 03, 21, 8, 37, 56, 12, time.UTC) 18 19 type post struct { 20 Text string 21 Created time.Time 22 } 23 24 type user struct { 25 Name string 26 Validated bool 27 } 28 29 type comment struct { 30 Author *user 31 Date time.Time 32 Text string 33 } 34 35 func is_admin(u *user) bool { 36 for _, a := range admin_list { 37 if a == u.Name { 38 return true 39 } 40 } 41 return false 42 } 43 44 func (u *user) Is_admin() *Value { 45 return AsValue(is_admin(u)) 46 } 47 48 func (u *user) Is_admin2() bool { 49 return is_admin(u) 50 } 51 52 func (p *post) String() string { 53 return ":-)" 54 } 55 56 /* 57 * Start setup sandbox 58 */ 59 60 type tagSandboxDemoTag struct { 61 } 62 63 func (node *tagSandboxDemoTag) Execute(ctx *ExecutionContext, buffer *bytes.Buffer) *Error { 64 buffer.WriteString("hello") 65 return nil 66 } 67 68 func tagSandboxDemoTagParser(doc *Parser, start *Token, arguments *Parser) (INodeTag, *Error) { 69 return &tagSandboxDemoTag{}, nil 70 } 71 72 func BannedFilterFn(in *Value, params *Value) (*Value, *Error) { 73 return in, nil 74 } 75 76 func init() { 77 DefaultSet.Debug = true 78 79 RegisterFilter("banned_filter", BannedFilterFn) 80 RegisterFilter("unbanned_filter", BannedFilterFn) 81 RegisterTag("banned_tag", tagSandboxDemoTagParser) 82 RegisterTag("unbanned_tag", tagSandboxDemoTagParser) 83 84 DefaultSet.BanFilter("banned_filter") 85 DefaultSet.BanTag("banned_tag") 86 87 // Allow different kind of levels inside template_tests/ 88 abs_path, err := filepath.Abs("./template_tests/*") 89 if err != nil { 90 panic(err) 91 } 92 DefaultSet.SandboxDirectories = append(DefaultSet.SandboxDirectories, abs_path) 93 94 abs_path, err = filepath.Abs("./template_tests/*/*") 95 if err != nil { 96 panic(err) 97 } 98 DefaultSet.SandboxDirectories = append(DefaultSet.SandboxDirectories, abs_path) 99 100 abs_path, err = filepath.Abs("./template_tests/*/*/*") 101 if err != nil { 102 panic(err) 103 } 104 DefaultSet.SandboxDirectories = append(DefaultSet.SandboxDirectories, abs_path) 105 106 // Allow pongo2 temp files 107 DefaultSet.SandboxDirectories = append(DefaultSet.SandboxDirectories, "/tmp/pongo2_*") 108 109 f, err := ioutil.TempFile("/tmp/", "pongo2_") 110 if err != nil { 111 panic("cannot write to /tmp/") 112 } 113 f.Write([]byte("Hello from pongo2")) 114 DefaultSet.Globals["temp_file"] = f.Name() 115 } 116 117 /* 118 * End setup sandbox 119 */ 120 121 var tplContext = Context{ 122 "number": 11, 123 "simple": map[string]interface{}{ 124 "number": 42, 125 "name": "john doe", 126 "included_file": "INCLUDES.helper", 127 "nil": nil, 128 "uint": uint(8), 129 "float": float64(3.1415), 130 "str": "string", 131 "chinese_hello_world": "你好世界", 132 "bool_true": true, 133 "bool_false": false, 134 "newline_text": `this is a text 135 with a new line in it`, 136 "long_text": `This is a simple text. 137 138 This too, as a paragraph. 139 Right? 140 141 Yep!`, 142 "escape_js_test": `escape sequences \r\n\'\" special chars "?!=$<>`, 143 "one_item_list": []int{99}, 144 "multiple_item_list": []int{1, 1, 2, 3, 5, 8, 13, 21, 34, 55}, 145 "misc_list": []interface{}{"Hello", 99, 3.14, "good"}, 146 "escape_text": "This is \\a Test. \"Yep\". 'Yep'.", 147 "xss": "<script>alert(\"uh oh\");</script>", 148 "intmap": map[int]string{ 149 1: "one", 150 2: "two", 151 5: "five", 152 }, 153 "func_add": func(a, b int) int { 154 return a + b 155 }, 156 "func_add_iface": func(a, b interface{}) interface{} { 157 return a.(int) + b.(int) 158 }, 159 "func_variadic": func(msg string, args ...interface{}) string { 160 return fmt.Sprintf(msg, args...) 161 }, 162 "func_variadic_sum_int": func(args ...int) int { 163 // Create a sum 164 s := 0 165 for _, i := range args { 166 s += i 167 } 168 return s 169 }, 170 "func_variadic_sum_int2": func(args ...*Value) *Value { 171 // Create a sum 172 s := 0 173 for _, i := range args { 174 s += i.Integer() 175 } 176 return AsValue(s) 177 }, 178 }, 179 "complex": map[string]interface{}{ 180 "is_admin": is_admin, 181 "post": post{ 182 Text: "<h2>Hello!</h2><p>Welcome to my new blog page. I'm using pongo2 which supports {{ variables }} and {% tags %}.</p>", 183 Created: time2, 184 }, 185 "comments": []*comment{ 186 &comment{ 187 Author: &user{ 188 Name: "user1", 189 Validated: true, 190 }, 191 Date: time1, 192 Text: "\"pongo2 is nice!\"", 193 }, 194 &comment{ 195 Author: &user{ 196 Name: "user2", 197 Validated: true, 198 }, 199 Date: time2, 200 Text: "comment2 with <script>unsafe</script> tags in it", 201 }, 202 &comment{ 203 Author: &user{ 204 Name: "user3", 205 Validated: false, 206 }, 207 Date: time1, 208 Text: "<b>hello!</b> there", 209 }, 210 }, 211 "comments2": []*comment{ 212 &comment{ 213 Author: &user{ 214 Name: "user1", 215 Validated: true, 216 }, 217 Date: time2, 218 Text: "\"pongo2 is nice!\"", 219 }, 220 &comment{ 221 Author: &user{ 222 Name: "user1", 223 Validated: true, 224 }, 225 Date: time1, 226 Text: "comment2 with <script>unsafe</script> tags in it", 227 }, 228 &comment{ 229 Author: &user{ 230 Name: "user3", 231 Validated: false, 232 }, 233 Date: time1, 234 Text: "<b>hello!</b> there", 235 }, 236 }, 237 }, 238 } 239 240 func TestTemplates(t *testing.T) { 241 debug = true 242 243 // Add a global to the default set 244 Globals["this_is_a_global_variable"] = "this is a global text" 245 246 matches, err := filepath.Glob("./template_tests/*.tpl") 247 if err != nil { 248 t.Fatal(err) 249 } 250 for idx, match := range matches { 251 t.Logf("[Template %3d] Testing '%s'", idx+1, match) 252 tpl, err := FromFile(match) 253 if err != nil { 254 t.Fatalf("Error on FromFile('%s'): %s", match, err.Error()) 255 } 256 test_filename := fmt.Sprintf("%s.out", match) 257 test_out, rerr := ioutil.ReadFile(test_filename) 258 if rerr != nil { 259 t.Fatalf("Error on ReadFile('%s'): %s", test_filename, rerr.Error()) 260 } 261 tpl_out, err := tpl.ExecuteBytes(tplContext) 262 if err != nil { 263 t.Fatalf("Error on Execute('%s'): %s", match, err.Error()) 264 } 265 if bytes.Compare(test_out, tpl_out) != 0 { 266 t.Logf("Template (rendered) '%s': '%s'", match, tpl_out) 267 err_filename := filepath.Base(fmt.Sprintf("%s.error", match)) 268 err := ioutil.WriteFile(err_filename, []byte(tpl_out), 0600) 269 if err != nil { 270 t.Fatalf(err.Error()) 271 } 272 t.Logf("get a complete diff with command: 'diff -ya %s %s'", test_filename, err_filename) 273 t.Errorf("Failed: test_out != tpl_out for %s", match) 274 } 275 } 276 } 277 278 func TestExecutionErrors(t *testing.T) { 279 debug = true 280 281 matches, err := filepath.Glob("./template_tests/*-execution.err") 282 if err != nil { 283 t.Fatal(err) 284 } 285 for idx, match := range matches { 286 t.Logf("[Errors %3d] Testing '%s'", idx+1, match) 287 288 test_data, err := ioutil.ReadFile(match) 289 tests := strings.Split(string(test_data), "\n") 290 291 check_filename := fmt.Sprintf("%s.out", match) 292 check_data, err := ioutil.ReadFile(check_filename) 293 if err != nil { 294 t.Fatalf("Error on ReadFile('%s'): %s", check_filename, err.Error()) 295 } 296 checks := strings.Split(string(check_data), "\n") 297 298 if len(checks) != len(tests) { 299 t.Fatal("Template lines != Checks lines") 300 } 301 302 for idx, test := range tests { 303 if strings.TrimSpace(test) == "" { 304 continue 305 } 306 if strings.TrimSpace(checks[idx]) == "" { 307 t.Fatalf("[%s Line %d] Check is empty (must contain an regular expression).", 308 match, idx+1) 309 } 310 311 tpl, err := FromString(test) 312 if err != nil { 313 t.Fatalf("Error on FromString('%s'): %s", test, err.Error()) 314 } 315 316 _, err = tpl.ExecuteBytes(tplContext) 317 if err == nil { 318 t.Fatalf("[%s Line %d] Expected error for (got none): %s", 319 match, idx+1, tests[idx]) 320 } 321 322 re := regexp.MustCompile(fmt.Sprintf("^%s$", checks[idx])) 323 if !re.MatchString(err.Error()) { 324 t.Fatalf("[%s Line %d] Error for '%s' (err = '%s') does not match the (regexp-)check: %s", 325 match, idx+1, test, err.Error(), checks[idx]) 326 } 327 } 328 } 329 } 330 331 func TestCompilationErrors(t *testing.T) { 332 debug = true 333 334 matches, err := filepath.Glob("./template_tests/*-compilation.err") 335 if err != nil { 336 t.Fatal(err) 337 } 338 for idx, match := range matches { 339 t.Logf("[Errors %3d] Testing '%s'", idx+1, match) 340 341 test_data, err := ioutil.ReadFile(match) 342 tests := strings.Split(string(test_data), "\n") 343 344 check_filename := fmt.Sprintf("%s.out", match) 345 check_data, err := ioutil.ReadFile(check_filename) 346 if err != nil { 347 t.Fatalf("Error on ReadFile('%s'): %s", check_filename, err.Error()) 348 } 349 checks := strings.Split(string(check_data), "\n") 350 351 if len(checks) != len(tests) { 352 t.Fatal("Template lines != Checks lines") 353 } 354 355 for idx, test := range tests { 356 if strings.TrimSpace(test) == "" { 357 continue 358 } 359 if strings.TrimSpace(checks[idx]) == "" { 360 t.Fatalf("[%s Line %d] Check is empty (must contain an regular expression).", 361 match, idx+1) 362 } 363 364 _, err = FromString(test) 365 if err == nil { 366 t.Fatalf("[%s | Line %d] Expected error for (got none): %s", match, idx+1, tests[idx]) 367 } 368 re := regexp.MustCompile(fmt.Sprintf("^%s$", checks[idx])) 369 if !re.MatchString(err.Error()) { 370 t.Fatalf("[%s | Line %d] Error for '%s' (err = '%s') does not match the (regexp-)check: %s", 371 match, idx+1, test, err.Error(), checks[idx]) 372 } 373 } 374 } 375 } 376 377 func TestBaseDirectory(t *testing.T) { 378 mustStr := "Hello from template_tests/base_dir_test/" 379 380 s := NewSet("test set with base directory") 381 s.Globals["base_directory"] = "template_tests/base_dir_test/" 382 if err := s.SetBaseDirectory(s.Globals["base_directory"].(string)); err != nil { 383 t.Fatal(err) 384 } 385 386 matches, err := filepath.Glob("./template_tests/base_dir_test/subdir/*") 387 if err != nil { 388 t.Fatal(err) 389 } 390 for _, match := range matches { 391 match = strings.Replace(match, "template_tests/base_dir_test/", "", -1) 392 393 tpl, err := s.FromFile(match) 394 if err != nil { 395 t.Fatal(err) 396 } 397 out, err := tpl.Execute(nil) 398 if err != nil { 399 t.Fatal(err) 400 } 401 if out != mustStr { 402 t.Errorf("%s: out ('%s') != mustStr ('%s')", match, out, mustStr) 403 } 404 } 405 } 406 407 func BenchmarkCache(b *testing.B) { 408 cache_set := NewSet("cache set") 409 for i := 0; i < b.N; i++ { 410 tpl, err := cache_set.FromCache("template_tests/complex.tpl") 411 if err != nil { 412 b.Fatal(err) 413 } 414 _, err = tpl.ExecuteBytes(tplContext) 415 if err != nil { 416 b.Fatal(err) 417 } 418 } 419 } 420 421 func BenchmarkCacheDebugOn(b *testing.B) { 422 cache_debug_set := NewSet("cache set") 423 cache_debug_set.Debug = true 424 for i := 0; i < b.N; i++ { 425 tpl, err := cache_debug_set.FromFile("template_tests/complex.tpl") 426 if err != nil { 427 b.Fatal(err) 428 } 429 _, err = tpl.ExecuteBytes(tplContext) 430 if err != nil { 431 b.Fatal(err) 432 } 433 } 434 } 435 436 func BenchmarkExecuteComplexWithSandboxActive(b *testing.B) { 437 tpl, err := FromFile("template_tests/complex.tpl") 438 if err != nil { 439 b.Fatal(err) 440 } 441 b.ResetTimer() 442 for i := 0; i < b.N; i++ { 443 _, err = tpl.ExecuteBytes(tplContext) 444 if err != nil { 445 b.Fatal(err) 446 } 447 } 448 } 449 450 func BenchmarkCompileAndExecuteComplexWithSandboxActive(b *testing.B) { 451 buf, err := ioutil.ReadFile("template_tests/complex.tpl") 452 if err != nil { 453 b.Fatal(err) 454 } 455 preloadedTpl := string(buf) 456 b.ResetTimer() 457 for i := 0; i < b.N; i++ { 458 tpl, err := FromString(preloadedTpl) 459 if err != nil { 460 b.Fatal(err) 461 } 462 463 _, err = tpl.ExecuteBytes(tplContext) 464 if err != nil { 465 b.Fatal(err) 466 } 467 } 468 } 469 470 func BenchmarkParallelExecuteComplexWithSandboxActive(b *testing.B) { 471 tpl, err := FromFile("template_tests/complex.tpl") 472 if err != nil { 473 b.Fatal(err) 474 } 475 b.ResetTimer() 476 b.RunParallel(func(pb *testing.PB) { 477 for pb.Next() { 478 _, err := tpl.ExecuteBytes(tplContext) 479 if err != nil { 480 b.Fatal(err) 481 } 482 } 483 }) 484 } 485 486 func BenchmarkExecuteComplexWithoutSandbox(b *testing.B) { 487 s := NewSet("set without sandbox") 488 tpl, err := s.FromFile("template_tests/complex.tpl") 489 if err != nil { 490 b.Fatal(err) 491 } 492 b.ResetTimer() 493 for i := 0; i < b.N; i++ { 494 _, err = tpl.ExecuteBytes(tplContext) 495 if err != nil { 496 b.Fatal(err) 497 } 498 } 499 } 500 501 func BenchmarkCompileAndExecuteComplexWithoutSandbox(b *testing.B) { 502 buf, err := ioutil.ReadFile("template_tests/complex.tpl") 503 if err != nil { 504 b.Fatal(err) 505 } 506 preloadedTpl := string(buf) 507 508 s := NewSet("set without sandbox") 509 510 b.ResetTimer() 511 for i := 0; i < b.N; i++ { 512 tpl, err := s.FromString(preloadedTpl) 513 if err != nil { 514 b.Fatal(err) 515 } 516 517 _, err = tpl.ExecuteBytes(tplContext) 518 if err != nil { 519 b.Fatal(err) 520 } 521 } 522 } 523 524 func BenchmarkParallelExecuteComplexWithoutSandbox(b *testing.B) { 525 s := NewSet("set without sandbox") 526 tpl, err := s.FromFile("template_tests/complex.tpl") 527 if err != nil { 528 b.Fatal(err) 529 } 530 b.ResetTimer() 531 b.RunParallel(func(pb *testing.PB) { 532 for pb.Next() { 533 _, err := tpl.ExecuteBytes(tplContext) 534 if err != nil { 535 b.Fatal(err) 536 } 537 } 538 }) 539 }