github.com/cloudwego/kitex@v0.9.0/pkg/remote/codec/thrift/thrift_frugal_test.go (about)

     1  //go:build (amd64 || arm64) && !windows && go1.16 && !go1.23 && !disablefrugal
     2  // +build amd64 arm64
     3  // +build !windows
     4  // +build go1.16
     5  // +build !go1.23
     6  // +build !disablefrugal
     7  
     8  /*
     9   * Copyright 2021 CloudWeGo Authors
    10   *
    11   * Licensed under the Apache License, Version 2.0 (the "License");
    12   * you may not use this file except in compliance with the License.
    13   * You may obtain a copy of the License at
    14   *
    15   *     http://www.apache.org/licenses/LICENSE-2.0
    16   *
    17   * Unless required by applicable law or agreed to in writing, software
    18   * distributed under the License is distributed on an "AS IS" BASIS,
    19   * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
    20   * See the License for the specific language governing permissions and
    21   * limitations under the License.
    22   */
    23  
    24  package thrift
    25  
    26  import (
    27  	"context"
    28  	"reflect"
    29  	"testing"
    30  
    31  	"github.com/cloudwego/kitex/internal/mocks/thrift/fast"
    32  	"github.com/cloudwego/kitex/internal/test"
    33  	"github.com/cloudwego/kitex/pkg/remote"
    34  	"github.com/cloudwego/kitex/pkg/rpcinfo"
    35  	"github.com/cloudwego/kitex/transport"
    36  )
    37  
    38  type MockFrugalTagReq struct {
    39  	Msg     string            `frugal:"1,default,string"`
    40  	StrMap  map[string]string `frugal:"2,default,map<string:string>"`
    41  	StrList []string          `frugal:"3,default,list<string>"`
    42  }
    43  
    44  type MockNoTagArgs struct {
    45  	Req *MockFrugalTagReq
    46  }
    47  
    48  func initNoTagSendMsg(tp transport.Protocol) remote.Message {
    49  	var _args MockNoTagArgs
    50  	ink := rpcinfo.NewInvocation("", "mock")
    51  	ri := rpcinfo.NewRPCInfo(nil, nil, ink, nil, nil)
    52  	msg := remote.NewMessage(&_args, svcInfo, ri, remote.Call, remote.Client)
    53  	msg.SetProtocolInfo(remote.NewProtocolInfo(tp, svcInfo.PayloadCodec))
    54  	return msg
    55  }
    56  
    57  type MockFrugalTagArgs struct {
    58  	Req *MockFrugalTagReq `frugal:"1,default,MockFrugalTagReq"`
    59  }
    60  
    61  func initFrugalTagSendMsg(tp transport.Protocol) remote.Message {
    62  	var _args MockFrugalTagArgs
    63  	_args.Req = &MockFrugalTagReq{
    64  		Msg:     "MockReq",
    65  		StrMap:  map[string]string{"0": "0", "1": "1", "2": "2"},
    66  		StrList: []string{"0", "1", "2"},
    67  	}
    68  	ink := rpcinfo.NewInvocation("", "mock")
    69  	ri := rpcinfo.NewRPCInfo(nil, nil, ink, nil, nil)
    70  	msg := remote.NewMessage(&_args, svcInfo, ri, remote.Call, remote.Client)
    71  	msg.SetProtocolInfo(remote.NewProtocolInfo(tp, svcInfo.PayloadCodec))
    72  	return msg
    73  }
    74  
    75  func initFrugalTagRecvMsg() remote.Message {
    76  	var _args MockFrugalTagArgs
    77  	ink := rpcinfo.NewInvocation("", "mock")
    78  	ri := rpcinfo.NewRPCInfo(nil, nil, ink, nil, rpcinfo.NewRPCStats())
    79  	msg := remote.NewMessage(&_args, svcInfo, ri, remote.Call, remote.Server)
    80  	return msg
    81  }
    82  
    83  func TestHyperCodecCheck(t *testing.T) {
    84  	msg := initFrugalTagRecvMsg()
    85  	msg.SetPayloadLen(0)
    86  	codec := &thriftCodec{}
    87  
    88  	// test CodecType check
    89  	test.Assert(t, codec.hyperMarshalEnabled() == false)
    90  	msg.SetPayloadLen(1)
    91  	test.Assert(t, codec.hyperMessageUnmarshalEnabled() == false)
    92  	msg.SetPayloadLen(0)
    93  
    94  	// test hyperMarshal check
    95  	codec = &thriftCodec{FrugalWrite}
    96  	test.Assert(t, hyperMarshalAvailable(&MockNoTagArgs{}) == false)
    97  	test.Assert(t, hyperMarshalAvailable(&MockFrugalTagArgs{}) == true)
    98  
    99  	// test hyperMessageUnmarshal check
   100  	codec = &thriftCodec{FrugalRead}
   101  	test.Assert(t, hyperMessageUnmarshalAvailable(&MockNoTagArgs{}, msg.PayloadLen()) == false)
   102  	test.Assert(t, hyperMessageUnmarshalAvailable(&MockFrugalTagArgs{}, msg.PayloadLen()) == false)
   103  	msg.SetPayloadLen(1)
   104  	test.Assert(t, hyperMessageUnmarshalAvailable(&MockFrugalTagArgs{}, msg.PayloadLen()) == true)
   105  }
   106  
   107  func TestFrugalCodec(t *testing.T) {
   108  	t.Run("configure frugal but data has not tag", func(t *testing.T) {
   109  		ctx := context.Background()
   110  		codec := &thriftCodec{FrugalRead | FrugalWrite}
   111  
   112  		// MockNoTagArgs cannot be marshaled
   113  		sendMsg := initNoTagSendMsg(transport.TTHeader)
   114  		out := remote.NewWriterBuffer(256)
   115  		err := codec.Marshal(ctx, sendMsg, out)
   116  		test.Assert(t, err != nil)
   117  	})
   118  	t.Run("configure frugal and data has tag", func(t *testing.T) {
   119  		ctx := context.Background()
   120  		codec := &thriftCodec{FrugalRead | FrugalWrite}
   121  
   122  		testFrugalDataConversion(t, ctx, codec)
   123  	})
   124  	t.Run("fallback to frugal and data has tag", func(t *testing.T) {
   125  		ctx := context.Background()
   126  		codec := NewThriftCodec()
   127  
   128  		testFrugalDataConversion(t, ctx, codec)
   129  	})
   130  	t.Run("configure BasicCodec to disable frugal fallback", func(t *testing.T) {
   131  		ctx := context.Background()
   132  		codec := NewThriftCodecWithConfig(Basic)
   133  
   134  		// MockNoTagArgs cannot be marshaled
   135  		sendMsg := initNoTagSendMsg(transport.TTHeader)
   136  		out := remote.NewWriterBuffer(256)
   137  		err := codec.Marshal(ctx, sendMsg, out)
   138  		test.Assert(t, err != nil)
   139  	})
   140  }
   141  
   142  func testFrugalDataConversion(t *testing.T, ctx context.Context, codec remote.PayloadCodec) {
   143  	// encode client side
   144  	sendMsg := initFrugalTagSendMsg(transport.TTHeader)
   145  	out := remote.NewWriterBuffer(256)
   146  	err := codec.Marshal(ctx, sendMsg, out)
   147  	test.Assert(t, err == nil, err)
   148  
   149  	// decode server side
   150  	recvMsg := initFrugalTagRecvMsg()
   151  	buf, err := out.Bytes()
   152  	recvMsg.SetPayloadLen(len(buf))
   153  	test.Assert(t, err == nil, err)
   154  	in := remote.NewReaderBuffer(buf)
   155  	err = codec.Unmarshal(ctx, recvMsg, in)
   156  	test.Assert(t, err == nil, err)
   157  
   158  	// compare Args
   159  	sendReq := (sendMsg.Data()).(*MockFrugalTagArgs).Req
   160  	recvReq := (recvMsg.Data()).(*MockFrugalTagArgs).Req
   161  	test.Assert(t, sendReq.Msg == recvReq.Msg)
   162  	test.Assert(t, len(sendReq.StrList) == len(recvReq.StrList))
   163  	test.Assert(t, len(sendReq.StrMap) == len(recvReq.StrMap))
   164  	for i, item := range sendReq.StrList {
   165  		test.Assert(t, item == recvReq.StrList[i])
   166  	}
   167  	for k := range sendReq.StrMap {
   168  		test.Assert(t, sendReq.StrMap[k] == recvReq.StrMap[k])
   169  	}
   170  }
   171  
   172  func TestMarshalThriftDataFrugal(t *testing.T) {
   173  	mockReqFrugal := &MockFrugalTagReq{
   174  		Msg: "hello",
   175  	}
   176  	successfulCodecs := []remote.PayloadCodec{
   177  		NewThriftCodecWithConfig(FrugalWrite),
   178  		// fallback to frugal
   179  		nil,
   180  		// fallback to frugal
   181  		NewThriftCodec(),
   182  	}
   183  	for _, codec := range successfulCodecs {
   184  		buf, err := MarshalThriftData(context.Background(), codec, mockReqFrugal)
   185  		test.Assert(t, err == nil, err)
   186  		test.Assert(t, reflect.DeepEqual(buf, mockReqThrift), buf)
   187  	}
   188  
   189  	// Basic can be used for disabling frugal
   190  	_, err := MarshalThriftData(context.Background(), NewThriftCodecWithConfig(Basic), mockReqFrugal)
   191  	test.Assert(t, err != nil, err)
   192  }
   193  
   194  func TestUnmarshalThriftDataFrugal(t *testing.T) {
   195  	req := &MockFrugalTagReq{}
   196  	successfulCodecs := []remote.PayloadCodec{
   197  		NewThriftCodecWithConfig(FrugalRead),
   198  		// fallback to frugal
   199  		nil,
   200  		// fallback to frugal
   201  		NewThriftCodec(),
   202  	}
   203  	for _, codec := range successfulCodecs {
   204  		err := UnmarshalThriftData(context.Background(), codec, "mock", mockReqThrift, req)
   205  		checkDecodeResult(t, err, &fast.MockReq{
   206  			Msg:     req.Msg,
   207  			StrList: req.StrList,
   208  			StrMap:  req.StrMap,
   209  		})
   210  
   211  	}
   212  
   213  	// Basic can be used for disabling frugal
   214  	err := UnmarshalThriftData(context.Background(), NewThriftCodecWithConfig(Basic), "mock", mockReqThrift, req)
   215  	test.Assert(t, err != nil, err)
   216  }
   217  
   218  func Test_verifyMarshalThriftDataFrugal(t *testing.T) {
   219  	err := verifyMarshalBasicThriftDataType(&MockFrugalTagArgs{})
   220  	test.Assert(t, err == errEncodeMismatchMsgType, err)
   221  	err = verifyMarshalBasicThriftDataType(&MockNoTagArgs{})
   222  	test.Assert(t, err == errEncodeMismatchMsgType, err)
   223  }
   224  
   225  func Test_verifyUnmarshalThriftDataFrugal(t *testing.T) {
   226  	err := verifyUnmarshalBasicThriftDataType(&MockFrugalTagArgs{})
   227  	test.Assert(t, err == errDecodeMismatchMsgType, err)
   228  	err = verifyUnmarshalBasicThriftDataType(&MockNoTagArgs{})
   229  	test.Assert(t, err == errDecodeMismatchMsgType, err)
   230  }