feat: implement unified XTDriver cache

This commit is contained in:
lilong.129
2025-05-26 19:39:46 +08:00
parent 1bd2b1ba5e
commit 2569670c7f
6 changed files with 675 additions and 143 deletions

View File

@@ -1 +1 @@
v5.0.0-beta-2505261608
v5.0.0-beta-2505261939

135
runner.go
View File

@@ -236,13 +236,6 @@ func (r *HRPRunner) Run(testcases ...ITestCase) (err error) {
return err
}
// release UI driver session
defer func() {
for _, client := range caseRunner.uixtDrivers {
client.DeleteSession()
}
}()
for it := caseRunner.parametersIterator; it.HasNext(); {
// case runner can run multiple times with different parameters
// each run has its own session runner
@@ -286,10 +279,9 @@ func NewCaseRunner(testcase TestCase, hrpRunner *HRPRunner) (*CaseRunner, error)
hrpRunner = NewRunner(nil)
}
caseRunner := &CaseRunner{
TestCase: testcase,
hrpRunner: hrpRunner,
parser: NewParser(),
uixtDrivers: make(map[string]*uixt.XTDriver),
TestCase: testcase,
hrpRunner: hrpRunner,
parser: NewParser(),
}
config := testcase.Config.Get()
@@ -353,9 +345,6 @@ type CaseRunner struct {
parser *Parser // each CaseRunner init its own Parser
parametersIterator *ParametersIterator
// UI automation clients for iOS and Android, key is udid/serial
uixtDrivers map[string]*uixt.XTDriver
}
func (r *CaseRunner) GetParametersIterator() *ParametersIterator {
@@ -446,6 +435,7 @@ func (r *CaseRunner) parseConfig() (parsedConfig *TConfig, err error) {
}
aiOpts = append(aiOpts, option.WithCVService(parsedConfig.CVService))
var driverConfigs []uixt.DriverCacheConfig
// parse android devices config
for _, androidDeviceOptions := range parsedConfig.Android {
err := r.parseDeviceConfig(androidDeviceOptions, parsedConfig.Variables)
@@ -453,21 +443,12 @@ func (r *CaseRunner) parseConfig() (parsedConfig *TConfig, err error) {
return nil, errors.Wrap(code.InvalidCaseError,
fmt.Sprintf("parse android config failed: %v", err))
}
device, err := uixt.NewAndroidDevice(androidDeviceOptions.Options()...)
if err != nil {
return nil, errors.Wrap(err, "init android device failed")
}
driver, err := device.NewDriver()
if err != nil {
return nil, errors.Wrap(err, "init android driver failed")
}
driverExt, err := uixt.NewXTDriver(driver, aiOpts...)
if err != nil {
return nil, errors.Wrap(err, "init android XTDriver failed")
}
r.RegisterUIXTDriver(androidDeviceOptions.SerialNumber, driverExt)
driverConfigs = append(driverConfigs, uixt.DriverCacheConfig{
Platform: "android",
Serial: androidDeviceOptions.SerialNumber,
AIOptions: aiOpts,
DeviceOpts: option.FromAndroidOptions(androidDeviceOptions),
})
}
// parse iOS devices config
for _, iosDeviceOptions := range parsedConfig.IOS {
@@ -476,21 +457,12 @@ func (r *CaseRunner) parseConfig() (parsedConfig *TConfig, err error) {
return nil, errors.Wrap(code.InvalidCaseError,
fmt.Sprintf("parse ios config failed: %v", err))
}
device, err := uixt.NewIOSDevice(iosDeviceOptions.Options()...)
if err != nil {
return nil, errors.Wrap(err, "init ios device failed")
}
driver, err := device.NewDriver()
if err != nil {
return nil, errors.Wrap(err, "init ios driver failed")
}
driverExt, err := uixt.NewXTDriver(driver, aiOpts...)
if err != nil {
return nil, errors.Wrap(err, "init ios XTDriver failed")
}
r.RegisterUIXTDriver(iosDeviceOptions.UDID, driverExt)
driverConfigs = append(driverConfigs, uixt.DriverCacheConfig{
Platform: "ios",
Serial: iosDeviceOptions.UDID,
AIOptions: aiOpts,
DeviceOpts: option.FromIOSOptions(iosDeviceOptions),
})
}
// parse harmony devices config
for _, harmonyDeviceOptions := range parsedConfig.Harmony {
@@ -499,21 +471,12 @@ func (r *CaseRunner) parseConfig() (parsedConfig *TConfig, err error) {
return nil, errors.Wrap(code.InvalidCaseError,
fmt.Sprintf("parse harmony config failed: %v", err))
}
device, err := uixt.NewHarmonyDevice(harmonyDeviceOptions.Options()...)
if err != nil {
return nil, errors.Wrap(err, "init harmony device failed")
}
driver, err := device.NewDriver()
if err != nil {
return nil, errors.Wrap(err, "init harmony driver failed")
}
driverExt, err := uixt.NewXTDriver(driver, aiOpts...)
if err != nil {
return nil, errors.Wrap(err, "init harmony XTDriver failed")
}
r.RegisterUIXTDriver(harmonyDeviceOptions.ConnectKey, driverExt)
driverConfigs = append(driverConfigs, uixt.DriverCacheConfig{
Platform: "harmony",
Serial: harmonyDeviceOptions.ConnectKey,
AIOptions: aiOpts,
DeviceOpts: option.FromHarmonyOptions(harmonyDeviceOptions),
})
}
// parse browser devices config
for _, browserDeviceOptions := range parsedConfig.Browser {
@@ -522,26 +485,35 @@ func (r *CaseRunner) parseConfig() (parsedConfig *TConfig, err error) {
return nil, errors.Wrap(code.InvalidCaseError,
fmt.Sprintf("parse browser config failed: %v", err))
}
device, err := uixt.NewBrowserDevice(browserDeviceOptions.Options()...)
driverConfigs = append(driverConfigs, uixt.DriverCacheConfig{
Platform: "browser",
Serial: browserDeviceOptions.BrowserID,
AIOptions: aiOpts,
DeviceOpts: option.FromBrowserOptions(browserDeviceOptions),
})
}
// init XTDriver and register to unified cache
for _, driverConfig := range driverConfigs {
driverExt, err := uixt.GetOrCreateXTDriver(driverConfig)
if err != nil {
return nil, errors.Wrap(err, "init browser device failed")
return nil, errors.Wrapf(err, "init %s XTDriver failed", driverConfig.Platform)
}
driver, err := device.NewDriver()
if err != nil {
return nil, errors.Wrap(err, "init browser driver failed")
if err := r.RegisterUIXTDriver(driverConfig.Serial, driverExt); err != nil {
return nil, err
}
driverExt, err := uixt.NewXTDriver(driver, aiOpts...)
if err != nil {
return nil, errors.Wrap(err, "init browser XTDriver failed")
}
r.RegisterUIXTDriver(browserDeviceOptions.BrowserID, driverExt)
}
return parsedConfig, nil
}
func (r *CaseRunner) RegisterUIXTDriver(serial string, driver *uixt.XTDriver) {
r.uixtDrivers[serial] = driver
func (r *CaseRunner) RegisterUIXTDriver(serial string, driver *uixt.XTDriver) error {
if err := uixt.RegisterXTDriver(serial, driver); err != nil {
log.Error().Err(err).Str("serial", serial).Msg("register XTDriver failed")
return err
}
log.Info().Str("serial", serial).Msg("register XTDriver success")
return nil
}
func (r *CaseRunner) parseDeviceConfig(device interface{}, configVariables map[string]interface{}) error {
@@ -580,21 +552,6 @@ func (r *CaseRunner) parseDeviceConfig(device interface{}, configVariables map[s
return nil
}
func (r *CaseRunner) GetUIXTDriver(serial string) (driver *uixt.XTDriver, err error) {
for key, driver := range r.uixtDrivers {
// return the driver with the same serial
if key == serial {
return driver, nil
}
// or return the first driver if serial is empty
if serial == "" {
r.uixtDrivers[serial] = driver
return driver, nil
}
}
return nil, errors.New("no driver found")
}
// each boomer task initiates a new session
// in order to avoid data racing
func (r *CaseRunner) NewSession() *SessionRunner {
@@ -653,12 +610,14 @@ func (r *SessionRunner) Start(givenVars map[string]interface{}) (summary *TestCa
summary.InOut.ConfigVars = config.Variables
// TODO: move to mobile ui step
for uuid, client := range r.caseRunner.uixtDrivers {
// Collect logs from cached drivers
for _, cached := range uixt.ListCachedDrivers() {
// add WDA/UIA logs to summary
logs := map[string]interface{}{
"uuid": uuid,
"uuid": cached.Serial,
}
client := cached.Driver
if client.GetDevice().LogEnabled() {
log, err1 := client.StopCaptureLog()
if err1 != nil {

View File

@@ -692,7 +692,10 @@ func runStepMobileUI(s *SessionRunner, step IStep) (stepResult *StepResult, err
})
// init wda/uia/hdc driver
uiDriver, err := s.caseRunner.GetUIXTDriver(mobileStep.Serial)
config := uixt.DriverCacheConfig{
Serial: mobileStep.Serial,
}
uiDriver, err := uixt.GetOrCreateXTDriver(config)
if err != nil {
return
}

View File

@@ -2,36 +2,222 @@ package uixt
import (
"context"
"fmt"
"strings"
"sync"
"github.com/httprunner/httprunner/v5/uixt/option"
"github.com/rs/zerolog/log"
)
var driverCache sync.Map // key is serial, value is *XTDriver
var driverCache sync.Map // key is serial, value is *CachedXTDriver
// setupXTDriver initializes an XTDriver based on the platform and serial.
func setupXTDriver(_ context.Context, args map[string]any) (*XTDriver, error) {
platform, _ := args["platform"].(string)
serial, _ := args["serial"].(string)
// CachedXTDriver wraps XTDriver with additional cache metadata
type CachedXTDriver struct {
Platform string
Serial string
Driver *XTDriver
RefCount int32 // reference count for resource management
}
// DriverCacheConfig holds configuration for driver creation
type DriverCacheConfig struct {
Platform string
Serial string
AIOptions []option.AIServiceOption
DeviceOpts *option.DeviceOptions // unified device options
}
// GetOrCreateXTDriver gets an existing driver from cache or creates a new one
func GetOrCreateXTDriver(config DriverCacheConfig) (*XTDriver, error) {
cacheKey := config.Serial
if cacheKey == "" {
return nil, fmt.Errorf("serial cannot be empty")
}
// Check if driver exists in cache
if cachedItem, ok := driverCache.Load(cacheKey); ok {
if cached, ok := cachedItem.(*CachedXTDriver); ok {
log.Info().Str("serial", cached.Serial).Msg("Using cached XTDriver")
// Increment reference count
cached.RefCount++
return cached.Driver, nil
}
}
// Create new driver
driverExt, err := createXTDriverWithConfig(config)
if err != nil {
return nil, fmt.Errorf("failed to create XTDriver: %w", err)
}
// Cache the driver
cached := &CachedXTDriver{
Platform: config.Platform,
Driver: driverExt,
Serial: config.Serial,
RefCount: 1,
}
driverCache.Store(cacheKey, cached)
log.Info().
Str("platform", config.Platform).
Str("serial", config.Serial).
Msg("Created and cached new XTDriver")
return driverExt, nil
}
// createXTDriverWithConfig creates a new XTDriver based on configuration
func createXTDriverWithConfig(config DriverCacheConfig) (*XTDriver, error) {
platform := config.Platform
if platform == "" {
log.Warn().Msg("platform is not set, using android as default")
platform = "android"
}
// Check if driver exists in cache
cacheKey := serial
if cachedDriver, ok := driverCache.Load(cacheKey); ok {
if driverExt, ok := cachedDriver.(*XTDriver); ok {
log.Info().Str("platform", platform).Str("serial", serial).Msg("Using cached driver")
return driverExt, nil
if config.Serial == "" {
return nil, fmt.Errorf("serial is empty")
}
// Create device based on platform and configuration
var device IDevice
var err error
// Try to create device with specific options first
if config.DeviceOpts != nil {
switch strings.ToLower(platform) {
case "android":
androidOpts := config.DeviceOpts.ToAndroidOptions().Options()
device, err = NewAndroidDevice(androidOpts...)
case "ios":
iosOpts := config.DeviceOpts.ToIOSOptions().Options()
device, err = NewIOSDevice(iosOpts...)
case "harmony":
harmonyOpts := config.DeviceOpts.ToHarmonyOptions().Options()
device, err = NewHarmonyDevice(harmonyOpts...)
case "browser":
browserOpts := config.DeviceOpts.ToBrowserOptions().Options()
device, err = NewBrowserDevice(browserOpts...)
}
} else {
device, err = NewDeviceWithDefault(platform, config.Serial)
}
if err != nil {
return nil, fmt.Errorf("failed to create device: %w", err)
}
// Create driver
driver, err := device.NewDriver()
if err != nil {
return nil, fmt.Errorf("failed to create driver: %w", err)
}
// Create XTDriver with AI options
aiOpts := config.AIOptions
if len(aiOpts) == 0 {
// Default AI options
aiOpts = []option.AIServiceOption{
option.WithCVService(option.CVServiceTypeVEDEM),
}
}
driverExt, err := NewXTDriverWithDefault(platform, serial)
driverExt, err := NewXTDriver(driver, aiOpts...)
if err != nil {
return nil, err
return nil, fmt.Errorf("failed to create XTDriver: %w", err)
}
// store driver in cache
driverCache.Store(cacheKey, driverExt)
return driverExt, nil
}
// ReleaseXTDriver decrements reference count and removes from cache when count reaches zero
func ReleaseXTDriver(serial string) error {
if cachedItem, ok := driverCache.Load(serial); ok {
if cached, ok := cachedItem.(*CachedXTDriver); ok {
cached.RefCount--
log.Debug().
Str("serial", serial).
Int32("refCount", cached.RefCount).
Msg("Released XTDriver reference")
// If no more references, clean up and remove from cache
if cached.RefCount <= 0 {
driverCache.Delete(serial)
// Clean up driver resources
if err := cached.Driver.DeleteSession(); err != nil {
log.Warn().Err(err).Str("serial", serial).Msg("Failed to delete driver session")
}
log.Info().Str("serial", serial).Msg("Cleaned up XTDriver from cache")
}
}
}
return nil
}
// CleanupAllDrivers cleans up all cached drivers
func CleanupAllDrivers() {
driverCache.Range(func(key, value interface{}) bool {
if serial, ok := key.(string); ok {
if cached, ok := value.(*CachedXTDriver); ok {
// Clean up driver resources
if err := cached.Driver.DeleteSession(); err != nil {
log.Warn().Err(err).Str("serial", serial).Msg("Failed to delete driver session")
}
log.Info().Str("serial", serial).Msg("Cleaned up XTDriver from cache")
}
driverCache.Delete(serial)
}
return true
})
}
// ListCachedDrivers returns information about all cached drivers
func ListCachedDrivers() []CachedXTDriver {
var drivers []CachedXTDriver
driverCache.Range(func(key, value interface{}) bool {
if cached, ok := value.(*CachedXTDriver); ok {
drivers = append(drivers, *cached)
}
return true
})
return drivers
}
// setupXTDriver initializes an XTDriver based on the platform and serial.
// This function is kept for backward compatibility with MCP integration
func setupXTDriver(_ context.Context, args map[string]any) (*XTDriver, error) {
platform, _ := args["platform"].(string)
serial, _ := args["serial"].(string)
config := DriverCacheConfig{
Platform: platform,
Serial: serial,
}
return GetOrCreateXTDriver(config)
}
// RegisterXTDriver registers an externally created XTDriver to the unified cache
func RegisterXTDriver(serial string, driver *XTDriver) error {
if serial == "" {
return fmt.Errorf("serial cannot be empty")
}
if driver == nil {
return fmt.Errorf("driver cannot be nil")
}
cached := &CachedXTDriver{
Driver: driver,
Serial: serial,
RefCount: 1,
}
driverCache.Store(serial, cached)
log.Info().
Str("serial", serial).
Msg("Registered external XTDriver to unified cache")
return nil
}

417
uixt/option/device.go Normal file
View File

@@ -0,0 +1,417 @@
package option
// DeviceOptions unified device options for all platforms using composition
type DeviceOptions struct {
// Common fields
Platform string `json:"platform,omitempty" yaml:"platform,omitempty"`
// Embedded platform-specific options
*AndroidDeviceOptions `json:"android,omitempty" yaml:"android,omitempty"`
*IOSDeviceOptions `json:"ios,omitempty" yaml:"ios,omitempty"`
*HarmonyDeviceOptions `json:"harmony,omitempty" yaml:"harmony,omitempty"`
*BrowserDeviceOptions `json:"browser,omitempty" yaml:"browser,omitempty"`
}
// DeviceOption unified device option function
type DeviceOption func(*DeviceOptions)
// NewDeviceOptions creates a new DeviceOptions with given options
func NewDeviceOptions(opts ...DeviceOption) *DeviceOptions {
config := &DeviceOptions{
AndroidDeviceOptions: &AndroidDeviceOptions{},
IOSDeviceOptions: &IOSDeviceOptions{},
HarmonyDeviceOptions: &HarmonyDeviceOptions{},
BrowserDeviceOptions: &BrowserDeviceOptions{},
}
for _, opt := range opts {
opt(config)
}
// Apply defaults based on platform
config.applyDefaults()
return config
}
// Unified DeviceOption functions
// WithPlatform sets the platform
func WithPlatform(platform string) DeviceOption {
return func(device *DeviceOptions) {
device.Platform = platform
}
}
// WithDeviceLogOn sets log on for any platform
func WithDeviceLogOn(logOn bool) DeviceOption {
return func(device *DeviceOptions) {
// Set LogOn for all platform options to avoid ambiguity
if device.AndroidDeviceOptions != nil {
device.AndroidDeviceOptions.LogOn = logOn
}
if device.IOSDeviceOptions != nil {
device.IOSDeviceOptions.LogOn = logOn
}
if device.HarmonyDeviceOptions != nil {
device.HarmonyDeviceOptions.LogOn = logOn
}
if device.BrowserDeviceOptions != nil {
device.BrowserDeviceOptions.LogOn = logOn
}
}
}
// Android unified options
func WithDeviceSerialNumber(serial string) DeviceOption {
return func(device *DeviceOptions) {
if device.AndroidDeviceOptions != nil {
device.AndroidDeviceOptions.SerialNumber = serial
}
if device.Platform == "" {
device.Platform = "android"
}
}
}
func WithDeviceUIA2(uia2On bool) DeviceOption {
return func(device *DeviceOptions) {
if device.AndroidDeviceOptions != nil {
device.AndroidDeviceOptions.UIA2 = uia2On
}
if device.Platform == "" {
device.Platform = "android"
}
}
}
func WithDeviceUIA2IP(ip string) DeviceOption {
return func(device *DeviceOptions) {
if device.AndroidDeviceOptions != nil {
device.AndroidDeviceOptions.UIA2IP = ip
}
if device.Platform == "" {
device.Platform = "android"
}
}
}
func WithDeviceUIA2Port(port int) DeviceOption {
return func(device *DeviceOptions) {
if device.AndroidDeviceOptions != nil {
device.AndroidDeviceOptions.UIA2Port = port
}
if device.Platform == "" {
device.Platform = "android"
}
}
}
// iOS unified options
func WithDeviceUDID(udid string) DeviceOption {
return func(device *DeviceOptions) {
if device.IOSDeviceOptions != nil {
device.IOSDeviceOptions.UDID = udid
}
if device.Platform == "" {
device.Platform = "ios"
}
}
}
func WithDeviceWireless(on bool) DeviceOption {
return func(device *DeviceOptions) {
if device.IOSDeviceOptions != nil {
device.IOSDeviceOptions.Wireless = on
}
if device.Platform == "" {
device.Platform = "ios"
}
}
}
func WithDeviceWDAPort(port int) DeviceOption {
return func(device *DeviceOptions) {
if device.IOSDeviceOptions != nil {
device.IOSDeviceOptions.WDAPort = port
}
if device.Platform == "" {
device.Platform = "ios"
}
}
}
func WithDeviceWDAMjpegPort(port int) DeviceOption {
return func(device *DeviceOptions) {
if device.IOSDeviceOptions != nil {
device.IOSDeviceOptions.WDAMjpegPort = port
}
if device.Platform == "" {
device.Platform = "ios"
}
}
}
func WithDeviceLazySetup(lazySetup bool) DeviceOption {
return func(device *DeviceOptions) {
if device.IOSDeviceOptions != nil {
device.IOSDeviceOptions.LazySetup = lazySetup
}
if device.Platform == "" {
device.Platform = "ios"
}
}
}
func WithDeviceResetHomeOnStartup(reset bool) DeviceOption {
return func(device *DeviceOptions) {
if device.IOSDeviceOptions != nil {
device.IOSDeviceOptions.ResetHomeOnStartup = reset
}
if device.Platform == "" {
device.Platform = "ios"
}
}
}
func WithDeviceSnapshotMaxDepth(depth int) DeviceOption {
return func(device *DeviceOptions) {
if device.IOSDeviceOptions != nil {
device.IOSDeviceOptions.SnapshotMaxDepth = depth
}
if device.Platform == "" {
device.Platform = "ios"
}
}
}
func WithDeviceAcceptAlertButtonSelector(selector string) DeviceOption {
return func(device *DeviceOptions) {
if device.IOSDeviceOptions != nil {
device.IOSDeviceOptions.AcceptAlertButtonSelector = selector
}
if device.Platform == "" {
device.Platform = "ios"
}
}
}
func WithDeviceDismissAlertButtonSelector(selector string) DeviceOption {
return func(device *DeviceOptions) {
if device.IOSDeviceOptions != nil {
device.IOSDeviceOptions.DismissAlertButtonSelector = selector
}
if device.Platform == "" {
device.Platform = "ios"
}
}
}
// Harmony unified options
func WithDeviceConnectKey(connectKey string) DeviceOption {
return func(device *DeviceOptions) {
if device.HarmonyDeviceOptions != nil {
device.HarmonyDeviceOptions.ConnectKey = connectKey
}
if device.Platform == "" {
device.Platform = "harmony"
}
}
}
// Browser unified options
func WithDeviceBrowserID(browserID string) DeviceOption {
return func(device *DeviceOptions) {
if device.BrowserDeviceOptions != nil {
device.BrowserDeviceOptions.BrowserID = browserID
}
if device.Platform == "" {
device.Platform = "browser"
}
}
}
func WithDeviceBrowserPageSize(width, height int) DeviceOption {
return func(device *DeviceOptions) {
if device.BrowserDeviceOptions != nil {
device.BrowserDeviceOptions.Width = width
device.BrowserDeviceOptions.Height = height
}
if device.Platform == "" {
device.Platform = "browser"
}
}
}
// setAndroidDefaults applies Android platform defaults
func (d *DeviceOptions) setAndroidDefaults() {
if d.AndroidDeviceOptions != nil {
// Apply defaults using existing NewAndroidDeviceOptions logic
d.AndroidDeviceOptions = NewAndroidDeviceOptions(d.AndroidDeviceOptions.Options()...)
}
}
// setIOSDefaults applies iOS platform defaults
func (d *DeviceOptions) setIOSDefaults() {
if d.IOSDeviceOptions != nil {
// Apply defaults using existing NewIOSDeviceOptions logic
d.IOSDeviceOptions = NewIOSDeviceOptions(d.IOSDeviceOptions.Options()...)
}
}
// setHarmonyDefaults applies Harmony platform defaults
func (d *DeviceOptions) setHarmonyDefaults() {
if d.HarmonyDeviceOptions != nil {
// Apply defaults using existing NewHarmonyDeviceOptions logic
d.HarmonyDeviceOptions = NewHarmonyDeviceOptions(d.HarmonyDeviceOptions.Options()...)
}
}
// setBrowserDefaults applies Browser platform defaults
func (d *DeviceOptions) setBrowserDefaults() {
if d.BrowserDeviceOptions != nil {
// Apply defaults using existing NewBrowserDeviceOptions logic
d.BrowserDeviceOptions = NewBrowserDeviceOptions(d.BrowserDeviceOptions.Options()...)
}
}
// applyDefaults applies platform-specific defaults based on the Platform field
func (d *DeviceOptions) applyDefaults() {
switch d.Platform {
case "android":
d.setAndroidDefaults()
case "ios":
d.setIOSDefaults()
case "harmony":
d.setHarmonyDefaults()
case "browser":
d.setBrowserDefaults()
}
}
// GetSerial returns the appropriate serial/identifier for the platform
func (d *DeviceOptions) GetSerial() string {
switch d.Platform {
case "android":
if d.AndroidDeviceOptions != nil {
return d.AndroidDeviceOptions.SerialNumber
}
case "ios":
if d.IOSDeviceOptions != nil {
return d.IOSDeviceOptions.UDID
}
case "harmony":
if d.HarmonyDeviceOptions != nil {
return d.HarmonyDeviceOptions.ConnectKey
}
case "browser":
if d.BrowserDeviceOptions != nil {
return d.BrowserDeviceOptions.BrowserID
}
}
return "" // fallback
}
// GetPlatformOptions returns platform-specific options slice
func (d *DeviceOptions) GetPlatformOptions() interface{} {
switch d.Platform {
case "android":
return d.ToAndroidOptions().Options()
case "ios":
return d.ToIOSOptions().Options()
case "harmony":
return d.ToHarmonyOptions().Options()
case "browser":
return d.ToBrowserOptions().Options()
default:
return nil
}
}
// ToAndroidOptions converts to AndroidDeviceOptions for backward compatibility
func (d *DeviceOptions) ToAndroidOptions() *AndroidDeviceOptions {
if d.AndroidDeviceOptions != nil {
return d.AndroidDeviceOptions
}
return &AndroidDeviceOptions{}
}
// ToIOSOptions converts to IOSDeviceOptions for backward compatibility
func (d *DeviceOptions) ToIOSOptions() *IOSDeviceOptions {
if d.IOSDeviceOptions != nil {
return d.IOSDeviceOptions
}
return &IOSDeviceOptions{}
}
// ToHarmonyOptions converts to HarmonyDeviceOptions for backward compatibility
func (d *DeviceOptions) ToHarmonyOptions() *HarmonyDeviceOptions {
if d.HarmonyDeviceOptions != nil {
return d.HarmonyDeviceOptions
}
return &HarmonyDeviceOptions{}
}
// ToBrowserOptions converts to BrowserDeviceOptions for backward compatibility
func (d *DeviceOptions) ToBrowserOptions() *BrowserDeviceOptions {
if d.BrowserDeviceOptions != nil {
return d.BrowserDeviceOptions
}
return &BrowserDeviceOptions{}
}
// FromAndroidOptions creates DeviceOptions from AndroidDeviceOptions
func FromAndroidOptions(opts *AndroidDeviceOptions) *DeviceOptions {
config := &DeviceOptions{
Platform: "android",
AndroidDeviceOptions: opts,
IOSDeviceOptions: &IOSDeviceOptions{},
HarmonyDeviceOptions: &HarmonyDeviceOptions{},
BrowserDeviceOptions: &BrowserDeviceOptions{},
}
// Apply defaults
config.applyDefaults()
return config
}
// FromIOSOptions creates DeviceOptions from IOSDeviceOptions
func FromIOSOptions(opts *IOSDeviceOptions) *DeviceOptions {
config := &DeviceOptions{
Platform: "ios",
AndroidDeviceOptions: &AndroidDeviceOptions{},
IOSDeviceOptions: opts,
HarmonyDeviceOptions: &HarmonyDeviceOptions{},
BrowserDeviceOptions: &BrowserDeviceOptions{},
}
// Apply defaults
config.applyDefaults()
return config
}
// FromHarmonyOptions creates DeviceOptions from HarmonyDeviceOptions
func FromHarmonyOptions(opts *HarmonyDeviceOptions) *DeviceOptions {
config := &DeviceOptions{
Platform: "harmony",
AndroidDeviceOptions: &AndroidDeviceOptions{},
IOSDeviceOptions: &IOSDeviceOptions{},
HarmonyDeviceOptions: opts,
BrowserDeviceOptions: &BrowserDeviceOptions{},
}
// Apply defaults
config.applyDefaults()
return config
}
// FromBrowserOptions creates DeviceOptions from BrowserDeviceOptions
func FromBrowserOptions(opts *BrowserDeviceOptions) *DeviceOptions {
config := &DeviceOptions{
Platform: "browser",
AndroidDeviceOptions: &AndroidDeviceOptions{},
IOSDeviceOptions: &IOSDeviceOptions{},
HarmonyDeviceOptions: &HarmonyDeviceOptions{},
BrowserDeviceOptions: opts,
}
// Apply defaults
config.applyDefaults()
return config
}

View File

@@ -113,31 +113,6 @@ func (dExt *XTDriver) ExecuteAction(action MobileAction) (err error) {
return nil
}
// NewXTDriverWithDefault is a helper function to create a XTDriver with default options
func NewXTDriverWithDefault(platform, serial string) (*XTDriver, error) {
device, err := NewDeviceWithDefault(platform, serial)
if err != nil {
return nil, err
}
// init driver
driver, err := device.NewDriver()
if err != nil {
return nil, fmt.Errorf("init driver failed: %w", err)
}
if err := driver.Setup(); err != nil {
return nil, fmt.Errorf("setup driver failed: %w", err)
}
// init XTDriver
driverExt, err := NewXTDriver(driver,
option.WithCVService(option.CVServiceTypeVEDEM))
if err != nil {
return nil, fmt.Errorf("init XT driver failed: %w", err)
}
return driverExt, nil
}
// NewDeviceWithDefault is a helper function to create a device with default options
func NewDeviceWithDefault(platform, serial string) (device IDevice, err error) {
if serial == "" {
@@ -146,11 +121,7 @@ func NewDeviceWithDefault(platform, serial string) (device IDevice, err error) {
switch strings.ToLower(platform) {
case "android":
device, err = NewAndroidDevice(
option.WithSerialNumber(serial))
if err != nil {
return
}
device, err = NewAndroidDevice(option.WithSerialNumber(serial))
case "ios":
device, err = NewIOSDevice(
option.WithUDID(serial),
@@ -158,17 +129,13 @@ func NewDeviceWithDefault(platform, serial string) (device IDevice, err error) {
option.WithWDAMjpegPort(8800),
option.WithResetHomeOnStartup(false),
)
if err != nil {
return
}
case "browser":
device, err = NewBrowserDevice(option.WithBrowserID(serial))
if err != nil {
return
}
case "harmony":
device, err = NewHarmonyDevice(option.WithConnectKey(serial))
default:
return nil, fmt.Errorf("invalid platform: %s", platform)
return nil, fmt.Errorf("unsupported platform: %s", platform)
}
return device, nil
return device, err
}