├── .classpath ├── .gitignore ├── .idea ├── AppiumXM.iml ├── inspectionProfiles │ └── profiles_settings.xml ├── misc.xml └── modules.xml ├── .project ├── .settings ├── org.eclipse.core.resources.prefs ├── org.eclipse.jdt.core.prefs └── org.eclipse.m2e.core.prefs ├── README.md ├── StartTest.xml ├── apps ├── wangyi.apk └── webview.apk ├── config.properties ├── devices ├── AndroidDevices.xlsx └── iOSDevices.xlsx ├── img ├── 585.png ├── 58home.png └── zaku.jpg ├── pom.xml ├── report1.png ├── report2.png ├── src └── main │ └── java │ └── com │ └── xiaoM │ ├── BeginScript │ └── BeginScript.java │ ├── Common │ └── Utils │ │ ├── Assertion.java │ │ ├── BaseDriver.java │ │ ├── CommonUtils.java │ │ ├── CopyFileForReport.java │ │ ├── DataFormat.java │ │ ├── FileManger.java │ │ ├── IOMananger.java │ │ ├── Log.java │ │ ├── Run.java │ │ ├── SqlHelper.java │ │ └── UseDevices.java │ ├── ExecuteScript │ └── ExecuteScript.java │ ├── OpenCV │ ├── JavaCVTest.java │ └── TemplateMatch.java │ ├── Report │ └── utils │ │ ├── TestListener.java │ │ └── TestReport.java │ └── appium │ └── utils │ ├── AppiumComm.java │ ├── AppiumElementAction.java │ ├── AppiumResourceMonitoring.java │ ├── AppiumScreenShot.java │ ├── AppiumServerUtils.java │ ├── CpuThread.java │ ├── MemThread.java │ ├── PieChartPicture.java │ └── PortProber.java ├── testcase ├── PageObject.xlsx └── TestCase.xlsx ├── 测试用例步骤.png └── 用例.png /.classpath: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | 9 | 10 | 11 | 12 | 13 | 14 | 15 | 16 | 17 | 18 | 19 | 20 | 21 | 22 | 23 | 24 | 25 | 26 | 27 | -------------------------------------------------------------------------------- /.gitignore: -------------------------------------------------------------------------------- 1 | /target/ 2 | -------------------------------------------------------------------------------- /.idea/AppiumXM.iml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | -------------------------------------------------------------------------------- /.idea/inspectionProfiles/profiles_settings.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 5 | -------------------------------------------------------------------------------- /.idea/misc.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | -------------------------------------------------------------------------------- /.idea/modules.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | -------------------------------------------------------------------------------- /.project: -------------------------------------------------------------------------------- 1 | 2 | 3 | AppiumXM 4 | 5 | 6 | 7 | 8 | 9 | org.eclipse.jdt.core.javabuilder 10 | 11 | 12 | 13 | 14 | org.eclipse.m2e.core.maven2Builder 15 | 16 | 17 | 18 | 19 | 20 | org.eclipse.jdt.core.javanature 21 | org.eclipse.m2e.core.maven2Nature 22 | 23 | 24 | -------------------------------------------------------------------------------- /.settings/org.eclipse.core.resources.prefs: -------------------------------------------------------------------------------- 1 | eclipse.preferences.version=1 2 | encoding//src/main/java=UTF-8 3 | encoding//src/test/java=UTF-8 4 | encoding/=UTF-8 5 | encoding/config.properties=UTF-8 6 | -------------------------------------------------------------------------------- /.settings/org.eclipse.jdt.core.prefs: -------------------------------------------------------------------------------- 1 | eclipse.preferences.version=1 2 | org.eclipse.jdt.core.compiler.codegen.inlineJsrBytecode=enabled 3 | org.eclipse.jdt.core.compiler.codegen.targetPlatform=1.8 4 | org.eclipse.jdt.core.compiler.codegen.unusedLocal=preserve 5 | org.eclipse.jdt.core.compiler.compliance=1.8 6 | org.eclipse.jdt.core.compiler.debug.lineNumber=generate 7 | org.eclipse.jdt.core.compiler.debug.localVariable=generate 8 | org.eclipse.jdt.core.compiler.debug.sourceFile=generate 9 | org.eclipse.jdt.core.compiler.problem.assertIdentifier=error 10 | org.eclipse.jdt.core.compiler.problem.enumIdentifier=error 11 | org.eclipse.jdt.core.compiler.problem.forbiddenReference=warning 12 | org.eclipse.jdt.core.compiler.source=1.8 13 | -------------------------------------------------------------------------------- /.settings/org.eclipse.m2e.core.prefs: -------------------------------------------------------------------------------- 1 | activeProfiles= 2 | eclipse.preferences.version=1 3 | resolveWorkspaceProjects=true 4 | version=1 5 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # AppiumXM 2 | AppiumXM自动化测试框架基于 Appium + testNG + maven 二次封装,采用java语言进行开发,适用于Android、iOS自动化测试,采用Excel关键字驱动实现无需编写代码即可进行自动化测试,支持jenkins持续集成,支持Android/iOS在真机或模拟器进行自动化测试,支持H5、Hybrid 、Native测试 3 | # 环境要求 4 | Appium >=1.6.3

5 | maven >=3.3.9

6 | OSX >=10.11.6

7 | Xcode >=8.2.1

8 | # demo 9 | **测试用例**

