github.com/powerman/golang-tools@v0.1.11-0.20220410185822-5ad214d8d803/cmd/stringer/golden_test.go (about)

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