gitee.com/KyleChenSource/lib-robot@v1.0.2/robottest/robot/robot.go (about) 1 package robot 2 3 import ( 4 "fmt" 5 "time" 6 7 "gitee.com/KyleChenSource/lib-robot/robottest/common" 8 "gitee.com/KyleChenSource/lib-robot/robottest/log" 9 "gitee.com/KyleChenSource/lib-robot/robottest/protos" 10 c "gitee.com/KyleChenSource/lib-robot/robottest/robot/net" 11 "gitee.com/KyleChenSource/lib-robot/robottest/robot/statistic" 12 ts "gitee.com/KyleChenSource/lib-robot/robottest/robot/testcase" 13 ) 14 15 type RobotState int 16 17 const ( 18 ROBOT_STATE_NONE RobotState = iota // 未启动 19 ROBOT_STATE_LOGINING // 登录中 20 ROBOT_STATE_RUN // 正常运行过程 21 ROBOT_STATE_RELOGIN // 重登状态 22 ROBOT_STATE_END // 结束 23 ) 24 25 // Robot继承于TestFlowRunner 26 type Robot struct { 27 _id int 28 _mgr *RobotManager 29 _state RobotState 30 _closing bool // 正在关闭中 31 _run bool 32 _err error 33 34 _net c.IClient // 底层网络 35 _net_err error // 记录网络上抛错误。如果没有错误进行关闭,就是主动关闭.重新连接清空 36 _net_close bool // 主动关闭网络标记.重新连接清空 37 38 _server string // 服务器地址 ip:prot 39 _account string 40 _cnfName string 41 _cnf *RobotConfig 42 _msgPrint bool 43 _msgIgnores map[protos.ProtoID]bool 44 _msgFocuses map[protos.ProtoID]bool 45 46 _msgHandlers map[protos.ProtoID]map[ts.ITestcaseAction]bool // 关注消息的所有Action清单 msgid -> set<action> 47 _timerMgr *common.TimeList 48 49 _runner_index int // 当前进行的testCase index 50 _runner_id int // 自增,用于生成testcase唯一id 51 _runner_cnt int // 当前testcase运行次数 52 _testcase *ts.Testcase // 当前执行的testcase, testcase执行完毕会根据配置执行列表 53 _testcaseLogin *ts.Testcase // login or relogin case 54 55 _netMsgs chan *c.Msg // net需要处理的事件消息 56 _signal chan struct{} // 关闭信号 57 ts.TestCtxImp 58 59 _statistics_time_start int64 // 开始时间,ms 60 _statistics_time_end int64 // 结束时间,ms 61 } 62 63 func (this *Robot) Initialize(id int, mgr *RobotManager, server string, account string, file string, cnfName string, msgPrint bool, msgIgnores map[protos.ProtoID]bool, msgFocuses map[protos.ProtoID]bool) error { 64 fullname := fmt.Sprintf("%s/%s", file, cnfName) 65 cnf, ok := ROBOT_CNF[fullname] 66 if !ok { 67 return fmt.Errorf("%s Nil", fullname) 68 } 69 70 cnf.Name = cnfName 71 return this.InitializeCnf(id, mgr, server, account, cnf, msgPrint, msgIgnores, msgFocuses) 72 } 73 74 func (this *Robot) InitializeCnf(id int, mgr *RobotManager, server string, account string, cnf *RobotConfig, msgPrint bool, msgIgnores map[protos.ProtoID]bool, msgFocuses map[protos.ProtoID]bool) error { 75 this._id = id 76 this._msgPrint = msgPrint 77 this._msgIgnores = msgIgnores 78 this._msgFocuses = msgFocuses 79 this._cnf = cnf 80 this._mgr = mgr 81 this._state = ROBOT_STATE_NONE 82 this._run = true 83 this._server = server 84 this._account = account 85 86 this._msgHandlers = make(map[protos.ProtoID]map[ts.ITestcaseAction]bool) 87 this._timerMgr = common.TimeListNew() 88 89 this._runner_index = 0 90 this._runner_id = 3 // 1 : loginCase, 2: reloginCase 91 this._runner_cnt = 1 92 this._cnfName = cnf.Name 93 this._netMsgs = make(chan *c.Msg, 1024) 94 this._signal = make(chan struct{}, 1) 95 // 临时网络代码 96 this._net = &c.ClientTcp{} 97 err := this._net.Initialize(this, mgr.Msglen_get, mgr.Msgmin_get, mgr.Msg_decode, mgr.Msg_encode) 98 if err != nil { 99 return err 100 } 101 102 this.TestCtxImp.Initialize() 103 this.Set("account", account) 104 105 log.LogInfo("robot:%s Initialize OK", this._account) 106 return nil 107 } 108 109 func (this *Robot) Get(s string) (any, bool) { 110 if s == "now" { 111 return time.Now().UnixMilli(), true 112 } 113 114 return this.TestCtxImp.Get(s) 115 } 116 117 func (this *Robot) Set(s string, v any) { 118 log.LogInfo("robot:%s state:%d Set:%s=%#v", this.Name(), this._state, s, v) 119 this.TestCtxImp.Set(s, v) 120 } 121 122 func (this *Robot) TestCtx() ts.ITestCtx { 123 return this 124 } 125 126 func (this *Robot) MsgChannel() chan<- *c.Msg { 127 return this._netMsgs 128 } 129 130 func (this *Robot) Close(err error) { 131 if this._closing { 132 log.LogError("robot:%s state:%d Close err:%v Closing", this.Name(), this._state, err) 133 return 134 } 135 136 if err != nil { 137 this._err = err 138 log.LogError("robot:%s state:%d Close err:%s", this.Name(), this._state, err.Error()) 139 } else { 140 log.LogInfo("robot:%s state:%d Close Success", this.Name(), this._state) 141 } 142 143 if this._net_err == nil { 144 this._net_close = true 145 } 146 147 this._closing = true 148 this._net.Close() 149 } 150 151 // 只能在消息处理中,调用。不允许其他方式调用 152 func (this *Robot) end() { 153 if this._testcase != nil { 154 this._testcase.Stop() 155 this._testcase = nil 156 } 157 158 if this._testcaseLogin != nil { 159 this._testcaseLogin.Stop() 160 this._testcaseLogin = nil 161 } 162 163 this._run = false 164 if this._err != nil { 165 log.LogError("robot:%s end err:%s index:%d time:%d", this.Name(), this._err.Error(), this._runner_index, this._runner_cnt) 166 } else { 167 log.LogInfo("robot:%s end success", this._account) 168 } 169 } 170 171 // ------------------------------------------- TestcaseRunner Start -------------------------------------- 172 func (this *Robot) Name() string { 173 return this._account 174 } 175 176 func (this *Robot) ID() int { 177 return this._id 178 } 179 180 func (this *Robot) CnfName() string { 181 return this._cnfName 182 } 183 184 func (this *Robot) TimerAdd(delay int64, callback common.TimeCallback, data any) (*common.TimeInterface, error) { 185 return this._timerMgr.AddTimer(delay, 0, 0, callback, data) 186 } 187 188 func (this *Robot) TimerDel(t *common.TimeInterface) { 189 this._timerMgr.DelTimer(t) 190 } 191 192 func (this *Robot) MsgHandleReg(protoId protos.ProtoID, a ts.ITestcaseAction) { 193 if _, ok := this._msgHandlers[protoId]; !ok { 194 this._msgHandlers[protoId] = make(map[ts.ITestcaseAction]bool) 195 } 196 197 this._msgHandlers[protoId][a] = true 198 } 199 200 func (this *Robot) MsgHandleUnreg(protoId protos.ProtoID, a ts.ITestcaseAction) { 201 delete(this._msgHandlers[protoId], a) 202 } 203 204 func (this *Robot) Send(protoId protos.ProtoID, data protos.JsonString) error { 205 if this._closing { 206 // 关闭过程中,不发送了 207 log.LogInfo("robot:%s Send[%d]:%s when closing", this._account, protoId, data) 208 return nil 209 } 210 211 if this._msgPrint { 212 if len(this._msgFocuses) > 0 { 213 if _, ok := (this._msgFocuses)[protoId]; ok { 214 log.LogInfo("robot:%s Send[%d]:%s", this._account, protoId, data) 215 } 216 } else { 217 if _, ok := (this._msgIgnores)[protoId]; !ok { 218 log.LogInfo("robot:%s Send[%d]:%s", this._account, protoId, data) 219 } 220 } 221 } 222 return this._net.Send(protoId, data) 223 } 224 225 func (this *Robot) NetClose(s string) { 226 log.LogInfo("robot:%s NetClose:%s", this._account, s) 227 this.Close(nil) 228 } 229 230 func (this *Robot) TestcaseEnd(err error, tc *ts.Testcase, c ts.TestcaseFailedType) { 231 if err != nil { 232 log.LogError("Testcase Failed. robot:%s case:%d-%s err:%s", this._account, tc.ID(), tc.Name(), err.Error()) 233 if c != ts.TESTCASEFAILED_CONTINUE_ROBOT { 234 this.Close(err) 235 return 236 } 237 } else { 238 costTime := time.Now().UnixMilli() - tc.TimeStart() 239 log.LogInfo("Testcase OK. robot:%s case:%d-%s costTime:%d", this._account, tc.ID(), tc.Name(), costTime) 240 } 241 242 if tc == this._testcase { 243 // 当前正在运行的testcase 244 if this._cnf.Cases[this._runner_index].Count >= 0 && this._runner_cnt >= this._cnf.Cases[this._runner_index].Count { 245 this._runner_index++ 246 this._runner_cnt = 1 247 248 if this._runner_index >= len(this._cnf.Cases) { 249 // 执行完毕 250 // 成功结束 251 this._state = ROBOT_STATE_END 252 this.Close(nil) 253 return 254 } 255 } else { 256 this._runner_cnt++ 257 } 258 259 this._runner_id++ 260 _, err := this.startCase_internal(this._cnf.Cases[this._runner_index].File, this._cnf.Cases[this._runner_index].Do, this._runner_id, false) 261 if err != nil { 262 this.Close(err) 263 } 264 return 265 } 266 267 if tc == this._testcaseLogin { 268 // 登录成功 269 this._testcaseLogin.Stop() 270 this._testcaseLogin = nil 271 272 if this._testcase == nil { 273 this._runner_id++ 274 _, err := this.startCase_internal(this._cnf.Cases[this._runner_index].File, this._cnf.Cases[this._runner_index].Do, this._runner_id, false) 275 if err != nil { 276 this.Close(err) 277 return 278 } 279 return 280 } 281 282 // 重连,继续之前的Testcase 283 err = this._testcase.Start() 284 if err != nil { 285 err = fmt.Errorf("case:%d-%s start err:%s", this._testcase.ID(), this._testcase.Name(), err.Error()) 286 this.Close(err) 287 } 288 return 289 } 290 291 log.LogError("robot:%s case:%d-%s nowcase:%v nowlogin:%v not active", this._account, tc.ID(), tc.Name(), this._testcase, this._testcaseLogin) 292 } 293 294 // ------------------------------------------- INetworkHandler Start -------------------------------------- 295 func (this *Robot) Server() string { 296 return this._server 297 } 298 299 // ------------------------------------------- INetworkHandler End -------------------------------------- 300 func (this *Robot) startCase_internal(file string, name string, id int, login bool) (*ts.Testcase, error) { 301 fullname := fmt.Sprintf("%s/%s", file, name) 302 testcase_cnf, find := TESTCASE_CNF[fullname] 303 if !find { 304 return nil, fmt.Errorf("case:%d-%s nil", id, fullname) 305 } 306 307 testcase := ts.Testcase{} 308 err := testcase.Initialize(this, id, testcase_cnf) 309 if err != nil { 310 return nil, fmt.Errorf("case:%d-%s intialize err:%s", id, name, err.Error()) 311 } 312 313 if login { 314 this._testcaseLogin = &testcase 315 } else { 316 this._testcase = &testcase 317 } 318 319 err = testcase.Start() 320 if err != nil { 321 if login { 322 this._testcaseLogin = nil 323 } else { 324 this._testcase = nil 325 } 326 return nil, fmt.Errorf("case:%d-%s start err:%s", id, name, err.Error()) 327 } 328 329 return &testcase, nil 330 } 331 332 func (this *Robot) startLogin() error { 333 c := "" 334 testcaseID := 0 335 if this._state == ROBOT_STATE_NONE { 336 this._state = ROBOT_STATE_LOGINING 337 c = this._cnf.Case_Login 338 testcaseID = 1 339 } else if this._cnf.Case_Relogin != "" { 340 c = this._cnf.Case_Relogin 341 this._state = ROBOT_STATE_RELOGIN 342 testcaseID = 2 343 } else { 344 c = this._cnf.Case_Login 345 this._state = ROBOT_STATE_RELOGIN 346 testcaseID = 2 347 } 348 349 _, err := this.startCase_internal(this._cnf.File_Login, c, testcaseID, true) 350 if err != nil { 351 return err 352 } 353 354 log.LogInfo("robot:%s case_login:%s intialize OK", this._account, c) 355 356 return nil 357 } 358 359 func (this *Robot) netStart() error { 360 this._err = nil 361 this._net_err = nil 362 this._net_close = false 363 364 err := this._net.Start(this._server) 365 statistic.Record(&statistic.StatisticEvent{ 366 Type: statistic.StatisticType_Net_Start, 367 }) 368 369 if err != nil { 370 statistic.Record(&statistic.StatisticEvent{ 371 Type: statistic.StatisticType_Net_Err, 372 }) 373 return err 374 } 375 log.LogInfo("robot:%s Network Start OK", this._account) 376 377 return nil 378 } 379 380 func (this *Robot) Start() error { 381 log.LogInfo("robot:%s Start", this._account) 382 err := this.netStart() 383 if err != nil { 384 return err 385 } 386 387 go this.run() 388 389 return nil 390 } 391 392 func (this *Robot) onMsg(msg *c.Msg) { 393 switch msg.Type { 394 case c.MSGTYPE_CONNECTED: 395 // 连接成功 396 log.LogInfo("robot:%s startLogin", this._account) 397 err := this.startLogin() 398 if err != nil { 399 err = fmt.Errorf("robot:%s stop. startLogin err:%s", this._account, err.Error()) 400 this.Close(err) 401 return 402 } 403 case c.MSGTYPE_RECV: 404 // 根据注册的回调,进行回调 405 protoId, err := this._mgr.Msg_decode_protoid(msg.Data) 406 if err != nil { 407 log.LogError("robot:%s Network Close ByMe. ProtoID err:%s", this._account, msg.Err.Error()) 408 this.Close(err) 409 return 410 } 411 412 a, bHandle := this._msgHandlers[protoId] 413 bPrint := false 414 415 if this._msgPrint { 416 if len(this._msgFocuses) > 0 { 417 if _, ok := (this._msgFocuses)[protoId]; ok { 418 bPrint = true 419 } 420 } else { 421 if _, ok := this._msgIgnores[protoId]; !ok { 422 bPrint = true 423 424 } 425 } 426 } 427 428 if !bHandle && !bPrint { 429 // 消息不处理 430 return 431 } 432 protoId, header, data, err := this._mgr.Msg_decode(msg.Data) 433 if err != nil { 434 log.LogError("robot:%s Network Close ByMe. Decode err:%s", this._account, msg.Err.Error()) 435 this.Close(err) 436 return 437 } 438 439 if bPrint { 440 log.LogInfo("robot:%s Recv[%d]:%s", this._account, protoId, data) 441 } 442 443 if bHandle { 444 for k, _ := range a { 445 k.OnMsg(protoId, header, data) 446 } 447 } 448 // log.LogDebug("robot:%s msg:%d recved", this._account, msg.ProtoId) 449 case c.MSGTYPE_ERR: 450 if this._net_close { 451 // 主动关闭,不用再处理网络异常 452 log.LogInfo("robot:%s Network Close ByMe. net err:%s", this._account, msg.Err.Error()) 453 454 statistic.Record(&statistic.StatisticEvent{ 455 Type: statistic.StatisticType_Net_Close, 456 }) 457 } else if this._net_err == nil { 458 log.LogError("robot:%s Network Close NetErr. net err:%s", this._account, msg.Err.Error()) 459 statistic.Record(&statistic.StatisticEvent{ 460 Type: statistic.StatisticType_Net_Err, 461 }) 462 this._net_err = msg.Err 463 this.Close(msg.Err) 464 } else { 465 log.LogError("robot:%s Network Close Dup. net err:%s", this._account, msg.Err.Error()) 466 } 467 case c.MSGTYPE_CLOSED: 468 log.LogInfo("robot:%s msg Closed state=%d relogin=%t", this._account, this._state, this._cnf.Relogin) 469 this._closing = false 470 // 根据是否需要重连,判定进行重连 471 if this._state == ROBOT_STATE_END { 472 // 结束了 473 this._err = nil 474 this.end() 475 return 476 } 477 478 if !this._cnf.Relogin { 479 // 异常退出 480 this.end() 481 return 482 } 483 484 log.LogInfo("robot:%s Restart after Close", this._account) 485 if this._testcase != nil { 486 this._testcase.Stop() 487 } 488 489 if this._testcaseLogin != nil { 490 this._testcaseLogin.Stop() 491 this._testcaseLogin = nil 492 } 493 494 err := this.netStart() 495 if err != nil { 496 this.Close(err) 497 } 498 } 499 } 500 501 func (this *Robot) kill() { 502 if len(this._signal) == 0 { 503 this._signal <- struct{}{} 504 } 505 } 506 507 func (this *Robot) run() { 508 defer func() { 509 close(this._netMsgs) 510 this._netMsgs = nil 511 this._statistics_time_end = time.Now().UnixMilli() 512 513 this._mgr.RobotFinCallback(this, this._err) 514 }() 515 defer common.PanicLog(func() { 516 this._err = fmt.Errorf("robot run panic") 517 }) 518 519 this._statistics_time_start = time.Now().UnixMilli() 520 521 for this._run { 522 select { 523 case msg := <-this._netMsgs: 524 this.onMsg(msg) 525 case <-this._timerMgr.Timer(): 526 this._timerMgr.Update() 527 case <-this._signal: 528 log.LogInfo("robot:%s Close from signal", this._account) 529 this._state = ROBOT_STATE_END 530 this.Close(fmt.Errorf("Close from signal")) 531 } 532 } 533 }