├── LICENSE ├── README.md ├── __init__.py ├── data ├── app.apk ├── appium_parameter.yaml ├── incidental │ ├── device_info.yaml │ ├── error.png │ └── test.db ├── minicap │ ├── bin │ │ ├── arm64-v8a │ │ │ ├── minicap │ │ │ └── minicap-nopie │ │ ├── armeabi-v7a │ │ │ ├── minicap │ │ │ └── minicap-nopie │ │ ├── x86 │ │ │ ├── minicap │ │ │ └── minicap-nopie │ │ └── x86_64 │ │ │ ├── minicap │ │ │ └── minicap-nopie │ ├── minitouch │ │ ├── arm64-v8a │ │ │ ├── minitouch │ │ │ └── minitouch-nopie │ │ ├── armeabi-v7a │ │ │ ├── minitouch │ │ │ └── minitouch-nopie │ │ ├── armeabi │ │ │ ├── minitouch │ │ │ └── minitouch-nopie │ │ ├── mips │ │ │ ├── minitouch │ │ │ └── minitouch-nopie │ │ ├── mips64 │ │ │ ├── minitouch │ │ │ └── minitouch-nopie │ │ ├── x86 │ │ │ ├── minitouch │ │ │ └── minitouch-nopie │ │ └── x86_64 │ │ │ ├── minitouch │ │ │ └── minitouch-nopie │ └── shared │ │ ├── android-10 │ │ └── armeabi-v7a │ │ │ └── minicap.so │ │ ├── android-14 │ │ ├── armeabi-v7a │ │ │ └── minicap.so │ │ └── x86 │ │ │ └── minicap.so │ │ ├── android-15 │ │ ├── armeabi-v7a │ │ │ └── minicap.so │ │ └── x86 │ │ │ └── minicap.so │ │ ├── android-16 │ │ ├── armeabi-v7a │ │ │ └── minicap.so │ │ └── x86 │ │ │ └── minicap.so │ │ ├── android-17 │ │ ├── armeabi-v7a │ │ │ └── minicap.so │ │ └── x86 │ │ │ └── minicap.so │ │ ├── android-18 │ │ ├── armeabi-v7a │ │ │ └── minicap.so │ │ └── x86 │ │ │ └── minicap.so │ │ ├── android-19 │ │ ├── armeabi-v7a │ │ │ └── minicap.so │ │ └── x86 │ │ │ └── minicap.so │ │ ├── android-21 │ │ ├── arm64-v8a │ │ │ └── minicap.so │ │ ├── armeabi-v7a │ │ │ └── minicap.so │ │ ├── x86 │ │ │ └── minicap.so │ │ └── x86_64 │ │ │ └── minicap.so │ │ ├── android-22 │ │ ├── arm64-v8a │ │ │ └── minicap.so │ │ ├── armeabi-v7a │ │ │ └── minicap.so │ │ ├── x86 │ │ │ └── minicap.so │ │ └── x86_64 │ │ │ └── minicap.so │ │ ├── android-9 │ │ └── armeabi-v7a │ │ │ └── minicap.so │ │ └── android-M │ │ ├── arm64-v8a │ │ └── minicap.so │ │ ├── armeabi-v7a │ │ └── minicap.so │ │ ├── x86 │ │ └── minicap.so │ │ └── x86_64 │ │ └── minicap.so └── test_info.ini ├── demo_run.py ├── doc ├── appium_wiki.md ├── controls_operations.md ├── parameter_configuration.md ├── run.md ├── test_case_writing.md ├── test_report.md └── update_log.md ├── lib ├── ScreenShot.py ├── Utils.py ├── __init__.py └── adbUtils.py ├── po ├── BasePage.py ├── ExecuteCase.py ├── __init__.py └── integration.py ├── public ├── Analyzelog.py ├── CheckEnvironment.py ├── CleanProcess.py ├── GenerateReports.py ├── GetCase.py ├── GetDevice.py ├── GetFilePath.py ├── GetHtml.py ├── GetLog.py ├── Performance.py ├── StartAppium.py ├── __init__.py └── installApp.py ├── run.py ├── setup.py └── testcase └── login ├── login.yaml └── login1.yaml /LICENSE: -------------------------------------------------------------------------------- 1 | The MIT License (MIT) 2 | 3 | Copyright (c) 2016 ztwo 4 | 5 | Permission is hereby granted, free of charge, to any person obtaining a copy of 6 | this software and associated documentation files (the "Software"), to deal in 7 | the Software without restriction, including without limitation the rights to 8 | use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of 9 | the Software, and to permit persons to whom the Software is furnished to do so, 10 | subject to the following conditions: 11 | 12 | The above copyright notice and this permission notice shall be included in all 13 | copies or substantial portions of the Software. 14 | 15 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 16 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS 17 | FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR 18 | COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER 19 | IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN 20 | CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. 21 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | ## Auto_Analysis 2 | 3 | ### 简介 4 | Auto_Analysis是基于appium编写的自动化测试工具。使用方法简单,编写yaml文件格式的测试用例即可。支持Android,多台设备并行,性能采集等。 5 | 6 | ### 环境要求 7 | 8 | * macOS,linux,windows 9 | * appium 1.5.0+ 10 | * python 2.7 11 | 12 | ### 工具特性 13 | 14 | 执行编写yaml格式的testcase,执行后即可得到测试报告 15 | 16 | * 1:支持Android 4.2.2 以上 17 | * 2:支持多设备并行测试 18 | * 3:性能采集与对比 19 | * 4:支持log采集与清洗 20 | * 5:对appium异常的一些封装 21 | * 6:用例编写支持继承多重继承,大部分用例仅需写两个步骤即可 22 | 23 | ### 快速开始 24 | 25 | * git clone https://github.com/ztwo/Auto_Analysis.git 26 | * cd Auto_Analysis 27 | * python setup.py install 28 | * python demo_run.py 29 | * result内查看测试报告 30 | 31 | ### 执行效果 32 | ![12](http://7xwbkf.com1.z0.glb.clouddn.com/2016-11-11%2017.22.53.gif) 33 | 34 | ### 报告样式 35 | ![20161123810772016-11-23pm.png](http://7xwbkf.com1.z0.glb.clouddn.com/20161123810772016-11-23pm.png) 36 | -------------------------------------------------------------------------------- /__init__.py: -------------------------------------------------------------------------------- 1 | __author__ = 'joko' -------------------------------------------------------------------------------- /data/app.apk: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/ztwo/Auto_Analysis/795db843dda0e677f20f1e1b16f38d945da83369/data/app.apk -------------------------------------------------------------------------------- /data/appium_parameter.yaml: -------------------------------------------------------------------------------- 1 | --- 2 | - 3 | appPackage: test.joko.com.myapplication 4 | appActivity: .welcome 5 | appWaitActivity: .welcome 6 | unicodeKeyboard: True 7 | resetKeyboard: True 8 | resetKeyboard: True 9 | noReset: False -------------------------------------------------------------------------------- /data/incidental/device_info.yaml: -------------------------------------------------------------------------------- 1 | - {deviceName: 0123456789ABCDEF, platformName: Android, platformVersion: 4.4.2} 2 | -------------------------------------------------------------------------------- /data/incidental/error.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/ztwo/Auto_Analysis/795db843dda0e677f20f1e1b16f38d945da83369/data/incidental/error.png -------------------------------------------------------------------------------- /data/incidental/test.db: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/ztwo/Auto_Analysis/795db843dda0e677f20f1e1b16f38d945da83369/data/incidental/test.db -------------------------------------------------------------------------------- /data/minicap/bin/arm64-v8a/minicap: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/ztwo/Auto_Analysis/795db843dda0e677f20f1e1b16f38d945da83369/data/minicap/bin/arm64-v8a/minicap -------------------------------------------------------------------------------- /data/minicap/bin/arm64-v8a/minicap-nopie: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/ztwo/Auto_Analysis/795db843dda0e677f20f1e1b16f38d945da83369/data/minicap/bin/arm64-v8a/minicap-nopie -------------------------------------------------------------------------------- /data/minicap/bin/armeabi-v7a/minicap: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/ztwo/Auto_Analysis/795db843dda0e677f20f1e1b16f38d945da83369/data/minicap/bin/armeabi-v7a/minicap -------------------------------------------------------------------------------- /data/minicap/bin/armeabi-v7a/minicap-nopie: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/ztwo/Auto_Analysis/795db843dda0e677f20f1e1b16f38d945da83369/data/minicap/bin/armeabi-v7a/minicap-nopie -------------------------------------------------------------------------------- /data/minicap/bin/x86/minicap: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/ztwo/Auto_Analysis/795db843dda0e677f20f1e1b16f38d945da83369/data/minicap/bin/x86/minicap -------------------------------------------------------------------------------- /data/minicap/bin/x86/minicap-nopie: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/ztwo/Auto_Analysis/795db843dda0e677f20f1e1b16f38d945da83369/data/minicap/bin/x86/minicap-nopie -------------------------------------------------------------------------------- /data/minicap/bin/x86_64/minicap: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/ztwo/Auto_Analysis/795db843dda0e677f20f1e1b16f38d945da83369/data/minicap/bin/x86_64/minicap -------------------------------------------------------------------------------- /data/minicap/bin/x86_64/minicap-nopie: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/ztwo/Auto_Analysis/795db843dda0e677f20f1e1b16f38d945da83369/data/minicap/bin/x86_64/minicap-nopie -------------------------------------------------------------------------------- /data/minicap/minitouch/arm64-v8a/minitouch: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/ztwo/Auto_Analysis/795db843dda0e677f20f1e1b16f38d945da83369/data/minicap/minitouch/arm64-v8a/minitouch -------------------------------------------------------------------------------- /data/minicap/minitouch/arm64-v8a/minitouch-nopie: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/ztwo/Auto_Analysis/795db843dda0e677f20f1e1b16f38d945da83369/data/minicap/minitouch/arm64-v8a/minitouch-nopie -------------------------------------------------------------------------------- /data/minicap/minitouch/armeabi-v7a/minitouch: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/ztwo/Auto_Analysis/795db843dda0e677f20f1e1b16f38d945da83369/data/minicap/minitouch/armeabi-v7a/minitouch -------------------------------------------------------------------------------- /data/minicap/minitouch/armeabi-v7a/minitouch-nopie: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/ztwo/Auto_Analysis/795db843dda0e677f20f1e1b16f38d945da83369/data/minicap/minitouch/armeabi-v7a/minitouch-nopie -------------------------------------------------------------------------------- /data/minicap/minitouch/armeabi/minitouch: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/ztwo/Auto_Analysis/795db843dda0e677f20f1e1b16f38d945da83369/data/minicap/minitouch/armeabi/minitouch -------------------------------------------------------------------------------- /data/minicap/minitouch/armeabi/minitouch-nopie: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/ztwo/Auto_Analysis/795db843dda0e677f20f1e1b16f38d945da83369/data/minicap/minitouch/armeabi/minitouch-nopie -------------------------------------------------------------------------------- /data/minicap/minitouch/mips/minitouch: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/ztwo/Auto_Analysis/795db843dda0e677f20f1e1b16f38d945da83369/data/minicap/minitouch/mips/minitouch -------------------------------------------------------------------------------- /data/minicap/minitouch/mips/minitouch-nopie: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/ztwo/Auto_Analysis/795db843dda0e677f20f1e1b16f38d945da83369/data/minicap/minitouch/mips/minitouch-nopie -------------------------------------------------------------------------------- /data/minicap/minitouch/mips64/minitouch: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/ztwo/Auto_Analysis/795db843dda0e677f20f1e1b16f38d945da83369/data/minicap/minitouch/mips64/minitouch -------------------------------------------------------------------------------- /data/minicap/minitouch/mips64/minitouch-nopie: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/ztwo/Auto_Analysis/795db843dda0e677f20f1e1b16f38d945da83369/data/minicap/minitouch/mips64/minitouch-nopie -------------------------------------------------------------------------------- /data/minicap/minitouch/x86/minitouch: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/ztwo/Auto_Analysis/795db843dda0e677f20f1e1b16f38d945da83369/data/minicap/minitouch/x86/minitouch -------------------------------------------------------------------------------- /data/minicap/minitouch/x86/minitouch-nopie: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/ztwo/Auto_Analysis/795db843dda0e677f20f1e1b16f38d945da83369/data/minicap/minitouch/x86/minitouch-nopie -------------------------------------------------------------------------------- /data/minicap/minitouch/x86_64/minitouch: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/ztwo/Auto_Analysis/795db843dda0e677f20f1e1b16f38d945da83369/data/minicap/minitouch/x86_64/minitouch -------------------------------------------------------------------------------- /data/minicap/minitouch/x86_64/minitouch-nopie: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/ztwo/Auto_Analysis/795db843dda0e677f20f1e1b16f38d945da83369/data/minicap/minitouch/x86_64/minitouch-nopie -------------------------------------------------------------------------------- /data/minicap/shared/android-10/armeabi-v7a/minicap.so: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/ztwo/Auto_Analysis/795db843dda0e677f20f1e1b16f38d945da83369/data/minicap/shared/android-10/armeabi-v7a/minicap.so -------------------------------------------------------------------------------- /data/minicap/shared/android-14/armeabi-v7a/minicap.so: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/ztwo/Auto_Analysis/795db843dda0e677f20f1e1b16f38d945da83369/data/minicap/shared/android-14/armeabi-v7a/minicap.so -------------------------------------------------------------------------------- /data/minicap/shared/android-14/x86/minicap.so: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/ztwo/Auto_Analysis/795db843dda0e677f20f1e1b16f38d945da83369/data/minicap/shared/android-14/x86/minicap.so -------------------------------------------------------------------------------- /data/minicap/shared/android-15/armeabi-v7a/minicap.so: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/ztwo/Auto_Analysis/795db843dda0e677f20f1e1b16f38d945da83369/data/minicap/shared/android-15/armeabi-v7a/minicap.so -------------------------------------------------------------------------------- /data/minicap/shared/android-15/x86/minicap.so: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/ztwo/Auto_Analysis/795db843dda0e677f20f1e1b16f38d945da83369/data/minicap/shared/android-15/x86/minicap.so -------------------------------------------------------------------------------- /data/minicap/shared/android-16/armeabi-v7a/minicap.so: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/ztwo/Auto_Analysis/795db843dda0e677f20f1e1b16f38d945da83369/data/minicap/shared/android-16/armeabi-v7a/minicap.so -------------------------------------------------------------------------------- /data/minicap/shared/android-16/x86/minicap.so: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/ztwo/Auto_Analysis/795db843dda0e677f20f1e1b16f38d945da83369/data/minicap/shared/android-16/x86/minicap.so -------------------------------------------------------------------------------- /data/minicap/shared/android-17/armeabi-v7a/minicap.so: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/ztwo/Auto_Analysis/795db843dda0e677f20f1e1b16f38d945da83369/data/minicap/shared/android-17/armeabi-v7a/minicap.so -------------------------------------------------------------------------------- /data/minicap/shared/android-17/x86/minicap.so: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/ztwo/Auto_Analysis/795db843dda0e677f20f1e1b16f38d945da83369/data/minicap/shared/android-17/x86/minicap.so -------------------------------------------------------------------------------- /data/minicap/shared/android-18/armeabi-v7a/minicap.so: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/ztwo/Auto_Analysis/795db843dda0e677f20f1e1b16f38d945da83369/data/minicap/shared/android-18/armeabi-v7a/minicap.so -------------------------------------------------------------------------------- /data/minicap/shared/android-18/x86/minicap.so: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/ztwo/Auto_Analysis/795db843dda0e677f20f1e1b16f38d945da83369/data/minicap/shared/android-18/x86/minicap.so -------------------------------------------------------------------------------- /data/minicap/shared/android-19/armeabi-v7a/minicap.so: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/ztwo/Auto_Analysis/795db843dda0e677f20f1e1b16f38d945da83369/data/minicap/shared/android-19/armeabi-v7a/minicap.so -------------------------------------------------------------------------------- /data/minicap/shared/android-19/x86/minicap.so: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/ztwo/Auto_Analysis/795db843dda0e677f20f1e1b16f38d945da83369/data/minicap/shared/android-19/x86/minicap.so -------------------------------------------------------------------------------- /data/minicap/shared/android-21/arm64-v8a/minicap.so: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/ztwo/Auto_Analysis/795db843dda0e677f20f1e1b16f38d945da83369/data/minicap/shared/android-21/arm64-v8a/minicap.so -------------------------------------------------------------------------------- /data/minicap/shared/android-21/armeabi-v7a/minicap.so: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/ztwo/Auto_Analysis/795db843dda0e677f20f1e1b16f38d945da83369/data/minicap/shared/android-21/armeabi-v7a/minicap.so -------------------------------------------------------------------------------- /data/minicap/shared/android-21/x86/minicap.so: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/ztwo/Auto_Analysis/795db843dda0e677f20f1e1b16f38d945da83369/data/minicap/shared/android-21/x86/minicap.so -------------------------------------------------------------------------------- /data/minicap/shared/android-21/x86_64/minicap.so: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/ztwo/Auto_Analysis/795db843dda0e677f20f1e1b16f38d945da83369/data/minicap/shared/android-21/x86_64/minicap.so -------------------------------------------------------------------------------- /data/minicap/shared/android-22/arm64-v8a/minicap.so: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/ztwo/Auto_Analysis/795db843dda0e677f20f1e1b16f38d945da83369/data/minicap/shared/android-22/arm64-v8a/minicap.so -------------------------------------------------------------------------------- /data/minicap/shared/android-22/armeabi-v7a/minicap.so: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/ztwo/Auto_Analysis/795db843dda0e677f20f1e1b16f38d945da83369/data/minicap/shared/android-22/armeabi-v7a/minicap.so -------------------------------------------------------------------------------- /data/minicap/shared/android-22/x86/minicap.so: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/ztwo/Auto_Analysis/795db843dda0e677f20f1e1b16f38d945da83369/data/minicap/shared/android-22/x86/minicap.so -------------------------------------------------------------------------------- /data/minicap/shared/android-22/x86_64/minicap.so: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/ztwo/Auto_Analysis/795db843dda0e677f20f1e1b16f38d945da83369/data/minicap/shared/android-22/x86_64/minicap.so -------------------------------------------------------------------------------- /data/minicap/shared/android-9/armeabi-v7a/minicap.so: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/ztwo/Auto_Analysis/795db843dda0e677f20f1e1b16f38d945da83369/data/minicap/shared/android-9/armeabi-v7a/minicap.so -------------------------------------------------------------------------------- /data/minicap/shared/android-M/arm64-v8a/minicap.so: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/ztwo/Auto_Analysis/795db843dda0e677f20f1e1b16f38d945da83369/data/minicap/shared/android-M/arm64-v8a/minicap.so -------------------------------------------------------------------------------- /data/minicap/shared/android-M/armeabi-v7a/minicap.so: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/ztwo/Auto_Analysis/795db843dda0e677f20f1e1b16f38d945da83369/data/minicap/shared/android-M/armeabi-v7a/minicap.so -------------------------------------------------------------------------------- /data/minicap/shared/android-M/x86/minicap.so: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/ztwo/Auto_Analysis/795db843dda0e677f20f1e1b16f38d945da83369/data/minicap/shared/android-M/x86/minicap.so -------------------------------------------------------------------------------- /data/minicap/shared/android-M/x86_64/minicap.so: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/ztwo/Auto_Analysis/795db843dda0e677f20f1e1b16f38d945da83369/data/minicap/shared/android-M/x86_64/minicap.so -------------------------------------------------------------------------------- /data/test_info.ini: -------------------------------------------------------------------------------- 1 | [test_package_name] 2 | package_name = test.joko.com.myapplication 3 | 4 | [test_info] 5 | info = /Users/joko/Documents/Auto_Analysis/data/appium_parameter.yaml 6 | 7 | [test_install_path] 8 | path = /Users/joko/Documents/Auto_Analysis/data/app.apk 9 | 10 | [test_device] 11 | device = /Users/joko/Documents/Auto_Analysis/data/incidental/device_info.yaml 12 | 13 | [test_case] 14 | case = /Users/joko/Documents/Auto_Analysis/testcase 15 | log_file = /Users/joko/Documents/Auto_Analysis/result 16 | error_img = /Users/joko/Documents/Auto_Analysis/data/incidental/error.png 17 | 18 | [minicap] 19 | minicap_path = /Users/joko/Documents/Auto_Analysis/data/minicap/bin/{}/minicap 20 | minitouch_path = /Users/joko/Documents/Auto_Analysis/data/minicap/minitouch/{}/minitouch 21 | minicapso_path = /Users/joko/Documents/Auto_Analysis/data/minicap/shared/android-{}/{}/minicap.so 22 | 23 | [test_db] 24 | test_result = /Users/joko/Documents/Auto_Analysis/data/incidental/test.db 25 | 26 | -------------------------------------------------------------------------------- /demo_run.py: -------------------------------------------------------------------------------- 1 | # -*- coding: utf-8 -*- 2 | __author__ = 'joko' 3 | 4 | """ 5 | @author:joko 6 | @time: 16/11/16 下午2:05 7 | """ 8 | import os 9 | import sys 10 | 11 | main_view = os.path.split(os.path.realpath(sys.argv[0]))[0] 12 | main_view = main_view.replace('\\', '/') 13 | sys.path.append(main_view) 14 | import lib.Utils as U 15 | import public.CheckEnvironment 16 | 17 | 18 | def initialization_arrangement_case(): 19 | ini = U.ConfigIni() 20 | ini.set_ini('minicap', 'minicap_path', main_view + '/data/minicap/bin/{}/minicap') 21 | ini.set_ini('minicap', 'minitouch_path', main_view + '/data/minicap/minitouch/{}/minitouch') 22 | ini.set_ini('minicap', 'minicapso_path', main_view + '/data/minicap/shared/android-{}/{}/minicap.so') 23 | 24 | ini.set_ini('test_case', 'case', main_view + '/testcase') 25 | ini.set_ini('test_case', 'log_file', main_view + '/result') 26 | ini.set_ini('test_case', 'error_img', main_view + '/data/incidental/error.png') 27 | 28 | ini.set_ini('test_device', 'device', main_view + '/data/incidental/device_info.yaml') 29 | 30 | ini.set_ini('test_db', 'test_result', main_view + '/data/incidental/test.db') 31 | 32 | ini.set_ini('test_install_path', 'path', main_view + '/data/app.apk') 33 | ini.set_ini('test_info', 'info', main_view + '/data/appium_parameter.yaml') 34 | 35 | 36 | if __name__ == '__main__': 37 | public.CheckEnvironment.check_environment() 38 | initialization_arrangement_case() 39 | import run 40 | run.run_device() -------------------------------------------------------------------------------- /doc/appium_wiki.md: -------------------------------------------------------------------------------- 1 | ## appium文档 2 | 3 | * 官网中文文档:[点我](http://appium.io/slate/cn/v1.6.0/?python#) 4 | 5 | * 开源地址:[点我](https://github.com/appium/appium) 6 | 7 | ### 安装方法 8 | 9 | * 1: npm install -g cnpm --registry=https://registry.npm.taobao.org 10 | 11 | * 2: cnpm install -g appium --no-cache 12 | 13 | ### 注销安装unlock setting 14 | 15 | * 取消安装unlock和setting 16 | 路径:/Users/joko/appium/node_modules/appium-android-driver/lib/android-helpers.js 17 | // await helpers.pushSettingsApp(adb); 18 | // await helpers.pushUnlock(adb); 19 | -------------------------------------------------------------------------------- /doc/controls_operations.md: -------------------------------------------------------------------------------- 1 | ## 控件查找方法 2 | 3 | ### id查找方式 4 | * 利用Android sdk 自带的uiautomatorviewer工具 5 | 6 | ### xpath的查找方式 7 | 8 | (资料)[http://www.cnblogs.com/paulwinflo/p/4738904.html] 9 | 10 | * 根据文字的属性查找 11 | 12 | //android.widget.TextView[contains(@text,'19')] 13 | 14 | * 根据控件的index数组下标查找 15 | 16 | //android.widget.TextView[contains(@index,0)] 17 | 18 | * 根据相对路径来查找,从明显的分界来查找 19 | 20 | //android.widget.LinearLayout[1]/android.widget.FrameLayout/android.widget.ListView/android.widget.TextView[contains(@index,0)] -------------------------------------------------------------------------------- /doc/parameter_configuration.md: -------------------------------------------------------------------------------- 1 | ## 执行的参数配置 2 | 3 | 4 | * 配置 data/test_info.ini , 5 | 6 | ```ini 7 | 8 | [test_package_name] 9 | # 应用包名 10 | package_name = com.x.x.x 11 | 12 | [test_install_path] 13 | * 被测试应用地址 14 | path = /Users/joko/Auto_Analysis/data/app.apk 15 | 16 | [test_device] 17 | * device信息存放地址 18 | device = /Users/joko/Auto_Analysis/data/incidental/device_info.yaml 19 | 20 | [test_case] 21 | * 测试case存放地址 22 | case = /Users/joko/Auto_Analysis/testcase 23 | * 测试报告存放地址 24 | log_file = /Users/joko/Auto_Analysis/result 25 | * 错误图片展示地址 26 | error_img = /Users/joko/Auto_Analysis/data/incidental/error.png 27 | 28 | [minicap] 29 | * minicap地址:用于截图 30 | minicap_path = /Users/joko/Auto_Analysis/data/minicap/bin/{}/minicap 31 | minitouch_path = /Users/joko/Auto_Analysis/data/minicap/minitouch/{}/minitouch 32 | minicapso_path = /Users/joko/Auto_Analysis/data/minicap/shared/android-{}/{}/minicap.so 33 | 34 | [test_db] 35 | * 存放测试记录的db地址 36 | test_result = /Users/joko/Auto_Analysis/data/incidental/test.db 37 | 38 | ``` 39 | 40 | 注:windows路径也如此相同写法:d:/file/1.x 41 | 42 | * 配置 data/appium_parameter.yaml 43 | 44 | ```yaml 45 | --- 46 | - 47 | appPackage: 应用包名 48 | appActivity: 启动Activity名 49 | appWaitActivity: 等待的Activity名 50 | unicodeKeyboard: True 51 | resetKeyboard: True 52 | resetKeyboard: True 53 | noReset: False 54 | ``` 55 | -------------------------------------------------------------------------------- /doc/run.md: -------------------------------------------------------------------------------- 1 | ## Auto_Analysis配置文档 2 | 3 | #### 安装node 4 | 5 | [官网地址](https://nodejs.org/en/download/) 6 | 7 | * OS:brew install node 8 | * windows,linux参照官网 9 | 10 | #### 安装appium 11 | 12 | * 1: npm install -g cnpm --registry=https://registry.npm.taobao.org 13 | 14 | * 2: cnpm install -g appium --no-cache 15 | 16 | ### python 17 | 18 | 方法请自行查找 19 | 20 | ### 运行 21 | 22 | * git 源码 23 | * cd Auto_Analysis 24 | * python setup.py install 25 | * python run.py 26 | 27 | 注:性能图片生成用到了matplotlib,如果setup未安装成功 28 | 29 | * Linux:sudo apt-get install python-matplotlib 30 | 31 | ### 参数配置 32 | 33 | * 参见:parameter_configuration文档 34 | 35 | ### 用例编写 36 | 37 | * 参见test_case_writing文档 38 | 39 | ### 测试报告 40 | 41 | * 参见test_report文档 42 | 43 | ### 其他 44 | 45 | * appium:appium_wiki文档 46 | 47 | * 控件查找:controls_operations文档 48 | 49 | * 如果想单线程运行,请运行:po.integration -------------------------------------------------------------------------------- /doc/test_case_writing.md: -------------------------------------------------------------------------------- 1 | ## 测试用例编写规范 2 | 3 | * 1: 需要了解yaml格式编写规范,建议使用pycharm编写,自带yaml文档检查器. 4 | [yaml语法学习地址](http://www.ruanyifeng.com/blog/2016/07/yaml.html) 5 | * 2: 用例名不可重复,会影响用例的继承 6 | 7 | ## 测试用例字段解释 8 | 9 | 10 | | 字段 | 解释 | 演示 | 包含字段 | 是否必须 | 11 | | ----------------- | ---------- | --------- | --------- | ---- | 12 | | test_name | 用例名 | login | / | 是 | 13 | | test_id | 用例id | 0001 | / | 否 | 14 | | test_control_type | 查找控件方式 | xapth | xpath, id | 否 | 15 | | test_action | 操作方法 | click | 见下表 | 是 | 16 | | test_control | 控件 | com.xx.id | / | 否 | 17 | | test_text | 断言、输入文本 | test | / | 否 | 18 | | test_inherit | 继承用例名 | login | / | 否 | 19 | | test_range | 循环本步骤次数 | 2 | / | 否 | 20 | | test_sleep | 步骤执行后,等待秒 | 2 | / | 否 | 21 | | test_wait | 配合断言,等待控件秒 | 30 | / | 否 | 22 | 23 | 24 | | test_action | 解释 | 所有字段 | 配合字段 | 辅助配合字段 | 25 | | ----------- | ---- | ---------------------------------------- | ---------------------------------------- | --------- | 26 | | click | 点击 | click | test_control_type,test_control | / | 27 | | send_keys | 发送文本 | send_keys | test_control_type,test_control,test_text | / | 28 | | swipe | 滑动 | swipe_left,swipe_right,swipe_up,swipe_down | / | / | 29 | | assert | 断言 | assert | test_control_type,test_control,test_text | test_wait | 30 | | entity | 实体按键 | entity_home,entity_back,entity_menu,entity_volume_up,entity_volume_down,entity_enter | / | / | 31 | 32 | ## 完整用例范例,用例名:login 33 | 34 | ```yaml 35 | --- 36 | - 37 | test_name: 点击跳过 38 | test_id: 0001 39 | test_control_type: id 40 | test_action: click 41 | test_control: test.joko.com.myapplication:id/button1 42 | - 43 | test_name: 输入帐号名 44 | test_id: 0002 45 | test_control_type: id 46 | test_action: send_keys 47 | test_control: test.joko.com.myapplication:id/editText 48 | test_text: 199999999 49 | - 50 | test_name: 输入密码 51 | test_id: 0003 52 | test_control_type: id 53 | test_action: send_keys 54 | test_control: test.joko.com.myapplication:id/editText2 55 | test_text: 9999 56 | 57 | - 58 | test_name: 点击登录 59 | test_id: 0004 60 | test_control_type: xpath 61 | test_action: click 62 | test_control: //android.widget.Button[contains(@text,'确定')] 63 | 64 | - 65 | test_name: 向上滑动页面 66 | test_id: 0005 67 | test_action: swipe_up 68 | test_range: 3 69 | 70 | - 71 | test_name: 向下滑动页面 72 | test_id: 0005 73 | test_action: swipe_down 74 | test_range: 3 75 | 76 | ``` -------------------------------------------------------------------------------- /doc/test_report.md: -------------------------------------------------------------------------------- 1 | ## 测试报告样式 2 | 3 | #### 字段解释 4 | 5 | * end time:测试结束时间 6 | * 测试应用包名,测试应用版本号 7 | * 设备名,磁盘状态 已用|可用,wifi名称,系统版本,分辨率 8 | * all_case:用例总数,passed:通过数,failed失败数 9 | * case_name:用例名 10 | * case_result:用例结果 11 | * case_img:用例执行后截图 12 | * case_per:会比对上一次与本次的性能状态截图 13 | * case_log:本case全量log 14 | * case_filter_log:筛选后log -------------------------------------------------------------------------------- /doc/update_log.md: -------------------------------------------------------------------------------- 1 | # 更新日志 2 | 3 | 4 | * 2016-12-01:增加对quit方法的错误捕获,增加开关定位方法 5 | 6 | * 2016-11-30:修复minicap无法截图,完善部分文档 -------------------------------------------------------------------------------- /lib/ScreenShot.py: -------------------------------------------------------------------------------- 1 | # -*- coding: utf-8 -*- 2 | __author__ = 'joko' 3 | 4 | """ 5 | @author:joko 6 | @time: 16/11/8 下午2:52 7 | """ 8 | 9 | import os 10 | import Utils as U 11 | import adbUtils 12 | import random 13 | 14 | 15 | class minicap(): 16 | def __init__(self, device): 17 | self.adb = adbUtils.ADB(device) 18 | ini = U.ConfigIni() 19 | self.minicap_path = ini.get_ini( 20 | 'minicap', 'minicap_path').format( 21 | self.adb.get_cpu_version()) 22 | self.minitouch_path = ini.get_ini( 23 | 'minicap', 'minitouch_path').format( 24 | self.adb.get_cpu_version()) 25 | self.minicapSO_path = ini.get_ini( 26 | 'minicap', 'minicapSO_path').format( 27 | self.adb.get_sdk_version(), self.adb.get_cpu_version()) 28 | 29 | def push_minicap(self): 30 | U.Logging.info('push_Cpu_minicap:' + self.minicap_path) 31 | self.adb.adb('push %s /data/local/tmp' % self.minicap_path) 32 | 33 | def push_minicaptouch(self): 34 | U.Logging.info('push_touch_minitouch:' + self.minitouch_path) 35 | self.adb.shell('chmod 777 /data/local/tmp/minitouch') 36 | 37 | def push_minicapSO(self): 38 | U.Logging.info('push_sdk_minicap.so:' + self.minicapSO_path) 39 | self.adb.adb('push %s /data/local/tmp' % self.minicapSO_path) 40 | self.adb.shell('chmod 777 /data/local/tmp/minicap') 41 | 42 | def check_minicap(self): 43 | width = None 44 | height = None 45 | for i in self.adb.shell( 46 | "'LD_LIBRARY_PATH=/data/local/tmp /data/local/tmp/minicap -i'").stdout.readlines(): 47 | if 'secure' in i and 'true' in i: 48 | U.Logging.info('push_minicap_sdk:success') 49 | elif 'width' in i: 50 | width = i.strip().split(':')[1].strip().split(',')[0] 51 | elif 'height' in i: 52 | height = i.strip().split(':')[1].strip().split(',')[0] 53 | 54 | if width is not None: 55 | return width, height 56 | return width, height 57 | 58 | def phone_screen(self, width_height): 59 | """ 60 | 截图 61 | :param width_height: 宽高 62 | :param filename: 存储的文件名 63 | :return: 64 | """ 65 | 66 | U.Logging.info('phone_screen:%s' % width_height) 67 | filename = str(random.randint(1,1000)) 68 | self.adb.shell( 69 | "'LD_LIBRARY_PATH=/data/local/tmp /data/local/tmp/minicap -P {}/0 -s > /data/local/tmp/{}.png'".format( 70 | width_height, filename)) 71 | U.Logging.info('phone_screen:success') 72 | return filename 73 | 74 | def pull_screen(self, filename, computer_path): 75 | self.adb.pull( 76 | '/data/local/tmp/{}.png'.format(filename), 77 | computer_path) 78 | 79 | def main(self,computer_path): 80 | if os.path.exists(self.minicapSO_path): 81 | width, height = self.check_minicap() 82 | width_height = '{}x{}@{}x{}'.format(width, height, width, height) 83 | # U.Logging.info('main:filename:%s' % filename) 84 | 85 | if 'None' not in width_height: 86 | U.Logging.info('main:exist minicap') 87 | filename = self.phone_screen(width_height) 88 | U.sleep(0.3) 89 | self.pull_screen(filename, computer_path) 90 | else: 91 | U.Logging.info('main:does not exist minicap') 92 | self.push_minicap() 93 | self.push_minicaptouch() 94 | self.push_minicapSO() 95 | U.sleep(0.3) 96 | width, height = self.check_minicap() 97 | width_height = '{}x{}@{}x{}'.format( 98 | width, height, width, height) 99 | filename = self.phone_screen(width_height) 100 | U.sleep(0.3) 101 | self.pull_screen(filename, computer_path) 102 | self.adb.rm_minicap_jpg(filename) 103 | else: 104 | U.Logging.warn('not minicap found ,check the directory') 105 | self.adb.screen_shot(computer_path) 106 | 107 | 108 | if __name__ == '__main__': 109 | a = minicap('BX903JC3WS') 110 | a.main('/Users/joko/Documents/Auto_Analysis/result/2016-11-30_14_00_1521/img/2.png') 111 | -------------------------------------------------------------------------------- /lib/Utils.py: -------------------------------------------------------------------------------- 1 | # -*- coding: utf-8 -*- 2 | __author__ = 'joko' 3 | 4 | """ 5 | @author:joko 6 | @time: 16/11/8 下午2:52 7 | """ 8 | import time 9 | import subprocess 10 | import os 11 | import sys 12 | import ConfigParser 13 | import sqlite3 14 | import re 15 | 16 | 17 | def get_now_time(): 18 | return time.strftime("%Y-%m-%d %H:%M:%S", time.localtime(time.time())) 19 | 20 | 21 | def sleep(s): 22 | return time.sleep(s) 23 | 24 | 25 | def cmd(cmd): 26 | return subprocess.Popen(cmd, shell=True, stdout=subprocess.PIPE, stderr=subprocess.PIPE,bufsize=1,close_fds=True) 27 | 28 | 29 | class ConfigIni(): 30 | def __init__(self): 31 | self.current_directory = os.path.split( 32 | os.path.realpath(sys.argv[0]))[0] 33 | self.path = os.path.split(__file__)[0].replace('lib','data/test_info.ini') 34 | self.cf = ConfigParser.ConfigParser() 35 | 36 | self.cf.read(self.path) 37 | 38 | def get_ini(self, title, value): 39 | return self.cf.get(title, value) 40 | 41 | def set_ini(self, title, value, text): 42 | self.cf.set(title, value, text) 43 | return self.cf.write(open(self.path, "wb")) 44 | 45 | def add_ini(self, title): 46 | self.cf.add_section(title) 47 | return self.cf.write(open(self.path)) 48 | 49 | def get_options(self, data): 50 | # 获取所有的section 51 | options = self.cf.options(data) 52 | return options 53 | 54 | 55 | class colour: 56 | @staticmethod 57 | def c(msg, colour): 58 | try: 59 | from termcolor import colored, cprint 60 | p = lambda x: cprint(x, '%s' % colour) 61 | return p(msg) 62 | except: 63 | print (msg) 64 | 65 | @staticmethod 66 | def show_verbose(msg): 67 | colour.c(msg, 'white') 68 | 69 | @staticmethod 70 | def show_debug(msg): 71 | colour.c(msg, 'blue') 72 | 73 | @staticmethod 74 | def show_info(msg): 75 | colour.c(msg, 'green') 76 | 77 | @staticmethod 78 | def show_warn(msg): 79 | colour.c(msg, 'yellow') 80 | 81 | @staticmethod 82 | def show_error(msg): 83 | colour.c(msg, 'red') 84 | 85 | 86 | class Logging: 87 | flag = True 88 | 89 | @staticmethod 90 | def error(msg): 91 | if Logging.flag == True: 92 | # print get_now_time() + " [Error]:" + "".join(msg) 93 | colour.show_error(get_now_time() + " [Error]:" + "".join(msg)) 94 | 95 | @staticmethod 96 | def warn(msg): 97 | if Logging.flag == True: 98 | colour.show_warn(get_now_time() + " [Warn]:" + "".join(msg)) 99 | 100 | @staticmethod 101 | def info(msg): 102 | if Logging.flag == True: 103 | colour.show_info(get_now_time() + " [Info]:" + "".join(msg)) 104 | 105 | @staticmethod 106 | def debug(msg): 107 | if Logging.flag == True: 108 | colour.show_debug(get_now_time() + " [Debug]:" + "".join(msg)) 109 | 110 | @staticmethod 111 | def success(msg): 112 | if Logging.flag == True: 113 | colour.show_verbose(get_now_time() + " [Success]:" + "".join(msg)) 114 | 115 | 116 | def l(): 117 | """ 118 | 打印log 119 | 文件名+函数名,return 120 | :return: 121 | """ 122 | 123 | def log(func): 124 | def wrapper(*args, **kwargs): 125 | t = func(*args, **kwargs) 126 | filename = str(sys.argv[0]).split('/')[-1].split('.')[0] 127 | Logging.success('{}:{}, return:{}'.format(filename, func.__name__, t)) 128 | return t 129 | 130 | return wrapper 131 | 132 | return log 133 | 134 | 135 | class Asql: 136 | def __init__(self, ): 137 | ini = ConfigIni() 138 | test_db_path = ini.get_ini('test_db', 'test_result') 139 | self.conn = sqlite3.connect(test_db_path) 140 | self.cursor = self.conn.cursor() 141 | self.__is_table() 142 | 143 | def execute(self, *args, **kwargs): 144 | """ 145 | 146 | :param args: 147 | :param kwargs: 148 | :return: 提交数据 149 | """ 150 | self.cursor.execute(*args, **kwargs) 151 | 152 | def close(self): 153 | self.cursor.close() 154 | self.conn.commit() 155 | self.conn.close() 156 | 157 | def __is_table(self): 158 | """ 159 | 判断表是否存在 160 | :return: 161 | """ 162 | self.cursor.execute("SELECT count(*) FROM sqlite_master WHERE type='table' AND name='test_results'") 163 | row = self.cursor.fetchone() 164 | if row[0] != 1: 165 | self.__built_table() 166 | 167 | def __built_table(self): 168 | """ 169 | 建表 170 | :return: 171 | """ 172 | self.execute(""" 173 | CREATE TABLE test_results 174 | ( 175 | case_id INTEGER PRIMARY KEY, 176 | case_name TEXT, 177 | device_name TEXT, 178 | cpu_list TEXT, 179 | mem_list TEXT, 180 | execution_status TEXT, 181 | created_time DATETIME DEFAULT (datetime('now', 'localtime')) 182 | );""") 183 | 184 | def insert_per(self, case_name, device_name, cpu_list, mem_list, execution_status, ): 185 | key = "(case_name,device_name,cpu_list,mem_list,execution_status,created_time)" 186 | values = "('{}','{}','{}','{}','{}','{}')" \ 187 | .format(case_name, device_name, cpu_list, mem_list, execution_status, get_now_time()) 188 | self.execute("INSERT INTO test_results {} VALUES {}".format(key, values)) 189 | 190 | def select_per(self, case_name, device_name): 191 | statement = "select * from test_results where " \ 192 | "case_name = '{}' " \ 193 | "and " \ 194 | "device_name = '{}' " \ 195 | "and " \ 196 | "execution_status = 1 " \ 197 | "order by created_time desc".format(case_name, device_name) 198 | self.cursor.execute(statement) 199 | row = self.cursor.fetchone() 200 | if row is not None: 201 | cpu = re.findall(r"\d+\.?\d*", row[3]) 202 | mem = re.findall(r"\d+\.?\d*", row[4]) 203 | return [int(i) for i in cpu], [int(i) for i in mem] 204 | else: 205 | return None 206 | 207 | 208 | if __name__ == '__main__': 209 | a = Asql() 210 | print (a.select_per('login1', 'sanxing')) 211 | a.close() 212 | -------------------------------------------------------------------------------- /lib/__init__.py: -------------------------------------------------------------------------------- 1 | # -*- coding: utf-8 -*- 2 | __author__ = 'joko' 3 | 4 | """ 5 | @author:joko 6 | @time: 16/11/17 上午10:13 7 | """ -------------------------------------------------------------------------------- /lib/adbUtils.py: -------------------------------------------------------------------------------- 1 | # -*- coding: utf-8 -*- 2 | __author__ = 'joko' 3 | 4 | """ 5 | @author:joko 6 | @time: 16/11/8 下午2:52 7 | """ 8 | import platform 9 | import subprocess 10 | import re 11 | from time import sleep 12 | import time 13 | import os 14 | import random 15 | 16 | PATH = lambda p: os.path.abspath(p) 17 | 18 | # 判断系统类型,windows使用findstr,linux使用grep 19 | system = platform.system() 20 | if system is "Windows": 21 | find_util = "findstr" 22 | else: 23 | find_util = "grep" 24 | 25 | # 判断是否设置环境变量ANDROID_HOME 26 | if "ANDROID_HOME" in os.environ: 27 | if system == "Windows": 28 | command = os.path.join( 29 | os.environ["ANDROID_HOME"], 30 | "platform-tools", 31 | "adb.exe") 32 | else: 33 | command = os.path.join( 34 | os.environ["ANDROID_HOME"], 35 | "platform-tools", 36 | "adb") 37 | else: 38 | raise EnvironmentError( 39 | "Adb not found in $ANDROID_HOME path: %s." % 40 | os.environ["ANDROID_HOME"]) 41 | 42 | 43 | class ADB(object): 44 | """ 45 | 单个设备,可不传入参数device_id 46 | """ 47 | 48 | def __init__(self, device_id=""): 49 | if device_id == "": 50 | self.device_id = "" 51 | else: 52 | self.device_id = "-s %s" % device_id 53 | 54 | def adb(self, args): 55 | cmd = "%s %s %s" % (command, self.device_id, str(args)) 56 | return subprocess.Popen( 57 | cmd, 58 | shell=True, 59 | stdout=subprocess.PIPE, 60 | stderr=subprocess.PIPE) 61 | 62 | def shell(self, args): 63 | cmd = "%s %s shell %s" % (command, self.device_id, str(args),) 64 | return subprocess.Popen( 65 | cmd, 66 | shell=True, 67 | stdout=subprocess.PIPE, 68 | stderr=subprocess.PIPE) 69 | 70 | def get_device_state(self): 71 | """ 72 | 获取设备状态: offline | bootloader | device 73 | """ 74 | return self.adb("get-state").stdout.read().strip() 75 | 76 | def get_device_id(self): 77 | """ 78 | 获取设备id号,return serialNo 79 | """ 80 | return self.adb("get-serialno").stdout.read().strip() 81 | 82 | def get_android_version(self): 83 | """ 84 | 获取设备中的Android版本号,如4.2.2 85 | """ 86 | return self.shell( 87 | "getprop ro.build.version.release").stdout.read().strip() 88 | 89 | def get_sdk_version(self): 90 | """ 91 | 获取设备SDK版本号 92 | """ 93 | return self.shell("getprop ro.build.version.sdk").stdout.read().strip() 94 | 95 | def get_device_model(self): 96 | """ 97 | 获取设备型号 98 | """ 99 | return self.shell("getprop ro.product.model").stdout.read().strip() 100 | 101 | def get_pid(self, package_name): 102 | """ 103 | 获取进程pid 104 | args: 105 | - packageName -: 应用包名 106 | usage: getPid("com.android.settings") 107 | """ 108 | if system is "Windows": 109 | pidinfo = self.shell( 110 | "ps | findstr %s$" % 111 | package_name).stdout.read() 112 | else: 113 | pidinfo = self.shell( 114 | "ps | %s -w %s" % 115 | (find_util, package_name)).stdout.read() 116 | 117 | if pidinfo == '': 118 | return "the process doesn't exist." 119 | 120 | pattern = re.compile(r"\d+") 121 | result = pidinfo.split(" ") 122 | result.remove(result[0]) 123 | 124 | return pattern.findall(" ".join(result))[0] 125 | 126 | def kill_process(self, pid): 127 | """ 128 | 杀死应用进程 129 | args: 130 | - pid -: 进程pid值 131 | usage: killProcess(154) 132 | 注:杀死系统应用进程需要root权限 133 | """ 134 | if self.shell("kill %s" % 135 | str(pid)).stdout.read().split(": ")[-1] == "": 136 | return "kill success" 137 | else: 138 | return self.shell("kill %s" % 139 | str(pid)).stdout.read().split(": ")[-1] 140 | 141 | def quit_app(self, package_name): 142 | """ 143 | 退出app,类似于kill掉进程 144 | usage: quitApp("com.android.settings") 145 | """ 146 | self.shell("am force-stop %s" % package_name) 147 | 148 | # def get_focused_package_and_activity(self): 149 | # """ 150 | # 获取当前应用界面的包名和Activity,返回的字符串格式为:packageName/activityName 151 | # """ 152 | # pattern = re.compile(r"[a-zA-Z0-9.]+/.[a-zA-Z0-9.]+") 153 | # out = self.shell( 154 | # "dumpsys window w | %s \/ | %s name=" % 155 | # (find_util, find_util)).stdout.read().strip() 156 | # 157 | # return pattern.findall(out)[0] 158 | 159 | def get_focused_package_and_activity(self): 160 | """ 161 | 获取当前应用界面的包名和Activity,返回的字符串格式为:packageName/activityName 162 | """ 163 | out = self.shell( 164 | "dumpsys activity activities | %s mFocusedActivity" % 165 | find_util).stdout.read().strip().split(' ')[3] 166 | return out 167 | 168 | def get_current_package_name(self): 169 | """ 170 | 获取当前运行的应用的包名 171 | """ 172 | return self.get_focused_package_and_activity().split("/")[0] 173 | 174 | def get_current_activity(self): 175 | """ 176 | 获取当前运行应用的activity 177 | """ 178 | return self.get_focused_package_and_activity().split("/")[-1] 179 | 180 | def get_battery_level(self): 181 | """ 182 | 获取电池电量 183 | """ 184 | level = self.shell("dumpsys battery | %s level" % 185 | find_util).stdout.read().split(": ")[-1] 186 | 187 | return int(level) 188 | 189 | def get_backstage_services(self, page_name): 190 | """ 191 | 192 | :return: 指定应用后台运行的services 193 | """ 194 | services_list = [] 195 | for line in self.shell( 196 | 'dumpsys activity services %s' % 197 | page_name).stdout.readlines(): 198 | if line.strip().startswith('intent'): 199 | service_name = line.strip().split('=')[-1].split('}')[0] 200 | if service_name not in services_list: 201 | services_list.append(service_name) 202 | 203 | return services_list 204 | 205 | def get_current_backstage_services(self): 206 | """ 207 | 208 | :return: 当前应用后台运行的services 209 | """ 210 | package = self.get_current_package_name() 211 | return self.get_backstage_services(package) 212 | 213 | def get_battery_status(self): 214 | """ 215 | 获取电池充电状态 216 | BATTERY_STATUS_UNKNOWN:未知状态 217 | BATTERY_STATUS_CHARGING: 充电状态 218 | BATTERY_STATUS_DISCHARGING: 放电状态 219 | BATTERY_STATUS_NOT_CHARGING:未充电 220 | BATTERY_STATUS_FULL: 充电已满 221 | """ 222 | status_dict = {1: "BATTERY_STATUS_UNKNOWN", 223 | 2: "BATTERY_STATUS_CHARGING", 224 | 3: "BATTERY_STATUS_DISCHARGING", 225 | 4: "BATTERY_STATUS_NOT_CHARGING", 226 | 5: "BATTERY_STATUS_FULL"} 227 | status = self.shell("dumpsys battery | %s status" % 228 | find_util).stdout.read().split(": ")[-1] 229 | 230 | return status_dict[int(status)] 231 | 232 | def get_battery_temp(self): 233 | """ 234 | 获取电池温度 235 | """ 236 | temp = self.shell("dumpsys battery | %s temperature" % 237 | find_util).stdout.read().split(": ")[-1] 238 | 239 | return int(temp) / 10.0 240 | 241 | def get_screen_resolution(self): 242 | """ 243 | 获取设备屏幕分辨率,return (width, high) 244 | """ 245 | pattern = re.compile(r"\d+") 246 | out = self.shell( 247 | "dumpsys display | %s DisplayDeviceInfo" % 248 | find_util).stdout.read() 249 | display = pattern.findall(out) 250 | 251 | return int(display[0]), int(display[1]) 252 | 253 | def reboot(self): 254 | """ 255 | 重启设备 256 | """ 257 | self.adb("reboot") 258 | 259 | def fast_boot(self): 260 | """ 261 | 进入fastboot模式 262 | """ 263 | self.adb("reboot bootloader") 264 | 265 | def get_system_app_list(self): 266 | """ 267 | 获取设备中安装的系统应用包名列表 268 | """ 269 | sysApp = [] 270 | for packages in self.shell("pm list packages -s").stdout.readlines(): 271 | sysApp.append(packages.split(":")[-1].splitlines()[0]) 272 | 273 | return sysApp 274 | 275 | def get_third_app_list(self): 276 | """ 277 | 获取设备中安装的第三方应用包名列表 278 | """ 279 | thirdApp = [] 280 | for packages in self.shell("pm list packages -3").stdout.readlines(): 281 | thirdApp.append(packages.split(":")[-1].splitlines()[0]) 282 | 283 | return thirdApp 284 | 285 | def get_matching_app_list(self, keyword): 286 | """ 287 | 模糊查询与keyword匹配的应用包名列表 288 | usage: getMatchingAppList("qq") 289 | """ 290 | matApp = [] 291 | for packages in self.shell( 292 | "pm list packages %s" % 293 | keyword).stdout.readlines(): 294 | matApp.append(packages.split(":")[-1].splitlines()[0]) 295 | 296 | return matApp 297 | 298 | def get_app_start_total_time(self, component): 299 | """ 300 | 获取启动应用所花时间 301 | usage: getAppStartTotalTime("com.android.settings/.Settings") 302 | """ 303 | time = self.shell("am start -W %s | %s TotalTime" % 304 | (component, find_util)).stdout.read().split(": ")[-1] 305 | return int(time) 306 | 307 | def install_app(self, app_file): 308 | """ 309 | 安装app,app名字不能含中文字符 310 | args: 311 | - appFile -: app路径 312 | usage: install("/Users/joko/Downloads/1.apk") 313 | INSTALL_FAILED_ALREADY_EXISTS 应用已经存在,或卸载了但没卸载干净 adb install 时使用 -r 参数,或者先 adb uninstall 再安装 314 | INSTALL_FAILED_INVALID_APK 无效的 APK 文件 315 | INSTALL_FAILED_INVALID_URI 无效的 APK 文件名 确保 APK 文件名里无中文 316 | INSTALL_FAILED_INSUFFICIENT_STORAGE 空间不足 清理空间 317 | INSTALL_FAILED_DUPLICATE_PACKAGE 已经存在同名程序 318 | INSTALL_FAILED_NO_SHARED_USER 请求的共享用户不存在 319 | INSTALL_FAILED_UPDATE_INCOMPATIBLE 以前安装过同名应用,但卸载时数据没有移除 先 adb uninstall 再安装 320 | INSTALL_FAILED_SHARED_USER_INCOMPATIBLE 请求的共享用户存在但签名不一致 321 | INSTALL_FAILED_MISSING_SHARED_LIBRARY 安装包使用了设备上不可用的共享库 322 | INSTALL_FAILED_REPLACE_COULDNT_DELETE 替换时无法删除 323 | INSTALL_FAILED_DEXOPT dex 优化验证失败或空间不足 324 | INSTALL_FAILED_OLDER_SDK 设备系统版本低于应用要求 325 | INSTALL_FAILED_CONFLICTING_PROVIDER 设备里已经存在与应用里同名的 content provider 326 | INSTALL_FAILED_NEWER_SDK 设备系统版本高于应用要求 327 | INSTALL_FAILED_TEST_ONLY 应用是 test-only 的,但安装时没有指定 -t 参数 328 | INSTALL_FAILED_CPU_ABI_INCOMPATIBLE 包含不兼容设备 CPU 应用程序二进制接口的 native code 329 | INSTALL_FAILED_MISSING_FEATURE 应用使用了设备不可用的功能 330 | INSTALL_FAILED_CONTAINER_ERROR sdcard 访问失败 确认 sdcard 可用,或者安装到内置存储 331 | INSTALL_FAILED_INVALID_INSTALL_LOCATION 不能安装到指定位置 切换安装位置,添加或删除 -s 参数 332 | INSTALL_FAILED_MEDIA_UNAVAILABLE 安装位置不可用 一般为 sdcard,确认 sdcard 可用或安装到内置存储 333 | INSTALL_FAILED_VERIFICATION_TIMEOUT 验证安装包超时 334 | INSTALL_FAILED_VERIFICATION_FAILURE 验证安装包失败 335 | INSTALL_FAILED_PACKAGE_CHANGED 应用与调用程序期望的不一致 336 | INSTALL_FAILED_UID_CHANGED 以前安装过该应用,与本次分配的 UID 不一致 清除以前安装过的残留文件 337 | INSTALL_FAILED_VERSION_DOWNGRADE 已经安装了该应用更高版本 使用 -d 参数 338 | INSTALL_FAILED_PERMISSION_MODEL_DOWNGRADE 已安装 target SDK 支持运行时权限的同名应用,要安装的版本不支持运行时权限 339 | INSTALL_PARSE_FAILED_NOT_APK 指定路径不是文件,或不是以 .apk 结尾 340 | INSTALL_PARSE_FAILED_BAD_MANIFEST 无法解析的 AndroidManifest.xml 文件 341 | INSTALL_PARSE_FAILED_UNEXPECTED_EXCEPTION 解析器遇到异常 342 | INSTALL_PARSE_FAILED_NO_CERTIFICATES 安装包没有签名 343 | INSTALL_PARSE_FAILED_INCONSISTENT_CERTIFICATES 已安装该应用,且签名与 APK 文件不一致 先卸载设备上的该应用,再安装 344 | INSTALL_PARSE_FAILED_CERTIFICATE_ENCODING 解析 APK 文件时遇到 CertificateEncodingException 345 | INSTALL_PARSE_FAILED_BAD_PACKAGE_NAME manifest 文件里没有或者使用了无效的包名 346 | INSTALL_PARSE_FAILED_BAD_SHARED_USER_ID manifest 文件里指定了无效的共享用户 ID 347 | INSTALL_PARSE_FAILED_MANIFEST_MALFORMED 解析 manifest 文件时遇到结构性错误 348 | INSTALL_PARSE_FAILED_MANIFEST_EMPTY 在 manifest 文件里找不到找可操作标签(instrumentation 或 application) 349 | INSTALL_FAILED_INTERNAL_ERROR 因系统问题安装失败 350 | INSTALL_FAILED_USER_RESTRICTED 用户被限制安装应用 351 | INSTALL_FAILED_DUPLICATE_PERMISSION 应用尝试定义一个已经存在的权限名称 352 | INSTALL_FAILED_NO_MATCHING_ABIS 应用包含设备的应用程序二进制接口不支持的 native code 353 | INSTALL_CANCELED_BY_USER 应用安装需要在设备上确认,但未操作设备或点了取消 在设备上同意安装 354 | INSTALL_FAILED_ACWF_INCOMPATIBLE 应用程序与设备不兼容 355 | does not contain AndroidManifest.xml 无效的 APK 文件 356 | is not a valid zip file 无效的 APK 文件 357 | Offline 设备未连接成功 先将设备与 adb 连接成功 358 | unauthorized 设备未授权允许调试 359 | error: device not found 没有连接成功的设备 先将设备与 adb 连接成功 360 | protocol failure 设备已断开连接 先将设备与 adb 连接成功 361 | Unknown option: -s Android 2.2 以下不支持安装到 sdcard 不使用 -s 参数 362 | No space left on devicerm 空间不足 清理空间 363 | Permission denied ... sdcard ... sdcard 不可用 364 | """ 365 | # for line in self.adb("install -r %s" % app_file).stdout.readlines(): 366 | # if 'Failure' in line: 367 | # print line.strip() 368 | return self.adb("install -r %s" % app_file) 369 | 370 | def is_install(self, packageName): 371 | """ 372 | 判断应用是否安装,已安装返回True,否则返回False 373 | usage: isInstall("com.example.apidemo") 374 | """ 375 | if self.get_matching_app_list(packageName): 376 | return True 377 | else: 378 | return False 379 | 380 | def remove_app(self, packageName): 381 | """ 382 | 卸载应用 383 | args: 384 | - packageName -:应用包名,非apk名 385 | """ 386 | return self.adb("uninstall %s" % packageName) 387 | 388 | def clear_app_data(self, packageName): 389 | """ 390 | 清除应用用户数据 391 | usage: clearAppData("com.android.contacts") 392 | """ 393 | if "Success" in self.shell( 394 | "pm clear %s" % 395 | packageName).stdout.read().splitlines(): 396 | return "clear user data success " 397 | else: 398 | return "make sure package exist" 399 | 400 | def reset_current_app(self): 401 | """ 402 | 重置当前应用 403 | """ 404 | packageName = self.get_current_package_name() 405 | component = self.get_focused_package_and_activity() 406 | self.clear_app_data(packageName) 407 | self.start_activity(component) 408 | 409 | def get_app_install_path(self, path_name): 410 | """ 411 | 获取第三方应用安装地址 412 | :return: 413 | """ 414 | t = self.shell("pm path %s" % path_name).stdout.readlines() 415 | return ''.join(t).strip().split(':')[1] 416 | 417 | def pull_install_app(self, save_path): 418 | """ 419 | 获取当前Android设备第三方应用包,并且pull到本地 420 | :param save_path: 存放路径 421 | :return: 422 | """ 423 | for app_package_name in self.get_third_app_list(): 424 | install_app_path = self.get_app_install_path(app_package_name) 425 | self.pull(install_app_path, save_path + '/' + app_package_name + '.apk') 426 | 427 | def start_activity(self, component): 428 | """ 429 | 启动一个Activity 430 | usage: startActivity(component = "com.android.settinrs/.Settings") 431 | """ 432 | self.shell("am start -n %s" % component) 433 | 434 | def start_web_page(self, url): 435 | """ 436 | 使用系统默认浏览器打开一个网页 437 | usage: startWebpage("http://www.baidu.com") 438 | """ 439 | self.shell("am start -a android.intent.action.VIEW -d %s" % url) 440 | 441 | def call_phone(self, number): 442 | """ 443 | 启动拨号器拨打电话 444 | usage: callPhone(10086) 445 | """ 446 | self.shell( 447 | "am start -a android.intent.action.CALL -d tel:%s" % 448 | str(number)) 449 | 450 | def send_key_event(self, keycode): 451 | """ 452 | 发送一个按键事件 453 | args: 454 | - keycode -: 455 | http://developer.android.com/reference/android/view/KeyEvent.html 456 | usage: sendKeyEvent(keycode.HOME) 457 | """ 458 | self.shell("input keyevent %s" % str(keycode)) 459 | sleep(0.5) 460 | 461 | def long_press_key(self, keycode): 462 | """ 463 | 发送一个按键长按事件,Android 4.4以上 464 | usage: longPressKey(keycode.HOME) 465 | """ 466 | self.shell("input keyevent --longpress %s" % str(keycode)) 467 | sleep(0.5) 468 | 469 | def touch(self, e=None, x=None, y=None): 470 | """ 471 | 触摸事件 472 | usage: touch(e), touch(x=0.5,y=0.5) 473 | """ 474 | width, high = self.get_screen_resolution() 475 | if (e is not None): 476 | x = e[0] 477 | y = e[1] 478 | if (0 < x < 1): 479 | x = x * width 480 | if (0 < y < 1): 481 | y = y * high 482 | 483 | self.shell("input tap %s %s" % (str(x), str(y))) 484 | sleep(0.5) 485 | 486 | def get_focused_package_xml(self, save_path): 487 | file_name = random.randint(10, 99) 488 | self.shell( 489 | 'uiautomator dump /data/local/tmp/{}.xml'.format(file_name)).communicate() 490 | self.adb('pull /data/local/tmp/{}.xml {}'.format(file_name, 491 | save_path)).communicate() 492 | 493 | def touch_by_element(self, element): 494 | """ 495 | 点击元素 496 | usage: touchByElement(Element().findElementByName(u"计算器")) 497 | """ 498 | self.shell("input tap %s %s" % (str(element[0]), str(element[1]))) 499 | sleep(0.5) 500 | 501 | def touch_by_ratio(self, ratioWidth, ratioHigh): 502 | """ 503 | 通过比例发送触摸事件 504 | args: 505 | - ratioWidth -:width占比, 0 %s&' % (log_path)) 841 | 842 | def get_cpu_version(self): 843 | """ 844 | 获取cpu基带版本 845 | :return: arm64-v8a 846 | """ 847 | t = self.shell( 848 | "getprop ro.product.cpu.abi | tr -d '\r'").stdout.readlines() 849 | return ''.join(t).strip() 850 | 851 | def pull(self, remote_file, local_file): 852 | """ 853 | 854 | :param remote_file: 拉取文件地址 855 | :param local_file: 存放文件地址 856 | :return: 857 | """ 858 | return self.adb('pull %s %s' % (remote_file, local_file)) 859 | 860 | def rm(self, remote_file): 861 | """ 862 | 863 | :param remote_file: 删除文件地址 864 | :return: 865 | """ 866 | return self.shell(remote_file) 867 | 868 | def rm_minicap_jpg(self, remote_file): 869 | """ 870 | 871 | :param remote_file: 删除minicap图片缓存 872 | :return: 873 | """ 874 | self.rm('rm -r /data/local/tmp/%s.jpg' % (remote_file)) 875 | 876 | def get_disk(self): 877 | """ 878 | 获取手机磁盘信息 879 | :return: Used:用户占用,Free:剩余空间 880 | """ 881 | for s in self.shell('df').stdout.readlines(): 882 | if '/mnt/shell/emulated' in s or '/storage/sdcard0' in s: 883 | lst = [] 884 | for i in s.split(' '): 885 | if i: 886 | lst.append(i) 887 | return 'Used:%s,Free:%s' % (lst[2], lst[3]) 888 | 889 | def get_dmesg(self): 890 | """ 891 | 892 | :return:内核日志 893 | """ 894 | t = self.shell("dmesg").stdout.readlines() 895 | return ''.join(t).strip() 896 | 897 | def get_device_name(self): 898 | """ 899 | 900 | :return: 设备名 :SM-G9006W 901 | """ 902 | t = self.shell("getprop ro.product.model").stdout.readlines() 903 | return ''.join(t).strip() 904 | 905 | def get_battery(self): 906 | """ 907 | 908 | :return:全部电量相关信息 909 | """ 910 | t = self.shell("dumpsys battery").stdout.readlines() 911 | return ''.join(t).strip() 912 | 913 | def get_wm_density(self): 914 | """ 915 | 屏幕密度 916 | :return:Physical density: 480 917 | """ 918 | t = self.shell("wm density").stdout.readlines() 919 | return ''.join(t).strip() 920 | 921 | def get_window_displays(self): 922 | """ 923 | 924 | :return:显示屏参数 925 | """ 926 | t = self.shell("dumpsys window displays").stdout.readlines() 927 | return ''.join(t).strip() 928 | 929 | def get_mac_address(self): 930 | """ 931 | 932 | :return:mac地址 933 | """ 934 | t = self.shell("cat /sys/class/net/wlan0/address").stdout.readlines() 935 | return ''.join(t).strip() 936 | 937 | def get_cpu_info_all(self): 938 | """ 939 | 940 | :return:cpu全部信息 941 | """ 942 | t = self.shell("cat /proc/cpuinfo").stdout.readlines() 943 | return ''.join(t).strip() 944 | 945 | def get_cpu_mem_all(self): 946 | """ 947 | 948 | :return:内存全部信息 949 | """ 950 | t = self.shell("cat /proc/meminfo").stdout.readlines() 951 | return ''.join(t).strip() 952 | 953 | def get_sys_all(self): 954 | """ 955 | 956 | :return:设备全部信息 957 | """ 958 | t = self.shell("cat /system/build.prop").stdout.readlines() 959 | return ''.join(t).strip() 960 | 961 | def get_ps(self): 962 | """ 963 | 964 | :return:设备全部进程信息 965 | """ 966 | t = self.shell("ps").stdout.readlines() 967 | return ''.join(t).strip() 968 | 969 | def get_cpu_mem_info(self): 970 | """ 971 | 972 | :return:当前设备cpu与内存全部信息 973 | """ 974 | t = self.shell("top -n 1 -d 0.5").stdout.readlines() 975 | return ''.join(t).strip() 976 | 977 | def get_phone_ime(self): 978 | """ 979 | 980 | :return:获取设备已安装的输入法包名 981 | """ 982 | ime_list = [ime.strip() for ime in self.shell("ime list -s").stdout.readlines()] 983 | return ime_list 984 | 985 | def set_phone_ime(self, arg): 986 | """ 987 | 988 | :return: 更改手机输入法 989 | """ 990 | self.shell("ime set %s" % arg) 991 | 992 | 993 | if __name__ == "__main__": 994 | A = ADB() 995 | print A.get_focused_package_and_activity() 996 | -------------------------------------------------------------------------------- /po/BasePage.py: -------------------------------------------------------------------------------- 1 | # -*- coding: utf-8 -*- 2 | __author__ = 'joko' 3 | 4 | """ 5 | @author:joko 6 | @time: 16/11/8 下午2:52 7 | """ 8 | from selenium.webdriver.support.ui import WebDriverWait 9 | import lib.Utils as U 10 | 11 | 12 | class Base: 13 | driver = None 14 | 15 | def __init__(self, appium_driver): 16 | self.driver = appium_driver 17 | 18 | # 重新封装单个元素定位方法 19 | def find_element(self, loc, wait=15): 20 | try: 21 | WebDriverWait( 22 | self.driver, wait).until( 23 | lambda driver: driver.find_element( 24 | *loc).is_displayed()) 25 | return self.driver.find_element(*loc) 26 | except: 27 | U.Logging.error(u"%s 页面中未能找到 %s 元素" % (self, loc)) 28 | 29 | # 重新封装一组元素定位方法 30 | 31 | def find_elements(self, loc): 32 | try: 33 | if len(self.driver.find_elements(*loc)): 34 | return self.driver.find_elements(*loc) 35 | except: 36 | U.Logging.error(u"%s 页面中未能找到 %s 元素" % (self, loc)) 37 | 38 | # 重新封装输入方法 39 | 40 | def send_keys(self, loc, value, clear_first=True, click_first=True): 41 | try: 42 | if click_first: 43 | self.find_element(loc).click() 44 | if clear_first: 45 | self.find_element(loc).clear() 46 | self.find_element(loc).send_keys(value) 47 | except AttributeError: 48 | U.Logging.error(u"%s 页面中未能找到 %s 元素" % (self, loc)) 49 | 50 | # 重新封装按钮点击方法 51 | 52 | def clickButton(self, loc, find_first=True): 53 | try: 54 | if find_first: 55 | self.find_element(loc) 56 | self.find_element(loc).click() 57 | except AttributeError: 58 | U.Logging.error("%s 页面未能找到 %s 按钮" % (self, loc)) 59 | 60 | def swipe(self, st, sy, ex, ey): 61 | """ 62 | 滑动 63 | 分别为:起始点x,y。结束点x,y。与滑动速度。滑动默认800 64 | """ 65 | return self.driver.swipe(st, sy, ex, ey, duration=900) 66 | 67 | def get_window_size(self): 68 | """ 69 | 获取屏幕分辨率 70 | {u'width': 1080, u'height': 1920} 71 | :return: 1080,1920 72 | """ 73 | 74 | screen_size = self.driver.get_window_size() 75 | width = screen_size['width'] 76 | height = screen_size['height'] 77 | return width, height 78 | 79 | def swipe_ratio(self, st, sy, ex, ey): 80 | """ 81 | 82 | :param st: 起始点宽 83 | :param sy: 起始点高 84 | :param ex: 结束点宽 85 | :param ey: 结束点高 86 | :return: 滑动动作 87 | """ 88 | width, height = self.get_window_size() 89 | return self.swipe(str(st * width), str(sy * height), 90 | str(ex * width), str(ey * height)) 91 | 92 | def swipe_left(self): 93 | """ 94 | 左滑屏幕 95 | """ 96 | self.swipe_ratio(0.8, 0.5, 0.2, 0.5) 97 | U.sleep(1) 98 | 99 | def swipe_right(self): 100 | """ 101 | 右滑屏幕 102 | """ 103 | self.swipe_ratio(0.2, 0.5, 0.8, 0.5) 104 | U.sleep(1) 105 | 106 | def swipe_up(self): 107 | """ 108 | 上滑屏幕 109 | """ 110 | self.swipe_ratio(0.5, 0.8, 0.5, 0.2) 111 | U.sleep(1) 112 | 113 | def swipe_down(self): 114 | """ 115 | 下滑屏幕 116 | """ 117 | self.swipe_ratio(0.5, 0.2, 0.5, 0.8) 118 | U.sleep(1) 119 | 120 | def swipe_all(self, t): 121 | """ 122 | 选择如何滑动屏幕 123 | """ 124 | if t == 'swipe_left': 125 | self.swipe_left() 126 | elif t == 'swipe_right': 127 | self.swipe_right() 128 | elif t == 'swipe_up': 129 | self.swipe_up() 130 | elif t == 'swipe_down': 131 | self.swipe_down() 132 | 133 | def save_screenshot(self, file_path): 134 | """ 135 | 136 | :param file_path: 137 | :return: 获取android设备截图 138 | """ 139 | 140 | return self.driver.save_screenshot(file_path) 141 | 142 | def start_activity(self, package, activity): 143 | """ 144 | 启动activity 145 | package:包名 146 | activity:.activity 147 | """ 148 | return self.driver.start_activity(package, activity) 149 | 150 | def open_notifications(self): 151 | """ 152 | 打开系统通知栏 153 | """ 154 | return self.driver.open_notifications() 155 | 156 | def is_app_installed(self, package): 157 | """ 158 | 检查是否安装 159 | package:包名 160 | """ 161 | return self.driver.is_app_installed(package) 162 | 163 | def install_app(self, path): 164 | """ 165 | 安装应用 166 | path:安装路径 167 | """ 168 | return self.driver.install_app(path) 169 | 170 | def remove_app(self, package): 171 | """ 172 | 删除应用 173 | package:包名 174 | """ 175 | return self.driver.remove_app(package) 176 | 177 | def shake(self, ): 178 | """ 179 | 摇晃设备 180 | """ 181 | return self.driver.shake() 182 | 183 | def close_app(self, ): 184 | """ 185 | 关闭当前应用 186 | """ 187 | return self.driver.close_app() 188 | 189 | def reset_app(self, ): 190 | """ 191 | 重置当前应用 192 | """ 193 | return self.driver.reset() 194 | 195 | def current_activity(self, ): 196 | """ 197 | 当前应用的activity 198 | """ 199 | return self.driver.current_activity 200 | 201 | def send_key_event(self, arg): 202 | """ 203 | 参考文献:http://blog.csdn.net/jlminghui/article/details/39268419 204 | 操作实体按键 205 | :return: 206 | """ 207 | event_list = {'entity_home': 3, 'entity_back': 4, 'entity_menu': 82, 'entity_volume_up': 24, 208 | 'entity_volume_down': 25, "entity_enter": 66} 209 | if arg in event_list: 210 | self.driver.keyevent(int(event_list[arg])) 211 | 212 | def toggle_location_services(self): 213 | """ 214 | 开关定位服务 215 | :return: 216 | """ 217 | return self.driver.toggle_location_services() 218 | 219 | 220 | if __name__ == '__main__': 221 | pass 222 | -------------------------------------------------------------------------------- /po/ExecuteCase.py: -------------------------------------------------------------------------------- 1 | # -*- coding: utf-8 -*- 2 | __author__ = 'joko' 3 | 4 | """ 5 | @author:joko 6 | @time: 16/11/8 下午2:52 7 | """ 8 | import BasePage 9 | import yaml 10 | import lib.Utils as U 11 | import lib.adbUtils 12 | import public.Performance 13 | import public.GetLog 14 | import public.GenerateReports 15 | import public.GetCase 16 | import traceback 17 | import sys 18 | 19 | reload(sys) 20 | sys.setdefaultencoding("utf-8") 21 | 22 | 23 | def e(): 24 | """ 25 | 捕获用例执行函数异常安装 26 | :return: True|AssertionError|AttributeError 27 | """ 28 | 29 | def E(func): 30 | def wrapper(*args, **kwargs): 31 | error_msg = True 32 | try: 33 | return func(*args, **kwargs) 34 | except AssertionError as e: 35 | U.Logging.warn(traceback.format_exc()) 36 | U.Logging.error(e) 37 | error_msg = 'Assertion error' 38 | except AttributeError as e: 39 | U.Logging.warn(traceback.format_exc()) 40 | U.Logging.error(e) 41 | error_msg = 'Attribute Error' 42 | except Exception as e: 43 | error_msg = traceback.format_exc() 44 | U.Logging.error(e) 45 | finally: 46 | return error_msg 47 | 48 | return wrapper 49 | 50 | return E 51 | 52 | 53 | class BB(BasePage.Base): 54 | """ 55 | 继承测试信息 56 | """ 57 | pass 58 | 59 | 60 | class start_case(): 61 | def __init__(self, driver, yaml_name, yaml_path, all_result_path, device): 62 | self.path_yaml = yaml_path 63 | self.filename = str(yaml_name).split('.')[0] 64 | self.dash_page = BB(driver) 65 | self.device = device 66 | self.all_result_path = all_result_path 67 | 68 | @U.l() 69 | def __save_screen_file(self): 70 | """ 71 | 截图: 72 | 1:首先调用appium自带方法,如果失败会调用minicap, 73 | 2:minicap失败会调用adb 截图 74 | :return: 存储的图片地址 75 | """ 76 | screen_file = self.all_result_path + '/img/{}.png'.format(self.filename) 77 | 78 | try: 79 | self.dash_page.save_screenshot(screen_file) 80 | except Exception as e: 81 | U.Logging.debug( 82 | '__save_screen_file:err,The minicap has been replaced') 83 | import lib.ScreenShot 84 | s = lib.ScreenShot.minicap( 85 | self.device) 86 | s.main(screen_file) 87 | 88 | return screen_file 89 | 90 | @U.l() 91 | def __save_cpu_mem(self, cpu, mem, h_cpu, h_mem): 92 | """ 93 | 94 | :param cpu: cpu值列表 95 | :param mem: 内存值列表 96 | :return: 返回性能生成图片 97 | """ 98 | per_img_file = self.all_result_path + \ 99 | '/per/{}.png'.format(self.filename) 100 | public.Performance.data_marker(cpu, mem, h_cpu, h_mem, per_img_file) 101 | return per_img_file 102 | 103 | @U.l() 104 | def __save_error_status(self): 105 | """ 106 | 测试用例的的状态 107 | :return: 错误日志路径 108 | """ 109 | error_file = self.all_result_path + \ 110 | '/status/{}.yaml'.format(self.filename) 111 | return error_file 112 | 113 | @U.l() 114 | def __save_android_log(self): 115 | """ 116 | 117 | :return:清理当前设备缓存log,并且记录当前设备log 118 | """ 119 | android_log = public.GetLog.Al(self.device) 120 | log_file = self.all_result_path + '/log/{}.log'.format(self.filename) 121 | android_log.main(log_file) 122 | return log_file 123 | 124 | @U.l() 125 | def __save_android_result(self): 126 | """ 127 | 生成测试报告 128 | :return: 测试报告路径 129 | """ 130 | r = public.GenerateReports.Gr(self.all_result_path, self.device) 131 | r.main() 132 | return self.all_result_path 133 | 134 | def __select_per(self, case_name, device_name, ): 135 | sql = U.Asql() 136 | return sql.select_per(case_name, device_name, ) 137 | 138 | def __save_sql(self, case_name, device_name, cpu_list, mem_list, execution_status): 139 | sql = U.Asql() 140 | 141 | sql.insert_per(case_name, device_name, cpu_list, mem_list, execution_status) 142 | sql.close() 143 | 144 | def get_all_case(self, path_yaml): 145 | """ 146 | 147 | :param path_yaml: 用例地址 148 | :return: 返回yaml内字典,且遍历继承的信息,支持多重继承 149 | """ 150 | 151 | def get_case(path_yaml): 152 | case_list = [] 153 | 154 | inherit_case_file = public.GetCase.case_yaml_file() 155 | with open(path_yaml) as f: 156 | for dic in yaml.load(f): 157 | if isinstance(dic, dict): 158 | if 'test_inherit' in dic: 159 | inherit_case_name = dic['test_inherit'] 160 | inherit_case = inherit_case_name + '.yaml' 161 | if inherit_case in inherit_case_file.keys(): 162 | case_list += case_list + get_case(inherit_case_file[inherit_case]) 163 | 164 | else: 165 | case_list.append(dic) 166 | else: 167 | U.Logging.warn('get_case:not dic') 168 | return case_list 169 | 170 | return get_case(path_yaml) 171 | 172 | @e() 173 | def __analysis_yaml(self, path_yaml): 174 | """ 175 | 测试用例解释器 176 | :param path_yaml: 测试用例地址 177 | 1:每执行一条用例会记录下当前的性能 178 | 179 | 180 | :return: 181 | """ 182 | adb = lib.adbUtils.ADB(self.device) 183 | ini = U.ConfigIni() 184 | package_name = ini.get_ini('test_package_name', 'package_name') 185 | cpu_list = [] 186 | mem_list = [] 187 | for dic in self.get_all_case(path_yaml): 188 | U.Logging.success(str(dic)) 189 | if isinstance(dic, dict): 190 | if 'test_name' in dic: 191 | test_name = str(dic['test_name']).decode('utf-8') 192 | U.Logging.info( 193 | 'Start the test_case: {}'.format( 194 | test_name)) 195 | range_num = 1 196 | 197 | if 'test_range' in dic: 198 | # 循环控制 199 | # todo:打印循环相关的日志 200 | range_num = dic['test_range'] 201 | 202 | for i in xrange(0, range_num): 203 | if dic['test_action'] == 'click': 204 | # 点击 205 | test_control = dic['test_control'] 206 | test_control_type = dic['test_control_type'] 207 | 208 | U.Logging.success('click {}'.format(test_control)) 209 | 210 | self.dash_page.clickButton((test_control_type, test_control)) 211 | 212 | elif dic['test_action'] == 'send_keys': 213 | # 发送文本 214 | test_control_type = dic['test_control_type'] 215 | test_control = dic['test_control'] 216 | test_text = dic['test_text'] 217 | 218 | U.Logging.success('send {} to {}'.format(test_text, test_control)) 219 | self.dash_page.send_keys((test_control_type, test_control), str(test_text)) 220 | 221 | elif 'swipe' in dic['test_action']: 222 | # 滑动 223 | test_action = dic['test_action'] 224 | U.Logging.success('{}'.format(test_action)) 225 | self.dash_page.swipe_all(test_action) 226 | 227 | elif 'entity' in dic['test_action']: 228 | # 实体按键 229 | test_action = dic['test_action'] 230 | U.Logging.success('{}'.format(test_action)) 231 | self.dash_page.send_key_event(test_action) 232 | 233 | elif dic['test_action'] == 'assert': 234 | # 断言 235 | test_wait = 15 236 | test_control = dic['test_control'] 237 | test_control_type = dic['test_control_type'] 238 | test_text = dic['test_text'] 239 | if dic.has_key('test_wait'): 240 | test_wait = int(dic['test_wait']) 241 | 242 | U.Logging.success('assert {}'.format(test_control)) 243 | 244 | el = self.dash_page.find_element((test_control_type, test_control), wait=test_wait) 245 | assert el.text == test_text 246 | 247 | if 'test_sleep' in dic: 248 | # 等待 249 | sleep = dic['test_sleep'] 250 | U.Logging.success('Wait {} seconds'.format(sleep)) 251 | U.sleep(int(sleep)) 252 | 253 | if True: 254 | # todo 增加性能的开关判断 255 | # U.Logging.success('Obtaining application performance') 256 | 257 | cpu = adb.get_cpu(package_name) 258 | mem = adb.get_mem(package_name) 259 | # U.Logging.success('cpu:{}'.format(cpu)) 260 | # U.Logging.success('mem:{}'.format(mem)) 261 | cpu_list.append(cpu) 262 | mem_list.append(mem) 263 | 264 | else: 265 | U.Logging.error( 266 | 'Yaml file format error, the current {}, you need dict'.format( 267 | type(dic))) 268 | 269 | historical_per = self.__select_per(self.filename, self.device, ) 270 | self.__save_sql(self.filename, self.device, cpu_list, mem_list, 1) 271 | if historical_per is not None: 272 | h_cpu = historical_per[0] 273 | h_mem = historical_per[1] 274 | self.__save_cpu_mem(cpu_list, mem_list, h_cpu, h_mem) 275 | else: 276 | self.__save_cpu_mem(cpu_list, mem_list, None, None) 277 | 278 | U.Logging.success('cpu_list:{}'.format(cpu_list)) 279 | U.Logging.success('mem_list:{}'.format(mem_list)) 280 | return True 281 | 282 | def __load_analysis(self): 283 | """ 284 | 执行测试 285 | 执行步骤: 286 | 1:开启记录log 287 | 2:执行测试 288 | 3:记录执行结果 289 | 4:存储执行结果 290 | 5:截图 291 | :return: 截图路径 292 | """ 293 | U.Logging.success('read the yaml file') 294 | self.__save_android_log() 295 | error_msg = self.__analysis_yaml(self.path_yaml) 296 | with open(self.__save_error_status(), 'w') as f: 297 | yaml.dump({'error_msg': error_msg}, f) 298 | U.Logging.debug(str('results of the:%s' % error_msg)) 299 | f.close() 300 | 301 | return self.__save_screen_file() 302 | 303 | def main(self): 304 | """ 305 | 执行步骤: 306 | 1:开启测试 307 | 2:生成测试报告 308 | :return: 309 | """ 310 | U.sleep(5) 311 | self.__load_analysis() 312 | U.sleep(1) 313 | self.__save_android_result() 314 | -------------------------------------------------------------------------------- /po/__init__.py: -------------------------------------------------------------------------------- 1 | __author__ = 'joko' -------------------------------------------------------------------------------- /po/integration.py: -------------------------------------------------------------------------------- 1 | # -*- coding: utf-8 -*- 2 | __author__ = 'joko' 3 | 4 | """ 5 | @author:joko 6 | @time: 16/11/5 下午1:00 7 | """ 8 | 9 | import time 10 | import os 11 | import yaml 12 | from appium import webdriver 13 | import lib.Utils as U 14 | import ExecuteCase 15 | import public.StartAppium 16 | import public.GetHtml 17 | import public.GetLog 18 | import public.CleanProcess 19 | import public.Performance 20 | import public.installApp 21 | import public.GetCase 22 | import random 23 | 24 | 25 | class RunApp(object): 26 | def __init__(self, device_l): 27 | """ 28 | self.time:用于建立存放文件的目录 29 | """ 30 | self.time = time.strftime( 31 | "%Y-%m-%d_%H_%M_%S", 32 | time.localtime( 33 | time.time())) 34 | self.device_l = device_l 35 | self.device = self.device_l['deviceName'] 36 | U.Logging.info('start test device:%s' % self.device) 37 | 38 | self.all_result_path = self.mkdir_file() 39 | self.ia = public.installApp.Ia(self.all_result_path, self.device) 40 | 41 | def mkdir_file(self): 42 | """ 43 | 44 | :return:创建日志存放文件夹 45 | """ 46 | ini = U.ConfigIni() 47 | result_file = str(ini.get_ini('test_case', 'log_file')) 48 | result_file_every = result_file + '/' + \ 49 | time.strftime("%Y-%m-%d_%H_%M_%S{}".format(random.randint(10, 99)), 50 | time.localtime(time.time())) 51 | file_list = [ 52 | result_file, 53 | result_file_every, 54 | result_file_every + '/log', 55 | result_file_every + '/per', 56 | result_file_every + '/img', 57 | result_file_every + '/status'] 58 | if not os.path.exists(result_file): 59 | os.mkdir(result_file) 60 | 61 | for file_path in file_list: 62 | if not os.path.exists(file_path): 63 | os.mkdir(file_path) 64 | return result_file_every 65 | 66 | def __install_app(self): 67 | self.ia.main() 68 | 69 | @U.l() 70 | def __get_appium_port(self): 71 | """ 72 | 73 | :return: 开启appium端口 74 | """ 75 | sp = public.StartAppium.Sp(self.device) 76 | self.appium_port = sp.main() 77 | return self.appium_port 78 | 79 | @U.l() 80 | def clear_process(self): 81 | """ 82 | 83 | :return: 清理appium与logcat进程 84 | """ 85 | cp = public.CleanProcess.Cp() 86 | cp.clean_process(self.appium_port, self.device) 87 | return self.appium_port 88 | 89 | def start_appium(self): 90 | """ 91 | 启动driver 92 | :return: 93 | """ 94 | 95 | number_of_starts = 0 96 | while number_of_starts < 6: 97 | try: 98 | self.driver = webdriver.Remote( 99 | 'http://127.0.0.1:%s/wd/hub' % 100 | self.__get_appium_port(), self.device_l) 101 | U.Logging.debug('appium start %s success' % self.device) 102 | return self.driver 103 | except Exception as e: 104 | number_of_starts += 1 105 | U.Logging.error('Failed to start appium :{}'.format(e)) 106 | U.Logging.error( 107 | 'Try restarting the appium :{},Trying the {} frequency'.format(self.device, number_of_starts)) 108 | U.sleep(5) 109 | if number_of_starts > 5: 110 | U.Logging.error('Can not start appium, the program exits') 111 | exit() 112 | 113 | def analysis(self, yaml_name, yaml_path): 114 | """ 115 | 继承driver开始测试 116 | :param path_yaml: 测试用例地址 117 | :return: 118 | """ 119 | 120 | s = ExecuteCase.start_case( 121 | self.start_appium(), 122 | yaml_name, 123 | yaml_path, 124 | self.all_result_path, 125 | self.device) 126 | return s.main() 127 | 128 | def case_start(self): 129 | """ 130 | 控制diver开启 and 关闭,且清理进程 131 | 执行步骤: 132 | 1:安装应用 133 | 2:开启driver,并且执行测试 134 | 3:关闭driver 135 | 4:清理logcat appium 进程 136 | :return: 137 | """ 138 | 139 | test_case_yaml = public.GetCase.case_yaml_file().items() 140 | if not test_case_yaml: 141 | U.Logging.error('not yaml found ') 142 | else: 143 | for yaml_name, yaml_path in test_case_yaml: 144 | U.Logging.success('yaml path:{}'.format(yaml_path)) 145 | self.__install_app() 146 | 147 | self.analysis(yaml_name, yaml_path) 148 | try: 149 | self.driver.quit() 150 | except Exception as e: 151 | U.Logging.warn('driver quit Error %s'%e) 152 | self.clear_process() 153 | 154 | 155 | def get_device_info(): 156 | """ 157 | 获取当前电脑连接的devices 158 | :return: 返回设备列表 159 | """ 160 | device_list = [] 161 | ini = U.ConfigIni() 162 | test_info = ini.get_ini('test_info', 'info') 163 | test_device = ini.get_ini('test_device', 'device') 164 | with open(test_info) as f: 165 | test_dic = yaml.load(f)[0] 166 | 167 | with open(test_device) as f: 168 | for device in yaml.load(f): 169 | device_list.append(dict(test_dic.items() + device.items())) 170 | 171 | return device_list 172 | 173 | 174 | if __name__ == '__main__': 175 | import public.GetDevice 176 | 177 | public.GetDevice.set_device_yaml() 178 | for device in get_device_info(): 179 | a = RunApp(device) 180 | a.case_start() 181 | -------------------------------------------------------------------------------- /public/Analyzelog.py: -------------------------------------------------------------------------------- 1 | # -*- coding: utf-8 -*- 2 | __author__ = 'joko' 3 | 4 | """ 5 | @author:joko 6 | @time: 16/11/15 下午1:44 7 | """ 8 | import lib.Utils as U 9 | import GetFilePath 10 | import os 11 | 12 | 13 | class Anl: 14 | def __init__(self, all_result_path): 15 | self.all_result_path = all_result_path 16 | 17 | @U.l() 18 | def __log_file(self, all_path_result, the_suffix_name): 19 | """ 20 | 21 | :return: 日志列表 22 | """ 23 | return GetFilePath.all_file_path( 24 | all_path_result, the_suffix_name).values() 25 | 26 | def analyze(self, log_file): 27 | """ 28 | 过滤Exception到log文件夹内 29 | :param log_file: log的路径 30 | :return: 31 | """ 32 | errorId = 0 33 | go_on_id = 0 34 | log_filter_name = os.path.split(log_file)[1].split('.')[0] 35 | with open(self.all_result_path + '/log/{}filter.log'.format(log_filter_name), 'w') as s: 36 | 37 | with open(log_file) as f: 38 | for line in f: 39 | if 'Exception' in line: 40 | go_on_id = 1 41 | s.write('#' + '-' * 40 + '\n') 42 | s.write(line) 43 | errorId = line.split('(')[1].split(')')[0].strip() 44 | elif go_on_id == 1: 45 | if errorId in line: 46 | s.write(line) 47 | else: 48 | go_on_id = 0 49 | 50 | def main(self): 51 | """ 52 | 获取log,生成filter log 53 | :return: 54 | """ 55 | for log_file in self.__log_file(self.all_result_path, '.log'): 56 | self.analyze(log_file) 57 | 58 | 59 | if __name__ == '__main__': 60 | a = Anl('/Users/joko/Documents/Auto_Analysis/result/2016-11-27_20_23_1239') 61 | a.main() 62 | -------------------------------------------------------------------------------- /public/CheckEnvironment.py: -------------------------------------------------------------------------------- 1 | # -*- coding: utf-8 -*- 2 | __author__ = 'joko' 3 | 4 | """ 5 | @author:joko 6 | @time: 16/12/6 下午6:31 7 | """ 8 | import lib.Utils as U 9 | import public.GetDevice 10 | 11 | 12 | def check_environment(): 13 | appium = U.cmd("appium -v").stdout.readline().strip() 14 | if '1.' not in appium: 15 | U.Logging.error('appium not in computer') 16 | exit() 17 | else: 18 | U.Logging.info('appium version {}'.format(appium)) 19 | if not public.GetDevice.get_device(): 20 | U.Logging.error('the computer is not connected to any devices') 21 | exit() 22 | 23 | if __name__ == '__main__': 24 | check_environment() -------------------------------------------------------------------------------- /public/CleanProcess.py: -------------------------------------------------------------------------------- 1 | # -*- coding: utf-8 -*- 2 | __author__ = 'joko' 3 | 4 | """ 5 | @author:joko 6 | @time: 16/11/8 下午2:52 7 | """ 8 | import lib.Utils as U 9 | import platform 10 | 11 | 12 | class Cp(object): 13 | 14 | def __darwin(self, port, device): 15 | # for line in U.cmd( 16 | # "lsof -i tcp:%s | grep node|awk '{print $2}'" % 17 | # str(port)).stdout.readlines(): 18 | # U.cmd('kill -9 %s' % line.strip()) 19 | # U.Logging.debug('CleanProcess:Darwin:kill appium') 20 | for line in U.cmd( 21 | "ps -A | grep logcat | grep %s" % device).stdout.readlines(): 22 | U.cmd('kill -9 %s' % line.strip()) 23 | U.Logging.debug('CleanProcess:Darwin:kill logcat') 24 | 25 | def __linux(self, port, device): 26 | # linux必须最高权限才可获取到端口 27 | # for line in U.cmd( 28 | # "lsof -i:%s |awk '{print $2}'" % 29 | # str(port)).stdout.readlines(): 30 | # U.cmd('kill -9 %s' % line.strip()) 31 | # U.Logging.debug('CleanProcess:linux:kill appium') 32 | for line in U.cmd( 33 | "ps -ef | grep logcat | grep %s|awk '{print $2}'" % 34 | device).stdout.readlines(): 35 | U.cmd('kill -9 %s' % line.strip()) 36 | U.Logging.debug('CleanProcess:linux:kill logcat') 37 | 38 | def __darwin_all(self, ): 39 | for line in U.cmd( 40 | "ps -A | grep logcat|awk '{print $1}'").stdout.readlines(): 41 | U.cmd('kill -9 %s' % line.strip()) 42 | U.Logging.debug('CleanProcess:Darwin:kill logcat') 43 | for line in U.cmd( 44 | "ps -A | grep appium|awk '{print $1}'").stdout.readlines(): 45 | U.cmd('kill -9 %s' % line.strip()) 46 | U.Logging.debug('CleanProcess:Darwin:kill appium') 47 | 48 | def __linux_all(self): 49 | for line in U.cmd( 50 | "ps -ef | grep logcat|grep -v grep|awk '{print $2}'").stdout.readlines(): 51 | U.cmd('kill -9 %s' % line.strip()) 52 | U.Logging.debug('CleanProcess:linux:kill logcat') 53 | 54 | for line in U.cmd( 55 | "ps -ef |grep appium |grep -v grep|awk '{print $2}'").stdout.readlines(): 56 | U.cmd('kill -9 %s' % line.strip()) 57 | U.Logging.debug('CleanProcess:linux:kill appium') 58 | 59 | def __windows(self): 60 | # todo windows未完成 61 | for line in U.cmd( 62 | "netstat -aon|findstr 4700").stdout.readlines(): 63 | pid = line.strip().split(' ')[-1] 64 | process_name = U.cmd( 65 | 'tasklist|findstr {}'.format(pid)).stdout.read().split(' ')[0] 66 | U.cmd('taskkill /f /t /im {}'.format(process_name)) 67 | 68 | def clean_process(self, port, device): 69 | """ 70 | 清理logcat与appium指定进程 71 | :return: 72 | """ 73 | if platform.system() == 'Darwin': 74 | self.__darwin(port, device) 75 | elif platform.system() == 'Linux': 76 | self.__linux(port, device) 77 | else: 78 | U.Logging.debug( 79 | 'CleanProcess:Not identifying your operating system') 80 | 81 | def clean_process_all(self, ): 82 | """ 83 | 清理logcat与appium所有进程 84 | :return: 85 | """ 86 | if platform.system() == 'Darwin': 87 | self.__darwin_all() 88 | elif platform.system() == 'Linux': 89 | self.__linux_all() 90 | else: 91 | U.Logging.debug( 92 | 'CleanProcess:Not identifying your operating system') 93 | 94 | 95 | if __name__ == '__main__': 96 | c = Cp() 97 | # c.clean_process(4723, 'T7G0215A14000220') 98 | c.clean_process_all() 99 | -------------------------------------------------------------------------------- /public/GenerateReports.py: -------------------------------------------------------------------------------- 1 | # -*- coding: utf-8 -*- 2 | __author__ = 'joko' 3 | 4 | """ 5 | @author:joko 6 | @time: 16/11/9 下午6:08 7 | """ 8 | import os 9 | import yaml 10 | import lib.adbUtils 11 | import Analyzelog 12 | import lib.Utils as U 13 | import GetFilePath 14 | 15 | 16 | class Gr: 17 | 18 | def __init__(self, all_result_path, device): 19 | """ 20 | 21 | :param all_result_path: 本次测试创建的文件夹 22 | :param device: 设备id 23 | """ 24 | self.all_result_path = all_result_path 25 | self.device = device 26 | self.adb = lib.adbUtils.ADB(self.device) 27 | 28 | def __yaml_file(self, all_path_result, the_suffix_name): 29 | """ 30 | 31 | :return: 错误报告列表 32 | """ 33 | return GetFilePath.all_file_path(all_path_result, the_suffix_name) 34 | 35 | def __confirm_file(self, file_path): 36 | """ 37 | 检查文件是否存在 38 | :param file_path:文件地址 39 | :return: 40 | """ 41 | if os.path.exists(file_path): 42 | return file_path 43 | else: 44 | return None 45 | 46 | def __open_yaml(self, file_path): 47 | """ 48 | 获取status yaml文件内的值 49 | :param file_path: status yaml文件路径 50 | :return: 51 | """ 52 | if file_path is None: 53 | return None 54 | with open(file_path) as f: 55 | y = yaml.load(f) 56 | return y['error_msg'] 57 | 58 | @U.l() 59 | def __device_info(self): 60 | """ 61 | 用于生成测试报告的device的信息 62 | :return: 设备名,磁盘状态,wifi名称 63 | """ 64 | 65 | return 'device_name:' + str(self.adb.get_device_name()), 'disk:' + str(self.adb.get_disk()), \ 66 | 'wifi_name:' + str(self.adb.wifi_name()), 'system_version:' + str(self.adb.get_android_version()), \ 67 | 'resolution:' + str(self.adb.get_screen_resolution()) 68 | 69 | def __app_info(self): 70 | """ 71 | 获取应用包名和版本号 72 | :return: 73 | """ 74 | ini = U.ConfigIni() 75 | package_name = ini.get_ini('test_package_name', 'package_name') 76 | package_name_version = self.adb.specifies_app_version_name( 77 | package_name) 78 | return package_name, package_name_version 79 | 80 | def __analyze_log(self): 81 | """ 82 | 过滤log,只留Exception相关日志 83 | :return: 84 | """ 85 | a = Analyzelog.Anl(self.all_result_path) 86 | a.main() 87 | 88 | def main(self): 89 | """ 90 | 生成测试报告主函数 91 | 根据status yaml的文件来生成测试报告 92 | :return: 93 | """ 94 | import GetHtml 95 | self.__analyze_log() 96 | result = self.__yaml_file(self.all_result_path, '.yaml') 97 | lst = [] 98 | for case_name, confirm_status in result.items(): 99 | case_name = str(case_name).split('.')[0] 100 | case_result = self.__open_yaml(confirm_status) 101 | case_img = self.__confirm_file( 102 | str(confirm_status).replace( 103 | 'status', 'img').replace( 104 | 'yaml', 'png')) 105 | case_per = self.__confirm_file( 106 | str(confirm_status).replace( 107 | 'status', 'per').replace( 108 | 'yaml', 'png')) 109 | case_log = self.__confirm_file( 110 | str(confirm_status).replace( 111 | 'status', 'log').replace( 112 | 'yaml', 'log')) 113 | case_filter = self.__confirm_file( 114 | str(confirm_status).replace( 115 | 'status', 'log').replace( 116 | 'yaml', 'log').replace(case_name, case_name + 'filter')) 117 | if case_per is None: 118 | # 获取error图片 119 | ini = U.ConfigIni() 120 | case_per = ini.get_ini('test_case', 'error_img') 121 | lst.append( 122 | GetHtml.get_html_tr( 123 | case_name, 124 | case_result, 125 | case_img, 126 | case_per, 127 | case_log, case_filter)) 128 | GetHtml.get_html( 129 | ''.join(lst), 130 | self.__app_info(), 131 | self.__device_info(), 132 | self.__test_case_execution_status(), 133 | self.all_result_path) 134 | 135 | @U.l() 136 | def __test_case_execution_status(self): 137 | """ 138 | 获取用例执行状态 139 | :return: 用例数,通过数,失败数 140 | """ 141 | number_of_test_cases = self.__yaml_file( 142 | self.all_result_path, '.yaml').values() 143 | passed_the_test = 0 144 | failed = 0 145 | for i in number_of_test_cases: 146 | if isinstance(self.__open_yaml(i), bool): 147 | passed_the_test += 1 148 | else: 149 | failed += 1 150 | return len(number_of_test_cases), passed_the_test, failed 151 | 152 | 153 | if __name__ == '__main__': 154 | a = Gr( 155 | '/Users/joko/Documents/Auto_Analysis/result/2016-11-23_09_53_0263', 156 | 'T7G0215A14000220') 157 | a.main() 158 | -------------------------------------------------------------------------------- /public/GetCase.py: -------------------------------------------------------------------------------- 1 | # -*- coding: utf-8 -*- 2 | __author__ = 'joko' 3 | 4 | """ 5 | @author:joko 6 | @time: 16/11/16 上午10:48 7 | """ 8 | import lib.Utils as U 9 | import GetFilePath 10 | 11 | 12 | @U.l() 13 | def case_yaml_file(): 14 | """ 15 | 16 | :return: 返回当前设备下的yaml test case列表 17 | """ 18 | ini = U.ConfigIni() 19 | yaml_path = ini.get_ini('test_case', 'case') 20 | return GetFilePath.all_file_path(yaml_path, '.yaml') 21 | 22 | 23 | if __name__ == '__main__': 24 | print case_yaml_file() 25 | -------------------------------------------------------------------------------- /public/GetDevice.py: -------------------------------------------------------------------------------- 1 | # -*- coding: utf-8 -*- 2 | __author__ = 'joko' 3 | 4 | """ 5 | @author:joko 6 | @time: 16/11/8 下午2:52 7 | """ 8 | import lib.Utils as U 9 | import lib.adbUtils 10 | import yaml 11 | 12 | 13 | def get_device(): 14 | """ 15 | 16 | :return: 返回Android设备列表 17 | """ 18 | 19 | android_devices_list = [] 20 | for device in U.cmd('adb devices').stdout.readlines(): 21 | if 'device' in device and 'devices' not in device: 22 | device = device.split('\t')[0] 23 | android_devices_list.append(device) 24 | 25 | return android_devices_list 26 | 27 | 28 | def set_device_yaml(): 29 | """ 30 | 获取当前设备的Android version并且保存到yaml里 31 | :return: 32 | """ 33 | device_lst = [] 34 | for device in get_device(): 35 | adb = lib.adbUtils.ADB(device) 36 | U.Logging.success( 37 | 'get device:{},Android version:{}'.format( 38 | device, adb.get_android_version())) 39 | device_lst.append({'platformVersion': adb.get_android_version( 40 | ), 'deviceName': device, 'platformName': 'Android'}) 41 | 42 | ini = U.ConfigIni() 43 | with open(ini.get_ini('test_device', 'device'), 'w') as f: 44 | yaml.dump(device_lst, f) 45 | f.close() 46 | 47 | 48 | if __name__ == '__main__': 49 | set_device_yaml() 50 | -------------------------------------------------------------------------------- /public/GetFilePath.py: -------------------------------------------------------------------------------- 1 | # -*- coding: utf-8 -*- 2 | __author__ = 'joko' 3 | 4 | """ 5 | @author:joko 6 | @time: 16/11/21 上午11:54 7 | """ 8 | import os 9 | import collections 10 | 11 | 12 | def all_file_path(root_directory, extension_name): 13 | """ 14 | 15 | :return: 遍历文件目录 16 | """ 17 | file_dic = collections.OrderedDict() 18 | for parent, dirnames, filenames in os.walk(root_directory): 19 | for filename in filenames: 20 | if 'filter' not in filename: 21 | if filename.endswith(extension_name): 22 | path = os.path.join(parent, filename).replace('\\', '/') 23 | file_dic[filename] = path 24 | return file_dic 25 | 26 | 27 | if __name__ == '__main__': 28 | for k, v in all_file_path('../testcase', '.yaml').items(): 29 | print k, v 30 | -------------------------------------------------------------------------------- /public/GetHtml.py: -------------------------------------------------------------------------------- 1 | # -*- coding: utf-8 -*- 2 | __author__ = 'joko' 3 | 4 | """ 5 | @author:joko 6 | @time: 16/11/8 下午2:52 7 | """ 8 | import lib.Utils as U 9 | 10 | 11 | def get_html_tr(case_id, passing_state, img_path, per, device_log, filter_log): 12 | """ 13 | 14 | :param case_id:编号 15 | :param passing_state: 文本 16 | :param img_path:图片地址 17 | :return: html tr部分 18 | """ 19 | tr = """ 20 | 21 | %(case_id)s 22 | %(pass)s 23 | %(img)s 24 | %(per)s 25 | %(device_log)s 26 | %(filter_log)s 27 | 28 | """ 29 | 30 | case_id = '{}'.format(case_id) 31 | passing_state = '{}'.format(passing_state) 32 | img = ''.format(img_path) 33 | per = ''.format(per) 34 | device_log = 'device_log'.format(device_log) 35 | filter_log = 'device_filter_log'.format(filter_log) 36 | 37 | result = {'case_id': case_id, 'pass': passing_state, 'img': img, 'per': per, 'device_log': device_log, 38 | 'filter_log': filter_log} 39 | return tr % result 40 | 41 | 42 | @U.l() 43 | def get_html(log, device, app_info, test_status, result_path): 44 | """ 45 | 46 | :param log: 测试报告报表 47 | :param device: device信息 48 | :param app_info: 测试应用信息 49 | :param test_status: 测试用例信息 50 | :param result_path: 输出文件夹 51 | :return: 52 | """ 53 | all_case, passed, failed = test_status 54 | template = ''' 55 | 56 | 57 | 58 | 59 | 60 | Test Report 61 | 62 | 63 |

