diff --git a/cmd/stringer/endtoend_test.go b/cmd/stringer/endtoend_test.go index f8530a6cbdc..6b349931ad4 100644 --- a/cmd/stringer/endtoend_test.go +++ b/cmd/stringer/endtoend_test.go @@ -130,7 +130,7 @@ func stringerCompileAndRun(t *testing.T, dir, stringer, typeName, fileName strin } stringSource := filepath.Join(dir, typeName+"_string.go") // Run stringer in temporary directory. - err = run(stringer, "-type", typeName, "-output", stringSource, source) + err = run(stringer, "-type", typeName, "-output", stringSource, "-withfromstring", source) if err != nil { t.Fatal(err) } diff --git a/cmd/stringer/golden_test.go b/cmd/stringer/golden_test.go index 4ba77881cea..974cd5b2d7c 100644 --- a/cmd/stringer/golden_test.go +++ b/cmd/stringer/golden_test.go @@ -16,22 +16,27 @@ import ( // 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 // exected output. + name string + trimPrefix string + lineComment bool + withFromString bool + input string // input; the package clause is provided when running the test. + output string // exected 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}, - {"prime", "", false, prime_in, prime_out}, - {"prefix", "Type", false, prefix_in, prefix_out}, - {"tokens", "", true, tokens_in, tokens_out}, + {"day", "", false, false, day_in, day_out}, + {"offset", "", false, false, offset_in, offset_out}, + {"gap", "", false, false, gap_in, gap_out}, + {"num", "", false, false, num_in, num_out}, + {"unum", "", false, false, unum_in, unum_out}, + {"prime", "", false, false, prime_in, prime_out}, + {"prefix", "Type", false, false, prefix_in, prefix_out}, + {"tokens", "", true, false, tokens_in, tokens_out}, + {"month", "", true, true, month_in, month_out}, + {"mood", "", true, true, mood_in, mood_out}, + {"fib", "", false, true, fib_in, fib_out}, + {"hundreds", "", false, true, hundreds_in, hundreds_out}, } // Each example starts with "type XXX [u]int", with a single space separating them. @@ -50,6 +55,8 @@ const ( ` const day_out = ` +import "strconv" + const _Day_name = "MondayTuesdayWednesdayThursdayFridaySaturdaySunday" var _Day_index = [...]uint8{0, 6, 13, 22, 30, 36, 44, 50} @@ -75,6 +82,8 @@ const ( ` const offset_out = ` +import "strconv" + const _Number_name = "OneTwoThree" var _Number_index = [...]uint8{0, 3, 6, 11} @@ -103,6 +112,8 @@ const ( ` const gap_out = ` +import "strconv" + const ( _Gap_name_0 = "TwoThree" _Gap_name_1 = "FiveSixSevenEightNine" @@ -142,6 +153,8 @@ const ( ` const num_out = ` +import "strconv" + const _Num_name = "m_2m_1m0m1m2" var _Num_index = [...]uint8{0, 3, 6, 8, 10, 12} @@ -170,6 +183,8 @@ const ( ` const unum_out = ` +import "strconv" + const ( _Unum_name_0 = "m0m1m2" _Unum_name_1 = "m_2m_1" @@ -215,25 +230,37 @@ const ( ` const prime_out = ` +import ( + "strconv" + "sync" +) + 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], +var _Prime_map map[Prime]string + +var populatePrimeMapOnce sync.Once + +func populatePrimeMap() { + _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 { + populatePrimeMapOnce.Do(populatePrimeMap) if str, ok := _Prime_map[i]; ok { return str } @@ -254,6 +281,8 @@ const ( ` const prefix_out = ` +import "strconv" + const _Type_name = "IntStringFloatRuneByteStructSlice" var _Type_index = [...]uint8{0, 3, 9, 14, 18, 22, 28, 33} @@ -284,6 +313,8 @@ const ( ` const tokens_out = ` +import "strconv" + const _Token_name = "&|+-Ident.SingleBeforeinlineinline general" var _Token_index = [...]uint8{0, 1, 2, 3, 4, 9, 10, 22, 28, 42} @@ -296,11 +327,222 @@ func (i Token) String() string { } ` +// Simple test: enumeration of type int starting at 0. +const month_in = `type Month int +const ( + January Month = iota + February + March + April + May + June + July + August + September + October + November + December +) +` + +const month_out = ` +import "strconv" + +const _Month_name = "JanuaryFebruaryMarchAprilMayJuneJulyAugustSeptemberOctoberNovemberDecember" + +var _Month_index = [...]uint8{0, 7, 15, 20, 25, 28, 32, 36, 42, 51, 58, 66, 74} + +func (i Month) String() string { + if i < 0 || i >= Month(len(_Month_index)-1) { + return "Month(" + strconv.FormatInt(int64(i), 10) + ")" + } + return _Month_name[_Month_index[i]:_Month_index[i+1]] +} + +func MonthFromString(s string) (i Month, ok bool) { + for v := range _Month_index { + v += 0 + if s == Month(v).String() { + return Month(v), true + } + } + return +} +` + +// Enumeration with an negative offset. +const mood_in = `type Mood int +const ( + Negative Mood = iota -1 + Neutral + Happy +) +` + +const mood_out = ` +import "strconv" + +const _Mood_name = "NegativeNeutralHappy" + +var _Mood_index = [...]uint8{0, 8, 15, 20} + +func (i Mood) String() string { + i -= -1 + if i < 0 || i >= Mood(len(_Mood_index)-1) { + return "Mood(" + strconv.FormatInt(int64(i+-1), 10) + ")" + } + return _Mood_name[_Mood_index[i]:_Mood_index[i+1]] +} + +func MoodFromString(s string) (i Mood, ok bool) { + for v := range _Mood_index { + v += -1 + if s == Mood(v).String() { + return Mood(v), true + } + } + return +} +` + +// Gaps and an offset with duplicates. +const fib_in = `type Fib int +const ( + Zero Fib = 0 + One Fib = 1 + AnotherOne Fib = 1 + Two Fib = 2 + Three Fib = 3 + Five Fib = 5 + Eight Fib = 8 + Thirteen Fib = 13 +) +` + +const fib_out = ` +import "strconv" + +const ( + _Fib_name_0 = "ZeroOneTwoThree" + _Fib_name_1 = "Five" + _Fib_name_2 = "Eight" + _Fib_name_3 = "Thirteen" +) + +var ( + _Fib_index_0 = [...]uint8{0, 4, 7, 10, 15} +) + +func (i Fib) String() string { + switch { + case 0 <= i && i <= 3: + return _Fib_name_0[_Fib_index_0[i]:_Fib_index_0[i+1]] + case i == 5: + return _Fib_name_1 + case i == 8: + return _Fib_name_2 + case i == 13: + return _Fib_name_3 + default: + return "Fib(" + strconv.FormatInt(int64(i), 10) + ")" + } +} + +func FibFromString(s string) (i Fib, ok bool) { + switch s { + case "Zero": + return Fib(0), true + case "One": + return Fib(1), true + case "Two": + return Fib(2), true + case "Three": + return Fib(3), true + case "Five": + return Fib(5), true + case "Eight": + return Fib(8), true + case "Thirteen": + return Fib(13), true + } + return +} +` + +// Enough gaps to trigger a map implementation of the method. +// Also includes a duplicate to test that it doesn't cause problems +const hundreds_in = `type Hundred int +const ( + One Hundred = 100 + Two Hundred = 200 + Three Hundred = 300 + Four Hundred = 400 + Five Hundred = 500 + HighFive Hundred = 500 // duplicate + Six Hundred = 600 + Seven Hundred = 700 + Eight Hundred = 800 + Nine Hundred = 900 + Ten Hundred = 1000 + Eleven Hundred = 1100 + Twelve Hundred = 1200 +) +` + +const hundreds_out = ` +import ( + "strconv" + "sync" +) + +const _Hundred_name = "OneTwoThreeFourFiveSixSevenEightNineTenElevenTwelve" + +var _Hundred_map map[Hundred]string + +var populateHundredMapOnce sync.Once + +func populateHundredMap() { + _Hundred_map = map[Hundred]string{ + 100: _Hundred_name[0:3], + 200: _Hundred_name[3:6], + 300: _Hundred_name[6:11], + 400: _Hundred_name[11:15], + 500: _Hundred_name[15:19], + 600: _Hundred_name[19:22], + 700: _Hundred_name[22:27], + 800: _Hundred_name[27:32], + 900: _Hundred_name[32:36], + 1000: _Hundred_name[36:39], + 1100: _Hundred_name[39:45], + 1200: _Hundred_name[45:51], + } +} + +func (i Hundred) String() string { + populateHundredMapOnce.Do(populateHundredMap) + if str, ok := _Hundred_map[i]; ok { + return str + } + return "Hundred(" + strconv.FormatInt(int64(i), 10) + ")" +} + +func HundredFromString(s string) (i Hundred, ok bool) { + populateHundredMapOnce.Do(populateHundredMap) + for k, v := range _Hundred_map { + if s == v { + return k, true + } + } + return +} +` + func TestGolden(t *testing.T) { for _, test := range golden { g := Generator{ - trimPrefix: test.trimPrefix, - lineComment: test.lineComment, + trimPrefix: test.trimPrefix, + lineComment: test.lineComment, + withFromString: test.withFromString, } input := "package test\n" + test.input file := test.name + ".go" diff --git a/cmd/stringer/stringer.go b/cmd/stringer/stringer.go index 5c3ad3547e6..cff8b315971 100644 --- a/cmd/stringer/stringer.go +++ b/cmd/stringer/stringer.go @@ -78,11 +78,12 @@ import ( ) var ( - typeNames = flag.String("type", "", "comma-separated list of type names; must be set") - output = flag.String("output", "", "output file name; default srcdir/_string.go") - trimprefix = flag.String("trimprefix", "", "trim the `prefix` from the generated constant names") - linecomment = flag.Bool("linecomment", false, "use line comment text as printed text when present") - buildTags = flag.String("tags", "", "comma-separated list of build tags to apply") + typeNames = flag.String("type", "", "comma-separated list of type names; must be set") + output = flag.String("output", "", "output file name; default srcdir/_string.go") + trimprefix = flag.String("trimprefix", "", "trim the `prefix` from the generated constant names") + linecomment = flag.Bool("linecomment", false, "use line comment text as printed text when present") + withfromstring = flag.Bool("withfromstring", false, "generate FromString method for type") + buildTags = flag.String("tags", "", "comma-separated list of build tags to apply") ) // Usage is a replacement usage function for the flags package. @@ -121,8 +122,9 @@ func main() { // Parse the package once. var dir string g := Generator{ - trimPrefix: *trimprefix, - lineComment: *linecomment, + trimPrefix: *trimprefix, + lineComment: *linecomment, + withFromString: *withfromstring, } if len(args) == 1 && isDirectory(args[0]) { dir = args[0] @@ -139,8 +141,6 @@ func main() { g.Printf("// Code generated by \"stringer %s\"; DO NOT EDIT.\n", strings.Join(os.Args[1:], " ")) g.Printf("\n") g.Printf("package %s", g.pkg.name) - g.Printf("\n") - g.Printf("import \"strconv\"\n") // Used by all methods. // Run generate for each type. for _, typeName := range types { @@ -177,8 +177,9 @@ type Generator struct { buf bytes.Buffer // Accumulated output. pkg *Package // Package we are scanning. - trimPrefix string - lineComment bool + trimPrefix string + lineComment bool + withFromString bool } func (g *Generator) Printf(format string, args ...interface{}) { @@ -193,8 +194,9 @@ type File struct { typeName string // Name of the constant type. values []Value // Accumulator for constant values of that type. - trimPrefix string - lineComment bool + trimPrefix string + lineComment bool + withFromString bool } type Package struct { @@ -263,10 +265,11 @@ func (g *Generator) parsePackage(directory string, names []string, text interfac } astFiles = append(astFiles, parsedFile) files = append(files, &File{ - file: parsedFile, - pkg: g.pkg, - trimPrefix: g.trimPrefix, - lineComment: g.lineComment, + file: parsedFile, + pkg: g.pkg, + trimPrefix: g.trimPrefix, + lineComment: g.lineComment, + withFromString: g.withFromString, }) } if len(astFiles) == 0 { @@ -312,6 +315,7 @@ func (g *Generator) generate(typeName string) { if len(values) == 0 { log.Fatalf("no values defined for type %s", typeName) } + runs := splitIntoRuns(values) // The decision of which pattern to use depends on the number of // runs in the numbers. If there's only one, it's easy. For more than @@ -577,8 +581,10 @@ func (g *Generator) declareNameVars(runs [][]Value, typeName string, suffix stri // buildOneRun generates the variables and String method for a single run of contiguous values. func (g *Generator) buildOneRun(runs [][]Value, typeName string) { values := runs[0] + g.Printf("\nimport \"strconv\"\n") g.Printf("\n") g.declareIndexAndNameVar(values, typeName) + // The generated code is simple enough to write as a Printf format. lessThanZero := "" if values[0].signed { @@ -589,6 +595,11 @@ func (g *Generator) buildOneRun(runs [][]Value, typeName string) { } else { g.Printf(stringOneRunWithOffset, typeName, values[0].String(), usize(len(values)), lessThanZero) } + + if g.withFromString { + g.Printf("\n") + g.Printf(fromStringOneRun, typeName, values[0].String(), usize(len(values)), lessThanZero) + } } // Arguments to format are: @@ -608,8 +619,6 @@ const stringOneRun = `func (i %[1]s) String() string { // [2]: lowest defined value for type, as a string // [3]: size of index element (8 for uint8 etc.) // [4]: less than zero check (for signed types) -/* - */ const stringOneRunWithOffset = `func (i %[1]s) String() string { i -= %[2]s if %[4]si >= %[1]s(len(_%[1]s_index)-1) { @@ -619,9 +628,26 @@ const stringOneRunWithOffset = `func (i %[1]s) String() string { } ` +// Arguments to format are: +// [1]: type name +// [2]: lowest defined value for type +// [3]: size of index element (8 for uint8 etc.) +// [4]: less than zero check (for signed types) +const fromStringOneRun = `func %[1]sFromString(s string) (i %[1]s, ok bool) { + for v := range _%[1]s_index { + v += %[2]s + if s == %[1]s(v).String() { + return %[1]s(v), true + } + } + return +} +` + // buildMultipleRuns generates the variables and String method for multiple runs of contiguous values. // For this pattern, a single Printf format won't do. func (g *Generator) buildMultipleRuns(runs [][]Value, typeName string) { + g.Printf("\nimport \"strconv\"\n") g.Printf("\n") g.declareIndexAndNameVars(runs, typeName) g.Printf("func (i %s) String() string {\n", typeName) @@ -643,30 +669,74 @@ func (g *Generator) buildMultipleRuns(runs [][]Value, typeName string) { g.Printf("\t\treturn \"%s(\" + strconv.FormatInt(int64(i), 10) + \")\"\n", typeName) g.Printf("\t}\n") g.Printf("}\n") + + if !g.withFromString { + return + } + + g.Printf("\n") + g.Printf("func %[1]sFromString(s string) (i %[1]s, ok bool) {\n", typeName) + g.Printf("\tswitch s {\n") + for _, values := range runs { + for _, value := range values { + g.Printf("\tcase \"%s\":\n", value.name) + g.Printf("\t\treturn %s(%d), true\n", typeName, value.value) + } + } + g.Printf("\t}\n") + g.Printf("\treturn\n") + g.Printf("}\n") } // buildMap handles the case where the space is so sparse a map is a reasonable fallback. // It's a rare situation but has simple code. func (g *Generator) buildMap(runs [][]Value, typeName string) { - g.Printf("\n") + g.Printf("\nimport (\n") + g.Printf("\t\"strconv\"\n") + g.Printf("\t\"sync\"\n") + g.Printf(")\n\n") g.declareNameVars(runs, typeName, "") - g.Printf("\nvar _%s_map = map[%s]string{\n", typeName, typeName) + g.Printf("\nvar _%[1]s_map map[%[1]s]string\n", typeName) + g.Printf("\nvar populate%[1]sMapOnce sync.Once\n", typeName) + + g.Printf("\nfunc populate%[1]sMap() {\n", typeName) + g.Printf("\t_%[1]s_map = map[%[1]s]string{\n", typeName) n := 0 for _, values := range runs { for _, value := range values { - g.Printf("\t%s: _%s_name[%d:%d],\n", &value, typeName, n, n+len(value.name)) + g.Printf("\t\t%s: _%s_name[%d:%d],\n", &value, typeName, n, n+len(value.name)) n += len(value.name) } } + g.Printf("\t}\n") g.Printf("}\n\n") + g.Printf(stringMap, typeName) + + if g.withFromString { + g.Printf("\n") + g.Printf(fromStringMap, typeName) + } } // Argument to format is the type name. const stringMap = `func (i %[1]s) String() string { + populate%[1]sMapOnce.Do(populate%[1]sMap) if str, ok := _%[1]s_map[i]; ok { return str } return "%[1]s(" + strconv.FormatInt(int64(i), 10) + ")" } ` + +// Argument to format is the type name. +const fromStringMap = `func %[1]sFromString(s string) (i %[1]s, ok bool) { + populate%[1]sMapOnce.Do(populate%[1]sMap) + for k, v := range _%[1]s_map { + if s == v { + return k, true + } + } + return +} +` diff --git a/cmd/stringer/testdata/day.go b/cmd/stringer/testdata/day.go index 35fa8dce8ad..c0a6c013879 100644 --- a/cmd/stringer/testdata/day.go +++ b/cmd/stringer/testdata/day.go @@ -30,6 +30,16 @@ func main() { ck(Sunday, "Sunday") ck(-127, "Day(-127)") ck(127, "Day(127)") + + fstr("Monday", Monday, true) + fstr("Tuesday", Tuesday, true) + fstr("Wednesday", Wednesday, true) + fstr("Thursday", Thursday, true) + fstr("Friday", Friday, true) + fstr("Saturday", Saturday, true) + fstr("Sunday", Sunday, true) + fstr("Day(-127)", 0, false) + fstr("Day(127)", 0, false) } func ck(day Day, str string) { @@ -37,3 +47,10 @@ func ck(day Day, str string) { panic("day.go: " + str) } } + +func fstr(str string, i Day, ok bool) { + res, found := DayFromString(str) + if res != i || ok != found { + panic("day.go: " + str) + } +} diff --git a/cmd/stringer/testdata/gap.go b/cmd/stringer/testdata/gap.go index bc8a90c5473..dde0be6d669 100644 --- a/cmd/stringer/testdata/gap.go +++ b/cmd/stringer/testdata/gap.go @@ -35,6 +35,20 @@ func main() { ck(10, "Gap(10)") ck(Eleven, "Eleven") ck(12, "Gap(12)") + + fstr("Zero", 0, false) + fstr("One", 0, false) + fstr("Two", Two, true) + fstr("Three", Three, true) + fstr("Four", 0, false) + fstr("Five", Five, true) + fstr("Six", Six, true) + fstr("Seven", Seven, true) + fstr("Eight", Eight, true) + fstr("Nine", Nine, true) + fstr("Ten", 0, false) + fstr("Eleven", Eleven, true) + fstr("Twelve", 0, false) } func ck(gap Gap, str string) { @@ -42,3 +56,10 @@ func ck(gap Gap, str string) { panic("gap.go: " + str) } } + +func fstr(str string, i Gap, ok bool) { + res, found := GapFromString(str) + if res != i || ok != found { + panic("gap.go: " + str) + } +} diff --git a/cmd/stringer/testdata/num.go b/cmd/stringer/testdata/num.go index 0d5ab107076..26209b99a4f 100644 --- a/cmd/stringer/testdata/num.go +++ b/cmd/stringer/testdata/num.go @@ -26,6 +26,14 @@ func main() { ck(m1, "m1") ck(m2, "m2") ck(3, "Num(3)") + + fstr("m_3", 0, false) + fstr("m_2", m_2, true) + fstr("m_1", m_1, true) + fstr("m0", m0, true) + fstr("m1", m1, true) + fstr("m2", m2, true) + fstr("m3", 0, false) } func ck(num Num, str string) { @@ -33,3 +41,10 @@ func ck(num Num, str string) { panic("num.go: " + str) } } + +func fstr(str string, i Num, ok bool) { + res, found := NumFromString(str) + if res != i || ok != found { + panic("num.go: " + str) + } +} diff --git a/cmd/stringer/testdata/number.go b/cmd/stringer/testdata/number.go index 7f1c8246c03..9714a6610d7 100644 --- a/cmd/stringer/testdata/number.go +++ b/cmd/stringer/testdata/number.go @@ -25,6 +25,12 @@ func main() { ck(Three, "Three") ck(AnotherOne, "One") ck(127, "Number(127)") + + fstr("One", One, true) + fstr("Two", Two, true) + fstr("Three", Three, true) + fstr("AnotherOne", 0, false) + fstr("127", 0, false) } func ck(num Number, str string) { @@ -32,3 +38,10 @@ func ck(num Number, str string) { panic("number.go: " + str) } } + +func fstr(str string, i Number, ok bool) { + res, found := NumberFromString(str) + if res != i || ok != found { + panic("number.go: " + str) + } +} diff --git a/cmd/stringer/testdata/prime.go b/cmd/stringer/testdata/prime.go index f551a1a0bbf..a8f728b289e 100644 --- a/cmd/stringer/testdata/prime.go +++ b/cmd/stringer/testdata/prime.go @@ -47,6 +47,25 @@ func main() { ck(p41, "p41") ck(p43, "p43") ck(44, "Prime(44)") + + fstr("p0", 0, false) + fstr("p1", 0, false) + fstr("p2", p2, true) + fstr("p3", p3, true) + fstr("p4", 0, false) + fstr("p5", p5, true) + fstr("p7", p7, true) + fstr("p77", 0, false) + fstr("p11", p11, true) + fstr("p13", p13, true) + fstr("p17", p17, true) + fstr("p19", p19, true) + fstr("p23", p23, true) + fstr("p29", p29, true) + fstr("p37", p37, true) + fstr("p41", p41, true) + fstr("p43", p43, true) + fstr("p44", 0, false) } func ck(prime Prime, str string) { @@ -54,3 +73,10 @@ func ck(prime Prime, str string) { panic("prime.go: " + str) } } + +func fstr(str string, i Prime, ok bool) { + res, found := PrimeFromString(str) + if res != i || ok != found { + panic("prime.go: " + str) + } +} diff --git a/cmd/stringer/testdata/unum.go b/cmd/stringer/testdata/unum.go index 2f8508f49cd..e24170451e2 100644 --- a/cmd/stringer/testdata/unum.go +++ b/cmd/stringer/testdata/unum.go @@ -29,6 +29,14 @@ func main() { ck(m1, "m1") ck(m2, "m2") ck(3, "Unum(3)") + + fstr("m_3", 0, false) + fstr("m_2", m_2, true) + fstr("m_1", m_1, true) + fstr("m0", m0, true) + fstr("m1", m1, true) + fstr("m2", m2, true) + fstr("m3", 0, false) } func ck(unum Unum, str string) { @@ -36,3 +44,10 @@ func ck(unum Unum, str string) { panic("unum.go: " + str) } } + +func fstr(str string, i Unum, ok bool) { + res, found := UnumFromString(str) + if res != i || ok != found { + panic("unum.go: " + str) + } +} diff --git a/cmd/stringer/testdata/unum2.go b/cmd/stringer/testdata/unum2.go index edbbedf33ad..5bba8f50ec2 100644 --- a/cmd/stringer/testdata/unum2.go +++ b/cmd/stringer/testdata/unum2.go @@ -22,6 +22,12 @@ func main() { ck(Two, "Two") ck(3, "Unum2(3)") ck(255, "Unum2(255)") + + fstr("Zero", Zero, true) + fstr("One", One, true) + fstr("Two", Two, true) + fstr("Three", 0, false) + fstr("Unum2(255)", 0, false) } func ck(unum Unum2, str string) { @@ -29,3 +35,10 @@ func ck(unum Unum2, str string) { panic("unum.go: " + str) } } + +func fstr(str string, i Unum2, ok bool) { + res, found := Unum2FromString(str) + if res != i || ok != found { + panic("unum2.go: " + str) + } +}