File: main_test.go

package info (click to toggle)
golang-github-benbjohnson-tmpl 0.0~git20160209.0.8e77bc5-7
  • links: PTS, VCS
  • area: main
  • in suites: bullseye, sid
  • size: 88 kB
  • sloc: makefile: 2
file content (223 lines) | stat: -rw-r--r-- 6,242 bytes parent folder | download | duplicates (3)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
package main_test

import (
	"bytes"
	"io"
	"io/ioutil"
	"os"
	"reflect"
	"testing"
	"time"

	main "github.com/benbjohnson/tmpl"
)

// Ensure paths can be parsed from command line flags.
func TestMain_ParseFlags_Paths(t *testing.T) {
	m := NewMain()
	if err := m.ParseFlags([]string{"a", "b", "c"}); err != nil {
		t.Fatal(err)
	} else if !reflect.DeepEqual(m.Paths, []string{"a", "b", "c"}) {
		t.Fatalf("unexpected paths: %+v", m.Paths)
	}
}

// Ensure data can be parsed from command line flags as JSON.
func TestMain_ParseFlags_Data_JSON(t *testing.T) {
	m := NewMain()
	if err := m.ParseFlags([]string{"-data", `{"foo":"bar"}`}); err != nil {
		t.Fatal(err)
	} else if !reflect.DeepEqual(m.Data, map[string]interface{}{"foo": "bar"}) {
		t.Fatalf("unexpected data: %#v", m.Data)
	}
}

// Ensure data can be parsed from command line flags as a filename.
func TestMain_ParseFlags_Data_File(t *testing.T) {
	m := NewMain()
	m.FileReadWriter.ReadFileFn = func(filename string) ([]byte, error) {
		if filename != "path/to/data" {
			t.Fatalf("unexpected filename: %s", filename)
		}
		return []byte(`{"foo":"bar"}`), nil
	}

	if err := m.ParseFlags([]string{"-data", `@path/to/data`}); err != nil {
		t.Fatal(err)
	} else if !reflect.DeepEqual(m.Data, map[string]interface{}{"foo": "bar"}) {
		t.Fatalf("unexpected data: %#v", m.Data)
	}
}

// Ensure a basic template file can be processed.
func TestMain_Run(t *testing.T) {
	m := NewMain()
	m.OS.StatFn = func(filename string) (os.FileInfo, error) {
		if filename != "a.tmpl" {
			t.Fatalf("unexpected filename: %s", filename)
		}
		return &fileInfo{mode: 0666}, nil
	}
	m.FileReadWriter.ReadFileFn = func(filename string) ([]byte, error) {
		if filename != "a.tmpl" {
			t.Fatalf("unexpected filename: %s", filename)
		}
		return []byte(`hi {{.name}}, you are {{.age}}`), nil
	}
	m.FileReadWriter.WriteFileFn = func(filename string, data []byte, perm os.FileMode) error {
		if filename != "a" {
			t.Fatalf("unexpected filename: %s", filename)
		} else if string(data) != `hi bob, you are 12` {
			t.Fatalf("unexpected data: %s", data)
		} else if perm != 0666 {
			t.Fatalf("unexpected perm: %s", perm)
		}
		return nil
	}
	m.Paths = []string{"a.tmpl"}
	m.Data = map[string]interface{}{"name": "bob", "age": 12}
	if err := m.Run(); err != nil {
		t.Fatal(err)
	}
}

// Ensure a file can be processed against array data.
func TestMain_Run_Array(t *testing.T) {
	m := NewMain()
	m.FileReadWriter.ReadFileFn = func(filename string) ([]byte, error) {
		return []byte(`I like{{range .}} ({{.}}){{end}}`), nil
	}
	m.FileReadWriter.WriteFileFn = func(filename string, data []byte, perm os.FileMode) error {
		if string(data) != `I like (apple) (pear)` {
			t.Fatalf("unexpected data: %s", data)
		}
		return nil
	}

	m.Paths = []string{"a.tmpl"}
	m.Data = []interface{}{"apple", "pear"}
	if err := m.Run(); err != nil {
		t.Fatal(err)
	}
}

