github.com/cockroachdb/tools@v0.0.0-20230222021103-a6d27438930d/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  	"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 := t.TempDir()
   455  	for _, test := range golden {
   456  		g := Generator{
   457  			trimPrefix:  test.trimPrefix,
   458  			lineComment: test.lineComment,
   459  		}
   460  		input := "package test\n" + test.input
   461  		file := test.name + ".go"
   462  		absFile := filepath.Join(dir, file)
   463  		err := os.WriteFile(absFile, []byte(input), 0644)
   464  		if err != nil {
   465  			t.Error(err)
   466  		}
   467  
   468  		g.parsePackage([]string{absFile}, nil)
   469  		// Extract the name and type of the constant from the first line.
   470  		tokens := strings.SplitN(test.input, " ", 3)
   471  		if len(tokens) != 3 {
   472  			t.Fatalf("%s: need type declaration on first line", test.name)
   473  		}
   474  		g.generate(tokens[1])
   475  		got := string(g.format())
   476  		if got != test.output {
   477  			t.Errorf("%s: got(%d)\n====\n%q====\nexpected(%d)\n====%q", test.name, len(got), got, len(test.output), test.output)
   478  		}
   479  	}
   480  }