feat: capture pcap file for iOS

This commit is contained in:
debugtalk
2022-12-15 17:04:58 +08:00
parent 1c8c3e0e80
commit f4af2d131a
13 changed files with 130 additions and 39 deletions

View File

@@ -8,6 +8,7 @@
- feat: run xctest before start ios automation - feat: run xctest before start ios automation
- feat: run step with specified loop times - feat: run step with specified loop times
- feat: add options for FindTexts - feat: add options for FindTexts
- feat: capture pcap file for iOS
- refactor: move all UI APIs to uixt pkg - refactor: move all UI APIs to uixt pkg
- docs: add examples for UI APIs - docs: add examples for UI APIs

View File

@@ -60,6 +60,11 @@ func TestIOSDouyinWorldCupLive(t *testing.T) {
uixt.WithWDAPort(8700), uixt.WithWDAPort(8700),
uixt.WithWDAMjpegPort(8800), uixt.WithWDAMjpegPort(8800),
uixt.WithXCTest("com.gtf.wda.runner.xctrunner"), uixt.WithXCTest("com.gtf.wda.runner.xctrunner"),
// uixt.WithIOSPerfOptions(
// uixt.WithIOSPerfNetwork(true),
// uixt.WithIOSPerfBundleID("com.ss.iphone.ugc.Aweme"),
// ),
uixt.WithIOSPcapOn(true),
), ),
TestSteps: []hrp.IStep{ TestSteps: []hrp.IStep{
hrp.NewStep("启动抖音"). hrp.NewStep("启动抖音").

View File

@@ -549,24 +549,16 @@ func (d *device) GetInterfaceOrientation() (orientation libimobiledevice.Orienta
return return
} }
func (d *device) PcapdService() (pcapd Pcapd, err error) { func (d *device) PcapStart() (lines <-chan []byte, err error) {
// if d.pcapd != nil { if d.pcapd == nil {
// return d.pcapd, nil if _, err = d.lockdownService(); err != nil {
// } return nil, err
if _, err = d.lockdownService(); err != nil { }
return nil, err if d.pcapd, err = d.lockdown.PcapdService(); err != nil {
return nil, err
}
} }
if d.pcapd, err = d.lockdown.PcapdService(); err != nil {
return nil, err
}
pcapd = d.pcapd
return
}
func (d *device) Pcap() (lines <-chan []byte, err error) {
if _, err = d.PcapdService(); err != nil {
return nil, err
}
return d.pcapd.Packet(), nil return d.pcapd.Packet(), nil
} }

View File

