gitee.com/KyleChenSource/lib-robot@v1.0.2/robottest/testcaseimp/lua.go (about)

     1  package testcaseimp
     2  
     3  // Lua文件返回表
     4  // {onStart, onMsg, onTimer, onStop}
     5  // 会在对应时机进行函数调用
     6  // 本身提供函数给Lua调用
     7  //	CtxGet(ctx LuaScript, key string)
     8  //	CtxSet(ctx LuaScript, key string, value)
     9  //	MsgRegister(ctx LuaScript, msgId)
    10  //	MsgUnRegister(ctx LuaScript, msgId)
    11  //	TimeRegister(ctx LuaScript, delay, key, cb)
    12  //	TimeUnRegister(ctx LuaScript, key)
    13  //	SendMsgWithCnf(ctx, file, key)
    14  //	SendMsg(ctx, msgId, data)
    15  // 	ActionEnd(ctx, string)
    16  
    17  import (
    18  	"fmt"
    19  	"math"
    20  	"math/rand"
    21  	"sync"
    22  
    23  	"gitee.com/KyleChenSource/lib-robot/robottest/robot/testcase"
    24  	lua "github.com/yuin/gopher-lua"
    25  
    26  	"gitee.com/KyleChenSource/lib-robot/robottest/protos"
    27  
    28  	"gitee.com/KyleChenSource/lib-robot/robottest/log"
    29  
    30  	"gitee.com/KyleChenSource/lib-robot/robottest/common"
    31  	"gitee.com/KyleChenSource/lib-robot/robottest/common/mlua"
    32  )
    33  
    34  type LuaScriptFactory struct {
    35  }
    36  
    37  type LuaScriptCnf struct {
    38  	testcase.TestcaseActionConfig_
    39  	LuaFile string `json:"file"`
    40  }
    41  
    42  func (this *LuaScriptFactory) Cnf() testcase.TestcaseActionCnf {
    43  	return &LuaScriptCnf{}
    44  }
    45  
    46  func (this *LuaScriptFactory) New(id testcase.TestActionID, tf testcase.ITestFlow, cnf testcase.TestcaseActionCnf) (testcase.ITestcaseAction, error) {
    47  	return &LuaScript{
    48  		_id:  id,
    49  		_tf:  tf,
    50  		_cnf: cnf.(*LuaScriptCnf),
    51  	}, nil
    52  }
    53  
    54  func init() {
    55  	testcase.TESTCASEACTION_MGR.Register("LuaScript", &LuaScriptFactory{})
    56  }
    57  
    58  var (
    59  	luaFiles sync.Map
    60  )
    61  
    62  type LuaScript struct {
    63  	_id       testcase.TestActionID
    64  	_tf       testcase.ITestFlow
    65  	_cnf      *LuaScriptCnf
    66  	_delay    *common.TimeInterface
    67  	_timerCbs map[*common.TimeInterface]*lua.LFunction
    68  	_timers   map[string]*common.TimeInterface
    69  	_msgIds   map[int]bool
    70  	luaScript *mlua.LuaScript
    71  	luaMe     *lua.LUserData
    72  }
    73  
    74  // 启动
    75  // 目前仅仅支持定时器和消息两种驱动模式
    76  func (this *LuaScript) Start() error {
    77  	var L *mlua.LuaCtx
    78  	if l, ok := this._tf.TestCtx().Get("Lua"); !ok {
    79  		L = mlua.LuaCtxCreate()
    80  		L.L.SetGlobal("CtxGet", L.L.NewFunction(ctxGet))
    81  		L.L.SetGlobal("CtxSet", L.L.NewFunction(ctxSet))
    82  		L.L.SetGlobal("MsgRegister", L.L.NewFunction(registerMsg))
    83  		L.L.SetGlobal("MsgUnRegister", L.L.NewFunction(unRegisterMsg))
    84  		L.L.SetGlobal("TimeRegister", L.L.NewFunction(registerTimer))
    85  		L.L.SetGlobal("TimeUnRegister", L.L.NewFunction(unRegisterTimer))
    86  		L.L.SetGlobal("ActionEnd", L.L.NewFunction(actionEnd))
    87  
    88  		L.L.SetGlobal("SendMsgWithCnf", L.L.NewFunction(sendMsgWithCnf))
    89  		L.L.SetGlobal("SendMsg", L.L.NewFunction(sendMsg))
    90  		this._tf.TestCtx().Set("Lua", L)
    91  	} else {
    92  		L = l.(*mlua.LuaCtx)
    93  	}
    94  
    95  	var err error
    96  	this.luaScript, err = L.Script(this._cnf.LuaFile)
    97  	if err != nil {
    98  		return fmt.Errorf("script:%s err:%s", this._cnf.LuaFile, err.Error())
    99  	}
   100  
   101  	this.luaMe = L.L.NewUserData()
   102  	this.luaMe.Value = this
   103  
   104  	this._timerCbs = make(map[*common.TimeInterface]*lua.LFunction)
   105  	this._timers = make(map[string]*common.TimeInterface)
   106  	this._msgIds = make(map[int]bool)
   107  
   108  	delay := int64(this._cnf.Delay)
   109  	if this._cnf.RandMin > 0 {
   110  		delay += (*this._cnf).RandMin
   111  	}
   112  	if this._cnf.RandMax > this._cnf.RandMin {
   113  		delay += rand.Int63n((*this._cnf).RandMax - (*this._cnf).RandMin)
   114  	}
   115  
   116  	if delay > 0 {
   117  		t, err := this._tf.TestCtx().TimerAdd(delay, this.OnTimerStart, nil)
   118  		if err != nil {
   119  			return err
   120  		}
   121  		this._delay = t
   122  		log.LogInfo("%s action:%d delay:%d", this._tf.LogPre(), this._id, delay)
   123  	} else {
   124  		this.onStart()
   125  	}
   126  
   127  	return nil
   128  }
   129  
   130  func (this *LuaScript) onStart() {
   131  	log.LogInfo("%s action:%d onStart", this._tf.LogPre(), this._id)
   132  	t, _ := this._tf.TestCtx().Get("Lua")
   133  	l := t.(*mlua.LuaCtx)
   134  
   135  	topB := l.L.GetTop()
   136  	err := l.L.CallByParam(lua.P{Fn: this.luaScript.OnStart, NRet: 1, Protect: true},
   137  		this.luaMe,
   138  	)
   139  
   140  	if err != nil {
   141  		this._tf.OnActionEnd(this, fmt.Errorf("onStart:%s", err.Error()), this._cnf.FailContinue)
   142  		return
   143  	}
   144  
   145  	topN := l.L.GetTop()
   146  	if topN-topB != 1 {
   147  		this._tf.OnActionEnd(this, fmt.Errorf("onStart return:%d", topN-topB), this._cnf.FailContinue)
   148  		return
   149  	}
   150  	ret := l.L.Get(topN)
   151  	if ret.Type() != lua.LTNil {
   152  		if ret.Type() == lua.LTString {
   153  			this._tf.OnActionEnd(this, fmt.Errorf("onStart return err:%s", ret.String()), this._cnf.FailContinue)
   154  			return
   155  		}
   156  		this._tf.OnActionEnd(this, fmt.Errorf("onStart return type:%d value:%s", ret.Type(), ret.String()), this._cnf.FailContinue)
   157  		return
   158  	}
   159  
   160  	l.L.Pop(topN - topB)
   161  }
   162  
   163  // 停止,需要删除目前所有的驱动注册
   164  func (this *LuaScript) Stop() {
   165  	for timer, _ := range this._timerCbs {
   166  		this._tf.TestCtx().TimerDel(timer)
   167  	}
   168  
   169  	for msgid, _ := range this._msgIds {
   170  		this._tf.TestCtx().MsgHandleUnreg(protos.ProtoID(msgid), this)
   171  	}
   172  }
   173  
   174  func (this *LuaScript) ID() testcase.TestActionID {
   175  	return this._id
   176  }
   177  
   178  func (this *LuaScript) Name() string {
   179  	return this._cnf.ActionName()
   180  }
   181  
   182  func (this *LuaScript) Testflow() testcase.ITestFlow {
   183  	return this._tf
   184  }
   185  
   186  func (this *LuaScript) OnMsg(protoId protos.ProtoID, header protos.JsonString, data protos.JsonString) {
   187  	t, _ := this._tf.TestCtx().Get("Lua")
   188  	l := t.(*mlua.LuaCtx)
   189  
   190  	topB := l.L.GetTop()
   191  	err := l.L.CallByParam(lua.P{Fn: this.luaScript.OnMsg, NRet: 1, Protect: true},
   192  		this.luaMe,
   193  		lua.LNumber(protoId),
   194  		lua.LString(data),
   195  	)
   196  
   197  	if err != nil {
   198  		this._tf.OnActionEnd(this, fmt.Errorf("OnMsg:%s", err.Error()), this._cnf.FailContinue)
   199  		return
   200  	}
   201  
   202  	topN := l.L.GetTop()
   203  	if topN-topB != 1 {
   204  		this._tf.OnActionEnd(this, fmt.Errorf("OnMsg return:%d", topN-topB), this._cnf.FailContinue)
   205  		return
   206  	}
   207  	ret := l.L.Get(topN)
   208  	if ret.Type() != lua.LTNil {
   209  		if ret.Type() == lua.LTString {
   210  			this._tf.OnActionEnd(this, fmt.Errorf("OnMsg return err:%s", ret.String()), this._cnf.FailContinue)
   211  			return
   212  		}
   213  		this._tf.OnActionEnd(this, fmt.Errorf("OnMsg return type:%d value:%s", ret.Type(), ret.String()), this._cnf.FailContinue)
   214  		return
   215  	}
   216  
   217  	l.L.Pop(topN - topB)
   218  }
   219  
   220  func (this *LuaScript) OnTimerStart(timer *common.TimeInterface, data any) int64 {
   221  	this._delay = nil
   222  
   223  	this.onStart()
   224  	return 0
   225  }
   226  
   227  func (this *LuaScript) OnTimerLua(timer *common.TimeInterface, data any) (nextTimer int64) {
   228  	nextTimer = 0
   229  	t, _ := this._tf.TestCtx().Get("Lua")
   230  	l := t.(*mlua.LuaCtx)
   231  
   232  	function, ok := this._timerCbs[timer]
   233  	if !ok {
   234  		this._tf.OnActionEnd(this, fmt.Errorf("OnTimerLua no cb"), this._cnf.FailContinue)
   235  		return 0
   236  	}
   237  	topB := l.L.GetTop()
   238  	err := l.L.CallByParam(lua.P{Fn: function, NRet: 2, Protect: true},
   239  		this.luaMe,
   240  	)
   241  
   242  	if err != nil {
   243  		this._tf.OnActionEnd(this, fmt.Errorf("OnTimerLua err:%s", err.Error()), this._cnf.FailContinue)
   244  		return 0
   245  	}
   246  
   247  	topN := l.L.GetTop()
   248  	if topN-topB != 2 {
   249  		this._tf.OnActionEnd(this, fmt.Errorf("OnMsg return:%d", topN-topB), this._cnf.FailContinue)
   250  		return 0
   251  	}
   252  
   253  	nextTimer = int64(l.L.ToNumber(topB + 1))
   254  	if nextTimer <= 0 {
   255  		delete(this._timerCbs, timer)
   256  		for key, v := range this._timers {
   257  			if v == timer {
   258  				delete(this._timers, key)
   259  				break
   260  			}
   261  		}
   262  	}
   263  
   264  	ret := l.L.Get(topN)
   265  	if ret.Type() != lua.LTNil {
   266  		if ret.Type() == lua.LTString {
   267  			this._tf.OnActionEnd(this, fmt.Errorf("OnMsg return err:%s", ret.String()), this._cnf.FailContinue)
   268  			return
   269  		}
   270  		this._tf.OnActionEnd(this, fmt.Errorf("onStart return type:%d", ret.Type()), this._cnf.FailContinue)
   271  		return
   272  	}
   273  
   274  	return
   275  }
   276  
   277  func registerMsg(L *lua.LState) int {
   278  	//a *ActionLua, id int
   279  	a := L.CheckUserData(1)
   280  	if a == nil {
   281  		L.Push(lua.LString("param 0 not UserData"))
   282  		return 1
   283  	}
   284  	this, ok := a.Value.(*LuaScript)
   285  	if !ok {
   286  		L.Push(lua.LString(fmt.Sprintf("param 0 not LuaScript:%T", a.Value)))
   287  		return 1
   288  	}
   289  
   290  	id := int(L.ToNumber(2))
   291  	this._tf.TestCtx().MsgHandleReg(protos.ProtoID(id), this)
   292  	this._msgIds[id] = true
   293  	return 0
   294  }
   295  
   296  func unRegisterMsg(L *lua.LState) int {
   297  	//a *ActionLua, id int
   298  	a := L.CheckUserData(1)
   299  	if a == nil {
   300  		L.Push(lua.LString("param 0 not UserData"))
   301  		return 1
   302  	}
   303  	this, ok := a.Value.(*LuaScript)
   304  	if !ok {
   305  		L.Push(lua.LString(fmt.Sprintf("param 0 not LuaScript:%T", a.Value)))
   306  		return 1
   307  	}
   308  
   309  	id := int(L.ToNumber(2))
   310  	this._tf.TestCtx().MsgHandleUnreg(protos.ProtoID(id), this)
   311  	delete(this._msgIds, id)
   312  	return 0
   313  }
   314  
   315  func registerTimer(L *lua.LState) int {
   316  	//a *ActionLua, string for functioin name
   317  	a := L.CheckUserData(1)
   318  	if a == nil {
   319  		L.Push(lua.LString("param 0 not UserData"))
   320  		return 1
   321  	}
   322  	this, ok := a.Value.(*LuaScript)
   323  	if !ok {
   324  		L.Push(lua.LString(fmt.Sprintf("param 0 not LuaScript:%T", a.Value)))
   325  		return 1
   326  	}
   327  
   328  	delay := int64(L.ToNumber(2))
   329  	key := L.ToString(3)
   330  	function := L.ToFunction(4)
   331  
   332  	t, err := this._tf.TestCtx().TimerAdd(delay, this.OnTimerLua, nil)
   333  	if err != nil {
   334  		L.Push(lua.LString(fmt.Sprintf("time Add Failed:%s", err.Error())))
   335  		return 1
   336  	}
   337  
   338  	this._timerCbs[t] = function
   339  	this._timers[key] = t
   340  	return 0
   341  }
   342  
   343  func unRegisterTimer(L *lua.LState) int {
   344  	//a *ActionLua, string for functioin name
   345  	a := L.CheckUserData(1)
   346  	if a == nil {
   347  		L.Push(lua.LString("param 0 not UserData"))
   348  		return 1
   349  	}
   350  	this, ok := a.Value.(*LuaScript)
   351  	if !ok {
   352  		L.Push(lua.LString(fmt.Sprintf("param 0 not LuaScript:%T", a.Value)))
   353  		return 1
   354  	}
   355  
   356  	key := L.ToString(2)
   357  	timer, ok := this._timers[key]
   358  	if !ok {
   359  		L.Push(lua.LString(fmt.Sprintf("timer:%s nil", key)))
   360  		return 1
   361  	}
   362  
   363  	this._tf.TestCtx().TimerDel(timer)
   364  
   365  	delete(this._timers, key)
   366  	delete(this._timerCbs, timer)
   367  	return 0
   368  }
   369  
   370  func actionEnd(L *lua.LState) int {
   371  	//a *ActionLua, string
   372  	a := L.CheckUserData(1)
   373  	if a == nil {
   374  		L.Push(lua.LString("param 0 not UserData"))
   375  		return 1
   376  	}
   377  	this, ok := a.Value.(*LuaScript)
   378  	if !ok {
   379  		L.Push(lua.LString(fmt.Sprintf("param 0 not LuaScript:%T", a.Value)))
   380  		return 1
   381  	}
   382  
   383  	key := L.ToString(2)
   384  	if len(key) == 0 {
   385  		this._tf.OnActionEnd(this, nil, this._cnf.FailContinue)
   386  	} else {
   387  		this._tf.OnActionEnd(this, fmt.Errorf(key), this._cnf.FailContinue)
   388  	}
   389  	return 0
   390  }
   391  
   392  func ctxGet(L *lua.LState) int {
   393  	//a *ActionLua, string
   394  	a := L.CheckUserData(1)
   395  	if a == nil {
   396  		L.Push(lua.LString("param 0 not UserData"))
   397  		return 1
   398  	}
   399  	this, ok := a.Value.(*LuaScript)
   400  	if !ok {
   401  		L.Push(lua.LString(fmt.Sprintf("param 0 not LuaScript:%T", a.Value)))
   402  		return 1
   403  	}
   404  
   405  	key := L.ToString(2)
   406  	v, ok := this._tf.TestCtx().Get(key)
   407  	if !ok {
   408  		L.Push(lua.LBool(true))
   409  		L.Push(lua.LNil)
   410  		return 2
   411  	}
   412  
   413  	switch x := v.(type) {
   414  	case int:
   415  		L.Push(lua.LBool(ok))
   416  		L.Push(lua.LNumber(x))
   417  		return 2
   418  	case int8:
   419  		L.Push(lua.LBool(ok))
   420  		L.Push(lua.LNumber(x))
   421  		return 2
   422  	case int16:
   423  		L.Push(lua.LBool(ok))
   424  		L.Push(lua.LNumber(x))
   425  		return 2
   426  	case int32:
   427  		L.Push(lua.LBool(ok))
   428  		L.Push(lua.LNumber(x))
   429  		return 2
   430  	case int64:
   431  		L.Push(lua.LBool(ok))
   432  		L.Push(lua.LNumber(x))
   433  		return 2
   434  	case uint:
   435  		L.Push(lua.LBool(ok))
   436  		L.Push(lua.LNumber(x))
   437  		return 2
   438  	case uint8:
   439  		L.Push(lua.LBool(ok))
   440  		L.Push(lua.LNumber(x))
   441  		return 2
   442  	case uint16:
   443  		L.Push(lua.LBool(ok))
   444  		L.Push(lua.LNumber(x))
   445  		return 2
   446  	case uint32:
   447  		L.Push(lua.LBool(ok))
   448  		L.Push(lua.LNumber(x))
   449  		return 2
   450  	case uint64:
   451  		L.Push(lua.LBool(ok))
   452  		L.Push(lua.LNumber(x))
   453  		return 2
   454  	case float32:
   455  		L.Push(lua.LBool(ok))
   456  		L.Push(lua.LNumber(x))
   457  		return 2
   458  	case float64:
   459  		L.Push(lua.LBool(ok))
   460  		L.Push(lua.LNumber(x))
   461  		return 2
   462  	case string:
   463  		L.Push(lua.LBool(ok))
   464  		L.Push(lua.LString(x))
   465  		return 2
   466  	case bool:
   467  		L.Push(lua.LBool(ok))
   468  		L.Push(lua.LBool(x))
   469  		return 2
   470  	}
   471  
   472  	L.Push(lua.LBool(false))
   473  	return 1
   474  }
   475  
   476  func ctxSet(L *lua.LState) int {
   477  	//a *ActionLua, string
   478  	a := L.CheckUserData(1)
   479  	if a == nil {
   480  		L.Push(lua.LString("param 0 not UserData"))
   481  		return 1
   482  	}
   483  	this, ok := a.Value.(*LuaScript)
   484  	if !ok {
   485  		L.Push(lua.LString(fmt.Sprintf("param 0 not LuaScript:%T", a.Value)))
   486  		return 1
   487  	}
   488  
   489  	ok = false
   490  	key := L.ToString(2)
   491  	value := L.Get(3)
   492  	switch value.Type() {
   493  	case lua.LTNumber:
   494  		f := float64(lua.LVAsNumber(value))
   495  		x := math.Trunc(f)
   496  		if x == f {
   497  			this._tf.TestCtx().Set(key, int64(x))
   498  		} else {
   499  			this._tf.TestCtx().Set(key, f)
   500  		}
   501  		ok = true
   502  	case lua.LTString:
   503  		this._tf.TestCtx().Set(key, lua.LVAsString(value))
   504  		ok = true
   505  	case lua.LTFunction:
   506  		this._tf.TestCtx().Set(key, value.(*lua.LFunction))
   507  		ok = true
   508  	case lua.LTTable:
   509  		this._tf.TestCtx().Set(key, value.(*lua.LTable))
   510  		ok = true
   511  	case lua.LTChannel:
   512  	case lua.LTUserData:
   513  	case lua.LTThread:
   514  	}
   515  
   516  	L.Push(lua.LBool(ok))
   517  	return 1
   518  }
   519  
   520  func sendMsgWithCnf(L *lua.LState) int {
   521  	//a *ActionLua, string
   522  	a := L.CheckUserData(1)
   523  	if a == nil {
   524  		L.Push(lua.LString("param 0 not UserData"))
   525  		return 1
   526  	}
   527  	this, ok := a.Value.(*LuaScript)
   528  	if !ok {
   529  		L.Push(lua.LString(fmt.Sprintf("param 0 not LuaScript:%T", a.Value)))
   530  		return 1
   531  	}
   532  
   533  	ok = false
   534  	file := L.ToString(2)
   535  	key := L.ToString(3)
   536  
   537  	pCnf, err := protos.NetworkProtoData(file, key)
   538  	if err != nil {
   539  		L.Push(lua.LString(fmt.Sprintf("NetworkProtoData err:%s", err.Error())))
   540  		return 1
   541  	}
   542  
   543  	if pCnf == nil {
   544  		L.Push(lua.LString(fmt.Sprintf("file:%s key:%s nil", file, key)))
   545  		return 1
   546  	}
   547  
   548  	// 实现json数据替换
   549  	data, err := protos.JsonReplace(pCnf.Data, this._tf.TestCtx())
   550  	if err != nil {
   551  		L.Push(lua.LString(fmt.Sprintf("JsonReplace err:%s", err.Error())))
   552  		return 1
   553  	}
   554  
   555  	err = this._tf.TestCtx().Send(pCnf.ID, data)
   556  	if err != nil {
   557  		L.Push(lua.LString(fmt.Sprintf("Send err:%s", err.Error())))
   558  		return 1
   559  	}
   560  
   561  	log.LogInfo("%s action:%d msgSend:%d data:%s", this._tf.LogPre(), this._id, pCnf.ID, data)
   562  	return 0
   563  }
   564  
   565  func sendMsg(L *lua.LState) int {
   566  	//a *ActionLua, string
   567  	a := L.CheckUserData(1)
   568  	if a == nil {
   569  		L.Push(lua.LString("param 0 not UserData"))
   570  		return 1
   571  	}
   572  	this, ok := a.Value.(*LuaScript)
   573  	if !ok {
   574  		L.Push(lua.LString(fmt.Sprintf("param 0 not LuaScript:%T", a.Value)))
   575  		return 1
   576  	}
   577  
   578  	ok = false
   579  	msgId := protos.ProtoID(L.ToNumber(2))
   580  	data := protos.JsonString(L.ToString(3))
   581  
   582  	err := this._tf.TestCtx().Send(msgId, data)
   583  	if err != nil {
   584  		L.Push(lua.LString(fmt.Sprintf("Send err:%s", err.Error())))
   585  		return 1
   586  	}
   587  
   588  	log.LogInfo("%s action:%d msgSend:%d data:%s", this._tf.LogPre(), this._id, msgId, data)
   589  	return 0
   590  }