// Ensure a file will add a comment header if generating a Go file.
func TestMain_Run_Header_Go(t *testing.T) {
	m := NewMain()
	m.FileReadWriter.ReadFileFn = func(filename string) ([]byte, error) {
		return []byte("\n\n\n\n\n\npackage foo"), nil
	}
	m.FileReadWriter.WriteFileFn = func(filename string, data []byte, perm os.FileMode) error {
		if string(data) != "// Generated by tmpl\n// https://github.com/benbjohnson/tmpl\n//\n// DO NOT EDIT!\n// Source: x.go.tmpl\n\npackage foo\n" {
			t.Fatalf("unexpected data: %s", data)
		}
		return nil
	}

	m.Paths = []string{"x.go.tmpl"}
	m.Data = []interface{}{"apple", "pear"}
	if err := m.Run(); err != nil {
		t.Fatal(err)
	}
}

// Main is a test wrapper for main.Main.
type Main struct {
	*main.Main

	OS             MainOS
	FileReadWriter MainFileReadWriter

	Stdin  bytes.Buffer
	Stdout bytes.Buffer
	Stderr bytes.Buffer
}

// NewMain returns a new instance of Main.
// If the verbose command line flag is set then stdout/stderr also go to the terminal.
func NewMain() *Main {
	m := &Main{Main: main.NewMain()}
	m.Main.OS = &m.OS
	m.Main.FileReadWriter = &m.FileReadWriter
	m.Main.Stdin = &m.Stdin
	m.Main.Stdout = &m.Stdout
	m.Main.Stderr = &m.Stderr

	if testing.Verbose() {
		m.Main.Stdout = io.MultiWriter(os.Stdout, m.Main.Stdout)
		m.Main.Stderr = io.MultiWriter(os.Stderr, m.Main.Stderr)
	}

	// Default stat() to use 0666.
	m.OS.StatFn = DefaultOSStat

	return m
}

// MainOS is a mockable implementation of Main.OS.
type MainOS struct {
	StatFn func(filename string) (os.FileInfo, error)
}

func (os *MainOS) Stat(filename string) (os.FileInfo, error) {
	return os.StatFn(filename)
}

func DefaultOSStat(filename string) (os.FileInfo, error) { return &fileInfo{mode: 0666}, nil }

// MainFileReadWriter is a mockable implementation of Main.FileReadWriter.
type MainFileReadWriter struct {
	ReadFileFn  func(filename string) ([]byte, error)
	WriteFileFn func(filename string, data []byte, perm os.FileMode) error
}

func (r *MainFileReadWriter) ReadFile(filename string) ([]byte, error) {
	return r.ReadFileFn(filename)
}

func (r *MainFileReadWriter) WriteFile(filename string, data []byte, perm os.FileMode) error {
	return r.WriteFileFn(filename, data, perm)
}

type fileInfo struct {
	mode os.FileMode
}

func (fi *fileInfo) Name() string       { return "" }
func (fi *fileInfo) Size() int64        { return 0 }
func (fi *fileInfo) Mode() os.FileMode  { return fi.mode }
func (fi *fileInfo) ModTime() time.Time { return time.Time{} }
func (fi *fileInfo) IsDir() bool        { return false }
func (fi *fileInfo) Sys() interface{}   { return nil }

// MustTempDir returns a temporary directory. Panic on error.
func MustTempDir() string {
	path, err := ioutil.TempDir("", "tmpl-")
	if err != nil {
		panic(err)
	}
	return path
}

// MustRemoveAll recursively removes a path. Panic on error.
func MustRemoveAll(path string) {
	if err := os.RemoveAll(path); err != nil {
		panic(err)
	}
}

// MustWriteFile writes data to filename. Panic on error.
func MustWriteFile(filename string, data []byte, perm os.FileMode) {
	if err := ioutil.WriteFile(filename, data, perm); err != nil {
		panic(err)
	}
}

// MustReadFile reads all data from filename. Panic on error.
func MustReadFile(filename string) []byte {
	data, err := ioutil.ReadFile(filename)
	if err != nil {
		panic(err)
	}
	return data
}