github.com/lingyao2333/mo-zero@v1.4.1/core/logx/logs_test.go (about) 1 package logx 2 3 import ( 4 "encoding/json" 5 "errors" 6 "fmt" 7 "io" 8 "log" 9 "os" 10 "reflect" 11 "runtime" 12 "strings" 13 "sync" 14 "sync/atomic" 15 "testing" 16 "time" 17 18 "github.com/stretchr/testify/assert" 19 ) 20 21 var ( 22 s = []byte("Sending #11 notification (id: 1451875113812010473) in #1 connection") 23 pool = make(chan []byte, 1) 24 _ Writer = (*mockWriter)(nil) 25 ) 26 27 type mockWriter struct { 28 lock sync.Mutex 29 builder strings.Builder 30 } 31 32 func (mw *mockWriter) Alert(v interface{}) { 33 mw.lock.Lock() 34 defer mw.lock.Unlock() 35 output(&mw.builder, levelAlert, v) 36 } 37 38 func (mw *mockWriter) Debug(v interface{}, fields ...LogField) { 39 mw.lock.Lock() 40 defer mw.lock.Unlock() 41 output(&mw.builder, levelDebug, v, fields...) 42 } 43 44 func (mw *mockWriter) Error(v interface{}, fields ...LogField) { 45 mw.lock.Lock() 46 defer mw.lock.Unlock() 47 output(&mw.builder, levelError, v, fields...) 48 } 49 50 func (mw *mockWriter) Info(v interface{}, fields ...LogField) { 51 mw.lock.Lock() 52 defer mw.lock.Unlock() 53 output(&mw.builder, levelInfo, v, fields...) 54 } 55 56 func (mw *mockWriter) Severe(v interface{}) { 57 mw.lock.Lock() 58 defer mw.lock.Unlock() 59 output(&mw.builder, levelSevere, v) 60 } 61 62 func (mw *mockWriter) Slow(v interface{}, fields ...LogField) { 63 mw.lock.Lock() 64 defer mw.lock.Unlock() 65 output(&mw.builder, levelSlow, v, fields...) 66 } 67 68 func (mw *mockWriter) Stack(v interface{}) { 69 mw.lock.Lock() 70 defer mw.lock.Unlock() 71 output(&mw.builder, levelError, v) 72 } 73 74 func (mw *mockWriter) Stat(v interface{}, fields ...LogField) { 75 mw.lock.Lock() 76 defer mw.lock.Unlock() 77 output(&mw.builder, levelStat, v, fields...) 78 } 79 80 func (mw *mockWriter) Close() error { 81 return nil 82 } 83 84 func (mw *mockWriter) Contains(text string) bool { 85 mw.lock.Lock() 86 defer mw.lock.Unlock() 87 return strings.Contains(mw.builder.String(), text) 88 } 89 90 func (mw *mockWriter) Reset() { 91 mw.lock.Lock() 92 defer mw.lock.Unlock() 93 mw.builder.Reset() 94 } 95 96 func (mw *mockWriter) String() string { 97 mw.lock.Lock() 98 defer mw.lock.Unlock() 99 return mw.builder.String() 100 } 101 102 func TestField(t *testing.T) { 103 tests := []struct { 104 name string 105 f LogField 106 want map[string]interface{} 107 }{ 108 { 109 name: "error", 110 f: Field("foo", errors.New("bar")), 111 want: map[string]interface{}{ 112 "foo": "bar", 113 }, 114 }, 115 { 116 name: "errors", 117 f: Field("foo", []error{errors.New("bar"), errors.New("baz")}), 118 want: map[string]interface{}{ 119 "foo": []interface{}{"bar", "baz"}, 120 }, 121 }, 122 { 123 name: "strings", 124 f: Field("foo", []string{"bar", "baz"}), 125 want: map[string]interface{}{ 126 "foo": []interface{}{"bar", "baz"}, 127 }, 128 }, 129 { 130 name: "duration", 131 f: Field("foo", time.Second), 132 want: map[string]interface{}{ 133 "foo": "1s", 134 }, 135 }, 136 { 137 name: "durations", 138 f: Field("foo", []time.Duration{time.Second, 2 * time.Second}), 139 want: map[string]interface{}{ 140 "foo": []interface{}{"1s", "2s"}, 141 }, 142 }, 143 { 144 name: "times", 145 f: Field("foo", []time.Time{ 146 time.Date(2020, time.January, 1, 0, 0, 0, 0, time.UTC), 147 time.Date(2020, time.January, 2, 0, 0, 0, 0, time.UTC), 148 }), 149 want: map[string]interface{}{ 150 "foo": []interface{}{"2020-01-01 00:00:00 +0000 UTC", "2020-01-02 00:00:00 +0000 UTC"}, 151 }, 152 }, 153 { 154 name: "stringer", 155 f: Field("foo", ValStringer{val: "bar"}), 156 want: map[string]interface{}{ 157 "foo": "bar", 158 }, 159 }, 160 { 161 name: "stringers", 162 f: Field("foo", []fmt.Stringer{ValStringer{val: "bar"}, ValStringer{val: "baz"}}), 163 want: map[string]interface{}{ 164 "foo": []interface{}{"bar", "baz"}, 165 }, 166 }, 167 } 168 169 for _, test := range tests { 170 test := test 171 t.Run(test.name, func(t *testing.T) { 172 w := new(mockWriter) 173 old := writer.Swap(w) 174 defer writer.Store(old) 175 176 Infow("foo", test.f) 177 validateFields(t, w.String(), test.want) 178 }) 179 } 180 } 181 182 func TestFileLineFileMode(t *testing.T) { 183 w := new(mockWriter) 184 old := writer.Swap(w) 185 defer writer.Store(old) 186 187 file, line := getFileLine() 188 Error("anything") 189 assert.True(t, w.Contains(fmt.Sprintf("%s:%d", file, line+1))) 190 191 file, line = getFileLine() 192 Errorf("anything %s", "format") 193 assert.True(t, w.Contains(fmt.Sprintf("%s:%d", file, line+1))) 194 } 195 196 func TestFileLineConsoleMode(t *testing.T) { 197 w := new(mockWriter) 198 old := writer.Swap(w) 199 defer writer.Store(old) 200 201 file, line := getFileLine() 202 Error("anything") 203 assert.True(t, w.Contains(fmt.Sprintf("%s:%d", file, line+1))) 204 205 w.Reset() 206 file, line = getFileLine() 207 Errorf("anything %s", "format") 208 assert.True(t, w.Contains(fmt.Sprintf("%s:%d", file, line+1))) 209 } 210 211 func TestStructedLogAlert(t *testing.T) { 212 w := new(mockWriter) 213 old := writer.Swap(w) 214 defer writer.Store(old) 215 216 doTestStructedLog(t, levelAlert, w, func(v ...interface{}) { 217 Alert(fmt.Sprint(v...)) 218 }) 219 } 220 221 func TestStructedLogDebug(t *testing.T) { 222 w := new(mockWriter) 223 old := writer.Swap(w) 224 defer writer.Store(old) 225 226 doTestStructedLog(t, levelDebug, w, func(v ...interface{}) { 227 Debug(v...) 228 }) 229 } 230 231 func TestStructedLogDebugf(t *testing.T) { 232 w := new(mockWriter) 233 old := writer.Swap(w) 234 defer writer.Store(old) 235 236 doTestStructedLog(t, levelDebug, w, func(v ...interface{}) { 237 Debugf(fmt.Sprint(v...)) 238 }) 239 } 240 241 func TestStructedLogDebugv(t *testing.T) { 242 w := new(mockWriter) 243 old := writer.Swap(w) 244 defer writer.Store(old) 245 246 doTestStructedLog(t, levelDebug, w, func(v ...interface{}) { 247 Debugv(fmt.Sprint(v...)) 248 }) 249 } 250 251 func TestStructedLogDebugw(t *testing.T) { 252 w := new(mockWriter) 253 old := writer.Swap(w) 254 defer writer.Store(old) 255 256 doTestStructedLog(t, levelDebug, w, func(v ...interface{}) { 257 Debugw(fmt.Sprint(v...), Field("foo", time.Second)) 258 }) 259 } 260 261 func TestStructedLogError(t *testing.T) { 262 w := new(mockWriter) 263 old := writer.Swap(w) 264 defer writer.Store(old) 265 266 doTestStructedLog(t, levelError, w, func(v ...interface{}) { 267 Error(v...) 268 }) 269 } 270 271 func TestStructedLogErrorf(t *testing.T) { 272 w := new(mockWriter) 273 old := writer.Swap(w) 274 defer writer.Store(old) 275 276 doTestStructedLog(t, levelError, w, func(v ...interface{}) { 277 Errorf("%s", fmt.Sprint(v...)) 278 }) 279 } 280 281 func TestStructedLogErrorv(t *testing.T) { 282 w := new(mockWriter) 283 old := writer.Swap(w) 284 defer writer.Store(old) 285 286 doTestStructedLog(t, levelError, w, func(v ...interface{}) { 287 Errorv(fmt.Sprint(v...)) 288 }) 289 } 290 291 func TestStructedLogErrorw(t *testing.T) { 292 w := new(mockWriter) 293 old := writer.Swap(w) 294 defer writer.Store(old) 295 296 doTestStructedLog(t, levelError, w, func(v ...interface{}) { 297 Errorw(fmt.Sprint(v...), Field("foo", "bar")) 298 }) 299 } 300 301 func TestStructedLogInfo(t *testing.T) { 302 w := new(mockWriter) 303 old := writer.Swap(w) 304 defer writer.Store(old) 305 306 doTestStructedLog(t, levelInfo, w, func(v ...interface{}) { 307 Info(v...) 308 }) 309 } 310 311 func TestStructedLogInfof(t *testing.T) { 312 w := new(mockWriter) 313 old := writer.Swap(w) 314 defer writer.Store(old) 315 316 doTestStructedLog(t, levelInfo, w, func(v ...interface{}) { 317 Infof("%s", fmt.Sprint(v...)) 318 }) 319 } 320 321 func TestStructedLogInfov(t *testing.T) { 322 w := new(mockWriter) 323 old := writer.Swap(w) 324 defer writer.Store(old) 325 326 doTestStructedLog(t, levelInfo, w, func(v ...interface{}) { 327 Infov(fmt.Sprint(v...)) 328 }) 329 } 330 331 func TestStructedLogInfow(t *testing.T) { 332 w := new(mockWriter) 333 old := writer.Swap(w) 334 defer writer.Store(old) 335 336 doTestStructedLog(t, levelInfo, w, func(v ...interface{}) { 337 Infow(fmt.Sprint(v...), Field("foo", "bar")) 338 }) 339 } 340 341 func TestStructedLogInfoConsoleAny(t *testing.T) { 342 w := new(mockWriter) 343 old := writer.Swap(w) 344 defer writer.Store(old) 345 346 doTestStructedLogConsole(t, w, func(v ...interface{}) { 347 old := atomic.LoadUint32(&encoding) 348 atomic.StoreUint32(&encoding, plainEncodingType) 349 defer func() { 350 atomic.StoreUint32(&encoding, old) 351 }() 352 353 Infov(v) 354 }) 355 } 356 357 func TestStructedLogInfoConsoleAnyString(t *testing.T) { 358 w := new(mockWriter) 359 old := writer.Swap(w) 360 defer writer.Store(old) 361 362 doTestStructedLogConsole(t, w, func(v ...interface{}) { 363 old := atomic.LoadUint32(&encoding) 364 atomic.StoreUint32(&encoding, plainEncodingType) 365 defer func() { 366 atomic.StoreUint32(&encoding, old) 367 }() 368 369 Infov(fmt.Sprint(v...)) 370 }) 371 } 372 373 func TestStructedLogInfoConsoleAnyError(t *testing.T) { 374 w := new(mockWriter) 375 old := writer.Swap(w) 376 defer writer.Store(old) 377 378 doTestStructedLogConsole(t, w, func(v ...interface{}) { 379 old := atomic.LoadUint32(&encoding) 380 atomic.StoreUint32(&encoding, plainEncodingType) 381 defer func() { 382 atomic.StoreUint32(&encoding, old) 383 }() 384 385 Infov(errors.New(fmt.Sprint(v...))) 386 }) 387 } 388 389 func TestStructedLogInfoConsoleAnyStringer(t *testing.T) { 390 w := new(mockWriter) 391 old := writer.Swap(w) 392 defer writer.Store(old) 393 394 doTestStructedLogConsole(t, w, func(v ...interface{}) { 395 old := atomic.LoadUint32(&encoding) 396 atomic.StoreUint32(&encoding, plainEncodingType) 397 defer func() { 398 atomic.StoreUint32(&encoding, old) 399 }() 400 401 Infov(ValStringer{ 402 val: fmt.Sprint(v...), 403 }) 404 }) 405 } 406 407 func TestStructedLogInfoConsoleText(t *testing.T) { 408 w := new(mockWriter) 409 old := writer.Swap(w) 410 defer writer.Store(old) 411 412 doTestStructedLogConsole(t, w, func(v ...interface{}) { 413 old := atomic.LoadUint32(&encoding) 414 atomic.StoreUint32(&encoding, plainEncodingType) 415 defer func() { 416 atomic.StoreUint32(&encoding, old) 417 }() 418 419 Info(fmt.Sprint(v...)) 420 }) 421 } 422 423 func TestStructedLogSlow(t *testing.T) { 424 w := new(mockWriter) 425 old := writer.Swap(w) 426 defer writer.Store(old) 427 428 doTestStructedLog(t, levelSlow, w, func(v ...interface{}) { 429 Slow(v...) 430 }) 431 } 432 433 func TestStructedLogSlowf(t *testing.T) { 434 w := new(mockWriter) 435 old := writer.Swap(w) 436 defer writer.Store(old) 437 438 doTestStructedLog(t, levelSlow, w, func(v ...interface{}) { 439 Slowf(fmt.Sprint(v...)) 440 }) 441 } 442 443 func TestStructedLogSlowv(t *testing.T) { 444 w := new(mockWriter) 445 old := writer.Swap(w) 446 defer writer.Store(old) 447 448 doTestStructedLog(t, levelSlow, w, func(v ...interface{}) { 449 Slowv(fmt.Sprint(v...)) 450 }) 451 } 452 453 func TestStructedLogSloww(t *testing.T) { 454 w := new(mockWriter) 455 old := writer.Swap(w) 456 defer writer.Store(old) 457 458 doTestStructedLog(t, levelSlow, w, func(v ...interface{}) { 459 Sloww(fmt.Sprint(v...), Field("foo", time.Second)) 460 }) 461 } 462 463 func TestStructedLogStat(t *testing.T) { 464 w := new(mockWriter) 465 old := writer.Swap(w) 466 defer writer.Store(old) 467 468 doTestStructedLog(t, levelStat, w, func(v ...interface{}) { 469 Stat(v...) 470 }) 471 } 472 473 func TestStructedLogStatf(t *testing.T) { 474 w := new(mockWriter) 475 old := writer.Swap(w) 476 defer writer.Store(old) 477 478 doTestStructedLog(t, levelStat, w, func(v ...interface{}) { 479 Statf(fmt.Sprint(v...)) 480 }) 481 } 482 483 func TestStructedLogSevere(t *testing.T) { 484 w := new(mockWriter) 485 old := writer.Swap(w) 486 defer writer.Store(old) 487 488 doTestStructedLog(t, levelSevere, w, func(v ...interface{}) { 489 Severe(v...) 490 }) 491 } 492 493 func TestStructedLogSeveref(t *testing.T) { 494 w := new(mockWriter) 495 old := writer.Swap(w) 496 defer writer.Store(old) 497 498 doTestStructedLog(t, levelSevere, w, func(v ...interface{}) { 499 Severef(fmt.Sprint(v...)) 500 }) 501 } 502 503 func TestStructedLogWithDuration(t *testing.T) { 504 const message = "hello there" 505 w := new(mockWriter) 506 old := writer.Swap(w) 507 defer writer.Store(old) 508 509 WithDuration(time.Second).Info(message) 510 var entry map[string]interface{} 511 if err := json.Unmarshal([]byte(w.String()), &entry); err != nil { 512 t.Error(err) 513 } 514 assert.Equal(t, levelInfo, entry[levelKey]) 515 assert.Equal(t, message, entry[contentKey]) 516 assert.Equal(t, "1000.0ms", entry[durationKey]) 517 } 518 519 func TestSetLevel(t *testing.T) { 520 SetLevel(ErrorLevel) 521 const message = "hello there" 522 w := new(mockWriter) 523 old := writer.Swap(w) 524 defer writer.Store(old) 525 526 Info(message) 527 assert.Equal(t, 0, w.builder.Len()) 528 } 529 530 func TestSetLevelTwiceWithMode(t *testing.T) { 531 testModes := []string{ 532 "mode", 533 "console", 534 "volumn", 535 } 536 w := new(mockWriter) 537 old := writer.Swap(w) 538 defer writer.Store(old) 539 540 for _, mode := range testModes { 541 testSetLevelTwiceWithMode(t, mode, w) 542 } 543 } 544 545 func TestSetLevelWithDuration(t *testing.T) { 546 SetLevel(ErrorLevel) 547 const message = "hello there" 548 w := new(mockWriter) 549 old := writer.Swap(w) 550 defer writer.Store(old) 551 552 WithDuration(time.Second).Info(message) 553 assert.Equal(t, 0, w.builder.Len()) 554 } 555 556 func TestErrorfWithWrappedError(t *testing.T) { 557 SetLevel(ErrorLevel) 558 const message = "there" 559 w := new(mockWriter) 560 old := writer.Swap(w) 561 defer writer.Store(old) 562 563 Errorf("hello %w", errors.New(message)) 564 assert.True(t, strings.Contains(w.String(), "hello there")) 565 } 566 567 func TestMustNil(t *testing.T) { 568 Must(nil) 569 } 570 571 func TestSetup(t *testing.T) { 572 defer func() { 573 SetLevel(InfoLevel) 574 atomic.StoreUint32(&encoding, jsonEncodingType) 575 }() 576 577 MustSetup(LogConf{ 578 ServiceName: "any", 579 Mode: "console", 580 TimeFormat: timeFormat, 581 }) 582 MustSetup(LogConf{ 583 ServiceName: "any", 584 Mode: "file", 585 Path: os.TempDir(), 586 }) 587 MustSetup(LogConf{ 588 ServiceName: "any", 589 Mode: "volume", 590 Path: os.TempDir(), 591 }) 592 MustSetup(LogConf{ 593 ServiceName: "any", 594 Mode: "console", 595 TimeFormat: timeFormat, 596 }) 597 MustSetup(LogConf{ 598 ServiceName: "any", 599 Mode: "console", 600 Encoding: plainEncoding, 601 }) 602 603 defer os.RemoveAll("CD01CB7D-2705-4F3F-889E-86219BF56F10") 604 assert.NotNil(t, setupWithVolume(LogConf{})) 605 assert.Nil(t, setupWithVolume(LogConf{ 606 ServiceName: "CD01CB7D-2705-4F3F-889E-86219BF56F10", 607 })) 608 assert.Nil(t, setupWithVolume(LogConf{ 609 ServiceName: "CD01CB7D-2705-4F3F-889E-86219BF56F10", 610 Rotation: sizeRotationRule, 611 })) 612 assert.NotNil(t, setupWithFiles(LogConf{})) 613 assert.Nil(t, setupWithFiles(LogConf{ 614 ServiceName: "any", 615 Path: os.TempDir(), 616 Compress: true, 617 KeepDays: 1, 618 MaxBackups: 3, 619 MaxSize: 1024 * 1024, 620 })) 621 setupLogLevel(LogConf{ 622 Level: levelInfo, 623 }) 624 setupLogLevel(LogConf{ 625 Level: levelError, 626 }) 627 setupLogLevel(LogConf{ 628 Level: levelSevere, 629 }) 630 _, err := createOutput("") 631 assert.NotNil(t, err) 632 Disable() 633 SetLevel(InfoLevel) 634 atomic.StoreUint32(&encoding, jsonEncodingType) 635 } 636 637 func TestDisable(t *testing.T) { 638 Disable() 639 640 var opt logOptions 641 WithKeepDays(1)(&opt) 642 WithGzip()(&opt) 643 WithMaxBackups(1)(&opt) 644 WithMaxSize(1024)(&opt) 645 assert.Nil(t, Close()) 646 assert.Nil(t, Close()) 647 } 648 649 func TestDisableStat(t *testing.T) { 650 DisableStat() 651 652 const message = "hello there" 653 w := new(mockWriter) 654 old := writer.Swap(w) 655 defer writer.Store(old) 656 Stat(message) 657 assert.Equal(t, 0, w.builder.Len()) 658 } 659 660 func TestSetWriter(t *testing.T) { 661 atomic.StoreUint32(&disableLog, 0) 662 Reset() 663 SetWriter(nopWriter{}) 664 assert.NotNil(t, writer.Load()) 665 assert.True(t, writer.Load() == nopWriter{}) 666 mocked := new(mockWriter) 667 SetWriter(mocked) 668 assert.Equal(t, mocked, writer.Load()) 669 } 670 671 func TestWithGzip(t *testing.T) { 672 fn := WithGzip() 673 var opt logOptions 674 fn(&opt) 675 assert.True(t, opt.gzipEnabled) 676 } 677 678 func TestWithKeepDays(t *testing.T) { 679 fn := WithKeepDays(1) 680 var opt logOptions 681 fn(&opt) 682 assert.Equal(t, 1, opt.keepDays) 683 } 684 685 func BenchmarkCopyByteSliceAppend(b *testing.B) { 686 for i := 0; i < b.N; i++ { 687 var buf []byte 688 buf = append(buf, getTimestamp()...) 689 buf = append(buf, ' ') 690 buf = append(buf, s...) 691 _ = buf 692 } 693 } 694 695 func BenchmarkCopyByteSliceAllocExactly(b *testing.B) { 696 for i := 0; i < b.N; i++ { 697 now := []byte(getTimestamp()) 698 buf := make([]byte, len(now)+1+len(s)) 699 n := copy(buf, now) 700 buf[n] = ' ' 701 copy(buf[n+1:], s) 702 } 703 } 704 705 func BenchmarkCopyByteSlice(b *testing.B) { 706 var buf []byte 707 for i := 0; i < b.N; i++ { 708 buf = make([]byte, len(s)) 709 copy(buf, s) 710 } 711 fmt.Fprint(io.Discard, buf) 712 } 713 714 func BenchmarkCopyOnWriteByteSlice(b *testing.B) { 715 var buf []byte 716 for i := 0; i < b.N; i++ { 717 size := len(s) 718 buf = s[:size:size] 719 } 720 fmt.Fprint(io.Discard, buf) 721 } 722 723 func BenchmarkCacheByteSlice(b *testing.B) { 724 for i := 0; i < b.N; i++ { 725 dup := fetch() 726 copy(dup, s) 727 put(dup) 728 } 729 } 730 731 func BenchmarkLogs(b *testing.B) { 732 b.ReportAllocs() 733 734 log.SetOutput(io.Discard) 735 for i := 0; i < b.N; i++ { 736 Info(i) 737 } 738 } 739 740 func fetch() []byte { 741 select { 742 case b := <-pool: 743 return b 744 default: 745 } 746 return make([]byte, 4096) 747 } 748 749 func getFileLine() (string, int) { 750 _, file, line, _ := runtime.Caller(1) 751 short := file 752 753 for i := len(file) - 1; i > 0; i-- { 754 if file[i] == '/' { 755 short = file[i+1:] 756 break 757 } 758 } 759 760 return short, line 761 } 762 763 func put(b []byte) { 764 select { 765 case pool <- b: 766 default: 767 } 768 } 769 770 func doTestStructedLog(t *testing.T, level string, w *mockWriter, write func(...interface{})) { 771 const message = "hello there" 772 write(message) 773 774 var entry map[string]interface{} 775 if err := json.Unmarshal([]byte(w.String()), &entry); err != nil { 776 t.Error(err) 777 } 778 779 assert.Equal(t, level, entry[levelKey]) 780 val, ok := entry[contentKey] 781 assert.True(t, ok) 782 assert.True(t, strings.Contains(val.(string), message)) 783 } 784 785 func doTestStructedLogConsole(t *testing.T, w *mockWriter, write func(...interface{})) { 786 const message = "hello there" 787 write(message) 788 assert.True(t, strings.Contains(w.String(), message)) 789 } 790 791 func testSetLevelTwiceWithMode(t *testing.T, mode string, w *mockWriter) { 792 writer.Store(nil) 793 SetUp(LogConf{ 794 Mode: mode, 795 Level: "error", 796 Path: "/dev/null", 797 }) 798 SetUp(LogConf{ 799 Mode: mode, 800 Level: "info", 801 Path: "/dev/null", 802 }) 803 const message = "hello there" 804 Info(message) 805 assert.Equal(t, 0, w.builder.Len()) 806 Infof(message) 807 assert.Equal(t, 0, w.builder.Len()) 808 ErrorStack(message) 809 assert.Equal(t, 0, w.builder.Len()) 810 ErrorStackf(message) 811 assert.Equal(t, 0, w.builder.Len()) 812 } 813 814 type ValStringer struct { 815 val string 816 } 817 818 func (v ValStringer) String() string { 819 return v.val 820 } 821 822 func validateFields(t *testing.T, content string, fields map[string]interface{}) { 823 var m map[string]interface{} 824 if err := json.Unmarshal([]byte(content), &m); err != nil { 825 t.Error(err) 826 } 827 828 for k, v := range fields { 829 if reflect.TypeOf(v).Kind() == reflect.Slice { 830 assert.EqualValues(t, v, m[k]) 831 } else { 832 assert.Equal(t, v, m[k], content) 833 } 834 } 835 }