Files
httprunner/uixt/cache_test.go

585 lines
15 KiB
Go

package uixt
import (
"testing"
"github.com/httprunner/httprunner/v5/uixt/option"
"github.com/stretchr/testify/assert"
"github.com/stretchr/testify/require"
)
// Helper function to clean up cache before each test
func setupTest() {
CleanupAllDrivers()
}
func TestGetOrCreateXTDriver_EmptySerial_AutoDetect(t *testing.T) {
setupTest()
config := DriverCacheConfig{
Platform: "android",
Serial: "", // Empty serial will be auto-detected by NewAndroidDevice
}
driver, err := GetOrCreateXTDriver(config)
// Auto-detection may succeed or fail depending on test environment
if err != nil {
// If device creation fails (no devices or multiple devices)
assert.Nil(t, driver)
assert.Contains(t, err.Error(), "failed to create XTDriver")
} else {
// If device creation succeeds (exactly one device connected)
assert.NotNil(t, driver)
// Verify that a driver was created and cached with actual serial
drivers := ListCachedDrivers()
assert.Len(t, drivers, 1)
assert.NotEmpty(t, drivers[0].Key) // Serial should be populated with actual device serial
}
}
func TestGetOrCreateXTDriver_EmptySerial_DefaultPlatform(t *testing.T) {
setupTest()
config := DriverCacheConfig{
Platform: "", // Empty platform should default to android in createXTDriverWithConfig
Serial: "", // Empty serial will be auto-detected by NewAndroidDevice
}
driver, err := GetOrCreateXTDriver(config)
// Device creation may succeed or fail depending on test environment
if err != nil {
// If device creation fails (no devices or multiple devices)
assert.Nil(t, driver)
assert.Contains(t, err.Error(), "failed to create XTDriver")
} else {
// If device creation succeeds (exactly one device connected)
assert.NotNil(t, driver)
// Verify that a driver was created and cached with actual serial
drivers := ListCachedDrivers()
assert.Len(t, drivers, 1)
assert.NotEmpty(t, drivers[0].Key) // Serial should be populated with actual device serial
}
}
func TestGetOrCreateXTDriver_WithUnifiedDeviceOptions(t *testing.T) {
setupTest()
// Test creating driver config with unified DeviceOptions
deviceOpts := option.NewDeviceOptions(
option.WithPlatform("android"),
option.WithDeviceSerialNumber("test_device_001"),
option.WithDeviceUIA2(true),
)
config := DriverCacheConfig{
Platform: deviceOpts.Platform,
Serial: deviceOpts.GetSerial(),
DeviceOpts: deviceOpts,
AIOptions: []option.AIServiceOption{
option.WithCVService(option.CVServiceTypeVEDEM),
},
}
// Verify config is properly constructed
assert.Equal(t, "android", config.Platform)
assert.Equal(t, "test_device_001", config.Serial)
assert.NotNil(t, config.DeviceOpts)
assert.Equal(t, "android", config.DeviceOpts.Platform)
assert.Equal(t, "test_device_001", config.DeviceOpts.GetSerial())
}
func TestGetOrCreateXTDriver_DifferentPlatformConfigs(t *testing.T) {
setupTest()
// Test Android config
androidOpts := option.NewDeviceOptions(
option.WithDeviceSerialNumber("android_001"),
option.WithDeviceUIA2(true),
)
androidConfig := DriverCacheConfig{
Platform: "android",
Serial: "android_001",
DeviceOpts: androidOpts,
}
assert.Equal(t, "android", androidConfig.DeviceOpts.Platform)
// Test iOS config
iosOpts := option.NewDeviceOptions(
option.WithDeviceUDID("ios_001"),
option.WithDeviceWDAPort(8100),
)
iosConfig := DriverCacheConfig{
Platform: "ios",
Serial: "ios_001",
DeviceOpts: iosOpts,
}
assert.Equal(t, "ios", iosConfig.DeviceOpts.Platform)
// Test Harmony config
harmonyOpts := option.NewDeviceOptions(
option.WithDeviceConnectKey("harmony_001"),
)
harmonyConfig := DriverCacheConfig{
Platform: "harmony",
Serial: "harmony_001",
DeviceOpts: harmonyOpts,
}
assert.Equal(t, "harmony", harmonyConfig.DeviceOpts.Platform)
// Test Browser config
browserOpts := option.NewDeviceOptions(
option.WithDeviceBrowserID("browser_001"),
option.WithDeviceBrowserPageSize(1920, 1080),
)
browserConfig := DriverCacheConfig{
Platform: "browser",
Serial: "browser_001",
DeviceOpts: browserOpts,
}
assert.Equal(t, "browser", browserConfig.DeviceOpts.Platform)
}
func TestRegisterXTDriver_EmptySerial(t *testing.T) {
setupTest()
err := RegisterXTDriver("", nil)
assert.Error(t, err)
assert.Contains(t, err.Error(), "serial cannot be empty")
}
func TestRegisterXTDriver_NilDriver(t *testing.T) {
setupTest()
err := RegisterXTDriver("test_serial", nil)
assert.Error(t, err)
assert.Contains(t, err.Error(), "driver cannot be nil")
}
func TestRegisterXTDriver_Success(t *testing.T) {
setupTest()
// Create a minimal XTDriver for testing
xtDriver := &XTDriver{}
// Register external driver
err := RegisterXTDriver("external_001", xtDriver)
require.NoError(t, err)
// Verify driver is cached
drivers := ListCachedDrivers()
assert.Len(t, drivers, 1)
assert.Equal(t, "external_001", drivers[0].Key)
assert.Equal(t, int32(1), drivers[0].RefCount)
assert.Equal(t, xtDriver, drivers[0].Item)
}
func TestReleaseXTDriver_NonExistentSerial(t *testing.T) {
setupTest()
// Release non-existent driver should not error
err := ReleaseXTDriver("non_existent")
assert.NoError(t, err)
}
func TestReleaseXTDriver_CleanupWhenZero(t *testing.T) {
setupTest()
// Register driver
xtDriver := &XTDriver{}
err := RegisterXTDriver("cleanup_test", xtDriver)
require.NoError(t, err)
// Verify driver is cached
drivers := ListCachedDrivers()
assert.Len(t, drivers, 1)
// Release driver (ref count goes to 0)
err = ReleaseXTDriver("cleanup_test")
require.NoError(t, err)
// Verify driver is removed from cache
drivers = ListCachedDrivers()
assert.Len(t, drivers, 0)
}
func TestCleanupAllDrivers(t *testing.T) {
setupTest()
// Create multiple drivers
xtDriver1 := &XTDriver{}
xtDriver2 := &XTDriver{}
xtDriver3 := &XTDriver{}
err := RegisterXTDriver("cleanup_all_1", xtDriver1)
require.NoError(t, err)
err = RegisterXTDriver("cleanup_all_2", xtDriver2)
require.NoError(t, err)
err = RegisterXTDriver("cleanup_all_3", xtDriver3)
require.NoError(t, err)
// Verify all drivers are cached
drivers := ListCachedDrivers()
assert.Len(t, drivers, 3)
// Cleanup all drivers
CleanupAllDrivers()
// Verify cache is empty
drivers = ListCachedDrivers()
assert.Len(t, drivers, 0)
}
func TestListCachedDrivers_Empty(t *testing.T) {
setupTest()
drivers := ListCachedDrivers()
assert.Len(t, drivers, 0)
}
func TestListCachedDrivers_Multiple(t *testing.T) {
setupTest()
// Register multiple drivers
xtDriver1 := &XTDriver{}
xtDriver2 := &XTDriver{}
err := RegisterXTDriver("list_test_1", xtDriver1)
require.NoError(t, err)
err = RegisterXTDriver("list_test_2", xtDriver2)
require.NoError(t, err)
// List drivers
drivers := ListCachedDrivers()
assert.Len(t, drivers, 2)
// Verify driver information
serials := make(map[string]bool)
for _, cached := range drivers {
serials[cached.Key] = true
assert.Equal(t, int32(1), cached.RefCount)
assert.NotNil(t, cached.Item)
}
assert.True(t, serials["list_test_1"])
assert.True(t, serials["list_test_2"])
}
func TestDriverCacheConfig_WithoutDeviceOpts(t *testing.T) {
setupTest()
// Test creating config without DeviceOpts
config := DriverCacheConfig{
Platform: "android",
Serial: "default_test",
// DeviceOpts is nil
}
// Verify config structure
assert.Equal(t, "android", config.Platform)
assert.Equal(t, "default_test", config.Serial)
assert.Nil(t, config.DeviceOpts)
}
func TestDriverCacheConfig_DefaultAIOptions(t *testing.T) {
setupTest()
deviceOpts := option.NewDeviceOptions(
option.WithPlatform("android"),
option.WithDeviceSerialNumber("ai_test"),
)
config := DriverCacheConfig{
Platform: deviceOpts.Platform,
Serial: deviceOpts.GetSerial(),
DeviceOpts: deviceOpts,
// AIOptions is empty, should use default
}
// Verify config structure
assert.Equal(t, "android", config.Platform)
assert.Equal(t, "ai_test", config.Serial)
assert.NotNil(t, config.DeviceOpts)
assert.Len(t, config.AIOptions, 0) // Empty AI options
}
func TestConcurrentAccess(t *testing.T) {
setupTest()
// Test concurrent access to cache with GetOrCreateXTDriver
const numGoroutines = 10
const serial = "concurrent_test"
deviceOpts := option.NewDeviceOptions(
option.WithPlatform("android"),
option.WithDeviceSerialNumber(serial),
)
config := DriverCacheConfig{
Platform: deviceOpts.Platform,
Serial: deviceOpts.GetSerial(),
DeviceOpts: deviceOpts,
}
// Create drivers concurrently - this tests the cache's ability to handle concurrent access
results := make(chan *XTDriver, numGoroutines)
errors := make(chan error, numGoroutines)
for i := 0; i < numGoroutines; i++ {
go func(index int) {
driver, err := GetOrCreateXTDriver(config)
results <- driver
errors <- err
}(i)
}
// Collect results
var drivers []*XTDriver
var errorCount int
for i := 0; i < numGoroutines; i++ {
driver := <-results
err := <-errors
if err != nil {
errorCount++
} else {
drivers = append(drivers, driver)
}
}
// All operations should succeed (or all fail if device creation fails)
if errorCount == 0 {
// If device creation succeeds, all drivers should be the same instance
assert.Len(t, drivers, numGoroutines)
firstDriver := drivers[0]
for _, driver := range drivers[1:] {
assert.Equal(t, firstDriver, driver)
}
// Verify ref count
cachedDrivers := ListCachedDrivers()
assert.Len(t, cachedDrivers, 1)
assert.Equal(t, int32(numGoroutines), cachedDrivers[0].RefCount)
} else {
// If device creation fails (expected in test environment), all should fail
assert.Equal(t, numGoroutines, errorCount)
assert.Len(t, drivers, 0)
}
}
func TestIntegrationExample_BasicUsage(t *testing.T) {
setupTest()
// Example 1: Basic external driver registration using unified DeviceOptions
deviceOpts := option.NewDeviceOptions(
option.WithPlatform("android"),
option.WithDeviceSerialNumber("integration_001"),
option.WithDeviceUIA2(true),
)
config := DriverCacheConfig{
Platform: deviceOpts.Platform,
Serial: deviceOpts.GetSerial(),
DeviceOpts: deviceOpts,
AIOptions: []option.AIServiceOption{
option.WithCVService(option.CVServiceTypeVEDEM),
},
}
// Verify config is properly constructed
assert.Equal(t, "android", config.Platform)
assert.Equal(t, "integration_001", config.Serial)
assert.NotNil(t, config.DeviceOpts)
assert.Len(t, config.AIOptions, 1)
}
func TestIntegrationExample_TraditionalWay(t *testing.T) {
setupTest()
// Example 1b: Traditional way (still supported)
xtDriver := &XTDriver{}
// Register using cache API directly
err := RegisterXTDriver("integration_002", xtDriver)
require.NoError(t, err)
// Verify registration
drivers := ListCachedDrivers()
assert.Len(t, drivers, 1)
assert.Equal(t, "integration_002", drivers[0].Key)
// Clean up
err = ReleaseXTDriver("integration_002")
require.NoError(t, err)
}
func TestIntegrationExample_MultipleDevices(t *testing.T) {
setupTest()
// Test multiple devices like in external_driver_example.go
devices := []struct {
platform string
serial string
opts *option.DeviceOptions
}{
{
platform: "android",
serial: "multi_android_001",
opts: option.NewDeviceOptions(
option.WithDeviceSerialNumber("multi_android_001"),
option.WithDeviceUIA2(true),
),
},
{
platform: "ios",
serial: "multi_ios_001",
opts: option.NewDeviceOptions(
option.WithDeviceUDID("multi_ios_001"),
option.WithDeviceWDAPort(8100),
),
},
{
platform: "harmony",
serial: "multi_harmony_001",
opts: option.NewDeviceOptions(
option.WithDeviceConnectKey("multi_harmony_001"),
),
},
{
platform: "browser",
serial: "multi_browser_001",
opts: option.NewDeviceOptions(
option.WithDeviceBrowserID("multi_browser_001"),
option.WithDeviceBrowserPageSize(1920, 1080),
),
},
}
// Create configs for all devices
var configs []DriverCacheConfig
for _, device := range devices {
config := DriverCacheConfig{
Platform: device.platform,
Serial: device.serial,
DeviceOpts: device.opts,
}
configs = append(configs, config)
}
// Verify all configs are properly constructed
assert.Len(t, configs, len(devices))
// Verify each device config
for i, config := range configs {
assert.Equal(t, devices[i].platform, config.Platform)
assert.Equal(t, devices[i].serial, config.Serial)
assert.NotNil(t, config.DeviceOpts)
assert.Equal(t, devices[i].platform, config.DeviceOpts.Platform)
}
}
func TestDeviceOptionsIntegration(t *testing.T) {
setupTest()
// Test unified DeviceOptions with different platforms
testCases := []struct {
name string
platform string
opts []option.DeviceOption
expected string
}{
{
name: "Android with auto-detection",
platform: "",
opts: []option.DeviceOption{
option.WithDeviceSerialNumber("android_auto"),
option.WithDeviceUIA2(true),
},
expected: "android",
},
{
name: "iOS with auto-detection",
platform: "",
opts: []option.DeviceOption{
option.WithDeviceUDID("ios_auto"),
option.WithDeviceWDAPort(8100),
},
expected: "ios",
},
{
name: "Harmony with auto-detection",
platform: "",
opts: []option.DeviceOption{
option.WithDeviceConnectKey("harmony_auto"),
},
expected: "harmony",
},
{
name: "Browser with auto-detection",
platform: "",
opts: []option.DeviceOption{
option.WithDeviceBrowserID("browser_auto"),
option.WithDeviceBrowserPageSize(1920, 1080),
},
expected: "browser",
},
{
name: "Explicit platform setting",
platform: "android",
opts: []option.DeviceOption{
option.WithPlatform("android"),
option.WithDeviceSerialNumber("explicit_android"),
},
expected: "android",
},
}
for _, tc := range testCases {
t.Run(tc.name, func(t *testing.T) {
deviceOpts := option.NewDeviceOptions(tc.opts...)
assert.Equal(t, tc.expected, deviceOpts.Platform)
assert.NotEmpty(t, deviceOpts.GetSerial())
})
}
}
func TestCacheReferenceCountManagement(t *testing.T) {
setupTest()
// Test reference count increment and decrement
xtDriver := &XTDriver{}
serial := "ref_count_test"
// Register driver
err := RegisterXTDriver(serial, xtDriver)
require.NoError(t, err)
// Verify initial ref count
drivers := ListCachedDrivers()
assert.Len(t, drivers, 1)
assert.Equal(t, int32(1), drivers[0].RefCount)
// Simulate multiple references by getting from cache (which increments ref count)
if cachedItem, ok := driverCacheManager.Get(serial); ok {
assert.NotNil(t, cachedItem.Item)
}
// Verify ref count increased
drivers = ListCachedDrivers()
assert.Len(t, drivers, 1)
assert.Equal(t, int32(2), drivers[0].RefCount)
// Release once
err = ReleaseXTDriver(serial)
require.NoError(t, err)
// Verify ref count decreased but driver still cached
drivers = ListCachedDrivers()
assert.Len(t, drivers, 1)
assert.Equal(t, int32(1), drivers[0].RefCount)
// Release again
err = ReleaseXTDriver(serial)
require.NoError(t, err)
// Verify driver removed from cache
drivers = ListCachedDrivers()
assert.Len(t, drivers, 0)
}