| 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