From 0d44e580b843c54420de9f24d4b25df28ac85e6c Mon Sep 17 00:00:00 2001 From: Le Zhou <2428499107@qq.com> Date: Mon, 6 Mar 2023 17:43:26 +0800 Subject: [PATCH] DeviceManager refactor2: Split deviceManger into agentManagementService and testDeviceManager (#332) * Split deviceManger into agentManagementService and testDeviceManager * style format * Move DeviceType to outside * delete todo --- .../agent/config/AppConfiguration.java | 82 +++-- .../agent/config/TestRunnerConfig.java | 38 +- .../runner/DeviceTaskControlExecutor.java | 6 +- .../hydralab/agent/runner/TestRunner.java | 15 +- .../runner/appium/AppiumCrossRunner.java | 6 +- .../agent/runner/appium/AppiumListener.java | 40 ++- .../agent/runner/appium/AppiumRunner.java | 17 +- .../agent/runner/appium/Junit5Listener.java | 11 +- .../agent/runner/espresso/EspressoRunner.java | 17 +- .../EspressoTestInfoProcessorListener.java | 13 +- .../agent/runner/monkey/AdbMonkeyRunner.java | 12 +- .../runner/monkey/AppiumMonkeyRunner.java | 7 +- .../agent/runner/smart/SmartRunner.java | 12 +- .../hydralab/agent/runner/t2c/T2CRunner.java | 6 +- .../agent/service/DeviceControlService.java | 15 +- .../agent/service/TestTaskEngineService.java | 12 +- .../common/entity/common/DeviceInfo.java | 17 +- .../hydralab/common/logger/LogCollector.java | 1 + .../logger/impl/ADBLogcatCollector.java | 6 +- .../common/logger/impl/IOSLogCollector.java | 6 +- .../management/AgentManagementService.java | 194 ++++++++++ .../hydralab/common/management/AgentType.java | 1 + .../common/management/device/DeviceType.java | 20 ++ .../management/device/TestDeviceManager.java | 240 +++++-------- .../device/impl/AndroidTestDeviceManager.java | 335 +++++++++--------- .../device/impl/IOSTestDeviceManager.java | 129 +++---- .../device/impl/WindowsTestDeviceManager.java | 30 +- .../listener/impl/DeviceStabilityMonitor.java | 5 +- .../listener/impl/PreInstallListener.java | 32 +- 29 files changed, 746 insertions(+), 579 deletions(-) create mode 100644 common/src/main/java/com/microsoft/hydralab/common/management/AgentManagementService.java create mode 100644 common/src/main/java/com/microsoft/hydralab/common/management/device/DeviceType.java diff --git a/agent/src/main/java/com/microsoft/hydralab/agent/config/AppConfiguration.java b/agent/src/main/java/com/microsoft/hydralab/agent/config/AppConfiguration.java index e903f7d44..205fdc5a3 100644 --- a/agent/src/main/java/com/microsoft/hydralab/agent/config/AppConfiguration.java +++ b/agent/src/main/java/com/microsoft/hydralab/agent/config/AppConfiguration.java @@ -10,6 +10,8 @@ import com.microsoft.hydralab.agent.runner.smart.SmartTestUtil; import com.microsoft.hydralab.agent.service.AgentWebSocketClientService; import com.microsoft.hydralab.agent.socket.AgentWebSocketClient; +import com.microsoft.hydralab.common.file.StorageServiceClientProxy; +import com.microsoft.hydralab.common.management.AgentManagementService; import com.microsoft.hydralab.common.management.AgentType; import com.microsoft.hydralab.common.management.AppiumServerManager; import com.microsoft.hydralab.common.management.device.TestDeviceManager; @@ -19,7 +21,6 @@ import com.microsoft.hydralab.common.monitor.MetricPushGateway; import com.microsoft.hydralab.common.util.ADBOperateUtil; import com.microsoft.hydralab.common.util.Const; -import com.microsoft.hydralab.common.file.StorageServiceClientProxy; import io.micrometer.core.instrument.MeterRegistry; import io.prometheus.client.CollectorRegistry; import io.prometheus.client.exporter.PushGateway; @@ -86,6 +87,44 @@ private File getScreenshotDir() { return dir; } + @Bean + public AgentManagementService agentManagementService(StorageServiceClientProxy storageServiceClientProxy, + DeviceStatusListenerManager deviceStatusListenerManager) { + AgentManagementService agentManagementService = new AgentManagementService(); + File testBaseDir = new File(appOptions.getTestCaseResultLocation()); + if (!testBaseDir.exists()) { + if (!testBaseDir.mkdirs()) { + throw new RuntimeException("agentManager dir.mkdirs() failed: " + testBaseDir); + } + } + agentManagementService.setTestBaseDir(testBaseDir); + File preAppDir = new File(appOptions.getPreAppStorageLocation()); + if (!preAppDir.exists()) { + if (!preAppDir.mkdirs()) { + throw new RuntimeException("agentManager dir.mkdirs() failed: " + preAppDir); + } + } + agentManagementService.setPreAppDir(preAppDir); + agentManagementService.setPreInstallFailurePolicy( + shutdownIfFail ? Const.PreInstallFailurePolicy.SHUTDOWN : Const.PreInstallFailurePolicy.IGNORE); + agentManagementService.setDeviceStatusListenerManager(deviceStatusListenerManager); + agentManagementService.setTestBaseDirUrlMapping(AppOptions.TEST_CASE_RESULT_STORAGE_MAPPING_REL_PATH); + File deviceLogBaseDir = new File(appOptions.getDeviceLogStorageLocation()); + if (!deviceLogBaseDir.exists()) { + if (!deviceLogBaseDir.mkdirs()) { + throw new RuntimeException("agentManager dir.mkdirs() failed: " + deviceLogBaseDir); + } + } + agentManagementService.setDeviceLogBaseDir(deviceLogBaseDir); + agentManagementService.setStorageServiceClientProxy(storageServiceClientProxy); + + agentManagementService.setScreenshotDir(getScreenshotDir()); + agentManagementService.setDeviceFolderUrlPrefix(AppOptions.DEVICE_STORAGE_MAPPING_REL_PATH); + agentManagementService.setDeviceStoragePath(appOptions.getDeviceStorageLocation()); + + return agentManagementService; + } + @Bean public AgentWebSocketClient agentWebSocketClient(AgentWebSocketClientService agentWebSocketClientService) throws Exception { @@ -98,8 +137,9 @@ public AgentWebSocketClient agentWebSocketClient(AgentWebSocketClientService age } @Bean - public TestDeviceManager initDeviceManager(StorageServiceClientProxy storageServiceClientProxy, ADBOperateUtil adbOperateUtil, AppiumServerManager appiumServerManager, - DeviceStatusListenerManager deviceStatusListenerManager) { + public TestDeviceManager initDeviceManager(AgentManagementService agentManagementService, + ADBOperateUtil adbOperateUtil, + AppiumServerManager appiumServerManager) { AgentType agentType = AgentType.formAgentType(agentTypeValue); TestDeviceManager testDeviceManager = agentType.getManager(); if (testDeviceManager instanceof AndroidTestDeviceManager) { @@ -109,36 +149,6 @@ public TestDeviceManager initDeviceManager(StorageServiceClientProxy storageServ logger.info("Setting the adb server hostname to {}", adbServerHost); adbOperateUtil.setAdbServerHost(adbServerHost); } - File testBaseDir = new File(appOptions.getTestCaseResultLocation()); - if (!testBaseDir.exists()) { - if (!testBaseDir.mkdirs()) { - throw new RuntimeException("adbDeviceManager dir.mkdirs() failed: " + testBaseDir); - } - } - testDeviceManager.setTestBaseDir(testBaseDir); - File preAppDir = new File(appOptions.getPreAppStorageLocation()); - if (!preAppDir.exists()) { - if (!preAppDir.mkdirs()) { - throw new RuntimeException("adbDeviceManager dir.mkdirs() failed: " + preAppDir); - } - } - testDeviceManager.setPreAppDir(preAppDir); - testDeviceManager.setPreInstallFailurePolicy( - shutdownIfFail ? Const.PreInstallFailurePolicy.SHUTDOWN : Const.PreInstallFailurePolicy.IGNORE); - testDeviceManager.setDeviceStatusListenerManager(deviceStatusListenerManager); - testDeviceManager.setTestBaseDirUrlMapping(AppOptions.TEST_CASE_RESULT_STORAGE_MAPPING_REL_PATH); - File deviceLogBaseDir = new File(appOptions.getDeviceLogStorageLocation()); - if (!deviceLogBaseDir.exists()) { - if (!deviceLogBaseDir.mkdirs()) { - throw new RuntimeException("adbDeviceManager dir.mkdirs() failed: " + deviceLogBaseDir); - } - } - testDeviceManager.setDeviceLogBaseDir(deviceLogBaseDir); - testDeviceManager.setStorageServiceClientProxy(storageServiceClientProxy); - - testDeviceManager.setScreenshotDir(getScreenshotDir()); - testDeviceManager.setDeviceFolderUrlPrefix(AppOptions.DEVICE_STORAGE_MAPPING_REL_PATH); - testDeviceManager.setDeviceStoragePath(appOptions.getDeviceStorageLocation()); if (StringUtils.isNotBlank(appiumServerHost)) { logger.info("Setting the appium server hostname to {}", appiumServerHost); @@ -146,6 +156,7 @@ public TestDeviceManager initDeviceManager(StorageServiceClientProxy storageServ } appiumServerManager.setWorkspacePath(appOptions.getLocation()); + testDeviceManager.setAgentManagementService(agentManagementService); testDeviceManager.setAppiumServerManager(appiumServerManager); return testDeviceManager; } @@ -177,13 +188,14 @@ public SmartTestUtil smartTestUtil() { } @Bean - public DeviceStabilityMonitor deviceStabilityMonitor(TestDeviceManager testDeviceManager, MeterRegistry meterRegistry) { + public DeviceStabilityMonitor deviceStabilityMonitor(AgentManagementService agentManagementService, + MeterRegistry meterRegistry) { DeviceStabilityMonitor deviceStabilityMonitor = new DeviceStabilityMonitor(); deviceStabilityMonitor.setDeviceStateChangeThreshold(deviceStateChangeThreshold); deviceStabilityMonitor.setDeviceStateChangeWindowTime(deviceStateChangeWindowTime); deviceStabilityMonitor.setDeviceStateChangeRecoveryTime(deviceStateChangeRecoveryTime); - deviceStabilityMonitor.setTestDeviceManager(testDeviceManager); + deviceStabilityMonitor.setAgentManagementService(agentManagementService); deviceStabilityMonitor.setMeterRegistry(meterRegistry); return deviceStabilityMonitor; diff --git a/agent/src/main/java/com/microsoft/hydralab/agent/config/TestRunnerConfig.java b/agent/src/main/java/com/microsoft/hydralab/agent/config/TestRunnerConfig.java index d92b34b48..619e38245 100644 --- a/agent/src/main/java/com/microsoft/hydralab/agent/config/TestRunnerConfig.java +++ b/agent/src/main/java/com/microsoft/hydralab/agent/config/TestRunnerConfig.java @@ -14,7 +14,7 @@ import com.microsoft.hydralab.agent.runner.t2c.T2CRunner; import com.microsoft.hydralab.agent.service.TestTaskEngineService; import com.microsoft.hydralab.common.entity.common.TestTask; -import com.microsoft.hydralab.common.management.device.TestDeviceManager; +import com.microsoft.hydralab.common.management.AgentManagementService; import com.microsoft.hydralab.common.util.ADBOperateUtil; import com.microsoft.hydralab.performance.PerformanceTestManagementService; import org.springframework.beans.factory.annotation.Value; @@ -49,54 +49,62 @@ public PerformanceTestManagementService performanceTestManagementService() { } @Bean - public EspressoRunner espressoRunner(TestDeviceManager testDeviceManager, TestTaskEngineService testTaskEngineService, + public EspressoRunner espressoRunner(AgentManagementService agentManagementService, + TestTaskEngineService testTaskEngineService, PerformanceTestManagementService performanceTestManagementService, ADBOperateUtil adbOperateUtil) { - return new EspressoRunner(testDeviceManager, testTaskEngineService, performanceTestManagementService, + return new EspressoRunner(agentManagementService, testTaskEngineService, performanceTestManagementService, adbOperateUtil); } @Bean - public AdbMonkeyRunner adbMonkeyRunner(TestDeviceManager testDeviceManager, TestTaskEngineService testTaskEngineService, + public AdbMonkeyRunner adbMonkeyRunner(AgentManagementService agentManagementService, + TestTaskEngineService testTaskEngineService, PerformanceTestManagementService performanceTestManagementService, ADBOperateUtil adbOperateUtil) { - return new AdbMonkeyRunner(testDeviceManager, testTaskEngineService, performanceTestManagementService, + return new AdbMonkeyRunner(agentManagementService, testTaskEngineService, performanceTestManagementService, adbOperateUtil); } @Bean - public AppiumMonkeyRunner appiumMonkeyRunner(TestDeviceManager testDeviceManager, + public AppiumMonkeyRunner appiumMonkeyRunner(AgentManagementService agentManagementService, TestTaskEngineService testTaskEngineService, PerformanceTestManagementService performanceTestManagementService) { - return new AppiumMonkeyRunner(testDeviceManager, testTaskEngineService, performanceTestManagementService); + return new AppiumMonkeyRunner(agentManagementService, testTaskEngineService, + performanceTestManagementService); } @Bean - public AppiumRunner appiumRunner(TestDeviceManager testDeviceManager, TestTaskEngineService testTaskEngineService, + public AppiumRunner appiumRunner(AgentManagementService agentManagementService, + TestTaskEngineService testTaskEngineService, PerformanceTestManagementService performanceTestManagementService) { - return new AppiumRunner(testDeviceManager, testTaskEngineService, performanceTestManagementService); + return new AppiumRunner(agentManagementService, testTaskEngineService, performanceTestManagementService); } @Bean - public AppiumCrossRunner appiumCrossRunner(TestDeviceManager testDeviceManager, + public AppiumCrossRunner appiumCrossRunner(AgentManagementService agentManagementService, TestTaskEngineService testTaskEngineService, PerformanceTestManagementService performanceTestManagementService) { - return new AppiumCrossRunner(testDeviceManager, testTaskEngineService, performanceTestManagementService, + return new AppiumCrossRunner(agentManagementService, testTaskEngineService, + performanceTestManagementService, agentName); } @Bean - public SmartRunner smartRunner(TestDeviceManager testDeviceManager, TestTaskEngineService testTaskEngineService, + public SmartRunner smartRunner(AgentManagementService agentManagementService, + TestTaskEngineService testTaskEngineService, PerformanceTestManagementService performanceTestManagementService, SmartTestUtil smartTestUtil) { - return new SmartRunner(testDeviceManager, testTaskEngineService, performanceTestManagementService, + return new SmartRunner(agentManagementService, testTaskEngineService, performanceTestManagementService, smartTestUtil); } @Bean - public T2CRunner t2cRunner(TestDeviceManager testDeviceManager, TestTaskEngineService testTaskEngineService, + public T2CRunner t2cRunner(AgentManagementService agentManagementService, + TestTaskEngineService testTaskEngineService, PerformanceTestManagementService performanceTestManagementService) { - return new T2CRunner(testDeviceManager, testTaskEngineService, performanceTestManagementService, agentName); + return new T2CRunner(agentManagementService, testTaskEngineService, performanceTestManagementService, + agentName); } @ConfigurationProperties(prefix = "app.device-script.commands") diff --git a/agent/src/main/java/com/microsoft/hydralab/agent/runner/DeviceTaskControlExecutor.java b/agent/src/main/java/com/microsoft/hydralab/agent/runner/DeviceTaskControlExecutor.java index cd4a899fa..9a46c7b7f 100644 --- a/agent/src/main/java/com/microsoft/hydralab/agent/runner/DeviceTaskControlExecutor.java +++ b/agent/src/main/java/com/microsoft/hydralab/agent/runner/DeviceTaskControlExecutor.java @@ -5,14 +5,12 @@ import com.microsoft.hydralab.common.entity.agent.DeviceTaskControl; import com.microsoft.hydralab.common.entity.common.DeviceInfo; -import com.microsoft.hydralab.common.management.device.TestDeviceManager; import com.microsoft.hydralab.common.util.ThreadPoolUtil; import org.jetbrains.annotations.Nullable; import org.slf4j.Logger; import org.slf4j.LoggerFactory; import org.springframework.stereotype.Service; -import javax.annotation.Resource; import java.util.Collection; import java.util.HashSet; import java.util.Set; @@ -22,8 +20,6 @@ public class DeviceTaskControlExecutor { @SuppressWarnings("constantname") static final Logger log = LoggerFactory.getLogger(DeviceTaskControlExecutor.class); - @Resource - TestDeviceManager testDeviceManager; @Nullable public DeviceTaskControl runForAllDeviceAsync(Collection allDevices, DeviceTask task, @@ -72,7 +68,7 @@ public DeviceTaskControl runForAllDeviceAsync(Collection allDevices, } Logger logger = null; if (logging) { - logger = testDeviceManager.getDeviceLogger(device); + logger = device.getTestDeviceManager().getDeviceLogger(device); } final Logger fLogger = logger; devices.add(device); diff --git a/agent/src/main/java/com/microsoft/hydralab/agent/runner/TestRunner.java b/agent/src/main/java/com/microsoft/hydralab/agent/runner/TestRunner.java index 7f9dddc71..35ed6dfab 100644 --- a/agent/src/main/java/com/microsoft/hydralab/agent/runner/TestRunner.java +++ b/agent/src/main/java/com/microsoft/hydralab/agent/runner/TestRunner.java @@ -8,6 +8,7 @@ import com.microsoft.hydralab.common.entity.common.DeviceInfo; import com.microsoft.hydralab.common.entity.common.TestRun; import com.microsoft.hydralab.common.entity.common.TestTask; +import com.microsoft.hydralab.common.management.AgentManagementService; import com.microsoft.hydralab.common.management.device.TestDeviceManager; import com.microsoft.hydralab.common.management.device.impl.IOSTestDeviceManager; import com.microsoft.hydralab.common.util.DateUtil; @@ -29,16 +30,17 @@ import java.util.concurrent.TimeoutException; public abstract class TestRunner { - protected final Logger log = LoggerFactory.getLogger(TestDeviceManager.class); - protected final TestDeviceManager testDeviceManager; + protected final Logger log = LoggerFactory.getLogger(TestRunner.class); + protected final AgentManagementService agentManagementService; protected final TestTaskRunCallback testTaskRunCallback; protected final PerformanceTestManagementService performanceTestManagementService; protected final XmlBuilder xmlBuilder = new XmlBuilder(); protected final ActionExecutor actionExecutor = new ActionExecutor(); + protected TestDeviceManager testDeviceManager; - public TestRunner(TestDeviceManager testDeviceManager, TestTaskRunCallback testTaskRunCallback, + public TestRunner(AgentManagementService agentManagementService, TestTaskRunCallback testTaskRunCallback, PerformanceTestManagementService performanceTestManagementService) { - this.testDeviceManager = testDeviceManager; + this.agentManagementService = agentManagementService; this.testTaskRunCallback = testTaskRunCallback; this.performanceTestManagementService = performanceTestManagementService; } @@ -177,7 +179,7 @@ protected void tearDown(DeviceInfo deviceInfo, TestTask testTask, TestRun testRu if (testRun.getTotalCount() > 0) { try { String absoluteReportPath = xmlBuilder.buildTestResultXml(testTask, testRun); - testRun.setTestXmlReportPath(testDeviceManager.getTestBaseRelPathInUrl(new File(absoluteReportPath))); + testRun.setTestXmlReportPath(agentManagementService.getTestBaseRelPathInUrl(new File(absoluteReportPath))); } catch (Exception e) { testRun.getLogger().error("Error in buildTestResultXml", e); } @@ -241,8 +243,7 @@ private Logger createLoggerForTestRun(TestRun testRun, String loggerNamePrefix, Logger reportLogger = LogUtils.getLoggerWithRollingFileAppender(loggerName, instrumentLogFile.getAbsolutePath(), "%d %p -- %m%n"); - // TODO the getTestBaseRelPathInUrl method shouldn't be in deviceManager, testBaseDir should be managed by agent - testRun.setInstrumentReportPath(testDeviceManager.getTestBaseRelPathInUrl(instrumentLogFile)); + testRun.setInstrumentReportPath(agentManagementService.getTestBaseRelPathInUrl(instrumentLogFile)); return reportLogger; } diff --git a/agent/src/main/java/com/microsoft/hydralab/agent/runner/appium/AppiumCrossRunner.java b/agent/src/main/java/com/microsoft/hydralab/agent/runner/appium/AppiumCrossRunner.java index 5fc59e46e..9ec7f20b2 100644 --- a/agent/src/main/java/com/microsoft/hydralab/agent/runner/appium/AppiumCrossRunner.java +++ b/agent/src/main/java/com/microsoft/hydralab/agent/runner/appium/AppiumCrossRunner.java @@ -7,16 +7,16 @@ import com.microsoft.hydralab.common.entity.common.DeviceInfo; import com.microsoft.hydralab.common.entity.common.TestRun; import com.microsoft.hydralab.common.entity.common.TestTask; -import com.microsoft.hydralab.common.management.device.TestDeviceManager; +import com.microsoft.hydralab.common.management.AgentManagementService; import com.microsoft.hydralab.performance.PerformanceTestManagementService; import org.slf4j.Logger; public class AppiumCrossRunner extends AppiumRunner { String agentName; - public AppiumCrossRunner(TestDeviceManager testDeviceManager, TestTaskRunCallback testTaskRunCallback, + public AppiumCrossRunner(AgentManagementService agentManagementService, TestTaskRunCallback testTaskRunCallback, PerformanceTestManagementService performanceTestManagementService, String agentName) { - super(testDeviceManager, testTaskRunCallback, performanceTestManagementService); + super(agentManagementService, testTaskRunCallback, performanceTestManagementService); this.agentName = agentName; } diff --git a/agent/src/main/java/com/microsoft/hydralab/agent/runner/appium/AppiumListener.java b/agent/src/main/java/com/microsoft/hydralab/agent/runner/appium/AppiumListener.java index 585e137ed..d7e9b1969 100644 --- a/agent/src/main/java/com/microsoft/hydralab/agent/runner/appium/AppiumListener.java +++ b/agent/src/main/java/com/microsoft/hydralab/agent/runner/appium/AppiumListener.java @@ -9,6 +9,7 @@ import com.microsoft.hydralab.common.entity.common.DeviceInfo; import com.microsoft.hydralab.common.entity.common.TestRun; import com.microsoft.hydralab.common.logger.LogCollector; +import com.microsoft.hydralab.common.management.AgentManagementService; import com.microsoft.hydralab.common.management.device.TestDeviceManager; import com.microsoft.hydralab.common.screen.ScreenRecorder; import com.microsoft.hydralab.performance.PerformanceTestListener; @@ -33,6 +34,7 @@ public class AppiumListener extends RunListener { private final AnimatedGifEncoder e = new AnimatedGifEncoder(); private final String pkgName; TestDeviceManager testDeviceManager; + AgentManagementService agentManagementService; private final PerformanceTestListener performanceTestListener; private long recordingStartTimeMillis; private int index; @@ -45,9 +47,10 @@ public class AppiumListener extends RunListener { private String currentTestName = ""; private int currentTestIndex = 0; - public AppiumListener(TestDeviceManager testDeviceManager, DeviceInfo deviceInfo, TestRun testRun, String pkgName, - PerformanceTestListener performanceTestListener, Logger logger) { - this.testDeviceManager = testDeviceManager; + public AppiumListener(AgentManagementService agentManagementService, DeviceInfo deviceInfo, TestRun testRun, + String pkgName, PerformanceTestListener performanceTestListener, Logger logger) { + this.agentManagementService = agentManagementService; + this.testDeviceManager = deviceInfo.getTestDeviceManager(); this.deviceInfo = deviceInfo; this.testRun = testRun; this.logger = logger; @@ -97,7 +100,7 @@ private void startTools() { logger.info("Start logcat collection"); String logcatFilePath = logcatCollector.start(); - testRun.setLogcatPath(testDeviceManager.getTestBaseRelPathInUrl(new File(logcatFilePath))); + testRun.setLogcatPath(agentManagementService.getTestBaseRelPathInUrl(new File(logcatFilePath))); } @Override @@ -151,20 +154,21 @@ public void testStarted(Description description) { testRun.addNewTestUnit(ongoingTestUnit); - testDeviceManager.updateScreenshotImageAsyncDelay(deviceInfo, TimeUnit.SECONDS.toMillis(15), (imagePNGFile -> { - if (imagePNGFile == null) { - return; - } - if (!e.isStarted()) { - return; - } - try { - e.addFrame(ImgUtil.toBufferedImage(ImgUtil.scale(ImageIO.read(imagePNGFile), 0.3f))); - addedFrameCount++; - } catch (IOException ioException) { - ioException.printStackTrace(); - } - }), logger); + testDeviceManager.updateScreenshotImageAsyncDelay(deviceInfo, TimeUnit.SECONDS.toMillis(15), + (imagePNGFile -> { + if (imagePNGFile == null) { + return; + } + if (!e.isStarted()) { + return; + } + try { + e.addFrame(ImgUtil.toBufferedImage(ImgUtil.scale(ImageIO.read(imagePNGFile), 0.3f))); + addedFrameCount++; + } catch (IOException ioException) { + ioException.printStackTrace(); + } + }), logger); performanceTestListener.testStarted(ongoingTestUnit.getTitle()); } diff --git a/agent/src/main/java/com/microsoft/hydralab/agent/runner/appium/AppiumRunner.java b/agent/src/main/java/com/microsoft/hydralab/agent/runner/appium/AppiumRunner.java index 1a6cedcda..9c3a4117f 100644 --- a/agent/src/main/java/com/microsoft/hydralab/agent/runner/appium/AppiumRunner.java +++ b/agent/src/main/java/com/microsoft/hydralab/agent/runner/appium/AppiumRunner.java @@ -10,7 +10,7 @@ import com.microsoft.hydralab.common.entity.common.DeviceInfo; import com.microsoft.hydralab.common.entity.common.TestRun; import com.microsoft.hydralab.common.entity.common.TestTask; -import com.microsoft.hydralab.common.management.device.TestDeviceManager; +import com.microsoft.hydralab.common.management.AgentManagementService; import com.microsoft.hydralab.common.util.IOSUtils; import com.microsoft.hydralab.common.util.LogUtils; import com.microsoft.hydralab.performance.PerformanceTestManagementService; @@ -34,9 +34,9 @@ public class AppiumRunner extends TestRunner { - public AppiumRunner(TestDeviceManager testDeviceManager, TestTaskRunCallback testTaskRunCallback, + public AppiumRunner(AgentManagementService agentManagementService, TestTaskRunCallback testTaskRunCallback, PerformanceTestManagementService performanceTestManagementService) { - super(testDeviceManager, testTaskRunCallback, performanceTestManagementService); + super(agentManagementService, testTaskRunCallback, performanceTestManagementService); } @Override @@ -48,7 +48,7 @@ protected void run(DeviceInfo deviceInfo, TestTask testTask, TestRun testRun) th runAndGetGif(testTask.getTestAppFile(), testTask.getTestSuite(), deviceInfo, testTask, testRun, testRun.getResultFolder(), reportLogger); if (gifFile != null && gifFile.exists() && gifFile.length() > 0) { - testRun.setTestGifPath(testDeviceManager.getTestBaseRelPathInUrl(gifFile)); + testRun.setTestGifPath(agentManagementService.getTestBaseRelPathInUrl(gifFile)); } } finally { //clear config @@ -85,7 +85,7 @@ protected File runAndGetGif(File appiumJarFile, String appiumCommand, DeviceInfo if (TestTask.TestFrameworkType.JUNIT5.equals(testTask.getFrameworkType())) { reportLogger.info("Start init listener"); Junit5Listener junit5Listener = - new Junit5Listener(testDeviceManager, deviceInfo, testRun, testTask.getPkgName(), + new Junit5Listener(agentManagementService, deviceInfo, testRun, testTask.getPkgName(), performanceTestManagementService, reportLogger); /** run the test */ @@ -98,8 +98,9 @@ protected File runAndGetGif(File appiumJarFile, String appiumCommand, DeviceInfo } else { /** xml report: parse listener */ reportLogger.info("Start init listener"); - AppiumListener listener = new AppiumListener(testDeviceManager, deviceInfo, testRun, testTask.getPkgName(), - performanceTestManagementService, reportLogger); + AppiumListener listener = + new AppiumListener(agentManagementService, deviceInfo, testRun, testTask.getPkgName(), + performanceTestManagementService, reportLogger); /** run the test */ reportLogger.info("Start appium test with junit4"); @@ -111,7 +112,7 @@ protected File runAndGetGif(File appiumJarFile, String appiumCommand, DeviceInfo } /** set paths */ String absoluteReportPath = deviceTestResultFolder.getAbsolutePath(); - testRun.setTestXmlReportPath(testDeviceManager.getTestBaseRelPathInUrl(new File(absoluteReportPath))); + testRun.setTestXmlReportPath(agentManagementService.getTestBaseRelPathInUrl(new File(absoluteReportPath))); return gifFile; } diff --git a/agent/src/main/java/com/microsoft/hydralab/agent/runner/appium/Junit5Listener.java b/agent/src/main/java/com/microsoft/hydralab/agent/runner/appium/Junit5Listener.java index 627ba330c..e6e6a5997 100644 --- a/agent/src/main/java/com/microsoft/hydralab/agent/runner/appium/Junit5Listener.java +++ b/agent/src/main/java/com/microsoft/hydralab/agent/runner/appium/Junit5Listener.java @@ -9,6 +9,7 @@ import com.microsoft.hydralab.common.entity.common.DeviceInfo; import com.microsoft.hydralab.common.entity.common.TestRun; import com.microsoft.hydralab.common.logger.LogCollector; +import com.microsoft.hydralab.common.management.AgentManagementService; import com.microsoft.hydralab.common.management.device.TestDeviceManager; import com.microsoft.hydralab.common.screen.ScreenRecorder; import com.microsoft.hydralab.performance.PerformanceTestListener; @@ -29,6 +30,7 @@ public class Junit5Listener extends SummaryGeneratingListener { private final TestDeviceManager testDeviceManager; + private final AgentManagementService agentManagementService; private final PerformanceTestListener performanceTestListener; private final DeviceInfo deviceInfo; private final TestRun testRun; @@ -47,9 +49,10 @@ public class Junit5Listener extends SummaryGeneratingListener { private String currentTestName = ""; private int currentTestIndex = 0; - public Junit5Listener(TestDeviceManager testDeviceManager, DeviceInfo deviceInfo, TestRun testRun, String pkgName, - PerformanceTestListener performanceTestListener, Logger logger) { - this.testDeviceManager = testDeviceManager; + public Junit5Listener(AgentManagementService agentManagementService, DeviceInfo deviceInfo, TestRun testRun, + String pkgName, PerformanceTestListener performanceTestListener, Logger logger) { + this.agentManagementService = agentManagementService; + this.testDeviceManager = deviceInfo.getTestDeviceManager(); this.deviceInfo = deviceInfo; this.testRun = testRun; this.logger = logger; @@ -93,7 +96,7 @@ private void startTools() { logger.info("Start logcat collection"); String logcatFilePath = logcatCollector.start(); - testRun.setLogcatPath(testDeviceManager.getTestBaseRelPathInUrl(new File(logcatFilePath))); + testRun.setLogcatPath(agentManagementService.getTestBaseRelPathInUrl(new File(logcatFilePath))); } @Override diff --git a/agent/src/main/java/com/microsoft/hydralab/agent/runner/espresso/EspressoRunner.java b/agent/src/main/java/com/microsoft/hydralab/agent/runner/espresso/EspressoRunner.java index 5206e8a8c..ba06fc670 100644 --- a/agent/src/main/java/com/microsoft/hydralab/agent/runner/espresso/EspressoRunner.java +++ b/agent/src/main/java/com/microsoft/hydralab/agent/runner/espresso/EspressoRunner.java @@ -10,7 +10,7 @@ import com.microsoft.hydralab.common.entity.common.DeviceInfo; import com.microsoft.hydralab.common.entity.common.TestRun; import com.microsoft.hydralab.common.entity.common.TestTask; -import com.microsoft.hydralab.common.management.device.TestDeviceManager; +import com.microsoft.hydralab.common.management.AgentManagementService; import com.microsoft.hydralab.common.util.ADBOperateUtil; import com.microsoft.hydralab.common.util.Const; import com.microsoft.hydralab.common.util.LogUtils; @@ -26,10 +26,10 @@ public class EspressoRunner extends TestRunner { final ADBOperateUtil adbOperateUtil; - public EspressoRunner(TestDeviceManager testDeviceManager, TestTaskRunCallback testTaskRunCallback, + public EspressoRunner(AgentManagementService agentManagementService, TestTaskRunCallback testTaskRunCallback, PerformanceTestManagementService performanceTestManagementService, ADBOperateUtil adbOperateUtil) { - super(testDeviceManager, testTaskRunCallback, performanceTestManagementService); + super(agentManagementService, testTaskRunCallback, performanceTestManagementService); this.adbOperateUtil = adbOperateUtil; } @@ -41,8 +41,10 @@ protected void run(DeviceInfo deviceInfo, TestTask testTask, TestRun testRun) th try { /** xml report: parse listener */ reportLogger.info("Start xml report: parse listener"); - EspressoTestInfoProcessorListener listener = new EspressoTestInfoProcessorListener(testDeviceManager, - adbOperateUtil, deviceInfo, testRun, testTask.getPkgName(), performanceTestManagementService); + EspressoTestInfoProcessorListener listener = + new EspressoTestInfoProcessorListener(agentManagementService, + adbOperateUtil, deviceInfo, testRun, testTask.getPkgName(), + performanceTestManagementService); instrumentationResultParser = new InstrumentationResultParser(testTask.getTestSuite(), Collections.singletonList(listener)) { @Override @@ -67,10 +69,11 @@ public boolean isCancelled() { /** set paths */ String absoluteReportPath = listener.getAbsoluteReportPath(); - testRun.setTestXmlReportPath(testDeviceManager.getTestBaseRelPathInUrl(new File(absoluteReportPath))); + testRun.setTestXmlReportPath( + agentManagementService.getTestBaseRelPathInUrl(new File(absoluteReportPath))); File gifFile = listener.getGifFile(); if (gifFile.exists() && gifFile.length() > 0) { - testRun.setTestGifPath(testDeviceManager.getTestBaseRelPathInUrl(gifFile)); + testRun.setTestGifPath(agentManagementService.getTestBaseRelPathInUrl(gifFile)); } } finally { diff --git a/agent/src/main/java/com/microsoft/hydralab/agent/runner/espresso/EspressoTestInfoProcessorListener.java b/agent/src/main/java/com/microsoft/hydralab/agent/runner/espresso/EspressoTestInfoProcessorListener.java index f79ec2436..b8b53b09f 100644 --- a/agent/src/main/java/com/microsoft/hydralab/agent/runner/espresso/EspressoTestInfoProcessorListener.java +++ b/agent/src/main/java/com/microsoft/hydralab/agent/runner/espresso/EspressoTestInfoProcessorListener.java @@ -11,6 +11,7 @@ import com.microsoft.hydralab.common.entity.common.DeviceInfo; import com.microsoft.hydralab.common.entity.common.TestRun; import com.microsoft.hydralab.common.logger.LogCollector; +import com.microsoft.hydralab.common.management.AgentManagementService; import com.microsoft.hydralab.common.management.device.TestDeviceManager; import com.microsoft.hydralab.common.screen.ScreenRecorder; import com.microsoft.hydralab.common.util.ADBOperateUtil; @@ -36,6 +37,7 @@ public class EspressoTestInfoProcessorListener extends XmlTestRunListener { private final Logger logger; private final AnimatedGifEncoder e = new AnimatedGifEncoder(); private final String pkgName; + private final AgentManagementService agentManagementService; private final TestDeviceManager testDeviceManager; private final PerformanceTestListener performanceTestListener; ADBOperateUtil adbOperateUtil; @@ -48,10 +50,12 @@ public class EspressoTestInfoProcessorListener extends XmlTestRunListener { private int pid; private int addedFrameCount; - public EspressoTestInfoProcessorListener(TestDeviceManager testDeviceManager, ADBOperateUtil adbOperateUtil, + public EspressoTestInfoProcessorListener(AgentManagementService agentManagementService, + ADBOperateUtil adbOperateUtil, DeviceInfo deviceInfo, TestRun testRun, String pkgName, PerformanceTestListener performanceTestListener) { - this.testDeviceManager = testDeviceManager; + this.agentManagementService = agentManagementService; + this.testDeviceManager = deviceInfo.getTestDeviceManager(); this.adbOperateUtil = adbOperateUtil; this.deviceInfo = deviceInfo; this.testRun = testRun; @@ -59,7 +63,8 @@ public EspressoTestInfoProcessorListener(TestDeviceManager testDeviceManager, AD this.pkgName = pkgName; this.performanceTestListener = performanceTestListener; adbLogcatCollector = testDeviceManager.getLogCollector(deviceInfo, pkgName, testRun, logger); - adbDeviceScreenRecorder = testDeviceManager.getScreenRecorder(deviceInfo, testRun.getResultFolder(), logger); + adbDeviceScreenRecorder = + testDeviceManager.getScreenRecorder(deviceInfo, testRun.getResultFolder(), logger); setReportDir(testRun.getResultFolder()); try { setHostName(InetAddress.getLocalHost().getHostName()); @@ -75,7 +80,7 @@ public File getGifFile() { public void startRecording(int maxTime) { logger.info("Start adb logcat collection"); String logcatFilePath = adbLogcatCollector.start(); - testRun.setLogcatPath(testDeviceManager.getTestBaseRelPathInUrl(new File(logcatFilePath))); + testRun.setLogcatPath(agentManagementService.getTestBaseRelPathInUrl(new File(logcatFilePath))); logger.info("Start record screen"); adbDeviceScreenRecorder.setupDevice(); adbDeviceScreenRecorder.startRecord(maxTime <= 0 ? 30 * 60 : maxTime); diff --git a/agent/src/main/java/com/microsoft/hydralab/agent/runner/monkey/AdbMonkeyRunner.java b/agent/src/main/java/com/microsoft/hydralab/agent/runner/monkey/AdbMonkeyRunner.java index 50d037885..b5a6688c7 100644 --- a/agent/src/main/java/com/microsoft/hydralab/agent/runner/monkey/AdbMonkeyRunner.java +++ b/agent/src/main/java/com/microsoft/hydralab/agent/runner/monkey/AdbMonkeyRunner.java @@ -13,7 +13,7 @@ import com.microsoft.hydralab.common.entity.common.TestTask; import com.microsoft.hydralab.common.logger.LogCollector; import com.microsoft.hydralab.common.logger.MultiLineNoCancelLoggingReceiver; -import com.microsoft.hydralab.common.management.device.TestDeviceManager; +import com.microsoft.hydralab.common.management.AgentManagementService; import com.microsoft.hydralab.common.screen.ScreenRecorder; import com.microsoft.hydralab.common.util.ADBOperateUtil; import com.microsoft.hydralab.common.util.LogUtils; @@ -42,10 +42,10 @@ public class AdbMonkeyRunner extends TestRunner { private File gifFile; private AndroidTestUnit ongoingMonkeyTest; - public AdbMonkeyRunner(TestDeviceManager testDeviceManager, TestTaskRunCallback testTaskRunCallback, + public AdbMonkeyRunner(AgentManagementService agentManagementService, TestTaskRunCallback testTaskRunCallback, PerformanceTestManagementService performanceTestManagementService, ADBOperateUtil adbOperateUtil) { - super(testDeviceManager, testTaskRunCallback, performanceTestManagementService); + super(agentManagementService, testTaskRunCallback, performanceTestManagementService); this.adbOperateUtil = adbOperateUtil; } @@ -86,10 +86,10 @@ protected void run(DeviceInfo deviceInfo, TestTask testTask, TestRun testRun) th /** set paths */ String absoluteReportPath = testRun.getResultFolder().getAbsolutePath(); - testRun.setTestXmlReportPath(testDeviceManager.getTestBaseRelPathInUrl(new File(absoluteReportPath))); + testRun.setTestXmlReportPath(agentManagementService.getTestBaseRelPathInUrl(new File(absoluteReportPath))); File gifFile = getGifFile(); if (gifFile.exists() && gifFile.length() > 0) { - testRun.setTestGifPath(testDeviceManager.getTestBaseRelPathInUrl(gifFile)); + testRun.setTestGifPath(agentManagementService.getTestBaseRelPathInUrl(gifFile)); } } @@ -114,7 +114,7 @@ private void startTools(TestRun testRun, Logger logger) { logger.info("Start adb logcat collection"); String logcatFilePath = logCollector.start(); - testRun.setLogcatPath(testDeviceManager.getTestBaseRelPathInUrl(new File(logcatFilePath))); + testRun.setLogcatPath(agentManagementService.getTestBaseRelPathInUrl(new File(logcatFilePath))); } public File getGifFile() { diff --git a/agent/src/main/java/com/microsoft/hydralab/agent/runner/monkey/AppiumMonkeyRunner.java b/agent/src/main/java/com/microsoft/hydralab/agent/runner/monkey/AppiumMonkeyRunner.java index 00788a999..ebbfb8afb 100644 --- a/agent/src/main/java/com/microsoft/hydralab/agent/runner/monkey/AppiumMonkeyRunner.java +++ b/agent/src/main/java/com/microsoft/hydralab/agent/runner/monkey/AppiumMonkeyRunner.java @@ -12,7 +12,7 @@ import com.microsoft.hydralab.common.entity.common.TestRun; import com.microsoft.hydralab.common.entity.common.TestTask; import com.microsoft.hydralab.common.logger.LogCollector; -import com.microsoft.hydralab.common.management.device.TestDeviceManager; +import com.microsoft.hydralab.common.management.AgentManagementService; import com.microsoft.hydralab.common.screen.ScreenRecorder; import com.microsoft.hydralab.performance.PerformanceTestManagementService; import org.slf4j.Logger; @@ -25,9 +25,10 @@ public class AppiumMonkeyRunner extends AppiumRunner { private final AnimatedGifEncoder e = new AnimatedGifEncoder(); - public AppiumMonkeyRunner(TestDeviceManager testDeviceManager, TestTaskRunCallback testTaskRunCallback, + public AppiumMonkeyRunner(AgentManagementService agentManagementService, + TestTaskRunCallback testTaskRunCallback, PerformanceTestManagementService performanceTestManagementService) { - super(testDeviceManager, testTaskRunCallback, performanceTestManagementService); + super(agentManagementService, testTaskRunCallback, performanceTestManagementService); } @Override diff --git a/agent/src/main/java/com/microsoft/hydralab/agent/runner/smart/SmartRunner.java b/agent/src/main/java/com/microsoft/hydralab/agent/runner/smart/SmartRunner.java index 79e42b7ec..dabcbba82 100644 --- a/agent/src/main/java/com/microsoft/hydralab/agent/runner/smart/SmartRunner.java +++ b/agent/src/main/java/com/microsoft/hydralab/agent/runner/smart/SmartRunner.java @@ -16,7 +16,7 @@ import com.microsoft.hydralab.common.entity.common.TestRun; import com.microsoft.hydralab.common.entity.common.TestTask; import com.microsoft.hydralab.common.logger.LogCollector; -import com.microsoft.hydralab.common.management.device.TestDeviceManager; +import com.microsoft.hydralab.common.management.AgentManagementService; import com.microsoft.hydralab.common.screen.ScreenRecorder; import com.microsoft.hydralab.common.util.Const; import com.microsoft.hydralab.performance.PerformanceTestManagementService; @@ -38,10 +38,10 @@ public class SmartRunner extends TestRunner { private File gifFile; private SmartTestParam smartTestParam; - public SmartRunner(TestDeviceManager testDeviceManager, TestTaskRunCallback testTaskRunCallback, + public SmartRunner(AgentManagementService agentManagementService, TestTaskRunCallback testTaskRunCallback, PerformanceTestManagementService performanceTestManagementService, SmartTestUtil smartTestUtil) { - super(testDeviceManager, testTaskRunCallback, performanceTestManagementService); + super(agentManagementService, testTaskRunCallback, performanceTestManagementService); this.smartTestUtil = smartTestUtil; } @@ -76,10 +76,10 @@ protected void run(DeviceInfo deviceInfo, TestTask testTask, TestRun testRun) th testRunEnded(deviceInfo, testRun); /** set paths */ String absoluteReportPath = testRun.getResultFolder().getAbsolutePath(); - testRun.setTestXmlReportPath(testDeviceManager.getTestBaseRelPathInUrl(new File(absoluteReportPath))); + testRun.setTestXmlReportPath(agentManagementService.getTestBaseRelPathInUrl(new File(absoluteReportPath))); File gifFile = getGifFile(); if (gifFile.exists() && gifFile.length() > 0) { - testRun.setTestGifPath(testDeviceManager.getTestBaseRelPathInUrl(gifFile)); + testRun.setTestGifPath(agentManagementService.getTestBaseRelPathInUrl(gifFile)); } } @@ -104,7 +104,7 @@ private void startTools(TestRun testRun, Logger logger) { logger.info("Start adb logcat collection"); String logcatFilePath = logCollector.start(); - testRun.setLogcatPath(testDeviceManager.getTestBaseRelPathInUrl(new File(logcatFilePath))); + testRun.setLogcatPath(agentManagementService.getTestBaseRelPathInUrl(new File(logcatFilePath))); } public File getGifFile() { diff --git a/agent/src/main/java/com/microsoft/hydralab/agent/runner/t2c/T2CRunner.java b/agent/src/main/java/com/microsoft/hydralab/agent/runner/t2c/T2CRunner.java index 6682952ad..b47c628d9 100644 --- a/agent/src/main/java/com/microsoft/hydralab/agent/runner/t2c/T2CRunner.java +++ b/agent/src/main/java/com/microsoft/hydralab/agent/runner/t2c/T2CRunner.java @@ -12,7 +12,7 @@ import com.microsoft.hydralab.common.entity.common.TestRun; import com.microsoft.hydralab.common.entity.common.TestTask; import com.microsoft.hydralab.common.logger.LogCollector; -import com.microsoft.hydralab.common.management.device.TestDeviceManager; +import com.microsoft.hydralab.common.management.AgentManagementService; import com.microsoft.hydralab.common.screen.ScreenRecorder; import com.microsoft.hydralab.performance.PerformanceTestManagementService; import org.slf4j.Logger; @@ -31,9 +31,9 @@ public class T2CRunner extends AppiumRunner { String agentName; private int currentIndex = 0; - public T2CRunner(TestDeviceManager testDeviceManager, TestTaskRunCallback testTaskRunCallback, + public T2CRunner(AgentManagementService agentManagementService, TestTaskRunCallback testTaskRunCallback, PerformanceTestManagementService performanceTestManagementService, String agentName) { - super(testDeviceManager, testTaskRunCallback, performanceTestManagementService); + super(agentManagementService, testTaskRunCallback, performanceTestManagementService); this.agentName = agentName; } diff --git a/agent/src/main/java/com/microsoft/hydralab/agent/service/DeviceControlService.java b/agent/src/main/java/com/microsoft/hydralab/agent/service/DeviceControlService.java index 924128d88..3dc5fbe87 100644 --- a/agent/src/main/java/com/microsoft/hydralab/agent/service/DeviceControlService.java +++ b/agent/src/main/java/com/microsoft/hydralab/agent/service/DeviceControlService.java @@ -11,6 +11,7 @@ import com.microsoft.hydralab.common.entity.common.AgentUser; import com.microsoft.hydralab.common.entity.common.DeviceInfo; import com.microsoft.hydralab.common.entity.common.Message; +import com.microsoft.hydralab.common.management.AgentManagementService; import com.microsoft.hydralab.common.management.device.TestDeviceManager; import com.microsoft.hydralab.common.management.device.impl.WindowsTestDeviceManager; import com.microsoft.hydralab.common.management.listener.DeviceStatusListener; @@ -38,6 +39,8 @@ public class DeviceControlService { @SuppressWarnings("constantname") static final Logger log = LoggerFactory.getLogger(DeviceControlService.class); @Resource + AgentManagementService agentManagementService; + @Resource TestDeviceManager testDeviceManager; @Resource MobileDeviceRepository mobileDeviceRepository; @@ -52,7 +55,7 @@ public class DeviceControlService { public Set getAllConnectedDevice() { updateAllDeviceScope(); - return testDeviceManager.getDeviceList(log); + return agentManagementService.getDeviceList(log); } public void provideDeviceList(AgentUser.BatteryStrategy batteryStrategy) { @@ -69,7 +72,7 @@ public void provideDeviceList(AgentUser.BatteryStrategy batteryStrategy) { } public void captureAllScreensSync(AgentUser.BatteryStrategy batteryStrategy) { - Set allConnectedDevices = testDeviceManager.getActiveDeviceList(log); + Set allConnectedDevices = agentManagementService.getActiveDeviceList(log); if (testDeviceManager instanceof WindowsTestDeviceManager) { Assert.isTrue(allConnectedDevices.size() == 1, "expect only 1 device"); } @@ -99,12 +102,12 @@ private void captureDevicesScreenSync(Collection allDevices, boolean private void updateAllDeviceScope() { List devices = mobileDeviceRepository.findAll(); for (MobileDevice device : devices) { - testDeviceManager.updateIsPrivateByDeviceSerial(device.getSerialNum(), device.getIsPrivate()); + agentManagementService.updateIsPrivateByDeviceSerial(device.getSerialNum(), device.getIsPrivate()); } } public DeviceInfo updateDeviceScope(String deviceSerial, Boolean isPrivate) { - Set allActiveConnectedDevice = testDeviceManager.getActiveDeviceList(log); + Set allActiveConnectedDevice = agentManagementService.getActiveDeviceList(log); List devices = allActiveConnectedDevice.stream() .filter(adbDeviceInfo -> deviceSerial.equals(adbDeviceInfo.getSerialNum())) .collect(Collectors.toList()); @@ -130,7 +133,7 @@ public DeviceInfo updateDeviceScope(String deviceSerial, Boolean isPrivate) { deviceCopy.setIsPrivate(isPrivate); mobileDeviceRepository.save(deviceCopy); - testDeviceManager.updateIsPrivateByDeviceSerial(deviceSerial, isPrivate); + agentManagementService.updateIsPrivateByDeviceSerial(deviceSerial, isPrivate); } return device; } @@ -168,7 +171,7 @@ public void onDeviceConnected(DeviceInfo deviceInfo) { agentWebSocketClientService.send(Message.ok(Const.Path.DEVICE_STATUS, data)); } }); - deviceStatusListenerManager.registerListener(new PreInstallListener(testDeviceManager)); + deviceStatusListenerManager.registerListener(new PreInstallListener(agentManagementService)); deviceStatusListenerManager.registerListener(deviceStabilityMonitor); try { testDeviceManager.init(); diff --git a/agent/src/main/java/com/microsoft/hydralab/agent/service/TestTaskEngineService.java b/agent/src/main/java/com/microsoft/hydralab/agent/service/TestTaskEngineService.java index 92d33c33f..db13efad9 100644 --- a/agent/src/main/java/com/microsoft/hydralab/agent/service/TestTaskEngineService.java +++ b/agent/src/main/java/com/microsoft/hydralab/agent/service/TestTaskEngineService.java @@ -15,7 +15,7 @@ import com.microsoft.hydralab.common.entity.common.TestRun; import com.microsoft.hydralab.common.entity.common.TestTask; import com.microsoft.hydralab.common.entity.common.TestTaskSpec; -import com.microsoft.hydralab.common.management.device.TestDeviceManager; +import com.microsoft.hydralab.common.management.AgentManagementService; import com.microsoft.hydralab.common.util.AttachmentService; import com.microsoft.hydralab.common.util.Const; import com.microsoft.hydralab.common.util.DateUtil; @@ -55,7 +55,7 @@ public class TestTaskEngineService implements TestTaskRunCallback { @Resource DeviceTaskControlExecutor deviceTaskControlExecutor; @Resource - TestDeviceManager testDeviceManager; + AgentManagementService agentManagementService; @Resource DeviceScriptCommandLoader deviceScriptCommandLoader; private final Map runningTestTask = new HashMap<>(); @@ -110,13 +110,13 @@ private void updateTaskSpecWithDefaultValues(TestTaskSpec testTaskSpec) { protected Set chooseDevices(TestTaskSpec testTaskSpec, TestRunner runner) { if ((runner instanceof AppiumCrossRunner) || (runner instanceof T2CRunner)) { - Set activeDeviceList = testDeviceManager.getActiveDeviceList(log); + Set activeDeviceList = agentManagementService.getActiveDeviceList(log); Assert.isTrue(activeDeviceList == null || activeDeviceList.size() <= 1, "No connected device!"); return activeDeviceList; } String identifier = testTaskSpec.deviceIdentifier; - Set allActiveConnectedDevice = testDeviceManager.getDeviceList(log); + Set allActiveConnectedDevice = agentManagementService.getDeviceList(log); log.info("Choosing devices from {}", allActiveConnectedDevice.size()); if (identifier.startsWith(Const.DeviceGroup.GROUP_NAME_PREFIX)) { @@ -132,7 +132,7 @@ protected Set chooseDevices(TestTaskSpec testTaskSpec, TestRunner ru } public void setupTestDir(TestTask testTask) { - File baseDir = new File(testDeviceManager.getTestBaseDir(), DateUtil.nowDirFormat.format(new Date())); + File baseDir = new File(agentManagementService.getTestBaseDir(), DateUtil.nowDirFormat.format(new Date())); if (!baseDir.exists()) { if (!baseDir.mkdirs()) { throw new RuntimeException("mkdirs fail for: " + baseDir); @@ -162,7 +162,7 @@ public boolean cancelTestTaskById(String testId) { return false; } testTask.setStatus(TestTask.TestStatus.CANCELED); - testDeviceManager.resetDeviceByTestId(testId, log); + agentManagementService.resetDeviceByTestId(testId, log); return true; } diff --git a/common/src/main/java/com/microsoft/hydralab/common/entity/common/DeviceInfo.java b/common/src/main/java/com/microsoft/hydralab/common/entity/common/DeviceInfo.java index 88c894011..e85f3e4eb 100644 --- a/common/src/main/java/com/microsoft/hydralab/common/entity/common/DeviceInfo.java +++ b/common/src/main/java/com/microsoft/hydralab/common/entity/common/DeviceInfo.java @@ -3,13 +3,18 @@ package com.microsoft.hydralab.common.entity.common; import com.microsoft.hydralab.common.entity.agent.MobileDevice; +import com.microsoft.hydralab.common.management.device.TestDeviceManager; import com.microsoft.hydralab.common.management.listener.MobileDeviceState; import lombok.Getter; import lombok.Setter; import lombok.ToString; import java.io.File; -import java.util.*; +import java.util.ArrayList; +import java.util.HashMap; +import java.util.HashSet; +import java.util.Map; +import java.util.Set; @Getter @Setter @@ -25,6 +30,7 @@ public class DeviceInfo extends MobileDevice { private final transient Map currentProcess = new HashMap<>(); private final transient Map currentTask = new HashMap<>(); private final transient Object lock = new Object(); + private final transient TestDeviceManager testDeviceManager; private String status; private String imageRelPath; private String pcImageRelPath; @@ -43,6 +49,15 @@ public class DeviceInfo extends MobileDevice { private transient File screenshotImageFile; private transient File pcScreenshotImageFile; private transient boolean adbTimeout = false; + private String type; + + public DeviceInfo() { + this.testDeviceManager = null; + } + + public DeviceInfo(TestDeviceManager testDeviceManager) { + this.testDeviceManager = testDeviceManager; + } public void setStatus(String status) { this.status = status; diff --git a/common/src/main/java/com/microsoft/hydralab/common/logger/LogCollector.java b/common/src/main/java/com/microsoft/hydralab/common/logger/LogCollector.java index c186c5c66..e720e8aec 100644 --- a/common/src/main/java/com/microsoft/hydralab/common/logger/LogCollector.java +++ b/common/src/main/java/com/microsoft/hydralab/common/logger/LogCollector.java @@ -3,6 +3,7 @@ package com.microsoft.hydralab.common.logger; public interface LogCollector { + static final String LOGGER_PREFIX = "logger.devices."; String start(); void stopAndAnalyse(); boolean isCrashFound(); diff --git a/common/src/main/java/com/microsoft/hydralab/common/logger/impl/ADBLogcatCollector.java b/common/src/main/java/com/microsoft/hydralab/common/logger/impl/ADBLogcatCollector.java index 1ea7cc52b..839525923 100644 --- a/common/src/main/java/com/microsoft/hydralab/common/logger/impl/ADBLogcatCollector.java +++ b/common/src/main/java/com/microsoft/hydralab/common/logger/impl/ADBLogcatCollector.java @@ -24,13 +24,11 @@ public class ADBLogcatCollector implements LogCollector { private final TestRun testRun; private final String pkgName; private final Logger infoLogger; - TestDeviceManager testDeviceManager; ADBOperateUtil adbOperateUtil; private boolean started; private String loggerFilePath; - public ADBLogcatCollector(TestDeviceManager testDeviceManager, ADBOperateUtil adbOperateUtil, DeviceInfo deviceInfo, String pkgName, TestRun testRun, Logger logger) { - this.testDeviceManager = testDeviceManager; + public ADBLogcatCollector(ADBOperateUtil adbOperateUtil, DeviceInfo deviceInfo, String pkgName, TestRun testRun, Logger logger) { this.adbOperateUtil = adbOperateUtil; this.connectedDevice = deviceInfo; this.testRun = testRun; @@ -69,7 +67,7 @@ private void runCommand(String comm) { public void stopAndAnalyse() { started = false; Logger logger = LogUtils.getLoggerWithRollingFileAppender( - TestDeviceManager.LOGGER_PREFIX + "logcat_" + connectedDevice.getSerialNum(), + LOGGER_PREFIX + "logcat_" + connectedDevice.getSerialNum(), loggerFilePath, "%logger{0}>> %m%n"); Process process = null; diff --git a/common/src/main/java/com/microsoft/hydralab/common/logger/impl/IOSLogCollector.java b/common/src/main/java/com/microsoft/hydralab/common/logger/impl/IOSLogCollector.java index e56bf8294..1ccc27bbe 100644 --- a/common/src/main/java/com/microsoft/hydralab/common/logger/impl/IOSLogCollector.java +++ b/common/src/main/java/com/microsoft/hydralab/common/logger/impl/IOSLogCollector.java @@ -5,8 +5,6 @@ import com.microsoft.hydralab.common.entity.common.DeviceInfo; import com.microsoft.hydralab.common.entity.common.TestRun; import com.microsoft.hydralab.common.logger.LogCollector; -import com.microsoft.hydralab.common.management.device.TestDeviceManager; -import com.microsoft.hydralab.common.management.device.impl.IOSTestDeviceManager; import com.microsoft.hydralab.common.util.IOSUtils; import org.slf4j.Logger; @@ -22,14 +20,12 @@ public class IOSLogCollector implements LogCollector { private final TestRun testRun; private final String pkgName; private final Logger infoLogger; - IOSTestDeviceManager deviceManager; private boolean started; private String loggerFilePath; private Process logProcess; private boolean crashFound; - public IOSLogCollector(TestDeviceManager testDeviceManager, DeviceInfo deviceInfo, String pkgName, TestRun testRun, Logger logger) { - this.deviceManager = (IOSTestDeviceManager) testDeviceManager; + public IOSLogCollector(DeviceInfo deviceInfo, String pkgName, TestRun testRun, Logger logger) { this.connectedDevice = deviceInfo; this.testRun = testRun; this.pkgName = pkgName; diff --git a/common/src/main/java/com/microsoft/hydralab/common/management/AgentManagementService.java b/common/src/main/java/com/microsoft/hydralab/common/management/AgentManagementService.java new file mode 100644 index 000000000..3606d1f9c --- /dev/null +++ b/common/src/main/java/com/microsoft/hydralab/common/management/AgentManagementService.java @@ -0,0 +1,194 @@ +// Copyright (c) Microsoft Corporation. +// Licensed under the MIT License. + +package com.microsoft.hydralab.common.management; + +import com.microsoft.hydralab.common.entity.common.DeviceInfo; +import com.microsoft.hydralab.common.file.StorageServiceClientProxy; +import com.microsoft.hydralab.common.management.listener.DeviceStatusListener; +import com.microsoft.hydralab.common.management.listener.DeviceStatusListenerManager; +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; + +import java.io.File; +import java.util.HashMap; +import java.util.HashSet; +import java.util.Map; +import java.util.Set; +import java.util.stream.Collectors; + +/** + * @author zhoule + * @date 02/22/2023 + */ + +public class AgentManagementService { + static final Logger classLogger = LoggerFactory.getLogger(AgentManagementService.class); + private final Map deviceInfoMap = new HashMap<>(); + protected File testBaseDir; + protected String testBaseDirUrlMapping; + protected File deviceLogBaseDir; + protected File screenshotDir; + protected File preAppDir; + protected DeviceStatusListenerManager deviceStatusListenerManager; + protected String deviceFolderUrlPrefix; + protected String deviceStoragePath; + protected StorageServiceClientProxy storageServiceClientProxy; + protected String preInstallFailurePolicy; + + public StorageServiceClientProxy getStorageServiceClientProxy() { + return storageServiceClientProxy; + } + + public void setStorageServiceClientProxy( + StorageServiceClientProxy storageServiceClientProxy) { + this.storageServiceClientProxy = storageServiceClientProxy; + } + + public String getPreInstallFailurePolicy() { + return preInstallFailurePolicy; + } + + public void setPreInstallFailurePolicy(String preInstallFailurePolicy) { + this.preInstallFailurePolicy = preInstallFailurePolicy; + } + + public DeviceStatusListenerManager getDeviceStatusListenerManager() { + return deviceStatusListenerManager; + } + + public void setDeviceStatusListenerManager(DeviceStatusListenerManager deviceStatusListenerManager) { + deviceStatusListenerManager.registerListener(new DeviceStatusListener() { + + @Override + public void onDeviceInactive(DeviceInfo deviceInfo) { + deviceInfoMap.remove(deviceInfo.getDeviceId()); + } + + @Override + public void onDeviceConnected(DeviceInfo deviceInfo) { + deviceInfoMap.put(deviceInfo.getDeviceId(), deviceInfo); + } + }); + this.deviceStatusListenerManager = deviceStatusListenerManager; + } + + public File getPreAppDir() { + return preAppDir; + } + + public void setPreAppDir(File preAppDir) { + this.preAppDir = preAppDir; + } + + public String getDeviceFolderUrlPrefix() { + return deviceFolderUrlPrefix; + } + + public void setDeviceFolderUrlPrefix(String deviceFolderUrlPrefix) { + this.deviceFolderUrlPrefix = deviceFolderUrlPrefix; + } + + public String getDeviceStoragePath() { + return deviceStoragePath; + } + + public void setDeviceStoragePath(String deviceStoragePath) { + this.deviceStoragePath = deviceStoragePath; + } + + public File getScreenshotDir() { + return screenshotDir; + } + + public void setScreenshotDir(File screenshotDir) { + this.screenshotDir = screenshotDir; + } + + public File getTestBaseDir() { + return testBaseDir; + } + + public void setTestBaseDir(File testBaseDir) { + this.testBaseDir = testBaseDir; + } + + public void setTestBaseDirUrlMapping(String testBaseDirUrlMapping) { + this.testBaseDirUrlMapping = testBaseDirUrlMapping; + } + + + public File getDeviceLogBaseDir() { + return deviceLogBaseDir; + } + + public void setDeviceLogBaseDir(File deviceLogBaseDir) { + this.deviceLogBaseDir = deviceLogBaseDir; + } + + public String getTestBaseRelPathInUrl(File report) { + return report.getAbsolutePath().replace(testBaseDir.getAbsolutePath(), testBaseDirUrlMapping) + .replace(File.separator, "/"); + } + + public Set getDeviceList(Logger log) { + Set set = new HashSet<>(); + Set> entries = null; + synchronized (this) { + entries = new HashSet<>(deviceInfoMap.entrySet()); + } + for (Map.Entry entry : entries) { + DeviceInfo value = entry.getValue(); + if (value != null) { + set.add(value); + } + } + return set; + } + + public Set getActiveDeviceList(Logger log) { + Set set = new HashSet<>(); + Set> entries = null; + synchronized (this) { + entries = new HashSet<>(deviceInfoMap.entrySet()); + } + for (Map.Entry entry : entries) { + DeviceInfo value = entry.getValue(); + if (value == null || !value.isAlive()) { + classLogger.debug("Invalid device: {}", value); + continue; + } + set.add(value); + } + return set; + } + + public void resetDeviceByTestId(String testId, Logger logger) { + Set devices = getActiveDeviceList(logger).stream() + .filter(adbDeviceInfo -> testId.equals(adbDeviceInfo.getRunningTaskId())) + .collect(Collectors.toSet()); + for (DeviceInfo device : devices) { + resetDevice(device.getSerialNum()); + } + } + + private void resetDevice(String serialNum) { + DeviceInfo deviceInfo = deviceInfoMap.get(serialNum); + if (deviceInfo == null) { + return; + } + synchronized (deviceInfo) { + deviceInfo.reset(); + } + } + + public void updateIsPrivateByDeviceSerial(String deviceSerial, Boolean isPrivate) { + DeviceInfo deviceInfo; + synchronized (deviceInfoMap) { + deviceInfo = deviceInfoMap.get(deviceSerial); + } + if (deviceInfo != null) { + deviceInfo.setIsPrivate(isPrivate); + } + } +} \ No newline at end of file diff --git a/common/src/main/java/com/microsoft/hydralab/common/management/AgentType.java b/common/src/main/java/com/microsoft/hydralab/common/management/AgentType.java index 6cb1f8d5a..9114bda82 100644 --- a/common/src/main/java/com/microsoft/hydralab/common/management/AgentType.java +++ b/common/src/main/java/com/microsoft/hydralab/common/management/AgentType.java @@ -1,5 +1,6 @@ // Copyright (c) Microsoft Corporation. // Licensed under the MIT License. + package com.microsoft.hydralab.common.management; diff --git a/common/src/main/java/com/microsoft/hydralab/common/management/device/DeviceType.java b/common/src/main/java/com/microsoft/hydralab/common/management/device/DeviceType.java new file mode 100644 index 000000000..953f0a77d --- /dev/null +++ b/common/src/main/java/com/microsoft/hydralab/common/management/device/DeviceType.java @@ -0,0 +1,20 @@ +// Copyright (c) Microsoft Corporation. +// Licensed under the MIT License. + +package com.microsoft.hydralab.common.management.device; + +public enum DeviceType { + // Define device type and bean name + ANDROID("androidDeviceManager"), + WINDOWS("windowsDeviceManager"), + IOS("iosDeviceManager"); + String beanName; + + DeviceType(String beanName) { + this.beanName = beanName; + } + + public String getBeanName() { + return beanName; + } +} \ No newline at end of file diff --git a/common/src/main/java/com/microsoft/hydralab/common/management/device/TestDeviceManager.java b/common/src/main/java/com/microsoft/hydralab/common/management/device/TestDeviceManager.java index f0fb9dc0d..79263dc2f 100644 --- a/common/src/main/java/com/microsoft/hydralab/common/management/device/TestDeviceManager.java +++ b/common/src/main/java/com/microsoft/hydralab/common/management/device/TestDeviceManager.java @@ -1,5 +1,6 @@ // Copyright (c) Microsoft Corporation. // Licensed under the MIT License. + package com.microsoft.hydralab.common.management.device; import com.android.ddmlib.InstallException; @@ -10,18 +11,26 @@ import com.microsoft.hydralab.common.entity.common.DeviceInfo; import com.microsoft.hydralab.common.entity.common.TestRun; import com.microsoft.hydralab.common.entity.common.TestTask; -import com.microsoft.hydralab.common.file.StorageServiceClientProxy; import com.microsoft.hydralab.common.logger.LogCollector; +import com.microsoft.hydralab.common.management.AgentManagementService; import com.microsoft.hydralab.common.management.AppiumServerManager; -import com.microsoft.hydralab.common.management.listener.DeviceStatusListenerManager; import com.microsoft.hydralab.common.management.listener.MobileDeviceState; import com.microsoft.hydralab.common.screen.ScreenRecorder; -import com.microsoft.hydralab.common.util.*; +import com.microsoft.hydralab.common.util.IOSUtils; +import com.microsoft.hydralab.common.util.LogUtils; +import com.microsoft.hydralab.common.util.ShellUtils; +import com.microsoft.hydralab.common.util.ThreadPoolUtil; +import com.microsoft.hydralab.common.util.ThreadUtils; import io.appium.java_client.appmanagement.ApplicationState; import io.appium.java_client.ios.IOSDriver; import org.jetbrains.annotations.NotNull; import org.jetbrains.annotations.Nullable; -import org.openqa.selenium.*; +import org.openqa.selenium.By; +import org.openqa.selenium.ElementNotInteractableException; +import org.openqa.selenium.StaleElementReferenceException; +import org.openqa.selenium.WebDriver; +import org.openqa.selenium.WebDriverException; +import org.openqa.selenium.WebElement; import org.slf4j.Logger; import org.slf4j.LoggerFactory; import org.springframework.util.Assert; @@ -31,41 +40,18 @@ import java.io.IOException; import java.util.List; import java.util.Random; -import java.util.Set; import java.util.concurrent.TimeUnit; import static com.android.ddmlib.IDevice.DeviceState; public abstract class TestDeviceManager { - public static final String LOGGER_PREFIX = "logger.devices."; static final Logger classLogger = LoggerFactory.getLogger(TestDeviceManager.class); - protected StorageServiceClientProxy storageServiceClientProxy; - protected File testBaseDir; - protected File preAppDir; - protected String preInstallFailurePolicy; - - public String getPreInstallFailurePolicy() { - return preInstallFailurePolicy; - } - - public void setPreInstallFailurePolicy(String preInstallFailurePolicy) { - this.preInstallFailurePolicy = preInstallFailurePolicy; - } - - protected String testBaseDirUrlMapping; - protected File deviceLogBaseDir; - protected File screenshotDir; - protected String deviceFolderUrlPrefix; - protected String deviceStoragePath; - protected DeviceStatusListenerManager deviceStatusListenerManager; - - public void setDeviceStatusListenerManager(DeviceStatusListenerManager deviceStatusListenerManager) { - this.deviceStatusListenerManager = deviceStatusListenerManager; - } - + protected AgentManagementService agentManagementService; protected AppiumServerManager appiumServerManager; + public abstract void init(); + public static MobileDeviceState mobileDeviceStateMapping(DeviceState adbState) { if (adbState == null) { return MobileDeviceState.OTHER; @@ -87,91 +73,18 @@ public AppiumServerManager getAppiumServerManager() { return appiumServerManager; } - public void setAppiumServerManager(AppiumServerManager appiumServerManager) { - this.appiumServerManager = appiumServerManager; - } - - - public StorageServiceClientProxy getStorageServiceClientProxy() { - return storageServiceClientProxy; - } - - public void setStorageServiceClientProxy(StorageServiceClientProxy storageServiceClientProxy) { - this.storageServiceClientProxy = storageServiceClientProxy; - } - - public File getTestBaseDir() { - return testBaseDir; - } - - public void setTestBaseDir(File testBaseDir) { - this.testBaseDir = testBaseDir; - } - - public File getPreAppDir() { - return preAppDir; - } - - public void setPreAppDir(File preAppDir) { - this.preAppDir = preAppDir; - } - - public String getTestBaseDirUrlMapping() { - return testBaseDirUrlMapping; - } - - public void setTestBaseDirUrlMapping(String testBaseDirUrlMapping) { - this.testBaseDirUrlMapping = testBaseDirUrlMapping; - } - - public File getDeviceLogBaseDir() { - return deviceLogBaseDir; - } - - public void setDeviceLogBaseDir(File deviceLogBaseDir) { - this.deviceLogBaseDir = deviceLogBaseDir; - } - - public File getScreenshotDir() { - return screenshotDir; - } - - public void setScreenshotDir(File screenshotDir) { - this.screenshotDir = screenshotDir; - } - - public String getDeviceFolderUrlPrefix() { - return deviceFolderUrlPrefix; - } - - public void setDeviceFolderUrlPrefix(String deviceFolderUrlPrefix) { - this.deviceFolderUrlPrefix = deviceFolderUrlPrefix; - } - - public String getDeviceStoragePath() { - return deviceStoragePath; - } - - public void setDeviceStoragePath(String deviceStoragePath) { - this.deviceStoragePath = deviceStoragePath; - } - - public abstract void init() throws Exception; - - public abstract Set getDeviceList(@Nullable Logger logger); - - public abstract Set getActiveDeviceList(@Nullable Logger logger); - public abstract File getScreenShot(@NotNull DeviceInfo deviceInfo, @Nullable Logger logger) throws Exception; - public File getScreenShotWithStrategy(@NotNull DeviceInfo deviceInfo, @Nullable Logger logger, @NotNull AgentUser.BatteryStrategy batteryStrategy) throws Exception { + public File getScreenShotWithStrategy(@NotNull DeviceInfo deviceInfo, @Nullable Logger logger, + @NotNull AgentUser.BatteryStrategy batteryStrategy) throws Exception { File screenshotImageFile = deviceInfo.getScreenshotImageFile(); if (screenshotImageFile == null || StringUtils.isEmpty(deviceInfo.getScreenshotImageUrl())) { return getScreenShot(deviceInfo, logger); } else if (batteryStrategy.wakeUpInterval > 0) { synchronized (deviceInfo.getLock()) { long now = System.currentTimeMillis(); - if (now - deviceInfo.getScreenshotUpdateTimeMilli() < TimeUnit.SECONDS.toMillis(batteryStrategy.wakeUpInterval)) { + if (now - deviceInfo.getScreenshotUpdateTimeMilli() < + TimeUnit.SECONDS.toMillis(batteryStrategy.wakeUpInterval)) { classLogger.warn("skip screen shot for too short interval {}", deviceInfo.getName()); return screenshotImageFile; } @@ -181,60 +94,68 @@ public File getScreenShotWithStrategy(@NotNull DeviceInfo deviceInfo, @Nullable return screenshotImageFile; } - public abstract void resetDeviceByTestId(@NotNull String testId, @Nullable Logger logger); - public abstract void wakeUpDevice(@NotNull DeviceInfo deviceInfo, @Nullable Logger logger); public abstract void backToHome(@NotNull DeviceInfo deviceInfo, @Nullable Logger logger); - public abstract void grantPermission(@NotNull DeviceInfo deviceInfo, @NotNull String packageName, @NotNull String permissionName, @Nullable Logger logger); + public abstract void grantPermission(@NotNull DeviceInfo deviceInfo, @NotNull String packageName, + @NotNull String permissionName, @Nullable Logger logger); - public abstract void addToBatteryWhiteList(@NotNull DeviceInfo deviceInfo, @NotNull String packageName, @NotNull Logger logger); + public abstract void addToBatteryWhiteList(@NotNull DeviceInfo deviceInfo, @NotNull String packageName, + @NotNull Logger logger); - public abstract boolean installApp(@NotNull DeviceInfo deviceInfo, @NotNull String packagePath, @Nullable Logger logger) throws InstallException; + public abstract boolean installApp(@NotNull DeviceInfo deviceInfo, @NotNull String packagePath, + @Nullable Logger logger) throws InstallException; - public abstract boolean uninstallApp(@NotNull DeviceInfo deviceInfo, @NotNull String packageName, @Nullable Logger logger) throws InstallException; + public abstract boolean uninstallApp(@NotNull DeviceInfo deviceInfo, @NotNull String packageName, + @Nullable Logger logger) throws InstallException; - public abstract void resetPackage(@NotNull DeviceInfo deviceInfo, @NotNull String packageName, @Nullable Logger logger); + public abstract void resetPackage(@NotNull DeviceInfo deviceInfo, @NotNull String packageName, + @Nullable Logger logger); - public abstract void pushFileToDevice(@NotNull DeviceInfo deviceInfo, @NotNull String pathOnAgent, @NotNull String pathOnDevice, @Nullable Logger logger) throws Exception; + public abstract void pushFileToDevice(@NotNull DeviceInfo deviceInfo, @NotNull String pathOnAgent, + @NotNull String pathOnDevice, @Nullable Logger logger) throws Exception; - public abstract void pullFileFromDevice(@NotNull DeviceInfo deviceInfo, @NotNull String pathOnDevice, @Nullable Logger logger) throws Exception; + public abstract void pullFileFromDevice(@NotNull DeviceInfo deviceInfo, @NotNull String pathOnDevice, + @Nullable Logger logger) throws Exception; - public abstract ScreenRecorder getScreenRecorder(@NotNull DeviceInfo deviceInfo, @NotNull File folder, @Nullable Logger logger); + public abstract ScreenRecorder getScreenRecorder(@NotNull DeviceInfo deviceInfo, @NotNull File folder, + @Nullable Logger logger); - public boolean grantAllTaskNeededPermissions(@NotNull DeviceInfo deviceInfo, @NotNull TestTask task, @Nullable Logger logger) { + public boolean grantAllTaskNeededPermissions(@NotNull DeviceInfo deviceInfo, @NotNull TestTask task, + @Nullable Logger logger) { return false; } - public boolean grantAllPackageNeededPermissions(@NotNull DeviceInfo deviceInfo, @NotNull File packageFile, @NotNull String targetPackage, boolean allowCustomizedPermissions, @Nullable Logger logger) { + public boolean grantAllPackageNeededPermissions(@NotNull DeviceInfo deviceInfo, @NotNull File packageFile, + @NotNull String targetPackage, + boolean allowCustomizedPermissions, @Nullable Logger logger) { return false; } public Logger getDeviceLogger(DeviceInfo device) { - String file = deviceLogBaseDir.getAbsolutePath() + "/" + device.getName() + "/device_control.log"; - return LogUtils.getLoggerWithRollingFileAppender(LOGGER_PREFIX + device.getSerialNum(), file, "%d %logger{0} %p [%t] - %m%n"); - } - - public String getTestBaseRelPathInUrl(File report) { - return report.getAbsolutePath().replace(testBaseDir.getAbsolutePath(), testBaseDirUrlMapping).replace(File.separator, "/"); - } - - public void updateScreenshotImageAsyncDelay(@NotNull DeviceInfo deviceInfo, long delayMillis, @NotNull FileAvailableCallback fileAvailableCallback, @NotNull Logger logger) { - ThreadPoolUtil.SCREENSHOT_EXECUTOR.execute(new Runnable() { - @Override - public void run() { - try { - ThreadUtils.safeSleep(delayMillis); - File imageFile = getScreenShot(deviceInfo, logger); - if (fileAvailableCallback != null) { - fileAvailableCallback.onFileReady(imageFile); - } - } catch (TimeoutException te) { - classLogger.error("{}: {}, updateScreenshotImageAsyncDelay", te.getClass().getSimpleName(), te.getMessage()); - } catch (Exception e) { - classLogger.error(e.getMessage(), e); + String file = agentManagementService.getDeviceLogBaseDir().getAbsolutePath() + "/" + device.getName() + + "/device_control.log"; + return LogUtils.getLoggerWithRollingFileAppender(LogCollector.LOGGER_PREFIX + device.getSerialNum(), file, + "%d %logger{0} %p [%t] - %m%n"); + } + + + public void updateScreenshotImageAsyncDelay(@NotNull DeviceInfo deviceInfo, long delayMillis, + @NotNull FileAvailableCallback fileAvailableCallback, + @NotNull Logger logger) { + ThreadPoolUtil.SCREENSHOT_EXECUTOR.execute(() -> { + try { + ThreadUtils.safeSleep(delayMillis); + File imageFile = getScreenShot(deviceInfo, logger); + if (fileAvailableCallback != null) { + fileAvailableCallback.onFileReady(imageFile); } + } catch (TimeoutException te) { + classLogger.error("{}: {}, updateScreenshotImageAsyncDelay", te.getClass().getSimpleName(), + te.getMessage()); + } catch (Exception e) { + classLogger.error(e.getMessage(), e); } }); } @@ -242,19 +163,24 @@ public void run() { public void updateAllDeviceInfo() { } - public abstract LogCollector getLogCollector(@NotNull DeviceInfo deviceInfo, @NotNull String pkgName, @NotNull TestRun testRun, @NotNull Logger logger); - - public abstract void setProperty(@NotNull DeviceInfo deviceInfo, @NotNull String property, String val, @Nullable Logger logger); + public abstract LogCollector getLogCollector(@NotNull DeviceInfo deviceInfo, @NotNull String pkgName, + @NotNull TestRun testRun, @NotNull Logger logger); - public abstract void updateIsPrivateByDeviceSerial(@NotNull String deviceSerial, @NotNull Boolean isPrivate); + public abstract void setProperty(@NotNull DeviceInfo deviceInfo, @NotNull String property, String val, + @Nullable Logger logger); - public abstract boolean setDefaultLauncher(@NotNull DeviceInfo deviceInfo, @NotNull String packageName, @NotNull String defaultActivity, @Nullable Logger logger); + public abstract boolean setDefaultLauncher(@NotNull DeviceInfo deviceInfo, @NotNull String packageName, + @NotNull String defaultActivity, @Nullable Logger logger); - public abstract boolean isAppInstalled(@NotNull DeviceInfo deviceInfo, @NotNull String packageName, @Nullable Logger logger); + public abstract boolean isAppInstalled(@NotNull DeviceInfo deviceInfo, @NotNull String packageName, + @Nullable Logger logger); - public abstract boolean grantProjectionAndBatteryPermission(@NotNull DeviceInfo deviceInfo, @NotNull String recordPackageName, @Nullable Logger logger); + public abstract boolean grantProjectionAndBatteryPermission(@NotNull DeviceInfo deviceInfo, + @NotNull String recordPackageName, + @Nullable Logger logger); - public abstract void testDeviceSetup(@NotNull DeviceInfo deviceInfo, @Nullable Logger logger) throws IOException; + public abstract void testDeviceSetup(@NotNull DeviceInfo deviceInfo, @Nullable Logger logger) + throws IOException; public abstract void removeFileInDevice(DeviceInfo deviceInfo, String pathOnDevice, Logger logger); @@ -271,7 +197,8 @@ public void execCommandOnAgent(DeviceInfo deviceInfo, String command, Logger log String newCommand = command; if (testRun != null) { // Variable only supported when the test run is ready - Assert.notNull(testRun.getResultFolder(), "The testRun instance in ThreadContext does not have resultFolder property!"); + Assert.notNull(testRun.getResultFolder(), + "The testRun instance in ThreadContext does not have resultFolder property!"); newCommand = ShellUtils.parseHydraLabVariable(command, testRun, deviceInfo); } ShellUtils.execLocalCommand(newCommand, logger); @@ -305,7 +232,8 @@ public void runAppiumMonkey(DeviceInfo deviceInfo, String packageName, int round } } else { if (count == 0 || !isAppRunningForeground(deviceInfo, packageName, logger)) { - logger.info("No element Found or App is Running in Background, Back to Home Screen and Restart App."); + logger.info( + "No element Found or App is Running in Background, Back to Home Screen and Restart App."); backToHome(deviceInfo, logger); IOSUtils.launchApp(deviceInfo.getSerialNum(), packageName, logger); eleList = driver.findElements(By.xpath("//*")); @@ -337,7 +265,15 @@ public boolean runAppiumT2CTest(DeviceInfo deviceInfo, File jsonFile, Logger rep return true; } + public void setAppiumServerManager(AppiumServerManager appiumServerManager) { + this.appiumServerManager = appiumServerManager; + } + + public void setAgentManagementService(AgentManagementService agentManagementService) { + this.agentManagementService = agentManagementService; + } + public interface FileAvailableCallback { void onFileReady(File file); } -} +} \ No newline at end of file diff --git a/common/src/main/java/com/microsoft/hydralab/common/management/device/impl/AndroidTestDeviceManager.java b/common/src/main/java/com/microsoft/hydralab/common/management/device/impl/AndroidTestDeviceManager.java index ae9375560..2e6a3445a 100644 --- a/common/src/main/java/com/microsoft/hydralab/common/management/device/impl/AndroidTestDeviceManager.java +++ b/common/src/main/java/com/microsoft/hydralab/common/management/device/impl/AndroidTestDeviceManager.java @@ -1,5 +1,6 @@ // Copyright (c) Microsoft Corporation. // Licensed under the MIT License. + package com.microsoft.hydralab.common.management.device.impl; import cn.hutool.core.img.ImgUtil; @@ -18,10 +19,14 @@ import com.microsoft.hydralab.common.logger.MultiLineNoCancelLoggingReceiver; import com.microsoft.hydralab.common.logger.MultiLineNoCancelReceiver; import com.microsoft.hydralab.common.logger.impl.ADBLogcatCollector; +import com.microsoft.hydralab.common.management.AgentManagementService; +import com.microsoft.hydralab.common.management.AppiumServerManager; +import com.microsoft.hydralab.common.management.device.DeviceType; import com.microsoft.hydralab.common.management.device.TestDeviceManager; import com.microsoft.hydralab.common.screen.PhoneAppScreenRecorder; import com.microsoft.hydralab.common.screen.ScreenRecorder; import com.microsoft.hydralab.common.util.ADBOperateUtil; +import com.microsoft.hydralab.common.util.HydraLabRuntimeException; import com.microsoft.hydralab.common.util.ThreadUtils; import net.dongliu.apk.parser.ApkFile; import net.dongliu.apk.parser.bean.ApkMeta; @@ -42,7 +47,6 @@ import java.io.IOException; import java.util.*; import java.util.concurrent.TimeUnit; -import java.util.stream.Collectors; import static com.android.ddmlib.IDevice.*; import static com.microsoft.hydralab.common.screen.PhoneAppScreenRecorder.recordPackageName; @@ -57,129 +61,82 @@ public class AndroidTestDeviceManager extends TestDeviceManager { static final Logger classLogger = LoggerFactory.getLogger(AndroidTestDeviceManager.class); private final Map adbDeviceInfoMap = new HashMap<>(); ADBOperateUtil adbOperateUtil; - private final AndroidDebugBridge.IDeviceChangeListener mListener = new AndroidDebugBridge.IDeviceChangeListener() { - @Override - public void deviceConnected(IDevice device) { - // from disconnected to connected (OFFLINE) - classLogger.warn("DeviceConnected {} {} {}", device.getSerialNumber(), device.getState(), device.getName()); - deviceInfoUpdate(device); - DeviceInfo deviceInfo = adbDeviceInfoMap.get(device.getSerialNumber()); - if (deviceInfo == null) { - return; - } - if (device.getState().equals(DeviceState.ONLINE)) { - deviceStatusListenerManager.onDeviceConnected(deviceInfo); - } else { - deviceStatusListenerManager.onDeviceInactive(deviceInfo); - } - } - @Override - public void deviceDisconnected(IDevice device) { - classLogger.error("DeviceDisconnected {} {} {}", device.getSerialNumber(), device.getState(), device.getName()); - deviceInfoUpdate(device); - DeviceInfo deviceInfo = adbDeviceInfoMap.get(device.getSerialNumber()); - if (deviceInfo == null) { - return; - } + private final AndroidDebugBridge.IDeviceChangeListener mListener = + new AndroidDebugBridge.IDeviceChangeListener() { + @Override + public void deviceConnected(IDevice device) { + // from disconnected to connected (OFFLINE) + classLogger.warn("DeviceConnected {} {} {}", device.getSerialNumber(), device.getState(), + device.getName()); + deviceInfoUpdate(device); + DeviceInfo deviceInfo = adbDeviceInfoMap.get(device.getSerialNumber()); + if (deviceInfo == null) { + return; + } + if (device.getState().equals(DeviceState.ONLINE)) { + agentManagementService.getDeviceStatusListenerManager().onDeviceConnected(deviceInfo); + } else { + agentManagementService.getDeviceStatusListenerManager().onDeviceInactive(deviceInfo); + } + } - deviceStatusListenerManager.onDeviceInactive(deviceInfo); - appiumServerManager.quitAndroidDriver(deviceInfo, classLogger); - } + @Override + public void deviceDisconnected(IDevice device) { + classLogger.error("DeviceDisconnected {} {} {}", device.getSerialNumber(), device.getState(), + device.getName()); + deviceInfoUpdate(device); + DeviceInfo deviceInfo = adbDeviceInfoMap.get(device.getSerialNumber()); + if (deviceInfo == null) { + return; + } - @Override - public void deviceChanged(IDevice device, int changeMask) { - if (changeMask != CHANGE_STATE) { - String changeType = ""; - if (changeMask == CHANGE_BUILD_INFO) { - changeType = "build info"; - } else if (changeMask == CHANGE_CLIENT_LIST) { - changeType = "client list"; + agentManagementService.getDeviceStatusListenerManager().onDeviceInactive(deviceInfo); + appiumServerManager.quitAndroidDriver(deviceInfo, classLogger); } - classLogger.warn("DeviceChanged, SN: {}, name: {}, {} changed", device.getSerialNumber(), device.getName(), changeType); - return; - } - classLogger.warn("DeviceChanged {} {} {}", device.getSerialNumber(), device.getState(), device.getName()); - deviceInfoUpdate(device); - DeviceInfo deviceInfo = adbDeviceInfoMap.get(device.getSerialNumber()); - if (deviceInfo == null) { - return; - } + @Override + public void deviceChanged(IDevice device, int changeMask) { + if (changeMask != CHANGE_STATE) { + String changeType = ""; + if (changeMask == CHANGE_BUILD_INFO) { + changeType = "build info"; + } else if (changeMask == CHANGE_CLIENT_LIST) { + changeType = "client list"; + } + classLogger.warn("DeviceChanged, SN: {}, name: {}, {} changed", device.getSerialNumber(), + device.getName(), changeType); + return; + } - if (device.getState().equals(DeviceState.ONLINE)) { - deviceStatusListenerManager.onDeviceConnected(deviceInfo); - } else { - deviceStatusListenerManager.onDeviceInactive(deviceInfo); - } - } - }; + classLogger.warn("DeviceChanged {} {} {}", device.getSerialNumber(), device.getState(), + device.getName()); + deviceInfoUpdate(device); + DeviceInfo deviceInfo = adbDeviceInfoMap.get(device.getSerialNumber()); + if (deviceInfo == null) { + return; + } + + if (device.getState().equals(DeviceState.ONLINE)) { + agentManagementService.getDeviceStatusListenerManager().onDeviceConnected(deviceInfo); + } else { + agentManagementService.getDeviceStatusListenerManager().onDeviceInactive(deviceInfo); + } + } + }; private static boolean isAndroidCommonPermission(String usesPermission) { return usesPermission.startsWith("android."); } - public void setADBOperateUtil(ADBOperateUtil adbOperateUtil) { - this.adbOperateUtil = adbOperateUtil; - } - - @Override - public void init() throws Exception { - adbOperateUtil.init(mListener); - PhoneAppScreenRecorder.copyAPK(preAppDir); - } - - @Override - public Set getDeviceList(Logger logger) { - Set set = new HashSet<>(); - Set> entries = null; - synchronized (this) { - entries = new HashSet<>(adbDeviceInfoMap.entrySet()); - } - for (Map.Entry entry : entries) { - DeviceInfo value = entry.getValue(); - if (value != null) { - set.add(value); - } - } - return set; - } - @Override - public Set getActiveDeviceList(Logger logger) { - Set set = new HashSet<>(); - Set> entries = null; - synchronized (this) { - entries = new HashSet<>(adbDeviceInfoMap.entrySet()); - } - for (Map.Entry entry : entries) { - DeviceInfo value = entry.getValue(); - if (value == null || !value.isAlive()) { - classLogger.debug("Invalid device: {}", value); - continue; - } - set.add(value); - } - return set; - } - - @Override - public void resetDeviceByTestId(String testId, Logger logger) { - Set devices = getActiveDeviceList(logger).stream() - .filter(adbDeviceInfo -> testId.equals(adbDeviceInfo.getRunningTaskId())) - .collect(Collectors.toSet()); - for (DeviceInfo device : devices) { - resetDevice(device.getSerialNum()); - } - } - - private void resetDevice(String serialNum) { - DeviceInfo deviceInfo = adbDeviceInfoMap.get(serialNum); - if (deviceInfo == null) { - return; - } - synchronized (deviceInfo) { - deviceInfo.reset(); + public void init() { + try { + adbOperateUtil.init(mListener); + PhoneAppScreenRecorder.copyAPK(agentManagementService.getPreAppDir()); + } catch (Exception e) { + classLogger.error("init adbOperateUtil failed", e); + throw new HydraLabRuntimeException(500, "adbOperateUtil init failed", e); } } @@ -206,9 +163,11 @@ public void backToHome(DeviceInfo deviceInfo, Logger logger) { * And refer to cts/common/device-side/bedstead/nene/common/src/main/java/com/android/bedstead/nene/permissions/CommonPermissions.java get all the common permission names. * Refer to https://developer.android.com/reference/android/Manifest.permission#ACCEPT_HANDOVER to see the permission level of each permission. */ - public void grantPermission(@NotNull DeviceInfo deviceInfo, @NotNull String packageName, @NotNull String permissionName, @Nullable Logger logger) { + public void grantPermission(@NotNull DeviceInfo deviceInfo, @NotNull String packageName, + @NotNull String permissionName, @Nullable Logger logger) { try { - adbOperateUtil.execOnDevice(deviceInfo, String.format("pm grant %s %s", packageName, permissionName), new MultiLineNoCancelLoggingReceiver(logger), logger); + adbOperateUtil.execOnDevice(deviceInfo, String.format("pm grant %s %s", packageName, permissionName), + new MultiLineNoCancelLoggingReceiver(logger), logger); } catch (Exception e) { Logger myLogger = classLogger; if (logger != null) { @@ -229,7 +188,9 @@ public void grantPermission(@NotNull DeviceInfo deviceInfo, @NotNull String pack * @return return true if succeeded. */ @Override - public boolean grantAllPackageNeededPermissions(@NotNull DeviceInfo deviceInfo, @NotNull File packageFile, @NotNull String targetPackage, boolean allowCustomizedPermissions, @Nullable Logger logger) { + public boolean grantAllPackageNeededPermissions(@NotNull DeviceInfo deviceInfo, @NotNull File packageFile, + @NotNull String targetPackage, + boolean allowCustomizedPermissions, @Nullable Logger logger) { Logger myLogger = classLogger; if (logger != null) { myLogger = logger; @@ -247,12 +208,15 @@ public boolean grantAllPackageNeededPermissions(@NotNull DeviceInfo deviceInfo, try { grantPermission(deviceInfo, targetPackage, usesPermission, logger); } catch (Exception e) { - myLogger.info(String.format("error occurred when granting permission %s to package %s", usesPermission, targetPackage), e); + myLogger.info(String.format("error occurred when granting permission %s to package %s", + usesPermission, targetPackage), e); } } return true; } catch (IOException e) { - myLogger.warn("grantAllPackageNeededPermissions for file failed, file path: " + packageFile.getAbsolutePath(), e); + myLogger.warn( + "grantAllPackageNeededPermissions for file failed, file path: " + packageFile.getAbsolutePath(), + e); } return false; } @@ -266,7 +230,8 @@ public boolean grantAllPackageNeededPermissions(@NotNull DeviceInfo deviceInfo, * @return return true if succeeded. */ @Override - public boolean grantAllTaskNeededPermissions(@NotNull DeviceInfo deviceInfo, @NotNull TestTask task, @Nullable Logger logger) { + public boolean grantAllTaskNeededPermissions(@NotNull DeviceInfo deviceInfo, @NotNull TestTask task, + @Nullable Logger logger) { Logger myLogger = classLogger; if (logger != null) { myLogger = logger; @@ -293,7 +258,9 @@ public boolean grantAllTaskNeededPermissions(@NotNull DeviceInfo deviceInfo, @No try { grantPermission(deviceInfo, task.getPkgName(), usesPermission, logger); } catch (Exception e) { - myLogger.info(String.format("error occurred when granting permission %s to package %s", usesPermission, task.getPkgName()), e); + myLogger.info( + String.format("error occurred when granting permission %s to package %s", usesPermission, + task.getPkgName()), e); } } return succeeded; @@ -302,7 +269,8 @@ public boolean grantAllTaskNeededPermissions(@NotNull DeviceInfo deviceInfo, @No @Override public void addToBatteryWhiteList(DeviceInfo deviceInfo, String packageName, Logger logger) { try { - adbOperateUtil.execOnDevice(deviceInfo, String.format("dumpsys deviceidle whitelist +%s", packageName), new MultiLineNoCancelLoggingReceiver(logger), logger); + adbOperateUtil.execOnDevice(deviceInfo, String.format("dumpsys deviceidle whitelist +%s", packageName), + new MultiLineNoCancelLoggingReceiver(logger), logger); } catch (RuntimeException e) { classLogger.error(e.getMessage(), e); } @@ -317,7 +285,8 @@ public void addToBatteryWhiteList(DeviceInfo deviceInfo, String packageName, Log * -t: Allow test APKs to be installed. * -g: Grant all permissions listed in the app manifest. */ - public boolean installApp(DeviceInfo deviceInfo, String packagePath, @Nullable Logger logger) throws InstallException { + public boolean installApp(DeviceInfo deviceInfo, String packagePath, @Nullable Logger logger) + throws InstallException { File apk = new File(packagePath); Assert.isTrue(apk.exists(), "apk not exist!!"); return adbOperateUtil.installApp(deviceInfo, apk.getAbsolutePath(), true, "-t -d -g", logger); @@ -331,22 +300,27 @@ public boolean uninstallApp(DeviceInfo deviceInfo, String packageName, Logger lo @Override public void resetPackage(DeviceInfo deviceInfo, String packageName, Logger logger) { try { - adbOperateUtil.execOnDevice(deviceInfo, String.format("pm clear %s", packageName), new MultiLineNoCancelLoggingReceiver(logger), logger); + adbOperateUtil.execOnDevice(deviceInfo, String.format("pm clear %s", packageName), + new MultiLineNoCancelLoggingReceiver(logger), logger); } catch (Exception e) { classLogger.error(e.getMessage(), e); } } @Override - public void pushFileToDevice(@NotNull DeviceInfo deviceInfo, @NotNull String pathOnAgent, @NotNull String pathOnDevice, @Nullable Logger logger) throws IOException, InterruptedException { + public void pushFileToDevice(@NotNull DeviceInfo deviceInfo, @NotNull String pathOnAgent, + @NotNull String pathOnDevice, @Nullable Logger logger) + throws IOException, InterruptedException { adbOperateUtil.pushFileToDevice(deviceInfo, pathOnAgent, pathOnDevice, logger); } @Override - public void pullFileFromDevice(@NotNull DeviceInfo deviceInfo, @NotNull String pathOnDevice, @Nullable Logger logger) throws IOException, InterruptedException { + public void pullFileFromDevice(@NotNull DeviceInfo deviceInfo, @NotNull String pathOnDevice, + @Nullable Logger logger) throws IOException, InterruptedException { ITestRun testRun = TestRunThreadContext.getTestRun(); Assert.notNull(testRun, "There is no testRun instance in ThreadContext!"); - Assert.notNull(testRun.getResultFolder(), "The testRun instance in ThreadContext does not have resultFolder property!"); + Assert.notNull(testRun.getResultFolder(), + "The testRun instance in ThreadContext does not have resultFolder property!"); String pathOnAgent = testRun.getResultFolder().getAbsolutePath() + "/"; adbOperateUtil.pullFileToDir(deviceInfo, pathOnAgent, pathOnDevice, logger); @@ -358,14 +332,16 @@ public ScreenRecorder getScreenRecorder(DeviceInfo deviceInfo, File folder, Logg } @Override - public ADBLogcatCollector getLogCollector(DeviceInfo deviceInfo, String pkgName, TestRun testRun, Logger logger) { - return new ADBLogcatCollector(this, this.adbOperateUtil, deviceInfo, pkgName, testRun, logger); + public ADBLogcatCollector getLogCollector(DeviceInfo deviceInfo, String pkgName, TestRun testRun, + Logger logger) { + return new ADBLogcatCollector(this.adbOperateUtil, deviceInfo, pkgName, testRun, logger); } @Override public void setProperty(DeviceInfo deviceInfo, String property, String val, Logger logger) { try { - adbOperateUtil.execOnDevice(deviceInfo, String.format("setprop %s %s", property, val), new MultiLineNoCancelLoggingReceiver(logger), logger); + adbOperateUtil.execOnDevice(deviceInfo, String.format("setprop %s %s", property, val), + new MultiLineNoCancelLoggingReceiver(logger), logger); } catch (Exception e) { classLogger.error(e.getMessage(), e); } @@ -373,7 +349,8 @@ public void setProperty(DeviceInfo deviceInfo, String property, String val, Logg public void changeGlobalSetting(DeviceInfo deviceInfo, String property, String val, Logger logger) { try { - adbOperateUtil.execOnDevice(deviceInfo, String.format("settings put global %s %s", property, val), new MultiLineNoCancelLoggingReceiver(logger), logger); + adbOperateUtil.execOnDevice(deviceInfo, String.format("settings put global %s %s", property, val), + new MultiLineNoCancelLoggingReceiver(logger), logger); } catch (Exception e) { classLogger.error(e.getMessage(), e); } @@ -381,7 +358,8 @@ public void changeGlobalSetting(DeviceInfo deviceInfo, String property, String v public void changeSystemSetting(DeviceInfo deviceInfo, String property, String val, Logger logger) { try { - adbOperateUtil.execOnDevice(deviceInfo, String.format("settings put system %s %s", property, val), new MultiLineNoCancelLoggingReceiver(logger), logger); + adbOperateUtil.execOnDevice(deviceInfo, String.format("settings put system %s %s", property, val), + new MultiLineNoCancelLoggingReceiver(logger), logger); } catch (Exception e) { classLogger.error(e.getMessage(), e); } @@ -397,17 +375,24 @@ private void enableTouchPositionDisplay(DeviceInfo deviceInfo, Logger logger) th public File getScreenShot(DeviceInfo deviceInfo, Logger logger) throws Exception { File screenshotImageFile = deviceInfo.getScreenshotImageFile(); if (screenshotImageFile == null) { - screenshotImageFile = new File(getScreenshotDir(), deviceInfo.getName() + "-" + deviceInfo.getSerialNum() + ".jpg"); + screenshotImageFile = new File(agentManagementService.getScreenshotDir(), + deviceInfo.getName() + "-" + deviceInfo.getSerialNum() + ".jpg"); deviceInfo.setScreenshotImageFile(screenshotImageFile); - String imageRelPath = screenshotImageFile.getAbsolutePath().replace(new File(getDeviceStoragePath()).getAbsolutePath(), ""); - imageRelPath = getDeviceFolderUrlPrefix() + imageRelPath.replace(File.separator, "/"); + String imageRelPath = screenshotImageFile.getAbsolutePath() + .replace(new File(agentManagementService.getDeviceStoragePath()).getAbsolutePath(), ""); + imageRelPath = + agentManagementService.getDeviceFolderUrlPrefix() + imageRelPath.replace(File.separator, "/"); deviceInfo.setImageRelPath(imageRelPath); } deviceInfo.setScreenshotUpdateTimeMilli(System.currentTimeMillis()); sendKeyEvent(deviceInfo, KEYCODE_WAKEUP, logger); screenCapture(deviceInfo, screenshotImageFile.getAbsolutePath(), null); - StorageFileInfo fileInfo = new StorageFileInfo(screenshotImageFile, "device/screenshots/" + screenshotImageFile.getName(), StorageFileInfo.FileType.SCREENSHOT, EntityType.SCREENSHOT); - String fileDownloadUrl = storageServiceClientProxy.upload(screenshotImageFile, fileInfo).getBlobUrl(); + StorageFileInfo fileInfo = + new StorageFileInfo(screenshotImageFile, "device/screenshots/" + screenshotImageFile.getName(), + StorageFileInfo.FileType.SCREENSHOT, EntityType.SCREENSHOT); + String fileDownloadUrl = + agentManagementService.getStorageServiceClientProxy().upload(screenshotImageFile, fileInfo) + .getBlobUrl(); if (StringUtils.isBlank(fileDownloadUrl)) { classLogger.warn("Screenshot download url is empty for device {}", deviceInfo.getName()); } else { @@ -416,18 +401,6 @@ public File getScreenShot(DeviceInfo deviceInfo, Logger logger) throws Exception return screenshotImageFile; } - @Override - public void updateIsPrivateByDeviceSerial(String deviceSerial, Boolean isPrivate) { - DeviceInfo deviceInfo; - synchronized (adbDeviceInfoMap) { - deviceInfo = adbDeviceInfoMap.get(deviceSerial); - } - if (deviceInfo != null) { - deviceInfo.setIsPrivate(isPrivate); - } - - } - private void deviceInfoUpdate(IDevice device) { DeviceState state = device.getState(); DeviceInfo deviceInfo; @@ -453,9 +426,12 @@ private void deviceInfoUpdate(IDevice device) { } @Override - public boolean setDefaultLauncher(DeviceInfo deviceInfo, String packageName, String defaultActivity, Logger logger) { + public boolean setDefaultLauncher(DeviceInfo deviceInfo, String packageName, String defaultActivity, + Logger logger) { try { - adbOperateUtil.execOnDevice(deviceInfo, String.format("cmd package set-home-activity %s/%s", packageName, defaultActivity), new MultiLineNoCancelLoggingReceiver(logger), logger); + adbOperateUtil.execOnDevice(deviceInfo, + String.format("cmd package set-home-activity %s/%s", packageName, defaultActivity), + new MultiLineNoCancelLoggingReceiver(logger), logger); return true; } catch (Exception e) { logger.error(e.getMessage(), e); @@ -490,7 +466,8 @@ private BufferedImage toBufferedImage(RawImage rawImage) { private boolean sendKeyEvent(DeviceInfo deviceInfo, String event, Logger logger) { try { - adbOperateUtil.execOnDevice(deviceInfo, String.format("input keyevent %s", event), new MultiLineNoCancelLoggingReceiver(logger), logger); + adbOperateUtil.execOnDevice(deviceInfo, String.format("input keyevent %s", event), + new MultiLineNoCancelLoggingReceiver(logger), logger); return true; } catch (Exception e) { classLogger.error(e.getMessage(), e); @@ -499,7 +476,7 @@ private boolean sendKeyEvent(DeviceInfo deviceInfo, String event, Logger logger) } private DeviceInfo getADBDeviceInfoFromDevice(IDevice device) { - DeviceInfo adbDevice = new DeviceInfo(); + DeviceInfo adbDevice = new DeviceInfo(this); updateADBDeviceInfoByDevice(device, adbDevice); return adbDevice; } @@ -535,7 +512,7 @@ private void updateADBDeviceInfoByDevice(IDevice device, DeviceInfo adbDevice) { adbDevice.setDeviceId(deviceId); adbDevice.setScreenDensity(device.getDensity()); adbDevice.setName(device.getName()); - + adbDevice.setType(DeviceType.ANDROID.name()); } } @@ -574,18 +551,19 @@ public void processNewLines(String[] lines) { private String getDeviceId(DeviceInfo deviceInfo, Logger logger) { try { final String[] idOutput = {null}; - adbOperateUtil.execOnDevice(deviceInfo, "settings get secure android_id", new MultiLineNoCancelReceiver() { - @Override - public void processNewLines(String[] lines) { - for (String line : lines) { - String trim = line.trim(); - if (StringUtils.isBlank(trim)) { - continue; + adbOperateUtil.execOnDevice(deviceInfo, "settings get secure android_id", + new MultiLineNoCancelReceiver() { + @Override + public void processNewLines(String[] lines) { + for (String line : lines) { + String trim = line.trim(); + if (StringUtils.isBlank(trim)) { + continue; + } + idOutput[0] = trim; + } } - idOutput[0] = trim; - } - } - }, logger); + }, logger); return idOutput[0]; } catch (Exception e) { classLogger.error(e.getMessage(), e); @@ -620,7 +598,8 @@ public void processNewLines(@NotNull String[] lines) { } @Override - public boolean grantProjectionAndBatteryPermission(DeviceInfo deviceInfo, String recordPackageName, Logger logger) { + public boolean grantProjectionAndBatteryPermission(DeviceInfo deviceInfo, String recordPackageName, + Logger logger) { boolean isProjectionPermissionGranted = false; stopPackageProcess(deviceInfo, recordPackageName, logger); startRecordActivity(deviceInfo, logger); @@ -648,7 +627,8 @@ public boolean grantProjectionAndBatteryPermission(DeviceInfo deviceInfo, String private String dumpView(DeviceInfo deviceInfo, Logger logger) { StringBuilder sb = new StringBuilder(); - adbOperateUtil.execOnDevice(deviceInfo, "uiautomator dump", new MultiLineNoCancelLoggingReceiver(logger), logger); + adbOperateUtil.execOnDevice(deviceInfo, "uiautomator dump", new MultiLineNoCancelLoggingReceiver(logger), + logger); adbOperateUtil.execOnDevice(deviceInfo, "cat /sdcard/window_dump.xml", new MultiLineNoCancelReceiver() { @Override public void processNewLines(@NotNull String[] lines) { @@ -662,7 +642,8 @@ public void processNewLines(@NotNull String[] lines) { private void stopPackageProcess(DeviceInfo deviceInfo, String packageName, Logger logger) { try { - adbOperateUtil.execOnDevice(Objects.requireNonNull(deviceInfo), "am force-stop " + packageName, new MultiLineNoCancelLoggingReceiver(logger), logger); + adbOperateUtil.execOnDevice(Objects.requireNonNull(deviceInfo), "am force-stop " + packageName, + new MultiLineNoCancelLoggingReceiver(logger), logger); } catch (Exception e) { logger.error(e.getMessage(), e); } @@ -670,17 +651,21 @@ private void stopPackageProcess(DeviceInfo deviceInfo, String packageName, Logge private void startRecordActivity(DeviceInfo deviceInfo, Logger logger) { try { - adbOperateUtil.execOnDevice(Objects.requireNonNull(deviceInfo), "am start -n " + recordPackageName + "/.MainActivity", new MultiLineNoCancelLoggingReceiver(logger), logger); + adbOperateUtil.execOnDevice(Objects.requireNonNull(deviceInfo), + "am start -n " + recordPackageName + "/.MainActivity", + new MultiLineNoCancelLoggingReceiver(logger), logger); } catch (Exception e) { logger.error(e.getMessage(), e); } } - private boolean clickNodeOnDeviceWithText(DeviceInfo deviceInfo, Logger logger, @NotNull String... possibleTexts) { + private boolean clickNodeOnDeviceWithText(DeviceInfo deviceInfo, Logger logger, + @NotNull String... possibleTexts) { String dump = dumpView(deviceInfo, logger); // classLogger.info("Dump on {}: {}", adbDeviceInfo.getSerialNum(), dump); if (StringUtils.isBlank(dump)) { - logger.error("did not find element with text {} on {}", Arrays.asList(possibleTexts).toString(), deviceInfo.getSerialNum()); + logger.error("did not find element with text {} on {}", Arrays.asList(possibleTexts).toString(), + deviceInfo.getSerialNum()); return false; } Document viewTree = Jsoup.parse(dump, "", Parser.xmlParser()); @@ -720,7 +705,8 @@ public void testDeviceUnset(DeviceInfo deviceInfo, Logger logger) { changeGlobalSetting(deviceInfo, "transition_animation_scale", "1", logger); changeGlobalSetting(deviceInfo, "animator_duration_scale", "1", logger); - changeSystemSetting(deviceInfo, "screen_off_timeout", String.valueOf(TimeUnit.SECONDS.toMillis(45)), logger); + changeSystemSetting(deviceInfo, "screen_off_timeout", String.valueOf(TimeUnit.SECONDS.toMillis(45)), + logger); } @Override @@ -741,9 +727,14 @@ public void execCommandOnDevice(DeviceInfo deviceInfo, String command, Logger lo @Override public void removeFileInDevice(DeviceInfo deviceInfo, String pathOnDevice, Logger logger) { try { - adbOperateUtil.execOnDevice(deviceInfo, "rm " + pathOnDevice, new MultiLineNoCancelLoggingReceiver(logger), logger); + adbOperateUtil.execOnDevice(deviceInfo, "rm " + pathOnDevice, + new MultiLineNoCancelLoggingReceiver(logger), logger); } catch (Exception e) { logger.error(e.getMessage(), e); } } -} + + public void setADBOperateUtil(ADBOperateUtil adbOperateUtil) { + this.adbOperateUtil = adbOperateUtil; + } +} \ No newline at end of file diff --git a/common/src/main/java/com/microsoft/hydralab/common/management/device/impl/IOSTestDeviceManager.java b/common/src/main/java/com/microsoft/hydralab/common/management/device/impl/IOSTestDeviceManager.java index 239849765..421a8d847 100644 --- a/common/src/main/java/com/microsoft/hydralab/common/management/device/impl/IOSTestDeviceManager.java +++ b/common/src/main/java/com/microsoft/hydralab/common/management/device/impl/IOSTestDeviceManager.java @@ -1,21 +1,24 @@ // Copyright (c) Microsoft Corporation. // Licensed under the MIT License. + package com.microsoft.hydralab.common.management.device.impl; import com.alibaba.fastjson.JSON; import com.alibaba.fastjson.JSONArray; import com.alibaba.fastjson.JSONObject; -import com.microsoft.hydralab.common.entity.common.StorageFileInfo; import com.microsoft.hydralab.common.entity.common.DeviceInfo; import com.microsoft.hydralab.common.entity.common.EntityType; +import com.microsoft.hydralab.common.entity.common.StorageFileInfo; import com.microsoft.hydralab.common.entity.common.TestRun; import com.microsoft.hydralab.common.logger.LogCollector; import com.microsoft.hydralab.common.logger.impl.IOSLogCollector; +import com.microsoft.hydralab.common.management.device.DeviceType; import com.microsoft.hydralab.common.management.device.TestDeviceManager; import com.microsoft.hydralab.common.screen.IOSAppiumScreenRecorderForMac; import com.microsoft.hydralab.common.screen.IOSAppiumScreenRecorderForWindows; import com.microsoft.hydralab.common.screen.ScreenRecorder; import com.microsoft.hydralab.common.util.AgentConstant; +import com.microsoft.hydralab.common.util.HydraLabRuntimeException; import com.microsoft.hydralab.common.util.IOSUtils; import com.microsoft.hydralab.common.util.ShellUtils; import org.apache.commons.lang3.StringUtils; @@ -29,10 +32,7 @@ import java.io.IOException; import java.time.Duration; import java.util.HashMap; -import java.util.HashSet; import java.util.Map; -import java.util.Set; -import java.util.stream.Collectors; import static com.microsoft.hydralab.common.util.AgentConstant.UNKNOWN_IOS_MODEL; @@ -48,68 +48,44 @@ public boolean isDeviceConnectedToWindows() { } @Override - public void init() throws Exception { + public void init() { String osName = System.getProperty("os.name"); - classLogger.info("Devices are connected to " + osName); if (osName.startsWith("Windows")) { isConnectedToWindowsOS = true; - IOSAppiumScreenRecorderForWindows.copyScript(testBaseDir); + IOSAppiumScreenRecorderForWindows.copyScript(agentManagementService.getTestBaseDir()); } else { // Mac, unix or linux isConnectedToWindowsOS = false; } - ShellUtils.killProcessByCommandStr("tidevice", classLogger); - IOSUtils.startIOSDeviceWatcher(classLogger, this); - } - - @Override - public Set getDeviceList(Logger logger) { - Set set = new HashSet<>(); - Set> entries = null; - synchronized (this) { - entries = new HashSet<>(iOSDeviceInfoMap.entrySet()); + try { + ShellUtils.killProcessByCommandStr("tidevice", classLogger); + IOSUtils.startIOSDeviceWatcher(classLogger, this); + } catch (Exception e) { + throw new HydraLabRuntimeException(500, "IOSDeviceManager init failed", e); } - for (Map.Entry entry : entries) { - DeviceInfo value = entry.getValue(); - if (value != null) { - set.add(value); - } - } - return set; - } - - @Override - public Set getActiveDeviceList(Logger logger) { - Set set = new HashSet<>(); - Set> entries = null; - synchronized (this) { - entries = new HashSet<>(iOSDeviceInfoMap.entrySet()); - } - for (Map.Entry entry : entries) { - DeviceInfo value = entry.getValue(); - if (value == null || !value.isAlive()) { - classLogger.debug("Invalid device: {}", value); - continue; - } - set.add(value); - } - return set; } @Override public File getScreenShot(DeviceInfo deviceInfo, Logger logger) throws Exception { File screenshotImageFile = deviceInfo.getScreenshotImageFile(); if (screenshotImageFile == null) { - screenshotImageFile = new File(getScreenshotDir(), deviceInfo.getName().replace(" ", "") + "-" + deviceInfo.getSerialNum() + ".jpg"); + screenshotImageFile = new File(agentManagementService.getScreenshotDir(), + deviceInfo.getName().replace(" ", "") + "-" + deviceInfo.getSerialNum() + ".jpg"); deviceInfo.setScreenshotImageFile(screenshotImageFile); - String imageRelPath = screenshotImageFile.getAbsolutePath().replace(new File(getDeviceStoragePath()).getAbsolutePath(), ""); - imageRelPath = getDeviceFolderUrlPrefix() + imageRelPath.replace(File.separator, "/"); + String imageRelPath = screenshotImageFile.getAbsolutePath() + .replace(new File(agentManagementService.getDeviceStoragePath()).getAbsolutePath(), ""); + imageRelPath = + agentManagementService.getDeviceFolderUrlPrefix() + imageRelPath.replace(File.separator, "/"); deviceInfo.setImageRelPath(imageRelPath); } IOSUtils.takeScreenshot(deviceInfo.getSerialNum(), screenshotImageFile.getAbsolutePath(), classLogger); deviceInfo.setScreenshotUpdateTimeMilli(System.currentTimeMillis()); - StorageFileInfo fileInfo = new StorageFileInfo(screenshotImageFile, "device/screenshots/" + screenshotImageFile.getName(), StorageFileInfo.FileType.SCREENSHOT, EntityType.SCREENSHOT); - String fileDownloadUrl = storageServiceClientProxy.upload(screenshotImageFile, fileInfo).getBlobUrl(); + StorageFileInfo fileInfo = + new StorageFileInfo(screenshotImageFile, "device/screenshots/" + screenshotImageFile.getName(), + StorageFileInfo.FileType.SCREENSHOT, EntityType.SCREENSHOT); + String fileDownloadUrl = + agentManagementService.getStorageServiceClientProxy().upload(screenshotImageFile, fileInfo) + .getBlobUrl(); if (StringUtils.isBlank(fileDownloadUrl)) { classLogger.warn("Screenshot download url is empty for device {}", deviceInfo.getName()); } else { @@ -118,26 +94,6 @@ public File getScreenShot(DeviceInfo deviceInfo, Logger logger) throws Exception return screenshotImageFile; } - @Override - public void resetDeviceByTestId(String testId, Logger logger) { - Set devices = getActiveDeviceList(logger).stream() - .filter(adbDeviceInfo -> testId.equals(adbDeviceInfo.getRunningTaskId())) - .collect(Collectors.toSet()); - for (DeviceInfo device : devices) { - resetDevice(device.getSerialNum()); - } - } - - private void resetDevice(String serialNum) { - DeviceInfo deviceInfo = iOSDeviceInfoMap.get(serialNum); - if (deviceInfo == null) { - return; - } - synchronized (deviceInfo) { - deviceInfo.reset(); - } - } - @Override public void wakeUpDevice(DeviceInfo deviceInfo, Logger logger) { classLogger.info("Unlocking may not work as expected, please keep your device wake."); @@ -156,7 +112,8 @@ public void grantPermission(DeviceInfo deviceInfo, String packageName, String pe } @Override - public void addToBatteryWhiteList(@NotNull DeviceInfo deviceInfo, @NotNull String packageName, @NotNull Logger logger) { + public void addToBatteryWhiteList(@NotNull DeviceInfo deviceInfo, @NotNull String packageName, + @NotNull Logger logger) { classLogger.info("Nothing Implemented for iOS in " + currentMethodName()); } @@ -178,12 +135,14 @@ public void resetPackage(DeviceInfo deviceInfo, String packageName, Logger logge } @Override - public void pushFileToDevice(@NotNull DeviceInfo deviceInfo, @NotNull String pathOnAgent, @NotNull String pathOnDevice, @Nullable Logger logger) { + public void pushFileToDevice(@NotNull DeviceInfo deviceInfo, @NotNull String pathOnAgent, + @NotNull String pathOnDevice, @Nullable Logger logger) { classLogger.info("Nothing Implemented for iOS in " + currentMethodName()); } @Override - public void pullFileFromDevice(@NotNull DeviceInfo deviceInfo, @NotNull String pathOnDevice, @Nullable Logger logger) { + public void pullFileFromDevice(@NotNull DeviceInfo deviceInfo, @NotNull String pathOnDevice, + @Nullable Logger logger) { classLogger.info("Nothing Implemented for iOS in " + currentMethodName()); } @@ -198,7 +157,7 @@ public ScreenRecorder getScreenRecorder(DeviceInfo deviceInfo, File folder, Logg @Override public LogCollector getLogCollector(DeviceInfo deviceInfo, String pkgName, TestRun testRun, Logger logger) { - return new IOSLogCollector(this, deviceInfo, pkgName, testRun, logger); + return new IOSLogCollector(deviceInfo, pkgName, testRun, logger); } @Override @@ -207,18 +166,8 @@ public void setProperty(DeviceInfo deviceInfo, String property, String val, Logg } @Override - public void updateIsPrivateByDeviceSerial(String deviceSerial, Boolean isPrivate) { - DeviceInfo deviceInfo; - synchronized (iOSDeviceInfoMap) { - deviceInfo = iOSDeviceInfoMap.get(deviceSerial); - } - if (deviceInfo != null) { - deviceInfo.setIsPrivate(isPrivate); - } - } - - @Override - public boolean setDefaultLauncher(DeviceInfo deviceInfo, String packageName, String defaultActivity, Logger logger) { + public boolean setDefaultLauncher(DeviceInfo deviceInfo, String packageName, String defaultActivity, + Logger logger) { classLogger.info("Nothing Implemented for iOS in " + currentMethodName()); return true; } @@ -256,7 +205,7 @@ public void updateAllDeviceInfo() { DeviceInfo removedInfo = latestDeviceInfoMap.remove(serialNum); if (removedInfo != null) { if (DeviceInfo.OFFLINE.equals(info.getStatus())) { - deviceStatusListenerManager.onDeviceConnected(info); + agentManagementService.getDeviceStatusListenerManager().onDeviceConnected(info); info.setStatus(DeviceInfo.ONLINE); // classLogger.info("Device " + serialNum + " updated"); } @@ -268,7 +217,7 @@ public void updateAllDeviceInfo() { // Device was disconnected // classLogger.info("Device " + serialNum + " disconnected"); info.setStatus(DeviceInfo.OFFLINE); - deviceStatusListenerManager.onDeviceInactive(info); + agentManagementService.getDeviceStatusListenerManager().onDeviceInactive(info); getAppiumServerManager().quitIOSDriver(info, classLogger); } } @@ -279,13 +228,13 @@ public void updateAllDeviceInfo() { info.setStatus(DeviceInfo.ONLINE); // Add new connected devices iOSDeviceInfoMap.put(serialNum, info); - deviceStatusListenerManager.onDeviceConnected(info); + agentManagementService.getDeviceStatusListenerManager().onDeviceConnected(info); } } } public DeviceInfo parseJsonToDevice(JSONObject deviceObject) { - DeviceInfo deviceInfo = new DeviceInfo(); + DeviceInfo deviceInfo = new DeviceInfo(this); String udid = deviceObject.getString("udid"); deviceInfo.setSerialNum(udid); deviceInfo.setDeviceId(udid); @@ -297,6 +246,7 @@ public DeviceInfo parseJsonToDevice(JSONObject deviceObject) { deviceInfo.setOsSDKInt(""); deviceInfo.setScreenDensity(0); deviceInfo.setScreenSize(""); + deviceInfo.setType(DeviceType.IOS.name()); updateDeviceDetailByUdid(deviceInfo, udid); return deviceInfo; } @@ -322,7 +272,8 @@ public void updateDeviceDetailByUdid(DeviceInfo deviceInfo, String udid) { } @Override - public boolean grantProjectionAndBatteryPermission(DeviceInfo deviceInfo, String recordPackageName, Logger logger) { + public boolean grantProjectionAndBatteryPermission(DeviceInfo deviceInfo, String recordPackageName, + Logger logger) { classLogger.info("Nothing Implemented for iOS in " + currentMethodName()); return true; } @@ -367,4 +318,4 @@ public void runAppiumMonkey(DeviceInfo deviceInfo, String packageName, int round IOSUtils.launchApp(deviceInfo.getSerialNum(), packageName, logger); super.runAppiumMonkey(deviceInfo, packageName, round, logger); } -} +} \ No newline at end of file diff --git a/common/src/main/java/com/microsoft/hydralab/common/management/device/impl/WindowsTestDeviceManager.java b/common/src/main/java/com/microsoft/hydralab/common/management/device/impl/WindowsTestDeviceManager.java index 752d72068..c6ad6d3bd 100644 --- a/common/src/main/java/com/microsoft/hydralab/common/management/device/impl/WindowsTestDeviceManager.java +++ b/common/src/main/java/com/microsoft/hydralab/common/management/device/impl/WindowsTestDeviceManager.java @@ -4,14 +4,21 @@ import cn.hutool.core.img.ImgUtil; import com.android.ddmlib.TimeoutException; -import com.microsoft.hydralab.common.entity.common.StorageFileInfo; import com.microsoft.hydralab.common.entity.common.DeviceInfo; import com.microsoft.hydralab.common.entity.common.EntityType; +import com.microsoft.hydralab.common.entity.common.StorageFileInfo; +import com.microsoft.hydralab.common.management.AgentManagementService; +import com.microsoft.hydralab.common.management.AppiumServerManager; import com.microsoft.hydralab.common.screen.AppiumE2ETestRecorder; import com.microsoft.hydralab.common.screen.ScreenRecorder; +import com.microsoft.hydralab.common.util.ADBOperateUtil; import com.microsoft.hydralab.common.util.ThreadPoolUtil; import com.microsoft.hydralab.common.util.ThreadUtils; -import com.microsoft.hydralab.t2c.runner.*; +import com.microsoft.hydralab.t2c.runner.ActionInfo; +import com.microsoft.hydralab.t2c.runner.DriverInfo; +import com.microsoft.hydralab.t2c.runner.T2CAppiumUtils; +import com.microsoft.hydralab.t2c.runner.T2CJsonParser; +import com.microsoft.hydralab.t2c.runner.TestInfo; import com.microsoft.hydralab.t2c.runner.controller.AndroidDriverController; import com.microsoft.hydralab.t2c.runner.controller.BaseDriverController; import com.microsoft.hydralab.t2c.runner.controller.EdgeDriverController; @@ -38,10 +45,13 @@ public File getScreenShot(DeviceInfo deviceInfo, Logger logger) throws Exception super.getScreenShot(deviceInfo, logger); File pcScreenShotImageFile = deviceInfo.getPcScreenshotImageFile(); if (pcScreenShotImageFile == null) { - pcScreenShotImageFile = new File(screenshotDir, deviceInfo.getName() + "-" + deviceInfo.getSerialNum() + "-" + "pc" + ".jpg"); + pcScreenShotImageFile = new File(agentManagementService.getScreenshotDir(), + deviceInfo.getName() + "-" + deviceInfo.getSerialNum() + "-" + "pc" + ".jpg"); deviceInfo.setPcScreenshotImageFile(pcScreenShotImageFile); - String pcImageRelPath = pcScreenShotImageFile.getAbsolutePath().replace(new File(getDeviceStoragePath()).getAbsolutePath(), ""); - pcImageRelPath = getDeviceFolderUrlPrefix() + pcImageRelPath.replace(File.separator, "/"); + String pcImageRelPath = pcScreenShotImageFile.getAbsolutePath() + .replace(new File(agentManagementService.getDeviceStoragePath()).getAbsolutePath(), ""); + pcImageRelPath = + agentManagementService.getDeviceFolderUrlPrefix() + pcImageRelPath.replace(File.separator, "/"); deviceInfo.setPcImageRelPath(pcImageRelPath); } try { @@ -49,8 +59,12 @@ public File getScreenShot(DeviceInfo deviceInfo, Logger logger) throws Exception } catch (IOException e) { classLogger.error("Screen capture failed for device: {}", deviceInfo, e); } - StorageFileInfo fileInfo = new StorageFileInfo(pcScreenShotImageFile, "device/screenshots/" + pcScreenShotImageFile.getName(), StorageFileInfo.FileType.SCREENSHOT, EntityType.SCREENSHOT); - String fileDownloadUrl = storageServiceClientProxy.upload(pcScreenShotImageFile, fileInfo).getBlobUrl(); + StorageFileInfo fileInfo = + new StorageFileInfo(pcScreenShotImageFile, "device/screenshots/" + pcScreenShotImageFile.getName(), + StorageFileInfo.FileType.SCREENSHOT, EntityType.SCREENSHOT); + String fileDownloadUrl = + agentManagementService.getStorageServiceClientProxy().upload(pcScreenShotImageFile, fileInfo) + .getBlobUrl(); if (StringUtils.isBlank(fileDownloadUrl)) { classLogger.warn("Screenshot download url is empty for device {}", deviceInfo.getName()); } else { @@ -98,7 +112,7 @@ public ScreenRecorder getScreenRecorder(DeviceInfo deviceInfo, File folder, Logg } public File joinImages(File PCFile, File PhoneFile, String outFileName) { - File outFile = new File(screenshotDir, outFileName); + File outFile = new File(agentManagementService.getScreenshotDir(), outFileName); try { BufferedImage image_pc = ImageIO.read(PCFile); int width_pc = image_pc.getWidth(); diff --git a/common/src/main/java/com/microsoft/hydralab/common/management/listener/impl/DeviceStabilityMonitor.java b/common/src/main/java/com/microsoft/hydralab/common/management/listener/impl/DeviceStabilityMonitor.java index 8e16b141d..8d029d7b3 100644 --- a/common/src/main/java/com/microsoft/hydralab/common/management/listener/impl/DeviceStabilityMonitor.java +++ b/common/src/main/java/com/microsoft/hydralab/common/management/listener/impl/DeviceStabilityMonitor.java @@ -5,6 +5,7 @@ import com.android.ddmlib.IDevice; import com.microsoft.hydralab.common.entity.agent.DeviceStateChangeRecord; import com.microsoft.hydralab.common.entity.common.DeviceInfo; +import com.microsoft.hydralab.common.management.AgentManagementService; import com.microsoft.hydralab.common.management.device.TestDeviceManager; import com.microsoft.hydralab.common.management.listener.DeviceStatusListener; import com.microsoft.hydralab.common.management.listener.MobileDeviceState; @@ -46,7 +47,7 @@ public class DeviceStabilityMonitor implements DeviceStatusListener { * 3. switching between connected(OFFLINE)/connected(ONLINE) */ - private TestDeviceManager testDeviceManager; + private AgentManagementService agentManagementService; private int deviceStateChangeThreshold; private long deviceStateChangeWindowTime; private long deviceStateChangeRecoveryTime; @@ -233,7 +234,7 @@ private int getDeviceADBTimeoutSignal(DeviceInfo deviceInfo) { @Scheduled(cron = "*/10 * * * * *") public void refreshDeviceStateChangeTimes() { LocalDateTime now = LocalDateTime.now(Clock.systemUTC()); - Set deviceInfos = testDeviceManager.getDeviceList(null); + Set deviceInfos = agentManagementService.getDeviceList(null); for (DeviceInfo info : deviceInfos) { cleanOutdatedDeviceStateChange(deviceStateChangesMap.get(info.getSerialNum()), now, info); diff --git a/common/src/main/java/com/microsoft/hydralab/common/management/listener/impl/PreInstallListener.java b/common/src/main/java/com/microsoft/hydralab/common/management/listener/impl/PreInstallListener.java index 4da610a3d..594ac5d5e 100644 --- a/common/src/main/java/com/microsoft/hydralab/common/management/listener/impl/PreInstallListener.java +++ b/common/src/main/java/com/microsoft/hydralab/common/management/listener/impl/PreInstallListener.java @@ -1,9 +1,11 @@ // Copyright (c) Microsoft Corporation. // Licensed under the MIT License. + package com.microsoft.hydralab.common.management.listener.impl; import com.microsoft.hydralab.common.entity.common.DeviceInfo; -import com.microsoft.hydralab.common.management.device.TestDeviceManager; +import com.microsoft.hydralab.common.management.AgentManagementService; +import com.microsoft.hydralab.common.management.device.DeviceType; import com.microsoft.hydralab.common.management.listener.DeviceStatusListener; import com.microsoft.hydralab.common.util.Const; import com.microsoft.hydralab.common.util.FlowUtil; @@ -13,6 +15,8 @@ import org.springframework.http.HttpStatus; import java.io.File; +import java.util.Map; +import java.util.Set; /** * @author zhoule @@ -20,11 +24,16 @@ */ public class PreInstallListener implements DeviceStatusListener { - TestDeviceManager testDeviceManager; + static Map> suffixMap = Map.of( + DeviceType.ANDROID, Set.of("apk"), + DeviceType.IOS, Set.of("ipa", "app"), + DeviceType.WINDOWS, Set.of("appx", "appxbundle") + ); + AgentManagementService agentManagementService; private Logger classLogger = LoggerFactory.getLogger(PreInstallListener.class); - public PreInstallListener(TestDeviceManager testDeviceManager) { - this.testDeviceManager = testDeviceManager; + public PreInstallListener(AgentManagementService agentManagementService) { + this.agentManagementService = agentManagementService; } @Override @@ -34,24 +43,27 @@ public void onDeviceInactive(DeviceInfo deviceInfo) { @Override public void onDeviceConnected(DeviceInfo deviceInfo) { - File appDir = testDeviceManager.getPreAppDir(); + File appDir = agentManagementService.getPreAppDir(); File[] appFiles = appDir.listFiles(); for (File appFile : appFiles) { - if (!appFile.isFile()) { + if (!appFile.isFile() || + suffixMap.getOrDefault(DeviceType.valueOf(deviceInfo.getType()), Set.of()) + .stream().noneMatch(suffix -> appFile.getName().endsWith(suffix))) { continue; } try { - FlowUtil.retryAndSleepWhenFalse(3, 10, () -> testDeviceManager.installApp(deviceInfo, appFile.getAbsolutePath(), classLogger)); + FlowUtil.retryAndSleepWhenFalse(3, 10, () -> deviceInfo.getTestDeviceManager() + .installApp(deviceInfo, appFile.getAbsolutePath(), classLogger)); classLogger.info("Pre-Install {} successfully", appFile.getAbsolutePath()); break; } catch (Exception e) { String errorMessage = String.format("Pre-Install %s failed", appFile.getAbsolutePath()); classLogger.error(errorMessage, e); - if (Const.PreInstallFailurePolicy.SHUTDOWN.equals(testDeviceManager.getPreInstallFailurePolicy())) { + if (Const.PreInstallFailurePolicy.SHUTDOWN.equals( + agentManagementService.getPreInstallFailurePolicy())) { throw new HydraLabRuntimeException(HttpStatus.INTERNAL_SERVER_ERROR.value(), errorMessage, e); } } } } - -} +} \ No newline at end of file