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 }