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  }