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  }