grpctest.go 3.1 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112
  1. /*
  2. *
  3. * Copyright 2018 gRPC authors.
  4. *
  5. * Licensed under the Apache License, Version 2.0 (the "License");
  6. * you may not use this file except in compliance with the License.
  7. * You may obtain a copy of the License at
  8. *
  9. * http://www.apache.org/licenses/LICENSE-2.0
  10. *
  11. * Unless required by applicable law or agreed to in writing, software
  12. * distributed under the License is distributed on an "AS IS" BASIS,
  13. * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
  14. * See the License for the specific language governing permissions and
  15. * limitations under the License.
  16. *
  17. */
  18. // Package grpctest implements testing helpers.
  19. package grpctest
  20. import (
  21. "reflect"
  22. "strings"
  23. "sync/atomic"
  24. "testing"
  25. "google.golang.org/grpc/internal/leakcheck"
  26. )
  27. var lcFailed uint32
  28. type errorer struct {
  29. t *testing.T
  30. }
  31. func (e errorer) Errorf(format string, args ...interface{}) {
  32. atomic.StoreUint32(&lcFailed, 1)
  33. e.t.Errorf(format, args...)
  34. }
  35. // Tester is an implementation of the x interface parameter to
  36. // grpctest.RunSubTests with default Setup and Teardown behavior. Setup updates
  37. // the tlogger and Teardown performs a leak check. Embed in a struct with tests
  38. // defined to use.
  39. type Tester struct{}
  40. // Setup updates the tlogger.
  41. func (Tester) Setup(t *testing.T) {
  42. TLogger.Update(t)
  43. }
  44. // Teardown performs a leak check.
  45. func (Tester) Teardown(t *testing.T) {
  46. if atomic.LoadUint32(&lcFailed) == 1 {
  47. return
  48. }
  49. leakcheck.Check(errorer{t: t})
  50. if atomic.LoadUint32(&lcFailed) == 1 {
  51. t.Log("Leak check disabled for future tests")
  52. }
  53. TLogger.EndTest(t)
  54. }
  55. func getTestFunc(t *testing.T, xv reflect.Value, name string) func(*testing.T) {
  56. if m := xv.MethodByName(name); m.IsValid() {
  57. if f, ok := m.Interface().(func(*testing.T)); ok {
  58. return f
  59. }
  60. // Method exists but has the wrong type signature.
  61. t.Fatalf("grpctest: function %v has unexpected signature (%T)", name, m.Interface())
  62. }
  63. return func(*testing.T) {}
  64. }
  65. // RunSubTests runs all "Test___" functions that are methods of x as subtests
  66. // of the current test. If x contains methods "Setup(*testing.T)" or
  67. // "Teardown(*testing.T)", those are run before or after each of the test
  68. // functions, respectively.
  69. //
  70. // For example usage, see example_test.go. Run it using:
  71. //
  72. // $ go test -v -run TestExample .
  73. //
  74. // To run a specific test/subtest:
  75. //
  76. // $ go test -v -run 'TestExample/^Something$' .
  77. func RunSubTests(t *testing.T, x interface{}) {
  78. xt := reflect.TypeOf(x)
  79. xv := reflect.ValueOf(x)
  80. setup := getTestFunc(t, xv, "Setup")
  81. teardown := getTestFunc(t, xv, "Teardown")
  82. for i := 0; i < xt.NumMethod(); i++ {
  83. methodName := xt.Method(i).Name
  84. if !strings.HasPrefix(methodName, "Test") {
  85. continue
  86. }
  87. tfunc := getTestFunc(t, xv, methodName)
  88. t.Run(strings.TrimPrefix(methodName, "Test"), func(t *testing.T) {
  89. // Run leakcheck in t.Cleanup() to guarantee it is run even if tfunc
  90. // or setup uses t.Fatal().
  91. //
  92. // Note that a defer would run before t.Cleanup, so if a goroutine
  93. // is closed by a test's t.Cleanup, a deferred leakcheck would fail.
  94. t.Cleanup(func() { teardown(t) })
  95. setup(t)
  96. tfunc(t)
  97. })
  98. }
  99. }