codeberg.org/gruf/go-format@v1.0.6/print_test.go (about) 1 package format_test 2 3 import ( 4 "errors" 5 "fmt" 6 "io" 7 "net/http" 8 "net/url" 9 "reflect" 10 "strconv" 11 "sync" 12 "testing" 13 "time" 14 15 "codeberg.org/gruf/go-format" 16 ) 17 18 // A is a test structure. 19 type A struct { 20 A string 21 B *string 22 } 23 24 // B is a test structure with unexported fields. 25 type B struct { 26 a string 27 b *string 28 } 29 30 // C is a test structure with fmt.Stringer implementation. 31 type C struct{} 32 33 func (c C) String() string { 34 return "c.String()" 35 } 36 37 // D is a test structure with format.Formattable implementation. 38 type D struct{} 39 40 func (d D) AppendFormat(b []byte) []byte { 41 return append(b, `d.AppendFormat()`...) 42 } 43 44 // PanicTest is a test structure with a fmt.Stringer implementation that panics. 45 type PanicTest int 46 47 func (t PanicTest) String() string { 48 panic(`oh no! a panic`) 49 } 50 51 // appendTests are just a list of arguments to 52 // format and append to buffer, these test results 53 // are not checked these are to ensure safety 54 var appendTests = []interface{}{} 55 56 // printfTests provide a list of format strings, with 57 // single argument and expected results. Intended to 58 // test that operands produce expected results. 59 var printfTests = []struct { 60 Fmt string 61 Arg interface{} 62 Out string 63 }{ 64 // default format 65 { 66 Fmt: `{}`, 67 Arg: nil, 68 Out: `nil`, 69 }, 70 { 71 Fmt: `{}`, 72 Arg: (*int)(nil), 73 Out: `nil`, 74 }, 75 { 76 Fmt: `{}`, 77 Arg: time.Second, 78 Out: time.Second.String(), 79 }, 80 { 81 Fmt: `{}`, 82 Arg: `hello`, 83 Out: `hello`, 84 }, 85 { 86 Fmt: `{}`, 87 Arg: `hello world`, 88 Out: `hello world`, 89 }, 90 { 91 Fmt: `{}`, 92 Arg: "hello\nworld\n", 93 Out: "hello\nworld\n", 94 }, 95 { 96 Fmt: `{}`, 97 Arg: errors.New("error!"), 98 Out: `error!`, 99 }, 100 { 101 Fmt: `{}`, 102 Arg: A{}, 103 Out: `{A="" B=nil}`, 104 }, 105 { 106 Fmt: `{}`, 107 Arg: B{}, 108 Out: `{a="" b=nil}`, 109 }, 110 { 111 Fmt: `{}`, 112 Arg: C{}, 113 Out: `c.String()`, 114 }, 115 { 116 Fmt: `{}`, 117 Arg: D{}, 118 Out: `d.AppendFormat()`, 119 }, 120 { 121 Fmt: `{}`, 122 Arg: PanicTest(0), 123 Out: `!{PANIC="oh no! a panic"}`, 124 }, 125 { 126 Fmt: `{}`, 127 Arg: int8(0), 128 Out: `0`, 129 }, 130 { 131 Fmt: `{}`, 132 Arg: int16(0), 133 Out: `0`, 134 }, 135 { 136 Fmt: `{}`, 137 Arg: int32(0), 138 Out: `0`, 139 }, 140 { 141 Fmt: `{}`, 142 Arg: int64(0), 143 Out: `0`, 144 }, 145 { 146 Fmt: `{}`, 147 Arg: uint8(0), // uint8=byte 148 Out: "\x00", // formatted as byte 149 }, 150 { 151 Fmt: `{}`, 152 Arg: uint16(0), 153 Out: `0`, 154 }, 155 { 156 Fmt: `{}`, 157 Arg: uint32(0), 158 Out: `0`, 159 }, 160 { 161 Fmt: `{}`, 162 Arg: uint64(0), 163 Out: `0`, 164 }, 165 { 166 Fmt: `{}`, 167 Arg: float32(0.0), 168 Out: `0`, 169 }, 170 { 171 Fmt: `{}`, 172 Arg: float64(0.0), 173 Out: `0`, 174 }, 175 { 176 Fmt: `{}`, 177 Arg: complex64(0), 178 Out: `0+0i`, 179 }, 180 { 181 Fmt: `{}`, 182 Arg: complex128(0), 183 Out: `0+0i`, 184 }, 185 { 186 Fmt: `{}`, 187 Arg: []byte(`hello world`), 188 Out: `[h,e,l,l,o, ,w,o,r,l,d]`, 189 }, 190 { 191 Fmt: `{}`, 192 Arg: byte('?'), 193 Out: `?`, 194 }, 195 { 196 Fmt: `{}`, 197 Arg: byte('\n'), 198 Out: "\n", 199 }, 200 201 // key format 202 { 203 Fmt: `{:k}`, 204 Arg: nil, 205 Out: `nil`, 206 }, 207 { 208 Fmt: `{:k}`, 209 Arg: (*int)(nil), 210 Out: `nil`, 211 }, 212 { 213 Fmt: `{:k}`, 214 Arg: time.Second, 215 Out: time.Second.String(), 216 }, 217 { 218 Fmt: `{:k}`, 219 Arg: `hello`, 220 Out: `hello`, 221 }, 222 { 223 Fmt: `{:k}`, 224 Arg: `hello world`, 225 Out: `"hello world"`, 226 }, 227 { 228 Fmt: `{:k}`, 229 Arg: "hello\nworld\n", 230 Out: `"hello\nworld\n"`, 231 }, 232 { 233 Fmt: `{:k}`, 234 Arg: errors.New("error!"), 235 Out: `error!`, 236 }, 237 { 238 Fmt: `{:k}`, 239 Arg: A{}, 240 Out: `{A="" B=nil}`, 241 }, 242 { 243 Fmt: `{:k}`, 244 Arg: B{}, 245 Out: `{a="" b=nil}`, 246 }, 247 { 248 Fmt: `{:k}`, 249 Arg: C{}, 250 Out: `c.String()`, 251 }, 252 { 253 Fmt: `{:k}`, 254 Arg: D{}, 255 Out: `d.AppendFormat()`, 256 }, 257 { 258 Fmt: `{:k}`, 259 Arg: PanicTest(0), 260 Out: `!{PANIC="oh no! a panic"}`, 261 }, 262 { 263 Fmt: `{:k}`, 264 Arg: []byte(`hello world`), 265 Out: `[h,e,l,l,o, ,w,o,r,l,d]`, 266 }, 267 { 268 Fmt: `{:k}`, 269 Arg: byte('?'), 270 Out: `'?'`, 271 }, 272 { 273 Fmt: `{:k}`, 274 Arg: byte('\n'), 275 Out: `'\n'`, 276 }, 277 278 // value format 279 { 280 Fmt: `{:v}`, 281 Arg: nil, 282 Out: `nil`, 283 }, 284 { 285 Fmt: `{:v}`, 286 Arg: (*int)(nil), 287 Out: `nil`, 288 }, 289 { 290 Fmt: `{:v}`, 291 Arg: time.Second, 292 Out: `"` + time.Second.String() + `"`, 293 }, 294 { 295 Fmt: `{:v}`, 296 Arg: `hello`, 297 Out: `"hello"`, 298 }, 299 { 300 Fmt: `{:v}`, 301 Arg: `hello world`, 302 Out: `"hello world"`, 303 }, 304 { 305 Fmt: `{:v}`, 306 Arg: "hello\nworld\n", 307 Out: `"hello\nworld\n"`, 308 }, 309 { 310 Fmt: `{:v}`, 311 Arg: errors.New("error!"), 312 Out: `"error!"`, 313 }, 314 { 315 Fmt: `{:v}`, 316 Arg: A{}, 317 Out: `{A="" B=nil}`, 318 }, 319 { 320 Fmt: `{:v}`, 321 Arg: B{}, 322 Out: `{a="" b=nil}`, 323 }, 324 { 325 Fmt: `{:v}`, 326 Arg: C{}, 327 Out: `"c.String()"`, 328 }, 329 { 330 Fmt: `{:v}`, 331 Arg: D{}, 332 Out: `d.AppendFormat()`, 333 }, 334 { 335 Fmt: `{:v}`, 336 Arg: PanicTest(0), 337 Out: `!{PANIC="oh no! a panic"}`, 338 }, 339 { 340 Fmt: `{:v}`, 341 Arg: []byte(`hello world`), 342 Out: `[h,e,l,l,o, ,w,o,r,l,d]`, 343 }, 344 { 345 Fmt: `{:v}`, 346 Arg: byte('?'), 347 Out: `'?'`, 348 }, 349 { 350 Fmt: `{:v}`, 351 Arg: byte('\n'), 352 Out: `'\n'`, 353 }, 354 355 // verbose format 356 { 357 Fmt: `{:?}`, 358 Arg: nil, 359 Out: `nil`, 360 }, 361 { 362 Fmt: `{:?}`, 363 Arg: (*int)(nil), 364 Out: `(*int)(nil)`, 365 }, 366 { 367 Fmt: `{:?}`, 368 Arg: time.Second, 369 Out: strconv.FormatInt(int64(time.Second), 10), 370 }, 371 { 372 Fmt: `{:?}`, 373 Arg: `hello`, 374 Out: `"hello"`, 375 }, 376 { 377 Fmt: `{:?}`, 378 Arg: `hello world`, 379 Out: `"hello world"`, 380 }, 381 { 382 Fmt: `{:?}`, 383 Arg: "hello\nworld\n", 384 Out: "\"hello\nworld\n\"", 385 }, 386 { 387 Fmt: `{:?}`, 388 Arg: errors.New("error!"), //nolint 389 Out: `*errors.errorString{s="error!"}`, 390 }, 391 { 392 Fmt: `{:?}`, 393 Arg: A{}, 394 Out: `format_test.A{A="" B=(*string)(nil)}`, 395 }, 396 { 397 Fmt: `{:?}`, 398 Arg: B{}, 399 Out: `format_test.B{a="" b=(*string)(nil)}`, 400 }, 401 { 402 Fmt: `{:?}`, 403 Arg: C{}, 404 Out: `format_test.C{}`, 405 }, 406 { 407 Fmt: `{:?}`, 408 Arg: D{}, 409 Out: `format_test.D{}`, 410 }, 411 { 412 Fmt: `{:?}`, 413 Arg: PanicTest(0), 414 Out: `0`, 415 }, 416 { 417 Fmt: `{:?}`, 418 Arg: []byte(`hello world`), 419 Out: `[h,e,l,l,o, ,w,o,r,l,d]`, 420 }, 421 { 422 Fmt: `{:?}`, 423 Arg: byte('?'), 424 Out: `'?'`, 425 }, 426 { 427 Fmt: `{:?}`, 428 Arg: byte('\n'), 429 Out: `'\n'`, 430 }, 431 432 // type format 433 { 434 Fmt: `{:T}`, 435 Arg: 0, 436 Out: reflect.TypeOf(0).String(), 437 }, 438 { 439 Fmt: `{:T}`, 440 Arg: uint64(0), 441 Out: reflect.TypeOf(uint64(0)).String(), 442 }, 443 { 444 Fmt: `{:T}`, 445 Arg: float64(0), 446 Out: reflect.TypeOf(float64(0)).String(), 447 }, 448 { 449 Fmt: `{:T}`, 450 Arg: time.Time{}, 451 Out: reflect.TypeOf(time.Time{}).String(), 452 }, 453 { 454 Fmt: `{:T}`, 455 Arg: time.Duration(0), 456 Out: reflect.TypeOf(time.Duration(0)).String(), 457 }, 458 { 459 Fmt: `{:T}`, 460 Arg: &time.Time{}, 461 Out: reflect.TypeOf(&time.Time{}).String(), 462 }, 463 { 464 Fmt: `{:T}`, 465 Arg: nil, 466 Out: `nil`, 467 }, 468 } 469 470 // printfMultiTests provide a list of more complex format 471 // strings, with any number of arguments and expected results. 472 // Intended to test that string format parsing and formatting 473 // produces expected results. 474 var printfMultiTests = []struct { 475 Fmt string 476 Arg []interface{} 477 Out string 478 }{} 479 480 var fmtTests = []interface{}{ 481 "hello world", 482 http.Server{}, 483 sync.Mutex{}, 484 sync.WaitGroup{}, 485 (*url.URL)(nil), 486 http.Request{}, 487 0, 488 1, 489 2, 490 3, 491 4, 492 5, 493 time.Second * 30, 494 map[string]string{ 495 "hello": "world", 496 "key": "value", 497 }, 498 []int{10, 11, 12, 13, 14, 15}, 499 []interface{}{"hello", "world", 12.2, 12, []byte("bytes")}, 500 []string{"hello", "world", "string", "array"}, 501 ',', 502 420.69, 503 time.Time{}, 504 A{}, 505 B{}, 506 C{}, 507 D{}, 508 PanicTest(0), 509 } 510 511 func TestAppend(t *testing.T) { 512 buf := format.Buffer{} 513 for _, arg := range appendTests { 514 format.Append(&buf, arg) 515 buf.Reset() 516 } 517 } 518 519 func TestPrintf(t *testing.T) { 520 for _, test := range printfTests { 521 out := format.Sprintf(test.Fmt, test.Arg) 522 if out != test.Out { 523 t.Fatalf("printf did not produce expected results\n"+ 524 "input={%q, %#v}\n"+ 525 "expect=%s\n"+ 526 "actual=%s\n", 527 test.Fmt, 528 test.Arg, 529 test.Out, 530 out, 531 ) 532 } 533 } 534 } 535 536 func TestPrintfMulti(t *testing.T) { 537 for _, test := range printfMultiTests { 538 out := format.Sprintf(test.Fmt, test.Arg...) 539 if out != test.Out { 540 t.Fatalf("printf (multi arg) did not produce expected results\n"+ 541 "input={%q, %#v}\n"+ 542 "expect=%s\n"+ 543 "actual=%s\n", 544 test.Fmt, 545 test.Arg, 546 test.Out, 547 out, 548 ) 549 } 550 } 551 } 552 553 func BenchmarkFprintf(b *testing.B) { 554 b.ReportAllocs() 555 b.ResetTimer() 556 b.RunParallel(func(pb *testing.PB) { 557 for pb.Next() { 558 for i := range fmtTests { 559 // Perform both non-verbose and verbose 560 format.Fprintf(io.Discard, "{}", fmtTests[i]) 561 format.Fprintf(io.Discard, "{:?}", fmtTests[i]) 562 } 563 } 564 }) 565 } 566 567 func BenchmarkFmtFprintf(b *testing.B) { 568 b.ReportAllocs() 569 b.ResetTimer() 570 b.RunParallel(func(pb *testing.PB) { 571 for pb.Next() { 572 for i := range fmtTests { 573 // Perform both non-verbose and verbose 574 fmt.Fprintf(io.Discard, "%v", fmtTests[i]) 575 fmt.Fprintf(io.Discard, "%#v", fmtTests[i]) 576 } 577 } 578 }) 579 }