github.com/matrixorigin/matrixone@v0.7.0/pkg/util/trace/impl/motrace/report_statement_test.go (about) 1 // Copyright 2022 Matrix Origin 2 // 3 // Licensed under the Apache License, Version 2.0 (the "License"); 4 // you may not use this file except in compliance with the License. 5 // You may obtain a copy of the License at 6 // 7 // http://www.apache.org/licenses/LICENSE-2.0 8 // 9 // Unless required by applicable law or agreed to in writing, software 10 // distributed under the License is distributed on an "AS IS" BASIS, 11 // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 12 // See the License for the specific language governing permissions and 13 // limitations under the License. 14 15 package motrace 16 17 import ( 18 "context" 19 "encoding/json" 20 "fmt" 21 "github.com/google/uuid" 22 "github.com/prashantv/gostub" 23 "github.com/stretchr/testify/assert" 24 "github.com/stretchr/testify/require" 25 "testing" 26 "time" 27 ) 28 29 func TestStatementInfo_Report_EndStatement(t *testing.T) { 30 type fields struct { 31 StatementID [16]byte 32 TransactionID [16]byte 33 SessionID [16]byte 34 Account string 35 User string 36 Host string 37 Database string 38 Statement string 39 StatementFingerprint string 40 StatementTag string 41 RequestAt time.Time 42 ExecPlan any 43 Status StatementInfoStatus 44 Error error 45 ResponseAt time.Time 46 Duration time.Duration 47 48 doReport bool 49 doExport bool 50 } 51 type args struct { 52 ctx context.Context 53 err error 54 } 55 56 tests := []struct { 57 name string 58 fields fields 59 args args 60 wantReportCnt int 61 }{ 62 { 63 name: "Report_Export_EndStatement", 64 fields: fields{ 65 doReport: true, 66 doExport: true, 67 }, 68 args: args{ 69 ctx: context.Background(), 70 err: nil, 71 }, 72 wantReportCnt: 2, 73 }, 74 { 75 name: "Report_EndStatement", 76 fields: fields{ 77 doReport: true, 78 doExport: false, 79 }, 80 args: args{ 81 ctx: context.Background(), 82 err: nil, 83 }, 84 wantReportCnt: 1, 85 }, 86 { 87 name: "just_EndStatement", 88 fields: fields{ 89 doReport: false, 90 doExport: false, 91 }, 92 args: args{ 93 ctx: context.Background(), 94 err: nil, 95 }, 96 wantReportCnt: 1, 97 }, 98 } 99 100 gotCnt := 0 101 dummyReportStmFunc := func(ctx context.Context, s *StatementInfo) error { 102 gotCnt++ 103 return nil 104 } 105 s := gostub.Stub(&ReportStatement, dummyReportStmFunc) 106 defer s.Reset() 107 108 dummyExport := func(s *StatementInfo) { 109 s.mux.Lock() 110 s.exported = true 111 s.mux.Unlock() 112 } 113 114 for _, tt := range tests { 115 t.Run(tt.name, func(t *testing.T) { 116 gotCnt = 0 117 s := &StatementInfo{ 118 StatementID: tt.fields.StatementID, 119 TransactionID: tt.fields.TransactionID, 120 SessionID: tt.fields.SessionID, 121 Account: tt.fields.Account, 122 User: tt.fields.User, 123 Host: tt.fields.Host, 124 Database: tt.fields.Database, 125 Statement: tt.fields.Statement, 126 StatementFingerprint: tt.fields.StatementFingerprint, 127 StatementTag: tt.fields.StatementTag, 128 RequestAt: tt.fields.RequestAt, 129 ExecPlan: tt.fields.ExecPlan, 130 Status: tt.fields.Status, 131 Error: tt.fields.Error, 132 ResponseAt: tt.fields.ResponseAt, 133 Duration: tt.fields.Duration, 134 } 135 if tt.fields.doExport && !tt.fields.doReport { 136 t.Errorf("export(%v) need report(%v) first.", tt.fields.doExport, tt.fields.doReport) 137 } 138 if tt.fields.doReport { 139 s.Report(tt.args.ctx) 140 } 141 if tt.fields.doExport { 142 dummyExport(s) 143 } 144 require.Equal(t, tt.fields.doReport, s.reported) 145 require.Equal(t, tt.fields.doExport, s.exported) 146 147 stmCtx := ContextWithStatement(tt.args.ctx, s) 148 EndStatement(stmCtx, tt.args.err) 149 require.Equal(t, tt.wantReportCnt, gotCnt) 150 }) 151 } 152 } 153 154 var realNoExecPlanJsonResult = `{"code":200,"message":"NO ExecPlan Serialize function","steps":null,"success":false,"uuid":"00000000-0000-0000-0000-000000000000"}` 155 var dummyNoExecPlanJsonResult = `{"code":200,"message":"no exec plan"}` 156 var dummyNoExecPlanJsonResult2 = `{"func":"dummy2","code":200,"message":"no exec plan"}` 157 158 var dummySerializeExecPlan = func(_ context.Context, plan any, _ uuid.UUID) ([]byte, []byte, Statistic) { 159 if plan == nil { 160 return []byte(dummyNoExecPlanJsonResult), []byte{}, Statistic{} 161 } 162 json, err := json.Marshal(plan) 163 if err != nil { 164 return []byte(fmt.Sprintf(`{"err": %q}`, err.Error())), []byte{}, Statistic{} 165 } 166 return json, []byte{}, Statistic{RowsRead: 1, BytesScan: 1} 167 } 168 169 var dummySerializeExecPlan2 = func(_ context.Context, plan any, _ uuid.UUID) ([]byte, []byte, Statistic) { 170 if plan == nil { 171 return []byte(dummyNoExecPlanJsonResult2), []byte{}, Statistic{} 172 } 173 json, err := json.Marshal(plan) 174 if err != nil { 175 return []byte(fmt.Sprintf(`{"func":"dymmy2","err": %q}`, err.Error())), []byte{}, Statistic{} 176 } 177 val := fmt.Sprintf(`{"func":"dummy2","result":%s}`, json) 178 return []byte(val), []byte{}, Statistic{} 179 } 180 181 func TestStatementInfo_ExecPlan2Json(t *testing.T) { 182 type args struct { 183 setDefault func() 184 ExecPlan any 185 SerializeExecPlan SerializeExecPlanFunc 186 } 187 188 dummyExecPlan := map[string]any{"key": "val", "int": 1} 189 defaultEPJson := `{"int":1,"key":"val"}` 190 dummyEPJson := `{"func":"dummy2","result":{"int":1,"key":"val"}}` 191 192 var setDefaultNil = func() { 193 SetDefaultSerializeExecPlan(nil) 194 } 195 var setDefaultDummy = func() { 196 SetDefaultSerializeExecPlan(dummySerializeExecPlan) 197 } 198 199 tests := []struct { 200 name string 201 args args 202 want string 203 }{ 204 { 205 name: "nil", 206 args: args{ 207 setDefault: setDefaultNil, 208 ExecPlan: nil, 209 SerializeExecPlan: nil, 210 }, 211 want: realNoExecPlanJsonResult, 212 }, 213 { 214 name: "nil_ep_nil", 215 args: args{ 216 setDefault: setDefaultNil, 217 ExecPlan: dummyExecPlan, 218 SerializeExecPlan: nil, 219 }, 220 want: realNoExecPlanJsonResult, 221 }, 222 { 223 name: "dummyDefault_nil_nil", 224 args: args{ 225 setDefault: setDefaultDummy, 226 ExecPlan: nil, 227 SerializeExecPlan: nil, 228 }, 229 want: dummyNoExecPlanJsonResult, 230 }, 231 { 232 name: "dummyDefault_ep_nil", 233 args: args{ 234 setDefault: setDefaultDummy, 235 ExecPlan: dummyExecPlan, 236 SerializeExecPlan: nil, 237 }, 238 want: defaultEPJson, 239 }, 240 { 241 name: "dummyDefault_ep_Serialize", 242 args: args{ 243 setDefault: setDefaultDummy, 244 ExecPlan: dummyExecPlan, 245 SerializeExecPlan: dummySerializeExecPlan2, 246 }, 247 want: dummyEPJson, 248 }, 249 { 250 name: "nil_ep_Serialize", 251 args: args{ 252 setDefault: setDefaultNil, 253 ExecPlan: dummyExecPlan, 254 SerializeExecPlan: dummySerializeExecPlan2, 255 }, 256 want: dummyEPJson, 257 }, 258 { 259 name: "dummyDefault_nil_Serialize", 260 args: args{ 261 setDefault: setDefaultDummy, 262 ExecPlan: nil, 263 SerializeExecPlan: dummySerializeExecPlan2, 264 }, 265 want: dummyNoExecPlanJsonResult2, 266 }, 267 { 268 name: "nil_nil_Serialize", 269 args: args{ 270 setDefault: setDefaultNil, 271 ExecPlan: nil, 272 SerializeExecPlan: dummySerializeExecPlan2, 273 }, 274 want: dummyNoExecPlanJsonResult2, 275 }, 276 } 277 278 ctx := DefaultContext() 279 for _, tt := range tests { 280 t.Run(tt.name, func(t *testing.T) { 281 tt.args.setDefault() 282 s := &StatementInfo{} 283 s.SetExecPlan(tt.args.ExecPlan, tt.args.SerializeExecPlan) 284 got, _ := s.ExecPlan2Json(ctx) 285 assert.Equalf(t, tt.want, got, "ExecPlan2Json()") 286 287 mapper := new(map[string]any) 288 err := json.Unmarshal([]byte(got), mapper) 289 require.Nil(t, err, "jons.Unmarshal failed") 290 }) 291 } 292 }