Test Report

64 |

End Time:{Time}

65 |

{device}

66 |

{app_info}

67 |

All_Case:{All_Case},passed:{passed},failed:{failed}

68 | 69 |
70 | 72 | 73 | 74 | 75 | 76 | 77 | 78 | 79 | 80 | 81 | %(tr)s 82 | 83 | 84 |
case_idcase_resultcase_imgcase_percase_logcase_filter_log
85 | 86 | 87 | '''.format(Time=U.get_now_time(), device=device, app_info=app_info, All_Case=all_case, passed=passed, failed=failed) 88 | data = {'tr': log} 89 | save_html_file = '%s/test.html' % result_path 90 | with open(save_html_file, 'w') as f: 91 | f.write(template % data) 92 | f.close() 93 | return save_html_file 94 | 95 | 96 | if __name__ == '__main__': 97 | a = get_html_tr(1, '问问问', '/Users/joko/Documents/Auto_Analysis/status/2016-11-08_10_50_34/zefsd.jpg', 'kahsdkhaskd') 98 | get_html(''.join(a), '123123') 99 | -------------------------------------------------------------------------------- /public/GetLog.py: -------------------------------------------------------------------------------- 1 | # -*- coding: utf-8 -*- 2 | __author__ = 'joko' 3 | 4 | """ 5 | @author:joko 6 | @time: 16/11/8 下午2:52 7 | """ 8 | import lib.adbUtils 9 | 10 | 11 | class Al: 12 | def __init__(self, device): 13 | self.device = device 14 | 15 | def _get_android_log(self, log_path): 16 | """ 17 | 18 | :return:清理当前设备缓存log,并且记录当前设备log 19 | """ 20 | 21 | adb = lib.adbUtils.ADB(self.device) 22 | adb.c_logcat() 23 | adb.logcat(log_path) 24 | 25 | def main(self, log_path): 26 | """ 27 | 28 | :return: 开启记录log 29 | """ 30 | return self._get_android_log(log_path) 31 | 32 | 33 | if __name__ == '__main__': 34 | a = Al('0530dc6a') 35 | -------------------------------------------------------------------------------- /public/Performance.py: -------------------------------------------------------------------------------- 1 | # -*- coding: utf-8 -*- 2 | __author__ = 'joko' 3 | 4 | """ 5 | @author:joko 6 | @time: 16/11/8 下午2:52 7 | """ 8 | import lib.Utils as U 9 | 10 | 11 | def data_marker(cpu, mem, h_cpu, h_mem, path): 12 | """ 13 | 14 | :param cpu: cpu列表 15 | :param mem: 内存列表 16 | :param path: 存储的文件路径 17 | :return: 18 | """ 19 | import matplotlib 20 | 21 | matplotlib.use('Agg') 22 | import pylab as pl 23 | pl.plot(cpu, 'r') 24 | pl.plot(mem, 'g') 25 | 26 | pl.title('performance') 27 | pl.xlabel('second') 28 | pl.ylabel('percent') 29 | 30 | pl.plot(cpu, color="red", linewidth=2.5, linestyle="-", label="this_cpu") 31 | pl.plot(mem, color="blue", linewidth=2.5, linestyle="-", label="this_mem") 32 | if h_mem is not None: 33 | pl.plot( 34 | h_cpu, 35 | color="magenta", 36 | linewidth=2.5, 37 | linestyle="-", 38 | label="historical_cpu") 39 | pl.plot( 40 | h_mem, 41 | color="green", 42 | linewidth=2.5, 43 | linestyle="-", 44 | label="historical_mem") 45 | pl.legend(loc='upper left') 46 | pl.xlim(0.0, len(mem)) 47 | pl.ylim(0.0, 100.0) 48 | pl.savefig(path) 49 | U.Logging.debug('Report:%s' % path) 50 | # pl.show() #调出GUI实时查看 51 | pl.close() # 必须关闭,不然值会在内存中不销毁 52 | 53 | # import matplotlib as mpl 54 | # print mpl.get_cachedir() 55 | 56 | 57 | if __name__ == '__main__': 58 | import random 59 | 60 | def get_num(): 61 | lst = [] 62 | for i in range(10): 63 | lst.append(random.randint(1, 60)) 64 | 65 | return lst 66 | 67 | for i in range(1): 68 | data_marker(get_num(), get_num(), get_num(), get_num(), '%s.png' % i) 69 | cpu_list = [] 70 | mem_list = [] 71 | -------------------------------------------------------------------------------- /public/StartAppium.py: -------------------------------------------------------------------------------- 1 | # -*- coding: utf-8 -*- 2 | __author__ = 'joko' 3 | 4 | """ 5 | @author:joko 6 | @time: 16/11/8 下午2:52 7 | """ 8 | import lib.Utils as U 9 | import random 10 | import platform 11 | 12 | 13 | class Sp: 14 | def __init__(self, device): 15 | self.device = device 16 | 17 | def __start_driver(self, aport, bpport): 18 | """ 19 | 清理logcat与appium所有进程 20 | :return: 21 | """ 22 | if platform.system() == 'Windows': 23 | # 在win10启动appium有bug,暂时处理方案 24 | import subprocess 25 | subprocess.Popen("appium -p %s -bp %s -U %s" % 26 | (aport, bpport, self.device), shell=True) 27 | 28 | else: 29 | appium = U.cmd("appium -p %s -bp %s -U %s" % 30 | (aport, bpport, self.device)) # 启动appium 31 | while True: 32 | appium_line = appium.stdout.readline().strip() 33 | U.Logging.debug(appium_line) 34 | U.sleep(1) 35 | if 'listener started' in appium_line or 'Error: listen' in appium_line: 36 | break 37 | 38 | def start_appium(self): 39 | """ 40 | 启动appium 41 | p:appium port 42 | bp:bootstrap port 43 | :return: 返回appium端口参数 44 | """ 45 | 46 | aport = random.randint(4700, 4900) 47 | bpport = random.randint(4700, 4900) 48 | self.__start_driver(aport, bpport) 49 | 50 | U.Logging.debug( 51 | 'start appium :p %s bp %s device:%s' % 52 | (aport, bpport, self.device)) 53 | U.sleep(10) 54 | return aport 55 | 56 | def main(self): 57 | """ 58 | 59 | :return: 启动appium 60 | """ 61 | return self.start_appium() 62 | 63 | 64 | if __name__ == '__main__': 65 | s = Sp('0530dc6a') 66 | s.main() 67 | -------------------------------------------------------------------------------- /public/__init__.py: -------------------------------------------------------------------------------- 1 | __author__ = 'joko' 2 | -------------------------------------------------------------------------------- /public/installApp.py: -------------------------------------------------------------------------------- 1 | # -*- coding: utf-8 -*- 2 | __author__ = 'joko' 3 | 4 | """ 5 | @author:joko 6 | @time: 16/11/11 上午10:52 7 | """ 8 | import lib.adbUtils 9 | import xml.etree.cElementTree as ET 10 | import re 11 | import lib.Utils as U 12 | import threading 13 | from multiprocessing import Queue 14 | import os 15 | 16 | 17 | class Ia: 18 | def __init__(self, all_result_path, device): 19 | """ 20 | Queue模块是用于进程间通信的模块 21 | 22 | :param all_result_path: 本次测试创建的文件夹 23 | :param device: 设备id 24 | """ 25 | self.all_result_path = all_result_path 26 | self.device = device 27 | self.adb = lib.adbUtils.ADB(self.device) 28 | self.queue = Queue(10) 29 | 30 | @U.l() 31 | def __uidump(self): 32 | """ 33 | 获取当前Activity控件树 34 | :return:xml在电脑内的地址存储地址 35 | """ 36 | save_path = self.all_result_path + "/dump.xml" 37 | self.adb.get_focused_package_xml(save_path) 38 | return save_path 39 | 40 | @U.l() 41 | def __element(self): 42 | """ 43 | 同属性单个元素,返回单个坐标元组 44 | button_list:常见的确认,同意,按钮控件id 45 | """ 46 | button0 = 'com.android.packageinstaller:id/ok_button' 47 | button1 = 'com.android.packageinstaller:id/btn_allow_once' 48 | button2 = 'com.android.packageinstaller:id/bottom_button_two' 49 | button3 = 'com.android.packageinstaller:id/btn_continue_install' 50 | button4 = 'android:id/button1' 51 | button5 = 'vivo:id/vivo_adb_install_ok_button' 52 | button_list = [button0, button1, button2, button3, button4, button5] 53 | self.__uidump() 54 | self.pattern = re.compile(r"\d+") 55 | if not os.path.exists(self.all_result_path + "/dump.xml"): 56 | U.Logging.warn('Failed to get xml') 57 | return None 58 | 59 | tree = ET.ElementTree(file=self.all_result_path + "/dump.xml") 60 | tree_iter = tree.iter(tag="node") 61 | for elem in tree_iter: 62 | if elem.attrib["resource-id"] in button_list: 63 | bounds = elem.attrib["bounds"] 64 | coord = self.pattern.findall(bounds) 65 | x_point = (int(coord[2]) - int(coord[0])) / 2.0 + int(coord[0]) 66 | y_point = (int(coord[3]) - int(coord[1])) / 2.0 + int(coord[1]) 67 | return x_point, y_point 68 | else: 69 | return None 70 | 71 | def tap(self): 72 | """ 73 | 点击动作 74 | :return: 75 | """ 76 | coordinate_points = self.__element() 77 | if coordinate_points is not None: 78 | self.adb.touch_by_element(coordinate_points) 79 | 80 | def tap_all(self): 81 | """ 82 | 不间断获取xml,并且点击。配合多线程使用 83 | :return: 84 | """ 85 | while True: 86 | self.tap() 87 | if not self.queue.empty(): 88 | break 89 | 90 | @U.l() 91 | def __install_app(self, package_name, app_file_path): 92 | """ 93 | 94 | :param package_name: 应用的报名:com:x.x 95 | :param app_file_path: 应用的安装路径,注意需要绝对路径 96 | :return: 97 | """ 98 | self.adb.quit_app( 99 | 'com.android.packageinstaller') # kill安装程序,用于处理oppo的一个bug 100 | if self.queue.empty(): 101 | if self.adb.is_install(package_name): 102 | U.Logging.success( 103 | 'del {}-{}'.format(self.device, package_name)) 104 | self.adb.remove_app(package_name) 105 | install_num = 0 106 | while install_num < 4: 107 | install_info = self.adb.install_app(app_file_path).stdout.readlines() 108 | U.Logging.success('install_info:%s'%install_info) 109 | if self.adb.is_install(package_name): 110 | self.queue.put(1) 111 | break 112 | else: 113 | U.Logging.error('Reinstalling %s %s '%(package_name,self.device)) 114 | install_num += 1 115 | else: 116 | raise AssertionError('Reinstalling app error') 117 | 118 | # kill安装程序,用于处理oppo的一个bug 119 | self.adb.quit_app('com.android.packageinstaller') 120 | 121 | def main(self): 122 | """ 123 | 开启多线程: 124 | 线程1:安装应用 125 | 线程2:获取当前页面是否有可点击的按钮 126 | :return: 127 | """ 128 | ini = U.ConfigIni() 129 | install_file = ini.get_ini('test_install_path', 'path') 130 | package_name = ini.get_ini('test_package_name', 'package_name') 131 | 132 | threads = [] 133 | 134 | click_button = threading.Thread(target=self.tap_all, args=()) 135 | threads.append(click_button) 136 | install_app = threading.Thread( 137 | target=self.__install_app, args=( 138 | package_name, install_file)) 139 | threads.append(install_app) 140 | process_list = range(len(threads)) 141 | 142 | for i in process_list: 143 | threads[i].start() 144 | for i in process_list: 145 | threads[i].join() 146 | 147 | self.adb.shell('"rm -r /data/local/tmp/*.xml"') 148 | 149 | 150 | if __name__ == '__main__': 151 | a = Ia('/Users/joko/Desktop/temp', 'VGAMCQEI99999999') 152 | a.main() 153 | -------------------------------------------------------------------------------- /run.py: -------------------------------------------------------------------------------- 1 | # -*- coding: utf-8 -*- 2 | __author__ = 'joko' 3 | 4 | """ 5 | @author:joko 6 | @time: 16/11/10 下午3:55 7 | """ 8 | import os 9 | import sys 10 | 11 | main_view = os.path.split(os.path.realpath(sys.argv[0]))[0] 12 | sys.path.append(main_view) 13 | import public.CleanProcess 14 | import public.GetDevice 15 | import po.integration 16 | import public.CheckEnvironment 17 | public.CheckEnvironment.check_environment() 18 | 19 | import threading 20 | 21 | 22 | class r(threading.Thread): 23 | def __init__(self, device, ): 24 | threading.Thread.__init__(self) 25 | self.device = device 26 | 27 | def run(self): 28 | a = po.integration.RunApp(self.device) 29 | a.case_start() 30 | 31 | 32 | def run_device(): 33 | public.GetDevice.set_device_yaml() 34 | device_list = po.integration.get_device_info() 35 | for device in device_list: 36 | test_run = r(device) 37 | test_run.start() 38 | test_run.join() 39 | 40 | 41 | if __name__ == '__main__': 42 | run_device() 43 | cl = public.CleanProcess.Cp() 44 | cl.clean_process_all() 45 | -------------------------------------------------------------------------------- /setup.py: -------------------------------------------------------------------------------- 1 | # -*- coding: utf-8 -*- 2 | __author__ = 'joko' 3 | 4 | """ 5 | @author:joko 6 | @time: 16/11/16 下午3:25 7 | """ 8 | try: 9 | from setuptools import setup, find_packages 10 | except ImportError: 11 | from distutils.core import setup, find_packages 12 | 13 | setup( 14 | name='Auto_Analysis', 15 | keywords='', 16 | version=1.0, 17 | packages=find_packages(), 18 | url='', 19 | license='MIT', 20 | author='joko', 21 | author_email='imjoko@gmail.com', 22 | description='', 23 | install_requires=[ 24 | 'pyyaml', 'matplotlib', 'Appium-Python-Client', 'selenium', 'termcolor' 25 | ] 26 | ) 27 | -------------------------------------------------------------------------------- /testcase/login/login.yaml: -------------------------------------------------------------------------------- 1 | --- 2 | - 3 | test_name: 点击跳过 4 | test_id: 0001 5 | test_control_type: id 6 | test_action: click 7 | test_control: test.joko.com.myapplication:id/button1 8 | - 9 | test_name: 输入帐号名 10 | test_id: 0002 11 | test_control_type: id 12 | test_action: send_keys 13 | test_control: test.joko.com.myapplication:id/editText 14 | test_text: 199999999 15 | - 16 | test_name: 输入密码 17 | test_id: 0003 18 | test_control_type: id 19 | test_action: send_keys 20 | test_control: test.joko.com.myapplication:id/editText2 21 | test_text: 9999 22 | 23 | - 24 | test_name: 点击登录 25 | test_id: 0004 26 | test_control_type: xpath 27 | test_action: click 28 | test_control: //android.widget.Button[contains(@text,'确定')] 29 | 30 | - 31 | test_name: 向上滑动页面 32 | test_id: 0005 33 | test_action: swipe_up 34 | test_range: 2 35 | 36 | - 37 | test_name: 向下滑动页面 38 | test_id: 0005 39 | test_action: swipe_down 40 | test_range: 2 41 | 42 | -------------------------------------------------------------------------------- /testcase/login/login1.yaml: -------------------------------------------------------------------------------- 1 | --- 2 | - 3 | test_inherit: login 4 | - 5 | test_name: 返回登陆页 6 | test_id: 0001 7 | test_action: entity_back 8 | test_range: 2 9 | 10 | - 11 | test_name: 验证启动页 12 | test_id: 0002 13 | test_control_type: id 14 | test_action: assert 15 | test_text: Button 16 | test_control: test.joko.com.myapplication:id/button1 17 | test_wait: 30 18 | --------------------------------------------------------------------------------