github.com/cloudwego/kitex@v0.9.0/pkg/generic/json_test/generic_test.go (about) 1 /* 2 * Copyright 2021 CloudWeGo Authors 3 * 4 * Licensed under the Apache License, Version 2.0 (the "License"); 5 * you may not use this file except in compliance with the License. 6 * You may obtain a copy of the License at 7 * 8 * http://www.apache.org/licenses/LICENSE-2.0 9 * 10 * Unless required by applicable law or agreed to in writing, software 11 * distributed under the License is distributed on an "AS IS" BASIS, 12 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 * See the License for the specific language governing permissions and 14 * limitations under the License. 15 */ 16 17 package test 18 19 import ( 20 "context" 21 "encoding/base64" 22 "encoding/json" 23 "fmt" 24 "math" 25 "net" 26 "reflect" 27 "runtime" 28 "runtime/debug" 29 "strings" 30 "testing" 31 "time" 32 33 "github.com/cloudwego/dynamicgo/conv" 34 "github.com/tidwall/gjson" 35 36 "github.com/cloudwego/kitex/client/callopt" 37 "github.com/cloudwego/kitex/client/genericclient" 38 kt "github.com/cloudwego/kitex/internal/mocks/thrift" 39 "github.com/cloudwego/kitex/internal/test" 40 "github.com/cloudwego/kitex/pkg/generic" 41 "github.com/cloudwego/kitex/pkg/generic/descriptor" 42 "github.com/cloudwego/kitex/server" 43 "github.com/cloudwego/kitex/transport" 44 ) 45 46 func TestRun(t *testing.T) { 47 t.Run("TestThrift", testThrift) 48 t.Run("TestThriftWithDynamicGo", testThriftWithDynamicGo) 49 t.Run("TestThriftPingMethod", testThriftPingMethod) 50 t.Run("TestThriftPingMethodWithDynamicGo", testThriftPingMethodWithDynamicGo) 51 t.Run("TestThriftError", testThriftError) 52 t.Run("TestThriftOnewayMethod", testThriftOnewayMethod) 53 t.Run("TestThriftOnewayMethodWithDynamicGo", testThriftOnewayMethodWithDynamicGo) 54 t.Run("TestThriftVoidMethod", testThriftVoidMethod) 55 t.Run("TestThriftVoidMethodWithDynamicGo", testThriftVoidMethodWithDynamicGo) 56 t.Run("TestThrift2NormalServer", testThrift2NormalServer) 57 t.Run("TestThriftException", testThriftException) 58 t.Run("TestJSONThriftGenericClientClose", testJSONThriftGenericClientClose) 59 t.Run("TestThriftRawBinaryEcho", testThriftRawBinaryEcho) 60 t.Run("TestThriftBase64BinaryEcho", testThriftBase64BinaryEcho) 61 t.Run("TestRegression", testRegression) 62 t.Run("TestJSONThriftGenericClientFinalizer", testJSONThriftGenericClientFinalizer) 63 } 64 65 func testThrift(t *testing.T) { 66 addr := test.GetLocalAddress() 67 svr := initThriftServer(t, addr, new(GenericServiceImpl), "./idl/example.thrift", nil, nil, false) 68 69 // normal way 70 cli := initThriftClient(transport.TTHeader, t, addr, "./idl/example.thrift", nil, nil, false) 71 resp, err := cli.GenericCall(context.Background(), "ExampleMethod", reqMsg, callopt.WithRPCTimeout(100*time.Second)) 72 test.Assert(t, err == nil, err) 73 respStr, ok := resp.(string) 74 test.Assert(t, ok) 75 test.Assert(t, reflect.DeepEqual(gjson.Get(respStr, "Msg").String(), "world"), "world") 76 77 // extend method 78 resp, err = cli.GenericCall(context.Background(), "ExtendMethod", reqExtendMsg, callopt.WithRPCTimeout(100*time.Second)) 79 test.Assert(t, err == nil, err) 80 respStr, ok = resp.(string) 81 test.Assert(t, ok) 82 test.Assert(t, respStr == reqExtendMsg) 83 84 svr.Stop() 85 } 86 87 func testThriftWithDynamicGo(t *testing.T) { 88 addr := test.GetLocalAddress() 89 svr := initThriftServer(t, addr, new(GenericServiceImpl), "./idl/example.thrift", nil, nil, true) 90 91 // write: dynamicgo (amd64 && go1.16), fallback (arm || !go1.16) 92 // read: dynamicgo 93 cli := initThriftClient(transport.TTHeader, t, addr, "./idl/example.thrift", nil, nil, true) 94 resp, err := cli.GenericCall(context.Background(), "ExampleMethod", reqMsg, callopt.WithRPCTimeout(100*time.Second)) 95 test.Assert(t, err == nil, err) 96 respStr, ok := resp.(string) 97 test.Assert(t, ok) 98 test.Assert(t, reflect.DeepEqual(gjson.Get(respStr, "Msg").String(), "world"), "world") 99 100 // client without dynamicgo 101 102 // server side: 103 // write: dynamicgo (amd64 && go1.16), fallback (arm || !go1.16) 104 // read: dynamicgo 105 cli = initThriftClient(transport.TTHeader, t, addr, "./idl/example.thrift", nil, nil, false) 106 resp, err = cli.GenericCall(context.Background(), "ExampleMethod", reqMsg, callopt.WithRPCTimeout(100*time.Second)) 107 test.Assert(t, err == nil, err) 108 respStr, ok = resp.(string) 109 test.Assert(t, ok) 110 test.Assert(t, reflect.DeepEqual(gjson.Get(respStr, "Msg").String(), "world"), "world") 111 112 // server side: 113 // write: dynamicgo (amd64 && go1.16), fallback (arm || !go1.16) 114 // read: fallback 115 cli = initThriftClient(transport.PurePayload, t, addr, "./idl/example.thrift", nil, nil, false) 116 resp, err = cli.GenericCall(context.Background(), "ExampleMethod", reqMsg, callopt.WithRPCTimeout(100*time.Second)) 117 test.Assert(t, err == nil, err) 118 respStr, ok = resp.(string) 119 test.Assert(t, ok) 120 test.Assert(t, reflect.DeepEqual(gjson.Get(respStr, "Msg").String(), "world"), "world") 121 122 svr.Stop() 123 } 124 125 func BenchmarkCompareDynamicGoAndOriginal_Small(b *testing.B) { 126 // small data 127 sobj := getSimpleValue() 128 sout, err := json.Marshal(sobj) 129 if err != nil { 130 panic(err) 131 } 132 simpleJSON := string(sout) 133 fmt.Println("small data size: ", len(simpleJSON)) 134 135 t := testing.T{} 136 137 b.Run("thrift_small_dynamicgo", func(b *testing.B) { 138 addr := test.GetLocalAddress() 139 svr := initThriftServer(&t, addr, new(GenericServiceBenchmarkImpl), "./idl/baseline.thrift", nil, nil, true) 140 cli := initThriftClient(transport.TTHeader, &t, addr, "./idl/baseline.thrift", nil, nil, true) 141 142 resp, err := cli.GenericCall(context.Background(), "SimpleMethod", simpleJSON, callopt.WithRPCTimeout(100*time.Second)) 143 test.Assert(&t, err == nil, err) 144 respStr, ok := resp.(string) 145 test.Assert(&t, ok) 146 test.Assert(&t, gjson.Get(respStr, "I64Field").Int() == math.MaxInt64, math.MaxInt64) 147 148 b.ResetTimer() 149 for i := 0; i < b.N; i++ { 150 cli.GenericCall(context.Background(), "SimpleMethod", simpleJSON, callopt.WithRPCTimeout(100*time.Second)) 151 } 152 svr.Stop() 153 }) 154 155 b.Run("thrift_small_original", func(b *testing.B) { 156 addr := test.GetLocalAddress() 157 svr := initThriftServer(&t, addr, new(GenericServiceBenchmarkImpl), "./idl/baseline.thrift", nil, nil, false) 158 cli := initThriftClient(transport.TTHeader, &t, addr, "./idl/baseline.thrift", nil, nil, false) 159 160 resp, err := cli.GenericCall(context.Background(), "SimpleMethod", simpleJSON, callopt.WithRPCTimeout(100*time.Second)) 161 test.Assert(&t, err == nil, err) 162 respStr, ok := resp.(string) 163 test.Assert(&t, ok) 164 test.Assert(&t, gjson.Get(respStr, "I64Field").Int() == math.MaxInt64, math.MaxInt64) 165 166 b.ResetTimer() 167 for i := 0; i < b.N; i++ { 168 cli.GenericCall(context.Background(), "SimpleMethod", simpleJSON, callopt.WithRPCTimeout(100*time.Second)) 169 } 170 svr.Stop() 171 }) 172 } 173 174 func BenchmarkCompareDynamicGoAndOriginal_Medium(b *testing.B) { 175 // medium data 176 nobj := getNestingValue() 177 nout, err := json.Marshal(nobj) 178 if err != nil { 179 panic(err) 180 } 181 nestingJSON := string(nout) 182 fmt.Println("medium data size: ", len(nestingJSON)) 183 184 t := testing.T{} 185 186 b.Run("thrift_medium_dynamicgo", func(b *testing.B) { 187 addr := test.GetLocalAddress() 188 svr := initThriftServer(&t, addr, new(GenericServiceBenchmarkImpl), "./idl/baseline.thrift", nil, nil, true) 189 cli := initThriftClient(transport.TTHeader, &t, addr, "./idl/baseline.thrift", nil, nil, true) 190 191 resp, err := cli.GenericCall(context.Background(), "NestingMethod", nestingJSON, callopt.WithRPCTimeout(100*time.Second)) 192 test.Assert(&t, err == nil, err) 193 respStr, ok := resp.(string) 194 test.Assert(&t, ok) 195 test.Assert(&t, gjson.Get(respStr, "Double").Float() == math.MaxFloat64, math.MaxFloat64) 196 197 b.ResetTimer() 198 for i := 0; i < b.N; i++ { 199 cli.GenericCall(context.Background(), "NestingMethod", nestingJSON, callopt.WithRPCTimeout(100*time.Second)) 200 } 201 svr.Stop() 202 }) 203 204 b.Run("thrift_medium_original", func(b *testing.B) { 205 addr := test.GetLocalAddress() 206 svr := initThriftServer(&t, addr, new(GenericServiceBenchmarkImpl), "./idl/baseline.thrift", nil, nil, false) 207 cli := initThriftClient(transport.TTHeader, &t, addr, "./idl/baseline.thrift", nil, nil, false) 208 209 resp, err := cli.GenericCall(context.Background(), "NestingMethod", nestingJSON, callopt.WithRPCTimeout(100*time.Second)) 210 test.Assert(&t, err == nil, err) 211 respStr, ok := resp.(string) 212 test.Assert(&t, ok) 213 test.Assert(&t, gjson.Get(respStr, "Double").Float() == math.MaxFloat64, math.MaxFloat64) 214 215 b.ResetTimer() 216 for i := 0; i < b.N; i++ { 217 cli.GenericCall(context.Background(), "NestingMethod", nestingJSON, callopt.WithRPCTimeout(100*time.Second)) 218 } 219 svr.Stop() 220 }) 221 } 222 223 func testThriftPingMethod(t *testing.T) { 224 addr := test.GetLocalAddress() 225 svr := initThriftServer(t, addr, new(GenericServicePingImpl), "./idl/example.thrift", nil, nil, false) 226 227 // normal way 228 cli := initThriftClient(transport.TTHeader, t, addr, "./idl/example.thrift", nil, nil, false) 229 resp, err := cli.GenericCall(context.Background(), "Ping", "hello", callopt.WithRPCTimeout(100*time.Second)) 230 test.Assert(t, err == nil, err) 231 respMap, ok := resp.(string) 232 test.Assert(t, ok) 233 test.Assert(t, reflect.DeepEqual(respMap, "hello"), respMap) 234 235 svr.Stop() 236 } 237 238 func testThriftPingMethodWithDynamicGo(t *testing.T) { 239 addr := test.GetLocalAddress() 240 svr := initThriftServer(t, addr, new(GenericServicePingImpl), "./idl/example.thrift", nil, nil, true) 241 242 // write: dynamicgo (amd64 && go1.16), fallback (arm || !go1.16) 243 // read: dynamicgo 244 cli := initThriftClient(transport.TTHeader, t, addr, "./idl/example.thrift", nil, nil, true) 245 resp, err := cli.GenericCall(context.Background(), "Ping", "hello", callopt.WithRPCTimeout(100*time.Second)) 246 test.Assert(t, err == nil, err) 247 respMap, ok := resp.(string) 248 test.Assert(t, ok) 249 test.Assert(t, reflect.DeepEqual(respMap, "hello"), respMap) 250 251 // client without dynamicgo 252 253 // server side: 254 // write: dynamicgo (amd64 && go1.16), fallback (arm || !go1.16) 255 // read: dynamicgo 256 cli = initThriftClient(transport.TTHeader, t, addr, "./idl/example.thrift", nil, nil, false) 257 resp, err = cli.GenericCall(context.Background(), "Ping", "hello", callopt.WithRPCTimeout(100*time.Second)) 258 test.Assert(t, err == nil, err) 259 respMap, ok = resp.(string) 260 test.Assert(t, ok) 261 test.Assert(t, reflect.DeepEqual(respMap, "hello"), respMap) 262 263 // server side: 264 // write: dynamicgo (amd64 && go1.16), fallback (arm || !go1.16) 265 // read: fallback 266 cli = initThriftClient(transport.PurePayload, t, addr, "./idl/example.thrift", nil, nil, false) 267 resp, err = cli.GenericCall(context.Background(), "Ping", "hello", callopt.WithRPCTimeout(100*time.Second)) 268 test.Assert(t, err == nil, err) 269 respMap, ok = resp.(string) 270 test.Assert(t, ok) 271 test.Assert(t, reflect.DeepEqual(respMap, "hello"), respMap) 272 273 svr.Stop() 274 } 275 276 func testThriftError(t *testing.T) { 277 addr := test.GetLocalAddress() 278 svr := initThriftServer(t, addr, new(GenericServiceErrorImpl), "./idl/example.thrift", nil, nil, false) 279 280 // normal way 281 cli := initThriftClient(transport.TTHeader, t, addr, "./idl/example.thrift", nil, nil, false) 282 _, err := cli.GenericCall(context.Background(), "ExampleMethod", reqMsg, callopt.WithRPCTimeout(100*time.Second)) 283 test.Assert(t, err != nil) 284 test.Assert(t, strings.Contains(err.Error(), errResp), err.Error()) 285 286 // with dynamicgo 287 cli = initThriftClient(transport.TTHeader, t, addr, "./idl/example.thrift", nil, nil, true) 288 _, err = cli.GenericCall(context.Background(), "ExampleMethod", reqMsg, callopt.WithRPCTimeout(100*time.Second)) 289 test.Assert(t, err != nil) 290 test.Assert(t, strings.Contains(err.Error(), errResp), err.Error()) 291 292 svr.Stop() 293 294 // server with dynamicgo 295 addr = test.GetLocalAddress() 296 svr = initThriftServer(t, addr, new(GenericServiceErrorImpl), "./idl/example.thrift", nil, nil, true) 297 298 // normal way 299 cli = initThriftClient(transport.TTHeader, t, addr, "./idl/example.thrift", nil, nil, false) 300 _, err = cli.GenericCall(context.Background(), "ExampleMethod", reqMsg, callopt.WithRPCTimeout(100*time.Second)) 301 test.Assert(t, err != nil) 302 test.Assert(t, strings.Contains(err.Error(), errResp), err.Error()) 303 304 // with dynamicgo 305 cli = initThriftClient(transport.TTHeader, t, addr, "./idl/example.thrift", nil, nil, true) 306 _, err = cli.GenericCall(context.Background(), "ExampleMethod", reqMsg, callopt.WithRPCTimeout(100*time.Second)) 307 test.Assert(t, err != nil) 308 test.Assert(t, strings.Contains(err.Error(), errResp), err.Error()) 309 310 svr.Stop() 311 } 312 313 func testThriftOnewayMethod(t *testing.T) { 314 addr := test.GetLocalAddress() 315 svr := initThriftServer(t, addr, new(GenericServiceOnewayImpl), "./idl/example.thrift", nil, nil, false) 316 317 // normal way 318 cli := initThriftClient(transport.TTHeader, t, addr, "./idl/example.thrift", nil, nil, false) 319 resp, err := cli.GenericCall(context.Background(), "Oneway", "hello", callopt.WithRPCTimeout(100*time.Second)) 320 test.Assert(t, err == nil, err) 321 test.Assert(t, resp == nil) 322 323 svr.Stop() 324 } 325 326 func testThriftOnewayMethodWithDynamicGo(t *testing.T) { 327 addr := test.GetLocalAddress() 328 svr := initThriftServer(t, addr, new(GenericServiceOnewayImpl), "./idl/example.thrift", nil, nil, true) 329 330 // write: dynamicgo (amd64 && go1.16), fallback (arm || !go1.16) 331 // read: dynamicgo 332 cli := initThriftClient(transport.TTHeader, t, addr, "./idl/example.thrift", nil, nil, true) 333 resp, err := cli.GenericCall(context.Background(), "Oneway", "hello", callopt.WithRPCTimeout(100*time.Second)) 334 test.Assert(t, err == nil, err) 335 test.Assert(t, resp == nil) 336 337 // client without dynamicgo 338 339 // server side: 340 // write: dynamicgo (amd64 && go1.16), fallback (arm || !go1.16) 341 // read: dynamicgo 342 cli = initThriftClient(transport.TTHeader, t, addr, "./idl/example.thrift", nil, nil, false) 343 resp, err = cli.GenericCall(context.Background(), "Oneway", "hello", callopt.WithRPCTimeout(100*time.Second)) 344 test.Assert(t, err == nil, err) 345 test.Assert(t, resp == nil) 346 347 // server side: 348 // write: dynamicgo (amd64 && go1.16), fallback (arm || !go1.16) 349 // read: fallback 350 cli = initThriftClient(transport.PurePayload, t, addr, "./idl/example.thrift", nil, nil, false) 351 resp, err = cli.GenericCall(context.Background(), "Oneway", "hello", callopt.WithRPCTimeout(100*time.Second)) 352 test.Assert(t, err == nil, err) 353 test.Assert(t, resp == nil) 354 355 svr.Stop() 356 } 357 358 func testThriftVoidMethod(t *testing.T) { 359 addr := test.GetLocalAddress() 360 svr := initThriftServer(t, addr, new(GenericServiceVoidImpl), "./idl/example.thrift", nil, nil, false) 361 362 // normal way 363 cli := initThriftClient(transport.TTHeader, t, addr, "./idl/example.thrift", nil, nil, false) 364 resp, err := cli.GenericCall(context.Background(), "Void", "hello", callopt.WithRPCTimeout(100*time.Second)) 365 test.Assert(t, err == nil, err) 366 test.Assert(t, resp == descriptor.Void{}) 367 368 svr.Stop() 369 370 time.Sleep(50 * time.Millisecond) 371 svr = initThriftServer(t, addr, new(GenericServiceVoidWithStringImpl), "./idl/example.thrift", nil, nil, false) 372 resp, err = cli.GenericCall(context.Background(), "Void", "hello", callopt.WithRPCTimeout(100*time.Second)) 373 test.Assert(t, err == nil, err) 374 test.Assert(t, resp == descriptor.Void{}) 375 376 svr.Stop() 377 } 378 379 func testThriftVoidMethodWithDynamicGo(t *testing.T) { 380 addr := test.GetLocalAddress() 381 svr := initThriftServer(t, addr, new(GenericServiceVoidImpl), "./idl/example.thrift", nil, nil, true) 382 383 // write: dynamicgo (amd64 && go1.16), fallback (arm || !go1.16) 384 // read: dynamicgo 385 cli := initThriftClient(transport.TTHeader, t, addr, "./idl/example.thrift", nil, nil, true) 386 resp, err := cli.GenericCall(context.Background(), "Void", "hello", callopt.WithRPCTimeout(100*time.Second)) 387 test.Assert(t, err == nil, err) 388 test.Assert(t, resp == descriptor.Void{}) 389 390 // client without dynamicgo 391 392 // server side: 393 // write: dynamicgo (amd64 && go1.16), fallback (arm || !go1.16) 394 // read: dynamicgo 395 cli = initThriftClient(transport.TTHeader, t, addr, "./idl/example.thrift", nil, nil, false) 396 resp, err = cli.GenericCall(context.Background(), "Void", "hello", callopt.WithRPCTimeout(100*time.Second)) 397 test.Assert(t, err == nil, err) 398 test.Assert(t, resp == descriptor.Void{}) 399 400 // server side: 401 // write: dynamicgo (amd64 && go1.16), fallback (arm || !go1.16) 402 // read: fallback 403 cli = initThriftClient(transport.PurePayload, t, addr, "./idl/example.thrift", nil, nil, false) 404 resp, err = cli.GenericCall(context.Background(), "Void", "hello", callopt.WithRPCTimeout(100*time.Second)) 405 test.Assert(t, err == nil, err) 406 test.Assert(t, resp == descriptor.Void{}) 407 408 svr.Stop() 409 410 time.Sleep(50 * time.Millisecond) 411 svr = initThriftServer(t, addr, new(GenericServiceVoidWithStringImpl), "./idl/example.thrift", nil, nil, true) 412 resp, err = cli.GenericCall(context.Background(), "Void", "hello", callopt.WithRPCTimeout(100*time.Second)) 413 test.Assert(t, err == nil, err) 414 test.Assert(t, resp == descriptor.Void{}) 415 416 svr.Stop() 417 } 418 419 func testThrift2NormalServer(t *testing.T) { 420 addr := test.GetLocalAddress() 421 svr := initMockServer(t, new(mockImpl), addr) 422 423 // client without dynamicgo 424 cli := initThriftMockClient(t, transport.TTHeader, false, addr) 425 _, err := cli.GenericCall(context.Background(), "Test", mockReq, callopt.WithRPCTimeout(100*time.Second)) 426 test.Assert(t, err == nil, err) 427 428 // client with dynamicgo 429 cli = initThriftMockClient(t, transport.TTHeader, true, addr) 430 _, err = cli.GenericCall(context.Background(), "Test", mockReq, callopt.WithRPCTimeout(100*time.Second)) 431 test.Assert(t, err == nil, err) 432 433 svr.Stop() 434 } 435 436 func testThriftException(t *testing.T) { 437 addr := test.GetLocalAddress() 438 svr := initMockServer(t, new(mockImpl), addr) 439 440 // client without dynamicgo 441 cli := initThriftMockClient(t, transport.TTHeader, false, addr) 442 443 _, err := cli.GenericCall(context.Background(), "ExceptionTest", mockReq, callopt.WithRPCTimeout(100*time.Second)) 444 test.Assert(t, err != nil, err) 445 test.DeepEqual(t, err.Error(), `remote or network error[remote]: map[string]interface {}{"code":400, "msg":"this is an exception"}`) 446 447 // client with dynaimcgo 448 cli = initThriftMockClient(t, transport.TTHeader, true, addr) 449 450 _, err = cli.GenericCall(context.Background(), "ExceptionTest", mockReq, callopt.WithRPCTimeout(100*time.Second)) 451 test.Assert(t, err != nil, err) 452 test.DeepEqual(t, err.Error(), `remote or network error[remote]: {"code":400,"msg":"this is an exception"}`) 453 454 svr.Stop() 455 } 456 457 func testThriftRawBinaryEcho(t *testing.T) { 458 var opts []generic.Option 459 opts = append(opts, generic.WithCustomDynamicGoConvOpts(&conv.Options{WriteRequireField: true, WriteDefaultField: true, NoBase64Binary: true})) 460 addr := test.GetLocalAddress() 461 svr := initThriftServer(t, addr, new(GenericServiceBinaryEchoImpl), "./idl/binary_echo.thrift", opts, &(&struct{ x bool }{false}).x, false) 462 463 // client without dynamicgo 464 cli := initThriftClient(transport.TTHeader, t, addr, "./idl/binary_echo.thrift", opts, &(&struct{ x bool }{false}).x, false) 465 466 req := "{\"msg\":\"" + mockMyMsg + "\", \"got_base64\":false}" 467 resp, err := cli.GenericCall(context.Background(), "BinaryEcho", req, callopt.WithRPCTimeout(100*time.Second)) 468 test.Assert(t, err == nil, err) 469 sr, ok := resp.(string) 470 test.Assert(t, ok) 471 test.Assert(t, strings.Contains(sr, mockMyMsg)) 472 473 // client with dynamicgo 474 cli = initThriftClient(transport.PurePayload, t, addr, "./idl/binary_echo.thrift", opts, &(&struct{ x bool }{false}).x, true) 475 476 req = "{\"msg\":\"" + mockMyMsg + "\", \"got_base64\":false}" 477 resp, err = cli.GenericCall(context.Background(), "BinaryEcho", req, callopt.WithRPCTimeout(100*time.Second)) 478 test.Assert(t, err == nil, err) 479 sr, ok = resp.(string) 480 test.Assert(t, ok) 481 test.Assert(t, strings.Contains(sr, mockMyMsg)) 482 483 svr.Stop() 484 } 485 486 func testThriftBase64BinaryEcho(t *testing.T) { 487 var opts []generic.Option 488 opts = append(opts, generic.WithCustomDynamicGoConvOpts(&conv.Options{WriteRequireField: true, WriteDefaultField: true, NoBase64Binary: false})) 489 addr := test.GetLocalAddress() 490 svr := initThriftServer(t, addr, new(GenericServiceBinaryEchoImpl), "./idl/binary_echo.thrift", opts, nil, false) 491 492 // client without dynamicgo 493 cli := initThriftClient(transport.TTHeader, t, addr, "./idl/binary_echo.thrift", opts, nil, false) 494 495 base64MockMyMsg := base64.StdEncoding.EncodeToString([]byte(mockMyMsg)) 496 req := "{\"msg\":\"" + base64MockMyMsg + "\", \"got_base64\":true}" 497 resp, err := cli.GenericCall(context.Background(), "BinaryEcho", req, callopt.WithRPCTimeout(100*time.Second)) 498 test.Assert(t, err == nil, err) 499 sr, ok := resp.(string) 500 test.Assert(t, ok) 501 test.Assert(t, strings.Contains(sr, base64MockMyMsg)) 502 503 // client with dynamicgo 504 cli = initThriftClient(transport.PurePayload, t, addr, "./idl/binary_echo.thrift", opts, nil, true) 505 506 base64MockMyMsg = base64.StdEncoding.EncodeToString([]byte(mockMyMsg)) 507 req = "{\"msg\":\"" + base64MockMyMsg + "\", \"got_base64\":true}" 508 resp, err = cli.GenericCall(context.Background(), "BinaryEcho", req, callopt.WithRPCTimeout(100*time.Second)) 509 test.Assert(t, err == nil, err) 510 sr, ok = resp.(string) 511 test.Assert(t, ok) 512 test.Assert(t, strings.Contains(sr, base64MockMyMsg)) 513 514 svr.Stop() 515 } 516 517 func testRegression(t *testing.T) { 518 addr := test.GetLocalAddress() 519 svr := initThriftServer(t, addr, new(GenericRegressionImpl), "./idl/example.thrift", nil, nil, false) 520 521 // normal way 522 cli := initThriftClient(transport.TTHeader, t, addr, "./idl/example.thrift", nil, nil, false) 523 resp, err := cli.GenericCall(context.Background(), "ExampleMethod", reqRegression, callopt.WithRPCTimeout(100*time.Second)) 524 test.Assert(t, err == nil, err) 525 respStr, ok := resp.(string) 526 test.Assert(t, ok) 527 test.Assert(t, gjson.Get(respStr, "num").Type == gjson.String) 528 test.Assert(t, gjson.Get(respStr, "num").String() == "64") 529 test.Assert(t, gjson.Get(respStr, "I8").Type == gjson.Number) 530 test.Assert(t, gjson.Get(respStr, "I8").Int() == int64(8)) 531 test.Assert(t, gjson.Get(respStr, "I16").Type == gjson.Number) 532 test.Assert(t, gjson.Get(respStr, "I32").Type == gjson.Number) 533 test.Assert(t, gjson.Get(respStr, "I64").Type == gjson.Number) 534 test.Assert(t, gjson.Get(respStr, "Double").Type == gjson.Number) 535 test.Assert(t, gjson.Get(respStr, "Double").Float() == 12.3) 536 537 svr.Stop() 538 539 addr = test.GetLocalAddress() 540 svr = initThriftServer(t, addr, new(GenericRegressionImpl), "./idl/example.thrift", nil, nil, true) 541 542 // dynamicgo way 543 cli = initThriftClient(transport.TTHeader, t, addr, "./idl/example.thrift", nil, nil, true) 544 resp, err = cli.GenericCall(context.Background(), "ExampleMethod", reqRegression, callopt.WithRPCTimeout(100*time.Second)) 545 test.Assert(t, err == nil, err) 546 respStr, ok = resp.(string) 547 test.Assert(t, ok) 548 test.Assert(t, gjson.Get(respStr, "num").Type == gjson.String) 549 test.Assert(t, gjson.Get(respStr, "num").String() == "64") 550 test.Assert(t, gjson.Get(respStr, "I8").Type == gjson.Number) 551 test.Assert(t, gjson.Get(respStr, "I8").Int() == int64(8)) 552 test.Assert(t, gjson.Get(respStr, "I16").Type == gjson.Number) 553 test.Assert(t, gjson.Get(respStr, "I32").Type == gjson.Number) 554 test.Assert(t, gjson.Get(respStr, "I64").Type == gjson.Number) 555 test.Assert(t, gjson.Get(respStr, "Double").Type == gjson.Number) 556 test.Assert(t, gjson.Get(respStr, "Double").Float() == 12.3) 557 558 svr.Stop() 559 } 560 561 func initThriftMockClient(t *testing.T, tp transport.Protocol, enableDynamicGo bool, address string) genericclient.Client { 562 var p generic.DescriptorProvider 563 var err error 564 if enableDynamicGo { 565 p, err = generic.NewThriftFileProviderWithDynamicGo("./idl/mock.thrift") 566 } else { 567 p, err = generic.NewThriftFileProvider("./idl/mock.thrift") 568 } 569 test.Assert(t, err == nil) 570 g, err := generic.JSONThriftGeneric(p) 571 test.Assert(t, err == nil) 572 cli := newGenericClient(tp, "destServiceName", g, address) 573 test.Assert(t, err == nil) 574 return cli 575 } 576 577 func initThriftClient(tp transport.Protocol, t *testing.T, addr, idl string, opts []generic.Option, base64Binary *bool, enableDynamicGo bool) genericclient.Client { 578 var p generic.DescriptorProvider 579 var err error 580 if enableDynamicGo { 581 p, err = generic.NewThriftFileProviderWithDynamicGo(idl) 582 } else { 583 p, err = generic.NewThriftFileProvider(idl) 584 } 585 test.Assert(t, err == nil) 586 g, err := generic.JSONThriftGeneric(p, opts...) 587 test.Assert(t, err == nil) 588 if base64Binary != nil { 589 generic.SetBinaryWithBase64(g, *base64Binary) 590 } 591 cli := newGenericClient(tp, "destServiceName", g, addr) 592 test.Assert(t, err == nil) 593 return cli 594 } 595 596 func initThriftServer(t *testing.T, address string, handler generic.Service, idlPath string, opts []generic.Option, base64Binary *bool, enableDynamicGo bool) server.Server { 597 addr, _ := net.ResolveTCPAddr("tcp", address) 598 var p generic.DescriptorProvider 599 var err error 600 if enableDynamicGo { 601 p, err = generic.NewThriftFileProviderWithDynamicGo(idlPath) 602 } else { 603 p, err = generic.NewThriftFileProvider(idlPath) 604 } 605 test.Assert(t, err == nil) 606 g, err := generic.JSONThriftGeneric(p, opts...) 607 test.Assert(t, err == nil) 608 if base64Binary != nil { 609 generic.SetBinaryWithBase64(g, *base64Binary) 610 } 611 svr := newGenericServer(g, addr, handler) 612 test.Assert(t, err == nil) 613 return svr 614 } 615 616 func initMockServer(t *testing.T, handler kt.Mock, address string) server.Server { 617 addr, _ := net.ResolveTCPAddr("tcp", address) 618 svr := newMockServer(handler, addr) 619 test.WaitServerStart(addr.String()) 620 return svr 621 } 622 623 func testJSONThriftGenericClientClose(t *testing.T) { 624 debug.SetGCPercent(-1) 625 defer debug.SetGCPercent(100) 626 627 var ms runtime.MemStats 628 runtime.ReadMemStats(&ms) 629 630 t.Logf("Before new clients, allocation: %f Mb, Number of allocation: %d\n", mb(ms.HeapAlloc), ms.HeapObjects) 631 632 clientCnt := 1000 633 clis := make([]genericclient.Client, clientCnt) 634 for i := 0; i < clientCnt; i++ { 635 p, err := generic.NewThriftFileProvider("./idl/mock.thrift") 636 test.Assertf(t, err == nil, "generic NewThriftFileProvider failed, err=%v", err) 637 g, err := generic.JSONThriftGeneric(p) 638 test.Assertf(t, err == nil, "generic JSONThriftGeneric failed, err=%v", err) 639 clis[i] = newGenericClient(transport.TTHeader, "destServiceName", g, "127.0.0.1:8129") 640 } 641 642 runtime.ReadMemStats(&ms) 643 preHeapAlloc, preHeapObjects := mb(ms.HeapAlloc), ms.HeapObjects 644 t.Logf("After new clients, allocation: %f Mb, Number of allocation: %d\n", preHeapAlloc, preHeapObjects) 645 646 for _, cli := range clis { 647 _ = cli.Close() 648 } 649 runtime.GC() 650 runtime.ReadMemStats(&ms) 651 afterGCHeapAlloc, afterGCHeapObjects := mb(ms.HeapAlloc), ms.HeapObjects 652 t.Logf("After close clients and GC be executed, allocation: %f Mb, Number of allocation: %d\n", afterGCHeapAlloc, afterGCHeapObjects) 653 test.Assert(t, afterGCHeapAlloc < preHeapAlloc && afterGCHeapObjects < preHeapObjects) 654 655 // Trigger the finalizer of kclient be executed 656 time.Sleep(200 * time.Millisecond) // ensure the finalizer be executed 657 runtime.GC() 658 runtime.ReadMemStats(&ms) 659 secondGCHeapAlloc, secondGCHeapObjects := mb(ms.HeapAlloc), ms.HeapObjects 660 t.Logf("After second GC, allocation: %f Mb, Number of allocation: %d\n", secondGCHeapAlloc, secondGCHeapObjects) 661 test.Assert(t, secondGCHeapAlloc/2 < afterGCHeapAlloc && secondGCHeapObjects/2 < afterGCHeapObjects) 662 } 663 664 func testJSONThriftGenericClientFinalizer(t *testing.T) { 665 debug.SetGCPercent(-1) 666 defer debug.SetGCPercent(100) 667 668 var ms runtime.MemStats 669 runtime.ReadMemStats(&ms) 670 t.Logf("Before new clients, allocation: %f Mb, Number of allocation: %d\n", mb(ms.HeapAlloc), ms.HeapObjects) 671 672 clientCnt := 1000 673 clis := make([]genericclient.Client, clientCnt) 674 for i := 0; i < clientCnt; i++ { 675 p, err := generic.NewThriftFileProvider("./idl/mock.thrift") 676 test.Assert(t, err == nil, "generic NewThriftFileProvider failed, err=%v", err) 677 g, err := generic.JSONThriftGeneric(p) 678 test.Assert(t, err == nil, "generic JSONThriftGeneric failed, err=%v", err) 679 clis[i] = newGenericClient(transport.TTHeader, "destServiceName", g, "127.0.0.1:8130") 680 } 681 682 runtime.ReadMemStats(&ms) 683 t.Logf("After new clients, allocation: %f Mb, Number of allocation: %d\n", mb(ms.HeapAlloc), ms.HeapObjects) 684 685 runtime.GC() 686 runtime.ReadMemStats(&ms) 687 firstGCHeapAlloc, firstGCHeapObjects := mb(ms.HeapAlloc), ms.HeapObjects 688 t.Logf("After first GC, allocation: %f Mb, Number of allocation: %d\n", firstGCHeapAlloc, firstGCHeapObjects) 689 690 // Trigger the finalizer of generic client be executed 691 time.Sleep(200 * time.Millisecond) // ensure the finalizer be executed 692 runtime.GC() 693 runtime.ReadMemStats(&ms) 694 secondGCHeapAlloc, secondGCHeapObjects := mb(ms.HeapAlloc), ms.HeapObjects 695 t.Logf("After second GC, allocation: %f Mb, Number of allocation: %d\n", secondGCHeapAlloc, secondGCHeapObjects) 696 test.Assert(t, secondGCHeapAlloc < firstGCHeapAlloc && secondGCHeapObjects < firstGCHeapObjects) 697 698 // Trigger the finalizer of kClient be executed 699 time.Sleep(200 * time.Millisecond) // ensure the finalizer be executed 700 runtime.GC() 701 runtime.ReadMemStats(&ms) 702 thirddGCHeapAlloc, thirdGCHeapObjects := mb(ms.HeapAlloc), ms.HeapObjects 703 t.Logf("After third GC, allocation: %f Mb, Number of allocation: %d\n", thirddGCHeapAlloc, thirdGCHeapObjects) 704 test.Assert(t, thirddGCHeapAlloc < secondGCHeapAlloc/2 && thirdGCHeapObjects < secondGCHeapObjects/2) 705 } 706 707 func mb(byteSize uint64) float32 { 708 return float32(byteSize) / float32(1024*1024) 709 }