diff --git a/pom.xml b/pom.xml
index 7e96cc33..016e9094 100644
--- a/pom.xml
+++ b/pom.xml
@@ -6,7 +6,7 @@
com.sonic
sonic-agent
- v1.2.0-release
+ v1.3.0-beta1
jar
diff --git a/src/main/java/com/sonic/agent/automation/AndroidStepHandler.java b/src/main/java/com/sonic/agent/automation/AndroidStepHandler.java
index b52e0467..2d2f713f 100644
--- a/src/main/java/com/sonic/agent/automation/AndroidStepHandler.java
+++ b/src/main/java/com/sonic/agent/automation/AndroidStepHandler.java
@@ -120,6 +120,11 @@ public void startAndroidDriver(String udId) throws InterruptedException {
desiredCapabilities.setCapability(AndroidMobileCapabilityType.ADB_EXEC_TIMEOUT, 60000);
//UIA2安装超时时间
desiredCapabilities.setCapability("uiautomator2ServerInstallTimeout", 30000);
+
+ //io.appium.uiautomator2.server io.appium.uiautomator2.server.test //io.appium.settings
+// desiredCapabilities.setCapability("skipServerInstallation",true);
+// desiredCapabilities.setCapability("disableWindowAnimation",true);
+// desiredCapabilities.setCapability("skipDeviceInitialization",true);
//等待新命令超时时间
desiredCapabilities.setCapability(MobileCapabilityType.NEW_COMMAND_TIMEOUT, 3600);
//不重置应用
@@ -361,13 +366,12 @@ public void startRecord() {
}
/**
- * @param udId
* @return void
* @author ZhouYiXun
* @des 停止录像
* @date 2021/8/16 23:56
*/
- public void stopRecord(String udId) {
+ public void stopRecord() {
File recordDir = new File("test-output/record");
if (!recordDir.exists()) {
recordDir.mkdirs();
diff --git a/src/main/java/com/sonic/agent/automation/IOSStepHandler.java b/src/main/java/com/sonic/agent/automation/IOSStepHandler.java
index 7abeae75..91742b1f 100644
--- a/src/main/java/com/sonic/agent/automation/IOSStepHandler.java
+++ b/src/main/java/com/sonic/agent/automation/IOSStepHandler.java
@@ -4,20 +4,31 @@
import com.alibaba.fastjson.JSONArray;
import com.alibaba.fastjson.JSONObject;
import com.sonic.agent.bridge.ios.TIDeviceTool;
+import com.sonic.agent.cv.*;
+import com.sonic.agent.interfaces.ErrorType;
import com.sonic.agent.interfaces.ResultDetailStatus;
import com.sonic.agent.interfaces.StepType;
import com.sonic.agent.maps.IOSProcessMap;
+import com.sonic.agent.maps.IOSSizeMap;
+import com.sonic.agent.tools.DownImageTool;
import com.sonic.agent.tools.LogTool;
import com.sonic.agent.tools.UploadTools;
import io.appium.java_client.MobileBy;
+import io.appium.java_client.MultiTouchAction;
import io.appium.java_client.Setting;
+import io.appium.java_client.TouchAction;
import io.appium.java_client.android.appmanagement.AndroidTerminateApplicationOptions;
import io.appium.java_client.android.nativekey.AndroidKey;
import io.appium.java_client.android.nativekey.KeyEvent;
+import io.appium.java_client.appmanagement.BaseInstallApplicationOptions;
+import io.appium.java_client.appmanagement.BaseTerminateApplicationOptions;
import io.appium.java_client.ios.IOSDriver;
+import io.appium.java_client.ios.IOSStartScreenRecordingOptions;
import io.appium.java_client.remote.AutomationName;
import io.appium.java_client.remote.IOSMobileCapabilityType;
import io.appium.java_client.remote.MobileCapabilityType;
+import io.appium.java_client.touch.WaitOptions;
+import io.appium.java_client.touch.offset.PointOption;
import org.jsoup.Jsoup;
import org.jsoup.nodes.Attribute;
import org.jsoup.nodes.Document;
@@ -27,15 +38,25 @@
import org.openqa.selenium.WebElement;
import org.openqa.selenium.remote.DesiredCapabilities;
import org.openqa.selenium.support.ui.ExpectedConditions;
+import org.springframework.util.Base64Utils;
+import org.springframework.util.FileCopyUtils;
+import java.io.File;
+import java.io.FileOutputStream;
import java.io.IOException;
import java.time.Duration;
+import java.util.Calendar;
+import java.util.HashMap;
import java.util.List;
+import java.util.Map;
import java.util.concurrent.TimeUnit;
+import static org.testng.Assert.*;
+
public class IOSStepHandler {
public LogTool log = new LogTool();
private IOSDriver iosDriver;
+ private JSONObject globalParams = new JSONObject();
private String testPackage = "";
private String udId = "";
//测试状态
@@ -49,10 +70,12 @@ public void setTestMode(int caseId, int resultId, String udId, String type, Stri
log.sessionId = sessionId;
}
- public int startIOSDriver(String udId) throws InterruptedException, IOException {
+ public void setGlobalParams(JSONObject jsonObject) {
+ globalParams = jsonObject;
+ }
+
+ public void startIOSDriver(String udId, int wdaPort) throws InterruptedException, IOException {
this.udId = udId;
- int wdaPort = TIDeviceTool.startWda(udId);
- int imgPort = TIDeviceTool.relayImg(udId);
Thread.sleep(2000);
DesiredCapabilities desiredCapabilities = new DesiredCapabilities();
desiredCapabilities.setCapability(MobileCapabilityType.PLATFORM_NAME, Platform.IOS);
@@ -64,8 +87,10 @@ public int startIOSDriver(String udId) throws InterruptedException, IOException
desiredCapabilities.setCapability("wdaConnectionTimeout", 60000);
desiredCapabilities.setCapability(IOSMobileCapabilityType.WEB_DRIVER_AGENT_URL, "http://127.0.0.1:" + wdaPort);
desiredCapabilities.setCapability("useXctestrunFile", false);
- desiredCapabilities.setCapability("mjpegServerPort", imgPort);
- desiredCapabilities.setCapability(IOSMobileCapabilityType.USE_PREBUILT_WDA, true);
+ desiredCapabilities.setCapability(IOSMobileCapabilityType.SHOW_IOS_LOG, false);
+ desiredCapabilities.setCapability(IOSMobileCapabilityType.SHOW_XCODE_LOG, false);
+ desiredCapabilities.setCapability("skipLogCapture", true);
+ desiredCapabilities.setCapability(IOSMobileCapabilityType.USE_PREBUILT_WDA, false);
try {
iosDriver = new IOSDriver(AppiumServer.service.getUrl(), desiredCapabilities);
iosDriver.manage().timeouts().implicitlyWait(30, TimeUnit.SECONDS);
@@ -77,7 +102,9 @@ public int startIOSDriver(String udId) throws InterruptedException, IOException
setResultDetailStatus(ResultDetailStatus.FAIL);
throw e;
}
- return imgPort;
+ int width = iosDriver.manage().window().getSize().width;
+ int height = iosDriver.manage().window().getSize().height;
+ IOSSizeMap.getMap().put(udId, width + "x" + height);
}
public void closeIOSDriver() {
@@ -112,6 +139,16 @@ public void closeIOSDriver() {
}
}
+ public void waitDevice(int waitCount) {
+ log.sendStepLog(StepType.INFO, "设备非空闲状态!第" + waitCount + "次等待连接...", "");
+ }
+
+ public void waitDeviceTimeOut() {
+ log.sendStepLog(StepType.ERROR, "等待设备超时!测试跳过!", "");
+ //测试标记为异常
+ setResultDetailStatus(ResultDetailStatus.WARN);
+ }
+
public IOSDriver getDriver() {
return iosDriver;
}
@@ -122,6 +159,31 @@ public void setResultDetailStatus(int status) {
}
}
+ public void sendStatus() {
+ log.sendStatusLog(status);
+ }
+
+ //判断有无出错
+ public int getStatus() {
+ return status;
+ }
+
+ //调试每次重设状态
+ public void resetResultDetailStatus() {
+ status = 1;
+ }
+
+ public boolean getBattery() {
+ double battery = iosDriver.getBatteryInfo().getLevel();
+ if (battery <= 0.1) {
+ log.sendStepLog(StepType.ERROR, "设备电量过低!", "跳过本次测试...");
+ return true;
+ } else {
+ return false;
+ }
+ }
+
+
private int xpathId = 1;
public JSONArray getResource() {
@@ -170,6 +232,95 @@ public JSONArray getChild(org.jsoup.select.Elements elements, String xpath) {
return elementList;
}
+ public void startRecord() {
+ try {
+ IOSStartScreenRecordingOptions recordOption = new IOSStartScreenRecordingOptions();
+ recordOption.withTimeLimit(Duration.ofMinutes(30));
+ recordOption.withVideoQuality(IOSStartScreenRecordingOptions.VideoQuality.LOW);
+ recordOption.enableForcedRestart();
+ recordOption.withFps(20);
+ recordOption.withVideoType("h264");
+ iosDriver.startRecordingScreen(recordOption);
+ } catch (Exception e) {
+ log.sendRecordLog(false, "", "");
+ }
+ }
+
+ public void stopRecord() {
+ File recordDir = new File("./test-output/record");
+ if (!recordDir.exists()) {//判断文件目录是否存在
+ recordDir.mkdirs();
+ }
+ long timeMillis = Calendar.getInstance().getTimeInMillis();
+ String fileName = timeMillis + "_" + udId.substring(0, 4) + ".mp4";
+ File uploadFile = new File(recordDir + File.separator + fileName);
+ try {
+ synchronized (IOSStepHandler.class) {
+ FileOutputStream fileOutputStream = new FileOutputStream(uploadFile);
+ byte[] bytes = Base64Utils.decodeFromString((iosDriver.stopRecordingScreen()));
+ fileOutputStream.write(bytes);
+ fileOutputStream.close();
+ }
+ log.sendRecordLog(true, fileName, UploadTools.uploadPatchRecord(uploadFile));
+ } catch (Exception e) {
+ log.sendRecordLog(false, fileName, "");
+ }
+ }
+
+ public void install(HandleDes handleDes, String path) {
+ handleDes.setStepDes("安装应用");
+ handleDes.setDetail("App安装路径: " + path);
+ try {
+ iosDriver.installApp(path, new BaseInstallApplicationOptions() {
+ @Override
+ public Map build() {
+ Map map = new HashMap<>();
+ map.put("timeout", 180000);
+ return map;
+ }
+ });
+ } catch (Exception e) {
+ handleDes.setE(e);
+ }
+ }
+
+ public void uninstall(HandleDes handleDes, String appPackage) {
+ handleDes.setStepDes("卸载应用");
+ handleDes.setDetail("App包名: " + appPackage);
+ try {
+ iosDriver.removeApp(appPackage);
+ } catch (Exception e) {
+ handleDes.setE(e);
+ }
+ }
+
+ public void terminate(HandleDes handleDes, String packageName) {
+ handleDes.setStepDes("终止应用");
+ handleDes.setDetail("应用包名: " + packageName);
+ try {
+ iosDriver.terminateApp(packageName, new BaseTerminateApplicationOptions() {
+ @Override
+ public Map build() {
+ Map map = new HashMap<>();
+ map.put("timeout", 2000);
+ return map;
+ }
+ });
+ } catch (Exception e) {
+ handleDes.setE(e);
+ }
+ }
+
+ public void runBackground(HandleDes handleDes, long time) {
+ handleDes.setStepDes("后台运行应用");
+ handleDes.setDetail("后台运行App " + time + " ms");
+ try {
+ iosDriver.runAppInBackground(Duration.ofMillis(time));
+ } catch (Exception e) {
+ handleDes.setE(e);
+ }
+ }
+
public void openApp(HandleDes handleDes, String appPackage) {
handleDes.setStepDes("打开应用");
handleDes.setDetail("App包名: " + appPackage);
@@ -199,6 +350,333 @@ public void unLock(HandleDes handleDes) {
}
}
+ public void asserts(HandleDes handleDes, String actual, String expect, String type) {
+ handleDes.setDetail("真实值: " + actual + " 期望值: " + expect);
+ try {
+ switch (type) {
+ case "assertEquals":
+ handleDes.setStepDes("断言验证(相等)");
+ assertEquals(actual, expect);
+ break;
+ case "assertTrue":
+ handleDes.setStepDes("断言验证(包含)");
+ assertTrue(actual.contains(expect));
+ break;
+ case "assertNotTrue":
+ handleDes.setStepDes("断言验证(不包含)");
+ assertFalse(actual.contains(expect));
+ break;
+ }
+ } catch (AssertionError e) {
+ handleDes.setE(e);
+ }
+ }
+
+ public String getText(HandleDes handleDes, String des, String selector, String pathValue) {
+ String s = "";
+ handleDes.setStepDes("获取" + des + "文本");
+ handleDes.setDetail("获取" + selector + ":" + pathValue + "文本");
+ try {
+ s = findEle(selector, pathValue).getText();
+ log.sendStepLog(StepType.INFO, "", "文本获取结果: " + s);
+ } catch (Exception e) {
+ handleDes.setE(e);
+ }
+ return s;
+ }
+
+ public void hideKey(HandleDes handleDes) {
+ handleDes.setStepDes("隐藏键盘");
+ handleDes.setDetail("隐藏弹出键盘");
+ try {
+ iosDriver.hideKeyboard();
+ } catch (Exception e) {
+ handleDes.setE(e);
+ }
+ }
+
+ public void click(HandleDes handleDes, String des, String selector, String pathValue) {
+ handleDes.setStepDes("点击" + des);
+ handleDes.setDetail("点击" + selector + ": " + pathValue);
+ try {
+ findEle(selector, pathValue).click();
+ } catch (Exception e) {
+ handleDes.setE(e);
+ }
+ }
+
+ public void sendKeys(HandleDes handleDes, String des, String selector, String pathValue, String keys) {
+ if (keys.contains("{{random}}")) {
+ String random = (int) (Math.random() * 10 + Math.random() * 10 * 2) + 5 + "";
+ keys = keys.replace("{{random}}", random);
+ }
+ if (keys.contains("{{timestamp}}")) {
+ String timeMillis = Calendar.getInstance().getTimeInMillis() + "";
+ keys = keys.replace("{{timestamp}}", timeMillis);
+ }
+ keys = replaceTrans(keys);
+ handleDes.setStepDes("对" + des + "输入内容");
+ handleDes.setDetail("对" + selector + ": " + pathValue + " 输入: " + keys);
+ try {
+ findEle(selector, pathValue).sendKeys(keys);
+ } catch (Exception e) {
+ handleDes.setE(e);
+ }
+ }
+
+ public void getTextAndAssert(HandleDes handleDes, String des, String selector, String pathValue, String expect) {
+ handleDes.setStepDes("获取" + des + "文本");
+ handleDes.setDetail("获取" + selector + ":" + pathValue + "文本");
+ try {
+ String s = findEle(selector, pathValue).getText();
+ log.sendStepLog(StepType.INFO, "", "文本获取结果: " + s);
+ try {
+ expect = replaceTrans(expect);
+ assertEquals(s, expect);
+ log.sendStepLog(StepType.INFO, "验证文本", "真实值: " + s + " 期望值: " + expect);
+ } catch (AssertionError e) {
+ log.sendStepLog(StepType.ERROR, "验证" + des + "文本失败!", "");
+ handleDes.setE(e);
+ }
+ } catch (Exception e) {
+ handleDes.setE(e);
+ }
+ }
+
+ public void longPressPoint(HandleDes handleDes, String des, String xy, int time) {
+ int x = Integer.parseInt(xy.substring(0, xy.indexOf(",")));
+ int y = Integer.parseInt(xy.substring(xy.indexOf(",") + 1));
+ handleDes.setStepDes("长按" + des);
+ handleDes.setDetail("长按坐标" + time + "毫秒 (" + x + "," + y + ")");
+ try {
+ TouchAction ta = new TouchAction(iosDriver);
+ ta.longPress(PointOption.point(x, y)).waitAction(WaitOptions.waitOptions(Duration.ofMillis(time))).release().perform();
+ } catch (Exception e) {
+ handleDes.setE(e);
+ }
+ }
+
+ public void keyCode(HandleDes handleDes, String key) {
+ handleDes.setStepDes("按系统按键" + key + "键");
+ try {
+ iosDriver.executeScript("mobile:pressButton", JSON.parse("{name: \"" + key + "\"}"));
+ } catch (Exception e) {
+ handleDes.setE(e);
+ }
+ }
+
+ public void multiAction(HandleDes handleDes, String des1, String xy1, String des2, String xy2, String des3, String xy3, String des4, String xy4) {
+ int x1 = Integer.parseInt(xy1.substring(0, xy1.indexOf(",")));
+ int y1 = Integer.parseInt(xy1.substring(xy1.indexOf(",") + 1));
+ int x2 = Integer.parseInt(xy2.substring(0, xy2.indexOf(",")));
+ int y2 = Integer.parseInt(xy2.substring(xy2.indexOf(",") + 1));
+ int x3 = Integer.parseInt(xy3.substring(0, xy3.indexOf(",")));
+ int y3 = Integer.parseInt(xy3.substring(xy3.indexOf(",") + 1));
+ int x4 = Integer.parseInt(xy4.substring(0, xy4.indexOf(",")));
+ int y4 = Integer.parseInt(xy4.substring(xy4.indexOf(",") + 1));
+ String detail = "坐标" + des1 + "( " + x1 + ", " + y1 + " )移动到坐标" + des2 + "( " + x2 + ", " + y2 + " ),同时坐标" + des3 + "( " + x3 + ", " + y3 + " )移动到坐标" + des4 + "( " + x4 + ", " + y4 + " )";
+ handleDes.setStepDes("双指操作");
+ handleDes.setDetail(detail);
+ try {
+ TouchAction hand1 = new TouchAction(iosDriver);
+ TouchAction hand2 = new TouchAction(iosDriver);
+ MultiTouchAction multiTouchAction = new MultiTouchAction(iosDriver);
+ hand1.press(PointOption.point(x1, y1)).moveTo(PointOption.point(x2, y2)).release();
+ hand2.press(PointOption.point(x3, y3)).moveTo(PointOption.point(x4, y4)).release();
+ multiTouchAction.add(hand1);
+ multiTouchAction.add(hand2);
+ multiTouchAction.perform();
+ } catch (Exception e) {
+ handleDes.setE(e);
+ }
+ }
+
+ public void tap(HandleDes handleDes, String des, String xy) {
+ int x = Integer.parseInt(xy.substring(0, xy.indexOf(",")));
+ int y = Integer.parseInt(xy.substring(xy.indexOf(",") + 1));
+ handleDes.setStepDes("点击" + des);
+ handleDes.setDetail("点击坐标(" + x + "," + y + ")");
+ try {
+ TouchAction ta = new TouchAction(iosDriver);
+ ta.tap(PointOption.point(x, y)).perform();
+ } catch (Exception e) {
+ handleDes.setE(e);
+ }
+ }
+
+ public void swipe(HandleDes handleDes, String des1, String xy1, String des2, String xy2) {
+ int x1 = Integer.parseInt(xy1.substring(0, xy1.indexOf(",")));
+ int y1 = Integer.parseInt(xy1.substring(xy1.indexOf(",") + 1));
+ int x2 = Integer.parseInt(xy2.substring(0, xy2.indexOf(",")));
+ int y2 = Integer.parseInt(xy2.substring(xy2.indexOf(",") + 1));
+ handleDes.setStepDes("滑动拖拽" + des1 + "到" + des2);
+ handleDes.setDetail("拖动坐标(" + x1 + "," + y1 + ")到(" + x2 + "," + y2 + ")");
+ try {
+ TouchAction ta = new TouchAction(iosDriver);
+ ta.press(PointOption.point(x1, y1)).waitAction(WaitOptions.waitOptions(Duration.ofMillis(300))).moveTo(PointOption.point(x2, y2)).release().perform();
+ } catch (Exception e) {
+ handleDes.setE(e);
+ }
+ }
+
+ public void longPress(HandleDes handleDes, String des, String selector, String pathValue, int time) {
+ handleDes.setStepDes("长按" + des);
+ handleDes.setDetail("长按控件元素" + time + "毫秒 ");
+ try {
+ TouchAction ta = new TouchAction(iosDriver);
+ WebElement webElement = findEle(selector, pathValue);
+ int x = webElement.getLocation().getX();
+ int y = webElement.getLocation().getY();
+ Duration duration = Duration.ofMillis(time);
+ ta.longPress(PointOption.point(x, y)).waitAction(WaitOptions.waitOptions(duration)).release().perform();
+ } catch (Exception e) {
+ handleDes.setE(e);
+ }
+ }
+
+ public void clear(HandleDes handleDes, String des, String selector, String pathValue) {
+ handleDes.setStepDes("清空" + des);
+ handleDes.setDetail("清空" + selector + ": " + pathValue);
+ try {
+ findEle(selector, pathValue).clear();
+ } catch (Exception e) {
+ handleDes.setE(e);
+ }
+ }
+
+ public void getTitle(HandleDes handleDes, String expect) {
+ String title = iosDriver.getTitle();
+ handleDes.setStepDes("验证网页标题");
+ handleDes.setDetail("标题:" + title + ",期望值:" + expect);
+ try {
+ assertEquals(title, expect);
+ } catch (AssertionError e) {
+ handleDes.setE(e);
+ }
+ }
+
+ public void clickByImg(HandleDes handleDes, String des, String pathValue) throws Exception {
+ handleDes.setStepDes("点击图片" + des);
+ handleDes.setDetail(pathValue);
+ File file = null;
+ if (pathValue.startsWith("http")) {
+ try {
+ file = DownImageTool.download(pathValue);
+ } catch (Exception e) {
+ handleDes.setE(e);
+ return;
+ }
+ }
+ FindResult findResult = null;
+ try {
+ SIFTFinder siftFinder = new SIFTFinder();
+ findResult = siftFinder.getSIFTFindResult(file, getScreenToLocal());
+ } catch (Exception e) {
+ log.sendStepLog(StepType.WARN, "SIFT图像算法出错,切换算法中...",
+ "");
+ }
+ if (findResult != null) {
+ log.sendStepLog(StepType.INFO, "图片定位到坐标:(" + findResult.getX() + "," + findResult.getY() + ") 耗时:" + findResult.getTime() + " ms",
+ findResult.getUrl());
+ } else {
+ log.sendStepLog(StepType.INFO, "SIFT算法无法定位图片,切换AKAZE算法中...",
+ "");
+ try {
+ AKAZEFinder akazeFinder = new AKAZEFinder();
+ findResult = akazeFinder.getAKAZEFindResult(file, getScreenToLocal());
+ } catch (Exception e) {
+ log.sendStepLog(StepType.WARN, "AKAZE图像算法出错,切换模版匹配算法中...",
+ "");
+ }
+ if (findResult != null) {
+ log.sendStepLog(StepType.INFO, "图片定位到坐标:(" + findResult.getX() + "," + findResult.getY() + ") 耗时:" + findResult.getTime() + " ms",
+ findResult.getUrl());
+ } else {
+ log.sendStepLog(StepType.INFO, "AKAZE算法无法定位图片,切换模版匹配算法中...",
+ "");
+ try {
+ TemMatcher temMatcher = new TemMatcher();
+ findResult = temMatcher.getTemMatchResult(file, getScreenToLocal());
+ } catch (Exception e) {
+ log.sendStepLog(StepType.WARN, "模版匹配算法出错",
+ "");
+ }
+ if (findResult != null) {
+ log.sendStepLog(StepType.INFO, "图片定位到坐标:(" + findResult.getX() + "," + findResult.getY() + ") 耗时:" + findResult.getTime() + " ms",
+ findResult.getUrl());
+ } else {
+ handleDes.setE(new Exception("图片定位失败!"));
+ }
+ }
+ }
+ if (findResult != null) {
+ try {
+ TouchAction ta = new TouchAction(iosDriver);
+ ta.tap(PointOption.point(findResult.getX(), findResult.getY())).perform();
+ } catch (Exception e) {
+ log.sendStepLog(StepType.ERROR, "点击" + des + "失败!", "");
+ handleDes.setE(e);
+ }
+ }
+ }
+
+
+ public void readText(HandleDes handleDes, String language, String text) throws Exception {
+ TextReader textReader = new TextReader();
+ String result = textReader.getTessResult(getScreenToLocal(), language);
+ log.sendStepLog(StepType.INFO, "",
+ "图像文字识别结果:
" + result);
+ String filter = result.replaceAll(" ", "");
+ handleDes.setStepDes("图像文字识别");
+ handleDes.setDetail("期望包含文本:" + text);
+ if (!filter.contains(text)) {
+ handleDes.setE(new Exception("图像文字识别不通过!"));
+ }
+ }
+
+ public File getScreenToLocal() {
+ File file = ((TakesScreenshot) iosDriver).getScreenshotAs(OutputType.FILE);
+ File resultFile = new File("test-output/" + log.udId + Calendar.getInstance().getTimeInMillis() + ".jpg");
+ try {
+ FileCopyUtils.copy(file, resultFile);
+ } catch (IOException e) {
+ e.printStackTrace();
+ }
+ return resultFile;
+ }
+
+ public String replaceTrans(String text) {
+ if (text.contains("{{") && text.contains("}}")) {
+ String tail = text.substring(text.indexOf("{{") + 2);
+ if (tail.contains("}}")) {
+ String child = tail.substring(tail.indexOf("}}") + 2);
+ String middle = tail.substring(0, tail.indexOf("}}"));
+ text = text.substring(0, text.indexOf("}}") + 2);
+ if (globalParams.getString(middle) != null) {
+ text = text.replace("{{" + middle + "}}", globalParams.getString(middle));
+ }
+ text = text + replaceTrans(child);
+ }
+ }
+ return text;
+ }
+
+ public void checkImage(HandleDes handleDes, String des, String pathValue, double matchThreshold) throws Exception {
+ log.sendStepLog(StepType.INFO, "开始检测" + des + "兼容", "检测与当前设备截图相似度,期望相似度为" + matchThreshold + "%");
+ File file = null;
+ if (pathValue.startsWith("http")) {
+ file = DownImageTool.download(pathValue);
+ }
+ double score = SimilarityChecker.getSimilarMSSIMScore(file, getScreenToLocal(), true);
+ handleDes.setStepDes("检测" + des + "图片相似度");
+ handleDes.setDetail("相似度为" + score * 100 + "%");
+ if (score == 0) {
+ handleDes.setE(new Exception("图片相似度检测不通过!比对图片分辨率不一致!"));
+ } else if (score < (matchThreshold / 100)) {
+ handleDes.setE(new Exception("图片相似度检测不通过!expect " + matchThreshold + " but " + score * 100));
+ }
+ }
+
public void siriCommand(HandleDes handleDes, String command) {
handleDes.setStepDes("siri指令");
handleDes.setDetail("对siri发送指令: " + command);
@@ -209,6 +687,20 @@ public void siriCommand(HandleDes handleDes, String command) {
}
}
+ public void exceptionLog(Throwable e) {
+ log.sendStepLog(StepType.WARN, "", "异常信息: " + e.fillInStackTrace().toString());
+ }
+
+ public void errorScreen() {
+ try {
+ iosDriver.context("NATIVE_APP");//先切换回app
+ log.sendStepLog(StepType.WARN, "获取异常截图", UploadTools
+ .upload(((TakesScreenshot) iosDriver).getScreenshotAs(OutputType.FILE), "imageFiles"));
+ } catch (Exception e) {
+ log.sendStepLog(StepType.ERROR, "捕获截图失败", "");
+ }
+ }
+
public String stepScreen(HandleDes handleDes) {
handleDes.setStepDes("获取截图");
String url = "";
@@ -222,6 +714,30 @@ public String stepScreen(HandleDes handleDes) {
return url;
}
+ public void pause(HandleDes handleDes, int time) {
+ handleDes.setStepDes("强制等待");
+ handleDes.setDetail("等待" + time + " ms");
+ try {
+ Thread.sleep(time);
+ } catch (InterruptedException e) {
+ handleDes.setE(e);
+ }
+ }
+
+ public void publicStep(HandleDes handleDes, String name, JSONArray stepArray) {
+ handleDes.setStepDes("执行公共步骤 " + name);
+ log.sendStepLog(StepType.WARN, "公共步骤 " + name + " 开始执行", "");
+ for (Object publicStep : stepArray) {
+ JSONObject stepDetail = (JSONObject) publicStep;
+ try {
+ runStep(stepDetail);
+ } catch (Throwable e) {
+ handleDes.setE(e);
+ break;
+ }
+ }
+ }
+
public WebElement findEle(String selector, String pathValue) {
WebElement we = null;
switch (selector) {
@@ -261,4 +777,140 @@ public WebElement findEle(String selector, String pathValue) {
}
return we;
}
+
+ public void runStep(JSONObject stepJSON) throws Throwable {
+ JSONObject step = stepJSON.getJSONObject("step");
+ JSONArray eleList = step.getJSONArray("elements");
+ HandleDes handleDes = new HandleDes();
+ switch (step.getString("stepType")) {
+ case "siriCommand":
+ siriCommand(handleDes, step.getString("content"));
+ break;
+ case "readText":
+ readText(handleDes, step.getString("content"), step.getString("text"));
+ break;
+ case "clickByImg":
+ clickByImg(handleDes, eleList.getJSONObject(0).getString("eleName")
+ , eleList.getJSONObject(0).getString("eleValue"));
+ break;
+ case "click":
+ click(handleDes, eleList.getJSONObject(0).getString("eleName"), eleList.getJSONObject(0).getString("eleType")
+ , eleList.getJSONObject(0).getString("eleValue"));
+ break;
+ case "getTitle":
+ getTitle(handleDes, step.getString("content"));
+ break;
+ case "sendKeys":
+ sendKeys(handleDes, eleList.getJSONObject(0).getString("eleName"), eleList.getJSONObject(0).getString("eleType")
+ , eleList.getJSONObject(0).getString("eleValue"), step.getString("content"));
+ break;
+ case "getText":
+ getTextAndAssert(handleDes, eleList.getJSONObject(0).getString("eleName"), eleList.getJSONObject(0).getString("eleType")
+ , eleList.getJSONObject(0).getString("eleValue"), step.getString("content"));
+ break;
+ case "clear":
+ clear(handleDes, eleList.getJSONObject(0).getString("eleName"), eleList.getJSONObject(0).getString("eleType")
+ , eleList.getJSONObject(0).getString("eleValue"));
+ break;
+ case "longPress":
+ longPress(handleDes, eleList.getJSONObject(0).getString("eleName"), eleList.getJSONObject(0).getString("eleType")
+ , eleList.getJSONObject(0).getString("eleValue"), Integer.parseInt(step.getString("content")));
+ break;
+ case "swipe":
+ swipe(handleDes, eleList.getJSONObject(0).getString("eleName"), eleList.getJSONObject(0).getString("eleValue")
+ , eleList.getJSONObject(1).getString("eleName"), eleList.getJSONObject(1).getString("eleValue"));
+ break;
+ case "tap":
+ tap(handleDes, eleList.getJSONObject(0).getString("eleName"), eleList.getJSONObject(0).getString("eleValue"));
+ break;
+ case "longPressPoint":
+ longPressPoint(handleDes, eleList.getJSONObject(0).getString("eleName"), eleList.getJSONObject(0).getString("eleValue")
+ , Integer.parseInt(step.getString("content")));
+ break;
+ case "pause":
+ pause(handleDes, Integer.parseInt(step.getString("content")));
+ break;
+ case "checkImage":
+ checkImage(handleDes, eleList.getJSONObject(0).getString("eleName"), eleList.getJSONObject(0).getString("eleValue")
+ , step.getDouble("content"));
+ break;
+ case "stepScreen":
+ stepScreen(handleDes);
+ break;
+ case "openApp":
+ openApp(handleDes, step.getString("text"));
+ break;
+ case "terminate":
+ terminate(handleDes, step.getString("text"));
+ break;
+ case "install":
+ install(handleDes, step.getString("text"));
+ break;
+ case "uninstall":
+ uninstall(handleDes, step.getString("text"));
+ break;
+ case "runBack":
+ runBackground(handleDes, Long.parseLong(step.getString("content")));
+ break;
+ case "lock":
+ lock(handleDes);
+ break;
+ case "unLock":
+ unLock(handleDes);
+ break;
+ case "zoom":
+ multiAction(handleDes, eleList.getJSONObject(0).getString("eleName"), eleList.getJSONObject(0).getString("eleValue")
+ , eleList.getJSONObject(1).getString("eleName"), eleList.getJSONObject(1).getString("eleValue")
+ , eleList.getJSONObject(2).getString("eleName"), eleList.getJSONObject(2).getString("eleValue")
+ , eleList.getJSONObject(3).getString("eleName"), eleList.getJSONObject(3).getString("eleValue"));
+ break;
+ case "keyCode":
+ keyCode(handleDes, step.getString("content"));
+ break;
+ case "assertEquals":
+ case "assertTrue":
+ case "assertNotTrue":
+ String actual = replaceTrans(step.getString("text"));
+ String expect = replaceTrans(step.getString("content"));
+ asserts(handleDes, actual, expect, step.getString("stepType"));
+ break;
+ case "getTextValue":
+ globalParams.put(step.getString("content"), getText(handleDes, eleList.getJSONObject(0).getString("eleName")
+ , eleList.getJSONObject(0).getString("eleType"), eleList.getJSONObject(0).getString("eleValue")));
+ break;
+ case "hideKey":
+ hideKey(handleDes);
+ break;
+// case "monkey":
+// runMonkey(handleDes, step.getJSONObject("content"), step.getJSONArray("text").toJavaList(JSONObject.class));
+// break;
+ case "publicStep":
+ publicStep(handleDes, step.getString("content"), stepJSON.getJSONArray("pubSteps"));
+ }
+ switchType(step.getInteger("error"), handleDes.getStepDes(), handleDes.getDetail(), handleDes.getE());
+ }
+
+ public void switchType(int error, String step, String detail, Throwable e) throws Throwable {
+ if (e != null) {
+ switch (error) {
+ case ErrorType.IGNORE:
+ log.sendStepLog(StepType.PASS, step + "异常!已忽略...", detail);
+ break;
+ case ErrorType.WARNING:
+ log.sendStepLog(StepType.WARN, step + "异常!", detail);
+ setResultDetailStatus(ResultDetailStatus.WARN);
+ errorScreen();
+ exceptionLog(e);
+ break;
+ case ErrorType.SHUTDOWN:
+ log.sendStepLog(StepType.ERROR, step + "异常!", detail);
+ setResultDetailStatus(ResultDetailStatus.FAIL);
+ errorScreen();
+ exceptionLog(e);
+ throw e;
+ }
+ } else {
+ log.sendStepLog(StepType.PASS, step, detail);
+ }
+ }
}
diff --git a/src/main/java/com/sonic/agent/bridge/android/AndroidDeviceBridgeTool.java b/src/main/java/com/sonic/agent/bridge/android/AndroidDeviceBridgeTool.java
index a95e2cb4..068aa9ca 100644
--- a/src/main/java/com/sonic/agent/bridge/android/AndroidDeviceBridgeTool.java
+++ b/src/main/java/com/sonic/agent/bridge/android/AndroidDeviceBridgeTool.java
@@ -1,6 +1,7 @@
package com.sonic.agent.bridge.android;
import com.android.ddmlib.*;
+import com.sonic.agent.tests.android.AndroidTemperThread;
import com.sonic.agent.tools.DownImageTool;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
@@ -26,7 +27,7 @@
@Component
public class AndroidDeviceBridgeTool implements ApplicationListener {
private static final Logger logger = LoggerFactory.getLogger(AndroidDeviceBridgeTool.class);
- private static AndroidDebugBridge androidDebugBridge = null;
+ public static AndroidDebugBridge androidDebugBridge = null;
@Autowired
private AndroidDeviceStatusListener androidDeviceStatusListener;
@@ -85,6 +86,7 @@ public void init() {
break;
}
}
+ new AndroidTemperThread().start();
}
/**
@@ -164,7 +166,7 @@ public static String getScreenSize(IDevice iDevice) {
size = "未知";
}
} catch (Exception e) {
- logger.info("获取屏幕尺寸失败!");
+ logger.info("获取屏幕尺寸失败!拔插瞬间可忽略该错误...");
}
return size;
}
@@ -366,6 +368,15 @@ public static void searchDevice(IDevice iDevice) {
executeCommand(iDevice, "am start -n com.sonic.plugins.assist/com.sonic.plugins.assist.SearchActivity");
}
+ public static void controlBattery(IDevice iDevice, int type) {
+ if (type == 0) {
+ executeCommand(iDevice, "dumpsys battery unplug && dumpsys battery set status 1");
+ }
+ if (type == 1) {
+ executeCommand(iDevice, "dumpsys battery reset");
+ }
+ }
+
/**
* @param udId
* @param packageName
diff --git a/src/main/java/com/sonic/agent/bridge/ios/IOSDeviceLocalStatus.java b/src/main/java/com/sonic/agent/bridge/ios/IOSDeviceLocalStatus.java
index c2c0fca6..960daeeb 100644
--- a/src/main/java/com/sonic/agent/bridge/ios/IOSDeviceLocalStatus.java
+++ b/src/main/java/com/sonic/agent/bridge/ios/IOSDeviceLocalStatus.java
@@ -3,6 +3,7 @@
import com.alibaba.fastjson.JSONObject;
import com.sonic.agent.interfaces.DeviceStatus;
import com.sonic.agent.maps.IOSDeviceManagerMap;
+import com.sonic.agent.maps.IOSSizeMap;
import com.sonic.agent.netty.NettyThreadPool;
public class IOSDeviceLocalStatus {
@@ -10,6 +11,7 @@ public class IOSDeviceLocalStatus {
public static void send(String udId, String status) {
JSONObject deviceDetail = new JSONObject();
deviceDetail.put("msg", "deviceDetail");
+ deviceDetail.put("size", IOSSizeMap.getMap().get(udId));
deviceDetail.put("udId", udId);
deviceDetail.put("status", status);
NettyThreadPool.send(deviceDetail);
diff --git a/src/main/java/com/sonic/agent/bridge/ios/TIDeviceTool.java b/src/main/java/com/sonic/agent/bridge/ios/TIDeviceTool.java
index a89f48b3..c6fbd876 100644
--- a/src/main/java/com/sonic/agent/bridge/ios/TIDeviceTool.java
+++ b/src/main/java/com/sonic/agent/bridge/ios/TIDeviceTool.java
@@ -4,6 +4,7 @@
import com.sonic.agent.interfaces.PlatformType;
import com.sonic.agent.maps.IOSDeviceManagerMap;
import com.sonic.agent.maps.IOSProcessMap;
+import com.sonic.agent.maps.IOSSizeMap;
import com.sonic.agent.netty.NettyThreadPool;
import com.sonic.agent.tools.PortTool;
import com.sonic.agent.tools.ProcessCommandTool;
@@ -90,6 +91,7 @@ public static void sendDisConnectStatus(String udId) {
deviceStatus.put("msg", "deviceDetail");
deviceStatus.put("udId", udId);
deviceStatus.put("status", "DISCONNECTED");
+ deviceStatus.put("size", IOSSizeMap.getMap().get(udId));
logger.info("iOS设备:" + udId + " 下线!");
NettyThreadPool.send(deviceStatus);
IOSDeviceManagerMap.getMap().remove(udId);
@@ -105,7 +107,7 @@ public static void sendOnlineStatus(String udId) {
deviceStatus.put("status", "ONLINE");
deviceStatus.put("platform", PlatformType.IOS);
deviceStatus.put("version", info.getString("version"));
- deviceStatus.put("size", "未知");
+ deviceStatus.put("size", IOSSizeMap.getMap().get(udId));
deviceStatus.put("cpu", info.getString("cpu"));
deviceStatus.put("manufacturer", "APPLE");
logger.info("iOS设备:" + udId + " 上线!");
@@ -158,27 +160,29 @@ public static int startWda(String udId) throws IOException, InterruptedException
}
}
int port = PortTool.getPort();
- Process wdaProcess;
+ Process wdaProcess = null;
String commandLine = "tidevice -u " + udId +
" wdaproxy" + " -B " + bundleId +
" --port " + port;
- if (System.getProperty("os.name").contains("Mac")) {
- wdaProcess = Runtime.getRuntime().exec(new String[]{"/bin/sh", "-c", commandLine});
- } else {
- wdaProcess = Runtime.getRuntime().exec(new String[]{"cmd", "/C", commandLine});
+ String system = System.getProperty("os.name").toLowerCase();
+ if (system.contains("win")) {
+ wdaProcess = Runtime.getRuntime().exec(new String[]{"cmd", "/c", commandLine});
+ } else if (system.contains("linux") || system.contains("mac")) {
+ wdaProcess = Runtime.getRuntime().exec(new String[]{"sh", "-c", commandLine});
+ }
+ BufferedReader stdInput = new BufferedReader(new
+ InputStreamReader(wdaProcess.getInputStream()));
+ String s;
+ while (wdaProcess.isAlive()) {
+ if ((s = stdInput.readLine()) != null) {
+ if (s.contains("WebDriverAgent start successfully")) {
+ break;
+ }
+ } else {
+ Thread.sleep(500);
+ }
+ logger.info(s);
}
-// BufferedReader stdInput = new BufferedReader(new
-// InputStreamReader(wdaProcess.getInputStream()));
-// String s;
-// while ((s = stdInput.readLine()) != null) {
-// if (s.contains("WebDriverAgent start successfully")) {
-// logger.info(udId + " wda启动完毕!");
-// break;
-// } else {
-// Thread.sleep(500);
-// }
-// }
- Thread.sleep(3000);
processList = new ArrayList<>();
processList.add(wdaProcess);
IOSProcessMap.getMap().put(udId, processList);
@@ -186,15 +190,16 @@ public static int startWda(String udId) throws IOException, InterruptedException
}
}
- public static int relayImg(String udId) throws IOException {
+ public static int relayImg(String udId) throws IOException, InterruptedException {
int port = PortTool.getPort();
- Process relayProcess;
+ Process relayProcess = null;
String commandLine = "tidevice -u " + udId +
" relay " + port + " " + 9100;
- if (System.getProperty("os.name").contains("Mac")) {
- relayProcess = Runtime.getRuntime().exec(new String[]{"/bin/sh", "-c", commandLine});
- } else {
- relayProcess = Runtime.getRuntime().exec(new String[]{"cmd", "/C", commandLine});
+ String system = System.getProperty("os.name").toLowerCase();
+ if (system.contains("win")) {
+ relayProcess = Runtime.getRuntime().exec(new String[]{"cmd", "/c", commandLine});
+ } else if (system.contains("linux") || system.contains("mac")) {
+ relayProcess = Runtime.getRuntime().exec(new String[]{"sh", "-c", commandLine});
}
List processList;
if (IOSProcessMap.getMap().get(udId) != null) {
@@ -204,6 +209,7 @@ public static int relayImg(String udId) throws IOException {
}
processList.add(relayProcess);
IOSProcessMap.getMap().put(udId, processList);
+ Thread.sleep(1000);
return port;
}
diff --git a/src/main/java/com/sonic/agent/maps/DevicesLockMap.java b/src/main/java/com/sonic/agent/maps/DevicesLockMap.java
new file mode 100644
index 00000000..6b1de9bb
--- /dev/null
+++ b/src/main/java/com/sonic/agent/maps/DevicesLockMap.java
@@ -0,0 +1,102 @@
+package com.sonic.agent.maps;
+
+import org.slf4j.Logger;
+import org.slf4j.LoggerFactory;
+import org.springframework.util.Assert;
+
+import java.util.Map;
+import java.util.concurrent.ConcurrentHashMap;
+import java.util.concurrent.Semaphore;
+import java.util.concurrent.TimeUnit;
+
+/**
+ * 设备逻辑锁 Map
+ *
+ * @author JayWenStar
+ * @date 2021/12/19 19:51 下午
+ */
+public class DevicesLockMap {
+
+ private final static Logger logger = LoggerFactory.getLogger(DevicesLockMap.class);
+
+ /**
+ * key: udId(设备序列号) value: Semaphore
+ */
+ private static Map devicesLockMap = new ConcurrentHashMap<>();
+
+ /**xxx
+ * 用设备序列号上锁(无)
+ *
+ * @see #lockByUdId(String, Long, TimeUnit)
+ */
+ public static boolean lockByUdId(String udId) throws InterruptedException {
+ return lockByUdId(udId, null, null);
+ }
+
+ /**
+ * 用设备序列号上锁(可设置超时)
+ *
+ * @param udId 设备序列号
+ * @param timeOut 获取锁超时时间 此参数必须和 timeUnit 同时存在
+ * @param timeUnit 时间单位 此参数必须和 timeOut 同时存在
+ * @return true: 上锁成功 false: 上锁失败
+ */
+ public static boolean lockByUdId(String udId, Long timeOut, TimeUnit timeUnit) throws InterruptedException {
+ // 校验参数
+ Assert.hasText(udId, "udId must not be blank");
+ if (timeOut != null || timeUnit != null) {
+ Assert.isTrue(
+ timeOut != null && timeUnit != null,
+ "timeOut and timeUnit must not be null at the same time"
+ );
+ }
+ Semaphore deviceLock;
+
+ // 原子操作,避免put后被别的线程remove造成当前线程lock失效
+ synchronized (WebSocketSessionMap.class) {
+ Semaphore res = devicesLockMap.get(udId);
+ if (res == null) {
+ deviceLock = new Semaphore(1);
+ devicesLockMap.put(udId, deviceLock);
+ } else {
+ deviceLock = res;
+ }
+ }
+
+ // 无超时获取锁逻辑,直接锁并返回true
+ if (timeOut == null) {
+ deviceLock.acquire();
+ return true;
+ }
+
+ // 如果有超时,则成功返回true,超时返回false
+ try {
+ return deviceLock.tryAcquire(timeOut, timeUnit);
+ } catch (InterruptedException e) {
+ logger.error("获取锁被中断,返回false");
+ return false;
+ }
+ }
+
+ /**
+ * 解锁并从map中移除锁
+ *
+ * @param udId 设备序列号
+ */
+ public static void unlockAndRemoveByUdId(String udId) {
+ Assert.hasText(udId, "udId must not be blank");
+ Semaphore deviceLock;
+ synchronized (WebSocketSessionMap.class) {
+ deviceLock = devicesLockMap.get(udId);
+ removeLockByUdId(udId);
+ }
+ if (deviceLock != null) {
+ deviceLock.release();
+ }
+ }
+
+ public static void removeLockByUdId(String udId) {
+ devicesLockMap.remove(udId);
+ }
+
+}
diff --git a/src/main/java/com/sonic/agent/maps/IOSSizeMap.java b/src/main/java/com/sonic/agent/maps/IOSSizeMap.java
new file mode 100644
index 00000000..1a89d365
--- /dev/null
+++ b/src/main/java/com/sonic/agent/maps/IOSSizeMap.java
@@ -0,0 +1,11 @@
+package com.sonic.agent.maps;
+
+import java.util.Map;
+import java.util.concurrent.ConcurrentHashMap;
+
+public class IOSSizeMap {
+ private static Map sizeMap = new ConcurrentHashMap();
+ public static Map getMap() {
+ return sizeMap;
+ }
+}
diff --git a/src/main/java/com/sonic/agent/maps/WebSocketSessionMap.java b/src/main/java/com/sonic/agent/maps/WebSocketSessionMap.java
index 78b3a154..a9078f75 100644
--- a/src/main/java/com/sonic/agent/maps/WebSocketSessionMap.java
+++ b/src/main/java/com/sonic/agent/maps/WebSocketSessionMap.java
@@ -1,17 +1,45 @@
-package com.sonic.agent.maps;
-
-import javax.websocket.Session;
-import java.util.Map;
-import java.util.concurrent.ConcurrentHashMap;
-
-/**
- * @author ZhouYiXun
- * @des webSocket的sessionId与session储存
- * @date 2021/8/16 19:54
- */
-public class WebSocketSessionMap {
- private static Map sessionMap = new ConcurrentHashMap();
- public static Map getMap() {
- return sessionMap;
- }
-}
+package com.sonic.agent.maps;
+
+import org.springframework.lang.NonNull;
+import org.springframework.util.Assert;
+
+import javax.websocket.Session;
+import java.util.Map;
+import java.util.concurrent.ConcurrentHashMap;
+
+/**
+ * @author ZhouYiXun
+ * @des webSocket的sessionId与session储存
+ * @date 2021/8/16 19:54
+ */
+public class WebSocketSessionMap {
+
+ /**
+ * key: sessionId value: session
+ */
+ private static Map sessionMap = new ConcurrentHashMap<>();
+
+
+ public static Map getSessionMap() {
+ return sessionMap;
+ }
+
+ public static void addSession(@NonNull Session session) {
+ sessionMap.put(session.getId(), session);
+ }
+
+ public static void removeSession(@NonNull Session session) {
+ removeSession(session.getId());
+ }
+
+ public static void removeSession(String sessionId) {
+ Assert.hasText(sessionId, "sessionId must not be blank");
+ sessionMap.remove(sessionId);
+ }
+
+ public static Session getSession(String sessionId) {
+ Assert.hasText(sessionId, "sessionId must not be blank");
+ return sessionMap.get(sessionId);
+ }
+
+}
diff --git a/src/main/java/com/sonic/agent/netty/NettyClient.java b/src/main/java/com/sonic/agent/netty/NettyClient.java
index f168e79b..2e216124 100644
--- a/src/main/java/com/sonic/agent/netty/NettyClient.java
+++ b/src/main/java/com/sonic/agent/netty/NettyClient.java
@@ -5,6 +5,7 @@
import io.netty.channel.nio.NioEventLoopGroup;
import io.netty.channel.socket.SocketChannel;
import io.netty.channel.socket.nio.NioSocketChannel;
+import io.netty.util.concurrent.DefaultThreadFactory;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.beans.factory.annotation.Value;
@@ -35,7 +36,9 @@ public class NettyClient implements ApplicationRunner {
@Override
public void run(ApplicationArguments args) {
- group = new NioEventLoopGroup();
+ // todo 根据 https://netty.io/wiki/native-transports.html 可针对不同系统进行优化,server端的netty同理
+ DefaultThreadFactory factory = new DefaultThreadFactory("AgentNettyClient", true);
+ group = new NioEventLoopGroup(factory);
bootstrap = new Bootstrap()
.group(group)
.option(ChannelOption.TCP_NODELAY, true)
diff --git a/src/main/java/com/sonic/agent/netty/NettyClientHandler.java b/src/main/java/com/sonic/agent/netty/NettyClientHandler.java
index f91fcc02..5d483705 100644
--- a/src/main/java/com/sonic/agent/netty/NettyClientHandler.java
+++ b/src/main/java/com/sonic/agent/netty/NettyClientHandler.java
@@ -5,6 +5,7 @@
import com.alibaba.fastjson.JSONObject;
import com.android.ddmlib.IDevice;
import com.sonic.agent.automation.AndroidStepHandler;
+import com.sonic.agent.automation.IOSStepHandler;
import com.sonic.agent.bridge.android.AndroidDeviceBridgeTool;
import com.sonic.agent.bridge.ios.TIDeviceTool;
import com.sonic.agent.interfaces.DeviceStatus;
@@ -13,6 +14,7 @@
import com.sonic.agent.maps.AndroidPasswordMap;
import com.sonic.agent.maps.HandlerMap;
import com.sonic.agent.tests.AndroidTests;
+import com.sonic.agent.tests.IOSTests;
import com.sonic.agent.tests.TaskManager;
import com.sonic.agent.tools.SpringTool;
import io.netty.channel.Channel;
@@ -87,6 +89,20 @@ public void channelRead(ChannelHandlerContext ctx, Object msg) throws Exception
}
androidStepHandler.sendStatus();
}
+ if (jsonObject.getInteger("pf") == PlatformType.IOS) {
+ IOSStepHandler iosStepHandler = HandlerMap.getIOSMap().get(jsonObject.getString("sessionId"));
+ iosStepHandler.resetResultDetailStatus();
+ iosStepHandler.setGlobalParams(jsonObject.getJSONObject("gp"));
+ List steps = jsonObject.getJSONArray("steps").toJavaList(JSONObject.class);
+ for (JSONObject step : steps) {
+ try {
+ iosStepHandler.runStep(step);
+ } catch (Throwable e) {
+ break;
+ }
+ }
+ iosStepHandler.sendStatus();
+ }
break;
case "suite":
List cases = jsonObject.getJSONArray("cases").toJavaList(JSONObject.class);
@@ -99,7 +115,12 @@ public void channelRead(ChannelHandlerContext ctx, Object msg) throws Exception
parameters.put("dataInfo", dataInfo.toJSONString());
xmlTest.setParameters(parameters);
List classes = new ArrayList<>();
- classes.add(new XmlClass(AndroidTests.class));
+ if (jsonObject.getInteger("pf") == PlatformType.ANDROID) {
+ classes.add(new XmlClass(AndroidTests.class));
+ }
+ if (jsonObject.getInteger("pf") == PlatformType.IOS) {
+ classes.add(new XmlClass(IOSTests.class));
+ }
xmlTest.setXmlClasses(classes);
}
suiteList.add(xmlSuite);
@@ -115,7 +136,7 @@ public void channelRead(ChannelHandlerContext ctx, Object msg) throws Exception
List deviceList = devices.toJavaList(JSONObject.class);
for (JSONObject device : deviceList) {
String udId = (String) device.get("udId");
- TaskManager.forceStopSuite(resultId, caseId, udId);
+ TaskManager.forceStopSuite(jsonObject.getInteger("pf"), resultId, caseId, udId);
}
}
break;
@@ -126,7 +147,6 @@ public void channelRead(ChannelHandlerContext ctx, Object msg) throws Exception
@Override
public void exceptionCaught(ChannelHandlerContext ctx, Throwable cause) throws Exception {
logger.info("服务器: {} 发生异常 {}", ctx.channel().remoteAddress(), cause.fillInStackTrace());
- ctx.close();
}
@Override
diff --git a/src/main/java/com/sonic/agent/tests/IOSTests.java b/src/main/java/com/sonic/agent/tests/IOSTests.java
index 59cf3b8c..01415c56 100644
--- a/src/main/java/com/sonic/agent/tests/IOSTests.java
+++ b/src/main/java/com/sonic/agent/tests/IOSTests.java
@@ -1,9 +1,65 @@
package com.sonic.agent.tests;
+import com.alibaba.fastjson.JSON;
+import com.alibaba.fastjson.JSONObject;
+import com.sonic.agent.automation.IOSStepHandler;
+import com.sonic.agent.bridge.ios.TIDeviceTool;
+import com.sonic.agent.interfaces.DeviceStatus;
+import com.sonic.agent.tests.ios.IOSTestTaskBootThread;
+import org.slf4j.Logger;
+import org.slf4j.LoggerFactory;
+import org.testng.ITestContext;
+import org.testng.annotations.DataProvider;
+import org.testng.annotations.Test;
+
+import java.io.IOException;
+import java.util.ArrayList;
+import java.util.List;
+
/**
* @author ZhouYiXun
* @des iOS测试执行类
* @date 2021/8/25 20:51
*/
public class IOSTests {
+ private final Logger logger = LoggerFactory.getLogger(IOSTests.class);
+
+ @DataProvider(name = "testData", parallel = true)
+ public Object[][] getTestData(ITestContext context) {
+ JSONObject dataInfo = JSON.parseObject(context.getCurrentXmlTest().getParameter("dataInfo"));
+ List dataProvider = new ArrayList<>();
+ for (JSONObject device : dataInfo.getJSONArray("device").toJavaList(JSONObject.class)) {
+ String udId = device.getString("udId");
+ if (!TIDeviceTool.getDeviceList().contains(udId)) {
+ continue;
+ }
+ JSONObject deviceTestData = new JSONObject();
+ deviceTestData.put("steps", dataInfo.getJSONArray("steps"));
+ deviceTestData.put("rid", dataInfo.getInteger("rid"));
+ deviceTestData.put("cid", dataInfo.getInteger("cid"));
+ deviceTestData.put("gp", dataInfo.getJSONObject("gp"));
+ deviceTestData.put("device", device);
+ dataProvider.add(deviceTestData);
+ }
+ Object[][] testDataProvider = new Object[dataProvider.size()][];
+ for (int i = 0; i < dataProvider.size(); i++) {
+ testDataProvider[i] = new Object[]{dataProvider.get(i)};
+ }
+ return testDataProvider;
+ }
+
+ @Test(dataProvider = "testData")
+ public void run(JSONObject jsonObject) throws IOException {
+ IOSStepHandler iosStepHandler = new IOSStepHandler();
+ int rid = jsonObject.getInteger("rid");
+ int cid = jsonObject.getInteger("cid");
+ String udId = jsonObject.getJSONObject("device").getString("udId");
+ JSONObject gp = jsonObject.getJSONObject("gp");
+ iosStepHandler.setGlobalParams(gp);
+ iosStepHandler.setTestMode(cid, rid, udId, DeviceStatus.TESTING, "");
+
+ // 启动任务
+ IOSTestTaskBootThread bootThread = new IOSTestTaskBootThread(jsonObject, iosStepHandler);
+ TaskManager.startBootThread(bootThread);
+ }
}
diff --git a/src/main/java/com/sonic/agent/tests/TaskManager.java b/src/main/java/com/sonic/agent/tests/TaskManager.java
index 135141c8..987a02b3 100644
--- a/src/main/java/com/sonic/agent/tests/TaskManager.java
+++ b/src/main/java/com/sonic/agent/tests/TaskManager.java
@@ -1,6 +1,8 @@
package com.sonic.agent.tests;
+import com.sonic.agent.interfaces.PlatformType;
import com.sonic.agent.tests.android.AndroidTestTaskBootThread;
+import com.sonic.agent.tests.ios.IOSTestTaskBootThread;
import org.springframework.util.CollectionUtils;
import java.util.HashSet;
@@ -45,7 +47,7 @@ public static void startBootThread(Thread bootThread) {
*
* @param bootThreads boot线程
*/
- public static void startBootThread(Thread...bootThreads) {
+ public static void startBootThread(Thread... bootThreads) {
for (Thread bootThread : bootThreads) {
startBootThread(bootThread);
}
@@ -62,7 +64,7 @@ public static void startChildThread(String key, Thread childThread) {
/**
* 启动子线程(批量)
*/
- public static void startChildThread(String key, Thread...childThreads) {
+ public static void startChildThread(String key, Thread... childThreads) {
for (Thread childThread : childThreads) {
startChildThread(key, childThread);
}
@@ -72,8 +74,8 @@ public static void startChildThread(String key, Thread...childThreads) {
/**
* 添加boot线程
*
- * @param key 用boot线程名作为key
- * @param bootThread boot线程
+ * @param key 用boot线程名作为key
+ * @param bootThread boot线程
*/
public static void addBootThread(String key, Thread bootThread) {
clearTerminatedThread();
@@ -153,12 +155,20 @@ public static void clearTerminatedThread() {
}
/**
- * 按照设备序列号强制停止手机正在执行的任务
+ * 按照结果id、用例id、设备序列号强制停止手机正在执行的任务
*
- * @param udid 设备序列号
+ * @param resultId 结果id
+ * @param caseId 用例id
+ * @param udId 设备序列号
*/
- public static void forceStopSuite(int resultId, int caseId, String udid) {
- String key = String.format(AndroidTestTaskBootThread.ANDROID_TEST_TASK_BOOT_PRE, resultId, caseId, udid);
+ public static void forceStopSuite(int platform, int resultId, int caseId, String udId) {
+ String key = "";
+ if (platform == PlatformType.ANDROID) {
+ key = String.format(AndroidTestTaskBootThread.ANDROID_TEST_TASK_BOOT_PRE, resultId, caseId, udId);
+ }
+ if (platform == PlatformType.IOS) {
+ key = String.format(IOSTestTaskBootThread.IOS_TEST_TASK_BOOT_PRE, resultId, caseId, udId);
+ }
// 停止boot线程
Thread bootThread = bootThreadsMap.get(key);
if (bootThread != null) {
diff --git a/src/main/java/com/sonic/agent/tests/android/AndroidRecordThread.java b/src/main/java/com/sonic/agent/tests/android/AndroidRecordThread.java
index 5cd33da6..3eae38bb 100644
--- a/src/main/java/com/sonic/agent/tests/android/AndroidRecordThread.java
+++ b/src/main/java/com/sonic/agent/tests/android/AndroidRecordThread.java
@@ -90,7 +90,7 @@ public void run() {
//处理录像
if (isSupportRecord) {
if (androidStepHandler.getStatus() == 3) {
- androidStepHandler.stopRecord(udId);
+ androidStepHandler.stopRecord();
return;
} else {
androidStepHandler.getAndroidDriver().stopRecordingScreen();
diff --git a/src/main/java/com/sonic/agent/tests/android/AndroidTemperThread.java b/src/main/java/com/sonic/agent/tests/android/AndroidTemperThread.java
new file mode 100644
index 00000000..4ed375a7
--- /dev/null
+++ b/src/main/java/com/sonic/agent/tests/android/AndroidTemperThread.java
@@ -0,0 +1,40 @@
+package com.sonic.agent.tests.android;
+
+import com.alibaba.fastjson.JSONObject;
+import com.android.ddmlib.IDevice;
+import com.sonic.agent.bridge.android.AndroidDeviceBridgeTool;
+import com.sonic.agent.netty.NettyThreadPool;
+
+import java.util.ArrayList;
+import java.util.List;
+
+public class AndroidTemperThread extends Thread {
+ @Override
+ public void run() {
+ while (AndroidDeviceBridgeTool.androidDebugBridge != null) {
+ IDevice[] deviceList = AndroidDeviceBridgeTool.getRealOnLineDevices();
+ List detail = new ArrayList<>();
+ for (IDevice iDevice : deviceList) {
+ JSONObject jsonObject = new JSONObject();
+ String temper = AndroidDeviceBridgeTool
+ .executeCommand(iDevice, "dumpsys battery");
+ if (temper != null && temper.length() > 0) {
+ String real = temper.substring(temper.indexOf("temperature")).trim();
+ int total = Integer.parseInt(real.substring(13, real.indexOf("\n")));
+ jsonObject.put("udId", iDevice.getSerialNumber());
+ jsonObject.put("tem", total);
+ detail.add(jsonObject);
+ }
+ }
+ JSONObject result = new JSONObject();
+ result.put("msg", "temperature");
+ result.put("detail", detail);
+ NettyThreadPool.send(result);
+ try {
+ Thread.sleep(30000);
+ } catch (InterruptedException e) {
+ e.printStackTrace();
+ }
+ }
+ }
+}
diff --git a/src/main/java/com/sonic/agent/tests/android/AndroidTestTaskBootThread.java b/src/main/java/com/sonic/agent/tests/android/AndroidTestTaskBootThread.java
index 1d9cf8fb..f2f3d804 100644
--- a/src/main/java/com/sonic/agent/tests/android/AndroidTestTaskBootThread.java
+++ b/src/main/java/com/sonic/agent/tests/android/AndroidTestTaskBootThread.java
@@ -144,6 +144,9 @@ public AndroidTestTaskBootThread setUdId(String udId) {
@Override
public void run() {
+
+ boolean startTestSuccess = false;
+
try {
int wait = 0;
while (!AndroidDeviceLocalStatus.startTest(udId)) {
@@ -158,6 +161,7 @@ public void run() {
}
}
+ startTestSuccess = true;
//启动测试
try {
androidStepHandler.startAndroidDriver(udId);
@@ -194,8 +198,10 @@ public void run() {
log.error("任务异常,中断:{}", e.getMessage());
androidStepHandler.setResultDetailStatus(ResultDetailStatus.FAIL);
} finally {
- AndroidDeviceLocalStatus.finish(udId);
- androidStepHandler.closeAndroidDriver();
+ if (startTestSuccess) {
+ AndroidDeviceLocalStatus.finish(udId);
+ androidStepHandler.closeAndroidDriver();
+ }
androidStepHandler.sendStatus();
}
}
diff --git a/src/main/java/com/sonic/agent/tests/android/OutputSocketThread.java b/src/main/java/com/sonic/agent/tests/android/OutputSocketThread.java
index bed56806..c3359ccc 100644
--- a/src/main/java/com/sonic/agent/tests/android/OutputSocketThread.java
+++ b/src/main/java/com/sonic/agent/tests/android/OutputSocketThread.java
@@ -70,6 +70,9 @@ public void run() {
byte[] oldBytes = new byte[0];
int count = 0;
while (sendImg.isAlive()) {
+ if (session == null || !session.isOpen()) {
+ return;
+ }
Queue dataQueue = sendImg.getDataQueue();
if (dataQueue.isEmpty()) {
continue;
diff --git a/src/main/java/com/sonic/agent/tests/ios/IOSPerfDataThread.java b/src/main/java/com/sonic/agent/tests/ios/IOSPerfDataThread.java
new file mode 100644
index 00000000..b3993396
--- /dev/null
+++ b/src/main/java/com/sonic/agent/tests/ios/IOSPerfDataThread.java
@@ -0,0 +1,35 @@
+package com.sonic.agent.tests.ios;
+
+import com.sonic.agent.automation.AndroidStepHandler;
+import com.sonic.agent.tests.android.AndroidRunStepThread;
+import com.sonic.agent.tests.android.AndroidTestTaskBootThread;
+import org.slf4j.Logger;
+import org.slf4j.LoggerFactory;
+
+/**
+ * 暂未开放
+ */
+public class IOSPerfDataThread extends Thread {
+
+ private final Logger log = LoggerFactory.getLogger(IOSPerfDataThread.class);
+
+ public final static String IOS_PERF_DATA_TASK_PRE = "ios-perf-data-task-%s-%s-%s";
+
+ private final IOSTestTaskBootThread iosTestTaskBootThread;
+
+ public IOSPerfDataThread(IOSTestTaskBootThread iosTestTaskBootThread) {
+ this.iosTestTaskBootThread = iosTestTaskBootThread;
+
+ this.setDaemon(true);
+ this.setName(iosTestTaskBootThread.formatThreadName(IOS_PERF_DATA_TASK_PRE));
+ }
+
+ public IOSTestTaskBootThread getIosTestTaskBootThread() {
+ return iosTestTaskBootThread;
+ }
+
+ @Override
+ public void run() {
+ return;
+ }
+}
diff --git a/src/main/java/com/sonic/agent/tests/ios/IOSRecordThread.java b/src/main/java/com/sonic/agent/tests/ios/IOSRecordThread.java
new file mode 100644
index 00000000..f6b97d2b
--- /dev/null
+++ b/src/main/java/com/sonic/agent/tests/ios/IOSRecordThread.java
@@ -0,0 +1,77 @@
+package com.sonic.agent.tests.ios;
+
+import com.android.ddmlib.IDevice;
+import com.sonic.agent.automation.AndroidStepHandler;
+import com.sonic.agent.automation.IOSStepHandler;
+import com.sonic.agent.bridge.android.AndroidDeviceBridgeTool;
+import com.sonic.agent.cv.RecordHandler;
+import com.sonic.agent.tests.android.AndroidRunStepThread;
+import com.sonic.agent.tests.android.AndroidTestTaskBootThread;
+import com.sonic.agent.tools.MiniCapTool;
+import org.bytedeco.javacv.FrameRecorder;
+import org.slf4j.Logger;
+import org.slf4j.LoggerFactory;
+
+import java.io.File;
+import java.util.ArrayList;
+import java.util.Calendar;
+import java.util.List;
+import java.util.concurrent.atomic.AtomicReference;
+
+public class IOSRecordThread extends Thread {
+
+ private final Logger log = LoggerFactory.getLogger(IOSRecordThread.class);
+
+ public final static String IOS_RECORD_TASK_PRE = "ios-record-task-%s-%s-%s";
+
+ private final IOSTestTaskBootThread iosTestTaskBootThread;
+
+ public IOSRecordThread(IOSTestTaskBootThread iosTestTaskBootThread) {
+ this.iosTestTaskBootThread = iosTestTaskBootThread;
+
+ this.setDaemon(true);
+ this.setName(iosTestTaskBootThread.formatThreadName(IOS_RECORD_TASK_PRE));
+ }
+
+ public IOSTestTaskBootThread getIosTestTaskBootThread() {
+ return iosTestTaskBootThread;
+ }
+
+ @Override
+ public void run() {
+ IOSStepHandler iosStepHandler = iosTestTaskBootThread.getIosStepHandler();
+ IOSRunStepThread runStepThread = iosTestTaskBootThread.getRunStepThread();
+
+ while (runStepThread.isAlive()) {
+ if (iosStepHandler.getDriver() == null) {
+ try {
+ Thread.sleep(500);
+ } catch (InterruptedException e) {
+ log.error(e.getMessage());
+ }
+ continue;
+ }
+ try {
+ iosStepHandler.startRecord();
+ } catch (Exception e) {
+ log.error(e.getMessage());
+ }
+ int w = 0;
+ while (w < 10 && (runStepThread.isAlive())) {
+ try {
+ Thread.sleep(10000);
+ } catch (InterruptedException e) {
+ log.error(e.getMessage());
+ }
+ w++;
+ }
+ //处理录像
+ if (iosStepHandler.getStatus() == 3) {
+ iosStepHandler.stopRecord();
+ return;
+ } else {
+ iosStepHandler.getDriver().stopRecordingScreen();
+ }
+ }
+ }
+}
diff --git a/src/main/java/com/sonic/agent/tests/ios/IOSRunStepThread.java b/src/main/java/com/sonic/agent/tests/ios/IOSRunStepThread.java
new file mode 100644
index 00000000..976563f1
--- /dev/null
+++ b/src/main/java/com/sonic/agent/tests/ios/IOSRunStepThread.java
@@ -0,0 +1,46 @@
+package com.sonic.agent.tests.ios;
+
+import com.alibaba.fastjson.JSONObject;
+import com.sonic.agent.automation.AndroidStepHandler;
+import com.sonic.agent.automation.IOSStepHandler;
+import com.sonic.agent.tests.android.AndroidTestTaskBootThread;
+import org.slf4j.Logger;
+import org.slf4j.LoggerFactory;
+
+import java.util.List;
+
+public class IOSRunStepThread extends Thread {
+
+ private final Logger log = LoggerFactory.getLogger(IOSRunStepThread.class);
+
+ public final static String IOS_RUN_STEP_TASK_PRE = "ios-run-step-task-%s-%s-%s";
+
+ private final IOSTestTaskBootThread iosTestTaskBootThread;
+
+ public IOSRunStepThread(IOSTestTaskBootThread iosTestTaskBootThread) {
+ this.iosTestTaskBootThread = iosTestTaskBootThread;
+
+ this.setDaemon(true);
+ this.setName(iosTestTaskBootThread.formatThreadName(IOS_RUN_STEP_TASK_PRE));
+ }
+
+ public IOSTestTaskBootThread getIosTestTaskBootThread() {
+ return iosTestTaskBootThread;
+ }
+
+ @Override
+ public void run() {
+ JSONObject jsonObject = iosTestTaskBootThread.getJsonObject();
+ IOSStepHandler iosStepHandler = iosTestTaskBootThread.getIosStepHandler();
+ List steps = jsonObject.getJSONArray("steps").toJavaList(JSONObject.class);
+
+ for (JSONObject step : steps) {
+ try {
+ iosStepHandler.runStep(step);
+ } catch (Throwable e) {
+ break;
+ }
+ }
+
+ }
+}
\ No newline at end of file
diff --git a/src/main/java/com/sonic/agent/tests/ios/IOSTestTaskBootThread.java b/src/main/java/com/sonic/agent/tests/ios/IOSTestTaskBootThread.java
new file mode 100644
index 00000000..5861e17e
--- /dev/null
+++ b/src/main/java/com/sonic/agent/tests/ios/IOSTestTaskBootThread.java
@@ -0,0 +1,201 @@
+package com.sonic.agent.tests.ios;
+
+import com.alibaba.fastjson.JSONObject;
+import com.sonic.agent.automation.IOSStepHandler;
+import com.sonic.agent.bridge.ios.IOSDeviceLocalStatus;
+import com.sonic.agent.bridge.ios.TIDeviceTool;
+import com.sonic.agent.interfaces.ResultDetailStatus;
+import com.sonic.agent.tests.TaskManager;
+import org.slf4j.Logger;
+import org.slf4j.LoggerFactory;
+
+import java.util.concurrent.Semaphore;
+
+public class IOSTestTaskBootThread extends Thread {
+
+ private final Logger log = LoggerFactory.getLogger(IOSTestTaskBootThread.class);
+
+ /**
+ * ios-test-task-boot-{resultId}-{caseId}-{udid}
+ */
+ public final static String IOS_TEST_TASK_BOOT_PRE = "ios-test-task-boot-%s-%s-%s";
+
+ /**
+ * 控制不同线程执行的信号量
+ */
+ private Semaphore runStepSemaphore = new Semaphore(1);
+
+
+ /**
+ * 一些任务信息
+ */
+ private JSONObject jsonObject;
+
+ private IOSStepHandler iosStepHandler;
+
+ /**
+ * 测试步骤线程
+ */
+ private IOSRunStepThread runStepThread;
+
+ /**
+ * 性能数据采集线程
+ */
+ private IOSPerfDataThread perfDataThread;
+
+ /**
+ * 录像线程
+ */
+ private IOSRecordThread recordThread;
+
+ /**
+ * 测试结果id 0表示debug线程
+ */
+ private int resultId = 0;
+
+ /**
+ * 测试用例id 0表示debug线程
+ */
+ private int caseId = 0;
+
+ /**
+ * 设备序列号
+ */
+ private String udId;
+
+ public String formatThreadName(String baseFormat) {
+ return String.format(baseFormat, this.resultId, this.caseId, this.udId);
+ }
+
+ /**
+ * debug线程构造
+ */
+ public IOSTestTaskBootThread() {
+ this.setName(this.formatThreadName(IOS_TEST_TASK_BOOT_PRE));
+ this.setDaemon(true);
+ }
+
+ /**
+ * 任务线程构造
+ *
+ * @param jsonObject 任务数据
+ * @param iosStepHandler ios步骤执行器
+ */
+ public IOSTestTaskBootThread(JSONObject jsonObject, IOSStepHandler iosStepHandler) {
+ this.iosStepHandler = iosStepHandler;
+ this.jsonObject = jsonObject;
+ this.resultId = jsonObject.getInteger("rid");
+ this.caseId = jsonObject.getInteger("cid");
+ this.udId = jsonObject.getJSONObject("device").getString("udId");
+
+ // 比如:test-task-thread-af80d1e4
+ this.setName(String.format(IOS_TEST_TASK_BOOT_PRE, resultId, caseId, udId));
+ this.setDaemon(true);
+ }
+
+ public Semaphore getRunStepSemaphore() {
+ return runStepSemaphore;
+ }
+
+ public JSONObject getJsonObject() {
+ return jsonObject;
+ }
+
+ public IOSStepHandler getIosStepHandler() {
+ return iosStepHandler;
+ }
+
+ public IOSRunStepThread getRunStepThread() {
+ return runStepThread;
+ }
+
+ public IOSPerfDataThread getPerfDataThread() {
+ return perfDataThread;
+ }
+
+ public IOSRecordThread getRecordThread() {
+ return recordThread;
+ }
+
+ public int getResultId() {
+ return resultId;
+ }
+
+ public int getCaseId() {
+ return caseId;
+ }
+
+ public String getUdId() {
+ return udId;
+ }
+
+ public IOSTestTaskBootThread setUdId(String udId) {
+ this.udId = udId;
+ return this;
+ }
+
+ @Override
+ public void run() {
+
+ boolean startTestSuccess = false;
+
+ try {
+ int wait = 0;
+ while (!IOSDeviceLocalStatus.startTest(udId)) {
+ wait++;
+ iosStepHandler.waitDevice(wait);
+ if (wait >= 6 * 10) {
+ iosStepHandler.waitDeviceTimeOut();
+ iosStepHandler.sendStatus();
+ return;
+ } else {
+ Thread.sleep(10000);
+ }
+ }
+
+ startTestSuccess = true;
+ //启动测试
+ try {
+ int wdaPort = TIDeviceTool.startWda(udId);
+ iosStepHandler.startIOSDriver(udId, wdaPort);
+ } catch (Exception e) {
+ log.error(e.getMessage());
+ iosStepHandler.closeIOSDriver();
+ iosStepHandler.sendStatus();
+ IOSDeviceLocalStatus.finishError(udId);
+ return;
+ }
+
+ //电量过低退出测试
+ if (iosStepHandler.getBattery()) {
+ iosStepHandler.closeIOSDriver();
+ iosStepHandler.sendStatus();
+ IOSDeviceLocalStatus.finish(udId);
+ return;
+ }
+
+ //正常运行步骤的线程
+ runStepThread = new IOSRunStepThread(this);
+ //性能数据获取线程
+ perfDataThread = new IOSPerfDataThread(this);
+ //录像线程
+ recordThread = new IOSRecordThread(this);
+ TaskManager.startChildThread(this.getName(), runStepThread, perfDataThread, recordThread);
+
+
+ //等待两个线程结束了才结束方法
+ while ((recordThread.isAlive()) || (runStepThread.isAlive())) {
+ Thread.sleep(1000);
+ }
+ } catch (InterruptedException e) {
+ log.error("任务异常,中断:{}", e.getMessage());
+ iosStepHandler.setResultDetailStatus(ResultDetailStatus.FAIL);
+ } finally {
+ if (startTestSuccess) {
+ IOSDeviceLocalStatus.finish(udId);
+ iosStepHandler.closeIOSDriver();
+ }
+ iosStepHandler.sendStatus();
+ }
+ }
+}
diff --git a/src/main/java/com/sonic/agent/tools/LaunchTool.java b/src/main/java/com/sonic/agent/tools/LaunchTool.java
index 3d88647d..35531291 100644
--- a/src/main/java/com/sonic/agent/tools/LaunchTool.java
+++ b/src/main/java/com/sonic/agent/tools/LaunchTool.java
@@ -11,6 +11,7 @@
import org.springframework.stereotype.Component;
import javax.annotation.PreDestroy;
+import java.io.File;
@Component
@DependsOn("nettyMsgInit")
@@ -19,6 +20,10 @@ public class LaunchTool implements ApplicationRunner {
@Override
public void run(ApplicationArguments args) {
+ File testFile = new File("test-output");
+ if (!testFile.exists()) {
+ testFile.mkdirs();
+ }
AppiumServer.start();
}
diff --git a/src/main/java/com/sonic/agent/tools/LogTool.java b/src/main/java/com/sonic/agent/tools/LogTool.java
index db9fddbb..577f58f1 100644
--- a/src/main/java/com/sonic/agent/tools/LogTool.java
+++ b/src/main/java/com/sonic/agent/tools/LogTool.java
@@ -39,7 +39,7 @@ public void send(JSONObject message) {
message.put("rid", resultId);
message.put("udId", udId);
if (type.equals(DeviceStatus.DEBUGGING)) {
- sendToWebSocket(WebSocketSessionMap.getMap().get(sessionId), message);
+ sendToWebSocket(WebSocketSessionMap.getSession(sessionId), message);
}
if (type.equals(DeviceStatus.TESTING)) {
sendToServer(message);
diff --git a/src/main/java/com/sonic/agent/tools/ProcessCommandTool.java b/src/main/java/com/sonic/agent/tools/ProcessCommandTool.java
index b2ffa451..c39ae265 100644
--- a/src/main/java/com/sonic/agent/tools/ProcessCommandTool.java
+++ b/src/main/java/com/sonic/agent/tools/ProcessCommandTool.java
@@ -17,10 +17,11 @@ public static List getProcessLocalCommand(String commandLine) {
List sdrResult = new ArrayList();
List sdrErrorResult = new ArrayList();
try {
- if (System.getProperty("os.name").contains("Mac")) {
- process = Runtime.getRuntime().exec(new String[]{"/bin/sh", "-c", commandLine});
+ String system = System.getProperty("os.name").toLowerCase();
+ if (system.contains("win")) {
+ process = Runtime.getRuntime().exec(new String[]{"cmd", "/c", commandLine});
} else {
- process = Runtime.getRuntime().exec(new String[]{"cmd", "/C", commandLine});
+ process = Runtime.getRuntime().exec(new String[]{"sh", "-c", commandLine});
}
inputStreamReader = new InputStreamReader(process.getInputStream());
consoleInput = new LineNumberReader(inputStreamReader);
diff --git a/src/main/java/com/sonic/agent/tools/ScrcpyTool.java b/src/main/java/com/sonic/agent/tools/ScrcpyTool.java
index 7836a335..84b07da5 100644
--- a/src/main/java/com/sonic/agent/tools/ScrcpyTool.java
+++ b/src/main/java/com/sonic/agent/tools/ScrcpyTool.java
@@ -17,7 +17,7 @@ public static void main(String[] args) throws IOException, InterruptedException
InputStream inputStream;
outputStream = capSocket.getOutputStream();
inputStream = capSocket.getInputStream();
-// outputStream.write(0);
+ outputStream.write(0);
int readLength;
int naluIndex = 0;
diff --git a/src/main/java/com/sonic/agent/websockets/AndroidWSServer.java b/src/main/java/com/sonic/agent/websockets/AndroidWSServer.java
index 8dc727af..1f6b531d 100644
--- a/src/main/java/com/sonic/agent/websockets/AndroidWSServer.java
+++ b/src/main/java/com/sonic/agent/websockets/AndroidWSServer.java
@@ -13,12 +13,12 @@
import com.sonic.agent.bridge.android.AndroidDeviceLocalStatus;
import com.sonic.agent.bridge.android.AndroidDeviceThreadPool;
import com.sonic.agent.interfaces.DeviceStatus;
-import com.sonic.agent.maps.GlobalProcessMap;
-import com.sonic.agent.maps.HandlerMap;
-import com.sonic.agent.maps.MiniCapMap;
-import com.sonic.agent.maps.WebSocketSessionMap;
+import com.sonic.agent.maps.*;
import com.sonic.agent.netty.NettyThreadPool;
-import com.sonic.agent.tools.*;
+import com.sonic.agent.tools.MiniCapTool;
+import com.sonic.agent.tools.PortTool;
+import com.sonic.agent.tools.ProcessCommandTool;
+import com.sonic.agent.tools.UploadTools;
import org.openqa.selenium.OutputType;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
@@ -45,6 +45,7 @@
@Component
@ServerEndpoint(value = "/websockets/android/{key}/{udId}/{token}", configurator = MyEndpointConfigure.class)
public class AndroidWSServer {
+
private final Logger logger = LoggerFactory.getLogger(AndroidWSServer.class);
@Value("${sonic.agent.key}")
private String key;
@@ -66,12 +67,21 @@ public void onOpen(Session session, @PathParam("key") String secretKey,
logger.info("拦截访问!");
return;
}
+
+ session.getUserProperties().put("udId", udId);
+ boolean lockSuccess = DevicesLockMap.lockByUdId(udId, 30L, TimeUnit.SECONDS);
+ if (!lockSuccess) {
+ logger.info("30s内获取设备锁失败,请确保设备无人使用");
+ return;
+ }
+ logger.info("android上锁udId:{}", udId);
+
JSONObject jsonDebug = new JSONObject();
jsonDebug.put("msg", "debugUser");
jsonDebug.put("token", token);
jsonDebug.put("udId", udId);
NettyThreadPool.send(jsonDebug);
- WebSocketSessionMap.getMap().put(session.getId(), session);
+ WebSocketSessionMap.addSession(session);
IDevice iDevice = AndroidDeviceBridgeTool.getIDeviceByUdId(udId);
if (iDevice == null) {
logger.info("设备未连接,请检查!");
@@ -202,7 +212,7 @@ public boolean isCancelled() {
e.printStackTrace();
}
// 超时就不继续等了,保证其它服务可运行
- if (wait > 6) {
+ if (wait > 20) {
return;
}
}
@@ -256,11 +266,11 @@ public boolean isCancelled() {
String system = System.getProperty("os.name").toLowerCase();
Process ps = null;
int port = PortTool.getPort();
+ String command = String.format("adbkit usb-device-to-tcp -p %d %s", port, udId);
if (system.contains("win")) {
- ps = Runtime.getRuntime().exec(String.format("cmd /c adbkit usb-device-to-tcp -p %d %s", port, udId));
- }
- if (system.contains("linux") || system.contains("mac")) {
- ps = Runtime.getRuntime().exec(String.format("sh -c adbkit usb-device-to-tcp -p %d %s", port, udId));
+ ps = Runtime.getRuntime().exec(new String[]{"cmd", "/c", command});
+ } else if (system.contains("linux") || system.contains("mac")) {
+ ps = Runtime.getRuntime().exec(new String[]{"sh", "-c", command});
}
GlobalProcessMap.getMap().put(processName, ps);
JSONObject adbkit = new JSONObject();
@@ -306,7 +316,13 @@ public boolean isCancelled() {
@OnClose
public void onClose(Session session) {
- exit(session);
+ String udId = (String) session.getUserProperties().get("udId");
+ try {
+ exit(session);
+ } finally {
+ DevicesLockMap.unlockAndRemoveByUdId(udId);
+ logger.info("android解锁udId:{}", udId);
+ }
}
@OnError
@@ -325,7 +341,7 @@ public void onMessage(String message, Session session) throws InterruptedExcepti
case "forwardView": {
JSONObject forwardView = new JSONObject();
IDevice iDevice = udIdMap.get(session);
- List wList = Arrays.asList("webview", "WebView");
+ List wList = Arrays.asList("webview", "WebView", "chrome_devtools_remote", "Terrace_devtools_remote");
List webViewList = new ArrayList<>();
for (String w : wList) {
webViewList.addAll(Arrays.asList(AndroidDeviceBridgeTool
@@ -398,6 +414,9 @@ public void onMessage(String message, Session session) throws InterruptedExcepti
case "find":
AndroidDeviceBridgeTool.searchDevice(udIdMap.get(session));
break;
+ case "battery":
+ AndroidDeviceBridgeTool.controlBattery(udIdMap.get(session), msg.getInteger("detail"));
+ break;
case "scan":
AndroidDeviceBridgeTool.pushToCamera(udIdMap.get(session), msg.getString("url"));
break;
@@ -445,92 +464,6 @@ public void onMessage(String message, Session session) throws InterruptedExcepti
AndroidDeviceBridgeTool.pressKey(udIdMap.get(session), msg.getInteger("detail"));
break;
case "debug":
- AndroidStepHandler androidStepHandler = HandlerMap.getAndroidMap().get(session.getId());
- try {
- if (msg.getString("detail").equals("tap")) {
- String xy = msg.getString("point");
- int x = Integer.parseInt(xy.substring(0, xy.indexOf(",")));
- int y = Integer.parseInt(xy.substring(xy.indexOf(",") + 1));
- AndroidDeviceBridgeTool.executeCommand(udIdMap.get(session), "input tap " + x + " " + y);
- }
- if (msg.getString("detail").equals("longPress")) {
- String xy = msg.getString("point");
- int x = Integer.parseInt(xy.substring(0, xy.indexOf(",")));
- int y = Integer.parseInt(xy.substring(xy.indexOf(",") + 1));
- AndroidDeviceBridgeTool.executeCommand(udIdMap.get(session), "input swipe " + x + " " + y + " " + x + " " + y + " 1500");
- }
- if (msg.getString("detail").equals("swipe")) {
- String xy1 = msg.getString("pointA");
- String xy2 = msg.getString("pointB");
- int x1 = Integer.parseInt(xy1.substring(0, xy1.indexOf(",")));
- int y1 = Integer.parseInt(xy1.substring(xy1.indexOf(",") + 1));
- int x2 = Integer.parseInt(xy2.substring(0, xy2.indexOf(",")));
- int y2 = Integer.parseInt(xy2.substring(xy2.indexOf(",") + 1));
- AndroidDeviceBridgeTool.executeCommand(udIdMap.get(session), "input swipe " + x1 + " " + y1 + " " + x2 + " " + y2 + " 200");
- }
- } catch (Exception e) {
- e.printStackTrace();
- }
- if (msg.getString("detail").equals("install")) {
- AndroidStepHandler finalAndroidStepHandler = androidStepHandler;
- AndroidDeviceThreadPool.cachedThreadPool.execute(() -> {
- JSONObject result = new JSONObject();
- result.put("msg", "installFinish");
- HandleDes handleDes = new HandleDes();
- finalAndroidStepHandler.install(handleDes, msg.getString("apk"));
- if (handleDes.getE() == null) {
- result.put("status", "success");
- } else {
- System.out.println(handleDes.getE());
- result.put("status", "fail");
- }
- sendText(session, result.toJSONString());
- });
- }
- if (msg.getString("detail").equals("tree")) {
- androidStepHandler = HandlerMap.getAndroidMap().get(session.getId());
- AndroidStepHandler finalAndroidStepHandler = androidStepHandler;
- AndroidDeviceThreadPool.cachedThreadPool.execute(() -> {
- try {
- JSONObject result = new JSONObject();
- result.put("msg", "tree");
- result.put("detail", finalAndroidStepHandler.getResource());
- HandleDes handleDes = new HandleDes();
- if (!msg.getBoolean("hasScreen")) {
- result.put("img", finalAndroidStepHandler.stepScreen(handleDes));
- }
- if (handleDes.getE() != null) {
- logger.error(handleDes.getE().getMessage());
- JSONObject resultFail = new JSONObject();
- resultFail.put("msg", "treeFail");
- sendText(session, resultFail.toJSONString());
- } else {
- result.put("webView", finalAndroidStepHandler.getWebView());
- result.put("activity", finalAndroidStepHandler.getCurrentActivity());
- sendText(session, result.toJSONString());
- }
- } catch (Throwable e) {
- logger.error(e.getMessage());
- JSONObject result = new JSONObject();
- result.put("msg", "treeFail");
- sendText(session, result.toJSONString());
- }
- });
- }
- if (msg.getString("detail").equals("eleScreen")) {
- androidStepHandler = HandlerMap.getAndroidMap().get(session.getId());
- AndroidStepHandler finalAndroidStepHandler = androidStepHandler;
- AndroidDeviceThreadPool.cachedThreadPool.execute(() -> {
- JSONObject result = new JSONObject();
- result.put("msg", "eleScreen");
- try {
- result.put("img", UploadTools.upload(finalAndroidStepHandler.findEle("xpath", msg.getString("xpath")).getScreenshotAs(OutputType.FILE), "keepFiles"));
- } catch (Exception e) {
- result.put("errMsg", "获取元素截图失败!");
- }
- sendText(session, result.toJSONString());
- });
- }
if (msg.getString("detail").equals("runStep")) {
JSONObject jsonDebug = new JSONObject();
jsonDebug.put("msg", "findSteps");
@@ -540,6 +473,94 @@ public void onMessage(String message, Session session) throws InterruptedExcepti
jsonDebug.put("sessionId", session.getId());
jsonDebug.put("caseId", msg.getInteger("caseId"));
NettyThreadPool.send(jsonDebug);
+ } else {
+ AndroidStepHandler androidStepHandler = HandlerMap.getAndroidMap().get(session.getId());
+ if (androidStepHandler == null || androidStepHandler.getAndroidDriver() == null) {
+ break;
+ }
+ try {
+ if (msg.getString("detail").equals("tap")) {
+ String xy = msg.getString("point");
+ int x = Integer.parseInt(xy.substring(0, xy.indexOf(",")));
+ int y = Integer.parseInt(xy.substring(xy.indexOf(",") + 1));
+ AndroidDeviceBridgeTool.executeCommand(udIdMap.get(session), "input tap " + x + " " + y);
+ }
+ if (msg.getString("detail").equals("longPress")) {
+ String xy = msg.getString("point");
+ int x = Integer.parseInt(xy.substring(0, xy.indexOf(",")));
+ int y = Integer.parseInt(xy.substring(xy.indexOf(",") + 1));
+ AndroidDeviceBridgeTool.executeCommand(udIdMap.get(session), "input swipe " + x + " " + y + " " + x + " " + y + " 1500");
+ }
+ if (msg.getString("detail").equals("swipe")) {
+ String xy1 = msg.getString("pointA");
+ String xy2 = msg.getString("pointB");
+ int x1 = Integer.parseInt(xy1.substring(0, xy1.indexOf(",")));
+ int y1 = Integer.parseInt(xy1.substring(xy1.indexOf(",") + 1));
+ int x2 = Integer.parseInt(xy2.substring(0, xy2.indexOf(",")));
+ int y2 = Integer.parseInt(xy2.substring(xy2.indexOf(",") + 1));
+ AndroidDeviceBridgeTool.executeCommand(udIdMap.get(session), "input swipe " + x1 + " " + y1 + " " + x2 + " " + y2 + " 200");
+ }
+ } catch (Exception e) {
+ e.printStackTrace();
+ }
+ if (msg.getString("detail").equals("install")) {
+ AndroidStepHandler finalAndroidStepHandler = androidStepHandler;
+ AndroidDeviceThreadPool.cachedThreadPool.execute(() -> {
+ JSONObject result = new JSONObject();
+ result.put("msg", "installFinish");
+ HandleDes handleDes = new HandleDes();
+ finalAndroidStepHandler.install(handleDes, msg.getString("apk"));
+ if (handleDes.getE() == null) {
+ result.put("status", "success");
+ } else {
+ System.out.println(handleDes.getE());
+ result.put("status", "fail");
+ }
+ sendText(session, result.toJSONString());
+ });
+ }
+ if (msg.getString("detail").equals("tree")) {
+ AndroidStepHandler finalAndroidStepHandler = androidStepHandler;
+ AndroidDeviceThreadPool.cachedThreadPool.execute(() -> {
+ try {
+ JSONObject result = new JSONObject();
+ result.put("msg", "tree");
+ result.put("detail", finalAndroidStepHandler.getResource());
+ HandleDes handleDes = new HandleDes();
+ if (!msg.getBoolean("hasScreen")) {
+ result.put("img", finalAndroidStepHandler.stepScreen(handleDes));
+ }
+ if (handleDes.getE() != null) {
+ logger.error(handleDes.getE().getMessage());
+ JSONObject resultFail = new JSONObject();
+ resultFail.put("msg", "treeFail");
+ sendText(session, resultFail.toJSONString());
+ } else {
+ result.put("webView", finalAndroidStepHandler.getWebView());
+ result.put("activity", finalAndroidStepHandler.getCurrentActivity());
+ sendText(session, result.toJSONString());
+ }
+ } catch (Throwable e) {
+ logger.error(e.getMessage());
+ JSONObject result = new JSONObject();
+ result.put("msg", "treeFail");
+ sendText(session, result.toJSONString());
+ }
+ });
+ }
+ if (msg.getString("detail").equals("eleScreen")) {
+ AndroidStepHandler finalAndroidStepHandler = androidStepHandler;
+ AndroidDeviceThreadPool.cachedThreadPool.execute(() -> {
+ JSONObject result = new JSONObject();
+ result.put("msg", "eleScreen");
+ try {
+ result.put("img", UploadTools.upload(finalAndroidStepHandler.findEle("xpath", msg.getString("xpath")).getScreenshotAs(OutputType.FILE), "keepFiles"));
+ } catch (Exception e) {
+ result.put("errMsg", "获取元素截图失败!");
+ }
+ sendText(session, result.toJSONString());
+ });
+ }
}
break;
}
@@ -571,10 +592,12 @@ private void exit(Session session) {
}
outputMap.remove(session);
udIdMap.remove(session);
- rotationMap.get(session).interrupt();
+ if (rotationMap.get(session) != null) {
+ rotationMap.get(session).interrupt();
+ }
rotationMap.remove(session);
MiniCapMap.getMap().get(session).interrupt();
- WebSocketSessionMap.getMap().remove(session.getId());
+ WebSocketSessionMap.removeSession(session);
try {
session.close();
} catch (IOException e) {
@@ -582,4 +605,4 @@ private void exit(Session session) {
}
logger.info(session.getId() + "退出");
}
-}
\ No newline at end of file
+}
diff --git a/src/main/java/com/sonic/agent/websockets/IOSWSServer.java b/src/main/java/com/sonic/agent/websockets/IOSWSServer.java
index 9b1f0b61..02ab5b11 100644
--- a/src/main/java/com/sonic/agent/websockets/IOSWSServer.java
+++ b/src/main/java/com/sonic/agent/websockets/IOSWSServer.java
@@ -1,26 +1,17 @@
package com.sonic.agent.websockets;
import com.alibaba.fastjson.JSON;
-import com.alibaba.fastjson.JSONArray;
import com.alibaba.fastjson.JSONObject;
-import com.android.ddmlib.IDevice;
-import com.sonic.agent.automation.AndroidStepHandler;
import com.sonic.agent.automation.HandleDes;
import com.sonic.agent.automation.IOSStepHandler;
-import com.sonic.agent.automation.RemoteDebugDriver;
-import com.sonic.agent.bridge.android.AndroidDeviceBridgeTool;
-import com.sonic.agent.bridge.android.AndroidDeviceThreadPool;
import com.sonic.agent.bridge.ios.IOSDeviceLocalStatus;
import com.sonic.agent.bridge.ios.IOSDeviceThreadPool;
import com.sonic.agent.bridge.ios.TIDeviceTool;
import com.sonic.agent.interfaces.DeviceStatus;
+import com.sonic.agent.maps.DevicesLockMap;
import com.sonic.agent.maps.HandlerMap;
-import com.sonic.agent.maps.MiniCapMap;
import com.sonic.agent.maps.WebSocketSessionMap;
import com.sonic.agent.netty.NettyThreadPool;
-import com.sonic.agent.tools.MiniCapTool;
-import com.sonic.agent.tools.PortTool;
-import com.sonic.agent.tools.ProcessCommandTool;
import com.sonic.agent.tools.UploadTools;
import io.appium.java_client.TouchAction;
import io.appium.java_client.touch.WaitOptions;
@@ -29,19 +20,18 @@
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.beans.factory.annotation.Value;
-import org.springframework.http.*;
import org.springframework.stereotype.Component;
import javax.websocket.*;
import javax.websocket.server.PathParam;
import javax.websocket.server.ServerEndpoint;
import java.io.IOException;
-import java.io.OutputStream;
import java.time.Duration;
import java.util.*;
import java.util.concurrent.ConcurrentHashMap;
-import java.util.concurrent.Future;
-import java.util.concurrent.atomic.AtomicReference;
+import java.util.concurrent.TimeUnit;
+
+import static com.sonic.agent.tools.AgentTool.sendText;
@Component
@ServerEndpoint(value = "/websockets/ios/{key}/{udId}/{token}", configurator = MyEndpointConfigure.class)
@@ -58,26 +48,41 @@ public void onOpen(Session session, @PathParam("key") String secretKey,
logger.info("拦截访问!");
return;
}
+
+ session.getUserProperties().put("udId", udId);
+ boolean lockSuccess = DevicesLockMap.lockByUdId(udId, 30L, TimeUnit.SECONDS);
+ if (!lockSuccess) {
+ logger.info("30s内获取设备锁失败,请确保设备无人使用");
+ return;
+ }
+ logger.info("ios上锁udId:{}", udId);
+
JSONObject jsonDebug = new JSONObject();
jsonDebug.put("msg", "debugUser");
jsonDebug.put("token", token);
jsonDebug.put("udId", udId);
NettyThreadPool.send(jsonDebug);
- WebSocketSessionMap.getMap().put(session.getId(), session);
+ WebSocketSessionMap.addSession(session);
if (!TIDeviceTool.getDeviceList().contains(udId)) {
logger.info("设备未连接,请检查!");
return;
}
udIdMap.put(session, udId);
+ int wdaPort = TIDeviceTool.startWda(udId);
+ int imgPort = TIDeviceTool.relayImg(udId);
+ JSONObject picFinish = new JSONObject();
+ picFinish.put("msg", "picFinish");
+ picFinish.put("port", imgPort);
+ sendText(session, picFinish.toJSONString());
+
IOSDeviceThreadPool.cachedThreadPool.execute(() -> {
IOSStepHandler iosStepHandler = new IOSStepHandler();
iosStepHandler.setTestMode(0, 0, udId, DeviceStatus.DEBUGGING, session.getId());
JSONObject result = new JSONObject();
try {
IOSDeviceLocalStatus.startDebug(udId);
- int imgPort = iosStepHandler.startIOSDriver(udId);
+ iosStepHandler.startIOSDriver(udId, wdaPort);
result.put("status", "success");
- result.put("port", imgPort);
result.put("width", iosStepHandler.getDriver().manage().window().getSize().width);
result.put("height", iosStepHandler.getDriver().manage().window().getSize().height);
result.put("detail", "初始化Driver完成!");
@@ -100,7 +105,13 @@ public void onOpen(Session session, @PathParam("key") String secretKey,
@OnClose
public void onClose(Session session) {
- exit(session);
+ String udId = (String) session.getUserProperties().get("udId");
+ try {
+ exit(session);
+ } finally {
+ DevicesLockMap.unlockAndRemoveByUdId(udId);
+ logger.info("ios解锁udId:{}", udId);
+ }
}
@OnError
@@ -117,130 +128,125 @@ public void onMessage(String message, Session session) throws InterruptedExcepti
logger.info(session.getId() + " 发送 " + msg);
switch (msg.getString("type")) {
case "debug":
- IOSStepHandler iosStepHandler;
- try {
- if (msg.getString("detail").equals("tap")) {
- iosStepHandler = HandlerMap.getIOSMap().get(session.getId());
- TouchAction ta = new TouchAction(iosStepHandler.getDriver());
- String xy = msg.getString("point");
- int x = Integer.parseInt(xy.substring(0, xy.indexOf(",")));
- int y = Integer.parseInt(xy.substring(xy.indexOf(",") + 1));
- ta.tap(PointOption.point(x, y)).perform();
+ if (msg.getString("detail").equals("runStep")) {
+ JSONObject jsonDebug = new JSONObject();
+ jsonDebug.put("msg", "findSteps");
+ jsonDebug.put("key", key);
+ jsonDebug.put("udId", udIdMap.get(session));
+ jsonDebug.put("sessionId", session.getId());
+ jsonDebug.put("caseId", msg.getInteger("caseId"));
+ NettyThreadPool.send(jsonDebug);
+ } else {
+ IOSStepHandler iosStepHandler = HandlerMap.getIOSMap().get(session.getId());
+ if (iosStepHandler == null || iosStepHandler.getDriver() == null) {
+ break;
}
- if (msg.getString("detail").equals("longPress")) {
- iosStepHandler = HandlerMap.getIOSMap().get(session.getId());
- TouchAction ta = new TouchAction(iosStepHandler.getDriver());
- String xy = msg.getString("point");
- int x = Integer.parseInt(xy.substring(0, xy.indexOf(",")));
- int y = Integer.parseInt(xy.substring(xy.indexOf(",") + 1));
- ta.longPress(PointOption.point(x, y)).waitAction(WaitOptions.waitOptions(Duration.ofMillis(1500))).release().perform();
- }
- if (msg.getString("detail").equals("swipe")) {
- iosStepHandler = HandlerMap.getIOSMap().get(session.getId());
- TouchAction ta = new TouchAction(iosStepHandler.getDriver());
- String xy1 = msg.getString("pointA");
- String xy2 = msg.getString("pointB");
- int x1 = Integer.parseInt(xy1.substring(0, xy1.indexOf(",")));
- int y1 = Integer.parseInt(xy1.substring(xy1.indexOf(",") + 1));
- int x2 = Integer.parseInt(xy2.substring(0, xy2.indexOf(",")));
- int y2 = Integer.parseInt(xy2.substring(xy2.indexOf(",") + 1));
- ta.press(PointOption.point(x1, y1)).waitAction(WaitOptions.waitOptions(Duration.ofMillis(300))).moveTo(PointOption.point(x2, y2)).release().perform();
- }
- } catch (Exception e) {
- e.printStackTrace();
- }
- if (msg.getString("detail").equals("keyEvent")) {
- iosStepHandler = HandlerMap.getIOSMap().get(session.getId());
try {
- if (msg.getString("key").equals("home") || msg.getString("key").equals("volumeup") || msg.getString("key").equals("volumedown")) {
- iosStepHandler.getDriver().executeScript("mobile:pressButton", JSON.parse("{name: \"" + msg.getString("key") + "\"}"));
- } else if (msg.getString("key").equals("lock")) {
- if (iosStepHandler.getDriver().isDeviceLocked()) {
- iosStepHandler.unLock(new HandleDes());
- } else {
- iosStepHandler.lock(new HandleDes());
- }
- } else {
- iosStepHandler.openApp(new HandleDes(), "com.apple." + msg.getString("key"));
+ if (msg.getString("detail").equals("tap")) {
+ TouchAction ta = new TouchAction(iosStepHandler.getDriver());
+ String xy = msg.getString("point");
+ int x = Integer.parseInt(xy.substring(0, xy.indexOf(",")));
+ int y = Integer.parseInt(xy.substring(xy.indexOf(",") + 1));
+ ta.tap(PointOption.point(x, y)).perform();
+ }
+ if (msg.getString("detail").equals("longPress")) {
+ TouchAction ta = new TouchAction(iosStepHandler.getDriver());
+ String xy = msg.getString("point");
+ int x = Integer.parseInt(xy.substring(0, xy.indexOf(",")));
+ int y = Integer.parseInt(xy.substring(xy.indexOf(",") + 1));
+ ta.longPress(PointOption.point(x, y)).waitAction(WaitOptions.waitOptions(Duration.ofMillis(1500))).release().perform();
}
- } catch (Throwable e) {
+ if (msg.getString("detail").equals("swipe")) {
+ TouchAction ta = new TouchAction(iosStepHandler.getDriver());
+ String xy1 = msg.getString("pointA");
+ String xy2 = msg.getString("pointB");
+ int x1 = Integer.parseInt(xy1.substring(0, xy1.indexOf(",")));
+ int y1 = Integer.parseInt(xy1.substring(xy1.indexOf(",") + 1));
+ int x2 = Integer.parseInt(xy2.substring(0, xy2.indexOf(",")));
+ int y2 = Integer.parseInt(xy2.substring(xy2.indexOf(",") + 1));
+ ta.press(PointOption.point(x1, y1)).waitAction(WaitOptions.waitOptions(Duration.ofMillis(300))).moveTo(PointOption.point(x2, y2)).release().perform();
+ }
+ } catch (Exception e) {
e.printStackTrace();
}
- }
- if (msg.getString("detail").equals("siri")) {
- iosStepHandler = HandlerMap.getIOSMap().get(session.getId());
- IOSStepHandler finalIOSStepHandler = iosStepHandler;
- IOSDeviceThreadPool.cachedThreadPool.execute(() -> {
- finalIOSStepHandler.siriCommand(new HandleDes(), msg.getString("command"));
- });
- }
-// if (msg.getString("detail").equals("install")) {
-// androidStepHandler = HandlerMap.getAndroidMap().get(session.getId());
-// AndroidStepHandler finalAndroidStepHandler = androidStepHandler;
-// AndroidDeviceThreadPool.cachedThreadPool.execute(() -> {
-// JSONObject result = new JSONObject();
-// result.put("msg", "installFinish");
-// HandleDes handleDes = new HandleDes();
-// finalAndroidStepHandler.install(handleDes, msg.getString("apk"));
-// if (handleDes.getE() == null) {
-// result.put("status", "success");
-// } else {
-// System.out.println(handleDes.getE());
-// result.put("status", "fail");
-// }
-// sendText(session, result.toJSONString());
-// });
-// }
- if (msg.getString("detail").equals("tree")) {
- iosStepHandler = HandlerMap.getIOSMap().get(session.getId());
- IOSStepHandler finalIOSStepHandler = iosStepHandler;
- IOSDeviceThreadPool.cachedThreadPool.execute(() -> {
+ if (msg.getString("detail").equals("keyEvent")) {
try {
+ if (msg.getString("key").equals("home") || msg.getString("key").equals("volumeup") || msg.getString("key").equals("volumedown")) {
+ iosStepHandler.getDriver().executeScript("mobile:pressButton", JSON.parse("{name: \"" + msg.getString("key") + "\"}"));
+ } else if (msg.getString("key").equals("lock")) {
+ if (iosStepHandler.getDriver().isDeviceLocked()) {
+ iosStepHandler.unLock(new HandleDes());
+ } else {
+ iosStepHandler.lock(new HandleDes());
+ }
+ } else {
+ iosStepHandler.openApp(new HandleDes(), "com.apple." + msg.getString("key"));
+ }
+ } catch (Throwable e) {
+ e.printStackTrace();
+ }
+ }
+ if (msg.getString("detail").equals("siri")) {
+ IOSStepHandler finalIOSStepHandler = iosStepHandler;
+ IOSDeviceThreadPool.cachedThreadPool.execute(() -> {
+ finalIOSStepHandler.siriCommand(new HandleDes(), msg.getString("command"));
+ });
+ }
+ if (msg.getString("detail").equals("install")) {
+ IOSStepHandler finalIosStepHandler = iosStepHandler;
+ IOSDeviceThreadPool.cachedThreadPool.execute(() -> {
JSONObject result = new JSONObject();
- result.put("msg", "tree");
- result.put("detail", finalIOSStepHandler.getResource());
+ result.put("msg", "installFinish");
HandleDes handleDes = new HandleDes();
- result.put("img", finalIOSStepHandler.stepScreen(handleDes));
- if (handleDes.getE() != null) {
- logger.error(handleDes.getE().getMessage());
- JSONObject resultFail = new JSONObject();
- resultFail.put("msg", "treeFail");
- sendText(session, resultFail.toJSONString());
+ finalIosStepHandler.install(handleDes, msg.getString("ipa"));
+ if (handleDes.getE() == null) {
+ result.put("status", "success");
} else {
+ System.out.println(handleDes.getE());
+ result.put("status", "fail");
+ }
+ sendText(session, result.toJSONString());
+ });
+ }
+ if (msg.getString("detail").equals("tree")) {
+ IOSStepHandler finalIOSStepHandler = iosStepHandler;
+ IOSDeviceThreadPool.cachedThreadPool.execute(() -> {
+ try {
+ JSONObject result = new JSONObject();
+ result.put("msg", "tree");
+ result.put("detail", finalIOSStepHandler.getResource());
+ HandleDes handleDes = new HandleDes();
+ result.put("img", finalIOSStepHandler.stepScreen(handleDes));
+ if (handleDes.getE() != null) {
+ logger.error(handleDes.getE().getMessage());
+ JSONObject resultFail = new JSONObject();
+ resultFail.put("msg", "treeFail");
+ sendText(session, resultFail.toJSONString());
+ } else {
+ sendText(session, result.toJSONString());
+ }
+ } catch (Throwable e) {
+ logger.error(e.getMessage());
+ JSONObject result = new JSONObject();
+ result.put("msg", "treeFail");
sendText(session, result.toJSONString());
}
- } catch (Throwable e) {
- logger.error(e.getMessage());
+ });
+ }
+ if (msg.getString("detail").equals("eleScreen")) {
+ IOSStepHandler finalIOSStepHandler = iosStepHandler;
+ IOSDeviceThreadPool.cachedThreadPool.execute(() -> {
JSONObject result = new JSONObject();
- result.put("msg", "treeFail");
+ result.put("msg", "eleScreen");
+ try {
+ result.put("img", UploadTools.upload(finalIOSStepHandler.findEle("xpath", msg.getString("xpath")).getScreenshotAs(OutputType.FILE), "keepFiles"));
+ } catch (Exception e) {
+ result.put("errMsg", "获取元素截图失败!");
+ }
sendText(session, result.toJSONString());
- }
- });
- }
- if (msg.getString("detail").equals("eleScreen")) {
- iosStepHandler = HandlerMap.getIOSMap().get(session.getId());
- IOSStepHandler finalIOSStepHandler = iosStepHandler;
- IOSDeviceThreadPool.cachedThreadPool.execute(() -> {
- JSONObject result = new JSONObject();
- result.put("msg", "eleScreen");
- try {
- result.put("img", UploadTools.upload(finalIOSStepHandler.findEle("xpath", msg.getString("xpath")).getScreenshotAs(OutputType.FILE), "keepFiles"));
- } catch (Exception e) {
- result.put("errMsg", "获取元素截图失败!");
- }
- sendText(session, result.toJSONString());
- });
+ });
+ }
}
-// if (msg.getString("detail").equals("runStep")) {
-// JSONObject jsonDebug = new JSONObject();
-// jsonDebug.put("msg", "findSteps");
-// jsonDebug.put("key", key);
-// jsonDebug.put("udId", udIdMap.get(session).getSerialNumber());
-// jsonDebug.put("pwd", msg.getString("pwd"));
-// jsonDebug.put("sessionId", session.getId());
-// jsonDebug.put("caseId", msg.getInteger("caseId"));
-// NettyThreadPool.send(jsonDebug);
-// }
break;
}
}
@@ -264,7 +270,7 @@ private void exit(Session session) {
} finally {
HandlerMap.getIOSMap().remove(session.getId());
}
- WebSocketSessionMap.getMap().remove(session.getId());
+ WebSocketSessionMap.removeSession(session);
try {
session.close();
} catch (IOException e) {
diff --git a/src/main/resources/application-dev.yml b/src/main/resources/application-dev.yml
index 94db2d2c..f4a33965 100644
--- a/src/main/resources/application-dev.yml
+++ b/src/main/resources/application-dev.yml
@@ -1,17 +1,17 @@
sonic:
agent:
- host: 172.25.128.1
+ host: 192.168.1.10
port: 7777
key: 29002272-4659-4808-a804-08ce3388b136
server:
- host: 172.25.128.1
+ host: 192.168.1.10
folder-port: 8094
transport-port: 8095
modules:
android:
enable: true
- use-adbkit: false
+ use-adbkit: true
ios:
enable: false
wda-bundle-id: com.sonic.wda.xctrunner