github.com/cloudwego/kitex@v0.9.0/pkg/generic/map_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 "net" 23 "reflect" 24 "runtime" 25 "runtime/debug" 26 "strings" 27 "testing" 28 "time" 29 30 "github.com/cloudwego/kitex/client/callopt" 31 "github.com/cloudwego/kitex/client/genericclient" 32 kt "github.com/cloudwego/kitex/internal/mocks/thrift" 33 "github.com/cloudwego/kitex/internal/test" 34 "github.com/cloudwego/kitex/pkg/generic" 35 "github.com/cloudwego/kitex/pkg/generic/descriptor" 36 "github.com/cloudwego/kitex/server" 37 ) 38 39 func TestThrift(t *testing.T) { 40 addr := test.GetLocalAddress() 41 svr := initThriftServer(t, addr, new(GenericServiceImpl), false, false) 42 cli := initThriftClient(t, addr, false, false) 43 44 cases := []struct { 45 reqMsg interface{} 46 wantResp interface{} 47 }{ 48 { 49 reqMsg: map[string]interface{}{ 50 "Msg": "hello", 51 "TestList": []interface{}{ 52 map[string]interface{}{ 53 "Bar": "foo", 54 }, 55 }, 56 "TestMap": map[interface{}]interface{}{ 57 "l1": map[string]interface{}{ 58 "Bar": "foo", 59 }, 60 }, 61 "StrList": []interface{}{ 62 "123", "456", 63 }, 64 "I64List": []interface{}{ 65 int64(123), int64(456), 66 }, 67 "B": true, 68 }, 69 wantResp: map[string]interface{}{ 70 "Msg": "hello", 71 "Foo": int32(0), 72 "TestList": []interface{}{ 73 map[string]interface{}{ 74 "Bar": "foo", 75 }, 76 }, 77 "TestMap": map[interface{}]interface{}{ 78 "l1": map[string]interface{}{ 79 "Bar": "foo", 80 }, 81 }, 82 "StrList": []interface{}{ 83 "123", "456", 84 }, 85 "I64List": []interface{}{ 86 int64(123), int64(456), 87 }, 88 "B": true, 89 "Base": map[string]interface{}{ 90 "LogID": "", 91 "Caller": "", 92 "Addr": "", 93 "Client": "", 94 }, 95 }, 96 }, 97 { 98 reqMsg: map[string]interface{}{ 99 "Msg": "hello", 100 "TestList": []interface{}{ 101 map[string]interface{}{ 102 "Bar": "foo", 103 }, 104 nil, 105 }, 106 "TestMap": map[interface{}]interface{}{ 107 "l1": map[string]interface{}{ 108 "Bar": "foo", 109 }, 110 "l2": nil, 111 }, 112 "StrList": []interface{}{ 113 "123", nil, 114 }, 115 "I64List": []interface{}{ 116 int64(123), nil, 117 }, 118 "B": nil, 119 }, 120 wantResp: map[string]interface{}{ 121 "Msg": "hello", 122 "Foo": int32(0), 123 "TestList": []interface{}{ 124 map[string]interface{}{ 125 "Bar": "foo", 126 }, 127 map[string]interface{}{ 128 "Bar": "", 129 }, 130 }, 131 "TestMap": map[interface{}]interface{}{ 132 "l1": map[string]interface{}{ 133 "Bar": "foo", 134 }, 135 "l2": map[string]interface{}{ 136 "Bar": "", 137 }, 138 }, 139 "StrList": []interface{}{ 140 "123", "", 141 }, 142 "I64List": []interface{}{ 143 int64(123), int64(0), 144 }, 145 "B": false, 146 "Base": map[string]interface{}{ 147 "LogID": "", 148 "Caller": "", 149 "Addr": "", 150 "Client": "", 151 }, 152 }, 153 }, 154 { 155 reqMsg: map[string]interface{}{ 156 "Msg": "hello", 157 "TestList": []interface{}{ 158 nil, 159 }, 160 "TestMap": map[interface{}]interface{}{ 161 "l2": nil, 162 }, 163 "StrList": []interface{}{ 164 nil, 165 }, 166 "I64List": []interface{}{ 167 nil, 168 }, 169 "B": nil, 170 }, 171 wantResp: map[string]interface{}{ 172 "Msg": "hello", 173 "Foo": int32(0), 174 "TestList": []interface{}{ 175 map[string]interface{}{ 176 "Bar": "", 177 }, 178 }, 179 "TestMap": map[interface{}]interface{}{ 180 "l2": map[string]interface{}{ 181 "Bar": "", 182 }, 183 }, 184 "StrList": []interface{}{ 185 "", 186 }, 187 "I64List": []interface{}{ 188 int64(0), 189 }, 190 "B": false, 191 "Base": map[string]interface{}{ 192 "LogID": "", 193 "Caller": "", 194 "Addr": "", 195 "Client": "", 196 }, 197 }, 198 }, 199 { 200 reqMsg: map[string]interface{}{ 201 "Msg": "hello", 202 "TestList": []interface{}{ 203 map[string]interface{}{ 204 "Bar": "foo", 205 }, 206 nil, 207 }, 208 "TestMap": map[interface{}]interface{}{ 209 "l1": map[string]interface{}{ 210 "Bar": "foo", 211 }, 212 "l2": nil, 213 }, 214 "StrList": []interface{}{ 215 "123", nil, 216 }, 217 "I64List": []interface{}{ 218 float64(123), nil, float64(456), 219 }, 220 "B": nil, 221 }, 222 wantResp: map[string]interface{}{ 223 "Msg": "hello", 224 "Foo": int32(0), 225 "TestList": []interface{}{ 226 map[string]interface{}{ 227 "Bar": "foo", 228 }, 229 map[string]interface{}{ 230 "Bar": "", 231 }, 232 }, 233 "TestMap": map[interface{}]interface{}{ 234 "l1": map[string]interface{}{ 235 "Bar": "foo", 236 }, 237 "l2": map[string]interface{}{ 238 "Bar": "", 239 }, 240 }, 241 "StrList": []interface{}{ 242 "123", "", 243 }, 244 "I64List": []interface{}{ 245 int64(123), int64(0), int64(456), 246 }, 247 "B": false, 248 "Base": map[string]interface{}{ 249 "LogID": "", 250 "Caller": "", 251 "Addr": "", 252 "Client": "", 253 }, 254 }, 255 }, 256 } 257 for _, tcase := range cases { 258 resp, err := cli.GenericCall(context.Background(), "ExampleMethod", tcase.reqMsg, callopt.WithRPCTimeout(100*time.Second)) 259 test.Assert(t, err == nil, err) 260 respMap, ok := resp.(map[string]interface{}) 261 test.Assert(t, ok) 262 test.Assert(t, reflect.DeepEqual(respMap, tcase.wantResp), respMap) 263 } 264 svr.Stop() 265 } 266 267 func TestThriftPingMethod(t *testing.T) { 268 addr := test.GetLocalAddress() 269 svr := initThriftServer(t, addr, new(GenericServicePingImpl), false, false) 270 cli := initThriftClient(t, addr, false, false) 271 272 resp, err := cli.GenericCall(context.Background(), "Ping", "hello", callopt.WithRPCTimeout(100*time.Second)) 273 test.Assert(t, err == nil, err) 274 respMap, ok := resp.(string) 275 test.Assert(t, ok) 276 test.Assert(t, reflect.DeepEqual(respMap, "hello"), respMap) 277 svr.Stop() 278 } 279 280 func TestThriftError(t *testing.T) { 281 addr := test.GetLocalAddress() 282 svr := initThriftServer(t, addr, new(GenericServiceErrorImpl), false, false) 283 cli := initThriftClient(t, addr, false, false) 284 285 _, err := cli.GenericCall(context.Background(), "ExampleMethod", reqMsg, callopt.WithRPCTimeout(100*time.Second)) 286 test.Assert(t, err != nil) 287 test.Assert(t, strings.Contains(err.Error(), errResp), err.Error()) 288 svr.Stop() 289 } 290 291 func TestThriftOnewayMethod(t *testing.T) { 292 addr := test.GetLocalAddress() 293 svr := initThriftServer(t, addr, new(GenericServiceOnewayImpl), false, false) 294 cli := initThriftClient(t, addr, false, false) 295 296 resp, err := cli.GenericCall(context.Background(), "Oneway", "hello", callopt.WithRPCTimeout(100*time.Second)) 297 test.Assert(t, err == nil, err) 298 test.Assert(t, resp == nil) 299 svr.Stop() 300 } 301 302 func TestThriftVoidMethod(t *testing.T) { 303 addr := test.GetLocalAddress() 304 svr := initThriftServer(t, addr, new(GenericServiceVoidImpl), false, false) 305 cli := initThriftClient(t, addr, false, false) 306 307 resp, err := cli.GenericCall(context.Background(), "Void", "hello", callopt.WithRPCTimeout(100*time.Second)) 308 test.Assert(t, err == nil, err) 309 test.Assert(t, resp == descriptor.Void{}) 310 svr.Stop() 311 } 312 313 func TestBase64Binary(t *testing.T) { 314 addr := test.GetLocalAddress() 315 svr := initThriftServer(t, addr, new(GenericServiceWithBase64Binary), true, false) 316 cli := initThriftClient(t, addr, true, false) 317 318 reqMsg = map[string]interface{}{"BinaryMsg": []byte("hello")} 319 resp, err := cli.GenericCall(context.Background(), "ExampleMethod", reqMsg, callopt.WithRPCTimeout(100*time.Second)) 320 test.Assert(t, err == nil, err) 321 gr, ok := resp.(map[string]interface{}) 322 test.Assert(t, ok) 323 test.Assert(t, reflect.DeepEqual(gr["BinaryMsg"], base64.StdEncoding.EncodeToString([]byte("hello")))) 324 325 svr.Stop() 326 } 327 328 func TestBinaryWithByteSlice(t *testing.T) { 329 addr := test.GetLocalAddress() 330 svr := initThriftServer(t, addr, new(GenericServiceWithByteSliceImpl), false, true) 331 cli := initThriftClient(t, addr, false, false) 332 333 reqMsg = map[string]interface{}{"BinaryMsg": []byte("hello")} 334 resp, err := cli.GenericCall(context.Background(), "ExampleMethod", reqMsg, callopt.WithRPCTimeout(100*time.Second)) 335 test.Assert(t, err == nil, err) 336 gr, ok := resp.(map[string]interface{}) 337 test.Assert(t, ok) 338 test.Assert(t, reflect.DeepEqual(gr["BinaryMsg"], "hello")) 339 svr.Stop() 340 } 341 342 func TestBinaryWithBase64AndByteSlice(t *testing.T) { 343 addr := test.GetLocalAddress() 344 svr := initThriftServer(t, addr, new(GenericServiceWithByteSliceImpl), true, true) 345 cli := initThriftClient(t, addr, true, true) 346 347 reqMsg = map[string]interface{}{"BinaryMsg": []byte("hello")} 348 resp, err := cli.GenericCall(context.Background(), "ExampleMethod", reqMsg, callopt.WithRPCTimeout(100*time.Second)) 349 test.Assert(t, err == nil, err) 350 gr, ok := resp.(map[string]interface{}) 351 test.Assert(t, ok) 352 test.Assert(t, reflect.DeepEqual(gr["BinaryMsg"], []byte("hello"))) 353 svr.Stop() 354 } 355 356 func TestThrift2NormalServer(t *testing.T) { 357 addr := test.GetLocalAddress() 358 svr := initMockServer(t, new(mockImpl), addr) 359 cli := initThriftMockClient(t, addr) 360 361 _, err := cli.GenericCall(context.Background(), "Test", mockReq, callopt.WithRPCTimeout(100*time.Second)) 362 test.Assert(t, err == nil, err) 363 svr.Stop() 364 } 365 366 func initThriftMockClient(t *testing.T, address string) genericclient.Client { 367 p, err := generic.NewThriftFileProvider("./idl/mock.thrift") 368 test.Assert(t, err == nil) 369 g, err := generic.MapThriftGeneric(p) 370 test.Assert(t, err == nil) 371 cli := newGenericClient("destServiceName", g, address) 372 test.Assert(t, err == nil) 373 return cli 374 } 375 376 func initThriftClient(t *testing.T, addr string, base64Binary, byteSlice bool) genericclient.Client { 377 return initThriftClientByIDL(t, addr, "./idl/example.thrift", base64Binary, byteSlice) 378 } 379 380 func initThriftClientByIDL(t *testing.T, addr, idl string, base64Binary, byteSlice bool) genericclient.Client { 381 p, err := generic.NewThriftFileProvider(idl) 382 test.Assert(t, err == nil) 383 g, err := generic.MapThriftGeneric(p) 384 test.Assert(t, err == nil) 385 err = generic.SetBinaryWithBase64(g, base64Binary) 386 test.Assert(t, err == nil) 387 err = generic.SetBinaryWithByteSlice(g, byteSlice) 388 test.Assert(t, err == nil) 389 cli := newGenericClient("destServiceName", g, addr) 390 test.Assert(t, err == nil) 391 return cli 392 } 393 394 func initThriftServer(t *testing.T, address string, handler generic.Service, base64Binary, byteSlice bool) server.Server { 395 addr, _ := net.ResolveTCPAddr("tcp", address) 396 p, err := generic.NewThriftFileProvider("./idl/example.thrift") 397 test.Assert(t, err == nil) 398 g, err := generic.MapThriftGeneric(p) 399 test.Assert(t, err == nil) 400 err = generic.SetBinaryWithBase64(g, base64Binary) 401 test.Assert(t, err == nil) 402 err = generic.SetBinaryWithByteSlice(g, byteSlice) 403 test.Assert(t, err == nil) 404 svr := newGenericServer(g, addr, handler) 405 test.Assert(t, err == nil) 406 return svr 407 } 408 409 func initMockServer(t *testing.T, handler kt.Mock, address string) server.Server { 410 addr, _ := net.ResolveTCPAddr("tcp", address) 411 svr := newMockServer(handler, addr) 412 return svr 413 } 414 415 func TestMapThriftGenericClientClose(t *testing.T) { 416 debug.SetGCPercent(-1) 417 defer debug.SetGCPercent(100) 418 419 var ms runtime.MemStats 420 runtime.ReadMemStats(&ms) 421 422 t.Logf("Before new clients, allocation: %f Mb, Number of allocation: %d\n", mb(ms.HeapAlloc), ms.HeapObjects) 423 424 clientCnt := 1000 425 clis := make([]genericclient.Client, clientCnt) 426 for i := 0; i < clientCnt; i++ { 427 p, err := generic.NewThriftFileProvider("./idl/mock.thrift") 428 test.Assert(t, err == nil, "generic NewThriftFileProvider failed, err=%v", err) 429 g, err := generic.MapThriftGeneric(p) 430 test.Assert(t, err == nil, "generic MapThriftGeneric failed, err=%v", err) 431 clis[i] = newGenericClient("destServiceName", g, "127.0.0.1:9020") 432 } 433 434 runtime.ReadMemStats(&ms) 435 preHeapAlloc, preHeapObjects := mb(ms.HeapAlloc), ms.HeapObjects 436 t.Logf("After new clients, allocation: %f Mb, Number of allocation: %d\n", preHeapAlloc, preHeapObjects) 437 438 for _, cli := range clis { 439 _ = cli.Close() 440 } 441 runtime.GC() 442 runtime.ReadMemStats(&ms) 443 afterGCHeapAlloc, afterGCHeapObjects := mb(ms.HeapAlloc), ms.HeapObjects 444 t.Logf("After close clients and GC be executed, allocation: %f Mb, Number of allocation: %d\n", afterGCHeapAlloc, afterGCHeapObjects) 445 test.Assert(t, afterGCHeapAlloc < preHeapAlloc && afterGCHeapObjects < preHeapObjects) 446 447 // Trigger the finalizer of kclient be executed 448 time.Sleep(200 * time.Millisecond) // ensure the finalizer be executed 449 runtime.GC() 450 runtime.ReadMemStats(&ms) 451 secondGCHeapAlloc, secondGCHeapObjects := mb(ms.HeapAlloc), ms.HeapObjects 452 t.Logf("After second GC, allocation: %f Mb, Number of allocation: %d\n", secondGCHeapAlloc, secondGCHeapObjects) 453 test.Assert(t, secondGCHeapAlloc/2 < afterGCHeapAlloc && secondGCHeapObjects/2 < afterGCHeapObjects) 454 } 455 456 func TestMapThriftGenericClientFinalizer(t *testing.T) { 457 debug.SetGCPercent(-1) 458 defer debug.SetGCPercent(100) 459 460 var ms runtime.MemStats 461 runtime.ReadMemStats(&ms) 462 t.Logf("Before new clients, allocation: %f Mb, Number of allocation: %d\n", mb(ms.HeapAlloc), ms.HeapObjects) 463 464 clientCnt := 1000 465 clis := make([]genericclient.Client, clientCnt) 466 for i := 0; i < clientCnt; i++ { 467 p, err := generic.NewThriftFileProvider("./idl/mock.thrift") 468 test.Assert(t, err == nil, "generic NewThriftFileProvider failed, err=%v", err) 469 g, err := generic.MapThriftGeneric(p) 470 test.Assert(t, err == nil, "generic MapThriftGeneric failed, err=%v", err) 471 clis[i] = newGenericClient("destServiceName", g, "127.0.0.1:9021") 472 } 473 474 runtime.ReadMemStats(&ms) 475 t.Logf("After new clients, allocation: %f Mb, Number of allocation: %d\n", mb(ms.HeapAlloc), ms.HeapObjects) 476 477 runtime.GC() 478 runtime.ReadMemStats(&ms) 479 firstGCHeapAlloc, firstGCHeapObjects := mb(ms.HeapAlloc), ms.HeapObjects 480 t.Logf("After first GC, allocation: %f Mb, Number of allocation: %d\n", firstGCHeapAlloc, firstGCHeapObjects) 481 482 // Trigger the finalizer of generic client be executed 483 time.Sleep(200 * time.Millisecond) // ensure the finalizer be executed 484 runtime.GC() 485 runtime.ReadMemStats(&ms) 486 secondGCHeapAlloc, secondGCHeapObjects := mb(ms.HeapAlloc), ms.HeapObjects 487 t.Logf("After second GC, allocation: %f Mb, Number of allocation: %d\n", secondGCHeapAlloc, secondGCHeapObjects) 488 // test.Assert(t, secondGCHeapAlloc < firstGCHeapAlloc && secondGCHeapObjects < firstGCHeapObjects) 489 490 // Trigger the finalizer of kClient be executed 491 time.Sleep(200 * time.Millisecond) // ensure the finalizer be executed 492 runtime.GC() 493 runtime.ReadMemStats(&ms) 494 thirdGCHeapAlloc, thirdGCHeapObjects := mb(ms.HeapAlloc), ms.HeapObjects 495 t.Logf("After third GC, allocation: %f Mb, Number of allocation: %d\n", thirdGCHeapAlloc, thirdGCHeapObjects) 496 test.Assert(t, thirdGCHeapAlloc < secondGCHeapAlloc/2 && thirdGCHeapObjects < secondGCHeapObjects/2) 497 } 498 499 func mb(byteSize uint64) float32 { 500 return float32(byteSize) / float32(1024*1024) 501 }