1 | // Copyright 2021 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 generator |
6 | |
7 | import ( |
8 | "bytes" |
9 | "os" |
10 | "os/exec" |
11 | "path/filepath" |
12 | "runtime" |
13 | "testing" |
14 | |
15 | "golang.org/x/tools/internal/testenv" |
16 | ) |
17 | |
18 | func mkGenState() *genstate { |
19 | |
20 | return &genstate{ |
21 | GenConfig: GenConfig{ |
22 | Tag: "gen", |
23 | OutDir: "/tmp", |
24 | NumTestPackages: 1, |
25 | NumTestFunctions: 10, |
26 | }, |
27 | ipref: "foo/", |
28 | derefFuncs: make(map[string]string), |
29 | assignFuncs: make(map[string]string), |
30 | allocFuncs: make(map[string]string), |
31 | globVars: make(map[string]string), |
32 | } |
33 | } |
34 | |
35 | func TestBasic(t *testing.T) { |
36 | checkTunables(tunables) |
37 | s := mkGenState() |
38 | for i := 0; i < 1000; i++ { |
39 | s.wr = NewWrapRand(int64(i), RandCtlChecks|RandCtlPanic) |
40 | fp := s.GenFunc(i, i) |
41 | var buf bytes.Buffer |
42 | var b *bytes.Buffer = &buf |
43 | wr := NewWrapRand(int64(i), RandCtlChecks|RandCtlPanic) |
44 | s.wr = wr |
45 | s.emitCaller(fp, b, i) |
46 | s.wr = NewWrapRand(int64(i), RandCtlChecks|RandCtlPanic) |
47 | s.emitChecker(fp, b, i, true) |
48 | wr.Check(s.wr) |
49 | } |
50 | if s.errs != 0 { |
51 | t.Errorf("%d errors during Generate", s.errs) |
52 | } |
53 | } |
54 | |
55 | func TestMoreComplicated(t *testing.T) { |
56 | saveit := tunables |
57 | defer func() { tunables = saveit }() |
58 | |
59 | checkTunables(tunables) |
60 | s := mkGenState() |
61 | for i := 0; i < 10000; i++ { |
62 | s.wr = NewWrapRand(int64(i), RandCtlChecks|RandCtlPanic) |
63 | fp := s.GenFunc(i, i) |
64 | var buf bytes.Buffer |
65 | var b *bytes.Buffer = &buf |
66 | wr := NewWrapRand(int64(i), RandCtlChecks|RandCtlPanic) |
67 | s.wr = wr |
68 | s.emitCaller(fp, b, i) |
69 | verb(1, "finished iter %d caller", i) |
70 | s.wr = NewWrapRand(int64(i), RandCtlChecks|RandCtlPanic) |
71 | s.emitChecker(fp, b, i, true) |
72 | verb(1, "finished iter %d checker", i) |
73 | wr.Check(s.wr) |
74 | if s.errs != 0 { |
75 | t.Errorf("%d errors during Generate iter %d", s.errs, i) |
76 | } |
77 | } |
78 | } |
79 | |
80 | func TestIsBuildable(t *testing.T) { |
81 | testenv.NeedsTool(t, "go") |
82 | if runtime.GOOS == "android" { |
83 | t.Skipf("the dependencies are not available on android") |
84 | } |
85 | |
86 | td := t.TempDir() |
87 | verb(1, "generating into temp dir %s", td) |
88 | checkTunables(tunables) |
89 | pack := filepath.Base(td) |
90 | s := GenConfig{ |
91 | Tag: "x", |
92 | OutDir: td, |
93 | PkgPath: pack, |
94 | NumTestFunctions: 10, |
95 | NumTestPackages: 10, |
96 | MaxFail: 10, |
97 | RandCtl: RandCtlChecks | RandCtlPanic, |
98 | } |
99 | errs := Generate(s) |
100 | if errs != 0 { |
101 | t.Errorf("%d errors during Generate", errs) |
102 | } |
103 | |
104 | verb(1, "building %s\n", td) |
105 | |
106 | cmd := exec.Command("go", "run", ".") |
107 | cmd.Dir = td |
108 | coutput, cerr := cmd.CombinedOutput() |
109 | if cerr != nil { |
110 | t.Errorf("go build command failed: %s\n", string(coutput)) |
111 | } |
112 | verb(1, "output is: %s\n", string(coutput)) |
113 | } |
114 | |
115 | // TestExhaustive does a series of code genreation runs, starting with |
116 | // (relatively) simple code and then getting progressively more |
117 | // complex (more params, deeper structs, turning on additional |
118 | // features such as address-taken vars and reflect testing). The |
119 | // intent here is mainly to insure that the tester still works if you |
120 | // turn things on and off, e.g. that each feature is separately |
121 | // controllable and not linked to other things. |
122 | func TestExhaustive(t *testing.T) { |
123 | testenv.NeedsTool(t, "go") |
124 | if runtime.GOOS == "android" { |
125 | t.Skipf("the dependencies are not available on android") |
126 | } |
127 | |
128 | if testing.Short() { |
129 | t.Skip("skipping test in short mode.") |
130 | } |
131 | |
132 | td := t.TempDir() |
133 | verb(1, "generating into temp dir %s", td) |
134 | |
135 | scenarios := []struct { |
136 | name string |
137 | adjuster func() |
138 | }{ |
139 | { |
140 | "minimal", |
141 | func() { |
142 | tunables.nParmRange = 3 |
143 | tunables.nReturnRange = 3 |
144 | tunables.structDepth = 1 |
145 | tunables.recurPerc = 0 |
146 | tunables.methodPerc = 0 |
147 | tunables.doReflectCall = false |
148 | tunables.doDefer = false |
149 | tunables.takeAddress = false |
150 | tunables.doFuncCallValues = false |
151 | tunables.doSkipCompare = false |
152 | checkTunables(tunables) |
153 | }, |
154 | }, |
155 | { |
156 | "moreparms", |
157 | func() { |
158 | tunables.nParmRange = 15 |
159 | tunables.nReturnRange = 7 |
160 | tunables.structDepth = 3 |
161 | checkTunables(tunables) |
162 | }, |
163 | }, |
164 | { |
165 | "addrecur", |
166 | func() { |
167 | tunables.recurPerc = 20 |
168 | checkTunables(tunables) |
169 | }, |
170 | }, |
171 | { |
172 | "addmethod", |
173 | func() { |
174 | tunables.methodPerc = 25 |
175 | tunables.pointerMethodCallPerc = 30 |
176 | checkTunables(tunables) |
177 | }, |
178 | }, |
179 | { |
180 | "addtakeaddr", |
181 | func() { |
182 | tunables.takeAddress = true |
183 | tunables.takenFraction = 20 |
184 | checkTunables(tunables) |
185 | }, |
186 | }, |
187 | { |
188 | "addreflect", |
189 | func() { |
190 | tunables.doReflectCall = true |
191 | checkTunables(tunables) |
192 | }, |
193 | }, |
194 | { |
195 | "adddefer", |
196 | func() { |
197 | tunables.doDefer = true |
198 | checkTunables(tunables) |
199 | }, |
200 | }, |
201 | { |
202 | "addfuncval", |
203 | func() { |
204 | tunables.doFuncCallValues = true |
205 | checkTunables(tunables) |
206 | }, |
207 | }, |
208 | { |
209 | "addfuncval", |
210 | func() { |
211 | tunables.doSkipCompare = true |
212 | checkTunables(tunables) |
213 | }, |
214 | }, |
215 | } |
216 | |
217 | // Loop over scenarios and make sure each one works properly. |
218 | for i, s := range scenarios { |
219 | t.Logf("running %s\n", s.name) |
220 | s.adjuster() |
221 | os.RemoveAll(td) |
222 | pack := filepath.Base(td) |
223 | c := GenConfig{ |
224 | Tag: "x", |
225 | OutDir: td, |
226 | PkgPath: pack, |
227 | NumTestFunctions: 10, |
228 | NumTestPackages: 10, |
229 | Seed: int64(i + 9), |
230 | MaxFail: 10, |
231 | RandCtl: RandCtlChecks | RandCtlPanic, |
232 | } |
233 | errs := Generate(c) |
234 | if errs != 0 { |
235 | t.Errorf("%d errors during scenarios %q Generate", errs, s.name) |
236 | } |
237 | cmd := exec.Command("go", "run", ".") |
238 | cmd.Dir = td |
239 | coutput, cerr := cmd.CombinedOutput() |
240 | if cerr != nil { |
241 | t.Fatalf("run failed for scenario %q: %s\n", s.name, string(coutput)) |
242 | } |
243 | verb(1, "output is: %s\n", string(coutput)) |
244 | } |
245 | } |
246 | |
247 | func TestEmitBadBuildFailure(t *testing.T) { |
248 | testenv.NeedsTool(t, "go") |
249 | if runtime.GOOS == "android" { |
250 | t.Skipf("the dependencies are not available on android") |
251 | } |
252 | |
253 | td := t.TempDir() |
254 | verb(1, "generating into temp dir %s", td) |
255 | |
256 | checkTunables(tunables) |
257 | pack := filepath.Base(td) |
258 | s := GenConfig{ |
259 | Tag: "x", |
260 | OutDir: td, |
261 | PkgPath: pack, |
262 | NumTestFunctions: 10, |
263 | NumTestPackages: 10, |
264 | MaxFail: 10, |
265 | RandCtl: RandCtlChecks | RandCtlPanic, |
266 | EmitBad: 1, |
267 | } |
268 | errs := Generate(s) |
269 | if errs != 0 { |
270 | t.Errorf("%d errors during Generate", errs) |
271 | } |
272 | |
273 | cmd := exec.Command("go", "build", ".") |
274 | cmd.Dir = td |
275 | coutput, cerr := cmd.CombinedOutput() |
276 | if cerr == nil { |
277 | t.Errorf("go build command passed, expected failure. output: %s\n", string(coutput)) |
278 | } |
279 | } |
280 | |
281 | func TestEmitBadRunFailure(t *testing.T) { |
282 | testenv.NeedsTool(t, "go") |
283 | if runtime.GOOS == "android" { |
284 | t.Skipf("the dependencies are not available on android") |
285 | } |
286 | |
287 | td := t.TempDir() |
288 | verb(1, "generating into temp dir %s", td) |
289 | |
290 | checkTunables(tunables) |
291 | pack := filepath.Base(td) |
292 | s := GenConfig{ |
293 | Tag: "x", |
294 | OutDir: td, |
295 | PkgPath: pack, |
296 | NumTestFunctions: 10, |
297 | NumTestPackages: 10, |
298 | MaxFail: 10, |
299 | RandCtl: RandCtlChecks | RandCtlPanic, |
300 | EmitBad: 2, |
301 | } |
302 | errs := Generate(s) |
303 | if errs != 0 { |
304 | t.Errorf("%d errors during Generate", errs) |
305 | } |
306 | |
307 | // build |
308 | cmd := exec.Command("go", "build", ".") |
309 | cmd.Dir = td |
310 | coutput, cerr := cmd.CombinedOutput() |
311 | if cerr != nil { |
312 | t.Fatalf("build failed: %s\n", string(coutput)) |
313 | } |
314 | |
315 | // run |
316 | cmd = exec.Command("./" + pack) |
317 | cmd.Dir = td |
318 | coutput, cerr = cmd.CombinedOutput() |
319 | if cerr == nil { |
320 | t.Fatalf("run passed, expected failure -- run output: %s", string(coutput)) |
321 | } |
322 | } |
323 |
Members