@@ -61,8 +61,7 @@ type Device interface {
Syslog() (lines <-chan string, err error) Syslog() (lines <-chan string, err error)
SyslogStop() SyslogStop()
PcapdService() (pcapd Pcapd, err error) PcapStart() (packet <-chan []byte, err error)
Pcap() (packet <-chan []byte, err error)
PcapStop() PcapStop()
Reboot() error Reboot() error

View File

@@ -163,7 +163,7 @@ func TestPerfAll(t *testing.T) {
func TestPcap(t *testing.T) { func TestPcap(t *testing.T) {
setupLockdownSrv(t) setupLockdownSrv(t)
data, err := dev.Pcap() data, err := dev.PcapStart()
if err != nil { if err != nil {
t.Fatal(err) t.Fatal(err)
} }

View File

@@ -134,10 +134,14 @@ func (dev *AndroidDevice) NewDriver(capabilities Capabilities) (driverExt *Drive
return nil, errors.Wrap(err, "failed to init UIA driver") return nil, errors.Wrap(err, "failed to init UIA driver")
} }
driverExt, err = Extend(driver) driverExt, err = NewDriverExt(dev, driver)
if err != nil {
return nil, err
}
err = driverExt.extendCV()
if err != nil { if err != nil {
return nil, errors.Wrap(code.MobileUIDriverError, return nil, errors.Wrap(code.MobileUIDriverError,
fmt.Sprintf("init UIA driver failed: %v", err)) fmt.Sprintf("extend OpenCV failed: %v", err))
} }
if dev.LogOn { if dev.LogOn {
@@ -147,7 +151,6 @@ func (dev *AndroidDevice) NewDriver(capabilities Capabilities) (driverExt *Drive
} }
} }
driverExt.UUID = dev.UUID()
return driverExt, nil return driverExt, nil
} }
@@ -189,6 +192,17 @@ func (dev *AndroidDevice) NewHTTPDriver(capabilities Capabilities) (driver *uiaD
return driver, nil return driver, nil
} }
func (dev *AndroidDevice) StartPcap() error {
// TODO
return nil
}
// StopPcap stops pcap monitor and returns the saved pcap file path
func (dev *AndroidDevice) StopPcap() string {
// TODO
return ""
}
func getFreePort() (int, error) { func getFreePort() (int, error) {
addr, err := net.ResolveTCPAddr("tcp", "localhost:0") addr, err := net.ResolveTCPAddr("tcp", "localhost:0")
if err != nil { if err != nil {

View File

@@ -199,7 +199,7 @@ func WithThreshold(threshold float64) CVOption {
} }
type DriverExt struct { type DriverExt struct {
UUID string // ios udid or android serial Device Device
Driver WebDriver Driver WebDriver
windowSize Size windowSize Size
frame *bytes.Buffer frame *bytes.Buffer
@@ -214,8 +214,11 @@ type DriverExt struct {
CVArgs CVArgs
} }
func extend(driver WebDriver) (dExt *DriverExt, err error) { func NewDriverExt(device Device, driver WebDriver) (dExt *DriverExt, err error) {
dExt = &DriverExt{Driver: driver} dExt = &DriverExt{
Device: device,
Driver: driver,
}
dExt.doneMjpegStream = make(chan bool, 1) dExt.doneMjpegStream = make(chan bool, 1)
// get device window size // get device window size

View File

@@ -927,8 +927,11 @@ func NewData(data map[string]interface{}, options ...DataOption) map[string]inte
// current implemeted device: IOSDevice, AndroidDevice // current implemeted device: IOSDevice, AndroidDevice
type Device interface { type Device interface {
UUID() string UUID() string // ios udid or android serial
NewDriver(capabilities Capabilities) (driverExt *DriverExt, err error) NewDriver(capabilities Capabilities) (driverExt *DriverExt, err error)
StartPcap() error
StopPcap() string
} }
// WebDriver defines methods supported by WebDriver drivers. // WebDriver defines methods supported by WebDriver drivers.

View File

@@ -14,6 +14,7 @@ import (
"net/http" "net/http"
"net/url" "net/url"
"os" "os"
"path/filepath"
"regexp" "regexp"
"strconv" "strconv"
"strings" "strings"
@@ -130,6 +131,12 @@ func WithIOSPerfOptions(options ...gidevice.PerfOption) IOSDeviceOption {
} }
} }
func WithIOSPcapOn(pcapOn bool) IOSDeviceOption {
return func(device *IOSDevice) {
device.PcapOn = pcapOn
}
}
func IOSDevices(udid ...string) (devices []gidevice.Device, err error) { func IOSDevices(udid ...string) (devices []gidevice.Device, err error) {
var usbmux gidevice.Usbmux var usbmux gidevice.Usbmux
if usbmux, err = gidevice.NewUsbmux(); err != nil { if usbmux, err = gidevice.NewUsbmux(); err != nil {
@@ -176,6 +183,9 @@ func GetIOSDeviceOptions(dev *IOSDevice) (deviceOptions []IOSDeviceOption) {
if dev.PerfOptions != nil { if dev.PerfOptions != nil {
deviceOptions = append(deviceOptions, WithIOSPerfOptions(dev.perfOpitons()...)) deviceOptions = append(deviceOptions, WithIOSPerfOptions(dev.perfOpitons()...))
} }
if dev.PcapOn {
deviceOptions = append(deviceOptions, WithIOSPcapOn(true))
}
if dev.XCTestBundleID != "" { if dev.XCTestBundleID != "" {
deviceOptions = append(deviceOptions, WithXCTest(dev.XCTestBundleID)) deviceOptions = append(deviceOptions, WithXCTest(dev.XCTestBundleID))
} }
@@ -243,6 +253,7 @@ type IOSDevice struct {
Port int `json:"port,omitempty" yaml:"port,omitempty"` // WDA remote port Port int `json:"port,omitempty" yaml:"port,omitempty"` // WDA remote port
MjpegPort int `json:"mjpeg_port,omitempty" yaml:"mjpeg_port,omitempty"` // WDA remote MJPEG port MjpegPort int `json:"mjpeg_port,omitempty" yaml:"mjpeg_port,omitempty"` // WDA remote MJPEG port
LogOn bool `json:"log_on,omitempty" yaml:"log_on,omitempty"` LogOn bool `json:"log_on,omitempty" yaml:"log_on,omitempty"`
PcapOn bool `json:"pcap_on,omitempty" yaml:"pcap_on,omitempty"`
XCTestBundleID string `json:"xctest_bundle_id,omitempty" yaml:"xctest_bundle_id,omitempty"` XCTestBundleID string `json:"xctest_bundle_id,omitempty" yaml:"xctest_bundle_id,omitempty"`
// switch to iOS springboard before init WDA session // switch to iOS springboard before init WDA session
@@ -252,6 +263,10 @@ type IOSDevice struct {
SnapshotMaxDepth int `json:"snapshot_max_depth,omitempty" yaml:"snapshot_max_depth,omitempty"` SnapshotMaxDepth int `json:"snapshot_max_depth,omitempty" yaml:"snapshot_max_depth,omitempty"`
AcceptAlertButtonSelector string `json:"accept_alert_button_selector,omitempty" yaml:"accept_alert_button_selector,omitempty"` AcceptAlertButtonSelector string `json:"accept_alert_button_selector,omitempty" yaml:"accept_alert_button_selector,omitempty"`
DismissAlertButtonSelector string `json:"dismiss_alert_button_selector,omitempty" yaml:"dismiss_alert_button_selector,omitempty"` DismissAlertButtonSelector string `json:"dismiss_alert_button_selector,omitempty" yaml:"dismiss_alert_button_selector,omitempty"`
// pcap monitor
pcapStop chan struct{} // stop pcap monitor
pcapFile string // saved pcap file path
} }
func (dev *IOSDevice) UUID() string { func (dev *IOSDevice) UUID() string {
@@ -284,10 +299,14 @@ func (dev *IOSDevice) NewDriver(capabilities Capabilities) (driverExt *DriverExt
} }
} }
driverExt, err = Extend(driver) driverExt, err = NewDriverExt(dev, driver)
if err != nil {
return nil, err
}
err = driverExt.extendCV()
if err != nil { if err != nil {
return nil, errors.Wrap(code.MobileUIDriverError, return nil, errors.Wrap(code.MobileUIDriverError,
fmt.Sprintf("extend WebDriver failed: %v", err)) fmt.Sprintf("extend OpenCV failed: %v", err))
} }
settings, err := driverExt.Driver.SetAppiumSettings(map[string]interface{}{ settings, err := driverExt.Driver.SetAppiumSettings(map[string]interface{}{
"snapshotMaxDepth": dev.SnapshotMaxDepth, "snapshotMaxDepth": dev.SnapshotMaxDepth,
@@ -327,10 +346,69 @@ func (dev *IOSDevice) NewDriver(capabilities Capabilities) (driverExt *DriverExt
}() }()
} }
driverExt.UUID = dev.UUID() if dev.PcapOn {
if err := dev.StartPcap(); err != nil {
return nil, err
}
}
return driverExt, nil return driverExt, nil
} }
func (dev *IOSDevice) StartPcap() error {
packets, err := dev.d.PcapStart()
if err != nil {
return err
}
rootDir, _ := os.Getwd()
dev.pcapFile = filepath.Join(rootDir,
fmt.Sprintf("dump_%s.pcap", time.Now().Format("20060102150405")))
log.Info().Str("pcapFile", dev.pcapFile).Msg("create pcap file")
file, err := os.OpenFile(dev.pcapFile,
os.O_CREATE|os.O_RDWR|os.O_TRUNC, 0o755)
if err != nil {
return err
}
// pcap magic number
// https://www.ietf.org/archive/id/draft-gharris-opsawg-pcap-01.html
_, _ = file.Write([]byte{
0xd4, 0xc3, 0xb2, 0xa1, 0x02, 0x00, 0x04, 0x00,
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
0xff, 0xff, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00,
})
dev.pcapStop = make(chan struct{})
// start pcap monitor
go func() {
for {
select {
case <-dev.pcapStop:
file.Close()
dev.d.PcapStop()
return
case d := <-packets:
_, err = file.Write(d)
if err != nil {
log.Error().Err(err).Msg("write pcap data failed")
}
}
}
}()
return nil
}
// StopPcap stops pcap monitor and returns the saved pcap file path
func (dev *IOSDevice) StopPcap() string {
if dev.pcapStop == nil {
return ""
}
close(dev.pcapStop)
return dev.pcapFile
}
func (dev *IOSDevice) forward(localPort, remotePort int) error { func (dev *IOSDevice) forward(localPort, remotePort int) error {
log.Info().Int("localPort", localPort).Int("remotePort", remotePort). log.Info().Int("localPort", localPort).Int("remotePort", remotePort).
Str("udid", dev.UDID).Msg("forward tcp port") Str("udid", dev.UDID).Msg("forward tcp port")

View File

@@ -42,17 +42,12 @@ const (
DmNotMatch DmNotMatch
) )
// Extend 获得扩展后的 Driver // extendCV 获得扩展后的 Driver
// 并指定匹配阀值, // 并指定匹配阀值,
// 获取当前设备的 Scale // 获取当前设备的 Scale
// 默认匹配模式为 TmCcoeffNormed // 默认匹配模式为 TmCcoeffNormed
// 默认关闭 OpenCV 匹配值计算后的输出 // 默认关闭 OpenCV 匹配值计算后的输出
func Extend(driver WebDriver, options ...CVOption) (dExt *DriverExt, err error) { func (dExt *DriverExt) extendCV(options ...CVOption) (err error) {
dExt, err = extend(driver)
if err != nil {
return nil, err
}
for _, option := range options { for _, option := range options {
option(&dExt.CVArgs) option(&dExt.CVArgs)
} }

View File

@@ -8,8 +8,8 @@ import (
"github.com/rs/zerolog/log" "github.com/rs/zerolog/log"
) )
func Extend(driver WebDriver, options ...CVOption) (dExt *DriverExt, err error) { func (dExt *DriverExt) extendCV(options ...CVOption) (err error) {
return extend(driver) return nil
} }
func (dExt *DriverExt) FindAllImageRect(search string) (rects []image.Rectangle, err error) { func (dExt *DriverExt) FindAllImageRect(search string) (rects []image.Rectangle, err error) {

View File

@@ -636,6 +636,7 @@ func (r *SessionRunner) GetSummary() (*TestCaseSummary, error) {
// stop performance monitor // stop performance monitor
logs["performance"] = client.GetPerfData() logs["performance"] = client.GetPerfData()
logs["pcap"] = client.Device.StopPcap()
caseSummary.Logs = append(caseSummary.Logs, logs) caseSummary.Logs = append(caseSummary.Logs, logs)
} }

View File

@@ -514,7 +514,7 @@ func (r *HRPRunner) initUIClient(uuid string, osType string) (client *uixt.Drive
if r.uiClients == nil { if r.uiClients == nil {
r.uiClients = make(map[string]*uixt.DriverExt) r.uiClients = make(map[string]*uixt.DriverExt)
} }
r.uiClients[client.UUID] = client r.uiClients[client.Device.UUID()] = client
return client, nil return client, nil
} }