| // Copyright 2014 The Go Authors. All rights reserved. |
| // Use of this source code is governed by a BSD-style |
| // license that can be found in the LICENSE file. |
| |
| // This file contains simple golden tests for various examples. |
| // Besides validating the results when the implementation changes, |
| // it provides a way to look at the generated code without having |
| // to execute the print statements in one's head. |
| |
| package main |
| |
| import ( |
| "os" |
| "path/filepath" |
| "strings" |
| "testing" |
| |
| "golang.org/x/tools/internal/testenv" |
| ) |
| |
| // Golden represents a test case. |
| type Golden struct { |
| name string |
| trimPrefix string |
| lineComment bool |
| input string // input; the package clause is provided when running the test. |
| output string // expected output. |
| } |
| |
| var golden = []Golden{ |
| {"day", "", false, day_in, day_out}, |
| {"offset", "", false, offset_in, offset_out}, |
| {"gap", "", false, gap_in, gap_out}, |
| {"num", "", false, num_in, num_out}, |
| {"unum", "", false, unum_in, unum_out}, |
| {"unumpos", "", false, unumpos_in, unumpos_out}, |
| {"prime", "", false, prime_in, prime_out}, |
| {"prefix", "Type", false, prefix_in, prefix_out}, |
| {"tokens", "", true, tokens_in, tokens_out}, |
| } |
| |
| // Each example starts with "type XXX [u]int", with a single space separating them. |
| |
| // Simple test: enumeration of type int starting at 0. |
| const day_in = `type Day int |
| const ( |
| Monday Day = iota |
| Tuesday |
| Wednesday |
| Thursday |
| Friday |
| Saturday |
| Sunday |
| ) |
| ` |
| |
| const day_out = `func _() { |
| // An "invalid array index" compiler error signifies that the constant values have changed. |
| // Re-run the stringer command to generate them again. |
| var x [1]struct{} |
| _ = x[Monday-0] |
| _ = x[Tuesday-1] |
| _ = x[Wednesday-2] |
| _ = x[Thursday-3] |
| _ = x[Friday-4] |
| _ = x[Saturday-5] |
| _ = x[Sunday-6] |
| } |
| |
| const _Day_name = "MondayTuesdayWednesdayThursdayFridaySaturdaySunday" |
| |
| var _Day_index = [...]uint8{0, 6, 13, 22, 30, 36, 44, 50} |
| |
| func (i Day) String() string { |
| if i < 0 || i >= Day(len(_Day_index)-1) { |
| return "Day(" + strconv.FormatInt(int64(i), 10) + ")" |
| } |
| return _Day_name[_Day_index[i]:_Day_index[i+1]] |
| } |
| ` |
| |
| // Enumeration with an offset. |
| // Also includes a duplicate. |
| const offset_in = `type Number int |
| const ( |
| _ Number = iota |
| One |
| Two |
| Three |
| AnotherOne = One // Duplicate; note that AnotherOne doesn't appear below. |
| ) |
| ` |
| |
| const offset_out = `func _() { |
| // An "invalid array index" compiler error signifies that the constant values have changed. |
| // Re-run the stringer command to generate them again. |
| var x [1]struct{} |
| _ = x[One-1] |
| _ = x[Two-2] |
| _ = x[Three-3] |
| } |
| |
| const _Number_name = "OneTwoThree" |
| |
| var _Number_index = [...]uint8{0, 3, 6, 11} |
| |
| func (i Number) String() string { |
| i -= 1 |
| if i < 0 || i >= Number(len(_Number_index)-1) { |
| return "Number(" + strconv.FormatInt(int64(i+1), 10) + ")" |
| } |
| return _Number_name[_Number_index[i]:_Number_index[i+1]] |
| } |
| ` |
| |
| // Gaps and an offset. |
| const gap_in = `type Gap int |
| const ( |
| Two Gap = 2 |
| Three Gap = 3 |
| Five Gap = 5 |
| Six Gap = 6 |
| Seven Gap = 7 |
| Eight Gap = 8 |
| Nine Gap = 9 |
| Eleven Gap = 11 |
| ) |
| ` |
| |
| const gap_out = `func _() { |
| // An "invalid array index" compiler error signifies that the constant values have changed. |
| // Re-run the stringer command to generate them again. |
| var x [1]struct{} |
| _ = x[Two-2] |
| _ = x[Three-3] |
| _ = x[Five-5] |
| _ = x[Six-6] |
| _ = x[Seven-7] |
| _ = x[Eight-8] |
| _ = x[Nine-9] |
| _ = x[Eleven-11] |
| } |
| |
| const ( |
| _Gap_name_0 = "TwoThree" |
| _Gap_name_1 = "FiveSixSevenEightNine" |
| _Gap_name_2 = "Eleven" |
| ) |
| |
| var ( |
| _Gap_index_0 = [...]uint8{0, 3, 8} |
| _Gap_index_1 = [...]uint8{0, 4, 7, 12, 17, 21} |
| ) |
| |
| func (i Gap) String() string { |
| switch { |
| case 2 <= i && i <= 3: |
| i -= 2 |
| return _Gap_name_0[_Gap_index_0[i]:_Gap_index_0[i+1]] |
| case 5 <= i && i <= 9: |
| i -= 5 |
| return _Gap_name_1[_Gap_index_1[i]:_Gap_index_1[i+1]] |
| case i == 11: |
| return _Gap_name_2 |
| default: |
| return "Gap(" + strconv.FormatInt(int64(i), 10) + ")" |
| } |
| } |
| ` |
| |
| // Signed integers spanning zero. |
| const num_in = `type Num int |
| const ( |
| m_2 Num = -2 + iota |
| m_1 |
| m0 |
| m1 |
| m2 |
| ) |
| ` |
| |
| const num_out = `func _() { |
| // An "invalid array index" compiler error signifies that the constant values have changed. |
| // Re-run the stringer command to generate them again. |
| var x [1]struct{} |
| _ = x[m_2 - -2] |
| _ = x[m_1 - -1] |
| _ = x[m0-0] |
| _ = x[m1-1] |
| _ = x[m2-2] |
| } |
| |
| const _Num_name = "m_2m_1m0m1m2" |
| |
| var _Num_index = [...]uint8{0, 3, 6, 8, 10, 12} |
| |
| func (i Num) String() string { |
| i -= -2 |
| if i < 0 || i >= Num(len(_Num_index)-1) { |
| return "Num(" + strconv.FormatInt(int64(i+-2), 10) + ")" |
| } |
| return _Num_name[_Num_index[i]:_Num_index[i+1]] |
| } |
| ` |
| |
| // Unsigned integers spanning zero. |
| const unum_in = `type Unum uint |
| const ( |
| m_2 Unum = iota + 253 |
| m_1 |
| ) |
| |
| const ( |
| m0 Unum = iota |
| m1 |
| m2 |
| ) |
| ` |
| |
| const unum_out = `func _() { |
| // An "invalid array index" compiler error signifies that the constant values have changed. |
| // Re-run the stringer command to generate them again. |
| var x [1]struct{} |
| _ = x[m_2-253] |
| _ = x[m_1-254] |
| _ = x[m0-0] |
| _ = x[m1-1] |
| _ = x[m2-2] |
| } |
| |
| const ( |
| _Unum_name_0 = "m0m1m2" |
| _Unum_name_1 = "m_2m_1" |
| ) |
| |
| var ( |
| _Unum_index_0 = [...]uint8{0, 2, 4, 6} |
| _Unum_index_1 = [...]uint8{0, 3, 6} |
| ) |
| |
| func (i Unum) String() string { |
| switch { |
| case i <= 2: |
| return _Unum_name_0[_Unum_index_0[i]:_Unum_index_0[i+1]] |
| case 253 <= i && i <= 254: |
| i -= 253 |
| return _Unum_name_1[_Unum_index_1[i]:_Unum_index_1[i+1]] |
| default: |
| return "Unum(" + strconv.FormatInt(int64(i), 10) + ")" |
| } |
| } |
| ` |
| |
| // Unsigned positive integers. |
| const unumpos_in = `type Unumpos uint |
| const ( |
| m253 Unumpos = iota + 253 |
| m254 |
| ) |
| |
| const ( |
| m1 Unumpos = iota + 1 |
| m2 |
| m3 |
| ) |
| ` |
| |
| const unumpos_out = `func _() { |
| // An "invalid array index" compiler error signifies that the constant values have changed. |
| // Re-run the stringer command to generate them again. |
| var x [1]struct{} |
| _ = x[m253-253] |
| _ = x[m254-254] |
| _ = x[m1-1] |
| _ = x[m2-2] |
| _ = x[m3-3] |
| } |
| |
| const ( |
| _Unumpos_name_0 = "m1m2m3" |
| _Unumpos_name_1 = "m253m254" |
| ) |
| |
| var ( |
| _Unumpos_index_0 = [...]uint8{0, 2, 4, 6} |
| _Unumpos_index_1 = [...]uint8{0, 4, 8} |
| ) |
| |
| func (i Unumpos) String() string { |
| switch { |
| case 1 <= i && i <= 3: |
| i -= 1 |
| return _Unumpos_name_0[_Unumpos_index_0[i]:_Unumpos_index_0[i+1]] |
| case 253 <= i && i <= 254: |
| i -= 253 |
| return _Unumpos_name_1[_Unumpos_index_1[i]:_Unumpos_index_1[i+1]] |
| default: |
| return "Unumpos(" + strconv.FormatInt(int64(i), 10) + ")" |
| } |
| } |
| ` |
| |
| // Enough gaps to trigger a map implementation of the method. |
| // Also includes a duplicate to test that it doesn't cause problems |
| const prime_in = `type Prime int |
| const ( |
| p2 Prime = 2 |
| p3 Prime = 3 |
| p5 Prime = 5 |
| p7 Prime = 7 |
| p77 Prime = 7 // Duplicate; note that p77 doesn't appear below. |
| p11 Prime = 11 |
| p13 Prime = 13 |
| p17 Prime = 17 |
| p19 Prime = 19 |
| p23 Prime = 23 |
| p29 Prime = 29 |
| p37 Prime = 31 |
| p41 Prime = 41 |
| p43 Prime = 43 |
| ) |
| ` |
| |
| const prime_out = `func _() { |
| // An "invalid array index" compiler error signifies that the constant values have changed. |
| // Re-run the stringer command to generate them again. |
| var x [1]struct{} |
| _ = x[p2-2] |
| _ = x[p3-3] |
| _ = x[p5-5] |
| _ = x[p7-7] |
| _ = x[p77-7] |
| _ = x[p11-11] |
| _ = x[p13-13] |
| _ = x[p17-17] |
| _ = x[p19-19] |
| _ = x[p23-23] |
| _ = x[p29-29] |
| _ = x[p37-31] |
| _ = x[p41-41] |
| _ = x[p43-43] |
| } |
| |
| const _Prime_name = "p2p3p5p7p11p13p17p19p23p29p37p41p43" |
| |
| var _Prime_map = map[Prime]string{ |
| 2: _Prime_name[0:2], |
| 3: _Prime_name[2:4], |
| 5: _Prime_name[4:6], |
| 7: _Prime_name[6:8], |
| 11: _Prime_name[8:11], |
| 13: _Prime_name[11:14], |
| 17: _Prime_name[14:17], |
| 19: _Prime_name[17:20], |
| 23: _Prime_name[20:23], |
| 29: _Prime_name[23:26], |
| 31: _Prime_name[26:29], |
| 41: _Prime_name[29:32], |
| 43: _Prime_name[32:35], |
| } |
| |
| func (i Prime) String() string { |
| if str, ok := _Prime_map[i]; ok { |
| return str |
| } |
| return "Prime(" + strconv.FormatInt(int64(i), 10) + ")" |
| } |
| ` |
| |
| const prefix_in = `type Type int |
| const ( |
| TypeInt Type = iota |
| TypeString |
| TypeFloat |
| TypeRune |
| TypeByte |
| TypeStruct |
| TypeSlice |
| ) |
| ` |
| |
| const prefix_out = `func _() { |
| // An "invalid array index" compiler error signifies that the constant values have changed. |
| // Re-run the stringer command to generate them again. |
| var x [1]struct{} |
| _ = x[TypeInt-0] |
| _ = x[TypeString-1] |
| _ = x[TypeFloat-2] |
| _ = x[TypeRune-3] |
| _ = x[TypeByte-4] |
| _ = x[TypeStruct-5] |
| _ = x[TypeSlice-6] |
| } |
| |
| const _Type_name = "IntStringFloatRuneByteStructSlice" |
| |
| var _Type_index = [...]uint8{0, 3, 9, 14, 18, 22, 28, 33} |
| |
| func (i Type) String() string { |
| if i < 0 || i >= Type(len(_Type_index)-1) { |
| return "Type(" + strconv.FormatInt(int64(i), 10) + ")" |
| } |
| return _Type_name[_Type_index[i]:_Type_index[i+1]] |
| } |
| ` |
| |
| const tokens_in = `type Token int |
| const ( |
| And Token = iota // & |
| Or // | |
| Add // + |
| Sub // - |
| Ident |
| Period // . |
| |
| // not to be used |
| SingleBefore |
| // not to be used |
| BeforeAndInline // inline |
| InlineGeneral /* inline general */ |
| ) |
| ` |
| |
| const tokens_out = `func _() { |
| // An "invalid array index" compiler error signifies that the constant values have changed. |
| // Re-run the stringer command to generate them again. |
| var x [1]struct{} |
| _ = x[And-0] |
| _ = x[Or-1] |
| _ = x[Add-2] |
| _ = x[Sub-3] |
| _ = x[Ident-4] |
| _ = x[Period-5] |
| _ = x[SingleBefore-6] |
| _ = x[BeforeAndInline-7] |
| _ = x[InlineGeneral-8] |
| } |
| |
| const _Token_name = "&|+-Ident.SingleBeforeinlineinline general" |
| |
| var _Token_index = [...]uint8{0, 1, 2, 3, 4, 9, 10, 22, 28, 42} |
| |
| func (i Token) String() string { |
| if i < 0 || i >= Token(len(_Token_index)-1) { |
| return "Token(" + strconv.FormatInt(int64(i), 10) + ")" |
| } |
| return _Token_name[_Token_index[i]:_Token_index[i+1]] |
| } |
| ` |
| |
| func TestGolden(t *testing.T) { |
| testenv.NeedsTool(t, "go") |
| |
| dir, err := os.MkdirTemp("", "stringer") |
| if err != nil { |
| t.Error(err) |
| } |
| defer os.RemoveAll(dir) |
| |
| for _, test := range golden { |
| g := Generator{ |
| trimPrefix: test.trimPrefix, |
| lineComment: test.lineComment, |
| } |
| input := "package test\n" + test.input |
| file := test.name + ".go" |
| absFile := filepath.Join(dir, file) |
| err := os.WriteFile(absFile, []byte(input), 0644) |
| if err != nil { |
| t.Error(err) |
| } |
| |
| g.parsePackage([]string{absFile}, nil) |
| // Extract the name and type of the constant from the first line. |
| tokens := strings.SplitN(test.input, " ", 3) |
| if len(tokens) != 3 { |
| t.Fatalf("%s: need type declaration on first line", test.name) |
| } |
| g.generate(tokens[1]) |
| got := string(g.format()) |
| if got != test.output { |
| t.Errorf("%s: got(%d)\n====\n%q====\nexpected(%d)\n====%q", test.name, len(got), got, len(test.output), test.output) |
| } |
| } |
| } |