go_dreamfactory/cmd/robot/robot.go
2022-07-13 18:26:07 +08:00

289 lines
6.8 KiB
Go
Raw Blame History

This file contains ambiguous Unicode characters

This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

package robot
import (
"bytes"
"encoding/json"
"fmt"
"go_dreamfactory/comm"
"go_dreamfactory/pb"
"io/ioutil"
"log"
"net/http"
"sync"
"time"
"github.com/gorilla/websocket"
jsoniter "github.com/json-iterator/go"
uuid "github.com/satori/go.uuid"
"google.golang.org/protobuf/proto"
)
type Robot struct {
ws *websocket.Conn
opts *Options
user *pb.DBUser
builderMap map[string]*TestCase
// linkCase *LinkCase
wg sync.WaitGroup
reqCh chan string //请求通道uuid
// rspCh chan string //响应通道uuid
}
func NewRobot(opts *Options) *Robot {
ws, _, err := websocket.DefaultDialer.Dial(opts.WsUrl, nil)
if err != nil {
log.Fatal(err)
}
r := &Robot{
ws: ws,
opts: opts,
builderMap: make(map[string]*TestCase),
reqCh: make(chan string, 1),
// rspCh: make(chan string, 10),
}
return r
}
func (r *Robot) Run() {
log.Print("Robot running...")
log.Printf("websocket %s \n", r.opts.WsUrl)
if r.opts.Create { //创建新用户
r.AccountRegister(r.opts.Account, int32(r.opts.ServerId))
} else {
if r.opts.Account == "" {
log.Fatal("WARNNING: account is required !!!")
}
r.AccountLogin()
}
//处理响应
go func() {
for {
var msg *pb.UserMessage = &pb.UserMessage{}
_, data, err := r.ws.ReadMessage()
if err != nil {
log.Println(err)
}
if err = proto.Unmarshal(data, msg); err != nil {
log.Fatal(err)
}
r.handleRsp(msg)
}
}()
r.wg.Wait()
}
type TestCase struct {
id string //用例ID 如果没有指定,会自动赋值uuid
desc string //用例描述
mainType string //协议类型 L1
subType string //协议类型 L2
req proto.Message //请求类型
rsp proto.Message //响应类型
enabled bool //是否启用
start time.Time //启用时间
requested bool //是否已请求 //请求标识 true已发
print func(rsp proto.Message) //定义打印
next func(robot *Robot, rsp proto.Message) //处理下一层用例请求
}
//添加测试用用例
func (r *Robot) addBuilders(builders []*TestCase) {
for _, b := range builders {
if b.enabled {
if b.id == "" {
uuid := uuid.NewV4().String()
b.id = uuid
r.builderMap[uuid] = b
} else {
r.builderMap[b.id] = b
}
}
}
}
//处理用例,发送请求
func (r *Robot) handleReq() {
for _, b := range r.builderMap {
if b.enabled && b.req != nil && !b.requested {
r.wg.Add(1)
time.Sleep(time.Second * 1)
b.start = time.Now()
head := &pb.UserMessage{MainType: b.mainType, SubType: b.subType}
defer traceFunc(head.MainType, head.SubType, r.user.GetUid(), b.req)
err := r.SendToClient(head, b.req)
if err != nil {
delete(r.builderMap, b.id)
log.Print(err)
continue
}
b.requested = true
r.reqCh <- b.id
}
}
}
//加入用例并执行请求
func (r *Robot) addTestCaseAndReq(tcs []*TestCase) {
r.addBuilders(tcs)
r.handleReq()
}
//处理响应
func (r *Robot) handleRsp(msg *pb.UserMessage) {
uuid := <-r.reqCh
if uuid == "" {
log.Printf("[%v.%v] uuid is empty", msg.MainType, msg.SubType)
return
}
if v, ok := r.builderMap[uuid]; ok {
if v.enabled &&
(msg.MainType == v.mainType &&
msg.SubType == v.subType) &&
v.requested {
if !comm.ProtoUnmarshal(msg, v.rsp) {
return
}
//执行自定义打印
if v.print == nil {
printReply(msg, v)
} else {
fmt.Println()
fmt.Printf("===== %s [%s.%s]=====\n", v.desc, msg.MainType, msg.SubType)
v.print(v.rsp)
fmt.Println("==============================")
}
//处理下一层用例
if v.next != nil {
v.next(r, v.rsp)
}
if msg.MainType == "user" && msg.SubType == "login" {
r.loginCallback(v.rsp)
} else {
//清除已执行的用例
delete(r.builderMap, v.id)
}
// r.rspCh <- v.id
r.wg.Done()
}
}
}
//登录回调
func (r *Robot) loginCallback(rsp proto.Message) {
//清除登录用例
delete(r.builderMap, "login")
lr := rsp.(*pb.UserLoginResp)
if lr.Data != nil {
r.user = lr.Data
r.onUserLoaded()
} else {
//请求Http接口模拟创建新账号
r.AccountRegister(r.opts.Account, int32(r.opts.ServerId))
}
}
//发送消息
func (r *Robot) SendToClient(msg *pb.UserMessage, rsp proto.Message) error {
//模拟客户端 每次请求都会生成新的秘钥
msg.Sec = r.BuildSecStr()
if comm.ProtoMarshal(rsp, msg) {
data, _ := proto.Marshal(msg)
return r.ws.WriteMessage(websocket.BinaryMessage, data)
}
return nil
}
//注册账号
func (r *Robot) AccountRegister(account string, sid int32) {
if account == "" {
log.Fatal("account value is empty")
}
//http
regReq := &pb.UserRegisterReq{Account: account, Sid: sid}
jsonByte, _ := json.Marshal(regReq)
req, err := http.NewRequest("POST", r.opts.RegUrl, bytes.NewReader(jsonByte))
if err != nil {
log.Fatalf("account register err %v", err)
}
req.Header.Set("Content-Type", "application/json;charset=UTF-8")
httpClient := &http.Client{}
rsp, err := httpClient.Do(req)
if err != nil {
panic(err)
}
defer rsp.Body.Close()
body, _ := ioutil.ReadAll(rsp.Body)
regRsp := &pb.UserRegisterResp{}
err = jsoniter.Unmarshal(body, regRsp)
if regRsp.Code == pb.ErrorCode_Success { //注册成功
fmt.Printf("account:%s 注册成功", regRsp.Account)
//登录
var user_builders = []*TestCase{
{
id: "login",
desc: "登录",
mainType: "user",
subType: "login",
req: &pb.UserLoginReq{
Account: account,
Sid: sid,
},
rsp: &pb.UserLoginResp{},
enabled: true,
},
}
r.addTestCaseAndReq(user_builders)
}
}
//打印响应
func printReply(msg *pb.UserMessage, builder *TestCase) {
if m, ok := builder.rsp.(*pb.NotifyErrorNotifyPush); ok {
var tt time.Duration
if builder.start.IsZero() {
tt = time.Duration(0)
} else {
tt = time.Since(builder.start)
}
log.Printf("rsp %s [%v] [%s.%s] [%v:%v]", builder.desc, tt, m.ReqMainType, m.ReqSubType, int32(m.Code), m.Data)
} else {
log.Printf("rsp %s [%v] [%s.%s] [%v]", builder.desc, time.Since(builder.start), msg.MainType, msg.SubType, builder.rsp)
}
}
//方法参数跟踪
func traceFunc(module string, funcName string, uid string, funcArgs interface{}) {
log.Printf("req [%s.%s] [%s] [%v]", module, funcName, uid, funcArgs)
}
//在这里添加玩家成功登录以后的测试方法
//次方法在用户登录成功后调用
func (r *Robot) onUserLoaded() {
//notify
r.RunNotify()
//user
r.RunUser()
//hero
r.RunHero()
//friend
r.RunFriend()
//pack
// r.RunPack()
//task
r.RunTask()
// story
r.RunStory()
}