10 | ![image](https://github.com/xiaoMGitHub/AppiumXM/blob/master/%E7%94%A8%E4%BE%8B.png)

11 | **测试步骤描述**

12 | ![image](https://github.com/xiaoMGitHub/AppiumXM/blob/master/%E6%B5%8B%E8%AF%95%E7%94%A8%E4%BE%8B%E6%AD%A5%E9%AA%A4.png)

13 | **测试报告(普通)**

14 | ![image](https://github.com/xiaoMGitHub/AppiumXM/blob/master/report1.png)

15 | **测试报告(性能监控)**

16 | ![image](https://github.com/xiaoMGitHub/AppiumXM/blob/master/report2.png)

17 | # 存在问题 18 | 1、测试报告展示测试结束时间有误

19 | 2、其他未知问题 20 | # 如何运行? 21 | 1、Devices文件夹下添加相应的设备信息

22 | 2、testcase文件夹下编辑测试步骤流程

23 | 3、支持操作:看源码。。。

24 | 4、安装testng插件运行StartTest.xml或定位到工程目录下运行mvn test,ant编译中文会乱码不建议

25 | 5、多设备参照StartTest.xml注释

26 | 6、持续集成,Jenkins + mvn 亲测运行完美

27 | 7、iOS真机需自行前往 /usr/local/lib/node_modules/appium/node_modules/appium-xcuitest-driver/WebDriverAgent 对 WebDriverAgent.xcodeproj 进行重签名(个人证书即可) 28 | # 缺陷 29 | 1、灵活性真的不高。。。 30 | -------------------------------------------------------------------------------- /StartTest.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | 9 | 10 | 11 | 12 | 13 | 14 | -------------------------------------------------------------------------------- /apps/wangyi.apk: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/mingming2513953126/AppiumXM/d6cf7f5f1efe0e849db70cca630213511456dc8b/apps/wangyi.apk -------------------------------------------------------------------------------- /apps/webview.apk: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/mingming2513953126/AppiumXM/d6cf7f5f1efe0e849db70cca630213511456dc8b/apps/webview.apk -------------------------------------------------------------------------------- /config.properties: -------------------------------------------------------------------------------- 1 | #appium安装路径,win系统无视 2 | APPIUM_JS_PATH=/usr/local/lib/node_modules/appium/build/lib/main.js 3 | #adb安装路径,win系统无视 4 | ADB_PATH=/Users/xiaoM/Desktop/OSX/xiaoMWork/sdk/platform-tools/adb 5 | #工程路径 6 | WORKSPAC_PATH=D:/xiaoMWork/workspace/AppiumXM 7 | #测试设备系统(Android\iOS) 8 | DEVICE_TYPE=Android 9 | #测试设备(线程数必须小于设备数) 10 | DEVICES=device1 11 | #测试报告标题 12 | REPORT_TITLE=自动化测试报告 13 | #被测试应用 14 | APP_NAME=wangyi.apk 15 | #测试应用包名 16 | PACKAGE_NAME=com.netease.mail 17 | #测试应用Activity 18 | ACTIVITY=com.netease.mobimail.activity.LaunchActivity 19 | #测试应用bundleId(IOS用) 20 | BUNDIEID=com.tencent.qqmail 21 | #测试类型(RM:资源监控(只支持Android);FT:功能测试) 22 | TEST_TYPE=FT 23 | #不清除缓存重置应用(暂时只针对Android) 24 | NORESET_APP=FALSE 25 | #测试用例 26 | TESTCASE=TestCase.xlsx 27 | -------------------------------------------------------------------------------- /devices/AndroidDevices.xlsx: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/mingming2513953126/AppiumXM/d6cf7f5f1efe0e849db70cca630213511456dc8b/devices/AndroidDevices.xlsx -------------------------------------------------------------------------------- /devices/iOSDevices.xlsx: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/mingming2513953126/AppiumXM/d6cf7f5f1efe0e849db70cca630213511456dc8b/devices/iOSDevices.xlsx -------------------------------------------------------------------------------- /img/585.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/mingming2513953126/AppiumXM/d6cf7f5f1efe0e849db70cca630213511456dc8b/img/585.png -------------------------------------------------------------------------------- /img/58home.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/mingming2513953126/AppiumXM/d6cf7f5f1efe0e849db70cca630213511456dc8b/img/58home.png -------------------------------------------------------------------------------- /img/zaku.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/mingming2513953126/AppiumXM/d6cf7f5f1efe0e849db70cca630213511456dc8b/img/zaku.jpg -------------------------------------------------------------------------------- /pom.xml: -------------------------------------------------------------------------------- 1 | 3 | 4.0.0 4 | com.xiaoM 5 | 0.0.1-SNAPSHOT 6 | jar 7 | AppiumXM 8 | https://github.com/xiaoMGitHub 9 | 10 | UTF-8 11 | 1.8 12 | 1.8 13 | 14 | AppiumXM 15 | This is App test framework base on appium 16 | 17 | 18 | The Apache License, Version 2.0 19 | http://www.apache.org/licenses/LICENSE-2.0.txt 20 | 21 | 22 | 23 | 24 | xiaoM Zhou 25 | zxmyhd@163.com 26 | 27 | 28 | 29 | 30 | org.seleniumhq.selenium 31 | selenium-server 32 | 3.4.0 33 | 34 | 35 | org.seleniumhq.selenium 36 | selenium-remote-driver 37 | 3.4.0 38 | 39 | 40 | io.appium 41 | java-client 42 | 5.0.0-BETA8 43 | 44 | 45 | log4j 46 | log4j 47 | 1.2.17 48 | 49 | 50 | org.testng 51 | testng 52 | 6.11 53 | 54 | 55 | org.apache.poi 56 | poi-ooxml 57 | 3.16 58 | 59 | 60 | org.jfree 61 | jfreechart 62 | 1.0.19 63 | 64 | 65 | com.aventstack 66 | extentreports 67 | 3.0.5 68 | 69 | 70 | org.bytedeco 71 | javacv-platform 72 | 1.3.2 73 | 74 | 75 | 76 | 77 | 78 | org.apache.maven.plugins 79 | maven-surefire-plugin 80 | 2.20 81 | 82 | once 83 | -Dfile.encoding=UTF-8 84 | 85 | StartTest.xml 86 | 87 | 88 | 89 | 90 | 91 | 92 | -------------------------------------------------------------------------------- /report1.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/mingming2513953126/AppiumXM/d6cf7f5f1efe0e849db70cca630213511456dc8b/report1.png -------------------------------------------------------------------------------- /report2.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/mingming2513953126/AppiumXM/d6cf7f5f1efe0e849db70cca630213511456dc8b/report2.png -------------------------------------------------------------------------------- /src/main/java/com/xiaoM/BeginScript/BeginScript.java: -------------------------------------------------------------------------------- 1 | package com.xiaoM.BeginScript; 2 | 3 | import java.io.IOException; 4 | 5 | import org.testng.annotations.DataProvider; 6 | import org.testng.annotations.Test; 7 | 8 | import com.xiaoM.Common.Utils.Run; 9 | import com.xiaoM.Common.Utils.UseDevices; 10 | import com.xiaoM.Report.utils.TestListener; 11 | 12 | 13 | public class BeginScript{ 14 | 15 | @DataProvider(parallel = true) 16 | public Object[][]Testcases() throws IOException{ 17 | return TestListener.RunCase; 18 | } 19 | 20 | @Test(dataProvider = "Testcases") 21 | public void run(String CaseType,String CaseName) throws Exception{ 22 | String getDevice = UseDevices.Device(); 23 | Run run = new Run(); 24 | run.testCase(CaseType,CaseName,getDevice); 25 | } 26 | } 27 | -------------------------------------------------------------------------------- /src/main/java/com/xiaoM/Common/Utils/Assertion.java: -------------------------------------------------------------------------------- 1 | package com.xiaoM.Common.Utils; 2 | 3 | import java.text.SimpleDateFormat; 4 | import java.util.Date; 5 | import org.testng.Assert; 6 | 7 | import com.xiaoM.Report.utils.TestListener; 8 | /** 9 | * Assert 验证类 10 | * @author xiaoM 11 | * 12 | */ 13 | public class Assertion { 14 | private static Log log=new Log(Assertion.class); 15 | //断言成功日志内容 16 | private static void AssertPassLog(String driverName){ 17 | log.info("设备: "+driverName+" "+"【Assert验证:pass!】"); 18 | } 19 | //断言失败日志内容 20 | private static void AssertFailedLog(String driverName){ 21 | log.error("设备: "+driverName+" "+"【Assert验证:failed!】"); 22 | } 23 | public static String formatDate(Date date){ 24 | SimpleDateFormat formatter = new SimpleDateFormat("yyyy-MM-dd HHmmssSSS"); 25 | return formatter.format(date).toString(); 26 | } 27 | 28 | public static void Verity(String checkWay,String actualText,String exceptText,String driverName,String sdkVersion){ 29 | if(checkWay.equals("文本校验")){ 30 | VerityText(actualText,exceptText,driverName,sdkVersion); 31 | } 32 | } 33 | 34 | /** 35 | * 验证某元素文本值是否与预期值exceptText一样 36 | * @param exceptText 预期文本值 37 | */ 38 | public static void VerityText(String actualText,String exceptText,String driverName,String sdkVersion){ 39 | String verityStr= "设备: "+driverName+" "+"Assert验证:某文本值是否与预期值一致{"+"实际值:"+actualText+","+"预期值:"+exceptText+"}"; 40 | log.info(verityStr); 41 | try { 42 | Assert.assertEquals(actualText, exceptText); 43 | AssertPassLog(driverName); 44 | } catch (Error e) { 45 | TestListener.messageList.add(driverName+"(安卓版本:"+sdkVersion+")::"+"Assert验证失败:{"+"实际值:"+actualText+","+"预期值:"+exceptText+"} 不一致"); 46 | AssertFailedLog(driverName); 47 | throw e; 48 | } 49 | } 50 | } 51 | -------------------------------------------------------------------------------- /src/main/java/com/xiaoM/Common/Utils/BaseDriver.java: -------------------------------------------------------------------------------- 1 | package com.xiaoM.Common.Utils; 2 | 3 | import java.io.File; 4 | import java.io.IOException; 5 | import java.net.URL; 6 | 7 | import org.openqa.selenium.remote.CapabilityType; 8 | import org.openqa.selenium.remote.DesiredCapabilities; 9 | 10 | import com.xiaoM.Report.utils.TestListener; 11 | import com.xiaoM.appium.utils.AppiumServerUtils; 12 | import com.xiaoM.appium.utils.PortProber; 13 | 14 | import io.appium.java_client.AppiumDriver; 15 | import io.appium.java_client.MobileElement; 16 | import io.appium.java_client.remote.AndroidMobileCapabilityType; 17 | import io.appium.java_client.remote.AutomationName; 18 | import io.appium.java_client.remote.IOSMobileCapabilityType; 19 | import io.appium.java_client.remote.MobileCapabilityType; 20 | 21 | public class BaseDriver { 22 | public Log log=new Log(this.getClass()); 23 | AppiumDriver driver ; 24 | AppiumServerUtils AppiumServer = null; 25 | public URL url; 26 | public AppiumDriver setUpApp(String device,String devicesPath) throws IOException { 27 | String driverName = null; 28 | try { 29 | Object[][] testBase = IOMananger.readExcelData(device,devicesPath); 30 | driverName = testBase[1][2].toString(); 31 | String ipAddress = testBase[2][2].toString(); 32 | String platformName =testBase[3][2].toString(); 33 | String deviceId =testBase[4][2].toString(); 34 | String deviceName =testBase[5][2].toString(); 35 | String sdkVersion =testBase[6][2].toString(); 36 | int Port = PortProber.getFreePort(); 37 | String bootstrapPort = String.valueOf(PortProber.getFreePort()); 38 | log.info("设备: "+driverName+" "+"开始执行测试"); 39 | log.info("设备: "+driverName+" "+"启动appium server"); 40 | log.info("设备: "+driverName+" "+"配置信息:Mobile Driver:"+driverName); 41 | log.info("设备: "+driverName+" "+"Appium Server:"+"http://"+ipAddress+":"+Port+"/wd/hub"); 42 | log.info("设备: "+driverName+" "+"设备Id:"+deviceId); 43 | try { 44 | DesiredCapabilities capabilities = new DesiredCapabilities(); 45 | if(TestListener.DeviceType.equals("Android")){ 46 | AppiumServer = new AppiumServerUtils(ipAddress,Port,bootstrapPort); 47 | url = AppiumServer.startServer(); 48 | File appDir=new File(TestListener.ProjectPath,"apps"); 49 | File app =new File(appDir,TestListener.appName); 50 | String appMainPackage =TestListener.PackageName; 51 | String appActivity =TestListener.Activity; 52 | capabilities.setCapability(MobileCapabilityType.AUTOMATION_NAME, AutomationName.APPIUM); 53 | capabilities.setCapability(MobileCapabilityType.PLATFORM_NAME, platformName); 54 | capabilities.setCapability(MobileCapabilityType.DEVICE_NAME, deviceName); 55 | capabilities.setCapability(MobileCapabilityType.PLATFORM_VERSION, sdkVersion); 56 | capabilities.setCapability(MobileCapabilityType.APP, app.getAbsolutePath()); 57 | capabilities.setCapability(AndroidMobileCapabilityType.APP_PACKAGE, appMainPackage); 58 | capabilities.setCapability(AndroidMobileCapabilityType.APP_ACTIVITY, appActivity); 59 | capabilities.setCapability(MobileCapabilityType.NO_RESET, TestListener.ResetApp); 60 | capabilities.setCapability(MobileCapabilityType.UDID, deviceId); 61 | capabilities.setCapability("unicodeKeyboard", "True"); 62 | capabilities.setCapability("resetKeyboard", "True"); 63 | capabilities.setCapability("noSign", "True"); 64 | }else{ 65 | AppiumServer = new AppiumServerUtils(); 66 | url = AppiumServer.startServer(ipAddress,Port); 67 | String bundleId = TestListener.bundleId; 68 | capabilities.setCapability(MobileCapabilityType.AUTOMATION_NAME, AutomationName.IOS_XCUI_TEST); 69 | capabilities.setCapability(MobileCapabilityType.PLATFORM_NAME, platformName); 70 | capabilities.setCapability(MobileCapabilityType.DEVICE_NAME, deviceName); 71 | capabilities.setCapability(MobileCapabilityType.PLATFORM_VERSION, sdkVersion); 72 | capabilities.setCapability(IOSMobileCapabilityType.LAUNCH_TIMEOUT, 500000); 73 | capabilities.setCapability(MobileCapabilityType.UDID, deviceId); 74 | capabilities.setCapability(IOSMobileCapabilityType.BUNDLE_ID,bundleId); 75 | } 76 | driver = new AppiumDriver(url, capabilities); 77 | } catch (Exception e) { 78 | e.printStackTrace(); 79 | log.error("设备: "+driverName+" "+"appium环境配置失败"); 80 | } 81 | } catch (Exception e) { 82 | log.error("设备: "+driverName+" "+"读取TestBase配置文件失败"); 83 | e.printStackTrace(); 84 | } 85 | return driver; 86 | } 87 | 88 | 89 | public AppiumDriver setUpWap(String device,String devicesPath) throws IOException { 90 | String driverName =null; 91 | try { 92 | Object[][] testBase = IOMananger.readExcelData(device,devicesPath); 93 | driverName = testBase[1][2].toString(); 94 | String ipAddress = testBase[2][2].toString(); 95 | String platformName =testBase[3][2].toString(); 96 | String deviceId =testBase[4][2].toString(); 97 | String deviceName =testBase[5][2].toString(); 98 | String sdkVersion =testBase[6][2].toString(); 99 | String Browser = testBase[7][2].toString(); 100 | int Port = PortProber.getFreePort(); 101 | String bootstrapPort = String.valueOf(PortProber.getFreePort()); 102 | log.info("设备: "+driverName+" "+"开始执行测试"); 103 | log.info("设备: "+driverName+" "+"启动appium server"); 104 | try { 105 | log.info("设备: "+driverName+" "+"配置信息:Mobile Driver:"+driverName); 106 | log.info("设备: "+driverName+" "+"Appium Server:"+"http://"+ipAddress+":"+Port+"/wd/hub"); 107 | log.info("设备: "+driverName+" "+"设备Id:"+deviceId); 108 | DesiredCapabilities capabilities = new DesiredCapabilities(); 109 | if(TestListener.DeviceType.equals("Android")){ 110 | AppiumServer = new AppiumServerUtils(ipAddress,Port,bootstrapPort); 111 | url = AppiumServer.startServer(); 112 | capabilities.setCapability(MobileCapabilityType.AUTOMATION_NAME, AutomationName.APPIUM); 113 | capabilities.setCapability(CapabilityType.BROWSER_NAME, Browser);// Browser 114 | capabilities.setCapability(MobileCapabilityType.PLATFORM_NAME, platformName); 115 | capabilities.setCapability(MobileCapabilityType.DEVICE_NAME, deviceName); 116 | capabilities.setCapability(MobileCapabilityType.PLATFORM_VERSION, sdkVersion); 117 | capabilities.setCapability(MobileCapabilityType.UDID, deviceId); 118 | capabilities.setCapability("unicodeKeyboard", "True"); 119 | capabilities.setCapability("resetKeyboard", "True"); 120 | }else{ 121 | AppiumServer = new AppiumServerUtils(); 122 | url = AppiumServer.startServer(ipAddress,Port); 123 | capabilities.setCapability(MobileCapabilityType.AUTOMATION_NAME, AutomationName.IOS_XCUI_TEST); 124 | capabilities.setCapability(CapabilityType.BROWSER_NAME, Browser);// Browser 125 | capabilities.setCapability(MobileCapabilityType.PLATFORM_NAME, platformName); 126 | capabilities.setCapability(MobileCapabilityType.DEVICE_NAME, deviceName); 127 | capabilities.setCapability(MobileCapabilityType.PLATFORM_VERSION, sdkVersion); 128 | capabilities.setCapability(MobileCapabilityType.UDID, deviceId); 129 | } 130 | driver = new AppiumDriver (url, capabilities); 131 | } catch (Exception e) { 132 | e.printStackTrace(); 133 | log.error("设备: "+driverName+" "+"appium环境配置失败"); 134 | } 135 | } catch (Exception e) { 136 | log.error("设备: "+driverName+" "+"读取TestBase配置文件失败"); 137 | e.printStackTrace(); 138 | } 139 | return driver; 140 | } 141 | } 142 | -------------------------------------------------------------------------------- /src/main/java/com/xiaoM/Common/Utils/CommonUtils.java: -------------------------------------------------------------------------------- 1 | package com.xiaoM.Common.Utils; 2 | 3 | import java.io.IOException; 4 | 5 | /** 6 | * 公共调用类 7 | * @author XiaoM 8 | * 9 | */ 10 | public class CommonUtils { 11 | /** 12 | * 执行cmd命令 13 | */ 14 | public static void executeCmd(String cmd) throws IOException { 15 | Runtime runtime=Runtime.getRuntime(); 16 | runtime.exec("cmd /c start "+cmd); 17 | } 18 | 19 | /** 20 | * 显式等待,程序休眠暂停 21 | * @param time 以秒为单位 22 | */ 23 | public static void sleep(long time){ 24 | try { 25 | Thread.sleep(time*1000); 26 | } catch (InterruptedException e) { 27 | e.printStackTrace(); 28 | } 29 | } 30 | } -------------------------------------------------------------------------------- /src/main/java/com/xiaoM/Common/Utils/CopyFileForReport.java: -------------------------------------------------------------------------------- 1 | package com.xiaoM.Common.Utils; 2 | 3 | import java.io.*; 4 | 5 | /** 6 | * Created by zhengshuheng on 2016/8/18 0018. 7 | */ 8 | public class CopyFileForReport { 9 | 10 | public static void main(String[] args){ 11 | CopyFileForReport copyReportResources=new CopyFileForReport(); 12 | copyReportResources.copyResources(); 13 | } 14 | public void copyResources(){ 15 | this.copyFile("resources/back.gif", "test-output/back.gif"); 16 | } 17 | /** 18 | * 复制图片及其他文件 19 | * @param sourceRelativePath 源文件相对路径 20 | * @param targetRelativePath 目标文件相对路径 21 | */ 22 | private void copyFile(String sourceRelativePath,String targetRelativePath){ 23 | //读取流字节流 24 | FileInputStream fileInputStream=null; 25 | //写入流字节流 26 | OutputStream imageOutputStream=null; 27 | try { 28 | fileInputStream=new FileInputStream(new File(sourceRelativePath)); 29 | //imageInputStream=CopyReportResources.class.getClassLoader().getResourceAsStream(sourceRelativePath);// 与复制的图片关联起来 30 | imageOutputStream=new FileOutputStream(targetRelativePath);// 与复制到的目的关联起来,这里的图片的名称可以与原来的相同,也可以不一样 31 | byte[] b = new byte[1024];// 定义字节数组,并指定长度 32 | int startbyte = -1; 33 | while ((startbyte= fileInputStream.read(b)) != -1) {// 读取 34 | imageOutputStream.write(b, 0, startbyte);// 写入,读多少写入多少,所以用 write(b,0,len) 35 | } 36 | } catch (IOException e) { 37 | e.printStackTrace(); 38 | }finally{ 39 | if (fileInputStream!=null) { 40 | try { 41 | fileInputStream.close(); 42 | 43 | } catch (IOException e2) { 44 | System.out.println("读取流关闭失败"); 45 | } 46 | } 47 | if (imageOutputStream!=null) { 48 | try { 49 | imageOutputStream.close(); 50 | } catch (Exception e2) { 51 | System.out.println("输出流关闭失败"); 52 | } 53 | } 54 | } 55 | } 56 | } 57 | -------------------------------------------------------------------------------- /src/main/java/com/xiaoM/Common/Utils/DataFormat.java: -------------------------------------------------------------------------------- 1 | package com.xiaoM.Common.Utils; 2 | 3 | import java.text.ParseException; 4 | import java.text.SimpleDateFormat; 5 | import java.util.Date; 6 | 7 | /** 8 | * 格式化时间工具类 9 | * @author XiaoM 10 | * 11 | */ 12 | public class DataFormat { 13 | 14 | public static String formatDate(Date date,String format){ 15 | SimpleDateFormat formatter = new SimpleDateFormat(format); 16 | System.out.println(formatter.format(date).toString()); 17 | return formatter.format(date).toString(); 18 | 19 | } 20 | public static String formatDate(long date,String format){ 21 | SimpleDateFormat formatter = new SimpleDateFormat(format); 22 | System.out.println(formatter.format(date)); 23 | return formatter.format(date); 24 | } 25 | //支持YY-MM-DD转换成YYYY-MM-DD 26 | public static String formatDate(String date,String format){ 27 | SimpleDateFormat sdf = new SimpleDateFormat("yy-MM-dd"); 28 | SimpleDateFormat sdf2 = new SimpleDateFormat(format); 29 | String sss = null; 30 | try { 31 | sss = sdf2.format(sdf.parse(date)); 32 | System.out.println(sss); 33 | } catch (ParseException e) { 34 | // TODO Auto-generated catch block 35 | e.printStackTrace(); 36 | } 37 | return sss; 38 | } 39 | public static void main(String[] args) { 40 | // TODO 自动生成的方法存根 41 | 42 | } 43 | 44 | } 45 | -------------------------------------------------------------------------------- /src/main/java/com/xiaoM/Common/Utils/FileManger.java: -------------------------------------------------------------------------------- 1 | package com.xiaoM.Common.Utils; 2 | 3 | import java.io.*; 4 | 5 | /** 6 | * 文件管理类 7 | * @author XiaoM 8 | * 9 | */ 10 | public class FileManger { 11 | 12 | /** 13 | * 递归删除文件 14 | * @param file 15 | */ 16 | private void deleteFile(File file){ 17 | if (file.isDirectory()) { 18 | File[] files=file.listFiles(); 19 | //递归条用删除方法 20 | for (int i = 0; i < files.length; i++) { 21 | deleteFile(files[i]); 22 | } 23 | } 24 | file.delete(); 25 | } 26 | /** 27 | * 删除文件夹和密码 28 | * @param workspaceRootPath 文件目录 29 | */ 30 | public void clearFile(String workspaceRootPath){ 31 | File file=new File(workspaceRootPath); 32 | if (file.exists()) { 33 | deleteFile(file); 34 | } 35 | 36 | } 37 | 38 | /** 39 | * 40 | * @param path 写入文件目标路径 41 | * @param encode 写入文件编码 42 | * @param append 是否文本追加模式 43 | * @param content 写入文本 44 | */ 45 | public void writeWithEncode(String path,String encode,boolean append,String content){ 46 | File file=new File(path); 47 | if (file.exists()){ 48 | file.delete(); 49 | } 50 | try { 51 | file.createNewFile(); 52 | BufferedWriter bufferedWriter=new BufferedWriter(new OutputStreamWriter( new FileOutputStream(file,append),encode)); 53 | bufferedWriter.write(content); 54 | bufferedWriter.close(); 55 | } catch (IOException e) { 56 | e.printStackTrace(); 57 | } 58 | 59 | } 60 | /** 61 | * 62 | * @param file 写入文件文件 63 | * @param encode 写入文件编码 64 | * @param append 是否文本追加模式 65 | * @param content 写入文本 66 | */ 67 | public static void writeWithEncode(File file,String encode,boolean append,String content){ 68 | try { 69 | file.createNewFile(); 70 | BufferedWriter bufferedWriter=new BufferedWriter(new OutputStreamWriter( new FileOutputStream(file,append),encode)); 71 | bufferedWriter.write(content); 72 | bufferedWriter.close(); 73 | } catch (IOException e) { 74 | e.printStackTrace(); 75 | } 76 | 77 | } 78 | 79 | 80 | public static void main(String[] args) { 81 | 82 | } 83 | } 84 | -------------------------------------------------------------------------------- /src/main/java/com/xiaoM/Common/Utils/IOMananger.java: -------------------------------------------------------------------------------- 1 | package com.xiaoM.Common.Utils; 2 | 3 | import java.io.BufferedWriter; 4 | import java.io.File; 5 | import java.io.FileInputStream; 6 | import java.io.FileNotFoundException; 7 | import java.io.FileOutputStream; 8 | import java.io.IOException; 9 | import java.io.InputStream; 10 | import java.io.OutputStreamWriter; 11 | import java.util.ArrayList; 12 | import java.util.LinkedList; 13 | import java.util.List; 14 | import java.util.Scanner; 15 | 16 | import org.apache.poi.hssf.usermodel.HSSFRow; 17 | import org.apache.poi.hssf.usermodel.HSSFSheet; 18 | import org.apache.poi.hssf.usermodel.HSSFWorkbook; 19 | import org.apache.poi.ss.usermodel.Cell; 20 | import org.apache.poi.ss.usermodel.CellType; 21 | import org.apache.poi.ss.usermodel.Row; 22 | import org.apache.poi.xssf.usermodel.XSSFRow; 23 | import org.apache.poi.xssf.usermodel.XSSFSheet; 24 | import org.apache.poi.xssf.usermodel.XSSFWorkbook; 25 | 26 | import com.xiaoM.Report.utils.TestListener; 27 | 28 | 29 | /** 30 | * IO类 31 | * @author XiaoM 32 | * 33 | */ 34 | public class IOMananger { 35 | /** 36 | * 读取excel 37 | * @param sheetName 38 | * @param path 39 | * @return 40 | * @throws IOException 41 | */ 42 | public static Object[][] readExcelData(String sheetName,String path) throws IOException{ 43 | Object[][] test = null; 44 | String[] xlsx = path.split("\\."); 45 | if(xlsx[1].toString().contains("xlsx")){ 46 | test = readExcelDataXlsx(sheetName, path); 47 | }else{ 48 | test = readExcelDataXls(sheetName, path); 49 | } 50 | return test; 51 | } 52 | /** 53 | * 定位Excel单元格,获取一行内容 54 | * @param sheetName 55 | * @param path 56 | * @return 57 | * @throws IOException 58 | */ 59 | public static List checkExcelData(String cellContent) throws IOException{ 60 | List test = null; 61 | String[] xlsx = TestListener.CasePath.split("\\."); 62 | if(xlsx[1].toString().contains("xlsx")){ 63 | test = checkExcelDataXlsx(cellContent); 64 | }else{ 65 | test = checkExcelDataXls(cellContent); 66 | } 67 | return test; 68 | } 69 | 70 | 71 | 72 | public static Object[][] readExcelDataXlsx(String sheetName,String path) throws IOException { 73 | InputStream is = new FileInputStream(path); 74 | XSSFWorkbook workbook = new XSSFWorkbook(is);//读取Excel 75 | XSSFSheet sheet = workbook.getSheet(sheetName);//读取sheet 76 | if(sheet!=null){ 77 | int lastrowNum = sheet.getLastRowNum()+1;//获取总行数 78 | int collNum = sheet.getRow(0).getLastCellNum();//获取列数 79 | Object[][] user = new Object[lastrowNum][collNum]; 80 | for(int rowNum=0;rowNum checkExcelDataXls( String cellContent) throws IOException { 125 | InputStream is = new FileInputStream(TestListener.CasePath); 126 | List list = new LinkedList(); 127 | HSSFWorkbook workbook = new HSSFWorkbook(is);//读取Excel 128 | HSSFSheet sheet = workbook.getSheet("TestCases");//读取sheet 129 | if(sheet!=null){ 130 | for (Row row : sheet) { 131 | for (Cell cell : row) { 132 | if (cell.getCellType() == Cell.CELL_TYPE_STRING) { 133 | if (cell.getRichStringCellValue().getString().trim().equals(cellContent)) { 134 | for(Cell cell2:row){ 135 | list.add(cell2.toString()); 136 | } 137 | workbook.close(); 138 | return list; 139 | } 140 | } 141 | } 142 | } 143 | } 144 | workbook.close(); 145 | return null; 146 | } 147 | 148 | public static List checkExcelDataXlsx( String cellContent) throws IOException { 149 | InputStream is = new FileInputStream(TestListener.CasePath); 150 | List list = new LinkedList(); 151 | XSSFWorkbook workbook = new XSSFWorkbook(is);//读取Excel 152 | XSSFSheet sheet = workbook.getSheet("TestCases");//读取sheet 153 | if(sheet!=null){ 154 | for (Row row : sheet) { 155 | for (Cell cell : row) { 156 | if (cell.getCellTypeEnum() == CellType.STRING) { 157 | if (cell.getRichStringCellValue().getString().trim().equals(cellContent)) { 158 | for(Cell cell2:row){ 159 | list.add(cell2.toString()); 160 | } 161 | workbook.close(); 162 | return list; 163 | } 164 | } 165 | } 166 | } 167 | } 168 | workbook.close(); 169 | return null; 170 | } 171 | 172 | public static Object[][] runTime(String sheetName,String path) throws IOException{ 173 | Object[][] Date = readExcelData(sheetName,path); 174 | List caseType = new LinkedList(); 175 | List caseName = new LinkedList(); 176 | for(int i=1;i readTxtFile(String filePath) throws FileNotFoundException{ 236 | List txt = new ArrayList(); 237 | Scanner in = new Scanner(new File(filePath)); 238 | while(in.hasNext()){ 239 | String str=in.nextLine(); 240 | if(!str.isEmpty()){ 241 | txt.add(str.toString()); 242 | } 243 | } 244 | in.close(); 245 | return txt; 246 | } 247 | 248 | /** 249 | * 删除文件夹下的所有文件 250 | * @param oldPath 251 | */ 252 | public static void deleteFile(File Path) { 253 | if (Path.isDirectory()) { 254 | File[] files = Path.listFiles(); 255 | for (File file : files) { 256 | deleteFile(file); 257 | } 258 | }else{ 259 | Path.delete(); 260 | } 261 | } 262 | /** 263 | *判断电脑本地是否存在某文件,存在则删除 264 | * @param path 文件路径 265 | * @return 266 | */ 267 | public static void deleteFile(String[] Paths){ 268 | for(String path:Paths){ 269 | try { 270 | File file = new File(path); 271 | if(file.exists()){ 272 | file.delete(); 273 | } 274 | } catch (Exception e) { 275 | System.out.println("删除文件失败,文件路径:"+ path); 276 | } 277 | } 278 | } 279 | /** 280 | * 测试日志分类 281 | * @param workSpase 282 | * @param data 283 | * @throws IOException 284 | */ 285 | public static void DealwithLog(String workSpase,String date) throws IOException{ 286 | String logPath = workSpase+"/test-output/log/log_"+date+".log"; 287 | String logDriverPath = workSpase+"/test-output/log/DevicesRunLog"; 288 | ListlogDrivers = IOMananger.readTxtFile(logPath); 289 | String[] devices = TestListener.Devices.split(","); 290 | for(String device:devices){ 291 | String logDevicePath = logDriverPath+"/"+device+".txt"; 292 | File file = new File(logDevicePath); 293 | if(file.exists()){ 294 | file.delete(); 295 | } 296 | for(int i=0;ilogDrivers = IOMananger.readTxtFile(logPath); 313 | String isDevice = DeviceName; 314 | int start = 0; 315 | for(String logDriver:logDrivers){ 316 | if(logDriver.contains(isDevice+" 执行用例:"+CaseName)){ 317 | start++; 318 | break; 319 | } 320 | start++; 321 | } 322 | String txtName = isDevice+"_"+CaseName; 323 | String logtxtNamePath = logDriverPath+"/"+txtName+".txt"; 324 | File file = new File(logtxtNamePath); 325 | if(file.exists()){ 326 | file.delete(); 327 | } 328 | for(int i=start-1;ilogDrivers = IOMananger.readTxtFile(logPath); 345 | String isDevice = DeviceName; 346 | int start = 0; 347 | for(String logDriver:logDrivers){ 348 | if(logDriver.contains(isDevice+" 执行用例:"+CaseName)){ 349 | start++; 350 | break; 351 | } 352 | start++; 353 | } 354 | String txtName = isDevice+"_"+CaseName; 355 | String logtxtNamePath = logDriverPath+"/"+txtName+".txt"; 356 | File file = new File(logtxtNamePath); 357 | if(file.exists()){ 358 | file.delete(); 359 | } 360 | for(int i=start-1;i clazz; 18 | private Logger logger; 19 | static String projectRootPath = new File(System.getProperty("user.dir")).getPath().concat("/"); 20 | static String src="test-output/log"; 21 | //设置日期格式 22 | static SimpleDateFormat dateFormat=new SimpleDateFormat("yyyy-MM-dd"); 23 | //获取当前日期 24 | static String date=dateFormat.format(new Date()).toString(); 25 | 26 | /** 27 | * 28 | * @param clazz 获取当前类 29 | */ 30 | public Log(Class clazz){ 31 | this.clazz=clazz; 32 | //Logger.getLogger的方法是调用的是LogManager.getLogger()方法,所以这两个方法都是返回logger 33 | this.logger=Logger.getLogger(this.clazz); 34 | Log.initlog4j(); 35 | } 36 | //初始化log4j,设置log4j的配置文件log4j.Properties 37 | private static void initlog4j(){ 38 | //创建Propderties对象 39 | Properties prop=new Properties(); 40 | /*Log4j建议只使用四个级别,优先级从高到低分别是ERROR、WARN、INFO、DEBUG 41 | 这里定义能显示到的最低级别,若定义到INFO级别,则看不到DEBUG级别的信息了~!级别后面是输出端*/ 42 | prop.setProperty("log4j.rootLogger", "INFO,CONSOLE,E,F"); 43 | prop.setProperty("log4j.appender.CONSOLE", "org.apache.log4j.ConsoleAppender"); 44 | prop.setProperty("log4j.appender.CONSOLE.layout", "org.apache.log4j.PatternLayout"); 45 | prop.setProperty("log4j.appender.CONSOLE.layout.ConversionPattern", "[%d{YYYY-MM-dd HH:mm:ss,SSS}] %-5p %c %m%n"); 46 | File dir = new File(projectRootPath+src); 47 | String filepath=dir.getAbsolutePath()+"/"+"log_"+date+".log"; 48 | 49 | prop.setProperty("log4j.appender.E","org.apache.log4j.FileAppender"); 50 | prop.setProperty("log4j.appender.E.file",filepath); 51 | prop.setProperty("log4j.appender.E.layout","org.apache.log4j.PatternLayout"); 52 | prop.setProperty("log4j.appender.E.layout.ConversionPattern", "[%d{YYYY-MM-dd HH:mm:ss,SSS}] %-5p %c %m%n"); 53 | 54 | prop.setProperty("log4j.appender.F","org.apache.log4j.FileAppender"); 55 | 56 | String filepathHtml=dir.getAbsolutePath()+"/"+"log_"+date+".html"; 57 | prop.setProperty("log4j.appender.F.file",filepathHtml); 58 | prop.setProperty("log4j.appender.F.layout","org.apache.log4j.HTMLLayout"); 59 | //prop.setProperty("log4j.appender.F.layout.ConversionPattern", "[%d{YYYY-MM-dd HH:mm:ss,SSS}] %-5p %c %m%n"); 60 | 61 | PropertyConfigurator.configure(prop); 62 | } 63 | public void info(String message){ 64 | logger.info(message); 65 | } 66 | public void warn(String message){ 67 | logger.warn(message); 68 | } 69 | public void error(String message){ 70 | logger.error(message); 71 | } 72 | public void debug(String message){ 73 | logger.debug(message); 74 | } 75 | 76 | } 77 | 78 | -------------------------------------------------------------------------------- /src/main/java/com/xiaoM/Common/Utils/Run.java: -------------------------------------------------------------------------------- 1 | package com.xiaoM.Common.Utils; 2 | 3 | import java.net.URL; 4 | 5 | import com.xiaoM.Report.utils.TestListener; 6 | import com.xiaoM.appium.utils.AppiumElementAction; 7 | import com.xiaoM.appium.utils.AppiumResourceMonitoring; 8 | import com.xiaoM.appium.utils.AppiumScreenShot; 9 | 10 | import io.appium.java_client.AppiumDriver; 11 | import io.appium.java_client.MobileElement; 12 | 13 | public class Run extends BaseDriver { 14 | public Log log=new Log(this.getClass()); 15 | AppiumDriver driver ; 16 | String driverName = null; 17 | public URL url; 18 | public void testCase(String CaseType,String CaseName,String device) throws Exception { 19 | String devicesPath =null; 20 | if(TestListener.DeviceType.equals("Android")){ 21 | devicesPath = TestListener.ProjectPath+"/devices/AndroidDevices.xlsx";//选择设备 22 | }else{ 23 | devicesPath = TestListener.ProjectPath+"/devices/iOSDevices.xlsx";//选择设备 24 | } 25 | try { 26 | switch (CaseType) { 27 | case "Wap": 28 | driver = setUpWap(device,devicesPath); 29 | break; 30 | default: 31 | driver = setUpApp(device,devicesPath); 32 | break; 33 | } 34 | Object[][] testBase = IOMananger.readExcelData(device,devicesPath); 35 | driverName = testBase[1][2].toString(); 36 | String deviceId =testBase[4][2].toString(); 37 | String sdkVersion = testBase[6][2].toString(); 38 | log.info("设备: "+driverName+" 执行用例:"+CaseName); 39 | Object[][] testStart = IOMananger.readExcelData(CaseName,TestListener.CasePath); 40 | if(CaseType.equals("RM")){ 41 | AppiumResourceMonitoring RM = new AppiumResourceMonitoring(); 42 | RM.driverStartApp(driver, deviceId, driverName); 43 | for(int a=1;a 数组对象集 288 | */ 289 | public static List executeQuery2(String sql){ 290 | List objLists=new ArrayList(); 291 | conn = getConnection(); 292 | try { 293 | Statement stmt = conn.createStatement(); 294 | rs = stmt.executeQuery(sql); 295 | ResultSetMetaData rmd = rs.getMetaData(); 296 | int colCount=rmd.getColumnCount(); 297 | while(rs.next()){ 298 | Object [] objData= new Object[colCount]; 299 | for(int i=0;i devices; 9 | static String device; 10 | 11 | public static synchronized String Device (){ 12 | devices = TestListener.deviceLists; 13 | device = devices.get(0); 14 | devices.remove(0); 15 | return device; 16 | } 17 | } 18 | -------------------------------------------------------------------------------- /src/main/java/com/xiaoM/ExecuteScript/ExecuteScript.java: -------------------------------------------------------------------------------- 1 | package com.xiaoM.ExecuteScript; 2 | 3 | import java.lang.reflect.InvocationTargetException; 4 | import java.util.HashMap; 5 | 6 | import org.openqa.selenium.JavascriptExecutor; 7 | 8 | import io.appium.java_client.AppiumDriver; 9 | import io.appium.java_client.MobileElement; 10 | 11 | public class ExecuteScript { 12 | public AppiumDriver driver; 13 | 14 | public ExecuteScript(){ 15 | } 16 | 17 | public ExecuteScript(AppiumDriver driver) { 18 | this.driver = driver; 19 | } 20 | /** 21 | * 执行指定的方法 22 | * @param MethodName 方法名 23 | */ 24 | public void doRun(String MethodName){ 25 | Class< ? extends ExecuteScript> run = this.getClass(); 26 | try { 27 | run.getMethod(MethodName).invoke(this); 28 | } catch (IllegalArgumentException e) { 29 | e.printStackTrace(); 30 | } catch (SecurityException e) { 31 | e.printStackTrace(); 32 | } catch (IllegalAccessException e) { 33 | e.printStackTrace(); 34 | } catch (InvocationTargetException e) { 35 | e.printStackTrace(); 36 | } catch (NoSuchMethodException e) { 37 | e.printStackTrace(); 38 | } 39 | } 40 | 41 | public void Demo(){ 42 | System.out.println("********************"); 43 | System.out.println("* test *"); 44 | System.out.println("********************"); 45 | } 46 | 47 | public void iosDemo(){ 48 | JavascriptExecutor js = (JavascriptExecutor) driver; 49 | HashMap scrollObject = new HashMap(); 50 | scrollObject.put("direction", "down"); 51 | js.executeScript("mobile: scroll", scrollObject); 52 | 53 | } 54 | @SuppressWarnings("deprecation") 55 | public void iosDemo2(){ 56 | driver.tap(1, 8, 349, 1000);//坐标点击 57 | } 58 | 59 | } 60 | -------------------------------------------------------------------------------- /src/main/java/com/xiaoM/OpenCV/JavaCVTest.java: -------------------------------------------------------------------------------- 1 | package com.xiaoM.OpenCV; 2 | 3 | import static org.bytedeco.javacpp.opencv_imgcodecs.cvLoadImage; 4 | 5 | public class JavaCVTest { 6 | 7 | public static void main(String[] args) { 8 | System.out.println("START..."); 9 | TemplateMatch tm = new TemplateMatch();//实例化TemplateMatch对象 10 | tm.load("img/585.png");//加载带比对图片,注此图片必须小于源图 11 | boolean result = tm.matchTemplate(cvLoadImage("img/58home.png"));//校验585.png是否包含于原图58home.png 12 | if (result){//打印匹配结果,boolean 13 | System.out.println("match"); 14 | }else{ 15 | System.out.println("un-match"); 16 | } 17 | System.out.println("END..."); 18 | } 19 | } -------------------------------------------------------------------------------- /src/main/java/com/xiaoM/OpenCV/TemplateMatch.java: -------------------------------------------------------------------------------- 1 | package com.xiaoM.OpenCV; 2 | 3 | 4 | import static org.bytedeco.javacpp.opencv_core.*; 5 | import static org.bytedeco.javacpp.opencv_imgcodecs.cvLoadImage; 6 | import static org.bytedeco.javacpp.opencv_imgproc.CV_TM_CCORR_NORMED; 7 | import static org.bytedeco.javacpp.opencv_imgproc.cvMatchTemplate; 8 | 9 | import org.bytedeco.javacpp.opencv_core.IplImage; 10 | import org.bytedeco.javacpp.opencv_core; 11 | 12 | 13 | public class TemplateMatch { 14 | 15 | private opencv_core.IplImage image; 16 | 17 | public void load(String filename) { 18 | image = cvLoadImage(filename); 19 | } 20 | 21 | public boolean matchTemplate(IplImage source) { 22 | boolean matchRes; 23 | IplImage result = cvCreateImage(opencv_core.cvSize( 24 | source.width() - this.image.width() + 1, 25 | source.height() - this.image.height() + 1), 26 | opencv_core.IPL_DEPTH_32F, 1); 27 | 28 | opencv_core.cvZero(result); 29 | cvMatchTemplate(source, this.image, result, CV_TM_CCORR_NORMED); 30 | double[] minVal = new double[2]; 31 | double[] maxVal = new double[2]; 32 | cvMinMaxLoc(result, minVal, maxVal); 33 | matchRes = maxVal[0] > 0.99f ? true : false; 34 | cvReleaseImage(result);//释放图像 35 | return matchRes; 36 | } 37 | } 38 | -------------------------------------------------------------------------------- /src/main/java/com/xiaoM/Report/utils/TestListener.java: -------------------------------------------------------------------------------- 1 | package com.xiaoM.Report.utils; 2 | 3 | import java.io.File; 4 | import java.io.FileInputStream; 5 | import java.io.IOException; 6 | import java.io.InputStreamReader; 7 | import java.util.ArrayList; 8 | import java.util.List; 9 | import java.util.Properties; 10 | 11 | import org.testng.TestListenerAdapter; 12 | 13 | import com.xiaoM.Common.Utils.IOMananger; 14 | import com.xiaoM.Common.Utils.Log; 15 | import com.xiaoM.appium.utils.AppiumComm; 16 | 17 | import io.appium.java_client.service.local.AppiumServiceBuilder; 18 | 19 | public class TestListener extends TestListenerAdapter{ 20 | Log log= new Log(this.getClass()); 21 | public static List messageList=new ArrayList(); 22 | public static List screenMessageList=new ArrayList(); 23 | public static List screenResourceList=new ArrayList(); 24 | public static List ResourceList=new ArrayList(); 25 | public static List mobileSuccessMessageList=new ArrayList(); 26 | public static List deviceLists=new ArrayList(); 27 | public static List FailCasesName=new ArrayList();//失败用例的CaseName 28 | public static String TestCase;//测试用例 29 | public static String ProjectPath;//工程路径 30 | public static String CasePath;//TestCase路径 31 | public static String appName;//测试应用 32 | public static String PackageName; 33 | public static String Activity; 34 | public static String bundleId; 35 | public static String ReportTile;//测试报告主题 36 | public static String Devices;//测试设备 37 | public static String TestType;//测试类型 38 | public static Object[][] RunCase;//执行测试case 39 | public static String DeviceType;//设备类型 40 | public static String ResetApp;//是否重置应用 41 | //配置初始化 42 | static{ 43 | Properties pp = new Properties(); 44 | try { 45 | InputStreamReader reader = new InputStreamReader(new FileInputStream("config.properties"),"UTF-8"); 46 | pp.load(reader); 47 | } catch (Exception e) { 48 | e.printStackTrace(); 49 | } 50 | String os = System.getProperty("os.name"); 51 | if(os.contains("Mac")){ 52 | String appiumPath = pp.getProperty("APPIUM_JS_PATH"); 53 | String adbPath = pp.getProperty("ADB_PATH"); 54 | System.setProperty(AppiumServiceBuilder.APPIUM_PATH , appiumPath); 55 | AppiumComm.adb = adbPath; 56 | } 57 | Devices = pp.getProperty("DEVICES"); 58 | String[] devices = Devices.split(","); 59 | for(String device:devices){ 60 | deviceLists.add(device); 61 | } 62 | ProjectPath = pp.getProperty("WORKSPAC_PATH"); 63 | TestCase = pp.getProperty("TESTCASE"); 64 | CasePath = ProjectPath +"/testcase/"+ TestCase; 65 | ReportTile = pp.getProperty("REPORT_TITLE"); 66 | DeviceType = pp.getProperty("DEVICE_TYPE"); 67 | ResetApp = pp.getProperty("NORESET_APP"); 68 | if(DeviceType.equals("Android")){ 69 | appName = pp.getProperty("APP_NAME"); 70 | PackageName = pp.getProperty("PACKAGE_NAME"); 71 | Activity = pp.getProperty("ACTIVITY"); 72 | }else{ 73 | bundleId = pp.getProperty("BUNDIEID"); 74 | } 75 | TestType = pp.getProperty("TEST_TYPE"); 76 | String logPath = ProjectPath+"/test-output/log/"; 77 | File path = new File(logPath); 78 | IOMananger.deleteFile(path);//删除日志文件 79 | try { 80 | RunCase = IOMananger.runTime("TestCases", CasePath); 81 | } catch (IOException e) { 82 | e.printStackTrace(); 83 | } 84 | } 85 | } 86 | -------------------------------------------------------------------------------- /src/main/java/com/xiaoM/Report/utils/TestReport.java: -------------------------------------------------------------------------------- 1 | package com.xiaoM.Report.utils; 2 | 3 | import java.io.IOException; 4 | import java.text.SimpleDateFormat; 5 | import java.util.Calendar; 6 | import java.util.Date; 7 | import java.util.List; 8 | import java.util.Map; 9 | 10 | import org.testng.IReporter; 11 | import org.testng.IResultMap; 12 | import org.testng.ISuite; 13 | import org.testng.ISuiteResult; 14 | import org.testng.ITestContext; 15 | import org.testng.ITestResult; 16 | import org.testng.Reporter; 17 | import org.testng.xml.XmlSuite; 18 | 19 | import com.aventstack.extentreports.ExtentReports; 20 | import com.aventstack.extentreports.ExtentTest; 21 | import com.aventstack.extentreports.MediaEntityBuilder; 22 | import com.aventstack.extentreports.Status; 23 | import com.aventstack.extentreports.reporter.ExtentHtmlReporter; 24 | import com.aventstack.extentreports.reporter.configuration.Theme; 25 | import com.xiaoM.Common.Utils.IOMananger; 26 | import com.xiaoM.appium.utils.AppiumComm; 27 | 28 | public class TestReport implements IReporter { 29 | 30 | private static final String OUTPUT_FOLDER = "test-output/"; 31 | private static final String FILE_NAME = "TestReport.html"; 32 | private ExtentReports extent; 33 | 34 | @Override 35 | public void generateReport(List xmlSuites, List suites, String outputDirectory) { 36 | SimpleDateFormat dateFormat=new SimpleDateFormat("yyyy-MM-dd");//设置日期格式 37 | String date=dateFormat.format(new Date()).toString();//获取当前日期 38 | try { 39 | IOMananger.DealwithLog(TestListener.ProjectPath, date); 40 | } catch (IOException e) { 41 | } 42 | init(TestListener.ReportTile);//html文件配置 43 | for (ISuite suite : suites) { 44 | Map result = suite.getResults(); 45 | for (ISuiteResult r : result.values()) { 46 | ITestContext context = r.getTestContext(); 47 | buildTestNodes(context.getFailedTests(), Status.FAIL); 48 | /* 49 | * 需要展示Skip用例执行该行代码 50 | * buildTestNodes(context.getSkippedTests(), Status.SKIP); 51 | */ 52 | buildTestNodes(context.getPassedTests(), Status.PASS); 53 | 54 | } 55 | } 56 | for (String s : Reporter.getOutput()) { 57 | extent.setTestRunnerOutput(s); 58 | } 59 | extent.flush(); 60 | } 61 | private void init(String ReportName) { 62 | ExtentHtmlReporter htmlReporter = new ExtentHtmlReporter(OUTPUT_FOLDER + FILE_NAME); 63 | htmlReporter.config().setDocumentTitle("AppiumXM designed by xiaoM");//html标题 64 | htmlReporter.config().setReportName(ReportName);//报告主题 65 | htmlReporter.config().setTheme(Theme.STANDARD);//主题:黑/白 66 | htmlReporter.config().setEncoding("utf-8"); 67 | extent = new ExtentReports(); 68 | extent.attachReporter(htmlReporter); 69 | extent.setSystemInfo("OS", System.getProperty("os.name")); 70 | extent.setSystemInfo("User Name",System.getProperty("user.name")); 71 | extent.setSystemInfo("Java Version", System.getProperty("java.version")); 72 | String AppiumVersion = null; 73 | try { 74 | AppiumVersion = AppiumComm.getAppiumVersion(); 75 | } catch (IOException e) { 76 | e.printStackTrace(); 77 | } 78 | extent.setSystemInfo("Appium Version", AppiumVersion); 79 | extent.setReportUsesManualConfiguration(true); 80 | } 81 | private void buildTestNodes(IResultMap tests, Status status) { 82 | 83 | String DeviceName = null; 84 | if (tests.size() > 0) { 85 | int i = 0; 86 | int j = 0; 87 | for (ITestResult result : tests.getAllResults()) { 88 | switch (result.getStatus()) { 89 | case 1://成功用例 90 | if(TestListener.TestType.equals("RM")){ 91 | DeviceName = TestListener.mobileSuccessMessageList.get(i); 92 | ExtentTest test = extent.createTest(DeviceName); 93 | test.assignCategory(DeviceName.split("-")[0]); 94 | test.log(status, TestListener.ResourceList.get(i).split(":::")[1]); 95 | String logName = DeviceName.split("\\(")[0]; 96 | test.getModel().setStartTime(getTime(result.getStartMillis())); 97 | test.getModel().setEndTime(getTime(result.getEndMillis())); 98 | test.log(status, "执行日志"); 99 | try { 100 | test.addScreenCaptureFromPath("../test-output/snapshot/"+logName+"_CPU.jpg"); 101 | test.addScreenCaptureFromPath("../test-output/snapshot/"+logName+"_Men.jpg"); 102 | } catch (IOException e1) { 103 | e1.printStackTrace(); 104 | } 105 | }else{ 106 | DeviceName = TestListener.mobileSuccessMessageList.get(i); 107 | ExtentTest test = extent.createTest(DeviceName); 108 | String DeviceName2 = DeviceName.split("\\(")[0]; 109 | String CaseName1 = DeviceName.split("\\)-")[1]; 110 | String txtName1 = DeviceName2+"_"+CaseName1; 111 | test.assignCategory(DeviceName.split("-")[0]); 112 | test.getModel().setStartTime(getTime(result.getStartMillis())); 113 | test.getModel().setEndTime(getTime(result.getEndMillis())); 114 | try { 115 | IOMananger.DealwithSuRunLog(TestListener.ProjectPath,DeviceName2, CaseName1); 116 | } catch (IOException e1) { 117 | e1.printStackTrace(); 118 | } 119 | test.log(status, "执行日志"); 120 | test.log(status, "Test " + status.toString().toLowerCase() + "ed"); 121 | } 122 | i++; 123 | break; 124 | case 2://失败用例 125 | String[] testDevice = TestListener.messageList.get(j).split(":::");//获取设备测试资料 126 | String CaseName2 = TestListener.FailCasesName.get(j); 127 | DeviceName = testDevice[0].split("\\(")[0]; 128 | String txtName2 = DeviceName+"_"+CaseName2; 129 | ExtentTest test = extent.createTest(testDevice[0]+"-"+CaseName2); 130 | test.assignCategory(testDevice[0]); 131 | test.getModel().setStartTime(getTime(result.getStartMillis())); 132 | test.getModel().setEndTime(getTime(result.getEndMillis())); 133 | try { 134 | IOMananger.DealwithFailRunLog(TestListener.ProjectPath,DeviceName, CaseName2);//只处理执行失败的用例对应的设备运行日志 135 | test.fail("报错截图:",MediaEntityBuilder.createScreenCaptureFromPath(TestListener.screenMessageList.get(j)).build()); 136 | } catch (IOException e) { 137 | e.printStackTrace(); 138 | } 139 | test.log(status, "执行日志"); 140 | test.log(status, testDevice[1]); //添加自定义报错 141 | test.log(status, result.getThrowable()); //testng捕抓报错 142 | j++; 143 | break; 144 | } 145 | 146 | } 147 | 148 | } 149 | } 150 | 151 | private Date getTime(long millis) { 152 | Calendar calendar = Calendar.getInstance(); 153 | calendar.setTimeInMillis(millis); 154 | return calendar.getTime(); 155 | } 156 | } 157 | -------------------------------------------------------------------------------- /src/main/java/com/xiaoM/appium/utils/AppiumComm.java: -------------------------------------------------------------------------------- 1 | package com.xiaoM.appium.utils; 2 | 3 | import java.io.BufferedReader; 4 | import java.io.IOException; 5 | import java.io.InputStreamReader; 6 | import java.util.ArrayList; 7 | import java.util.List; 8 | import java.util.Scanner; 9 | 10 | import com.xiaoM.Common.Utils.IOMananger; 11 | import com.xiaoM.Report.utils.TestListener; 12 | 13 | public class AppiumComm { 14 | public static String adb = "adb"; 15 | 16 | /** 17 | * 获得应用的UID 18 | * @param appPackage应用包名 19 | * @return UID 20 | * @throws IOException 21 | */ 22 | public static String getMobileAppUid(String appPackage,String device) throws IOException { 23 | //CMD 上运行需要改为adb shell "dumpsys package com.netease.mail | grep userId=" 24 | ProcessBuilder pb = new ProcessBuilder(adb, "-s",device ,"shell", "dumpsys package " + appPackage + " | grep userId="); 25 | try { 26 | Process p = pb.start(); 27 | String line = null; 28 | BufferedReader bf = new BufferedReader(new InputStreamReader(p.getInputStream(), "utf-8")); 29 | if ((line = bf.readLine()) != null) { 30 | return line.split("=")[1].split(" ")[0]; // 分割字符串 31 | } 32 | } catch (IOException e) { 33 | e.printStackTrace(); 34 | } 35 | return null; 36 | } 37 | /** 38 | * 获取应用流量数据 39 | * @param appPackage 应用包名 40 | * @return 上下行流量数据 41 | */ 42 | public static void getMobileAppNet(String appPackage,String device,String driverName) throws IOException { 43 | String line = null; 44 | String[] splitArr = null; 45 | String uid = getMobileAppUid(appPackage,device); 46 | ProcessBuilder pb1 = new ProcessBuilder(adb, "-s",device,"shell", "cat /proc/net/xt_qtaguid/stats" + " | grep " + uid); 47 | try { 48 | Process p = pb1.start(); 49 | BufferedReader bf = new BufferedReader(new InputStreamReader(p.getInputStream(), "utf-8")); 50 | int rx = 0; 51 | int st = 0; 52 | while ((line = bf.readLine()) != null) { 53 | splitArr = line.split(" "); 54 | for (int x = 0; x < splitArr.length; x++) { 55 | if (x == 5) { 56 | rx += Integer.parseInt(splitArr[x]);//下行数据 57 | } 58 | if (x == 7) { 59 | st += Integer.parseInt(splitArr[x]); 60 | } 61 | } 62 | } 63 | String workSpace = TestListener.ProjectPath+"/test-output/MonitorResoure/Net"; 64 | IOMananger.saveToFile(workSpace, driverName, String.valueOf(rx)); 65 | IOMananger.saveToFile(workSpace, driverName, String.valueOf(st)); 66 | } catch (IOException e) { 67 | e.printStackTrace(); 68 | } 69 | } 70 | /** 71 | * 强制停止应用 72 | * @param appPackage 包名 73 | * @throws IOException 74 | */ 75 | public static void forceStop(String appPackage,String device) throws IOException { 76 | ProcessBuilder pb = new ProcessBuilder(adb,"-s",device, "shell", "am force-stop " + appPackage); 77 | try { 78 | pb.start(); 79 | } catch (Exception e) { 80 | e.printStackTrace(); 81 | } 82 | } 83 | /** 84 | * 清除缓存 85 | * 86 | * @param appPackage 87 | * @return 88 | */ 89 | public static void adbClearCache(String appPackage,String device) { 90 | ProcessBuilder pb1 = new ProcessBuilder(adb,"-s",device,"shell", "pm", "clear", appPackage); 91 | try { 92 | pb1.start(); 93 | } catch (IOException e) { 94 | e.printStackTrace(); 95 | } 96 | } 97 | /** 98 | * 启动app 99 | * @param appPackageActivity 100 | * @param device 101 | */ 102 | public static void adbStartAPP(String appPackageActivity,String device) { 103 | ProcessBuilder pb1 = new ProcessBuilder(adb,"-s",device, "shell", "am", "start", "-n", appPackageActivity); 104 | try { 105 | pb1.start(); 106 | } catch (IOException e1) { 107 | e1.printStackTrace(); 108 | } 109 | } 110 | /** 111 | * app启动时延 112 | * @param cpuList 113 | * @return 114 | */ 115 | public static String appLuanchTime(String appPackageActivity,String device){ 116 | ProcessBuilder pb1 = new ProcessBuilder(adb,"-s",device, "shell", "am", "start", "-W","-n", appPackageActivity); 117 | List results = new ArrayList(); 118 | String Time =null; 119 | try { 120 | Process process =pb1.start(); 121 | Scanner scanner = new Scanner(process.getInputStream()); 122 | while (scanner.hasNextLine()) { 123 | results.add(scanner.nextLine().toString()); 124 | } 125 | scanner.close(); 126 | for(String result:results){ 127 | if(result.contains("ThisTime:")){ 128 | Time = result.split(": ")[1]; 129 | } 130 | } 131 | return Time; 132 | } catch (IOException e1) { 133 | e1.printStackTrace(); 134 | } 135 | return null; 136 | } 137 | 138 | public static int cpuMaxComp(List cpuList) { 139 | int cpuMax = 0;// 计算最大值 140 | int cpu; 141 | for (int i = 0; i < cpuList.size(); i++) { 142 | cpu = cpuList.get(i); 143 | if (cpu > cpuMax) { 144 | cpuMax = cpu; 145 | } 146 | } 147 | return cpuMax; 148 | } 149 | public static int cpuAvg(List cpuList) { 150 | int cpuAvg = 0;// 计算均值 151 | int cpuSum = 0; 152 | for(int cpu:cpuList){ 153 | cpuSum = cpuSum + cpu; 154 | } 155 | cpuAvg = Integer.valueOf(cpuSum/cpuList.size()); 156 | return cpuAvg; 157 | } 158 | public static Double memMaxComp(List menList) { 159 | double memMax = 0; 160 | double men; 161 | for (int i = 0; i < menList.size(); i++) { 162 | men = menList.get(i); 163 | if (men > memMax) { 164 | memMax = men; 165 | } 166 | } 167 | return memMax; 168 | } 169 | public static Double menAvg(List menList) { 170 | Double menAvg = 0.0;// 计算均值 171 | Double menSum = 0.0; 172 | for(Double men:menList){ 173 | menSum = menSum + men; 174 | } 175 | menAvg = menSum/menList.size(); 176 | return menAvg; 177 | } 178 | public static String getAppiumVersion() throws IOException{ 179 | Process process = null; 180 | if(System.getProperty("os.name").contains("Mac")){ 181 | ProcessBuilder pb1 = new ProcessBuilder("appium","-v"); 182 | process = pb1.start(); 183 | }else{ 184 | Runtime runtime=Runtime.getRuntime(); 185 | process = runtime.exec("cmd /c appium -v"); 186 | } 187 | Scanner scanner = new Scanner(process.getInputStream()); 188 | String result = null; 189 | while (scanner.hasNextLine()) { 190 | result = scanner.nextLine().toString(); 191 | } 192 | scanner.close(); 193 | return result; 194 | } 195 | 196 | public static void main(String[] args) throws IOException { 197 | System.out.println(getAppiumVersion()); 198 | } 199 | // public static void main(String[] args) { 200 | // System.out.println(appLuanchTime("com.netease.mail/com.netease.mobimail.activity.LaunchActivity","192.168.1.103:5555")); 201 | // } 202 | } 203 | -------------------------------------------------------------------------------- /src/main/java/com/xiaoM/appium/utils/AppiumElementAction.java: -------------------------------------------------------------------------------- 1 | package com.xiaoM.appium.utils; 2 | 3 | import java.util.ArrayList; 4 | import java.util.Arrays; 5 | import java.util.HashMap; 6 | import java.util.List; 7 | import java.util.Map; 8 | import java.util.Set; 9 | import java.util.regex.Matcher; 10 | import java.util.regex.Pattern; 11 | 12 | import org.openqa.selenium.By; 13 | import org.openqa.selenium.WebDriver; 14 | import org.openqa.selenium.WebElement; 15 | import org.openqa.selenium.support.ui.ExpectedCondition; 16 | import org.openqa.selenium.support.ui.Select; 17 | import org.openqa.selenium.support.ui.WebDriverWait; 18 | 19 | import com.xiaoM.Common.Utils.Assertion; 20 | import com.xiaoM.Common.Utils.IOMananger; 21 | import com.xiaoM.Common.Utils.Log; 22 | import com.xiaoM.ExecuteScript.ExecuteScript; 23 | import com.xiaoM.Report.utils.TestListener; 24 | 25 | import io.appium.java_client.AppiumDriver; 26 | import io.appium.java_client.MobileElement; 27 | import io.appium.java_client.TouchAction; 28 | 29 | /** 30 | * 解析操作核心类 31 | * @author XiaoM 32 | * 33 | */ 34 | public class AppiumElementAction{ 35 | 36 | public Log log=new Log(this.getClass()); 37 | private static Map map = new HashMap(); 38 | public WebElement getElement(AppiumDriver driver,Object[] locator,String driverName){ 39 | String[] control = locator[5].toString().split("::"); 40 | log.info("设备: "+driverName+" "+"查找元素:"+locator[3].toString()+" 方式 : "+control[0]+" [ "+control[1]+" ]"); 41 | WebElement webElement; 42 | switch (control[0]){ 43 | case "ByXpath" : 44 | webElement=driver.findElement(By.xpath(control[1])); 45 | break; 46 | case "ById": 47 | webElement=driver.findElement(By.id(control[1])); 48 | break; 49 | case "ByCssSelector": 50 | webElement=driver.findElement(By.cssSelector(control[1])); 51 | break; 52 | case "ByName": 53 | webElement=driver.findElement(By.name(control[1])); 54 | break; 55 | case "ByClassName": 56 | webElement=driver.findElement(By.className(control[1])); 57 | break; 58 | case "ByLinkText": 59 | webElement=driver.findElement(By.linkText(control[1])); 60 | break; 61 | case "ByPartialLinkText": 62 | webElement=driver.findElement(By.partialLinkText(control[1])); 63 | break; 64 | case "ByTagName": 65 | webElement=driver.findElement(By.tagName(control[1])); 66 | break; 67 | default : 68 | webElement=driver.findElement(By.xpath(control[1])); 69 | break; 70 | 71 | } 72 | return webElement; 73 | } 74 | 75 | /* *//** 76 | * 通过定位信息获取一组元素 77 | * @param locator 元素locator 78 | * @return 返回 WebElement 79 | * @throws NoSuchElementException 找不到元素异常 80 | *//* 81 | public List getElements(AppiumDriver driver,Object[] locator,String driverName){ 82 | String[] control = locator[5].toString().split("::"); 83 | log.info("设备: "+driverName+" "+"查找元素:"+locator[3].toString()+" 方式 : "+control[0]+" [ "+control[1]+" ]"); 84 | List webElements; 85 | switch (control[0]){ 86 | case "ByXpath" : 87 | webElements=driver.findElements(By.xpath(control[1])); 88 | break; 89 | case "ById": 90 | webElements=driver.findElements(By.id(control[1])); 91 | break; 92 | case "ByCssSelector": 93 | webElements=driver.findElements(By.cssSelector(control[1])); 94 | break; 95 | case "ByName": 96 | webElements=driver.findElements(By.name(control[1])); 97 | break; 98 | case "ByClassName": 99 | webElements=driver.findElements(By.className(control[1])); 100 | break; 101 | case "ByLinkText": 102 | webElements=driver.findElements(By.linkText(control[1])); 103 | break; 104 | case "ByPartialLinkText": 105 | webElements=driver.findElements(By.partialLinkText(control[1])); 106 | break; 107 | case "ByTagName": 108 | webElements=driver.findElements(By.tagName(control[1])); 109 | break; 110 | default : 111 | webElements=driver.findElements(By.xpath(control[1])); 112 | break; 113 | } 114 | return webElements; 115 | }*/ 116 | 117 | /** 118 | * 等待元素出现 119 | * @param by 120 | * @param timeout 121 | * @return 122 | */ 123 | public WebElement waitForElement(final AppiumDriver driver,final Object[] locator,final String driverName,String sdkVersion){ 124 | WebElement webElement=null; 125 | int i = Integer.valueOf(locator[6].toString()); 126 | try { 127 | webElement=(new WebDriverWait(driver, i)).until( 128 | new ExpectedCondition() { 129 | @Override 130 | public WebElement apply(WebDriver dr) { 131 | return getElement(driver,locator,driverName); 132 | } 133 | }); 134 | return webElement; 135 | } catch (Exception e) { 136 | log.error("设备: "+driverName+" "+"【failed】 等待:"+i+"s 找不到元素:"+locator[3].toString()+" 方式 "+locator[4].toString()+":[ "+locator[5].toString()+" ]"); 137 | TestListener.messageList.add(driverName+"(系统版本:"+sdkVersion+"):::"+"等待:"+i+"s 找不到元素:"+locator[3].toString()+" 方式 "+locator[4].toString()+":[ "+locator[5].toString()+" ]"); 138 | throw e; 139 | } 140 | } 141 | /* *//** 142 | * 查找一组元素 143 | * @param locator 元素定位信息 144 | * @return 145 | *//* 146 | public List waitForElements(final AppiumDriver driver,final Object[] locator,final String driverName,String sdkVersion){ 147 | List webElements=null; 148 | try { 149 | webElements=(new WebDriverWait(driver, 20)).until( 150 | new ExpectedCondition>() { 151 | @Override 152 | public List apply(WebDriver dr) { 153 | List element = getElements(driver,locator,driverName); 154 | return element; 155 | } 156 | }); 157 | return webElements; 158 | } catch (Exception e) { 159 | log.error("设备: "+driverName+" "+"【failed】 等待:"+20+"s 找不到元素:"+locator[3].toString()+" 方式 "+locator[4].toString()+":[ "+locator[5].toString()+" ]"); 160 | TestListener.messageList.add(driverName+"(系统版本:"+sdkVersion+"):::"+"等待:"+20+"s 找不到元素:"+locator[3].toString()+" 方式 "+locator[4].toString()+":[ "+locator[5].toString()+" ]"); 161 | throw e; 162 | } 163 | }*/ 164 | 165 | public void elementSelectForIndex(AppiumDriver driver,Object[] locator,String i,String driverName,String sdkVersion){ 166 | WebElement webElement = waitForElement(driver,locator,driverName,sdkVersion); 167 | int index = Integer.valueOf(i); 168 | Select select = new Select(webElement); 169 | try { 170 | select.selectByIndex(index); 171 | log.info("设备: "+driverName+" "+"选择操作:Index="+i); 172 | } catch (Exception e) { 173 | log.error("设备: "+driverName+" "+"复选框选择失败 方式: [ index ] 值: [ "+locator[8].toString()+" ]"); 174 | TestListener.messageList.add(driverName+"(系统版本:"+sdkVersion+"):::"+"复选框选择失败 方式: [ index ] 值: [ "+locator[8].toString()+" ]"); 175 | throw e; 176 | } 177 | } 178 | public void elementSelectForText(AppiumDriver driver,Object[] locator,String text,String driverName,String sdkVersion){ 179 | WebElement webElement = waitForElement(driver,locator,driverName,sdkVersion); 180 | Select select = new Select(webElement); 181 | try { 182 | select.selectByVisibleText(text); 183 | log.info("设备: "+driverName+" "+"选择操作:Text="+text); 184 | } catch (Exception e) { 185 | log.error("设备: "+driverName+" "+"复选框选择失败 方式: [ text ] 值: [ "+locator[8].toString()+" ]"); 186 | TestListener.messageList.add(driverName+"(系统版本:"+sdkVersion+"):::"+"复选框选择失败 方式: [ text ] 值: [ "+locator[8].toString()+" ]"); 187 | throw e; 188 | } 189 | } 190 | public void elementSelectForValue(AppiumDriver driver,Object[] locator,String value,String driverName,String sdkVersion){ 191 | WebElement webElement = waitForElement(driver,locator,driverName,sdkVersion); 192 | Select select = new Select(webElement); 193 | try { 194 | select.selectByValue(value); 195 | log.info("设备: "+driverName+" "+"选择操作:Value="+value); 196 | } catch (Exception e) { 197 | log.error("设备: "+driverName+" "+"复选框选择失败 方式: [ value ] 值: [ "+locator[8].toString()+" ]"); 198 | TestListener.messageList.add(driverName+"(系统版本:"+sdkVersion+"):::"+"复选框选择失败 方式: [ value ] 值: [ "+locator[8].toString()+" ]"); 199 | throw e; 200 | } 201 | } 202 | /** 203 | * 下拉选择操作 204 | * @param locator 205 | */ 206 | public void elementSelect(AppiumDriver driver,Object[] locator,String driverName,String sdkVersion){ 207 | String[] Selects = locator[8].toString().split("="); 208 | switch (Selects[0].toString()){ 209 | case "Index": 210 | elementSelectForIndex(driver,locator,Selects[1],driverName,sdkVersion); 211 | break; 212 | case "Text": 213 | elementSelectForText(driver,locator,Selects[1],driverName,sdkVersion); 214 | break; 215 | case "Value": 216 | elementSelectForValue(driver,locator,Selects[1],driverName,sdkVersion); 217 | break; 218 | } 219 | } 220 | /** 221 | * 输出控件信息 222 | * @param driver 223 | * @param locator 224 | */ 225 | private void elementExport(AppiumDriver driver, Object[] locator,String driverName,String sdkVersion) { 226 | WebElement webElement = waitForElement(driver,locator,driverName,sdkVersion); 227 | String text = null; 228 | try { 229 | text = webElement.getText(); 230 | log.info("设备: "+driverName+" "+"输出数据为: "+text); 231 | map.put(locator[2].toString(), text); 232 | } catch (Exception e) { 233 | log.error("设备: "+driverName+" "+"获取数据失败!"); 234 | TestListener.messageList.add(driverName+"(系统版本:"+sdkVersion+"):::"+"获取数据失败!"); 235 | throw e; 236 | } 237 | } 238 | 239 | private void elementIncoming(AppiumDriver driver, Object[] locator,String driverName,String sdkVersion) { 240 | WebElement webElement = waitForElement(driver,locator,driverName,sdkVersion); 241 | String text =null; 242 | try { 243 | text = map.get(locator[8].toString()).toString(); 244 | log.info("设备: "+driverName+" "+"控件传入数据: [ "+ text +" ]"); 245 | webElement.sendKeys(text); 246 | } catch (Exception e) { 247 | log.error("设备: "+driverName+" "+"控件传入数据失败!"); 248 | TestListener.messageList.add(driverName+"(系统版本:"+sdkVersion+"):::"+"控件传入数据失败,传入数据为:"+text); 249 | throw e; 250 | } 251 | } 252 | /** 253 | * 元素操作 254 | * @param locator 255 | */ 256 | public void elementOperation(AppiumDriver driver,Object[] locator,String driverName,String sdkVersion){ 257 | WebElement webElement = waitForElement(driver,locator,driverName,sdkVersion); 258 | switch (locator[7].toString()){ 259 | case "sendKeys" : 260 | try { 261 | log.info("设备: "+driverName+" "+"输入:"+ locator[8].toString()); 262 | webElement.sendKeys(locator[8].toString()); 263 | } catch (Exception e) { 264 | log.error("设备: "+driverName+" "+"控件输入失败!"); 265 | TestListener.messageList.add(driverName+"(系统版本:"+sdkVersion+"):::"+"控件输入失败,输入数据为:"+locator[8].toString()); 266 | throw e; 267 | } 268 | break; 269 | case "click" : 270 | try { 271 | log.info("设备: "+driverName+" "+"点击控件"); 272 | webElement.click(); 273 | } catch (Exception e) { 274 | log.error("设备: "+driverName+" "+"点击控件失败!"); 275 | TestListener.messageList.add(driverName+"(系统版本:"+sdkVersion+"):::"+"点击控件失败!"); 276 | throw e; 277 | } 278 | break; 279 | case "longPress": 280 | try { 281 | log.info("设备: "+driverName+" "+"长按控件"); 282 | TouchAction touchAction=new TouchAction(driver); 283 | touchAction.longPress(waitForElement(driver,locator,driverName,sdkVersion)); 284 | } catch (Exception e) { 285 | log.error("设备: "+driverName+" "+"长按控件失败!"); 286 | TestListener.messageList.add(driverName+"(系统版本:"+sdkVersion+"):::"+"长按控件失败!"); 287 | throw e; 288 | } 289 | break; 290 | case "verity": 291 | String actualText = webElement.getText(); 292 | Assertion.Verity(locator[9].toString(),actualText, locator[10].toString(),driverName,sdkVersion); 293 | break; 294 | case "select": 295 | elementSelect(driver,locator,driverName,sdkVersion); 296 | break; 297 | case "export": 298 | elementExport(driver,locator,driverName,sdkVersion); 299 | break; 300 | case "incoming": 301 | elementIncoming(driver,locator,driverName,sdkVersion); 302 | break; 303 | } 304 | } 305 | 306 | 307 | 308 | /** 309 | * 坐标点击 310 | * @param locator 311 | */ 312 | public void driverClick(AppiumDriver driver,Object[] locator,String driverName){ 313 | String[] coordinate = locator[5].toString().split(","); 314 | log.info("设备: "+driverName+" "+"坐标点击:"+coordinate[0]+","+ coordinate[1]); 315 | int x = Integer.valueOf(coordinate[0]); 316 | int y =Integer.valueOf(coordinate[1]); 317 | try { 318 | TouchAction tAction=new TouchAction(driver); 319 | tAction.press(x, y).perform(); 320 | } catch (Exception e) { 321 | log.error("设备: "+driverName+" "+"坐标点击失败:[ "+coordinate[0]+","+ coordinate[1]+" ]"); 322 | } 323 | } 324 | 325 | /** 326 | * 坐标滑动 327 | * @param startx 328 | * @param starty 329 | * @param endx 330 | * @param endy 331 | */ 332 | @SuppressWarnings("deprecation") 333 | public void driverSwipe(AppiumDriver driver,Object[] locator,String driverName){ 334 | String[] coordinates = locator[5].toString().split(":"); 335 | String[] A = coordinates[0].split(","); 336 | String[] B = coordinates[1].split(","); 337 | int startx = Integer.valueOf(A[0]); 338 | int starty =Integer.valueOf(A[1]); 339 | int endx =Integer.valueOf(B[0]); 340 | int endy =Integer.valueOf(B[1]); 341 | log.info("设备: "+driverName+" "+"坐标滑动:开始坐标:[ "+ A[0] +","+ A[1] +" ] "+"结束坐标:[ "+ B[0] +","+ B[1] +" ]"); 342 | try { 343 | driver.swipe(startx, starty, endx, endy, 500); 344 | } catch (Exception e) { 345 | log.error("设备: "+driverName+" "+"坐标滑动失败:开始坐标:[ "+ A[0] +","+ A[1] +" ] "+"结束坐标:[ "+ B[0] +","+ B[1] +" ]"); 346 | } 347 | } 348 | /** 349 | * 坐标拖拽 350 | * @param startx 351 | * @param starty 352 | * @param endx 353 | * @param endy 354 | */ 355 | public void driverDragAndDrop(AppiumDriver driver,Object[] locator,String driverName){ 356 | String[] coordinates = locator[5].toString().split(":"); 357 | String[] A = coordinates[0].split(","); 358 | String[] B = coordinates[1].split(","); 359 | int startx = Integer.valueOf(A[0]); 360 | int starty =Integer.valueOf(A[1]); 361 | int endx =Integer.valueOf(B[0]); 362 | int endy =Integer.valueOf(B[1]); 363 | TouchAction tAction=new TouchAction(driver); 364 | try { 365 | tAction.longPress(startx, starty).moveTo(endx, endy).release().perform(); 366 | log.info("设备: "+driverName+" "+"坐标拖拽:开始坐标:[ "+ startx +","+ starty +" ] "+"结束坐标: [ "+ endx +","+ endy +" ]"); 367 | } catch (Exception e) { 368 | log.error("设备: "+driverName+" "+"坐标拖拽失败 开始坐标:[ "+ startx +","+ starty +" ] "+"结束坐标: [ "+ endx +","+ endy +" ]"); 369 | TestListener.messageList.add("坐标拖拽失败 开始坐标:[ "+ startx +","+ starty +" ] "+"结束坐标: [ "+ endx +","+ endy +" ]"); 370 | throw e; 371 | } 372 | } 373 | /** 374 | * 坐标长按 375 | * @param locator 376 | */ 377 | public void driverLongPress(AppiumDriver driver,Object[] locator,String driverName){ 378 | String[] coordinates = locator[5].toString().split(":"); 379 | int x = Integer.valueOf(coordinates[0]); 380 | int y =Integer.valueOf(coordinates[1]); 381 | log.info("设备: "+driverName+" "+"坐标长按: [ "+ x +","+ y +" ] "); 382 | try { 383 | TouchAction tAction=new TouchAction(driver); 384 | tAction.longPress(x, y).release().perform(); 385 | } catch (Exception e) { 386 | log.error("设备: "+driverName+" "+"坐标长按: [ "+ x +","+ y +" ] 失败"); 387 | TestListener.messageList.add("坐标长按: [ "+ x +","+ y +" ] 失败"); 388 | throw e; 389 | } 390 | } 391 | 392 | /** 393 | * 坐标选择操作 394 | * @param locator 395 | */ 396 | public void coordinatesOperation(AppiumDriver driver,Object[] locator,String driverName){ 397 | switch (locator[7].toString()){ 398 | case "click": 399 | driverClick(driver,locator,driverName); 400 | break; 401 | case "swipe": 402 | driverSwipe(driver,locator,driverName); 403 | break; 404 | case "dragAndDrop": 405 | driverDragAndDrop(driver,locator,driverName); 406 | break; 407 | case "longPress": 408 | driverLongPress(driver,locator,driverName); 409 | break; 410 | } 411 | } 412 | /** 413 | * 显式等待,程序休眠暂停 414 | * @param time 以秒为单位 415 | */ 416 | public void sleep(long time){ 417 | try { 418 | Thread.sleep(time*1000); 419 | } catch (InterruptedException e) { 420 | e.printStackTrace(); 421 | } 422 | } 423 | 424 | private void SimulateAction(Object[] locator,String deviceId,String driverName,String sdkVersion){ 425 | ProcessBuilder pb = new ProcessBuilder(AppiumComm.adb, "-s",deviceId ,"shell", "input","keyevent",locator[5].toString()); 426 | try { 427 | log.info("设备: "+driverName+" "+locator[3].toString()); 428 | pb.start(); 429 | sleep(1); 430 | } catch (Exception e) { 431 | log.error("设备: "+driverName+" "+locator[3].toString()+"失败"); 432 | TestListener.messageList.add(driverName+"(系统版本:"+sdkVersion+"):::"+locator[3].toString()+"失败"); 433 | } 434 | } 435 | 436 | private void ExecuteScript(AppiumDriver driver,Object[] locator,String driverName,String sdkVersion){ 437 | log.info("设备: "+driverName+" "+"执行外部脚本:"+locator[5].toString()); 438 | try { 439 | ExecuteScript execute = new ExecuteScript(driver); 440 | execute.doRun(locator[5].toString()); 441 | } catch (Exception e) { 442 | log.error("设备: "+driverName+" "+"执行外部脚本:"+locator[5].toString()+"失败"); 443 | TestListener.messageList.add(driverName+"(系统版本:"+sdkVersion+"):::"+"执行外部脚本:"+locator[5].toString()+"失败"); 444 | } 445 | } 446 | 447 | private void HybrisAPP(AppiumDriver driver,Object[] locator) { 448 | if(locator[1].toString().contains("WEBVIEW")||locator[1].toString().contains("NATIVE_APP")){ 449 | Set contextNames = driver.getContextHandles(); 450 | for (String contextName : contextNames) { 451 | if (contextName.contains(locator[1].toString())){ 452 | driver.context(contextName); 453 | break; 454 | } 455 | } 456 | } 457 | } 458 | 459 | private void runPageObject(AppiumDriver driver,Object[] locator, String deviceId, String driverName, String sdkVersion) throws Exception { 460 | String[] Paths = locator[5].toString().split("::"); 461 | String PageObjectPath = TestListener.ProjectPath+"/testcase/"+Paths[0]+".xlsx"; 462 | log.info("设备: "+driverName+" "+"执行 PageObject "+locator[5].toString()); 463 | Object[][] testStart =null; 464 | if(Paths[1].contains("(")){ 465 | Pattern p=Pattern.compile("\"(.*?)\""); 466 | Matcher m=p.matcher(locator[5].toString()); 467 | String data = null; 468 | List datalist =new ArrayList(); 469 | while(m.find()) { 470 | data = m.group().replace("\"", ""); 471 | datalist.add(data); 472 | } 473 | testStart = IOMananger.readExcelData(Paths[1].split("\\(")[0],PageObjectPath); 474 | for(Object[] testStart1:testStart){ 475 | if(Arrays.asList(testStart1).contains("***")){ //传值替换 476 | Arrays.fill(testStart1, 8, 9,datalist.get(0)); 477 | datalist.remove(0); 478 | } 479 | } 480 | }else{ 481 | testStart = IOMananger.readExcelData(Paths[1],PageObjectPath); 482 | } 483 | 484 | for(int a=1;a driver,Object[] locator,String deviceId,String driverName,String sdkVersion) throws Exception{ 492 | HybrisAPP(driver,locator); 493 | switch (locator[4].toString()){ 494 | case "Sleep": 495 | int time = Integer.valueOf(locator[5].toString()); 496 | log.info("设备: "+driverName+" "+locator[3].toString()); 497 | sleep(time); 498 | break; 499 | case "Coordinate": 500 | coordinatesOperation(driver,locator,driverName); 501 | break; 502 | case "Script": 503 | ExecuteScript(driver,locator,driverName,sdkVersion); 504 | break; 505 | case "OpenURL": 506 | log.info("设备: "+driverName+" "+"打开URL:"+ locator[5].toString()); 507 | driver.get(locator[5].toString()); 508 | break; 509 | case "Control": 510 | elementOperation(driver,locator,driverName,sdkVersion); 511 | break; 512 | case "Simulate": 513 | SimulateAction(locator,deviceId,driverName,sdkVersion); 514 | break; 515 | case "PageObject": 516 | runPageObject(driver,locator,deviceId,driverName,sdkVersion); 517 | break; 518 | } 519 | } 520 | } 521 | -------------------------------------------------------------------------------- /src/main/java/com/xiaoM/appium/utils/AppiumResourceMonitoring.java: -------------------------------------------------------------------------------- 1 | package com.xiaoM.appium.utils; 2 | 3 | import java.text.DecimalFormat; 4 | import java.util.ArrayList; 5 | import java.util.List; 6 | 7 | import com.xiaoM.Common.Utils.CommonUtils; 8 | import com.xiaoM.Common.Utils.IOMananger; 9 | import com.xiaoM.Common.Utils.Log; 10 | import com.xiaoM.Report.utils.TestListener; 11 | 12 | import io.appium.java_client.AppiumDriver; 13 | import io.appium.java_client.MobileElement; 14 | 15 | public class AppiumResourceMonitoring { 16 | public Log log=new Log(this.getClass()); 17 | // static String[] luanch = null;//存放测试应用包名和Activity 18 | DecimalFormat df =new DecimalFormat("0.00");//格式化数值,保留两位小数 19 | public void driverStartApp(AppiumDriver driver,String deviceId,String driverName) throws Exception { 20 | try { 21 | log.info("设备: "+driverName+" "+"启动资源监控器"); 22 | String CpuPath = TestListener.ProjectPath+"/test-output/MonitorResoure/Cpu/"+driverName+".txt"; 23 | String MenPath = TestListener.ProjectPath+"/test-output/MonitorResoure/Mem/"+driverName+".txt"; 24 | String NetPath = TestListener.ProjectPath+"/test-output/MonitorResoure/Net/"+driverName+".txt"; 25 | String[] Paths = {CpuPath,MenPath,NetPath}; 26 | IOMananger.deleteFile(Paths);//删除监控日志文件 27 | AppiumComm.getMobileAppNet(TestListener.PackageName, deviceId,driverName); 28 | CpuThread cpuThread = new CpuThread(TestListener.PackageName, deviceId,driverName); // CPU监控线程1 29 | MemThread memThread = new MemThread(TestListener.PackageName, deviceId,driverName);//内存监控线程2 30 | cpuThread.start();// CPU监控线程启动 31 | memThread.start();// 内存监控线程启动 32 | } catch (Exception e) { 33 | log.error("设备: "+driverName+" "+"启动资源监控器失败设备:"+driverName); 34 | throw e; 35 | } 36 | } 37 | 38 | public void driverStop(String deviceId,String driverName,String sdkVersion) throws Exception { 39 | AppiumComm.getMobileAppNet(TestListener.PackageName,deviceId,driverName); 40 | CommonUtils.sleep(10);//线程等待 41 | String CpuPath = TestListener.ProjectPath+"/test-output/MonitorResoure/Cpu/"+driverName+".txt"; 42 | String MenPath = TestListener.ProjectPath+"/test-output/MonitorResoure/Mem/"+driverName+".txt"; 43 | String NetPath = TestListener.ProjectPath+"/test-output/MonitorResoure/Net/"+driverName+".txt"; 44 | List cpuList = new ArrayList(); 45 | List menList = new ArrayList(); 46 | List NetList = new ArrayList(); 47 | String[] Men = null; 48 | String[] Cpu = null; 49 | AppiumComm.adbClearCache(TestListener.PackageName, deviceId); 50 | AppiumComm.forceStop(TestListener.PackageName, deviceId); 51 | CommonUtils.sleep(2); 52 | String appPackageActivity = TestListener.PackageName+"/"+TestListener.Activity; 53 | String luanchTime = AppiumComm.appLuanchTime(appPackageActivity, deviceId); 54 | try { 55 | int cpuMax = 0; 56 | Double memMax = null; 57 | List Cpus = IOMananger.readTxtFile(CpuPath); 58 | int a; 59 | double k; 60 | for(int i=0;i Mens = IOMananger.readTxtFile(MenPath); 66 | for(int i=0;i Nets = IOMananger.readTxtFile(NetPath); 72 | while(Nets.size()!=4){ 73 | Nets = IOMananger.readTxtFile(NetPath); 74 | } 75 | for(String net : Nets) { 76 | int i = Integer.parseInt(net); 77 | NetList.add(i); 78 | } 79 | PieChartPicture picture = new PieChartPicture (driverName,CpuPath,MenPath); 80 | picture.createScreen(); 81 | cpuMax = AppiumComm.cpuMaxComp(cpuList); 82 | memMax = AppiumComm.memMaxComp(menList); 83 | int cpuAvg = AppiumComm.cpuAvg(cpuList); 84 | double menAvg = AppiumComm.menAvg(menList); 85 | String netshangxing = df.format((double) (NetList.get(2)-NetList.get(0))/1024.0); 86 | String netxiaxing = df.format((double) (NetList.get(3)-NetList.get(1))/1024.0); 87 | log.info("设备: "+driverName+" "+"首次启动时延为:" + luanchTime +"ms"); 88 | log.info("设备: "+driverName+" "+"执行业务时CPU峰值为:" + cpuMax +"%"); 89 | log.info("设备: "+driverName+" "+"执行业务时内存峰值为:" + df.format(memMax)+"MB"); 90 | log.info("设备: "+driverName+" "+"上行流量:"+ netshangxing+"KB"); 91 | log.info("设备: "+driverName+" "+"下行流量:"+ netxiaxing+"KB"); 92 | TestListener.ResourceList.add(driverName+"(版本:"+sdkVersion+"):::"+"" 93 | + "
指标启动时延(S)CPU 峰值(%)CPU 均值(%)内存峰值(MB)内存均值(MB)上行流量(KB)下行流量(KB)
数值" + luanchTime +"ms" + cpuMax +"%" + cpuAvg +"%" + df.format(memMax)+"MB" + df.format(menAvg)+"MB"+ netxiaxing+"KB"+netshangxing+"KB
"); 94 | log.info("设备: "+driverName+" "+"关闭资源监控器"); 95 | } catch (Exception e) { 96 | log.error("设备: "+driverName+" "+"读取资源监控信息失败!"); 97 | throw e; 98 | } 99 | } 100 | } 101 | -------------------------------------------------------------------------------- /src/main/java/com/xiaoM/appium/utils/AppiumScreenShot.java: -------------------------------------------------------------------------------- 1 | package com.xiaoM.appium.utils; 2 | 3 | import java.io.File; 4 | import java.io.IOException; 5 | 6 | import org.openqa.selenium.OutputType; 7 | import org.openqa.selenium.TakesScreenshot; 8 | import com.google.common.io.Files; 9 | import com.xiaoM.Common.Utils.Log; 10 | import com.xiaoM.Report.utils.TestListener; 11 | 12 | import io.appium.java_client.AppiumDriver; 13 | import io.appium.java_client.MobileElement; 14 | 15 | /** 16 | * 截图工具类 17 | * @author XiaoM 18 | * 19 | */ 20 | public class AppiumScreenShot{ 21 | private AppiumDriver driver; 22 | private String CaseName; 23 | private String driverName; 24 | Log log =new Log(this.getClass()); 25 | 26 | public void setscreenName(String driverName,String CaseName){ 27 | this.CaseName=CaseName; 28 | this.driverName = driverName; 29 | } 30 | public AppiumScreenShot(AppiumDriver driver){ 31 | this.driver = driver; 32 | } 33 | private void takeScreenshot(String screenPath) { 34 | //appium在Chrome没法截图,需用原生app下进行截图(这里纯属为了WAP端可以进行截图) 35 | driver.context("NATIVE_APP");//切换到NATIVE_APP进行app截图 36 | try { 37 | File scrFile = ((TakesScreenshot) driver).getScreenshotAs(OutputType.FILE); 38 | Files.copy(scrFile, new File(screenPath)); 39 | log.error("错误截图:"+screenPath); 40 | } catch (Exception e) { 41 | e.printStackTrace(); 42 | } 43 | } 44 | 45 | public void takeScreenshot() throws IOException { 46 | String screenName =this.driverName+"-"+this.CaseName+ ".jpg"; 47 | File dir = new File("test-output/snapshot"); 48 | if (!dir.exists()){ 49 | dir.mkdirs(); 50 | } 51 | String path = "../test-output/snapshot/"+screenName; 52 | TestListener.screenMessageList.add(path); 53 | String screenPath = dir.getAbsolutePath() + "/" + screenName; 54 | takeScreenshot(screenPath); 55 | } 56 | 57 | } 58 | -------------------------------------------------------------------------------- /src/main/java/com/xiaoM/appium/utils/AppiumServerUtils.java: -------------------------------------------------------------------------------- 1 | package com.xiaoM.appium.utils; 2 | 3 | import java.net.URL; 4 | import io.appium.java_client.service.local.AppiumDriverLocalService; 5 | import io.appium.java_client.service.local.AppiumServiceBuilder; 6 | import io.appium.java_client.service.local.flags.AndroidServerFlag; 7 | 8 | /** 9 | * @author xiaoM 10 | * @since 2017-03-30 11 | */ 12 | public class AppiumServerUtils { 13 | String ipAddress; 14 | int port; 15 | String bp; 16 | AppiumDriverLocalService service; 17 | 18 | public AppiumServerUtils(){ 19 | } 20 | public AppiumServerUtils(String ipAddress, int port,String bp){ 21 | this.ipAddress = ipAddress; 22 | this.port = port; 23 | this.bp = bp; 24 | } 25 | 26 | /** 27 | * 使用默认(即127.0.0.1:4723) 28 | * @return 29 | */ 30 | public URL startAppiumServerByDefault() { 31 | AppiumDriverLocalService service = AppiumDriverLocalService.buildDefaultService(); 32 | service.start(); 33 | if (service == null || !service.isRunning()) { 34 | throw new RuntimeException("An appium server node is not started!"); 35 | } 36 | return service.getUrl(); 37 | } 38 | 39 | public URL startAppiumServerNoReset() { 40 | AppiumServiceBuilder builder = new AppiumServiceBuilder(); 41 | AppiumDriverLocalService service = AppiumDriverLocalService.buildService(builder); 42 | service.start(); 43 | if (service == null || !service.isRunning()) { 44 | throw new RuntimeException("An appium server node is not started!"); 45 | } 46 | return service.getUrl(); 47 | } 48 | 49 | public URL startServer() { 50 | AppiumServiceBuilder builder = new AppiumServiceBuilder(); 51 | builder.withIPAddress(ipAddress); 52 | builder.usingPort(port); 53 | builder.withArgument(AndroidServerFlag.BOOTSTRAP_PORT_NUMBER, bp); 54 | service = AppiumDriverLocalService.buildService(builder); 55 | service.start(); 56 | if (service == null || !service.isRunning()) { 57 | throw new RuntimeException("An appium server node is not started!"); 58 | } 59 | return service.getUrl(); 60 | } 61 | 62 | public URL startServer(String ipAddress,int port) { 63 | AppiumServiceBuilder builder = new AppiumServiceBuilder(); 64 | builder.withIPAddress(ipAddress); 65 | builder.usingPort(port); 66 | service = AppiumDriverLocalService.buildService(builder); 67 | service.start(); 68 | if (service == null || !service.isRunning()) { 69 | throw new RuntimeException("An appium server node is not started!"); 70 | } 71 | return service.getUrl(); 72 | } 73 | 74 | public void stopServer(){ 75 | if(service!=null){ 76 | service.stop(); 77 | } 78 | } 79 | } 80 | -------------------------------------------------------------------------------- /src/main/java/com/xiaoM/appium/utils/CpuThread.java: -------------------------------------------------------------------------------- 1 | package com.xiaoM.appium.utils; 2 | 3 | 4 | import java.io.BufferedReader; 5 | import java.io.IOException; 6 | import java.io.InputStreamReader; 7 | import java.text.SimpleDateFormat; 8 | import java.util.Date; 9 | 10 | import com.xiaoM.Common.Utils.IOMananger; 11 | import com.xiaoM.Report.utils.TestListener; 12 | 13 | 14 | public class CpuThread extends Thread { 15 | int cpuForThisTime; 16 | String appPackage; 17 | String device; 18 | String driverName; 19 | SimpleDateFormat dateFormat=new SimpleDateFormat("yyyy-MM-dd_HH:mm:ss"); 20 | String date; 21 | public CpuThread( String appPackage,String device,String driverName) { 22 | this.appPackage = appPackage; 23 | this.device = device; 24 | this.driverName = driverName; 25 | } 26 | 27 | @Override 28 | public void run() { 29 | String workSpace = TestListener.ProjectPath+"/test-output/MonitorResoure/Cpu"; 30 | while(true){ 31 | try { 32 | Thread.sleep(50); 33 | cpuForThisTime = getCpu(appPackage,device); 34 | date =dateFormat.format(new Date()).toString(); 35 | IOMananger.saveToFile(workSpace, driverName, String.valueOf(cpuForThisTime+" "+date)); 36 | } catch (IOException | InterruptedException e) { 37 | e.printStackTrace(); 38 | } 39 | } 40 | } 41 | 42 | /** 43 | * 读取手机CPU数据 44 | * @param app要测试的应用的包 45 | * @return 返回该次的CPU百分比 46 | * @throws IOException 47 | * @throws InterruptedException 48 | */ 49 | public static int getCpu(String appPackage,String device) throws IOException, InterruptedException { 50 | // 读取属于应用的那一行CPU数据 51 | String line = null; 52 | // 将line的数据分类 53 | String[] splitArr = null; 54 | // 拿出百分比的那一个数据 55 | String per; 56 | // 将百分比的百分号去掉 方便转换 57 | String pernum; 58 | // 去除百分比的数字 59 | int perint; 60 | ProcessBuilder pb = new ProcessBuilder(AppiumComm.adb, "-s",device,"shell", "top -d 1 -m 10 -n 1"); 61 | Process p = pb.start(); 62 | BufferedReader bf = new BufferedReader(new InputStreamReader(p.getInputStream(), "utf-8")); 63 | while ((line = bf.readLine()) != null) { 64 | if (line.contains(appPackage)) { 65 | splitArr = line.split(" ");// 根据两个空格的间隔进行分割 66 | for (int j = 0; j < splitArr.length; j++) { 67 | if (splitArr[j].contains("%")) { 68 | // 将后面的R S去掉(R for Running , S for Sleeping) 69 | per = splitArr[j].split(" ")[0]; 70 | // 去掉百分号 71 | pernum = per.replace("%", ""); 72 | // 若取值为空(一般不为空 保险操作) 73 | if (pernum.equals("") || pernum.isEmpty()) { 74 | return 0; 75 | } 76 | // 转换成数字并返回 77 | perint = Integer.parseInt(pernum); 78 | return perint; 79 | } 80 | } 81 | } 82 | } 83 | return 0; 84 | } 85 | } 86 | -------------------------------------------------------------------------------- /src/main/java/com/xiaoM/appium/utils/MemThread.java: -------------------------------------------------------------------------------- 1 | package com.xiaoM.appium.utils; 2 | 3 | 4 | import java.io.BufferedReader; 5 | import java.io.IOException; 6 | import java.io.InputStream; 7 | import java.io.InputStreamReader; 8 | import java.text.SimpleDateFormat; 9 | import java.util.Date; 10 | 11 | import com.xiaoM.Common.Utils.IOMananger; 12 | import com.xiaoM.Report.utils.TestListener; 13 | 14 | public class MemThread extends Thread { 15 | 16 | Double memForThisTime; 17 | String appPackage; 18 | String device; 19 | String driverName; 20 | 21 | public MemThread(String appPackage,String device,String driverName) { 22 | this.appPackage = appPackage; 23 | this.device = device; 24 | this.driverName = driverName; 25 | } 26 | 27 | public void run() { 28 | SimpleDateFormat dateFormat = new SimpleDateFormat("yyyy-MM-dd_HH:mm:ss"); 29 | String date; 30 | String workSpace = TestListener.ProjectPath+"/test-output/MonitorResoure/Mem"; 31 | while(true){ 32 | try { 33 | Thread.sleep(50); 34 | memForThisTime = getMobileMem(appPackage,device); 35 | date = dateFormat.format(new Date()).toString(); 36 | IOMananger.saveToFile(workSpace, driverName, String.valueOf(memForThisTime+" "+date)); 37 | } catch (IOException | InterruptedException e) { 38 | e.printStackTrace(); 39 | } 40 | } 41 | } 42 | /** 43 | * 测试内存数据 44 | * @param appPackage 包名 45 | * @return Double类型的内存数据?(MB) 46 | * @throws IOException 47 | * @throws InterruptedException 48 | */ 49 | public static Double getMobileMem(String appPackage,String device) throws IOException, InterruptedException { 50 | String line = null; 51 | String mem = null; 52 | ProcessBuilder pb = new ProcessBuilder(AppiumComm.adb, "-s",device, "shell", "dumpsys meminfo | grep " + appPackage); 53 | Process process = pb.start(); 54 | InputStream is = process.getInputStream(); 55 | BufferedReader bf = new BufferedReader(new InputStreamReader(is)); 56 | while ((line = bf.readLine()) != null) { 57 | if (line.contains("activities")) { 58 | // 截取前面数字部分 59 | mem = line.substring(0, 9); 60 | // 删除空格 并返回Double 61 | mem = mem.trim(); 62 | return ((Double.valueOf(mem)) / 1024); 63 | } 64 | } 65 | return 0.0; 66 | } 67 | } 68 | -------------------------------------------------------------------------------- /src/main/java/com/xiaoM/appium/utils/PieChartPicture.java: -------------------------------------------------------------------------------- 1 | package com.xiaoM.appium.utils; 2 | 3 | import java.awt.BasicStroke; 4 | import java.awt.Color; 5 | import java.awt.Font; 6 | import java.awt.Paint; 7 | import java.io.File; 8 | import java.io.FileNotFoundException; 9 | import java.io.FileOutputStream; 10 | import java.io.IOException; 11 | import java.util.List; 12 | 13 | import org.jfree.chart.ChartFactory; 14 | import org.jfree.chart.ChartUtilities; 15 | import org.jfree.chart.JFreeChart; 16 | import org.jfree.chart.StandardChartTheme; 17 | import org.jfree.chart.axis.CategoryAxis; 18 | import org.jfree.chart.axis.CategoryLabelPositions; 19 | import org.jfree.chart.axis.ValueAxis; 20 | import org.jfree.chart.block.BlockBorder; 21 | import org.jfree.chart.plot.CategoryPlot; 22 | import org.jfree.chart.plot.DefaultDrawingSupplier; 23 | import org.jfree.chart.plot.PieLabelLinkStyle; 24 | import org.jfree.chart.renderer.category.LineAndShapeRenderer; 25 | import org.jfree.chart.renderer.category.StandardBarPainter; 26 | import org.jfree.chart.renderer.xy.StandardXYBarPainter; 27 | import org.jfree.data.category.CategoryDataset; 28 | import org.jfree.data.category.DefaultCategoryDataset; 29 | import org.jfree.ui.RectangleInsets; 30 | 31 | import com.xiaoM.Common.Utils.IOMananger; 32 | 33 | 34 | public class PieChartPicture { 35 | 36 | String PictureName; 37 | String CpuPath; 38 | String MenPath; 39 | public PieChartPicture(String name,String CpuPath,String MenPath){ 40 | this.PictureName = name; 41 | this.CpuPath = CpuPath; 42 | this.MenPath = MenPath; 43 | } 44 | private static String NO_DATA_MSG = "数据加载失败"; 45 | private static Font FONT = new Font("宋体", Font.PLAIN, 12); 46 | private static Font FONT2 = new Font("宋体", Font.PLAIN, 18); 47 | public static Color[] CHART_COLORS = { 48 | new Color(31,129,188), new Color(92,92,97), new Color(144,237,125), new Color(255,188,117), 49 | new Color(153,158,255), new Color(255,117,153), new Color(253,236,109), new Color(128,133,232), 50 | new Color(158,90,102),new Color(255, 204, 102) };//颜色 51 | 52 | static { 53 | setChartTheme(); 54 | } 55 | /** 56 | * 中文主题样式 解决乱码 57 | */ 58 | public static void setChartTheme() { 59 | // 设置中文主题样式 解决乱码 60 | StandardChartTheme chartTheme = new StandardChartTheme("CN"); 61 | // 设置标题字体 62 | chartTheme.setExtraLargeFont(FONT2); 63 | // 设置图例的字体 64 | chartTheme.setRegularFont(FONT); 65 | // 设置轴向的字体 66 | chartTheme.setLargeFont(FONT); 67 | chartTheme.setSmallFont(FONT); 68 | chartTheme.setTitlePaint(new Color(51, 51, 51)); 69 | chartTheme.setSubtitlePaint(new Color(85, 85, 85)); 70 | 71 | chartTheme.setLegendBackgroundPaint(Color.WHITE);// 设置标注 72 | chartTheme.setLegendItemPaint(Color.BLACK);// 73 | chartTheme.setChartBackgroundPaint(Color.WHITE); 74 | // 绘制颜色绘制颜色.轮廓供应商 75 | // paintSequence,outlinePaintSequence,strokeSequence,outlineStrokeSequence,shapeSequence 76 | 77 | Paint[] OUTLINE_PAINT_SEQUENCE = new Paint[] { Color.WHITE }; 78 | // 绘制器颜色源 79 | DefaultDrawingSupplier drawingSupplier = new DefaultDrawingSupplier(CHART_COLORS, CHART_COLORS, OUTLINE_PAINT_SEQUENCE, 80 | DefaultDrawingSupplier.DEFAULT_STROKE_SEQUENCE, DefaultDrawingSupplier.DEFAULT_OUTLINE_STROKE_SEQUENCE, 81 | DefaultDrawingSupplier.DEFAULT_SHAPE_SEQUENCE); 82 | chartTheme.setDrawingSupplier(drawingSupplier); 83 | 84 | chartTheme.setPlotBackgroundPaint(Color.WHITE);// 绘制区域 85 | chartTheme.setPlotOutlinePaint(Color.WHITE);// 绘制区域外边框 86 | chartTheme.setLabelLinkPaint(new Color(8, 55, 114));// 链接标签颜色 87 | chartTheme.setLabelLinkStyle(PieLabelLinkStyle.CUBIC_CURVE); 88 | 89 | chartTheme.setAxisOffset(new RectangleInsets(5, 12, 5, 12)); 90 | chartTheme.setDomainGridlinePaint(new Color(192, 208, 224));// X坐标轴垂直网格颜色 91 | chartTheme.setRangeGridlinePaint(new Color(192, 192, 192));// Y坐标轴水平网格颜色 92 | 93 | chartTheme.setBaselinePaint(Color.WHITE); 94 | chartTheme.setCrosshairPaint(Color.BLUE);// 不确定含义 95 | chartTheme.setAxisLabelPaint(new Color(51, 51, 51));// 坐标轴标题文字颜色 96 | chartTheme.setTickLabelPaint(new Color(67, 67, 72));// 刻度数字 97 | chartTheme.setBarPainter(new StandardBarPainter());// 设置柱状图渲染 98 | chartTheme.setXYBarPainter(new StandardXYBarPainter());// XYBar 渲染 99 | 100 | chartTheme.setItemLabelPaint(Color.black); 101 | chartTheme.setThermometerPaint(Color.white);// 温度计 102 | 103 | ChartFactory.setChartTheme(chartTheme); 104 | } 105 | /** 106 | * 调试用 107 | * @return 108 | */ 109 | public static CategoryDataset createDataSet() { 110 | DefaultCategoryDataset dataset = new DefaultCategoryDataset(); 111 | dataset.setValue(10, "CPU占用(%)","2017-04-01 16:25:53"); 112 | dataset.setValue(20, "CPU占用(%)","2017-04-01 16:26:13"); 113 | dataset.setValue(23, "CPU占用(%)","2017-04-01 16:26:17"); 114 | dataset.setValue(15, "CPU占用(%)","2017-04-01 16:26:22"); 115 | dataset.setValue(29, "CPU占用(%)","2017-04-01 16:27:01"); 116 | dataset.setValue(20, "CPU占用(%)","2017-04-01 16:27:05"); 117 | dataset.setValue(23, "CPU占用(%)","2017-04-01 16:27:09"); 118 | dataset.setValue(21, "CPU占用(%)","2017-04-01 16:27:16"); 119 | dataset.setValue(16, "CPU占用(%)","2017-04-01 16:27:19"); 120 | dataset.setValue(29, "CPU占用(%)","2017-04-01 16:27:23"); 121 | dataset.setValue(10, "CPU占用(%)","2017-04-01 16:27:26"); 122 | dataset.setValue(20, "CPU占用(%)","2017-04-01 16:27:35"); 123 | dataset.setValue(23, "CPU占用(%)","2017-04-01 16:27:39"); 124 | dataset.setValue(15, "CPU占用(%)","2017-04-01 16:27:42"); 125 | dataset.setValue(29, "CPU占用(%)","2017-04-01 16:27:47"); 126 | dataset.setValue(20, "CPU占用(%)","2017-04-01 16:27:51"); 127 | dataset.setValue(23, "CPU占用(%)","2017-04-01 16:27:54"); 128 | dataset.setValue(21, "CPU占用(%)","2017-04-01 16:27:57"); 129 | dataset.setValue(16, "CPU占用(%)","2017-04-01 16:28:30"); 130 | dataset.setValue(29, "CPU占用(%)","2017-04-01 16:28:33"); 131 | dataset.setValue(10, "CPU占用(%)","2017-04-01 16:28:36"); 132 | dataset.setValue(20, "CPU占用(%)","2017-04-01 16:28:39"); 133 | dataset.setValue(23, "CPU占用(%)","2017-04-01 16:29:06"); 134 | dataset.setValue(15, "CPU占用(%)","2017-04-01 16:29:09"); 135 | dataset.setValue(29, "CPU占用(%)","2017-04-01 16:29:11"); 136 | dataset.setValue(20, "CPU占用(%)","2017-04-01 16:29:14"); 137 | dataset.setValue(23, "CPU占用(%)","2017-04-01 16:29:26"); 138 | dataset.setValue(21, "CPU占用(%)","2017-04-01 16:29:29"); 139 | dataset.setValue(16, "CPU占用(%)","2017-04-01 16:29:31"); 140 | dataset.setValue(29, "CPU占用(%)","2017-04-01 16:29:53"); 141 | dataset.setValue(10, "CPU占用(%)","2017-04-01 16:29:56"); 142 | dataset.setValue(20, "CPU占用(%)","2017-04-01 16:30:01"); 143 | dataset.setValue(23, "CPU占用(%)","2017-04-01 16:30:03"); 144 | dataset.setValue(15, "CPU占用(%)","2017-04-01 16:30:07"); 145 | dataset.setValue(29, "CPU占用(%)","2017-04-01 16:30:10"); 146 | dataset.setValue(20, "CPU占用(%)","2017-04-01 16:30:14"); 147 | dataset.setValue(23, "CPU占用(%)","2017-04-01 16:30:17"); 148 | dataset.setValue(21, "CPU占用(%)","2017-04-01 16:30:19"); 149 | dataset.setValue(16, "CPU占用(%)","2017-04-01 16:30:22"); 150 | dataset.setValue(29, "CPU占用(%)","2017-04-01 16:30:25"); 151 | return dataset; 152 | } 153 | /** CPU 154 | * 创建 简单数据集对象 155 | * 组合数据集对象 156 | * @return 157 | * @throws FileNotFoundException 158 | */ 159 | public CategoryDataset createCpuDataSet() throws FileNotFoundException { 160 | String[] Cpu = null; 161 | int k; 162 | List CpuDatas = IOMananger.readTxtFile(CpuPath); 163 | DefaultCategoryDataset dataset = new DefaultCategoryDataset(); 164 | for(int i=0;i MenDatas = IOMananger.readTxtFile(MenPath); 184 | DefaultCategoryDataset dataset = new DefaultCategoryDataset(); 185 | for(int i=0;i freeBelow) { 57 | FIRST_PORT = ephemeralRangeDetector.getHighestEphemeralPort(); 58 | LAST_PORT = 65535; 59 | } else { 60 | FIRST_PORT = 1024; 61 | LAST_PORT = ephemeralRangeDetector.getLowestEphemeralPort(); 62 | } 63 | 64 | if (FIRST_PORT == LAST_PORT) { 65 | return FIRST_PORT; 66 | } 67 | if (FIRST_PORT > LAST_PORT) { 68 | throw new UnsupportedOperationException("Could not find ephemeral port to use"); 69 | } 70 | final int randomInt = random.nextInt(); 71 | final int portWithoutOffset = Math.abs(randomInt % (LAST_PORT - FIRST_PORT + 1)); 72 | return portWithoutOffset + FIRST_PORT; 73 | } 74 | } 75 | 76 | private static int checkPortIsFree(int port) { 77 | ServerSocket socket; 78 | try { 79 | socket = new ServerSocket(); 80 | socket.setReuseAddress(true); 81 | socket.bind(new InetSocketAddress("localhost", port)); 82 | int localPort = socket.getLocalPort(); 83 | socket.close(); 84 | return localPort; 85 | } catch (IOException e) { 86 | return -1; 87 | } 88 | } 89 | 90 | public static void main(String[] args) { 91 | System.out.println(getFreePort()); 92 | } 93 | } 94 | -------------------------------------------------------------------------------- /testcase/PageObject.xlsx: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/mingming2513953126/AppiumXM/d6cf7f5f1efe0e849db70cca630213511456dc8b/testcase/PageObject.xlsx -------------------------------------------------------------------------------- /testcase/TestCase.xlsx: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/mingming2513953126/AppiumXM/d6cf7f5f1efe0e849db70cca630213511456dc8b/testcase/TestCase.xlsx -------------------------------------------------------------------------------- /测试用例步骤.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/mingming2513953126/AppiumXM/d6cf7f5f1efe0e849db70cca630213511456dc8b/测试用例步骤.png -------------------------------------------------------------------------------- /用例.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/mingming2513953126/AppiumXM/d6cf7f5f1efe0e849db70cca630213511456dc8b/用例.png --------------------------------------------------------------------------------