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  }