mirror of
https://github.com/httprunner/httprunner.git
synced 2026-05-13 08:59:44 +08:00
change: update adb logs
This commit is contained in:
@@ -11,12 +11,12 @@ import (
|
||||
func TestAndroidDouYinLive(t *testing.T) {
|
||||
testCase := &hrp.TestCase{
|
||||
Config: hrp.NewConfig("通过 feed 头像进入抖音直播间").
|
||||
SetAndroid(hrp.WithAdbLogOn(true)),
|
||||
SetAndroid(hrp.WithAdbLogOn(true), hrp.WithSerialNumber("2d06bf70")),
|
||||
TestSteps: []hrp.IStep{
|
||||
hrp.NewStep("打开网页").
|
||||
Android().
|
||||
Home().
|
||||
AppTerminate("com.google.android.apps.chrome.Main").Sleep(1). // 关闭已运行的抖音,确保启动抖音后在「抖音」首页
|
||||
AppTerminate("com.google.android.apps.chrome.Main").Sleep(1).
|
||||
SwipeToTapApp("Chrome", hrp.WithMaxRetryTimes(5)).TapByOCR("搜索").Input("https://gtftask.bytedance.com/local-time").TapByOCR("前往").Sleep(5).
|
||||
Validate().
|
||||
AssertOCRExists("1664", "网页打开失败"),
|
||||
|
||||
@@ -2,12 +2,19 @@ package uixt
|
||||
|
||||
import (
|
||||
"bytes"
|
||||
"context"
|
||||
"fmt"
|
||||
"net"
|
||||
"os/exec"
|
||||
"reflect"
|
||||
"regexp"
|
||||
"strconv"
|
||||
"strings"
|
||||
"syscall"
|
||||
|
||||
"github.com/electricbubble/gadb"
|
||||
"github.com/pkg/errors"
|
||||
"github.com/rs/zerolog/log"
|
||||
)
|
||||
|
||||
var (
|
||||
@@ -19,6 +26,15 @@ var (
|
||||
|
||||
const forwardToPrefix = "forward-to-"
|
||||
|
||||
const (
|
||||
regexFloat = `[0-9\.]*`
|
||||
)
|
||||
|
||||
var (
|
||||
regexCompileSwipe = regexp.MustCompile(fmt.Sprintf(`timesec=(%s)\s*startX=(%s)\s*startY=(%s)\s*endX=(%s)\s*endY=(%s)`, regexFloat, regexFloat, regexFloat, regexFloat, regexFloat)) // parse ${var} or $var
|
||||
regexCompileTap = regexp.MustCompile(fmt.Sprintf(`timesec=(%s)\s*x=(%s)\s*y=(%s)`, regexFloat, regexFloat, regexFloat)) // parse ${func1($a, $b)} // parse number
|
||||
)
|
||||
|
||||
func InitUIAClient(device *AndroidDevice) (*DriverExt, error) {
|
||||
var deviceOptions []AndroidDeviceOption
|
||||
if device.SerialNumber != "" {
|
||||
@@ -51,10 +67,10 @@ func InitUIAClient(device *AndroidDevice) (*DriverExt, error) {
|
||||
}
|
||||
|
||||
if device.LogOn {
|
||||
// TODO
|
||||
err = driverExt.StartLogRecording("hrp_adb_log")
|
||||
}
|
||||
|
||||
return driverExt, nil
|
||||
return driverExt, err
|
||||
}
|
||||
|
||||
type AndroidDeviceOption func(*AndroidDevice)
|
||||
@@ -106,6 +122,7 @@ func NewAndroidDevice(options ...AndroidDeviceOption) (device *AndroidDevice, er
|
||||
|
||||
device.SerialNumber = dev.Serial()
|
||||
device.d = dev
|
||||
device.logcat = NewAdbLogcat(serialNumber)
|
||||
return device, nil
|
||||
}
|
||||
|
||||
@@ -114,6 +131,7 @@ func NewAndroidDevice(options ...AndroidDeviceOption) (device *AndroidDevice, er
|
||||
|
||||
type AndroidDevice struct {
|
||||
d gadb.Device
|
||||
logcat *DeviceLogcat
|
||||
SerialNumber string `json:"serial,omitempty" yaml:"serial,omitempty"`
|
||||
IP string `json:"ip,omitempty" yaml:"ip,omitempty"`
|
||||
Port int `json:"port,omitempty" yaml:"port,omitempty"`
|
||||
@@ -152,6 +170,7 @@ func (dev *AndroidDevice) NewUSBDriver(capabilities Capabilities) (driver *uiaDr
|
||||
return nil, err
|
||||
}
|
||||
driver.adbDevice = dev.d
|
||||
driver.logcat = dev.logcat
|
||||
driver.localPort = localPort
|
||||
|
||||
return driver, nil
|
||||
@@ -182,6 +201,151 @@ func getFreePort() (int, error) {
|
||||
return l.Addr().(*net.TCPAddr).Port, nil
|
||||
}
|
||||
|
||||
type DeviceLogcat struct {
|
||||
serial string
|
||||
logBuffer *bytes.Buffer
|
||||
errs []error
|
||||
stopping chan struct{}
|
||||
done chan struct{}
|
||||
cmd *exec.Cmd
|
||||
}
|
||||
|
||||
func NewAdbLogcat(serial string) *DeviceLogcat {
|
||||
return &DeviceLogcat{
|
||||
serial: serial,
|
||||
logBuffer: new(bytes.Buffer),
|
||||
stopping: make(chan struct{}),
|
||||
done: make(chan struct{}),
|
||||
}
|
||||
}
|
||||
|
||||
// CatchLogcatContext starts logcat with timeout context
|
||||
func (l *DeviceLogcat) CatchLogcatContext(timeoutCtx context.Context) (err error) {
|
||||
if err = l.CatchLogcat(); err != nil {
|
||||
return
|
||||
}
|
||||
go func() {
|
||||
select {
|
||||
case <-timeoutCtx.Done():
|
||||
_ = l.Stop()
|
||||
case <-l.stopping:
|
||||
}
|
||||
}()
|
||||
return
|
||||
}
|
||||
|
||||
func (l *DeviceLogcat) Stop() error {
|
||||
select {
|
||||
case <-l.stopping:
|
||||
default:
|
||||
close(l.stopping)
|
||||
<-l.done
|
||||
close(l.done)
|
||||
}
|
||||
return l.Errors()
|
||||
}
|
||||
|
||||
func (l *DeviceLogcat) Errors() (err error) {
|
||||
for _, e := range l.errs {
|
||||
if err != nil {
|
||||
err = fmt.Errorf("%v |[DeviceLogcatErr] %v", err, e)
|
||||
} else {
|
||||
err = fmt.Errorf("[DeviceLogcatErr] %v", e)
|
||||
}
|
||||
}
|
||||
return
|
||||
}
|
||||
|
||||
func (l *DeviceLogcat) CatchLogcat() (err error) {
|
||||
if l.cmd != nil {
|
||||
err = fmt.Errorf("logcat already start")
|
||||
}
|
||||
command := fmt.Sprintf("adb -s %s logcat -c && adb -s %s logcat -v time -s iesqaMonitor:V", l.serial, l.serial)
|
||||
l.cmd = exec.Command("bash", "-c", command)
|
||||
l.cmd.Stderr = l.logBuffer
|
||||
l.cmd.Stdout = l.logBuffer
|
||||
l.cmd.SysProcAttr = &syscall.SysProcAttr{Setpgid: true}
|
||||
if err = l.cmd.Start(); err != nil {
|
||||
return
|
||||
}
|
||||
go func() {
|
||||
<-l.stopping
|
||||
if e := syscall.Kill(-l.cmd.Process.Pid, syscall.SIGKILL); e != nil {
|
||||
l.errs = append(l.errs, fmt.Errorf("kill logcat process err:%v", e))
|
||||
}
|
||||
l.done <- struct{}{}
|
||||
}()
|
||||
return
|
||||
}
|
||||
|
||||
func (l *DeviceLogcat) BufferedLogcat() (err error) {
|
||||
// -d: dump the current buffered logcat result and exits
|
||||
command := fmt.Sprintf("adb -s %s logcat -d", l.serial)
|
||||
cmd := exec.Command("bash", "-c", command)
|
||||
cmd.Stdout = l.logBuffer
|
||||
cmd.Stderr = l.logBuffer
|
||||
if err = cmd.Run(); err != nil {
|
||||
return
|
||||
}
|
||||
return
|
||||
}
|
||||
|
||||
type ExportPoint struct {
|
||||
Start int `json:"start" yaml:"start"`
|
||||
End int `json:"end" yaml:"end"`
|
||||
From interface{} `json:"from" yaml:"from"`
|
||||
To interface{} `json:"to" yaml:"to"`
|
||||
Operation string `json:"operation" yaml:"operation"`
|
||||
Ext string `json:"ext" yaml:"ext"`
|
||||
RunTime int `json:"run_time,omitempty" yaml:"run_time,omitempty"`
|
||||
}
|
||||
|
||||
func ConvertPoints(data string) (eps []ExportPoint) {
|
||||
lines := strings.Split(data, "\n")
|
||||
for _, line := range lines {
|
||||
if strings.Contains(line, "startX") {
|
||||
matched := regexCompileSwipe.FindStringSubmatch(line)
|
||||
if len(matched) != 6 {
|
||||
log.Error().Msg("failed to parse point data")
|
||||
continue
|
||||
}
|
||||
start, _ := strconv.Atoi(matched[1])
|
||||
fromX, _ := strconv.ParseFloat(matched[2], 64)
|
||||
fromY, _ := strconv.ParseFloat(matched[3], 64)
|
||||
toX, _ := strconv.ParseFloat(matched[4], 64)
|
||||
toY, _ := strconv.ParseFloat(matched[5], 64)
|
||||
p := ExportPoint{
|
||||
Start: start,
|
||||
End: start,
|
||||
From: []float64{fromX, fromY},
|
||||
To: []float64{toX, toY},
|
||||
Operation: "Gtf-Drag",
|
||||
Ext: "",
|
||||
}
|
||||
eps = append(eps, p)
|
||||
} else if strings.Contains(line, "x=") {
|
||||
matched := regexCompileTap.FindStringSubmatch(line)
|
||||
if len(matched) != 4 {
|
||||
log.Error().Msg("failed to parse point data")
|
||||
continue
|
||||
}
|
||||
start, _ := strconv.Atoi(matched[1])
|
||||
x, _ := strconv.ParseFloat(matched[2], 64)
|
||||
y, _ := strconv.ParseFloat(matched[3], 64)
|
||||
p := ExportPoint{
|
||||
Start: start,
|
||||
End: start,
|
||||
From: []float64{x, y},
|
||||
To: []float64{x, y},
|
||||
Operation: "Gtf-Tap",
|
||||
Ext: "",
|
||||
}
|
||||
eps = append(eps, p)
|
||||
}
|
||||
}
|
||||
return
|
||||
}
|
||||
|
||||
type UiSelectorHelper struct {
|
||||
value *bytes.Buffer
|
||||
}
|
||||
|
||||
18
hrp/internal/uixt/android_device_test.go
Normal file
18
hrp/internal/uixt/android_device_test.go
Normal file
@@ -0,0 +1,18 @@
|
||||
package uixt
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
"testing"
|
||||
|
||||
"github.com/httprunner/httprunner/v4/hrp/internal/json"
|
||||
)
|
||||
|
||||
func TestConvertPoints(t *testing.T) {
|
||||
data := "09-29 15:02:08.379 I/iesqaMonitor( 9938): [tap]\ttimesec=1664434928378\tstartX=720.000000\tstartY=1462.000000\tendX=1296.000000\tendY=1462.000000\n09-29 15:02:09.433 I/iesqaMonitor( 9938): [tap]\ttimesec=1664434929432\tstartX=720.000000\tstartY=1462.000000\tendX=1296.000000\tendY=1462.000000\n09-29 15:02:10.452 I/iesqaMonitor( 9938): [tap]\ttimesec=1664434930452\tstartX=720.000000\tstartY=1462.000000\tendX=1296.000000\tendY=1462.000000\n09-29 15:02:11.451 I/iesqaMonitor( 9938): [tap]\ttimesec=1664434931450\tstartX=720.000000\tstartY=1462.000000\tendX=1296.000000\tendY=1462.000000\n09-29 15:02:12.491 I/iesqaMonitor( 9938): [tap]\ttimesec=1664434932489\tstartX=720.000000\tstartY=1462.000000\tendX=1296.000000\tendY=1462.000000\n09-29 15:02:16.028 I/iesqaMonitor( 9938): [tap]\ttimesec=1664434936027\tstartX=720.000000\tstartY=1462.000000\tendX=144.000000\tendY=1462.000000\n09-29 15:02:21.424 I/iesqaMonitor( 9938): [tap]\ttimesec=1664434941423\tstartX=720.000000\tstartY=1462.000000\tendX=144.000000\tendY=1462.000000\n09-29 15:02:27.923 I/iesqaMonitor( 9938): [tap]\ttimesec=1664434947922\tstartX=720.000000\tstartY=1462.000000\tendX=144.000000\tendY=1462.000000\n09-29 15:02:33.628 I/iesqaMonitor( 9938): [tap]\ttimesec=1664434953628\tstartX=720.000000\tstartY=1462.000000\tendX=144.000000\tendY=1462.000000\n09-29 15:02:39.347 I/iesqaMonitor( 9938): [tap]\ttimesec=1664434959347\tx=1259.5y=1868.5"
|
||||
eps := ConvertPoints(data)
|
||||
if len(eps) != 10 {
|
||||
t.Fatal()
|
||||
}
|
||||
jsons, _ := json.Marshal(eps)
|
||||
println(fmt.Sprintf("%v", string(jsons)))
|
||||
}
|
||||
@@ -22,6 +22,7 @@ type uiaDriver struct {
|
||||
Driver
|
||||
|
||||
adbDevice gadb.Device
|
||||
logcat *DeviceLogcat
|
||||
localPort int
|
||||
}
|
||||
|
||||
|
||||
@@ -301,6 +301,45 @@ func (dExt *DriverExt) IsImageExist(text string) bool {
|
||||
return err == nil
|
||||
}
|
||||
|
||||
func (dExt *DriverExt) StartLogRecording(identifier string) error {
|
||||
if _, ok := dExt.Driver.(*wdaDriver); ok {
|
||||
log.Info().Msg("start WDA log recording")
|
||||
data := map[string]interface{}{"action": "start", "type": 2, "identifier": identifier}
|
||||
_, err := dExt.triggerWDALog(data)
|
||||
if err != nil {
|
||||
return errors.Wrap(err, "failed to start WDA log recording")
|
||||
}
|
||||
} else {
|
||||
log.Info().Msg("start adb log recording")
|
||||
err := dExt.Driver.(*uiaDriver).logcat.CatchLogcat()
|
||||
if err != nil {
|
||||
return errors.Wrap(err, "failed to start adb log recording")
|
||||
}
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
func (dExt *DriverExt) GetLogs() (interface{}, error) {
|
||||
if _, ok := dExt.Driver.(*wdaDriver); ok {
|
||||
log.Info().Msg("stop WDA log recording")
|
||||
data := map[string]interface{}{"action": "stop"}
|
||||
reply, err := dExt.triggerWDALog(data)
|
||||
if err != nil {
|
||||
return "", errors.Wrap(err, "failed to get WDA logs")
|
||||
}
|
||||
return reply.Value, nil
|
||||
} else {
|
||||
log.Info().Msg("stop adb log recording")
|
||||
err := dExt.Driver.(*uiaDriver).logcat.Stop()
|
||||
if err != nil {
|
||||
println("failed to get adb log recording")
|
||||
//return "", errors.Wrap(err, "failed to get adb log recording")
|
||||
}
|
||||
content := dExt.Driver.(*uiaDriver).logcat.logBuffer.String()
|
||||
return ConvertPoints(content), err
|
||||
}
|
||||
}
|
||||
|
||||
var errActionNotImplemented = errors.New("UI action not implemented")
|
||||
|
||||
func (dExt *DriverExt) DoAction(action MobileAction) error {
|
||||
|
||||
@@ -477,7 +477,7 @@ func (r *HRPRunner) initUIClient(device uixt.Device) (client *uixt.DriverExt, er
|
||||
uuid := device.UUID()
|
||||
|
||||
// avoid duplicate init
|
||||
if uuid == "" && len(r.uiClients) == 1 {
|
||||
if uuid == "" && len(r.uiClients) > 0 {
|
||||
for _, v := range r.uiClients {
|
||||
return v, nil
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user