1 | // Copyright 2014 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 | // This file contains simple golden tests for various examples. |
6 | // Besides validating the results when the implementation changes, |
7 | // it provides a way to look at the generated code without having |
8 | // to execute the print statements in one's head. |
9 | |
10 | package main |
11 | |
12 | import ( |
13 | "os" |
14 | "path/filepath" |
15 | "strings" |
16 | "testing" |
17 | |
18 | "golang.org/x/tools/internal/testenv" |
19 | ) |
20 | |
21 | // Golden represents a test case. |
22 | type Golden struct { |
23 | name string |
24 | trimPrefix string |
25 | lineComment bool |
26 | input string // input; the package clause is provided when running the test. |
27 | output string // expected output. |
28 | } |
29 | |
30 | var golden = []Golden{ |
31 | {"day", "", false, day_in, day_out}, |
32 | {"offset", "", false, offset_in, offset_out}, |
33 | {"gap", "", false, gap_in, gap_out}, |
34 | {"num", "", false, num_in, num_out}, |
35 | {"unum", "", false, unum_in, unum_out}, |
36 | {"unumpos", "", false, unumpos_in, unumpos_out}, |
37 | {"prime", "", false, prime_in, prime_out}, |
38 | {"prefix", "Type", false, prefix_in, prefix_out}, |
39 | {"tokens", "", true, tokens_in, tokens_out}, |
40 | } |
41 | |
42 | // Each example starts with "type XXX [u]int", with a single space separating them. |
43 | |
44 | // Simple test: enumeration of type int starting at 0. |
45 | const day_in = `type Day int |
46 | const ( |
47 | Monday Day = iota |
48 | Tuesday |
49 | Wednesday |
50 | Thursday |
51 | Friday |
52 | Saturday |
53 | Sunday |
54 | ) |
55 | ` |
56 | |
57 | const day_out = `func _() { |
58 | // An "invalid array index" compiler error signifies that the constant values have changed. |
59 | // Re-run the stringer command to generate them again. |
60 | var x [1]struct{} |
61 | _ = x[Monday-0] |
62 | _ = x[Tuesday-1] |
63 | _ = x[Wednesday-2] |
64 | _ = x[Thursday-3] |
65 | _ = x[Friday-4] |
66 | _ = x[Saturday-5] |
67 | _ = x[Sunday-6] |
68 | } |
69 | |
70 | const _Day_name = "MondayTuesdayWednesdayThursdayFridaySaturdaySunday" |
71 | |
72 | var _Day_index = [...]uint8{0, 6, 13, 22, 30, 36, 44, 50} |
73 | |
74 | func (i Day) String() string { |
75 | if i < 0 || i >= Day(len(_Day_index)-1) { |
76 | return "Day(" + strconv.FormatInt(int64(i), 10) + ")" |
77 | } |
78 | return _Day_name[_Day_index[i]:_Day_index[i+1]] |
79 | } |
80 | ` |
81 | |
82 | // Enumeration with an offset. |
83 | // Also includes a duplicate. |
84 | const offset_in = `type Number int |
85 | const ( |
86 | _ Number = iota |
87 | One |
88 | Two |
89 | Three |
90 | AnotherOne = One // Duplicate; note that AnotherOne doesn't appear below. |
91 | ) |
92 | ` |
93 | |
94 | const offset_out = `func _() { |
95 | // An "invalid array index" compiler error signifies that the constant values have changed. |
96 | // Re-run the stringer command to generate them again. |
97 | var x [1]struct{} |
98 | _ = x[One-1] |
99 | _ = x[Two-2] |
100 | _ = x[Three-3] |
101 | } |
102 | |
103 | const _Number_name = "OneTwoThree" |
104 | |
105 | var _Number_index = [...]uint8{0, 3, 6, 11} |
106 | |
107 | func (i Number) String() string { |
108 | i -= 1 |
109 | if i < 0 || i >= Number(len(_Number_index)-1) { |
110 | return "Number(" + strconv.FormatInt(int64(i+1), 10) + ")" |
111 | } |
112 | return _Number_name[_Number_index[i]:_Number_index[i+1]] |
113 | } |
114 | ` |
115 | |
116 | // Gaps and an offset. |
117 | const gap_in = `type Gap int |
118 | const ( |
119 | Two Gap = 2 |
120 | Three Gap = 3 |
121 | Five Gap = 5 |
122 | Six Gap = 6 |
123 | Seven Gap = 7 |
124 | Eight Gap = 8 |
125 | Nine Gap = 9 |
126 | Eleven Gap = 11 |
127 | ) |
128 | ` |
129 | |
130 | const gap_out = `func _() { |
131 | // An "invalid array index" compiler error signifies that the constant values have changed. |
132 | // Re-run the stringer command to generate them again. |
133 | var x [1]struct{} |
134 | _ = x[Two-2] |
135 | _ = x[Three-3] |
136 | _ = x[Five-5] |
137 | _ = x[Six-6] |
138 | _ = x[Seven-7] |
139 | _ = x[Eight-8] |
140 | _ = x[Nine-9] |
141 | _ = x[Eleven-11] |
142 | } |
143 | |
144 | const ( |
145 | _Gap_name_0 = "TwoThree" |
146 | _Gap_name_1 = "FiveSixSevenEightNine" |
147 | _Gap_name_2 = "Eleven" |
148 | ) |
149 | |
150 | var ( |
151 | _Gap_index_0 = [...]uint8{0, 3, 8} |
152 | _Gap_index_1 = [...]uint8{0, 4, 7, 12, 17, 21} |
153 | ) |
154 | |
155 | func (i Gap) String() string { |
156 | switch { |
157 | case 2 <= i && i <= 3: |
158 | i -= 2 |
159 | return _Gap_name_0[_Gap_index_0[i]:_Gap_index_0[i+1]] |
160 | case 5 <= i && i <= 9: |
161 | i -= 5 |
162 | return _Gap_name_1[_Gap_index_1[i]:_Gap_index_1[i+1]] |
163 | case i == 11: |
164 | return _Gap_name_2 |
165 | default: |
166 | return "Gap(" + strconv.FormatInt(int64(i), 10) + ")" |
167 | } |
168 | } |
169 | ` |
170 | |
171 | // Signed integers spanning zero. |
172 | const num_in = `type Num int |
173 | const ( |
174 | m_2 Num = -2 + iota |
175 | m_1 |
176 | m0 |
177 | m1 |
178 | m2 |
179 | ) |
180 | ` |
181 | |
182 | const num_out = `func _() { |
183 | // An "invalid array index" compiler error signifies that the constant values have changed. |
184 | // Re-run the stringer command to generate them again. |
185 | var x [1]struct{} |
186 | _ = x[m_2 - -2] |
187 | _ = x[m_1 - -1] |
188 | _ = x[m0-0] |
189 | _ = x[m1-1] |
190 | _ = x[m2-2] |
191 | } |
192 | |
193 | const _Num_name = "m_2m_1m0m1m2" |
194 | |
195 | var _Num_index = [...]uint8{0, 3, 6, 8, 10, 12} |
196 | |
197 | func (i Num) String() string { |
198 | i -= -2 |
199 | if i < 0 || i >= Num(len(_Num_index)-1) { |
200 | return "Num(" + strconv.FormatInt(int64(i+-2), 10) + ")" |
201 | } |
202 | return _Num_name[_Num_index[i]:_Num_index[i+1]] |
203 | } |
204 | ` |
205 | |
206 | // Unsigned integers spanning zero. |
207 | const unum_in = `type Unum uint |
208 | const ( |
209 | m_2 Unum = iota + 253 |
210 | m_1 |
211 | ) |
212 | |
213 | const ( |
214 | m0 Unum = iota |
215 | m1 |
216 | m2 |
217 | ) |
218 | ` |
219 | |
220 | const unum_out = `func _() { |
221 | // An "invalid array index" compiler error signifies that the constant values have changed. |
222 | // Re-run the stringer command to generate them again. |
223 | var x [1]struct{} |
224 | _ = x[m_2-253] |
225 | _ = x[m_1-254] |
226 | _ = x[m0-0] |
227 | _ = x[m1-1] |
228 | _ = x[m2-2] |
229 | } |
230 | |
231 | const ( |
232 | _Unum_name_0 = "m0m1m2" |
233 | _Unum_name_1 = "m_2m_1" |
234 | ) |
235 | |
236 | var ( |
237 | _Unum_index_0 = [...]uint8{0, 2, 4, 6} |
238 | _Unum_index_1 = [...]uint8{0, 3, 6} |
239 | ) |
240 | |
241 | func (i Unum) String() string { |
242 | switch { |
243 | case i <= 2: |
244 | return _Unum_name_0[_Unum_index_0[i]:_Unum_index_0[i+1]] |
245 | case 253 <= i && i <= 254: |
246 | i -= 253 |
247 | return _Unum_name_1[_Unum_index_1[i]:_Unum_index_1[i+1]] |
248 | default: |
249 | return "Unum(" + strconv.FormatInt(int64(i), 10) + ")" |
250 | } |
251 | } |
252 | ` |
253 | |
254 | // Unsigned positive integers. |
255 | const unumpos_in = `type Unumpos uint |
256 | const ( |
257 | m253 Unumpos = iota + 253 |
258 | m254 |
259 | ) |
260 | |
261 | const ( |
262 | m1 Unumpos = iota + 1 |
263 | m2 |
264 | m3 |
265 | ) |
266 | ` |
267 | |
268 | const unumpos_out = `func _() { |
269 | // An "invalid array index" compiler error signifies that the constant values have changed. |
270 | // Re-run the stringer command to generate them again. |
271 | var x [1]struct{} |
272 | _ = x[m253-253] |
273 | _ = x[m254-254] |
274 | _ = x[m1-1] |
275 | _ = x[m2-2] |
276 | _ = x[m3-3] |
277 | } |
278 | |
279 | const ( |
280 | _Unumpos_name_0 = "m1m2m3" |
281 | _Unumpos_name_1 = "m253m254" |
282 | ) |
283 | |
284 | var ( |
285 | _Unumpos_index_0 = [...]uint8{0, 2, 4, 6} |
286 | _Unumpos_index_1 = [...]uint8{0, 4, 8} |
287 | ) |
288 | |
289 | func (i Unumpos) String() string { |
290 | switch { |
291 | case 1 <= i && i <= 3: |
292 | i -= 1 |
293 | return _Unumpos_name_0[_Unumpos_index_0[i]:_Unumpos_index_0[i+1]] |
294 | case 253 <= i && i <= 254: |
295 | i -= 253 |
296 | return _Unumpos_name_1[_Unumpos_index_1[i]:_Unumpos_index_1[i+1]] |
297 | default: |
298 | return "Unumpos(" + strconv.FormatInt(int64(i), 10) + ")" |
299 | } |
300 | } |
301 | ` |
302 | |
303 | // Enough gaps to trigger a map implementation of the method. |
304 | // Also includes a duplicate to test that it doesn't cause problems |
305 | const prime_in = `type Prime int |
306 | const ( |
307 | p2 Prime = 2 |
308 | p3 Prime = 3 |
309 | p5 Prime = 5 |
310 | p7 Prime = 7 |
311 | p77 Prime = 7 // Duplicate; note that p77 doesn't appear below. |
312 | p11 Prime = 11 |
313 | p13 Prime = 13 |
314 | p17 Prime = 17 |
315 | p19 Prime = 19 |
316 | p23 Prime = 23 |
317 | p29 Prime = 29 |
318 | p37 Prime = 31 |
319 | p41 Prime = 41 |
320 | p43 Prime = 43 |
321 | ) |
322 | ` |
323 | |
324 | const prime_out = `func _() { |
325 | // An "invalid array index" compiler error signifies that the constant values have changed. |
326 | // Re-run the stringer command to generate them again. |
327 | var x [1]struct{} |
328 | _ = x[p2-2] |
329 | _ = x[p3-3] |
330 | _ = x[p5-5] |
331 | _ = x[p7-7] |
332 | _ = x[p77-7] |
333 | _ = x[p11-11] |
334 | _ = x[p13-13] |
335 | _ = x[p17-17] |
336 | _ = x[p19-19] |
337 | _ = x[p23-23] |
338 | _ = x[p29-29] |
339 | _ = x[p37-31] |
340 | _ = x[p41-41] |
341 | _ = x[p43-43] |
342 | } |
343 | |
344 | const _Prime_name = "p2p3p5p7p11p13p17p19p23p29p37p41p43" |
345 | |
346 | var _Prime_map = map[Prime]string{ |
347 | 2: _Prime_name[0:2], |
348 | 3: _Prime_name[2:4], |
349 | 5: _Prime_name[4:6], |
350 | 7: _Prime_name[6:8], |
351 | 11: _Prime_name[8:11], |
352 | 13: _Prime_name[11:14], |
353 | 17: _Prime_name[14:17], |
354 | 19: _Prime_name[17:20], |
355 | 23: _Prime_name[20:23], |
356 | 29: _Prime_name[23:26], |
357 | 31: _Prime_name[26:29], |
358 | 41: _Prime_name[29:32], |
359 | 43: _Prime_name[32:35], |
360 | } |
361 | |
362 | func (i Prime) String() string { |
363 | if str, ok := _Prime_map[i]; ok { |
364 | return str |
365 | } |
366 | return "Prime(" + strconv.FormatInt(int64(i), 10) + ")" |
367 | } |
368 | ` |
369 | |
370 | const prefix_in = `type Type int |
371 | const ( |
372 | TypeInt Type = iota |
373 | TypeString |
374 | TypeFloat |
375 | TypeRune |
376 | TypeByte |
377 | TypeStruct |
378 | TypeSlice |
379 | ) |
380 | ` |
381 | |
382 | const prefix_out = `func _() { |
383 | // An "invalid array index" compiler error signifies that the constant values have changed. |
384 | // Re-run the stringer command to generate them again. |
385 | var x [1]struct{} |
386 | _ = x[TypeInt-0] |
387 | _ = x[TypeString-1] |
388 | _ = x[TypeFloat-2] |
389 | _ = x[TypeRune-3] |
390 | _ = x[TypeByte-4] |
391 | _ = x[TypeStruct-5] |
392 | _ = x[TypeSlice-6] |
393 | } |
394 | |
395 | const _Type_name = "IntStringFloatRuneByteStructSlice" |
396 | |
397 | var _Type_index = [...]uint8{0, 3, 9, 14, 18, 22, 28, 33} |
398 | |
399 | func (i Type) String() string { |
400 | if i < 0 || i >= Type(len(_Type_index)-1) { |
401 | return "Type(" + strconv.FormatInt(int64(i), 10) + ")" |
402 | } |
403 | return _Type_name[_Type_index[i]:_Type_index[i+1]] |
404 | } |
405 | ` |
406 | |
407 | const tokens_in = `type Token int |
408 | const ( |
409 | And Token = iota // & |
410 | Or // | |
411 | Add // + |
412 | Sub // - |
413 | Ident |
414 | Period // . |
415 | |
416 | // not to be used |
417 | SingleBefore |
418 | // not to be used |
419 | BeforeAndInline // inline |
420 | InlineGeneral /* inline general */ |
421 | ) |
422 | ` |
423 | |
424 | const tokens_out = `func _() { |
425 | // An "invalid array index" compiler error signifies that the constant values have changed. |
426 | // Re-run the stringer command to generate them again. |
427 | var x [1]struct{} |
428 | _ = x[And-0] |
429 | _ = x[Or-1] |
430 | _ = x[Add-2] |
431 | _ = x[Sub-3] |
432 | _ = x[Ident-4] |
433 | _ = x[Period-5] |
434 | _ = x[SingleBefore-6] |
435 | _ = x[BeforeAndInline-7] |
436 | _ = x[InlineGeneral-8] |
437 | } |
438 | |
439 | const _Token_name = "&|+-Ident.SingleBeforeinlineinline general" |
440 | |
441 | var _Token_index = [...]uint8{0, 1, 2, 3, 4, 9, 10, 22, 28, 42} |
442 | |
443 | func (i Token) String() string { |
444 | if i < 0 || i >= Token(len(_Token_index)-1) { |
445 | return "Token(" + strconv.FormatInt(int64(i), 10) + ")" |
446 | } |
447 | return _Token_name[_Token_index[i]:_Token_index[i+1]] |
448 | } |
449 | ` |
450 | |
451 | func TestGolden(t *testing.T) { |
452 | testenv.NeedsTool(t, "go") |
453 | |
454 | dir, err := os.MkdirTemp("", "stringer") |
455 | if err != nil { |
456 | t.Error(err) |
457 | } |
458 | defer os.RemoveAll(dir) |
459 | |
460 | for _, test := range golden { |
461 | g := Generator{ |
462 | trimPrefix: test.trimPrefix, |
463 | lineComment: test.lineComment, |
464 | } |
465 | input := "package test\n" + test.input |
466 | file := test.name + ".go" |
467 | absFile := filepath.Join(dir, file) |
468 | err := os.WriteFile(absFile, []byte(input), 0644) |
469 | if err != nil { |
470 | t.Error(err) |
471 | } |
472 | |
473 | g.parsePackage([]string{absFile}, nil) |
474 | // Extract the name and type of the constant from the first line. |
475 | tokens := strings.SplitN(test.input, " ", 3) |
476 | if len(tokens) != 3 { |
477 | t.Fatalf("%s: need type declaration on first line", test.name) |
478 | } |
479 | g.generate(tokens[1]) |
480 | got := string(g.format()) |
481 | if got != test.output { |
482 | t.Errorf("%s: got(%d)\n====\n%q====\nexpected(%d)\n====%q", test.name, len(got), got, len(test.output), test.output) |
483 | } |
484 | } |
485 | } |
486 |
Members