github.com/ydb-platform/ydb-go-sdk/v3@v3.89.2/trace/trace_test.go (about) 1 package trace 2 3 import ( 4 "fmt" 5 "reflect" 6 "testing" 7 ) 8 9 // Stub is a helper function that stubs all functional fields of x with given f. 10 func Stub(x interface{}, f func(name string, args ...interface{})) { 11 (FieldStubber{ 12 OnCall: f, 13 }).Stub(reflect.ValueOf(x)) 14 } 15 16 func ClearContext(x interface{}) interface{} { 17 p := reflect.ValueOf(x).Index(0) 18 t := p.Elem().Type() 19 f, has := t.FieldByName("Context") 20 if has && f.Type.Kind() == reflect.Interface { 21 x := reflect.New(t) 22 x.Elem().Set(p.Elem()) 23 c := x.Elem().FieldByName(f.Name) 24 c.Set(reflect.Zero(c.Type())) 25 p.Set(x) 26 } 27 28 return p.Interface() 29 } 30 31 // FieldStubber contains options of filling all struct functional fields. 32 // Internals: https://github.com/ydb-platform/ydb-go-sdk/blob/master/VERSIONING.md#internals 33 type FieldStubber struct { 34 // OnStub is an optional callback that is called when field getting 35 // stubbed. 36 // Internals: https://github.com/ydb-platform/ydb-go-sdk/blob/master/VERSIONING.md#internals 37 OnStub func(name string) 38 39 // OnCall is an optional callback that will be called for each stubbed 40 // field getting called. 41 // Internals: https://github.com/ydb-platform/ydb-go-sdk/blob/master/VERSIONING.md#internals 42 OnCall func(name string, args ...interface{}) 43 } 44 45 // Stub fills in given x struct. 46 // Internals: https://github.com/ydb-platform/ydb-go-sdk/blob/master/VERSIONING.md#internals 47 func (f FieldStubber) Stub(x reflect.Value) { 48 var ( 49 v = x.Elem() 50 t = v.Type() 51 ) 52 for i := 0; i < t.NumField(); i++ { 53 var ( 54 fx = v.Field(i) 55 ft = fx.Type() 56 ) 57 if ft.Kind() != reflect.Func { 58 continue 59 } 60 name := t.Field(i).Name 61 if f.OnStub != nil { 62 f.OnStub(name) 63 } 64 out := []reflect.Value{} 65 for i := 0; i < ft.NumOut(); i++ { 66 ti := reflect.New(ft.Out(i)).Elem() 67 out = append(out, ti) 68 } 69 fn := reflect.MakeFunc(ft, func(args []reflect.Value) []reflect.Value { 70 if f.OnCall == nil { 71 return out 72 } 73 params := make([]interface{}, len(args)) 74 for i, arg := range args { 75 params[i] = arg.Interface() 76 } 77 f.OnCall(name, params...) 78 79 return out 80 }) 81 fx.Set(fn) 82 } 83 } 84 85 func TestTable(t *testing.T) { 86 testSingleTrace(t, &Table{}, "Table") 87 } 88 89 func TestDriver(t *testing.T) { 90 testSingleTrace(t, &Driver{}, "Driver") 91 } 92 93 func TestRetry(t *testing.T) { 94 testSingleTrace(t, &Retry{}, "Retry") 95 } 96 97 func TestCoordination(t *testing.T) { 98 testSingleTrace(t, &Table{}, "Coordination") 99 } 100 101 func TestRatelimiter(t *testing.T) { 102 testSingleTrace(t, &Ratelimiter{}, "Ratelimiter") 103 } 104 105 func TestTopic(t *testing.T) { 106 testSingleTrace(t, &Topic{}, "Topic") 107 } 108 109 func TestDiscovery(t *testing.T) { 110 testSingleTrace(t, &Discovery{}, "Discovery") 111 } 112 113 func TestDatabaseSQL(t *testing.T) { 114 testSingleTrace(t, &DatabaseSQL{}, "DatabaseSQL") 115 } 116 117 func testSingleTrace(t *testing.T, x interface{}, traceName string) { 118 t.Helper() 119 v := reflect.ValueOf(x) 120 m := v.MethodByName("Compose") 121 m.Call( 122 []reflect.Value{reflect.New(reflect.ValueOf(x).Elem().Type())}, 123 ) 124 a := reflect.New(reflect.TypeOf(x).Elem()) 125 defer assertCalled(t, traceName, stubEachFunc(a)) 126 callEachFunc(a.Elem()) 127 } 128 129 func assertCalled(t *testing.T, prefix string, called map[string]bool) { 130 t.Helper() 131 for name, called := range called { 132 if !called { 133 t.Error(prefix, fmt.Sprintf("%s field is not called", name)) 134 } 135 } 136 } 137 138 func stubEachFunc(x reflect.Value) map[string]bool { 139 fs := make(map[string]bool) 140 (FieldStubber{ 141 OnStub: func(name string) { 142 fs[name] = false 143 }, 144 OnCall: func(name string, _ ...interface{}) { 145 fs[name] = true 146 }, 147 }).Stub(x) 148 149 return fs 150 } 151 152 func callFunc(f reflect.Value, ft reflect.Type) { 153 if ft.Kind() != reflect.Func { 154 return 155 } 156 if f.IsNil() { 157 return 158 } 159 args := make([]reflect.Value, ft.NumIn()) 160 for j := range args { 161 args[j] = reflect.New(ft.In(j)).Elem() 162 } 163 for _, xx := range f.Call(args) { 164 switch xx.Type().Kind() { 165 case reflect.Struct: 166 callEachFunc(xx) 167 case reflect.Func: 168 callFunc(xx, xx.Type()) 169 default: 170 _ = xx 171 } 172 } 173 } 174 175 func callEachFunc(x reflect.Value) { 176 t := x.Type() 177 for i := 0; i < t.NumField(); i++ { 178 var ( 179 f = x.Field(i) 180 ft = f.Type() 181 ) 182 callFunc(f, ft) 183 } 184 }