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 ch 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), ch: 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.batchhandleRsp(msg) } }() r.wg.Wait() } type TestCase struct { id string //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 { log.Fatal(err) } b.requested = true r.ch <- b.id } } } //执行请求 func (r *Robot) AddTestCases(tcs []*TestCase) { r.addBuilders(tcs) r.handleReq() } //执行响应 func (r *Robot) batchhandleRsp(msg *pb.UserMessage) { uuid := <-r.ch 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.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 { r.AccountRegister(r.opts.Account, int32(r.opts.ServerId)) //请求Http接口,模拟创建新账号 } } 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) onUserLoaded() { //notify r.RunNotify() //user r.RunUser() //hero r.RunHero() //friend r.RunFriend() //pack // r.RunPack() //task r.RunTask() // story r.RunStory() } //注册账号 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{ { desc: "登录", mainType: "user", subType: "login", req: &pb.UserLoginReq{ Account: account, Sid: sid, }, rsp: &pb.UserLoginResp{}, enabled: true, }, } r.AddTestCases(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) }