github.com/timestee/gotemplate@v0.0.0-20170505123500-7c22f8f4ddd0/template_test.go (about)

     1  // Tests for template
     2  
     3  package main
     4  
     5  import (
     6  	"bytes"
     7  	"go/build"
     8  	"io/ioutil"
     9  	"log"
    10  	"os"
    11  	"os/exec"
    12  	"path"
    13  	"testing"
    14  )
    15  
    16  type TestTemplate struct {
    17  	title   string
    18  	args    string
    19  	pkg     string
    20  	in      string
    21  	outName string
    22  	out     string
    23  }
    24  
    25  const basicTest = `package tt
    26  
    27  import "fmt"
    28  
    29  // template type Set(A)
    30  type A int
    31  
    32  type Set struct { a A }
    33  func NewSet(a A) A { return A(0) }
    34  func NewSizedSet(a A) A { return A(1) }
    35  func UtilityFunc1() {}
    36  func utilityFunc() {}
    37  func (a A) f0() {}
    38  func (a *A) F1() {}
    39  var AVar1 int
    40  var aVar2 int
    41  var (
    42  	AVar3 int
    43  	aVar4 int
    44  )
    45  `
    46  
    47  var tests = []TestTemplate{
    48  	{
    49  		title:   "Simple test public",
    50  		args:    "MySet(int)",
    51  		pkg:     "main",
    52  		in:      basicTest,
    53  		outName: "gotemplate_MySet.go",
    54  		out: `// Code generated by gotemplate. DO NOT EDIT.
    55  
    56  package main
    57  
    58  import "fmt"
    59  
    60  // template type Set(A)
    61  
    62  type MySet struct{ a int }
    63  
    64  func NewMySet(a int) int      { return int(0) }
    65  func NewSizedMySet(a int) int { return int(1) }
    66  func UtilityFunc1MySet()      {}
    67  func utilityFuncMySet()       {}
    68  func (a int) f0()             {}
    69  func (a *int) F1()            {}
    70  
    71  var AVar1MySet int
    72  var aVar2MySet int
    73  var (
    74  	AVar3MySet int
    75  	aVar4MySet int
    76  )
    77  `,
    78  	},
    79  	{
    80  		title:   "Simple test private",
    81  		args:    "mySet(float64)",
    82  		pkg:     "main",
    83  		in:      basicTest,
    84  		outName: "gotemplate_mySet.go",
    85  		out: `// Code generated by gotemplate. DO NOT EDIT.
    86  
    87  package main
    88  
    89  import "fmt"
    90  
    91  // template type Set(A)
    92  
    93  type mySet struct{ a float64 }
    94  
    95  func newMySet(a float64) float64      { return float64(0) }
    96  func newSizedMySet(a float64) float64 { return float64(1) }
    97  func utilityFunc1MySet()              {}
    98  func utilityFuncMySet()               {}
    99  func (a float64) f0()                 {}
   100  func (a *float64) F1()                {}
   101  
   102  var aVar1MySet int
   103  var aVar2MySet int
   104  var (
   105  	aVar3MySet int
   106  	aVar4MySet int
   107  )
   108  `,
   109  	},
   110  	{
   111  		title: "Test function",
   112  		args:  "Min(int8, func(a int8, b int8) bool { return a < b })",
   113  		pkg:   "main",
   114  		in: `package tt
   115  
   116  // template type TT(A, Less)
   117  type A int
   118  func Less(a, b A) { return a < b }
   119  
   120  func TT(a, b A) A { return Less(a, b) }
   121  func TTone(a A) A { return !Less(a, b) }
   122  `,
   123  		outName: "gotemplate_Min.go",
   124  		out: `// Code generated by gotemplate. DO NOT EDIT.
   125  
   126  package main
   127  
   128  // template type TT(A, Less)
   129  
   130  func Min(a, b int8) int8 {
   131  	return func(a int8, b int8) bool {
   132  		return a < b
   133  	}(a, b)
   134  }
   135  func Minone(a int8) int8 {
   136  	return !func(a int8, b int8) bool {
   137  		return a < b
   138  	}(a, b)
   139  }
   140  `,
   141  	},
   142  	{
   143  		title: "Simple Test constants",
   144  		args:  "Vector2(float32, 2)",
   145  		pkg:   "main",
   146  		in: `package tt
   147  
   148  // template type Vector(A, n)
   149  type A float32
   150  const n = 3
   151  
   152  type Vector [n]A
   153  
   154  func (v Vector) Add(b Vector) {
   155  	for i := range v {
   156  		v += b[i]
   157  	}
   158  }
   159  `,
   160  		outName: "gotemplate_Vector2.go",
   161  		out: `// Code generated by gotemplate. DO NOT EDIT.
   162  
   163  package main
   164  
   165  // template type Vector(A, n)
   166  
   167  type Vector2 [2]float32
   168  
   169  func (v Vector2) Add(b Vector2) {
   170  	for i := range v {
   171  		v += b[i]
   172  	}
   173  }
   174  `,
   175  	},
   176  	{
   177  		title: "Test constants",
   178  		args:  "Matrix22(float32, 2, 2)",
   179  		pkg:   "main",
   180  		in: `package mat
   181  
   182  // template type Matrix(A, n, m)
   183  type A float32
   184  
   185  const (
   186  	n, a, b, m = 1, 2, 3, 1
   187  )
   188  
   189  type Matrix [n][m]A
   190  
   191  func (mat Matrix) Add(x Matrix) {
   192  	for i := range mat {
   193  		for j := range mat[i] {
   194  			mat[i][j] += x[i][j]
   195  		}
   196  	}
   197  }
   198  `,
   199  		outName: "gotemplate_Matrix22.go",
   200  		out: `// Code generated by gotemplate. DO NOT EDIT.
   201  
   202  package main
   203  
   204  // template type Matrix(A, n, m)
   205  
   206  const (
   207  	aMatrix22, bMatrix22 = 2, 3
   208  )
   209  
   210  type Matrix22 [2][2]float32
   211  
   212  func (mat Matrix22) Add(x Matrix22) {
   213  	for i := range mat {
   214  		for j := range mat[i] {
   215  			mat[i][j] += x[i][j]
   216  		}
   217  	}
   218  }
   219  `,
   220  	},
   221  	{
   222  		title: "Test vars",
   223  		args:  "ProgXX(xx1, xx2, xx3, xx4, xx5, xx6)",
   224  		pkg:   "main",
   225  		in: `package prog
   226  
   227  // template type Prog(a, b, c, d, e, f)
   228  type A float32
   229  
   230  var (
   231  	a, z = 1, 2
   232  	b, n, m, c = 3, 4, 5, 6
   233  	d = 7
   234  )
   235  
   236  var (
   237  	o = 8
   238  	e = 8
   239  )
   240  
   241  var (
   242  	e = 8
   243  )
   244  
   245  var (
   246  	oo = 9
   247  	e = 10
   248  )
   249  
   250  var (
   251  	p, f, q = 11, 12, 13
   252  )
   253  
   254  func Prog() int {return a+b+c+d+e+f}
   255  `,
   256  		outName: "gotemplate_ProgXX.go",
   257  		out: `// Code generated by gotemplate. DO NOT EDIT.
   258  
   259  package main
   260  
   261  // template type Prog(a, b, c, d, e, f)
   262  type AProgXX float32
   263  
   264  var (
   265  	zProgXX          = 2
   266  	nProgXX, mProgXX = 4, 5
   267  )
   268  
   269  var (
   270  	oProgXX = 8
   271  )
   272  
   273  var (
   274  	ooProgXX = 9
   275  )
   276  
   277  var (
   278  	pProgXX, qProgXX = 11, 13
   279  )
   280  
   281  func ProgXX() int { return xx1 + xx2 + xx3 + xx4 + xx5 + xx6 }
   282  `,
   283  	},
   284  	{
   285  		title: "Test complex type decls",
   286  		args:  "tmpl(int, string, map[string]map[string]chan int, float32, rune, chan []string)",
   287  		pkg:   "main",
   288  		in: `package tt
   289  
   290  // template type TMPL(A, B, C, D, E, F)
   291  type A int
   292  
   293  type TMPL struct {
   294  	a A
   295  	b B
   296  	c C
   297  	d D
   298  	e E
   299  	f F
   300  }
   301  
   302  type ImportantType bool
   303  
   304  type (
   305  	ImportantType1 int
   306  	B struct {
   307  		v map[int][][][]rune
   308  	}
   309  	importantType2 map[int]int
   310  	C chan struct {
   311  		x []string
   312  	}
   313  )
   314  
   315  type (
   316  	D rune
   317  	importantType3 struct{}
   318  	E string
   319  	F map[string]int
   320  )
   321  `,
   322  		outName: "gotemplate_tmpl.go",
   323  		out: `// Code generated by gotemplate. DO NOT EDIT.
   324  
   325  package main
   326  
   327  // template type TMPL(A, B, C, D, E, F)
   328  
   329  type tmpl struct {
   330  	a int
   331  	b string
   332  	c map[string]map[string]chan int
   333  	d float32
   334  	e rune
   335  	f chan []string
   336  }
   337  
   338  type importantTypeTmpl bool
   339  
   340  type (
   341  	importantType1Tmpl int
   342  
   343  	importantType2Tmpl map[int]int
   344  )
   345  
   346  type (
   347  	importantType3Tmpl struct{}
   348  )
   349  `,
   350  	},
   351  }
   352  
   353  func testTemplate(t *testing.T, test *TestTemplate) {
   354  	// Disable logging
   355  	log.SetOutput(ioutil.Discard)
   356  
   357  	// Make temporary directory
   358  	dir, err := ioutil.TempDir("", "gotemplate_test")
   359  	if err != nil {
   360  		t.Fatalf("Failed to make temp dir: %v", err)
   361  	}
   362  	defer func() {
   363  		err := os.RemoveAll(dir)
   364  		if err != nil {
   365  			t.Logf("Failed to remove temp dir: %v", err)
   366  		}
   367  	}()
   368  
   369  	// Make subdirectories
   370  	src := path.Join(dir, "src")
   371  	err = os.Mkdir(src, 0700)
   372  	if err != nil {
   373  		t.Fatalf("Failed to make dir %q: %v", src, err)
   374  	}
   375  	input := path.Join(src, "input")
   376  	err = os.Mkdir(input, 0700)
   377  	if err != nil {
   378  		t.Fatalf("Failed to make dir %q: %v", input, err)
   379  	}
   380  	output := path.Join(src, "output")
   381  	err = os.Mkdir(output, 0700)
   382  	if err != nil {
   383  		t.Fatalf("Failed to make dir %q: %v", output, err)
   384  	}
   385  
   386  	// Change directory to output directory
   387  	cwd, err := os.Getwd()
   388  	if err != nil {
   389  		t.Fatalf("Failed to read cwd: %v", err)
   390  	}
   391  	err = os.Chdir(output)
   392  	if err != nil {
   393  		t.Fatalf("Failed to cd %q dir: %v", output, err)
   394  	}
   395  	defer func() {
   396  		err := os.Chdir(cwd)
   397  		if err != nil {
   398  			t.Logf("Failed to change back to cwd: %v", err)
   399  		}
   400  	}()
   401  
   402  	// Set GOPATH to directory
   403  	build.Default.GOPATH = dir
   404  
   405  	// Write template input
   406  	tmpl := path.Join(input, "main.go")
   407  	err = ioutil.WriteFile(tmpl, []byte(test.in), 0600)
   408  	if err != nil {
   409  		t.Fatalf("Failed to write %q: %v", tmpl, err)
   410  	}
   411  
   412  	// Write main.go for output
   413  	main := path.Join(output, "main.go")
   414  	err = ioutil.WriteFile(main, []byte("package main"), 0600)
   415  	if err != nil {
   416  		t.Fatalf("Failed to write %q: %v", main, err)
   417  	}
   418  
   419  	// Instantiate template
   420  	template := newTemplate(output, "input", test.args)
   421  	template.instantiate()
   422  
   423  	// Check output
   424  	expectedFile := path.Join(output, test.outName)
   425  	actualBytes, err := ioutil.ReadFile(expectedFile)
   426  	if err != nil {
   427  		t.Fatalf("Failed to read %q: %v", expectedFile, err)
   428  	}
   429  	actual := string(actualBytes)
   430  	if actual != test.out {
   431  		t.Errorf(`Output is wrong
   432  Got
   433  -------------
   434  %s
   435  -------------
   436  Expected
   437  -------------
   438  %s
   439  -------------
   440  `, actual, test.out)
   441  		actualFile := expectedFile + ".actual"
   442  		err = ioutil.WriteFile(actualFile, []byte(test.out), 0600)
   443  		if err != nil {
   444  			t.Fatalf("Failed to write %q: %v", actualFile, err)
   445  		}
   446  		cmd := exec.Command("diff", "-u", actualFile, expectedFile)
   447  		var out bytes.Buffer
   448  		cmd.Stdout = &out
   449  		cmd.Stderr = &out
   450  		_ = cmd.Run()
   451  		t.Errorf("Diff\n----\n%s", out.String())
   452  	}
   453  
   454  }
   455  
   456  func TestSub(t *testing.T) {
   457  	fatalf = func(format string, args ...interface{}) {
   458  		t.Fatalf(format, args...)
   459  	}
   460  	for i := range tests {
   461  		t.Logf("Test[%d] %q", i, tests[i].title)
   462  		testTemplate(t, &tests[i])
   463  	}
   464  }