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 }