├── .classpath ├── .gitignore ├── .project ├── .settings └── org.eclipse.jdt.core.prefs ├── README.md ├── docs ├── how_to_develop.md ├── how_to_use_in_mac.md ├── how_to_use_in_windows.md └── troubleshoot.md ├── libs ├── chimpchat-22.2.0.jar ├── commons-compress-1.8.1.jar ├── ddmlib-prebuilt.jar ├── gson-2.2.4.jar ├── guava-17.0.jar ├── hosttestlib.jar ├── imagedraw.jar ├── javalib-deviceinfo.jar ├── javalib-deviceutil.jar ├── jline-0.9.9.jar ├── kxml2-2.3.0.jar └── tradefederation.jar ├── res ├── config │ └── cts.xml └── report │ ├── bootstrap.css │ ├── index.xsl │ ├── result.xsl │ └── trace.xsl └── src └── com └── android └── cts └── tradefed ├── build ├── CtsBuildHelper.java └── CtsBuildProvider.java ├── command └── CtsConsole.java ├── device ├── DeviceInfoCollector.java ├── DeviceNetWorkListener.java ├── DeviceUnlock.java └── MonkeyActivityListener.java ├── result ├── AbstractXmlPullParser.java ├── CrashAnalyzer.java ├── CtsTestStatus.java ├── CtsXmlResultReporter.java ├── DeviceInfoResult.java ├── ITestResultRepo.java ├── ITestSummary.java ├── IssueReporter.java ├── MonkeyReporter.java ├── MultipartForm.java ├── PlanCreator.java ├── PtsHostStore.java ├── PtsReportUtil.java ├── ResultReporter.java ├── Test.java ├── TestCase.java ├── TestPackageResult.java ├── TestResultRepo.java ├── TestResults.java ├── TestSuite.java ├── TestSummaryXml.java ├── TimeUtil.java └── monkey │ ├── DragTag.java │ ├── EventTag.java │ ├── KeyTag.java │ ├── MonkeyTestTag.java │ ├── MotionTag.java │ ├── TapTag.java │ └── TouchTag.java ├── targetprep ├── CtsRootDeviceSetup.java └── SettingsToggler.java ├── test └── Test.java └── testtype ├── AccessibilityServiceTestRunner.java ├── AccessibilityTestRunner.java ├── CtsTest.java ├── DisplayTestRunner.java ├── GeeTest.java ├── GeeTestResultParser.java ├── ITestPackageDef.java ├── ITestPackageRepo.java ├── ITestPlan.java ├── InstrumentationApkTest.java ├── JarHostTest.java ├── MonkeyTest.java ├── ResultFilter.java ├── TestFilter.java ├── TestPackageDef.java ├── TestPackageRepo.java ├── TestPackageXmlParser.java ├── TestPlan.java ├── TestTimeoutException.java ├── UiAutomatorJarTest.java ├── VMHostTest.java ├── WrappedGTest.java ├── WrappedGTestResultParser.java └── monkey ├── Monkey.java ├── MonkeyDragEvent.java ├── MonkeyEvent.java ├── MonkeyEventQueue.java ├── MonkeyEventSource.java ├── MonkeyKeyEvent.java ├── MonkeyMotionEvent.java ├── MonkeySourceRandom.java ├── MonkeyTapEvent.java └── Rectangle.java /.classpath: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | 9 | 10 | 11 | 12 | 13 | 14 | 15 | 16 | 17 | 18 | 19 | 20 | 21 | 22 | -------------------------------------------------------------------------------- /.gitignore: -------------------------------------------------------------------------------- 1 | bin 2 | -------------------------------------------------------------------------------- /.project: -------------------------------------------------------------------------------- 1 | 2 | 3 | cts-tradefed-host 4 | 5 | 6 | 7 | 8 | 9 | org.eclipse.jdt.core.javabuilder 10 | 11 | 12 | 13 | 14 | 15 | org.eclipse.jdt.core.javanature 16 | 17 | 18 | -------------------------------------------------------------------------------- /.settings/org.eclipse.jdt.core.prefs: -------------------------------------------------------------------------------- 1 | eclipse.preferences.version=1 2 | org.eclipse.jdt.core.compiler.codegen.inlineJsrBytecode=enabled 3 | org.eclipse.jdt.core.compiler.codegen.methodParameters=do not generate 4 | org.eclipse.jdt.core.compiler.codegen.targetPlatform=1.6 5 | org.eclipse.jdt.core.compiler.codegen.unusedLocal=preserve 6 | org.eclipse.jdt.core.compiler.compliance=1.6 7 | org.eclipse.jdt.core.compiler.debug.lineNumber=generate 8 | org.eclipse.jdt.core.compiler.debug.localVariable=generate 9 | org.eclipse.jdt.core.compiler.debug.sourceFile=generate 10 | org.eclipse.jdt.core.compiler.problem.assertIdentifier=error 11 | org.eclipse.jdt.core.compiler.problem.enumIdentifier=error 12 | org.eclipse.jdt.core.compiler.source=1.6 13 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # 简介 2 | 3 | CrashMonkey4Android,是一个依靠Cts框架,对原生Monkey进行改造后的产物,拥有以下新增功能: 4 | 5 | 1. 保存每一步的截图. 6 | 2. 保存logcat. 7 | 3. 保存每一个Monkey事件的信息. 8 | 4. 分析Crash. 9 | 5. Html报告. 10 | 6. 支持多设备. 11 | 12 | 13 | # 环境要求 14 | 15 | 16 | 1. 安装JDK1.6+并配置环境变量. 17 | 2. 安装SDK并配置环境变量. 18 | 19 | 20 | 21 | # 如何使用 22 | 23 | 24 | [安装-MAC版](./docs/how_to_use_in_mac.md). 25 | 26 | [如何进行二次开发](./docs/how_to_develop.md). 27 | 28 | 29 | 30 | # 参数配置 31 | 32 | 我们提供了很多可供配置的参数. 33 | 34 | ## 查看参数 35 | 36 | > 我们可以通过在命令行下输入`run cts --help-all` 获取所有的可设置参数: 37 | 38 | ``` 39 | test options: 40 | --p package of test app 41 | --a main activity of test app 42 | --v monkey event count Default: 20. 43 | --throttle The delay time between the events Default: 300. 44 | --pct-tap percentage of tap event Default: 25.0. 45 | --pct-motion percentage of motion event Default: 0.0. 46 | --pct-nav percentage of navigation event Default: 15.0. 47 | --pct-majornav percentage of major navigation event Default: 15.0. 48 | --pct-syskeys percentage of system key event Default: 15.0. 49 | --pct-drag percentage of drag evnet Default: 30.0. 50 | --logcat-size The max number of logcat data in bytes to capture when --logcat-on-failure is on. Should be an amount that can comfortably fit in memory. Default: 20480. 51 | --plan the test plan to run. 52 | --[no-]reboot Do not reboot device after running some amount of tests. Default behavior is to reboot. Default: false. 53 | --[no-]skip-device-info 54 | flag to control whether to collect info from device. Providing this flag will speed up test execution for short test runs but will result in required data being omitted from the test report. Default: false. 55 | --[no-]device-unlock unlock device Default: false. 56 | --app-path local app's path 57 | --wifiSsdk wifi username 58 | --wifiPsk wifi password 59 | --[no-]skip-uninstall-app 60 | no uninstall test app Default: true. 61 | --monkey-log-size monkey log size Default: 10485760. 62 | -b, --[no-]bugreport take a bugreport after each failed test. Warning: can potentially use a lot of disk space. Default: false. 63 | --[no-]tracefile get trace file ,in /data/anr/trace.txt Default: false. 64 | 65 | 'stdout' logger options: 66 | --log-level minimum log level to display. Default: INFO. Valid values: [VERBOSE, DEBUG, INFO, WARN, ERROR, ASSERT] 67 | 68 | 69 | ``` 70 | ## 主要属性: 71 | 72 | 73 | 1. p :测试app的包名. 74 | 2. a :测试app的主activity,如果正确设置上面两项,Monkey会针对上面-p指定的应用测试,一直保持在该应用界面. 75 | 注意:-a和-p两个参数要一起使用,否则不起作用. 76 | 3. throttle:2个Monkey事件之间的间隔,默认为300毫秒. 77 | 4. pct-tap:点击事件的百分比,默认为25%. 78 | 5. pct-motion:多点滑动事件百分比,默认为0%,(暂时还没实现). 79 | 6. pct-nav: 导航事件的百分比(导航事件由方向输入设备的上下左右按键所触发的事件组成),默认为15%. 80 | 7. pct-majornav:主要导航事件的百分比.(这些导航事件通常会导致UI界面中的动作事件,如5-way键盘的中间键,回退按键、菜单按键),默认为15%. 81 | 8. pct-syskeys:系统事件百分比.(这些按键通常由系统保留使用,如Home、Back、Start Call、End Call、音量调节),默认为15%. 82 | 9. pct-drag:拖拽事件的百分比,默认为30%. 83 | 84 | >目前只实现了5/6,但是上面的数字相加一定要为100%. 85 | 86 | 87 | 10. reboot : 重启机器,默认为false,不重启.如果想要重启的话,直接在命令行附上该参数,不用在后面加true,因为boolen类型的设置方式和其他不一样. 88 | 11. device-unlock:解锁手机,默认为false,如果收集重启的话,建议将该属性设置为true.解锁原理就是利用appium自带的apk来解锁的. 89 | 12. skip-device-info:是否跳过设备信息获取,默认为false.因为我们的报告中用到了设备信息,所以建议不要将该属性设置为true. 90 | 13. app-path:如果应用需要从本地安装,用该属性设置app路径,会自动安装app到收集端. 91 | 14. wifiSsdk:wifi的用户名 92 | 15. wifiPsk:wifi的密码 93 | 94 | > 因为该工具支持自动连接wifi,所以你的app需要在wifi情况下工作,请设置这两个属性,它会自动检测断网并重连. 95 | 96 | 16. skip-uninstall-app:是否跳过卸载app的阶段,因为如果使用本地app安装后,有时想卸载应用,可以设置该属性为false.默认是不卸载. 97 | 17. monkey-log-size:如果针对某一个应用测试,该工具为该app单独收集log,这里可以设置log可以最大到多少B. 98 | 18. bugreport:是否保存bugreport信息,默认为false.如果研发想要bugreport信息,将该属性设置为true. 99 | 19. tracefile:是否保存trace.txt文件,该文件位于/data/anr/trace.txt.一般发生crash的时候会用到该文件分析问题. 100 | 101 | # 总结 102 | 103 | 目前CrashMonkey4Android还处于pre-release阶段,需要改善的地方还有很多,请大家多多提出建议. 104 | 105 | 目前我们已经推出了iOS和Android两个平台的Monkey,归并到一个组织[58Automation](https://github.com/58Automation)中,欢迎大家fork,有问题请提Issue. 106 | 107 | 108 | 109 | 110 | 111 | 112 | 113 | 114 | 115 | 116 | -------------------------------------------------------------------------------- /docs/how_to_develop.md: -------------------------------------------------------------------------------- 1 | # 如何进行二次开发 2 | 3 | ## 下载源码 4 | 我们可以开发的源码分两部分,这个我在cts专辑中也讲过,cts分两部分,一个是基础库,一个是运行库,所以我们的源码也分为两部分 5 | ### 运行库 6 | 同步CrashMonkey4Android源码: 7 | ` https://github.com/DoctorQ/CrashMonkey4Android.git` 8 | 或者下载zip包,解压.然后导入到eclipse中. 9 | 10 | ### 基础库 11 | 同步CrashMonkey4Android_tradefederation源码: 12 | ` git clone https://github.com/DoctorQ/CrashMonkey4Android_tradefederation.git` 13 | 或者下载zip包,解压.然后导入到eclipse中. 14 | 15 | 导入后,eclipse含有如下两个项目: 16 | 17 | ![这里写图片描述](http://img.blog.csdn.net/20150605111733336) 18 | 19 | 20 | ## eclipse配置 21 | 22 | ### 为cts-tradefed-host关联tradefederation 23 | 24 | 在cts-tradefed-host右键点击Build Path->Configure Build Path,切换到Projects一栏,然后添加tradefederation,添加后的效果如下: 25 | ![这里写图片描述](http://img.blog.csdn.net/20150605112113506) 26 | 27 | 然后切换到Order and Export保持上面的项目在tradefederation.jar之上: 28 | ![这里写图片描述](http://img.blog.csdn.net/20150605112153701) 29 | 30 | 这样你在tradefedertion项目中的修改就会应用到cts-tradefed-host项目中。 31 | 32 | ### 配置运行参数 33 | 34 | 找到CtsConsole.java文件,位于`com.android.cts.tradefed.command`包下,右击Run As->Run Configurations,切换到Arguments一栏: 35 | 36 | ![这里写图片描述](http://img.blog.csdn.net/20150605113912716) 37 | 38 | 其中VM arguments中填入`-DCTS_ROOT=/Users/wuxian/Documents` 39 | 后面的路径一定要指向你下载的可执行文件的根目录,比如我本地的可执行文件存放路径为`/Users/wuxian/Documents/android-cts`,所以我CTS_ROOT变量设置为android-cts的根目录`/Users/wuxian/Documents`。 40 | 然后在Program arguments一栏输入`run cts --plan Monkey`,点击Run 按钮就可以运行了。 41 | 42 | 43 | ## 生成自己的执行文件 44 | 45 | 首先我们来看看执行文件tools目录下的文件: 46 | 47 | ![这里写图片描述](http://img.blog.csdn.net/20150605112353072) 48 | 49 | 其中cts-tradefed.jar就是cts-tradefed-host对应的jar包,tradefederation.jar就是tradefedertion项目对应的jar包,所以如果你开发完以后,想生成可执行文件,直接用你的项目替换这两个jar就行,下面说说如何导出成jar包。 50 | 51 | ### cts-tradefed.jar 52 | 53 | 在cts-tradefed-host项目右键,在弹出菜单中选择Export,选择Java项目下的jar file,点击Next: 54 | 55 | ![这里写图片描述](http://img.blog.csdn.net/20150605112929899) 56 | 57 | 在到处的资源文件选择中,选择src和res/report,不选择res/config的原因是我已经把该文件放到外面了,你可以在tools目录下看到,如果这里就不要选择了。然后在JAR file一栏点击Browse按钮,找到可执行文件路径下的cts-tradefed.jar,点击Finish就会将原来的替换掉。 58 | 59 | # tradefederation.jar 60 | 61 | 在tradefederation项目选择同样找到上面的导出配置页面: 62 | 63 | ![这里写图片描述](http://img.blog.csdn.net/20150605113201228) 64 | 65 | 这里我们选择src和res文件,JAR file选择tools下的tradefederation.jar,就会替换掉之前的jar包。 66 | 67 | ## 友情提示 68 | 69 | 如遇问题,请转至[toubleshoot.md](toubleshoot.md)查找解决方法. 70 | 71 | 72 | 73 | 74 | 75 | 76 | 77 | 78 | 79 | 80 | -------------------------------------------------------------------------------- /docs/how_to_use_in_mac.md: -------------------------------------------------------------------------------- 1 | # Mac下配置 2 | 3 | ## 第一步 4 | 5 | 6 | 同步[CrashMonkey4Androd_bin](https://github.com/DoctorQ/CrashMonkey4Androd_bin.git) 项目来获取可执行文件,或者直接下载ZIP包,解压。 7 | 8 | ## 第二步 9 | 10 | 解压后,项目目录结构如下: 11 | 12 | ![这里写图片描述](http://img.blog.csdn.net/20150604153335077) 13 | 14 | 找到tools目录下的cts-tradefed文件,双击会出现终端窗口: 15 | 16 | ![这里写图片描述](http://img.blog.csdn.net/20150604153616125) 17 | 18 | (可以看出来我们支持多设备) 19 | 20 | ## 第三步 21 | 22 | 在终端输入命令 : `run cts --plan Monkey` 按`return` ,@Monkey开跑了。 23 | 24 | ![这里写图片描述](http://img.blog.csdn.net/20150604154251327) 25 | 26 | ## 第四步 27 | 28 | 参看报告,在tools的同级目录repository下有2个目录很重要 29 | 1. logs:保存测试过程中的截图和log信息 30 | 2. results: 保存测试报告 31 | 32 | ![这里写图片描述](http://img.blog.csdn.net/20150604154414734) 33 | 34 | 首先去results下打开报告,一个文件夹代表一次测试,我们刚才有2台设备,所以生成了2个报告,进入文件下找到index.html,打开: 35 | 36 | ![这里写图片描述](http://img.blog.csdn.net/20150604154900065) 37 | 38 | **有crash版本:** 39 | 40 | ![这里写图片描述](http://img.blog.csdn.net/20150604160419624) 41 | `index.html`为结果总结页面,上面显示了测试设备的硬件信息(Hardware),被测应用信息(Application),测试周期(Span),结果(Results)。下方有一个表格中显示了测试所花时间(Duration),Monkey的事件数(20)。点击result一览的链接进入详细报告: 42 | **无crash版本:** 43 | 44 | ![这里写图片描述](http://img.blog.csdn.net/20150604155242764) 45 | **有crash版本** 46 | ![这里写图片描述](http://img.blog.csdn.net/20150604160526900) 47 | 48 | 详细页面中显示了最后50步的操作截图(少于50的全部列出),截图上绘制了操作的类型,还可以点击图片查看该步操作的相关logcat信息。还包括3按钮(crash log按钮会在发生crash的时候显示),点击`system log` 可以看到系统log: 49 | 50 | ![这里写图片描述](http://img.blog.csdn.net/20150604155706646) 51 | 52 | 点击`uiauto trace`按钮会显示所有步骤的信息: 53 | 54 | ![这里写图片描述](http://img.blog.csdn.net/20150604155805271) 55 | 56 | 如果有`crash log` 按钮,会显示crash的简短信息,目前只是简单的从logcat分析ANR和Java Crash信息,后续会详细研究一下crash知识: 57 | 58 | ![这里写图片描述](http://img.blog.csdn.net/20150604160101279) 59 | -------------------------------------------------------------------------------- /docs/how_to_use_in_windows.md: -------------------------------------------------------------------------------- 1 | # WINDOWS下配置 2 | 3 | ## 第一步 4 | 5 | 6 | 同步[CrashMonkey4Androd_bin](https://github.com/DoctorQ/CrashMonkey4Androd_bin.git) 项目来获取可执行文件,或者直接下载ZIP包,解压。 7 | 8 | ## 第二步 9 | 10 | 解压后,项目目录结构如下: 11 | 12 | ![这里写图片描述](http://img.blog.csdn.net/20150605155345923) 13 | 14 | 找到tools目录下的cts-tradefed.bat文件,双击会出现终端窗口: 15 | 16 | ![这里写图片描述](http://img.blog.csdn.net/20150605155436600) 17 | 18 | 19 | 20 | ## 第三步 21 | 22 | 在终端输入命令 : `run cts --plan Monkey` 按`return` ,@Monkey开跑了。 23 | 24 | ![这里写图片描述](http://img.blog.csdn.net/20150605155636736) 25 | 26 | ## 第四步 27 | 28 | 参看报告,在tools的同级目录repository下有2个目录很重要 29 | 1. logs:保存测试过程中的截图和log信息 30 | 2. results: 保存测试报告 31 | 32 | ![这里写图片描述](http://img.blog.csdn.net/20150605155930153) 33 | 34 | 首先去results下打开报告,一个文件夹代表一次测试,我们刚才有1台设备,所以生成了1个报告,进入文件下找到index.html,打开: 35 | 36 | ![这里写图片描述](http://img.blog.csdn.net/20150605160151474) 37 | 38 | 39 | 40 | 其他信息和mac版一样.不再详细介绍. 41 | -------------------------------------------------------------------------------- /docs/troubleshoot.md: -------------------------------------------------------------------------------- 1 | ## TroubleShoot 2 | 3 | 配置过程中如遇问题,请查看下面的问题总结: 4 | 5 | ### 1. Unable to locate adb 6 | 7 | ``` 8 | 9 | 06-04 19:34:10 W/DeviceManager: Fastboot is not available. 10 | 06-04 19:34:10 E/adb: Unable to locate adb. 11 | Please use SDK Manager and check if Android SDK platform-tools are installed. 12 | Android CTS 4.4_r0 13 | Non-interactive mode: Running initial command then exiting. 14 | Using commandline arguments as starting command: [run, cts, --plan, Monkey, -v, 10] 15 | 06-04 19:34:10 I/ConfigurationFactory: Loading configuration 'cts' 16 | 06-04 19:34:11 I/CommandScheduler: Waiting for invocation threads to complete 17 | 06-04 19:34:11 I/LogRegistry: Saved log to /var/folders/3j/s3hfvmy572vcn3h02c_rxcbm0000gn/T/tradefed_global_log_5366589741908633505.txt 18 | 06-04 19:34:11 I/CommandScheduler: All done 19 | 20 | ``` 21 | 22 | 在Mac环境下会遇到这种问题,需要使用shell脚本启动命令行,eclipse自带的一个名为eclipse的脚本可以启动,然后就可以识别了. 23 | 24 | 25 | 26 | 27 | -------------------------------------------------------------------------------- /libs/chimpchat-22.2.0.jar: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/DoctorQ/CrashMonkey4Android/513ab7a54c6b333ef08361b39f47da389ea495df/libs/chimpchat-22.2.0.jar -------------------------------------------------------------------------------- /libs/commons-compress-1.8.1.jar: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/DoctorQ/CrashMonkey4Android/513ab7a54c6b333ef08361b39f47da389ea495df/libs/commons-compress-1.8.1.jar -------------------------------------------------------------------------------- /libs/ddmlib-prebuilt.jar: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/DoctorQ/CrashMonkey4Android/513ab7a54c6b333ef08361b39f47da389ea495df/libs/ddmlib-prebuilt.jar -------------------------------------------------------------------------------- /libs/gson-2.2.4.jar: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/DoctorQ/CrashMonkey4Android/513ab7a54c6b333ef08361b39f47da389ea495df/libs/gson-2.2.4.jar -------------------------------------------------------------------------------- /libs/guava-17.0.jar: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/DoctorQ/CrashMonkey4Android/513ab7a54c6b333ef08361b39f47da389ea495df/libs/guava-17.0.jar -------------------------------------------------------------------------------- /libs/hosttestlib.jar: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/DoctorQ/CrashMonkey4Android/513ab7a54c6b333ef08361b39f47da389ea495df/libs/hosttestlib.jar -------------------------------------------------------------------------------- /libs/imagedraw.jar: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/DoctorQ/CrashMonkey4Android/513ab7a54c6b333ef08361b39f47da389ea495df/libs/imagedraw.jar -------------------------------------------------------------------------------- /libs/javalib-deviceinfo.jar: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/DoctorQ/CrashMonkey4Android/513ab7a54c6b333ef08361b39f47da389ea495df/libs/javalib-deviceinfo.jar -------------------------------------------------------------------------------- /libs/javalib-deviceutil.jar: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/DoctorQ/CrashMonkey4Android/513ab7a54c6b333ef08361b39f47da389ea495df/libs/javalib-deviceutil.jar -------------------------------------------------------------------------------- /libs/jline-0.9.9.jar: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/DoctorQ/CrashMonkey4Android/513ab7a54c6b333ef08361b39f47da389ea495df/libs/jline-0.9.9.jar -------------------------------------------------------------------------------- /libs/kxml2-2.3.0.jar: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/DoctorQ/CrashMonkey4Android/513ab7a54c6b333ef08361b39f47da389ea495df/libs/kxml2-2.3.0.jar -------------------------------------------------------------------------------- /libs/tradefederation.jar: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/DoctorQ/CrashMonkey4Android/513ab7a54c6b333ef08361b39f47da389ea495df/libs/tradefederation.jar -------------------------------------------------------------------------------- /res/config/cts.xml: -------------------------------------------------------------------------------- 1 | 2 | 17 | 18 | 19 | 109 | -------------------------------------------------------------------------------- /src/com/android/cts/tradefed/build/CtsBuildHelper.java: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright (C) 2010 The Android Open Source Project 3 | * 4 | * Licensed under the Apache License, Version 2.0 (the "License"); 5 | * you may not use this file except in compliance with the License. 6 | * You may obtain a copy of the License at 7 | * 8 | * http://www.apache.org/licenses/LICENSE-2.0 9 | * 10 | * Unless required by applicable law or agreed to in writing, software 11 | * distributed under the License is distributed on an "AS IS" BASIS, 12 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 | * See the License for the specific language governing permissions and 14 | * limitations under the License. 15 | */ 16 | 17 | package com.android.cts.tradefed.build; 18 | 19 | import com.android.tradefed.build.IBuildInfo; 20 | import com.android.tradefed.build.IFolderBuildInfo; 21 | 22 | import java.io.File; 23 | import java.io.FileNotFoundException; 24 | 25 | /** 26 | * Helper class for retrieving files from the CTS install. 27 | *

28 | * Encapsulates the filesystem layout of the CTS installation. 29 | */ 30 | public class CtsBuildHelper { 31 | 32 | static final String CTS_DIR_NAME = "android-cts"; 33 | private final String mSuiteName = "CTS"; 34 | /** The root location of the extracted CTS package */ 35 | private final File mRootDir; 36 | /** the {@link CTS_DIR_NAME} directory */ 37 | private final File mCtsDir; 38 | 39 | /** 40 | * Creates a {@link CtsBuildHelper}. 41 | * 42 | * @param rootDir the parent folder that contains the "android-cts" directory and all its 43 | * contents. 44 | */ 45 | public CtsBuildHelper(File rootDir) { 46 | mRootDir = rootDir; 47 | mCtsDir = new File(mRootDir, CTS_DIR_NAME); 48 | } 49 | 50 | /** 51 | * Alternate {@link CtsBuildHelper} constructor that takes the {@link IFolderBuildInfo} 52 | * representation of a CTS build. 53 | * 54 | * @param build the {@link IFolderBuildInfo} 55 | * @throws FileNotFoundException 56 | */ 57 | public CtsBuildHelper(IFolderBuildInfo build) throws FileNotFoundException { 58 | this(build.getRootDir()); 59 | } 60 | 61 | /** 62 | * A helper factory method that creates and validates a {@link CtsBuildHelper} given an 63 | * {@link IBuildInfo}. 64 | * 65 | * @param build the {@link IBuildInfo} 66 | * @return the {@link CtsBuildHelper} 67 | * @throws IllegalArgumentException if provided build is not a valid CTS build 68 | */ 69 | public static CtsBuildHelper createBuildHelper(IBuildInfo build) { 70 | if (!(build instanceof IFolderBuildInfo)) { 71 | throw new IllegalArgumentException(String.format( 72 | "Wrong build type. Expected %s, received %s", IFolderBuildInfo.class.getName(), 73 | build.getClass().getName())); 74 | } 75 | try { 76 | CtsBuildHelper ctsBuild = new CtsBuildHelper((IFolderBuildInfo)build); 77 | ctsBuild.validateStructure(); 78 | return ctsBuild; 79 | } catch (FileNotFoundException e) { 80 | throw new IllegalArgumentException("Invalid CTS build provided.", e); 81 | } 82 | } 83 | 84 | public String getSuiteName() { 85 | return mSuiteName; 86 | } 87 | 88 | /** 89 | * @return a {@link File} representing the parent folder of the CTS installation 90 | */ 91 | public File getRootDir() { 92 | return mRootDir; 93 | } 94 | 95 | /** 96 | * @return a {@link File} representing the "android-cts" folder of the CTS installation 97 | */ 98 | public File getCtsDir() { 99 | return mCtsDir; 100 | } 101 | 102 | /** 103 | * @return a {@link File} representing the test application file with given name 104 | * @throws FileNotFoundException if file does not exist 105 | */ 106 | public File getTestApp(String appFileName) throws FileNotFoundException { 107 | File apkFile = new File(getTestCasesDir(), appFileName); 108 | if (!apkFile.exists()) { 109 | throw new FileNotFoundException(String.format("CTS test app file %s does not exist", 110 | apkFile.getAbsolutePath())); 111 | } 112 | return apkFile; 113 | } 114 | 115 | private File getRepositoryDir() { 116 | return new File(getCtsDir(), "repository"); 117 | } 118 | 119 | /** 120 | * @return a {@link File} representing the results directory. 121 | */ 122 | public File getResultsDir() { 123 | return new File(getRepositoryDir(), "results"); 124 | } 125 | 126 | /** 127 | * @return a {@link File} representing the directory to store result logs. 128 | */ 129 | public File getLogsDir() { 130 | return new File(getRepositoryDir(), "logs"); 131 | } 132 | 133 | /** 134 | * @return a {@link File} representing the test cases directory 135 | */ 136 | public File getTestCasesDir() { 137 | return new File(getRepositoryDir(), "testcases"); 138 | } 139 | 140 | /** 141 | * @return a {@link File} representing the test plan directory 142 | */ 143 | public File getTestPlansDir() { 144 | return new File(getRepositoryDir(), "plans"); 145 | } 146 | 147 | /** 148 | * @return a {@link File} representing the test plan with given name. note: no attempt will be 149 | * made to ensure the plan actually exists 150 | * @throws FileNotFoundException if plans directory does not exist 151 | */ 152 | public File getTestPlanFile(String planName) throws FileNotFoundException { 153 | String ctsPlanRelativePath = String.format("%s.xml", planName); 154 | return new File(getTestPlansDir(), ctsPlanRelativePath); 155 | } 156 | 157 | /** 158 | * Check the validity of the CTS build file system structure. 159 | * @throws FileNotFoundException if any major directories are missing 160 | */ 161 | public void validateStructure() throws FileNotFoundException { 162 | if (!getCtsDir().exists()) { 163 | throw new FileNotFoundException(String.format( 164 | "CTS install folder %s does not exist", getCtsDir().getAbsolutePath())); 165 | } 166 | if (!getTestCasesDir().exists()) { 167 | throw new FileNotFoundException(String.format( 168 | "CTS test cases folder %s does not exist", 169 | getTestCasesDir().getAbsolutePath())); 170 | } 171 | if (!getTestPlansDir().exists()) { 172 | throw new FileNotFoundException(String.format( 173 | "CTS test plans folder %s does not exist", 174 | getTestPlansDir().getAbsolutePath())); 175 | } 176 | } 177 | } 178 | -------------------------------------------------------------------------------- /src/com/android/cts/tradefed/build/CtsBuildProvider.java: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright (C) 2010 The Android Open Source Project 3 | * 4 | * Licensed under the Apache License, Version 2.0 (the "License"); 5 | * you may not use this file except in compliance with the License. 6 | * You may obtain a copy of the License at 7 | * 8 | * http://www.apache.org/licenses/LICENSE-2.0 9 | * 10 | * Unless required by applicable law or agreed to in writing, software 11 | * distributed under the License is distributed on an "AS IS" BASIS, 12 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 | * See the License for the specific language governing permissions and 14 | * limitations under the License. 15 | */ 16 | package com.android.cts.tradefed.build; 17 | 18 | import com.android.tradefed.build.FolderBuildInfo; 19 | import com.android.tradefed.build.IBuildInfo; 20 | import com.android.tradefed.build.IBuildProvider; 21 | import com.android.tradefed.build.IFolderBuildInfo; 22 | import com.android.tradefed.config.Option; 23 | 24 | import java.io.File; 25 | 26 | /** 27 | * A simple {@link IBuildProvider} that uses a pre-existing CTS install. 28 | */ 29 | public class CtsBuildProvider implements IBuildProvider { 30 | 31 | @Option(name="cts-install-path", description="the path to the cts installation to use") 32 | private String mCtsRootDirPath = System.getProperty("CTS_ROOT"); 33 | 34 | public static final String CTS_BUILD_VERSION = "4.4_r0"; 35 | 36 | /** 37 | * {@inheritDoc} 38 | */ 39 | @Override 40 | public IBuildInfo getBuild() { 41 | if (mCtsRootDirPath == null) { 42 | throw new IllegalArgumentException("Missing --cts-install-path"); 43 | } 44 | IFolderBuildInfo ctsBuild = new FolderBuildInfo(CTS_BUILD_VERSION, "cts", "cts"); 45 | ctsBuild.setRootDir(new File(mCtsRootDirPath)); 46 | return ctsBuild; 47 | } 48 | 49 | /** 50 | * {@inheritDoc} 51 | */ 52 | @Override 53 | public void buildNotTested(IBuildInfo info) { 54 | // ignore 55 | } 56 | 57 | /** 58 | * {@inheritDoc} 59 | */ 60 | @Override 61 | public void cleanUp(IBuildInfo info) { 62 | // ignore 63 | } 64 | } 65 | -------------------------------------------------------------------------------- /src/com/android/cts/tradefed/device/DeviceInfoCollector.java: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright (C) 2010 The Android Open Source Project 3 | * 4 | * Licensed under the Apache License, Version 2.0 (the "License"); 5 | * you may not use this file except in compliance with the License. 6 | * You may obtain a copy of the License at 7 | * 8 | * http://www.apache.org/licenses/LICENSE-2.0 9 | * 10 | * Unless required by applicable law or agreed to in writing, software 11 | * distributed under the License is distributed on an "AS IS" BASIS, 12 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 | * See the License for the specific language governing permissions and 14 | * limitations under the License. 15 | */ 16 | package com.android.cts.tradefed.device; 17 | 18 | import com.android.ddmlib.Log; 19 | import com.android.tradefed.device.DeviceNotAvailableException; 20 | import com.android.tradefed.device.ITestDevice; 21 | import com.android.tradefed.result.ITestInvocationListener; 22 | import com.android.tradefed.testtype.InstrumentationTest; 23 | 24 | import java.io.File; 25 | 26 | /** 27 | * Collects info from device under test. 28 | *

29 | * This class simply serves as a conduit for grabbing info from device using the device info 30 | * collector apk, and forwarding that data directly to the {@link ITestInvocationListener} as run 31 | * metrics. 32 | */ 33 | public class DeviceInfoCollector { 34 | 35 | private static final String LOG_TAG = "DeviceInfoCollector"; 36 | private static final String APK_NAME = "TestDeviceSetup"; 37 | public static final String APP_PACKAGE_NAME = "android.tests.devicesetup"; 38 | private static final String INSTRUMENTATION_NAME = "android.tests.getinfo.DeviceInfoInstrument"; 39 | 40 | /** 41 | * Installs and runs the device info collector instrumentation, and forwards results 42 | * to the listener 43 | * 44 | * @param device 45 | * @param listener 46 | * @throws DeviceNotAvailableException 47 | */ 48 | public static void collectDeviceInfo(ITestDevice device, File testApkDir, 49 | ITestInvocationListener listener) throws DeviceNotAvailableException { 50 | File apkFile = new File(testApkDir, String.format("%s.apk", APK_NAME)); 51 | if (!apkFile.exists()) { 52 | Log.e(LOG_TAG, String.format("Could not find %s", apkFile.getAbsolutePath())); 53 | } 54 | // collect the instrumentation bundle results using instrumentation test 55 | // should work even though no tests will actually be run 56 | InstrumentationTest instrTest = new InstrumentationTest(); 57 | instrTest.setDevice(device); 58 | instrTest.setInstallFile(apkFile); 59 | // no need to collect tests and re-run 60 | instrTest.setRerunMode(false); 61 | instrTest.setPackageName(APP_PACKAGE_NAME); 62 | instrTest.setRunnerName(INSTRUMENTATION_NAME); 63 | instrTest.run(listener); 64 | } 65 | } 66 | -------------------------------------------------------------------------------- /src/com/android/cts/tradefed/device/DeviceNetWorkListener.java: -------------------------------------------------------------------------------- 1 | package com.android.cts.tradefed.device; 2 | 3 | import com.android.tradefed.device.DeviceNotAvailableException; 4 | import com.android.tradefed.device.ITestDevice; 5 | import com.android.tradefed.log.LogUtil.CLog; 6 | import com.android.tradefed.targetprep.TargetSetupError; 7 | 8 | public class DeviceNetWorkListener implements Runnable { 9 | public boolean isRunning() { 10 | return running; 11 | } 12 | 13 | public void setRunning(boolean running) { 14 | this.running = running; 15 | } 16 | 17 | private String mWifiSSID = null; 18 | private String mWifiPsk = null; 19 | private boolean running = true; 20 | 21 | private ITestDevice mDevice; 22 | 23 | public DeviceNetWorkListener(ITestDevice device, String wifiSsid, 24 | String wifiPsk) { 25 | // TODO Auto-generated constructor stub 26 | this.mDevice = device; 27 | this.mWifiSSID = wifiSsid; 28 | this.mWifiPsk = wifiPsk; 29 | } 30 | 31 | @Override 32 | public void run() { 33 | if (mWifiSSID == null) 34 | return; 35 | while (running) { 36 | try { 37 | if (mDevice.checkWifiConnection(mWifiSSID)) { 38 | CLog.i(String.format("%s connected ", mWifiSSID)); 39 | } else { 40 | CLog.i(String.format("please connect wifi : %s", mWifiSSID)); 41 | mDevice.connectToWifiNetwork(mWifiSSID, mWifiPsk); 42 | } 43 | } catch (TargetSetupError e) { 44 | // TODO Auto-generated catch block 45 | e.printStackTrace(); 46 | } catch (DeviceNotAvailableException e) { 47 | // TODO Auto-generated catch block 48 | e.printStackTrace(); 49 | } 50 | 51 | } 52 | 53 | CLog.i("DeviceNetWorkListener stop....."); 54 | // TODO Auto-generated method stub 55 | 56 | } 57 | 58 | } 59 | -------------------------------------------------------------------------------- /src/com/android/cts/tradefed/device/DeviceUnlock.java: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright (C) 2010 The Android Open Source Project 3 | * 4 | * Licensed under the Apache License, Version 2.0 (the "License"); 5 | * you may not use this file except in compliance with the License. 6 | * You may obtain a copy of the License at 7 | * 8 | * http://www.apache.org/licenses/LICENSE-2.0 9 | * 10 | * Unless required by applicable law or agreed to in writing, software 11 | * distributed under the License is distributed on an "AS IS" BASIS, 12 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 | * See the License for the specific language governing permissions and 14 | * limitations under the License. 15 | */ 16 | package com.android.cts.tradefed.device; 17 | 18 | import com.android.ddmlib.Log; 19 | import com.android.tradefed.device.DeviceNotAvailableException; 20 | import com.android.tradefed.device.ITestDevice; 21 | import com.android.tradefed.log.LogUtil.CLog; 22 | import com.android.tradefed.result.ITestInvocationListener; 23 | import com.android.tradefed.testtype.InstrumentationTest; 24 | 25 | import java.io.File; 26 | 27 | /** 28 | * Collects info from device under test. 29 | *

30 | * This class simply serves as a conduit for grabbing info from device using the 31 | * device info collector apk, and forwarding that data directly to the 32 | * {@link ITestInvocationListener} as run metrics. 33 | */ 34 | public class DeviceUnlock { 35 | 36 | private static final String LOG_TAG = "DeviceUnlock"; 37 | private static final String APK_NAME = "unlock_apk-debug"; 38 | public static final String APP_PACKAGE_NAME = "io.appium.unlock"; 39 | private static final String ACTIVITY_NAME = ".Unlock"; 40 | 41 | /** 42 | * Installs and runs the device info collector instrumentation, and forwards 43 | * results to the listener 44 | * 45 | * @param device 46 | * @param listener 47 | * @throws DeviceNotAvailableException 48 | */ 49 | public static void unlockDevice(ITestDevice device, File testApkDir) 50 | throws DeviceNotAvailableException { 51 | File apkFile = new File(testApkDir, String.format("%s.apk", APK_NAME)); 52 | if (!apkFile.exists()) { 53 | Log.e(LOG_TAG, 54 | String.format("Could not find %s", 55 | apkFile.getAbsolutePath())); 56 | } 57 | // collect the instrumentation bundle results using instrumentation test 58 | // should work even though no tests will actually be run 59 | device.installPackage(apkFile, true); 60 | String start = "am start " + APP_PACKAGE_NAME + "/" + ACTIVITY_NAME; 61 | CLog.i(start); 62 | device.executeShellCommand(start); 63 | //device.uninstallPackage(APP_PACKAGE_NAME); 64 | CLog.i("Finsihed to disable keyguard on %s using %s ", 65 | device.getSerialNumber(), APK_NAME); 66 | } 67 | 68 | } 69 | -------------------------------------------------------------------------------- /src/com/android/cts/tradefed/device/MonkeyActivityListener.java: -------------------------------------------------------------------------------- 1 | package com.android.cts.tradefed.device; 2 | 3 | import com.android.tradefed.device.DeviceNotAvailableException; 4 | import com.android.tradefed.device.ITestDevice; 5 | import com.android.tradefed.log.LogUtil.CLog; 6 | 7 | public class MonkeyActivityListener implements Runnable { 8 | public boolean isRunning() { 9 | return running; 10 | } 11 | 12 | public void setRunning(boolean running) { 13 | this.running = running; 14 | } 15 | 16 | private ITestDevice mDevice; 17 | private String mPackage; 18 | private String mActivity; 19 | private boolean running = true; 20 | private static final String GET_ACTIVITY_CMD = "dumpsys activity top | grep ACTIVITY"; 21 | 22 | public MonkeyActivityListener(ITestDevice device, String goalPackage, 23 | String goalActivity) { 24 | this.mDevice = device; 25 | this.mPackage = goalPackage; 26 | this.mActivity = goalActivity; 27 | } 28 | 29 | @Override 30 | public void run() { 31 | while (running) { 32 | try { 33 | String result = mDevice.executeShellCommand(GET_ACTIVITY_CMD); 34 | if (!result.contains(mPackage)&&running) { 35 | CLog.i(String 36 | .format("Current activity is %s, It is not your test app %s.restart app", 37 | result, mPackage)); 38 | mDevice.executeShellCommand("am start " + mPackage + "/" 39 | + mActivity); 40 | } 41 | } catch (DeviceNotAvailableException e) { 42 | // TODO Auto-generated catch block 43 | e.printStackTrace(); 44 | } 45 | 46 | } 47 | CLog.i("MonkeyActivityListener stop....."); 48 | } 49 | } 50 | -------------------------------------------------------------------------------- /src/com/android/cts/tradefed/result/AbstractXmlPullParser.java: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright (C) 2011 The Android Open Source Project 3 | * 4 | * Licensed under the Apache License, Version 2.0 (the "License"); 5 | * you may not use this file except in compliance with the License. 6 | * You may obtain a copy of the License at 7 | * 8 | * http://www.apache.org/licenses/LICENSE-2.0 9 | * 10 | * Unless required by applicable law or agreed to in writing, software 11 | * distributed under the License is distributed on an "AS IS" BASIS, 12 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 | * See the License for the specific language governing permissions and 14 | * limitations under the License. 15 | */ 16 | 17 | package com.android.cts.tradefed.result; 18 | 19 | import com.android.tradefed.util.xml.AbstractXmlParser.ParseException; 20 | 21 | import org.xmlpull.v1.XmlPullParser; 22 | import org.xmlpull.v1.XmlPullParserException; 23 | import org.xmlpull.v1.XmlPullParserFactory; 24 | 25 | import java.io.IOException; 26 | import java.io.Reader; 27 | 28 | /** 29 | * Helper abstract class for XmlPullParser 30 | * 31 | * TODO: move to com.android.tradefed.util.xml 32 | */ 33 | public abstract class AbstractXmlPullParser { 34 | 35 | /** 36 | * Parse the summary data from the given input data. 37 | * 38 | * @param xmlReader the input XML 39 | * @throws ParseException if failed to parse the summary data. 40 | */ 41 | public void parse(Reader xmlReader) throws ParseException { 42 | try { 43 | XmlPullParserFactory fact = org.xmlpull.v1.XmlPullParserFactory.newInstance(); 44 | XmlPullParser parser = fact.newPullParser(); 45 | parser.setInput (xmlReader); 46 | parse(parser); 47 | } catch (XmlPullParserException e) { 48 | throw new ParseException(e); 49 | } catch (IOException e) { 50 | throw new ParseException(e); 51 | } 52 | } 53 | 54 | public abstract void parse(XmlPullParser parser) throws XmlPullParserException, IOException; 55 | 56 | /** 57 | * Parse an integer value from an XML attribute 58 | * 59 | * @param parser the {@link XmlPullParser} 60 | * @param name the attribute name 61 | * @return the parsed value or 0 if it could not be parsed 62 | */ 63 | protected int parseIntAttr(XmlPullParser parser, String name) { 64 | try { 65 | String value = parser.getAttributeValue(null, name); 66 | if (value != null) { 67 | return Integer.parseInt(value); 68 | } 69 | } catch (NumberFormatException e) { 70 | // ignore 71 | } 72 | return 0; 73 | } 74 | 75 | /** 76 | * Parse a boolean attribute value 77 | */ 78 | protected boolean parseBooleanAttr(XmlPullParser parser, String name) { 79 | String stringValue = parser.getAttributeValue(null, name); 80 | return stringValue != null && 81 | Boolean.parseBoolean(stringValue); 82 | } 83 | 84 | /** 85 | * Helper method for retrieving attribute value with given name 86 | * 87 | * @param parser the XmlPullParser 88 | * @param name the attribute name 89 | * @return the attribute value 90 | */ 91 | protected String getAttribute(XmlPullParser parser, String name) { 92 | return parser.getAttributeValue(null, name); 93 | } 94 | } 95 | -------------------------------------------------------------------------------- /src/com/android/cts/tradefed/result/CrashAnalyzer.java: -------------------------------------------------------------------------------- 1 | package com.android.cts.tradefed.result; 2 | 3 | import java.io.BufferedReader; 4 | import java.io.File; 5 | import java.io.FileNotFoundException; 6 | import java.io.FileReader; 7 | import java.io.FileWriter; 8 | import java.io.IOException; 9 | import java.util.regex.Pattern; 10 | 11 | public class CrashAnalyzer { 12 | public File getmCrashFile() { 13 | return mCrashFile; 14 | } 15 | 16 | public void setmCrashFile(File mCrashFile) { 17 | this.mCrashFile = mCrashFile; 18 | } 19 | 20 | private static final String ANR = "ANR"; 21 | private static final String JAVACRASH = "JAVACRASH"; 22 | private static final String CRASH_FILE = "crash.txt"; 23 | private static final String HEAD_REGEX = "^[0-1][0-9]-[0-2][0-9]\\s[0-2][0-9]:[0-6][0-9]:[0-5][0-9].[\\d]+\\s+[\\d]+\\s+[\\d]+\\s+E\\s"; 24 | private static final String ANR_REGEX = HEAD_REGEX 25 | + "ActivityManager:\\sANR\\sin.*"; 26 | private static final String CRASH_REGEX = HEAD_REGEX + "AndroidRuntime:.*"; 27 | private static final String COMMON_REGEX = "^[0-1][0-9]-[0-2][0-9]\\s[0-2][0-9]:[0-6][0-9]:[0-5][0-9].[\\d]+\\s+[\\d]+\\s+[\\d]+\\s+[VDIWE]\\s.*"; 28 | private File mLogFile = null; 29 | private File mCrashFile = null; 30 | private boolean isCrashMessage = false; 31 | private int crashCount = 0; 32 | private int anrCount = 0; 33 | 34 | // private static Map mCrash = new HashMap(); 36 | // static { 37 | // mCrash.put(ANR, "ANR"); 38 | // mCrash.put(JAVACRASH, "java.lang.NullPointerException"); 39 | // } 40 | 41 | public CrashAnalyzer(File file, File crashFile) { 42 | mLogFile = file; 43 | mCrashFile = crashFile; 44 | } 45 | 46 | public CrashAnalyzer(File logFile) { 47 | mLogFile = logFile; 48 | if (mCrashFile == null) 49 | mCrashFile = new File(mLogFile.getParent(), CRASH_FILE); 50 | 51 | } 52 | 53 | public void parserLogcat() { 54 | FileReader fr = null; 55 | BufferedReader br = null; 56 | FileWriter wr = null; 57 | 58 | try { 59 | fr = new FileReader(mLogFile); 60 | br = new BufferedReader(fr); 61 | wr = new FileWriter(mCrashFile); 62 | String line = null; 63 | boolean continueWrite = false; 64 | while ((line = br.readLine()) != null) { 65 | 66 | // 先判断有没有crash 67 | if (Pattern.matches(CRASH_REGEX, line)) { 68 | if (Pattern.matches(HEAD_REGEX 69 | + "AndroidRuntime:\\s+Process.*", line)) { 70 | String str = getHead("Crash " + (crashCount++) 71 | + " Message"); 72 | wr.write(str); 73 | } 74 | wr.write(line + "\n"); 75 | continueWrite = true; 76 | continue; 77 | } 78 | 79 | // 判断有没有ANR 80 | if (Pattern.matches(ANR_REGEX, line)) { 81 | String str = getHead("ANR " + anrCount + " Message"); 82 | wr.write(str); 83 | wr.write(line + "\n"); 84 | continueWrite = true; 85 | continue; 86 | } 87 | 88 | // 输出有E标识的信息 89 | if (Pattern.matches(HEAD_REGEX + ".*", line)) { 90 | wr.write(line + "\n"); 91 | // continueWrite = true; 92 | continue; 93 | } 94 | // 结束符 95 | if (Pattern.matches(COMMON_REGEX, line)) { 96 | if (!continueWrite) 97 | continue; 98 | String str = getHead("Done"); 99 | 100 | wr.write(str + "\n"); 101 | continueWrite = false; 102 | } 103 | 104 | } 105 | } catch (FileNotFoundException e) { 106 | // TODO Auto-generated catch block 107 | e.printStackTrace(); 108 | } catch (IOException e) { 109 | // TODO Auto-generated catch block 110 | e.printStackTrace(); 111 | } finally { 112 | try { 113 | if (wr != null) 114 | wr.close(); 115 | } catch (IOException e) { 116 | // TODO Auto-generated catch block 117 | e.printStackTrace(); 118 | } 119 | try { 120 | if (fr != null) 121 | fr.close(); 122 | } catch (IOException e) { 123 | // TODO Auto-generated catch block 124 | e.printStackTrace(); 125 | } 126 | try { 127 | if (br != null) 128 | br.close(); 129 | } catch (IOException e) { 130 | // TODO Auto-generated catch block 131 | e.printStackTrace(); 132 | } 133 | } 134 | } 135 | 136 | private String getHead(String str) { 137 | String output = "====================================================================" 138 | + str 139 | + "====================================================================\n"; 140 | 141 | return output; 142 | } 143 | 144 | public boolean hasCrash() { 145 | return anrCount > 0 || crashCount > 0; 146 | } 147 | 148 | public int getCrashCount(){ 149 | return anrCount + crashCount; 150 | } 151 | 152 | public static void main(String[] args) { 153 | File file = new File( 154 | "/Users/wuxian/Documents/android-cts/repository/logs/2015.06.03_11.41.21/monkey_8465242561598443342.txt"); 155 | CrashAnalyzer analyzer = new CrashAnalyzer(file); 156 | analyzer.parserLogcat(); 157 | 158 | } 159 | 160 | } 161 | -------------------------------------------------------------------------------- /src/com/android/cts/tradefed/result/CtsTestStatus.java: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright (C) 2011 The Android Open Source Project 3 | * 4 | * Licensed under the Apache License, Version 2.0 (the "License"); 5 | * you may not use this file except in compliance with the License. 6 | * You may obtain a copy of the License at 7 | * 8 | * http://www.apache.org/licenses/LICENSE-2.0 9 | * 10 | * Unless required by applicable law or agreed to in writing, software 11 | * distributed under the License is distributed on an "AS IS" BASIS, 12 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 | * See the License for the specific language governing permissions and 14 | * limitations under the License. 15 | */ 16 | package com.android.cts.tradefed.result; 17 | 18 | /** 19 | * An enum of possible test statuses. 20 | */ 21 | public enum CtsTestStatus { 22 | PASS("pass"), 23 | FAIL("fail"), 24 | NOT_EXECUTED("notExecuted"); 25 | 26 | private String mValue; 27 | 28 | CtsTestStatus(String storedValue) { 29 | mValue = storedValue; 30 | } 31 | 32 | /** 33 | * Get the String representation of this test status that should be stored in 34 | * xml 35 | * @return 36 | */ 37 | String getValue() { 38 | return mValue; 39 | } 40 | 41 | /** 42 | * Find the {@link CtsTestStatus} corresponding to given string value 43 | *

44 | * Performs a case insensitive search 45 | * 46 | * @param value 47 | * @return the CtsTestStatus or null if it could not be found 48 | */ 49 | static CtsTestStatus getStatus(String value) { 50 | for (CtsTestStatus status : CtsTestStatus.values()) { 51 | if (value.compareToIgnoreCase(status.getValue()) == 0) { 52 | return status; 53 | } 54 | } 55 | return null; 56 | } 57 | } 58 | -------------------------------------------------------------------------------- /src/com/android/cts/tradefed/result/ITestResultRepo.java: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright (C) 2011 The Android Open Source Project 3 | * 4 | * Licensed under the Apache License, Version 2.0 (the "License"); 5 | * you may not use this file except in compliance with the License. 6 | * You may obtain a copy of the License at 7 | * 8 | * http://www.apache.org/licenses/LICENSE-2.0 9 | * 10 | * Unless required by applicable law or agreed to in writing, software 11 | * distributed under the License is distributed on an "AS IS" BASIS, 12 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 | * See the License for the specific language governing permissions and 14 | * limitations under the License. 15 | */ 16 | package com.android.cts.tradefed.result; 17 | 18 | import java.io.File; 19 | import java.util.List; 20 | 21 | /** 22 | * Repository for CTS results. 23 | */ 24 | public interface ITestResultRepo { 25 | 26 | /** 27 | * @return the list of {@link ITestSummary}s. The index of the {@link ITestSummary} in the 28 | * list is its session id 29 | */ 30 | public List getSummaries(); 31 | 32 | /** 33 | * Get the {@link TestResults} for given session id. 34 | * 35 | * @param sessionId the session id 36 | * @return the {@link TestResults} or null if the result with that session id 37 | * cannot be retrieved 38 | */ 39 | public TestResults getResult(int sessionId); 40 | 41 | /** 42 | * Get the report directory for given result 43 | * @param sessionId 44 | * @return 45 | */ 46 | public File getReportDir(int sessionId); 47 | 48 | } 49 | -------------------------------------------------------------------------------- /src/com/android/cts/tradefed/result/ITestSummary.java: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright (C) 2011 The Android Open Source Project 3 | * 4 | * Licensed under the Apache License, Version 2.0 (the "License"); 5 | * you may not use this file except in compliance with the License. 6 | * You may obtain a copy of the License at 7 | * 8 | * http://www.apache.org/licenses/LICENSE-2.0 9 | * 10 | * Unless required by applicable law or agreed to in writing, software 11 | * distributed under the License is distributed on an "AS IS" BASIS, 12 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 | * See the License for the specific language governing permissions and 14 | * limitations under the License. 15 | */ 16 | package com.android.cts.tradefed.result; 17 | 18 | /** 19 | * Interface for a single CTS result summary. 20 | */ 21 | public interface ITestSummary { 22 | 23 | /** 24 | * @return the session id 25 | */ 26 | int getId(); 27 | 28 | /** 29 | * @return the starting timestamp, also known as result directory name 30 | */ 31 | String getTimestamp(); 32 | 33 | /** 34 | * @return the num of not executed tests 35 | */ 36 | int getNumIncomplete(); 37 | 38 | /** 39 | * @return the number of failed tests 40 | */ 41 | int getNumFailed(); 42 | 43 | /** 44 | * @return the number of passed tests 45 | */ 46 | int getNumPassed(); 47 | 48 | /** 49 | * @return the test plan associated with result 50 | */ 51 | String getTestPlan(); 52 | 53 | /** 54 | * Return the user-friendly displayed start time stored in result XML. 55 | *

56 | * Expected format: {@link TimeUtil#getTimestamp()} 57 | */ 58 | String getStartTime(); 59 | 60 | /** 61 | * @return a comma separated list of device serials associated with result 62 | */ 63 | String getDeviceSerials(); 64 | 65 | } 66 | -------------------------------------------------------------------------------- /src/com/android/cts/tradefed/result/IssueReporter.java: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright (C) 2011 The Android Open Source Project 3 | * 4 | * Licensed under the Apache License, Version 2.0 (the "License"); 5 | * you may not use this file except in compliance with the License. 6 | * You may obtain a copy of the License at 7 | * 8 | * http://www.apache.org/licenses/LICENSE-2.0 9 | * 10 | * Unless required by applicable law or agreed to in writing, software 11 | * distributed under the License is distributed on an "AS IS" BASIS, 12 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 | * See the License for the specific language governing permissions and 14 | * limitations under the License. 15 | */ 16 | 17 | package com.android.cts.tradefed.result; 18 | 19 | import com.android.ddmlib.testrunner.TestIdentifier; 20 | import com.android.tradefed.build.IBuildInfo; 21 | import com.android.tradefed.config.Option; 22 | import com.android.tradefed.log.LogUtil.CLog; 23 | import com.android.tradefed.result.ITestInvocationListener; 24 | import com.android.tradefed.result.InputStreamSource; 25 | import com.android.tradefed.result.LogDataType; 26 | import com.android.tradefed.result.TestSummary; 27 | 28 | import java.io.ByteArrayOutputStream; 29 | import java.io.IOException; 30 | import java.io.InputStream; 31 | import java.util.Map; 32 | import java.util.concurrent.Callable; 33 | import java.util.concurrent.ExecutorService; 34 | import java.util.concurrent.Executors; 35 | import java.util.concurrent.TimeUnit; 36 | import java.util.zip.GZIPOutputStream; 37 | 38 | /** 39 | * Class that sends a HTTP POST multipart/form-data request containing details 40 | * about a test failure. 41 | */ 42 | public class IssueReporter implements ITestInvocationListener { 43 | 44 | private static final int BUGREPORT_SIZE = 500 * 1024; 45 | 46 | private static final String PRODUCT_NAME_KEY = "buildName"; 47 | private static final String BUILD_TYPE_KEY = "build_type"; 48 | private static final String BUILD_ID_KEY = "buildID"; 49 | 50 | @Option(name = "issue-server", description = "Server url to post test failures to.") 51 | private String mServerUrl; 52 | 53 | private final ExecutorService mReporterService = Executors.newCachedThreadPool(); 54 | 55 | private Issue mCurrentIssue; 56 | private String mBuildId; 57 | private String mBuildType; 58 | private String mProductName; 59 | 60 | @Override 61 | public void testFailed(TestFailure status, TestIdentifier test, String trace) { 62 | mCurrentIssue = new Issue(); 63 | mCurrentIssue.mTestName = test.toString(); 64 | mCurrentIssue.mStackTrace = trace; 65 | } 66 | 67 | @Override 68 | public void testLog(String dataName, LogDataType dataType, InputStreamSource dataStream) { 69 | if (dataName.startsWith("bug-")) { 70 | try { 71 | setBugReport(dataStream); 72 | } catch (IOException e) { 73 | CLog.e(e); 74 | } 75 | } 76 | } 77 | 78 | /** 79 | * Set the bug report for the current test failure. GZip it to save space. 80 | * This is only called when the --bugreport option is enabled. 81 | */ 82 | private void setBugReport(InputStreamSource dataStream) throws IOException { 83 | if (mCurrentIssue != null) { 84 | // Only one bug report can be stored at a time and they are gzipped to 85 | // about 0.5 MB so there shoudn't be any memory leak bringing down CTS. 86 | InputStream input = null; 87 | try { 88 | input = dataStream.createInputStream(); 89 | mCurrentIssue.mBugReport = getBytes(input, BUGREPORT_SIZE); 90 | } finally { 91 | if (input != null) { 92 | input.close(); 93 | } 94 | } 95 | } else { 96 | CLog.e("setBugReport is getting called on an empty issue..."); 97 | } 98 | } 99 | 100 | /** 101 | * @param input that will be gzipped and returne as a byte array 102 | * @param size of the output expected 103 | * @return the byte array with the input's data 104 | * @throws IOException 105 | */ 106 | static byte[] getBytes(InputStream input, int size) throws IOException { 107 | ByteArrayOutputStream byteOutput = new ByteArrayOutputStream(size); 108 | GZIPOutputStream gzipOutput = new GZIPOutputStream(byteOutput); 109 | for (byte[] buffer = new byte[1024]; ; ) { 110 | int numRead = input.read(buffer); 111 | if (numRead < 0) { 112 | break; 113 | } 114 | gzipOutput.write(buffer, 0, numRead); 115 | } 116 | gzipOutput.close(); 117 | return byteOutput.toByteArray(); 118 | } 119 | 120 | @Override 121 | public void testEnded(TestIdentifier test, Map testMetrics) { 122 | if (mCurrentIssue != null) { 123 | mReporterService.submit(mCurrentIssue); 124 | mCurrentIssue = null; 125 | } 126 | } 127 | 128 | @Override 129 | public void testRunEnded(long elapsedTime, Map runMetrics) { 130 | setDeviceMetrics(runMetrics); 131 | } 132 | 133 | /** Set device information. Populated once when the device info app runs. */ 134 | private void setDeviceMetrics(Map metrics) { 135 | if (metrics.containsKey(BUILD_ID_KEY)) { 136 | mBuildId = metrics.get(BUILD_ID_KEY); 137 | } 138 | if (metrics.containsKey(BUILD_TYPE_KEY)) { 139 | mBuildType = metrics.get(BUILD_TYPE_KEY); 140 | } 141 | if (metrics.containsKey(PRODUCT_NAME_KEY)) { 142 | mProductName = metrics.get(PRODUCT_NAME_KEY); 143 | } 144 | } 145 | 146 | @Override 147 | public void invocationEnded(long elapsedTime) { 148 | try { 149 | mReporterService.shutdown(); 150 | if (!mReporterService.awaitTermination(1, TimeUnit.MINUTES)) { 151 | CLog.i("Some issues could not be reported..."); 152 | } 153 | } catch (InterruptedException e) { 154 | CLog.e(e); 155 | } 156 | } 157 | 158 | class Issue implements Callable { 159 | 160 | private String mTestName; 161 | private String mStackTrace; 162 | private byte[] mBugReport; 163 | 164 | @Override 165 | public Void call() throws Exception { 166 | if (isEmpty(mServerUrl) 167 | || isEmpty(mBuildId) 168 | || isEmpty(mBuildType) 169 | || isEmpty(mProductName) 170 | || isEmpty(mTestName) 171 | || isEmpty(mStackTrace)) { 172 | return null; 173 | } 174 | 175 | new MultipartForm(mServerUrl) 176 | .addFormValue("productName", mProductName) 177 | .addFormValue("buildType", mBuildType) 178 | .addFormValue("buildId", mBuildId) 179 | .addFormValue("testName", mTestName) 180 | .addFormValue("stackTrace", mStackTrace) 181 | .addFormFile("bugReport", "bugreport.txt.gz", mBugReport) 182 | .submit(); 183 | 184 | return null; 185 | } 186 | 187 | private boolean isEmpty(String value) { 188 | return value == null || value.trim().isEmpty(); 189 | } 190 | } 191 | 192 | @Override 193 | public void invocationStarted(IBuildInfo buildInfo) { 194 | } 195 | 196 | @Override 197 | public void testRunStarted(String name, int numTests) { 198 | } 199 | 200 | @Override 201 | public void testStarted(TestIdentifier test) { 202 | } 203 | 204 | @Override 205 | public void testRunFailed(String arg0) { 206 | } 207 | 208 | @Override 209 | public void testRunStopped(long elapsedTime) { 210 | } 211 | 212 | @Override 213 | public void invocationFailed(Throwable cause) { 214 | } 215 | 216 | @Override 217 | public TestSummary getSummary() { 218 | return null; 219 | } 220 | } 221 | -------------------------------------------------------------------------------- /src/com/android/cts/tradefed/result/MultipartForm.java: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright (C) 2011 The Android Open Source Project 3 | * 4 | * Licensed under the Apache License, Version 2.0 (the "License"); 5 | * you may not use this file except in compliance with the License. 6 | * You may obtain a copy of the License at 7 | * 8 | * http://www.apache.org/licenses/LICENSE-2.0 9 | * 10 | * Unless required by applicable law or agreed to in writing, software 11 | * distributed under the License is distributed on an "AS IS" BASIS, 12 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 | * See the License for the specific language governing permissions and 14 | * limitations under the License. 15 | */ 16 | 17 | package com.android.cts.tradefed.result; 18 | 19 | import java.io.ByteArrayOutputStream; 20 | import java.io.IOException; 21 | import java.io.InputStream; 22 | import java.io.OutputStream; 23 | import java.io.OutputStreamWriter; 24 | import java.io.PrintWriter; 25 | import java.net.HttpURLConnection; 26 | import java.net.URL; 27 | import java.util.HashMap; 28 | import java.util.Map; 29 | 30 | /** MultipartForm builds a multipart form and submits it. */ 31 | class MultipartForm { 32 | 33 | private static final String FORM_DATA_BOUNDARY = "C75I55u3R3p0r73r"; 34 | 35 | private final String mServerUrl; 36 | 37 | private final Map mFormValues = new HashMap(); 38 | 39 | private String mName; 40 | private String mFileName; 41 | private byte[] mData; 42 | 43 | public MultipartForm(String serverUrl) { 44 | mServerUrl = serverUrl; 45 | } 46 | 47 | public MultipartForm addFormValue(String name, String value) { 48 | mFormValues.put(name, value); 49 | return this; 50 | } 51 | 52 | public MultipartForm addFormFile(String name, String fileName, byte[] data) { 53 | mName = name; 54 | mFileName = fileName; 55 | mData = data; 56 | return this; 57 | } 58 | 59 | public void submit() throws IOException { 60 | String redirectUrl = submitForm(mServerUrl); 61 | if (redirectUrl != null) { 62 | submitForm(redirectUrl); 63 | } 64 | } 65 | 66 | /** 67 | * @param serverUrl to post the data to 68 | * @return a url if the server redirected to another url 69 | * @throws IOException 70 | */ 71 | private String submitForm(String serverUrl) throws IOException { 72 | HttpURLConnection connection = null; 73 | try { 74 | URL url = new URL(serverUrl); 75 | connection = (HttpURLConnection) url.openConnection(); 76 | connection.setInstanceFollowRedirects(false); 77 | connection.setRequestMethod("POST"); 78 | connection.setDoOutput(true); 79 | connection.setRequestProperty("Content-Type", 80 | "multipart/form-data; boundary=" + FORM_DATA_BOUNDARY); 81 | 82 | byte[] body = getContentBody(); 83 | connection.setRequestProperty("Content-Length", Integer.toString(body.length)); 84 | 85 | OutputStream output = connection.getOutputStream(); 86 | try { 87 | output.write(body); 88 | } finally { 89 | output.close(); 90 | } 91 | 92 | // Open the stream to get a response. Otherwise request will be cancelled. 93 | InputStream input = connection.getInputStream(); 94 | input.close(); 95 | 96 | if (connection.getResponseCode() == 302) { 97 | return connection.getHeaderField("Location"); 98 | } 99 | } finally { 100 | if (connection != null) { 101 | connection.disconnect(); 102 | } 103 | } 104 | 105 | return null; 106 | } 107 | 108 | private byte[] getContentBody() throws IOException { 109 | ByteArrayOutputStream byteOutput = new ByteArrayOutputStream(); 110 | PrintWriter writer = new PrintWriter(new OutputStreamWriter(byteOutput)); 111 | writer.println(); 112 | 113 | for (Map.Entry formValue : mFormValues.entrySet()) { 114 | writeFormField(writer, formValue.getKey(), formValue.getValue()); 115 | } 116 | 117 | if (mData != null) { 118 | writeFormFileHeader(writer, mName, mFileName); 119 | writer.flush(); // Must flush here before writing to the byte stream! 120 | byteOutput.write(mData); 121 | writer.println(); 122 | } 123 | writer.append("--").append(FORM_DATA_BOUNDARY).println("--"); 124 | writer.flush(); 125 | writer.close(); 126 | return byteOutput.toByteArray(); 127 | } 128 | 129 | private void writeFormField(PrintWriter writer, String name, String value) { 130 | writer.append("--").println(FORM_DATA_BOUNDARY); 131 | writer.append("Content-Disposition: form-data; name=\"").append(name).println("\""); 132 | writer.println(); 133 | writer.println(value); 134 | } 135 | 136 | private void writeFormFileHeader(PrintWriter writer, String name, String fileName) { 137 | writer.append("--").println(FORM_DATA_BOUNDARY); 138 | writer.append("Content-Disposition: form-data; name=\"").append(name); 139 | writer.append("\"; filename=\"").append(fileName).println("\""); 140 | writer.println("Content-Type: application/x-gzip"); 141 | writer.println("Content-Transfer-Encoding: binary"); 142 | writer.println(); 143 | } 144 | } 145 | -------------------------------------------------------------------------------- /src/com/android/cts/tradefed/result/PlanCreator.java: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright (C) 2011 The Android Open Source Project 3 | * 4 | * Licensed under the Apache License, Version 2.0 (the "License"); 5 | * you may not use this file except in compliance with the License. 6 | * You may obtain a copy of the License at 7 | * 8 | * http://www.apache.org/licenses/LICENSE-2.0 9 | * 10 | * Unless required by applicable law or agreed to in writing, software 11 | * distributed under the License is distributed on an "AS IS" BASIS, 12 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 | * See the License for the specific language governing permissions and 14 | * limitations under the License. 15 | */ 16 | package com.android.cts.tradefed.result; 17 | 18 | import com.android.cts.tradefed.build.CtsBuildHelper; 19 | import com.android.cts.tradefed.testtype.CtsTest; 20 | import com.android.cts.tradefed.testtype.ITestPackageDef; 21 | import com.android.cts.tradefed.testtype.ITestPackageRepo; 22 | import com.android.cts.tradefed.testtype.ITestPlan; 23 | import com.android.cts.tradefed.testtype.TestPackageRepo; 24 | import com.android.cts.tradefed.testtype.TestPlan; 25 | import com.android.ddmlib.Log; 26 | import com.android.ddmlib.Log.LogLevel; 27 | import com.android.ddmlib.testrunner.TestIdentifier; 28 | import com.android.tradefed.config.ConfigurationException; 29 | import com.android.tradefed.config.Option; 30 | import com.android.tradefed.config.Option.Importance; 31 | import com.android.tradefed.log.LogUtil.CLog; 32 | 33 | import java.io.BufferedOutputStream; 34 | import java.io.File; 35 | import java.io.FileNotFoundException; 36 | import java.io.FileOutputStream; 37 | import java.io.IOException; 38 | import java.util.Collection; 39 | import java.util.LinkedHashSet; 40 | 41 | /** 42 | * Class for creating test plans from CTS result XML. 43 | */ 44 | public class PlanCreator { 45 | 46 | @Option (name = "plan", shortName = 'p', description = "the name of the plan to create", 47 | importance=Importance.IF_UNSET) 48 | private String mPlanName = null; 49 | 50 | @Option (name = "session", shortName = 's', description = "the session id to derive from", 51 | importance=Importance.IF_UNSET) 52 | private Integer mSessionId = null; 53 | 54 | @Option (name = "result", shortName = 'r', 55 | description = "the result type to filter. One of pass, fail, notExecuted.", 56 | importance=Importance.IF_UNSET) 57 | private String mResultFilterString = null; 58 | 59 | @Option(name = CtsTest.RUN_KNOWN_FAILURES_OPTION) 60 | private boolean mIncludeKnownFailures = false; 61 | 62 | private CtsTestStatus mResultFilter = null; 63 | private TestResults mResult = null; 64 | 65 | private File mPlanFile; 66 | 67 | /** 68 | * Create an empty {@link PlanCreator}. 69 | *

70 | * All {@link Option} fields must be populated via 71 | * {@link com.android.tradefed.config.ArgsOptionParser} 72 | */ 73 | public PlanCreator() { 74 | } 75 | 76 | /** 77 | * Create a {@link PlanCreator} using the specified option values. 78 | */ 79 | public PlanCreator(String planName, int session, CtsTestStatus result) { 80 | mPlanName = planName; 81 | mSessionId = session; 82 | mResultFilterString = result.getValue(); 83 | } 84 | 85 | /** 86 | * Create and serialize a test plan derived from a result. 87 | *

88 | * {@link Option} values must all be set before this is called. 89 | * @throws ConfigurationException 90 | */ 91 | public void createAndSerializeDerivedPlan(CtsBuildHelper build) throws ConfigurationException { 92 | ITestPlan derivedPlan = createDerivedPlan(build); 93 | if (derivedPlan != null) { 94 | try { 95 | derivedPlan.serialize(new BufferedOutputStream(new FileOutputStream(mPlanFile))); 96 | } catch (IOException e) { 97 | Log.logAndDisplay(LogLevel.ERROR, "", String.format("Failed to create plan file %s", 98 | mPlanName)); 99 | CLog.e(e); 100 | } 101 | } 102 | } 103 | 104 | /** 105 | * Create a test plan derived from a result. 106 | *

107 | * {@link Option} values must all be set before this is called. 108 | * 109 | * @param build 110 | * @return test plan 111 | * @throws ConfigurationException 112 | */ 113 | public ITestPlan createDerivedPlan(CtsBuildHelper build) throws ConfigurationException { 114 | checkFields(build); 115 | ITestPackageRepo pkgDefRepo = new TestPackageRepo(build.getTestCasesDir(), 116 | mIncludeKnownFailures); 117 | ITestPlan derivedPlan = new TestPlan(mPlanName); 118 | for (TestPackageResult pkg : mResult.getPackages()) { 119 | Collection filteredTests = pkg.getTestsWithStatus(mResultFilter); 120 | if (!filteredTests.isEmpty()) { 121 | String pkgUri = pkg.getAppPackageName(); 122 | ITestPackageDef pkgDef = pkgDefRepo.getTestPackage(pkgUri); 123 | if (pkgDef != null) { 124 | Collection excludedTests = new LinkedHashSet( 125 | pkgDef.getTests()); 126 | excludedTests.removeAll(filteredTests); 127 | derivedPlan.addPackage(pkgUri); 128 | derivedPlan.addExcludedTests(pkgUri, excludedTests); 129 | } else { 130 | CLog.e("Could not find package %s in repository", pkgUri); 131 | } 132 | } 133 | } 134 | return derivedPlan; 135 | } 136 | 137 | /** 138 | * Check that all {@Option}s have been populated with valid values. 139 | * @param build 140 | * @throws ConfigurationException if any option has an invalid value 141 | */ 142 | private void checkFields(CtsBuildHelper build) throws ConfigurationException { 143 | if (mSessionId == null) { 144 | throw new ConfigurationException("Missing --session argument"); 145 | } 146 | ITestResultRepo repo = new TestResultRepo(build.getResultsDir()); 147 | mResult = repo.getResult(mSessionId); 148 | if (mResult == null) { 149 | throw new ConfigurationException(String.format("Could not find session with id %d", 150 | mSessionId)); 151 | } 152 | if (mResultFilterString == null) { 153 | throw new ConfigurationException("Missing --result argument"); 154 | } 155 | mResultFilter = CtsTestStatus.getStatus(mResultFilterString); 156 | if (mResultFilter == null) { 157 | throw new ConfigurationException( 158 | "Invalid result argument. Expected one of pass,fail,notExecuted"); 159 | } 160 | if (mPlanName == null) { 161 | throw new ConfigurationException("Missing --plan argument"); 162 | } 163 | try { 164 | mPlanFile = build.getTestPlanFile(mPlanName); 165 | if (mPlanFile.exists()) { 166 | throw new ConfigurationException(String.format("Test plan %s already exists", 167 | mPlanName)); 168 | } 169 | } catch (FileNotFoundException e) { 170 | throw new ConfigurationException("Could not find plans directory"); 171 | } 172 | } 173 | } 174 | -------------------------------------------------------------------------------- /src/com/android/cts/tradefed/result/PtsHostStore.java: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright (C) 2013 The Android Open Source Project 3 | * 4 | * Licensed under the Apache License, Version 2.0 (the "License"); 5 | * you may not use this file except in compliance with the License. 6 | * You may obtain a copy of the License at 7 | * 8 | * http://www.apache.org/licenses/LICENSE-2.0 9 | * 10 | * Unless required by applicable law or agreed to in writing, software 11 | * distributed under the License is distributed on an "AS IS" BASIS, 12 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 | * See the License for the specific language governing permissions and 14 | * limitations under the License. 15 | */ 16 | package com.android.cts.tradefed.result; 17 | 18 | import com.android.ddmlib.testrunner.TestIdentifier; 19 | import com.android.tradefed.device.ITestDevice; 20 | 21 | import java.util.concurrent.ConcurrentHashMap; 22 | 23 | /** 24 | * Utility class for storing Pts Results. 25 | * This is necessary for host tests where test metrics cannot be passed. 26 | */ 27 | public class PtsHostStore { 28 | 29 | // needs concurrent verion as there can be multiple client accessing this. 30 | // But there is no additional protection for the same key as that should not happen. 31 | private static final ConcurrentHashMap mMap = 32 | new ConcurrentHashMap(); 33 | 34 | /** 35 | * Stores PTS result. Existing result with the same key will be replaced. 36 | * Note that key is generated in the form of device_serial#class#method name. 37 | * So there should be no concurrent test for the same (serial, class, method). 38 | * @param device 39 | * @param test 40 | * @param result PTS result string 41 | */ 42 | public static void storePtsResult(String deviceSerial, String classMethodName, String result) { 43 | mMap.put(generateTestKey(deviceSerial, classMethodName), result); 44 | } 45 | 46 | /** 47 | * retrieves a PTS result for the given condition and remove it from the internal 48 | * storage. If there is no result for the given condition, it will return null. 49 | */ 50 | public static String removePtsResult(String deviceSerial, TestIdentifier test) { 51 | return mMap.remove(generateTestKey(deviceSerial, test)); 52 | } 53 | 54 | /** 55 | * return test key in the form of device_serial#class_name#method_name 56 | */ 57 | private static String generateTestKey(String deviceSerial, TestIdentifier test) { 58 | return String.format("%s#%s", deviceSerial, test.toString()); 59 | 60 | } 61 | 62 | /** 63 | * return test key in the form of device_serial#class_name#method_name 64 | */ 65 | private static String generateTestKey(String deviceSerial, String classMethodName) { 66 | return String.format("%s#%s", deviceSerial, classMethodName); 67 | 68 | } 69 | } 70 | -------------------------------------------------------------------------------- /src/com/android/cts/tradefed/result/PtsReportUtil.java: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright (C) 2013 The Android Open Source Project 3 | * 4 | * Licensed under the Apache License, Version 2.0 (the "License"); 5 | * you may not use this file except in compliance with the License. 6 | * You may obtain a copy of the License at 7 | * 8 | * http://www.apache.org/licenses/LICENSE-2.0 9 | * 10 | * Unless required by applicable law or agreed to in writing, software 11 | * distributed under the License is distributed on an "AS IS" BASIS, 12 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 | * See the License for the specific language governing permissions and 14 | * limitations under the License. 15 | */ 16 | package com.android.cts.tradefed.result; 17 | 18 | import com.android.ddmlib.testrunner.TestIdentifier; 19 | import com.android.tradefed.device.ITestDevice; 20 | 21 | import java.util.Map; 22 | import java.util.concurrent.ConcurrentHashMap; 23 | 24 | /** 25 | * Static utility class for handling Pts Results. 26 | */ 27 | public class PtsReportUtil { 28 | private static final String PTS_RESULT_KEY = "PTS_RESULT"; 29 | 30 | /** 31 | * Utility method to extract PTS result from test metrics 32 | * @param testMetrics 33 | * @return result or null if not found 34 | */ 35 | public static String getPtsResultFromMetrics(Map testMetrics) { 36 | for (Map.Entry entry: testMetrics.entrySet()) { 37 | if (PTS_RESULT_KEY.equals(entry.getKey())) { 38 | return entry.getValue(); 39 | } 40 | } 41 | return null; 42 | } 43 | } 44 | -------------------------------------------------------------------------------- /src/com/android/cts/tradefed/result/ResultReporter.java: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright (C) 2011 The Android Open Source Project 3 | * 4 | * Licensed under the Apache License, Version 2.0 (the "License"); 5 | * you may not use this file except in compliance with the License. 6 | * You may obtain a copy of the License at 7 | * 8 | * http://www.apache.org/licenses/LICENSE-2.0 9 | * 10 | * Unless required by applicable law or agreed to in writing, software 11 | * distributed under the License is distributed on an "AS IS" BASIS, 12 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 | * See the License for the specific language governing permissions and 14 | * limitations under the License. 15 | */ 16 | 17 | package com.android.cts.tradefed.result; 18 | 19 | import java.io.File; 20 | import java.io.FileInputStream; 21 | import java.io.IOException; 22 | import java.io.InputStream; 23 | 24 | /** 25 | * Class that sends a HTTP POST multipart/form-data request containing 26 | * the test result XML. 27 | */ 28 | class ResultReporter { 29 | 30 | private static final int RESULT_XML_BYTES = 500 * 1024; 31 | 32 | private final String mServerUrl; 33 | private final String mSuiteName; 34 | 35 | ResultReporter(String serverUrl, String suiteName) { 36 | mServerUrl = serverUrl; 37 | mSuiteName = suiteName; 38 | } 39 | 40 | public void reportResult(File reportFile) throws IOException { 41 | if (isEmpty(mServerUrl)) { 42 | return; 43 | } 44 | 45 | InputStream input = new FileInputStream(reportFile); 46 | try { 47 | byte[] data = IssueReporter.getBytes(input, RESULT_XML_BYTES); 48 | new MultipartForm(mServerUrl) 49 | .addFormValue("suite", mSuiteName) 50 | .addFormFile("resultXml", "testResult.xml.gz", data) 51 | .submit(); 52 | } finally { 53 | input.close(); 54 | } 55 | } 56 | 57 | private boolean isEmpty(String value) { 58 | return value == null || value.trim().isEmpty(); 59 | } 60 | } 61 | -------------------------------------------------------------------------------- /src/com/android/cts/tradefed/result/TestCase.java: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright (C) 2011 The Android Open Source Project 3 | * 4 | * Licensed under the Apache License, Version 2.0 (the "License"); 5 | * you may not use this file except in compliance with the License. 6 | * You may obtain a copy of the License at 7 | * 8 | * http://www.apache.org/licenses/LICENSE-2.0 9 | * 10 | * Unless required by applicable law or agreed to in writing, software 11 | * distributed under the License is distributed on an "AS IS" BASIS, 12 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 | * See the License for the specific language governing permissions and 14 | * limitations under the License. 15 | */ 16 | package com.android.cts.tradefed.result; 17 | 18 | import com.android.ddmlib.testrunner.TestIdentifier; 19 | import com.android.tradefed.util.ArrayUtil; 20 | 21 | import org.kxml2.io.KXmlSerializer; 22 | import org.xmlpull.v1.XmlPullParser; 23 | import org.xmlpull.v1.XmlPullParserException; 24 | 25 | import java.io.IOException; 26 | import java.util.Collection; 27 | import java.util.Deque; 28 | import java.util.LinkedHashMap; 29 | import java.util.Map; 30 | 31 | /** 32 | * Data structure that represents a "TestCase" XML element and its children. 33 | */ 34 | class TestCase extends AbstractXmlPullParser { 35 | 36 | static final String TAG = "TestCase"; 37 | 38 | private String mName; 39 | 40 | Map mChildTestMap = new LinkedHashMap(); 41 | 42 | /** 43 | * Create a {@link TestCase} 44 | * 45 | * @param testCaseName 46 | */ 47 | public TestCase(String testCaseName) { 48 | setName(testCaseName); 49 | } 50 | 51 | public TestCase() { 52 | } 53 | 54 | public void setName(String name) { 55 | mName = name; 56 | } 57 | 58 | public String getName() { 59 | return mName; 60 | } 61 | 62 | /** 63 | * Gets the child tests 64 | */ 65 | public Collection getTests() { 66 | return mChildTestMap.values(); 67 | } 68 | 69 | /** 70 | * @param testName 71 | * @param insertIfMissing 72 | * @return 73 | */ 74 | public Test findTest(String testName, boolean insertIfMissing) { 75 | Test t = mChildTestMap.get(testName); 76 | if (t == null && insertIfMissing) { 77 | t = new Test(testName); 78 | mChildTestMap.put(t.getName(), t); 79 | } 80 | return t; 81 | } 82 | 83 | /** 84 | * Serialize this object and all its contents to XML. 85 | * 86 | * @param serializer 87 | * @throws IOException 88 | */ 89 | public void serialize(KXmlSerializer serializer) throws IOException { 90 | serializer.startTag(CtsXmlResultReporter.ns, TAG); 91 | serializer.attribute(CtsXmlResultReporter.ns, "name", getName()); 92 | // unused 93 | serializer.attribute(CtsXmlResultReporter.ns, "priority", ""); 94 | for (Test t : mChildTestMap.values()) { 95 | t.serialize(serializer); 96 | } 97 | serializer.endTag(CtsXmlResultReporter.ns, TAG); 98 | } 99 | 100 | /** 101 | * Populates this class with test case result data parsed from XML. 102 | * 103 | * @param parser 104 | * the {@link XmlPullParser}. Expected to be pointing at start of 105 | * a TestCase tag 106 | */ 107 | @Override 108 | public void parse(XmlPullParser parser) throws XmlPullParserException, 109 | IOException { 110 | if (!parser.getName().equals(TAG)) { 111 | throw new XmlPullParserException(String.format( 112 | "invalid XML: Expected %s tag but received %s", TAG, 113 | parser.getName())); 114 | } 115 | setName(getAttribute(parser, "name")); 116 | int eventType = parser.next(); 117 | while (eventType != XmlPullParser.END_DOCUMENT) { 118 | if (eventType == XmlPullParser.START_TAG 119 | && parser.getName().equals(Test.TAG)) { 120 | Test test = new Test(); 121 | test.parse(parser); 122 | mChildTestMap.put(test.getName(), test); 123 | } else if (eventType == XmlPullParser.END_TAG 124 | && parser.getName().equals(TAG)) { 125 | return; 126 | } 127 | eventType = parser.next(); 128 | } 129 | } 130 | 131 | /** 132 | * Adds tests contained in this result that have the given 133 | * resultFilter. 134 | * 135 | * @param tests 136 | * the {@link Collection} of {@link TestIdentifier}s to add to 137 | * @param parentSuiteNames 138 | * a {@link Deque} of parent suite names. Used to construct the 139 | * full class name of the test 140 | * @param resultFilter 141 | * the {@link CtsTestStatus} to filter by 142 | */ 143 | void addTestsWithStatus(Collection tests, 144 | Deque parentSuiteNames, CtsTestStatus resultFilter) { 145 | if (getName() != null) { 146 | parentSuiteNames.addLast(getName()); 147 | } 148 | String fullClassName = ArrayUtil.join(".", parentSuiteNames); 149 | for (Test test : mChildTestMap.values()) { 150 | if (resultFilter.equals(test.getResult())) { 151 | tests.add(new TestIdentifier(fullClassName, test.getName())); 152 | } 153 | } 154 | if (getName() != null) { 155 | parentSuiteNames.removeLast(); 156 | } 157 | } 158 | 159 | /** 160 | * Count the number of tests in this {@link TestCase} with given status. 161 | * 162 | * @param status 163 | * @return the test count 164 | */ 165 | public int countTests(CtsTestStatus status) { 166 | int total = 0; 167 | for (Test test : mChildTestMap.values()) { 168 | if (test.getResult().equals(status)) { 169 | total++; 170 | } 171 | } 172 | return total; 173 | } 174 | } 175 | -------------------------------------------------------------------------------- /src/com/android/cts/tradefed/result/TestResultRepo.java: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright (C) 2011 The Android Open Source Project 3 | * 4 | * Licensed under the Apache License, Version 2.0 (the "License"); 5 | * you may not use this file except in compliance with the License. 6 | * You may obtain a copy of the License at 7 | * 8 | * http://www.apache.org/licenses/LICENSE-2.0 9 | * 10 | * Unless required by applicable law or agreed to in writing, software 11 | * distributed under the License is distributed on an "AS IS" BASIS, 12 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 | * See the License for the specific language governing permissions and 14 | * limitations under the License. 15 | */ 16 | package com.android.cts.tradefed.result; 17 | 18 | import com.android.tradefed.log.LogUtil.CLog; 19 | import com.android.tradefed.util.xml.AbstractXmlParser.ParseException; 20 | 21 | import java.io.BufferedReader; 22 | import java.io.File; 23 | import java.io.FileFilter; 24 | import java.io.FileNotFoundException; 25 | import java.io.FileReader; 26 | import java.util.ArrayList; 27 | import java.util.Collections; 28 | import java.util.Comparator; 29 | import java.util.List; 30 | 31 | /** 32 | * An implementation of {@link ITestResultsRepo}. 33 | */ 34 | public class TestResultRepo implements ITestResultRepo { 35 | 36 | /** 37 | * ordered list of result directories. the index of each file is its session id. 38 | */ 39 | private List mResultDirs; 40 | 41 | /** 42 | * Create a {@link TestResultRepo} from a directory of results 43 | * 44 | * @param testResultsDir the parent directory of results 45 | */ 46 | public TestResultRepo(File testResultsDir) { 47 | mResultDirs = new ArrayList(); 48 | File[] resultArray = testResultsDir.listFiles(new ResultDirFilter()); 49 | if (resultArray != null) { 50 | List resultList = new ArrayList(); 51 | Collections.addAll(resultList, resultArray); 52 | Collections.sort(resultList, new FileComparator()); 53 | for (int i=0; i < resultList.size(); i++) { 54 | File resultFile = new File(resultList.get(i), 55 | CtsXmlResultReporter.TEST_RESULT_FILE_NAME); 56 | if (resultFile.exists()) { 57 | mResultDirs.add(resultList.get(i)); 58 | } 59 | } 60 | } 61 | } 62 | 63 | @Override 64 | public File getReportDir(int sessionId) { 65 | return mResultDirs.get(sessionId); 66 | } 67 | 68 | private ITestSummary parseSummary(int id, File resultDir) { 69 | TestSummaryXml result = new TestSummaryXml(id, resultDir.getName()); 70 | try { 71 | result.parse(new BufferedReader(new FileReader(new File(resultDir, 72 | CtsXmlResultReporter.TEST_RESULT_FILE_NAME)))); 73 | return result; 74 | } catch (ParseException e) { 75 | CLog.e(e); 76 | } catch (FileNotFoundException e) { 77 | // should never happen, since we check for file existence above. Barf the stack trace 78 | CLog.e(e); 79 | } 80 | return result; 81 | } 82 | 83 | /** 84 | * {@inheritDoc} 85 | */ 86 | @Override 87 | public List getSummaries() { 88 | // parsing the summary data should be relatively quick, so just parse it every time 89 | // rather than caching it 90 | List summaries = new ArrayList(mResultDirs.size()); 91 | for (int i = 0; i < mResultDirs.size(); i++) { 92 | summaries.add(parseSummary(i, mResultDirs.get(i))); 93 | } 94 | return summaries; 95 | } 96 | 97 | /** 98 | * {@inheritDoc} 99 | */ 100 | @Override 101 | public TestResults getResult(int sessionId) { 102 | // TODO: consider caching the results in future 103 | if (mResultDirs.size() <= sessionId) { 104 | CLog.e("Session id %d does not exist", sessionId); 105 | return null; 106 | } 107 | try { 108 | TestResults results = new TestResults(); 109 | File resultFile = new File(mResultDirs.get(sessionId), 110 | CtsXmlResultReporter.TEST_RESULT_FILE_NAME); 111 | results.parse(new BufferedReader(new FileReader(resultFile))); 112 | return results; 113 | } catch (FileNotFoundException e) { 114 | CLog.e("Could not find result file for session %d", sessionId); 115 | } catch (ParseException e) { 116 | CLog.e("Failed to parse result file for session %d", sessionId); 117 | } 118 | return null; 119 | } 120 | 121 | private class ResultDirFilter implements FileFilter { 122 | 123 | /** 124 | * {@inheritDoc} 125 | */ 126 | @Override 127 | public boolean accept(File file) { 128 | return file.isDirectory(); 129 | } 130 | } 131 | 132 | /** 133 | * A {@link Comparator} that compares {@link File}s by name. 134 | */ 135 | private class FileComparator implements Comparator { 136 | 137 | /** 138 | * {@inheritDoc} 139 | */ 140 | @Override 141 | public int compare(File file0, File file1) { 142 | return file0.getName().compareTo(file1.getName()); 143 | } 144 | } 145 | } 146 | -------------------------------------------------------------------------------- /src/com/android/cts/tradefed/result/TestSummaryXml.java: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright (C) 2011 The Android Open Source Project 3 | * 4 | * Licensed under the Apache License, Version 2.0 (the "License"); 5 | * you may not use this file except in compliance with the License. 6 | * You may obtain a copy of the License at 7 | * 8 | * http://www.apache.org/licenses/LICENSE-2.0 9 | * 10 | * Unless required by applicable law or agreed to in writing, software 11 | * distributed under the License is distributed on an "AS IS" BASIS, 12 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 | * See the License for the specific language governing permissions and 14 | * limitations under the License. 15 | */ 16 | package com.android.cts.tradefed.result; 17 | 18 | import com.android.tradefed.util.xml.AbstractXmlParser.ParseException; 19 | 20 | import org.xmlpull.v1.XmlPullParser; 21 | import org.xmlpull.v1.XmlPullParserException; 22 | 23 | import android.tests.getinfo.DeviceInfoConstants; 24 | 25 | import java.io.FileNotFoundException; 26 | import java.io.IOException; 27 | 28 | /** 29 | * A {@link ITestSummary} that parses summary data from the CTS result XML. 30 | */ 31 | public class TestSummaryXml extends AbstractXmlPullParser implements ITestSummary { 32 | 33 | private final int mId; 34 | private final String mTimestamp; 35 | private int mNumFailed = 0; 36 | private int mNumNotExecuted = 0; 37 | private int mNumPassed = 0; 38 | private String mPlan = "NA"; 39 | private String mStartTime = "unknown"; 40 | private String mDeviceSerials = "unknown"; 41 | 42 | /** 43 | * @param id 44 | * @param resultFile 45 | * @throws ParseException 46 | * @throws FileNotFoundException 47 | */ 48 | public TestSummaryXml(int id, String timestamp) { 49 | mId = id; 50 | mTimestamp = timestamp; 51 | } 52 | 53 | /** 54 | * {@inheritDoc} 55 | */ 56 | @Override 57 | public int getId() { 58 | return mId; 59 | } 60 | 61 | /** 62 | * {@inheritDoc} 63 | */ 64 | @Override 65 | public String getTimestamp() { 66 | return mTimestamp; 67 | } 68 | 69 | /** 70 | * {@inheritDoc} 71 | */ 72 | @Override 73 | public int getNumIncomplete() { 74 | return mNumNotExecuted; 75 | } 76 | 77 | /** 78 | * {@inheritDoc} 79 | */ 80 | @Override 81 | public int getNumFailed() { 82 | return mNumFailed; 83 | } 84 | 85 | /** 86 | * {@inheritDoc} 87 | */ 88 | @Override 89 | public int getNumPassed() { 90 | return mNumPassed; 91 | } 92 | 93 | /** 94 | * {@inheritDoc} 95 | */ 96 | @Override 97 | public String getTestPlan() { 98 | return mPlan ; 99 | } 100 | 101 | 102 | @Override 103 | public 104 | void parse(XmlPullParser parser) throws XmlPullParserException, IOException { 105 | int eventType = parser.getEventType(); 106 | while (eventType != XmlPullParser.END_DOCUMENT) { 107 | if (eventType == XmlPullParser.START_TAG && parser.getName().equals( 108 | CtsXmlResultReporter.RESULT_TAG)) { 109 | mPlan = getAttribute(parser, CtsXmlResultReporter.PLAN_ATTR); 110 | mStartTime = getAttribute(parser, CtsXmlResultReporter.STARTTIME_ATTR); 111 | } else if (eventType == XmlPullParser.START_TAG && parser.getName().equals( 112 | DeviceInfoResult.BUILD_TAG)) { 113 | mDeviceSerials = getAttribute(parser, DeviceInfoConstants.SERIAL_NUMBER); 114 | } else if (eventType == XmlPullParser.START_TAG && parser.getName().equals( 115 | TestResults.SUMMARY_TAG)) { 116 | mNumFailed = parseIntAttr(parser, TestResults.FAILED_ATTR) + 117 | parseIntAttr(parser, TestResults.TIMEOUT_ATTR); 118 | mNumNotExecuted = parseIntAttr(parser, TestResults.NOT_EXECUTED_ATTR); 119 | mNumPassed = parseIntAttr(parser, TestResults.PASS_ATTR); 120 | // abort after parsing Summary, which should be the last tag 121 | return; 122 | } 123 | eventType = parser.next(); 124 | } 125 | throw new XmlPullParserException("Could not find Summary tag"); 126 | } 127 | 128 | /** 129 | * {@inheritDoc} 130 | */ 131 | @Override 132 | public String getStartTime() { 133 | return mStartTime; 134 | } 135 | 136 | /** 137 | * {@inheritDoc} 138 | */ 139 | @Override 140 | public String getDeviceSerials() { 141 | return mDeviceSerials; 142 | } 143 | } 144 | -------------------------------------------------------------------------------- /src/com/android/cts/tradefed/result/TimeUtil.java: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright (C) 2011 The Android Open Source Project 3 | * 4 | * Licensed under the Apache License, Version 2.0 (the "License"); 5 | * you may not use this file except in compliance with the License. 6 | * You may obtain a copy of the License at 7 | * 8 | * http://www.apache.org/licenses/LICENSE-2.0 9 | * 10 | * Unless required by applicable law or agreed to in writing, software 11 | * distributed under the License is distributed on an "AS IS" BASIS, 12 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 | * See the License for the specific language governing permissions and 14 | * limitations under the License. 15 | */ 16 | package com.android.cts.tradefed.result; 17 | 18 | import java.text.SimpleDateFormat; 19 | import java.util.Date; 20 | import java.util.concurrent.TimeUnit; 21 | 22 | /** 23 | * Utility class for formatting times as strings. 24 | */ 25 | class TimeUtil { 26 | 27 | /** 28 | * Return a prettified version of the given elapsed time 29 | * @return 30 | */ 31 | static String formatElapsedTime(long elapsedTimeMs) { 32 | long seconds = TimeUnit.MILLISECONDS.toSeconds(elapsedTimeMs) % 60; 33 | long minutes = TimeUnit.MILLISECONDS.toMinutes(elapsedTimeMs) % 60; 34 | long hours = TimeUnit.MILLISECONDS.toHours(elapsedTimeMs); 35 | StringBuilder time = new StringBuilder(); 36 | if (hours > 0) { 37 | time.append(hours); 38 | time.append("h "); 39 | } 40 | if (minutes > 0) { 41 | time.append(minutes); 42 | time.append("m "); 43 | } 44 | time.append(seconds); 45 | time.append("s"); 46 | 47 | return time.toString(); 48 | } 49 | 50 | /** 51 | * Return the current timestamp as a {@link String} suitable for displaying. 52 | *

53 | * Example: Fri Aug 20 15:13:03 PDT 2010 54 | */ 55 | static String getTimestamp() { 56 | return getTimestamp(System.currentTimeMillis()); 57 | } 58 | 59 | /** 60 | * Return the given time as a {@link String} suitable for displaying. 61 | *

62 | * Example: Fri Aug 20 15:13:03 PDT 2010 63 | * 64 | * @param time the epoch time in ms since midnight Jan 1, 1970 65 | */ 66 | static String getTimestamp(long time) { 67 | SimpleDateFormat dateFormat = new SimpleDateFormat("EEE MMM dd HH:mm:ss zzz yyyy"); 68 | return dateFormat.format(new Date(time)); 69 | } 70 | 71 | /** 72 | * Return the current timestamp in a compressed format, used to uniquely identify results. 73 | *

74 | * Example: 2010.08.16_11.42.12 75 | */ 76 | static String getResultTimestamp() { 77 | SimpleDateFormat dateFormat = new SimpleDateFormat("yyyy.MM.dd_HH.mm.ss"); 78 | return dateFormat.format(new Date()); 79 | } 80 | private final static SimpleDateFormat TIME_FORMAT = new SimpleDateFormat("yyyy-MM-dd HH:mm:ss"); 81 | private final static SimpleDateFormat TIME_FORMAT_MSEC = new SimpleDateFormat("yyyy-MM-dd HH:mm:ss SSS"); 82 | private final static SimpleDateFormat FILE_TIME_FORMAT = new SimpleDateFormat("yyyy-MM-dd_HH-mm-ss"); 83 | 84 | // only static methods, don't allow construction 85 | private TimeUtil() { 86 | } 87 | 88 | 89 | 90 | /** 91 | * Return a readable formatted version of the given epoch time. 92 | * 93 | * @param epochTime the epoch time in milliseconds 94 | * @return a user readable string 95 | */ 96 | public static String formatTimeStamp(long epochTime) { 97 | return TIME_FORMAT.format(new Date(epochTime)); 98 | } 99 | 100 | 101 | public static String formatTimeStampMsec(long epochTime) { 102 | return TIME_FORMAT_MSEC.format(new Date(epochTime)); 103 | } 104 | 105 | public static String getTimestampMsec() { 106 | return formatTimeStampMsec(System.currentTimeMillis()); 107 | } 108 | 109 | public static String formatTimeForFile(long epochTime) { 110 | return FILE_TIME_FORMAT.format(new Date(epochTime)); 111 | } 112 | 113 | public static String getTimestampForFile() { 114 | return formatTimeForFile(System.currentTimeMillis()); 115 | } 116 | 117 | 118 | } 119 | -------------------------------------------------------------------------------- /src/com/android/cts/tradefed/result/monkey/DragTag.java: -------------------------------------------------------------------------------- 1 | package com.android.cts.tradefed.result.monkey; 2 | 3 | import java.awt.Point; 4 | import java.io.IOException; 5 | import java.util.LinkedList; 6 | import java.util.List; 7 | 8 | import org.kxml2.io.KXmlSerializer; 9 | import org.xmlpull.v1.XmlPullParser; 10 | import org.xmlpull.v1.XmlPullParserException; 11 | 12 | import com.android.cts.tradefed.result.CtsXmlResultReporter; 13 | 14 | public class DragTag extends EventTag { 15 | 16 | public static final String DIRECTION_UP = "up"; 17 | public static final String DIRECTION_DOWN = "down"; 18 | public static final String DIRECTION_MOVE = "move"; 19 | 20 | public List getTouches() { 21 | return touches; 22 | } 23 | 24 | public DragTag() { 25 | setType(EVENT_TYPE_DRAG); 26 | } 27 | 28 | private List touches = new LinkedList(); 29 | 30 | @Override 31 | public void serialize(KXmlSerializer serializer, int index,int count) 32 | throws IOException { 33 | setIndex(index); 34 | setPos(count - 1 - index); 35 | // TODO Auto-generated method stub 36 | serializer.startTag(CtsXmlResultReporter.ns, EVENT_TAG); 37 | serializer.attribute(CtsXmlResultReporter.ns, INDEX_ATTR, getIndex() 38 | + ""); 39 | serializer.attribute(CtsXmlResultReporter.ns, TYPE_ATTR, getType()); 40 | serializer.attribute(CtsXmlResultReporter.ns, TIME_ATTR, getTime()); 41 | serializer.attribute(CtsXmlResultReporter.ns, IMAGE_ATTR, getImage()); 42 | serializer.attribute(CtsXmlResultReporter.ns, LOG_ATTR, getLog()); 43 | serializer.attribute(CtsXmlResultReporter.ns, POS_ATTR, getPos() + ""); 44 | for (TouchTag touchTag : touches) { 45 | touchTag.serialize(serializer); 46 | } 47 | serializer.endTag(CtsXmlResultReporter.ns, EVENT_TAG); 48 | } 49 | 50 | public void addTouchTag(TouchTag touchTag) { 51 | touches.add(touchTag); 52 | } 53 | 54 | public void addTouchUp(Point point) { 55 | TouchTag tag = new TouchTag(); 56 | tag.setDirection(DIRECTION_UP); 57 | setXY(point, tag); 58 | } 59 | 60 | public void addTouchMove(Point point) { 61 | TouchTag tag = new TouchTag(); 62 | tag.setDirection(DIRECTION_MOVE); 63 | setXY(point, tag); 64 | } 65 | 66 | public void addTouchDown(Point point) { 67 | TouchTag tag = new TouchTag(); 68 | tag.setDirection(DIRECTION_DOWN); 69 | setXY(point, tag); 70 | } 71 | 72 | private void setXY(Point point, TouchTag tag) { 73 | tag.setX(point.x); 74 | tag.setY(point.y); 75 | touches.add(tag); 76 | } 77 | 78 | @Override 79 | public void parse(XmlPullParser parser) throws XmlPullParserException, 80 | IOException { 81 | if (!parser.getName().equals(EVENT_TAG)) { 82 | throw new XmlPullParserException(String.format( 83 | "invalid XML: Expected %s tag but received %s", EVENT_TAG, 84 | parser.getName())); 85 | } 86 | setIndex(Integer.parseInt(getAttribute(parser, INDEX_ATTR))); 87 | setImage(getAttribute(parser, IMAGE_ATTR)); 88 | setLog(getAttribute(parser, LOG_ATTR)); 89 | int eventType = parser.getEventType(); 90 | while (eventType != XmlPullParser.END_DOCUMENT) { 91 | if (eventType == XmlPullParser.START_TAG 92 | && parser.getName().equals(TouchTag.TOUCH_TAG)) { 93 | TouchTag touchTag = new TouchTag(); 94 | touchTag.parse(parser); 95 | touches.add(touchTag); 96 | } else if (eventType == XmlPullParser.END_TAG 97 | && parser.getName().equals(EVENT_TAG)) { 98 | return; 99 | } 100 | 101 | eventType = parser.next(); 102 | } 103 | } 104 | 105 | 106 | } 107 | -------------------------------------------------------------------------------- /src/com/android/cts/tradefed/result/monkey/EventTag.java: -------------------------------------------------------------------------------- 1 | package com.android.cts.tradefed.result.monkey; 2 | 3 | import java.io.IOException; 4 | import java.util.LinkedList; 5 | import java.util.List; 6 | 7 | import org.kxml2.io.KXmlSerializer; 8 | 9 | import com.android.cts.tradefed.result.AbstractXmlPullParser; 10 | 11 | public abstract class EventTag extends AbstractXmlPullParser { 12 | public String getTime() { 13 | return time; 14 | } 15 | 16 | public void setTime(String time) { 17 | this.time = time; 18 | } 19 | 20 | public int getPos() { 21 | return pos; 22 | } 23 | 24 | public void setPos(int pos) { 25 | this.pos = pos; 26 | } 27 | 28 | protected static final String EVENT_TAG = "Event"; 29 | protected static final String INDEX_ATTR = "index"; 30 | protected static final String TYPE_ATTR = "type"; 31 | protected static final String TIME_ATTR = "time"; 32 | protected static final String IMAGE_ATTR = "image"; 33 | protected static final String LOG_ATTR = "log"; 34 | protected static final String POS_ATTR = "pos"; 35 | 36 | public static final String EVENT_TYPE_DRAG = "drag"; 37 | public static final String EVENT_TYPE_TAP = "tap"; 38 | public static final String EVENT_TYPE_KEY = "key"; 39 | private int index = 0; 40 | private String type = "NA"; 41 | private String time = "NA"; 42 | private String image = "ss"; 43 | private String log = "NA"; 44 | private int pos = 0; 45 | 46 | 47 | public int getIndex() { 48 | return index; 49 | } 50 | 51 | /* 52 | * (non-Javadoc) 53 | * 54 | * @see com.android.cts.tradefed.result.monkey.Event#setIndex(int) 55 | */ 56 | public void setIndex(int index) { 57 | this.index = index; 58 | } 59 | 60 | /* 61 | * (non-Javadoc) 62 | * 63 | * @see com.android.cts.tradefed.result.monkey.Event#getType() 64 | */ 65 | public String getType() { 66 | return type; 67 | } 68 | 69 | /* 70 | * (non-Javadoc) 71 | * 72 | * @see 73 | * com.android.cts.tradefed.result.monkey.Event#setType(java.lang.String) 74 | */ 75 | 76 | public void setType(String type) { 77 | this.type = type; 78 | } 79 | 80 | /* 81 | * (non-Javadoc) 82 | * 83 | * @see com.android.cts.tradefed.result.monkey.Event#getImage() 84 | */ 85 | 86 | public String getImage() { 87 | return image; 88 | } 89 | 90 | /* 91 | * (non-Javadoc) 92 | * 93 | * @see 94 | * com.android.cts.tradefed.result.monkey.Event#setImage(java.lang.String) 95 | */ 96 | 97 | public void setImage(String image) { 98 | this.image = image; 99 | } 100 | 101 | /* 102 | * (non-Javadoc) 103 | * 104 | * @see com.android.cts.tradefed.result.monkey.Event#getLog() 105 | */ 106 | 107 | public String getLog() { 108 | return log; 109 | } 110 | 111 | /* 112 | * (non-Javadoc) 113 | * 114 | * @see 115 | * com.android.cts.tradefed.result.monkey.Event#setLog(java.lang.String) 116 | */ 117 | 118 | public void setLog(String log) { 119 | this.log = log; 120 | } 121 | 122 | /* 123 | * (non-Javadoc) 124 | * 125 | * @see com.android.cts.tradefed.result.monkey.Event#serialize(org.kxml2.io. 126 | * KXmlSerializer, int) 127 | */ 128 | 129 | public abstract void serialize(KXmlSerializer serializer, int index,int count) 130 | throws IOException; 131 | 132 | } 133 | -------------------------------------------------------------------------------- /src/com/android/cts/tradefed/result/monkey/KeyTag.java: -------------------------------------------------------------------------------- 1 | package com.android.cts.tradefed.result.monkey; 2 | 3 | import java.io.IOException; 4 | 5 | import org.kxml2.io.KXmlSerializer; 6 | import org.xmlpull.v1.XmlPullParser; 7 | import org.xmlpull.v1.XmlPullParserException; 8 | 9 | import sun.awt.SunHints.Value; 10 | 11 | import com.android.cts.tradefed.result.CtsXmlResultReporter; 12 | 13 | public class KeyTag extends EventTag { 14 | 15 | private static final String VALUE_ATTR = "value"; 16 | 17 | private String value = "NA"; 18 | 19 | public KeyTag() { 20 | setType(EVENT_TYPE_KEY); 21 | } 22 | 23 | public String getValue() { 24 | return value; 25 | } 26 | 27 | public void setValue(String value) { 28 | this.value = value; 29 | } 30 | 31 | @Override 32 | public void serialize(KXmlSerializer serializer, int index, int count) 33 | throws IOException { 34 | setIndex(index); 35 | setPos(count - 1 - index); 36 | serializer.startTag(CtsXmlResultReporter.ns, EVENT_TAG); 37 | serializer.attribute(CtsXmlResultReporter.ns, INDEX_ATTR, getIndex() 38 | + ""); 39 | serializer.attribute(CtsXmlResultReporter.ns, TYPE_ATTR, getType()); 40 | 41 | serializer.attribute(CtsXmlResultReporter.ns, VALUE_ATTR, getValue()); 42 | serializer.attribute(CtsXmlResultReporter.ns, TIME_ATTR, getTime()); 43 | serializer.attribute(CtsXmlResultReporter.ns, IMAGE_ATTR, getImage()); 44 | serializer.attribute(CtsXmlResultReporter.ns, LOG_ATTR, getLog()); 45 | serializer.attribute(CtsXmlResultReporter.ns, POS_ATTR, getPos() + ""); 46 | serializer.endTag(CtsXmlResultReporter.ns, EVENT_TAG); 47 | 48 | } 49 | 50 | @Override 51 | public void parse(XmlPullParser parser) throws XmlPullParserException, 52 | IOException { 53 | if (!parser.getName().equals(EVENT_TAG)) { 54 | throw new XmlPullParserException(String.format( 55 | "invalid XML: Expected %s tag but received %s", EVENT_TAG, 56 | parser.getName())); 57 | } 58 | setValue(getAttribute(parser, VALUE_ATTR)); 59 | setIndex(Integer.parseInt(getAttribute(parser, INDEX_ATTR))); 60 | setImage(getAttribute(parser, IMAGE_ATTR)); 61 | setLog(getAttribute(parser, LOG_ATTR)); 62 | } 63 | 64 | } 65 | -------------------------------------------------------------------------------- /src/com/android/cts/tradefed/result/monkey/MonkeyTestTag.java: -------------------------------------------------------------------------------- 1 | package com.android.cts.tradefed.result.monkey; 2 | 3 | import java.io.IOException; 4 | import java.util.LinkedList; 5 | import java.util.List; 6 | 7 | import org.kxml2.io.KXmlSerializer; 8 | import org.xmlpull.v1.XmlPullParser; 9 | import org.xmlpull.v1.XmlPullParserException; 10 | 11 | import com.android.cts.tradefed.result.AbstractXmlPullParser; 12 | import com.android.cts.tradefed.result.CtsXmlResultReporter; 13 | 14 | public class MonkeyTestTag extends AbstractXmlPullParser { 15 | 16 | public String getFinalLog() { 17 | return finalLog; 18 | } 19 | 20 | public void setFinalLog(String finalLog) { 21 | this.finalLog = finalLog; 22 | } 23 | 24 | public String getFinalPng() { 25 | return finalPng; 26 | } 27 | 28 | public void setFinalPng(String finalPng) { 29 | this.finalPng = finalPng; 30 | } 31 | 32 | public int getCount() { 33 | return count; 34 | } 35 | 36 | public void setCount(int count) { 37 | this.count = count; 38 | } 39 | 40 | public String getApplication() { 41 | return application; 42 | } 43 | 44 | public void setApplication(String application) { 45 | this.application = application; 46 | } 47 | 48 | public String getResult() { 49 | return result; 50 | } 51 | 52 | public void setResult(String result) { 53 | this.result = result; 54 | } 55 | 56 | public List getEvents() { 57 | return events; 58 | } 59 | 60 | public void setEvents(List events) { 61 | this.events = events; 62 | } 63 | 64 | private static final String MONKEY_TEST_TAG = "MonkeyTest"; 65 | private static final String APPLICATION_ATTR = "application"; 66 | private static final String RESULT_ATTR = "result"; 67 | private static final String EVENT_COUNT_ATTR = "count"; 68 | private static final String FINALPNG_ATT = "final"; 69 | private static final String FINALLOG_ATT = "log"; 70 | private List events = new LinkedList(); 71 | private EventTag currentTag = null; 72 | 73 | private String application = "NA"; 74 | private String result = "NA"; 75 | private int count = 0; 76 | private String finalPng = "NA"; 77 | private String finalLog = "NA"; 78 | 79 | public void addEvent(EventTag event) { 80 | events.add(event); 81 | currentTag = event; 82 | } 83 | 84 | public void serialize(KXmlSerializer serializer) throws IOException { 85 | serializer.startTag(CtsXmlResultReporter.ns, MONKEY_TEST_TAG); 86 | serializer.attribute(CtsXmlResultReporter.ns, APPLICATION_ATTR, 87 | getApplication()); 88 | serializer.attribute(CtsXmlResultReporter.ns, EVENT_COUNT_ATTR, 89 | getCount() + ""); 90 | serializer.attribute(CtsXmlResultReporter.ns, RESULT_ATTR, getResult()); 91 | serializer.attribute(CtsXmlResultReporter.ns, FINALPNG_ATT, 92 | getFinalPng()); 93 | serializer.attribute(CtsXmlResultReporter.ns, FINALLOG_ATT, 94 | getFinalLog()); 95 | for (int i = events.size() - 1; i >= 0; i--) { 96 | events.get(i).serialize(serializer, i, count); 97 | } 98 | serializer.endTag(CtsXmlResultReporter.ns, MONKEY_TEST_TAG); 99 | } 100 | 101 | public EventTag getCurrentTag() { 102 | return currentTag; 103 | } 104 | 105 | public void setCurrentTag(EventTag currentTag) { 106 | this.currentTag = currentTag; 107 | } 108 | 109 | @Override 110 | public void parse(XmlPullParser parser) throws XmlPullParserException, 111 | IOException { 112 | int eventType = parser.getEventType(); 113 | while (eventType != XmlPullParser.END_DOCUMENT) { 114 | if (eventType == XmlPullParser.START_TAG 115 | && parser.getName().equals(EventTag.EVENT_TAG)) { 116 | String type = getAttribute(parser, EventTag.TYPE_ATTR); 117 | if (EventTag.EVENT_TYPE_DRAG.equals(type)) { 118 | DragTag dragTag = new DragTag(); 119 | dragTag.parse(parser); 120 | events.add(dragTag); 121 | } else if (EventTag.EVENT_TYPE_KEY.equals(type)) { 122 | KeyTag keyTag = new KeyTag(); 123 | keyTag.parse(parser); 124 | events.add(keyTag); 125 | } else if (EventTag.EVENT_TYPE_TAP.equals(type)) { 126 | TapTag tapTag = new TapTag(); 127 | tapTag.parse(parser); 128 | events.add(tapTag); 129 | } 130 | if (events.size() >= 50) 131 | return; 132 | } 133 | eventType = parser.next(); 134 | } 135 | 136 | } 137 | 138 | } 139 | -------------------------------------------------------------------------------- /src/com/android/cts/tradefed/result/monkey/MotionTag.java: -------------------------------------------------------------------------------- 1 | package com.android.cts.tradefed.result.monkey; 2 | 3 | import java.awt.Point; 4 | import java.io.IOException; 5 | import java.util.LinkedList; 6 | import java.util.List; 7 | 8 | import org.kxml2.io.KXmlSerializer; 9 | import org.xmlpull.v1.XmlPullParser; 10 | import org.xmlpull.v1.XmlPullParserException; 11 | 12 | import com.android.cts.tradefed.result.CtsXmlResultReporter; 13 | 14 | public class MotionTag extends EventTag { 15 | 16 | public static final String DIRECTION_UP = "up"; 17 | public static final String DIRECTION_DOWN = "down"; 18 | public static final String DIRECTION_MOVE = "move"; 19 | 20 | public List getTouches() { 21 | return touches; 22 | } 23 | 24 | public MotionTag() { 25 | setType(EVENT_TYPE_DRAG); 26 | } 27 | 28 | private List touches = new LinkedList(); 29 | 30 | @Override 31 | public void serialize(KXmlSerializer serializer, int index,int count) 32 | throws IOException { 33 | setIndex(index); 34 | setPos(count - 1 - index); 35 | // TODO Auto-generated method stub 36 | serializer.startTag(CtsXmlResultReporter.ns, EVENT_TAG); 37 | serializer.attribute(CtsXmlResultReporter.ns, INDEX_ATTR, getIndex() 38 | + ""); 39 | serializer.attribute(CtsXmlResultReporter.ns, TYPE_ATTR, getType()); 40 | serializer.attribute(CtsXmlResultReporter.ns, IMAGE_ATTR, getImage()); 41 | serializer.attribute(CtsXmlResultReporter.ns, LOG_ATTR, getLog()); 42 | serializer.attribute(CtsXmlResultReporter.ns, POS_ATTR, getPos() + ""); 43 | for (TouchTag touchTag : touches) { 44 | touchTag.serialize(serializer); 45 | } 46 | serializer.endTag(CtsXmlResultReporter.ns, EVENT_TAG); 47 | } 48 | 49 | public void addTouchTag(TouchTag touchTag) { 50 | touches.add(touchTag); 51 | } 52 | 53 | public void addTouchUp(Point point) { 54 | TouchTag tag = new TouchTag(); 55 | tag.setDirection(DIRECTION_UP); 56 | setXY(point, tag); 57 | } 58 | 59 | public void addTouchMove(Point point) { 60 | TouchTag tag = new TouchTag(); 61 | tag.setDirection(DIRECTION_MOVE); 62 | setXY(point, tag); 63 | } 64 | 65 | public void addTouchDown(Point point) { 66 | TouchTag tag = new TouchTag(); 67 | tag.setDirection(DIRECTION_DOWN); 68 | setXY(point, tag); 69 | } 70 | 71 | private void setXY(Point point, TouchTag tag) { 72 | tag.setX(point.x); 73 | tag.setY(point.y); 74 | touches.add(tag); 75 | } 76 | 77 | @Override 78 | public void parse(XmlPullParser parser) throws XmlPullParserException, 79 | IOException { 80 | if (!parser.getName().equals(EVENT_TAG)) { 81 | throw new XmlPullParserException(String.format( 82 | "invalid XML: Expected %s tag but received %s", EVENT_TAG, 83 | parser.getName())); 84 | } 85 | setIndex(Integer.parseInt(getAttribute(parser, INDEX_ATTR))); 86 | setImage(getAttribute(parser, IMAGE_ATTR)); 87 | setLog(getAttribute(parser, LOG_ATTR)); 88 | int eventType = parser.getEventType(); 89 | while (eventType != XmlPullParser.END_DOCUMENT) { 90 | if (eventType == XmlPullParser.START_TAG 91 | && parser.getName().equals(TouchTag.TOUCH_TAG)) { 92 | TouchTag touchTag = new TouchTag(); 93 | touchTag.parse(parser); 94 | touches.add(touchTag); 95 | } else if (eventType == XmlPullParser.END_TAG 96 | && parser.getName().equals(EVENT_TAG)) { 97 | return; 98 | } 99 | 100 | eventType = parser.next(); 101 | } 102 | } 103 | 104 | 105 | } 106 | -------------------------------------------------------------------------------- /src/com/android/cts/tradefed/result/monkey/TapTag.java: -------------------------------------------------------------------------------- 1 | package com.android.cts.tradefed.result.monkey; 2 | 3 | import java.io.IOException; 4 | 5 | import org.kxml2.io.KXmlSerializer; 6 | import org.xmlpull.v1.XmlPullParser; 7 | import org.xmlpull.v1.XmlPullParserException; 8 | 9 | import com.android.cts.tradefed.result.CtsXmlResultReporter; 10 | 11 | public class TapTag extends EventTag { 12 | 13 | public TapTag() { 14 | setType(EVENT_TYPE_TAP); 15 | } 16 | 17 | public float getX() { 18 | return x; 19 | } 20 | 21 | public void setX(float x) { 22 | this.x = x; 23 | } 24 | 25 | public float getY() { 26 | return y; 27 | } 28 | 29 | public void setY(float y) { 30 | this.y = y; 31 | } 32 | 33 | private static final String X_ATTR = "x"; 34 | private static final String Y_ATTR = "y"; 35 | 36 | private float x = 0; 37 | private float y = 0; 38 | 39 | @Override 40 | public void serialize(KXmlSerializer serializer, int index, int count) 41 | throws IOException { 42 | setIndex(index); 43 | setPos(count - 1 - index); 44 | // TODO Auto-generated method stub 45 | serializer.startTag(CtsXmlResultReporter.ns, EVENT_TAG); 46 | serializer.attribute(CtsXmlResultReporter.ns, INDEX_ATTR, getIndex() 47 | + ""); 48 | serializer.attribute(CtsXmlResultReporter.ns, TYPE_ATTR, getType()); 49 | 50 | serializer.attribute(CtsXmlResultReporter.ns, X_ATTR, getX() + ""); 51 | serializer.attribute(CtsXmlResultReporter.ns, Y_ATTR, getY() + ""); 52 | serializer.attribute(CtsXmlResultReporter.ns, TIME_ATTR, getTime()); 53 | serializer.attribute(CtsXmlResultReporter.ns, IMAGE_ATTR, getImage()); 54 | serializer.attribute(CtsXmlResultReporter.ns, LOG_ATTR, getLog()); 55 | serializer.attribute(CtsXmlResultReporter.ns, POS_ATTR, getPos() + ""); 56 | serializer.endTag(CtsXmlResultReporter.ns, EVENT_TAG); 57 | 58 | } 59 | 60 | @Override 61 | public void parse(XmlPullParser parser) throws XmlPullParserException, 62 | IOException { 63 | if (!parser.getName().equals(EVENT_TAG)) { 64 | throw new XmlPullParserException(String.format( 65 | "invalid XML: Expected %s tag but received %s", EVENT_TAG, 66 | parser.getName())); 67 | } 68 | setIndex(Integer.parseInt(getAttribute(parser, INDEX_ATTR))); 69 | setImage(getAttribute(parser, IMAGE_ATTR)); 70 | setLog(getAttribute(parser, LOG_ATTR)); 71 | setX(Float.parseFloat(getAttribute(parser, X_ATTR))); 72 | setY(Float.parseFloat(getAttribute(parser, Y_ATTR))); 73 | } 74 | 75 | } 76 | -------------------------------------------------------------------------------- /src/com/android/cts/tradefed/result/monkey/TouchTag.java: -------------------------------------------------------------------------------- 1 | package com.android.cts.tradefed.result.monkey; 2 | 3 | import java.io.IOException; 4 | 5 | import org.kxml2.io.KXmlSerializer; 6 | import org.xmlpull.v1.XmlPullParser; 7 | import org.xmlpull.v1.XmlPullParserException; 8 | 9 | import com.android.cts.tradefed.result.AbstractXmlPullParser; 10 | import com.android.cts.tradefed.result.CtsXmlResultReporter; 11 | 12 | public class TouchTag extends AbstractXmlPullParser { 13 | public static final String TOUCH_TAG = "Touch"; 14 | private static final String DIRECTION_ATTR = "direction"; 15 | private static final String X_ATTR = "x"; 16 | private static final String Y_ATTR = "y"; 17 | private String direction = "NA"; 18 | private int x = 0; 19 | private int y = 0; 20 | 21 | public String getDirection() { 22 | return direction; 23 | } 24 | 25 | public void setDirection(String direction) { 26 | this.direction = direction; 27 | } 28 | 29 | public int getX() { 30 | return x; 31 | } 32 | 33 | public void setX(int x) { 34 | this.x = x; 35 | } 36 | 37 | public int getY() { 38 | return y; 39 | } 40 | 41 | public void setY(int y) { 42 | this.y = y; 43 | } 44 | 45 | public void serialize(KXmlSerializer serializer) throws IOException { 46 | serializer.startTag(CtsXmlResultReporter.ns, TOUCH_TAG); 47 | serializer.attribute(CtsXmlResultReporter.ns, DIRECTION_ATTR, 48 | getDirection()); 49 | serializer.attribute(CtsXmlResultReporter.ns, X_ATTR, getX() + ""); 50 | serializer.attribute(CtsXmlResultReporter.ns, Y_ATTR, getY() + ""); 51 | serializer.endTag(CtsXmlResultReporter.ns, TOUCH_TAG); 52 | 53 | } 54 | 55 | @Override 56 | public void parse(XmlPullParser parser) throws XmlPullParserException, 57 | IOException { 58 | if (!parser.getName().equals(TOUCH_TAG)) { 59 | throw new XmlPullParserException(String.format( 60 | "invalid XML: Expected %s tag but received %s", TOUCH_TAG, 61 | parser.getName())); 62 | } 63 | 64 | setDirection(getAttribute(parser, DIRECTION_ATTR)); 65 | setX(Integer.parseInt(getAttribute(parser, X_ATTR))); 66 | setY(Integer.parseInt(getAttribute(parser, Y_ATTR))); 67 | } 68 | 69 | } 70 | -------------------------------------------------------------------------------- /src/com/android/cts/tradefed/targetprep/CtsRootDeviceSetup.java: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright (C) 2010 The Android Open Source Project 3 | * 4 | * Licensed under the Apache License, Version 2.0 (the "License"); 5 | * you may not use this file except in compliance with the License. 6 | * You may obtain a copy of the License at 7 | * 8 | * http://www.apache.org/licenses/LICENSE-2.0 9 | * 10 | * Unless required by applicable law or agreed to in writing, software 11 | * distributed under the License is distributed on an "AS IS" BASIS, 12 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 | * See the License for the specific language governing permissions and 14 | * limitations under the License. 15 | */ 16 | package com.android.cts.tradefed.targetprep; 17 | 18 | import com.android.cts.tradefed.build.CtsBuildHelper; 19 | import com.android.tradefed.build.IBuildInfo; 20 | import com.android.tradefed.build.IFolderBuildInfo; 21 | import com.android.tradefed.device.DeviceNotAvailableException; 22 | import com.android.tradefed.device.ITestDevice; 23 | import com.android.tradefed.log.LogUtil.CLog; 24 | import com.android.tradefed.targetprep.DeviceSetup; 25 | import com.android.tradefed.targetprep.ITargetPreparer; 26 | import com.android.tradefed.targetprep.TargetSetupError; 27 | 28 | import java.io.FileNotFoundException; 29 | 30 | /** 31 | * A {@link ITargetPreparer} that attempts to automatically perform the CTS-specific manual steps 32 | * for setting up a device for CTS testing. 33 | *

34 | * This class is NOT intended for 'official' CTS runs against a production device as the steps 35 | * performed by this class require a debug build (aka 'adb root' must succeed). 36 | *

37 | * This class currently performs the 'Allow mock locations' and 'accessibililty setup' steps 38 | * documented in the CTS user manual. It is intended to be used in conjunction with 39 | * a {@link DeviceSetup} which will enable the 'Stay Awake' setting and verify that external 40 | * storage is present. 41 | */ 42 | public class CtsRootDeviceSetup implements ITargetPreparer { 43 | 44 | private static final String DEVICE_ADMIN_APK_FILE_NAME = "CtsDeviceAdmin.apk"; 45 | 46 | /** 47 | * {@inheritDoc} 48 | */ 49 | @Override 50 | public void setUp(ITestDevice device, IBuildInfo buildInfo) throws TargetSetupError, 51 | DeviceNotAvailableException { 52 | if (!(buildInfo instanceof IFolderBuildInfo)) { 53 | throw new IllegalArgumentException("Provided buildInfo is not a IFolderBuildInfo"); 54 | } 55 | CLog.i("Setting up %s to run CTS tests", device.getSerialNumber()); 56 | 57 | IFolderBuildInfo ctsBuild = (IFolderBuildInfo)buildInfo; 58 | try { 59 | CtsBuildHelper buildHelper = new CtsBuildHelper(ctsBuild.getRootDir()); 60 | 61 | if (!device.enableAdbRoot()) { 62 | throw new TargetSetupError(String.format( 63 | "Failed to set root on device %s.", device.getSerialNumber())); 64 | } 65 | 66 | // perform CTS setup steps that only work if adb is root 67 | SettingsToggler.setSecureInt(device, "mock_location", 1); 68 | enableDeviceAdmin(device, buildHelper); 69 | // This is chrome specific setting to disable the first screen. 70 | // For other browser, it will not do anything. 71 | device.executeShellCommand( 72 | "echo \"chrome --disable-fre\" > /data/local/chrome-command-line"); 73 | // end root setup steps 74 | } catch (FileNotFoundException e) { 75 | throw new TargetSetupError("Invalid CTS installation", e); 76 | } 77 | } 78 | 79 | private void enableDeviceAdmin(ITestDevice device, CtsBuildHelper ctsBuild) 80 | throws DeviceNotAvailableException, TargetSetupError, FileNotFoundException { 81 | String errorCode = device.installPackage(ctsBuild.getTestApp(DEVICE_ADMIN_APK_FILE_NAME), 82 | true); 83 | if (errorCode != null) { 84 | // TODO: retry ? 85 | throw new TargetSetupError(String.format( 86 | "Failed to install %s on device %s. Reason: %s", 87 | DEVICE_ADMIN_APK_FILE_NAME, device.getSerialNumber(), errorCode)); 88 | } 89 | // TODO: enable device admin Settings 90 | } 91 | } 92 | -------------------------------------------------------------------------------- /src/com/android/cts/tradefed/targetprep/SettingsToggler.java: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright (C) 2012 The Android Open Source Project 3 | * 4 | * Licensed under the Apache License, Version 2.0 (the "License"); 5 | * you may not use this file except in compliance with the License. 6 | * You may obtain a copy of the License at 7 | * 8 | * http://www.apache.org/licenses/LICENSE-2.0 9 | * 10 | * Unless required by applicable law or agreed to in writing, software 11 | * distributed under the License is distributed on an "AS IS" BASIS, 12 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 | * See the License for the specific language governing permissions and 14 | * limitations under the License. 15 | */ 16 | package com.android.cts.tradefed.targetprep; 17 | 18 | import com.android.tradefed.device.DeviceNotAvailableException; 19 | import com.android.tradefed.device.ITestDevice; 20 | 21 | /** 22 | * {@link SettingsToggler} sets settings by using the "adb shell content" command. 23 | */ 24 | public class SettingsToggler { 25 | private static final String GROUP_SECURE = "secure"; 26 | private static final String GROUP_GLOBAL = "global"; 27 | 28 | /** Sets a setting by deleting and then inserting the string value. */ 29 | public static void setString(ITestDevice device, String group, String name, String value) 30 | throws DeviceNotAvailableException { 31 | deleteSetting(device, group, name); 32 | device.executeShellCommand( 33 | "content insert" 34 | + " --uri content://settings/" + group 35 | + " --bind name:s:" + name 36 | + " --bind value:s:" + value); 37 | } 38 | 39 | /** Sets a secure setting by deleting and then inserting the string value. */ 40 | public static void setSecureString(ITestDevice device, String name, String value) 41 | throws DeviceNotAvailableException { 42 | setString(device, GROUP_SECURE, name, value); 43 | } 44 | 45 | /** Sets a global setting by deleting and then inserting the string value. */ 46 | public static void setGlobalString(ITestDevice device, String name, String value) 47 | throws DeviceNotAvailableException { 48 | setString(device, GROUP_GLOBAL, name, value); 49 | } 50 | 51 | /** Sets a setting by deleting and then inserting the int value. */ 52 | public static void setInt(ITestDevice device, String group, String name, int value) 53 | throws DeviceNotAvailableException { 54 | deleteSetting(device, group, name); 55 | device.executeShellCommand( 56 | "content insert" 57 | + " --uri content://settings/" + group 58 | + " --bind name:s:" + name 59 | + " --bind value:i:" + value); 60 | } 61 | 62 | /** Sets a secure setting by deleting and then inserting the int value. */ 63 | public static void setSecureInt(ITestDevice device, String name, int value) 64 | throws DeviceNotAvailableException { 65 | setInt(device, GROUP_SECURE, name, value); 66 | } 67 | 68 | /** Sets a global setting by deleting and then inserting the int value. */ 69 | public static void setGlobalInt(ITestDevice device, String name, int value) 70 | throws DeviceNotAvailableException { 71 | setInt(device, GROUP_GLOBAL, name, value); 72 | } 73 | 74 | public static void updateString(ITestDevice device, String group, String name, String value) 75 | throws DeviceNotAvailableException { 76 | device.executeShellCommand( 77 | "content update" 78 | + " --uri content://settings/" + group 79 | + " --bind value:s:" + value 80 | + " --where \"name='" + name + "'\""); 81 | } 82 | 83 | public static void updateSecureString(ITestDevice device, String name, String value) 84 | throws DeviceNotAvailableException { 85 | updateString(device, GROUP_SECURE, name, value); 86 | } 87 | 88 | public static void updateGlobalString(ITestDevice device, String name, String value) 89 | throws DeviceNotAvailableException { 90 | updateString(device, GROUP_GLOBAL, name, value); 91 | } 92 | 93 | public static void updateInt(ITestDevice device, String group, String name, int value) 94 | throws DeviceNotAvailableException { 95 | device.executeShellCommand( 96 | "content update" 97 | + " --uri content://settings/" + group 98 | + " --bind value:i:" + value 99 | + " --where \"name='" + name + "'\""); 100 | } 101 | 102 | public static void updateSecureInt(ITestDevice device, String name, int value) 103 | throws DeviceNotAvailableException { 104 | updateInt(device, GROUP_SECURE, name, value); 105 | } 106 | 107 | public static void updateGlobalInt(ITestDevice device, String name, int value) 108 | throws DeviceNotAvailableException { 109 | updateInt(device, GROUP_GLOBAL, name, value); 110 | } 111 | 112 | private static void deleteSetting(ITestDevice device, String group, String name) 113 | throws DeviceNotAvailableException { 114 | device.executeShellCommand( 115 | "content delete" 116 | + " --uri content://settings/" + group 117 | + " --where \"name='" + name + "'\""); 118 | } 119 | } 120 | -------------------------------------------------------------------------------- /src/com/android/cts/tradefed/test/Test.java: -------------------------------------------------------------------------------- 1 | package com.android.cts.tradefed.test; 2 | 3 | import java.awt.Color; 4 | import java.awt.Font; 5 | import java.io.File; 6 | import java.io.IOException; 7 | 8 | import pl.vgtworld.imagedraw.processing.ImageProcessing; 9 | 10 | import com.android.cts.tradefed.result.MonkeyReporter; 11 | 12 | import junit.framework.TestCase; 13 | 14 | public class Test extends TestCase { 15 | 16 | public void test_MonkeyReporter() throws IOException { 17 | 18 | MonkeyReporter reporter = new MonkeyReporter( 19 | new File( 20 | "/Users/wuxian/Downloads/android-cts/repository/results/2015.05.27_18.23.30/testResult.xml"), null); 21 | reporter.drawImage(); 22 | // reporter.createReporter(); 23 | // reporter.transferToHtml("/Users/wuxian/Downloads/android-cts/repository/results/2015.05.27_10.56.35/report/index.xsl", 24 | // "/Users/wuxian/Downloads/android-cts/repository/results/2015.05.27_10.56.35/testResult.xml", 25 | // "/Users/wuxian/Downloads/android-cts/repository/results/2015.05.27_10.56.35/report/index.html"); 26 | // reporter.transferToHtml("/Users/wuxian/Downloads/android-cts/repository/results/2015.05.27_10.56.35/report/result.xsl", 27 | // "/Users/wuxian/Downloads/android-cts/repository/results/2015.05.27_10.56.35/testResult.xml", 28 | // "/Users/wuxian/Downloads/android-cts/repository/results/2015.05.27_10.56.35/report/result.html"); 29 | } 30 | 31 | public void test_MonkeyReporter1() { 32 | 33 | MonkeyReporter reporter = new MonkeyReporter( 34 | new File( 35 | "/Users/wuxian/Downloads/android-cts/repository/results/2015.05.26_11.14.34/testResult.xml"), 36 | new File("/Users/wuxian/Desktop/index"), null); 37 | } 38 | 39 | public void test_File() throws IOException { 40 | ImageProcessing image = new ImageProcessing(); 41 | File imageFile = new File("/Users/wuxian/Desktop/123.png"); 42 | image.open(imageFile); 43 | image.drawText("Helloworld1", Color.RED, new Font("SansSerif", 44 | Font.BOLD, 40), 0, 50); 45 | image.save(imageFile); 46 | } 47 | 48 | public void test_math() { 49 | int count = (int)Float.parseFloat("48.0"); 50 | System.out.println(count); 51 | } 52 | 53 | } 54 | -------------------------------------------------------------------------------- /src/com/android/cts/tradefed/testtype/AccessibilityServiceTestRunner.java: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright (C) 2012 The Android Open Source Project 3 | * 4 | * Licensed under the Apache License, Version 2.0 (the "License"); 5 | * you may not use this file except in compliance with the License. 6 | * You may obtain a copy of the License at 7 | * 8 | * http://www.apache.org/licenses/LICENSE-2.0 9 | * 10 | * Unless required by applicable law or agreed to in writing, software 11 | * distributed under the License is distributed on an "AS IS" BASIS, 12 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 | * See the License for the specific language governing permissions and 14 | * limitations under the License. 15 | */ 16 | 17 | package com.android.cts.tradefed.testtype; 18 | 19 | import com.android.cts.tradefed.build.CtsBuildHelper; 20 | import com.android.tradefed.build.IBuildInfo; 21 | import com.android.tradefed.device.DeviceNotAvailableException; 22 | import com.android.tradefed.result.ITestInvocationListener; 23 | import com.android.tradefed.util.FileUtil; 24 | 25 | import junit.framework.TestCase; 26 | 27 | import java.io.File; 28 | 29 | /** 30 | * Running the accessibility tests requires modification of secure 31 | * settings. Secure settings cannot be changed from device CTS tests 32 | * since system signature permission is required. Such settings can 33 | * be modified by the shell user, so a host side test is used for 34 | * installing a package with a delegating accessibility service, enabling 35 | * this service, running these tests, disabling the service, and removing 36 | * the delegating accessibility service package. 37 | * 38 | * @deprecated This class is not required in current CTS builds. Still 39 | * maintained so cts-tradefed can run against older CTS builds that still 40 | * require this class. 41 | */ 42 | public class AccessibilityServiceTestRunner extends InstrumentationApkTest { 43 | 44 | private static final String DELEGATING_ACCESSIBLITY_SERVICE_PACKAGE_NAME = 45 | "android.accessibilityservice.delegate"; 46 | 47 | private static final String DELEGATING_ACCESSIBLITY_SERVICE_NAME = 48 | "android.accessibilityservice.delegate.DelegatingAccessibilityService"; 49 | 50 | private static final String DELEGATING_ACCESSIBLITY_SERVICE_APK = 51 | "CtsDelegatingAccessibilityService.apk"; 52 | 53 | private CtsBuildHelper mCtsBuild; 54 | 55 | @Override 56 | public void setBuild(IBuildInfo build) { 57 | super.setBuild(build); 58 | mCtsBuild = CtsBuildHelper.createBuildHelper(build); 59 | } 60 | 61 | @Override 62 | public void run(ITestInvocationListener listener) throws DeviceNotAvailableException { 63 | beforeTest(); 64 | super.run(listener); 65 | afterTest(); 66 | } 67 | 68 | private void beforeTest() throws DeviceNotAvailableException { 69 | installApkAndAssert(DELEGATING_ACCESSIBLITY_SERVICE_APK); 70 | enableAccessibilityAndDelegatingService(); 71 | } 72 | 73 | private void afterTest() throws DeviceNotAvailableException { 74 | AccessibilityTestRunner.disableAccessibilityAndServices(getDevice()); 75 | uninstallAndAssert(DELEGATING_ACCESSIBLITY_SERVICE_PACKAGE_NAME); 76 | } 77 | 78 | private void installApkAndAssert(String apkName) throws DeviceNotAvailableException { 79 | File file = FileUtil.getFileForPath(mCtsBuild.getTestCasesDir(), apkName); 80 | String errorMessage = getDevice().installPackage(file, true); 81 | TestCase.assertNull("Error installing: " + apkName, errorMessage); 82 | } 83 | 84 | private void uninstallAndAssert(String packageName) throws DeviceNotAvailableException { 85 | String errorMessage = getDevice().uninstallPackage(packageName); 86 | TestCase.assertNull("Error uninstalling: " + packageName, errorMessage); 87 | } 88 | 89 | private void enableAccessibilityAndDelegatingService() throws DeviceNotAvailableException { 90 | String componentName = DELEGATING_ACCESSIBLITY_SERVICE_PACKAGE_NAME + "/" 91 | + DELEGATING_ACCESSIBLITY_SERVICE_NAME; 92 | AccessibilityTestRunner.enableAccessibilityAndServices(getDevice(), 93 | componentName); 94 | } 95 | } 96 | -------------------------------------------------------------------------------- /src/com/android/cts/tradefed/testtype/AccessibilityTestRunner.java: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright (C) 2012 The Android Open Source Project 3 | * 4 | * Licensed under the Apache License, Version 2.0 (the "License"); 5 | * you may not use this file except in compliance with the License. 6 | * You may obtain a copy of the License at 7 | * 8 | * http://www.apache.org/licenses/LICENSE-2.0 9 | * 10 | * Unless required by applicable law or agreed to in writing, software 11 | * distributed under the License is distributed on an "AS IS" BASIS, 12 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 | * See the License for the specific language governing permissions and 14 | * limitations under the License. 15 | */ 16 | 17 | package com.android.cts.tradefed.testtype; 18 | 19 | import com.android.cts.tradefed.build.CtsBuildHelper; 20 | import com.android.cts.tradefed.targetprep.SettingsToggler; 21 | import com.android.tradefed.build.IBuildInfo; 22 | import com.android.tradefed.device.DeviceNotAvailableException; 23 | import com.android.tradefed.device.ITestDevice; 24 | import com.android.tradefed.result.ITestInvocationListener; 25 | import com.android.tradefed.util.FileUtil; 26 | 27 | import junit.framework.TestCase; 28 | 29 | import java.io.File; 30 | 31 | /** 32 | * Running the accessibility tests requires modification of secure 33 | * settings. Secure settings cannot be changed from device CTS tests 34 | * since system signature permission is required. Such settings can 35 | * be modified by the shell user, so a host side test is used for 36 | * installing a package with some accessibility services, enabling 37 | * these services, running the tests, disabling the services, and removing 38 | * the accessibility services package. 39 | */ 40 | public class AccessibilityTestRunner extends InstrumentationApkTest { 41 | 42 | private static final String SOME_ACCESSIBLITY_SERVICES_PACKAGE_NAME = 43 | "android.view.accessibility.services"; 44 | 45 | private static final String SPEAKING_ACCESSIBLITY_SERVICE_NAME = 46 | "android.view.accessibility.services.SpeakingAccessibilityService"; 47 | 48 | private static final String VIBRATING_ACCESSIBLITY_SERVICE_NAME = 49 | "android.view.accessibility.services.VibratingAccessibilityService"; 50 | 51 | private static final String SOME_ACCESSIBLITY_SERVICES_APK = 52 | "CtsSomeAccessibilityServices.apk"; 53 | 54 | private CtsBuildHelper mCtsBuild; 55 | 56 | @Override 57 | public void setBuild(IBuildInfo build) { 58 | super.setBuild(build); 59 | mCtsBuild = CtsBuildHelper.createBuildHelper(build); 60 | } 61 | 62 | @Override 63 | public void run(ITestInvocationListener listener) throws DeviceNotAvailableException { 64 | beforeTest(); 65 | super.run(listener); 66 | afterTest(); 67 | } 68 | 69 | private void beforeTest() throws DeviceNotAvailableException { 70 | installApkAndAssert(SOME_ACCESSIBLITY_SERVICES_APK); 71 | enableAccessibilityAndServices(); 72 | } 73 | 74 | private void afterTest() throws DeviceNotAvailableException { 75 | disableAccessibilityAndServices(getDevice()); 76 | uninstallAndAssert(SOME_ACCESSIBLITY_SERVICES_PACKAGE_NAME); 77 | } 78 | 79 | private void installApkAndAssert(String apkName) throws DeviceNotAvailableException { 80 | File file = FileUtil.getFileForPath(mCtsBuild.getTestCasesDir(), apkName); 81 | String errorMessage = getDevice().installPackage(file, true); 82 | TestCase.assertNull("Error installing: " + apkName, errorMessage); 83 | } 84 | 85 | private void uninstallAndAssert(String packageName) throws DeviceNotAvailableException { 86 | String errorMessage = getDevice().uninstallPackage(packageName); 87 | TestCase.assertNull("Error uninstalling: " + packageName, errorMessage); 88 | } 89 | 90 | private void enableAccessibilityAndServices() throws DeviceNotAvailableException { 91 | String enabledServicesValue = 92 | SOME_ACCESSIBLITY_SERVICES_PACKAGE_NAME + "/" + SPEAKING_ACCESSIBLITY_SERVICE_NAME 93 | + ":" 94 | + SOME_ACCESSIBLITY_SERVICES_PACKAGE_NAME + "/" + VIBRATING_ACCESSIBLITY_SERVICE_NAME; 95 | enableAccessibilityAndServices(getDevice(), enabledServicesValue); 96 | } 97 | 98 | static void enableAccessibilityAndServices(ITestDevice device, String value) 99 | throws DeviceNotAvailableException { 100 | SettingsToggler.setSecureString(device, "enabled_accessibility_services", value); 101 | SettingsToggler.setSecureString(device, 102 | "touch_exploration_granted_accessibility_services", value); 103 | SettingsToggler.setSecureInt(device, "accessibility_enabled", 1); 104 | } 105 | 106 | static void disableAccessibilityAndServices(ITestDevice device) 107 | throws DeviceNotAvailableException { 108 | SettingsToggler.updateSecureString(device, "enabled_accessibility_services", ""); 109 | SettingsToggler.updateSecureString(device, 110 | "touch_exploration_granted_accessibility_services", ""); 111 | SettingsToggler.updateSecureInt(device, "accessibility_enabled", 0); 112 | } 113 | } 114 | -------------------------------------------------------------------------------- /src/com/android/cts/tradefed/testtype/DisplayTestRunner.java: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright (C) 2012 The Android Open Source Project 3 | * 4 | * Licensed under the Apache License, Version 2.0 (the "License"); 5 | * you may not use this file except in compliance with the License. 6 | * You may obtain a copy of the License at 7 | * 8 | * http://www.apache.org/licenses/LICENSE-2.0 9 | * 10 | * Unless required by applicable law or agreed to in writing, software 11 | * distributed under the License is distributed on an "AS IS" BASIS, 12 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 | * See the License for the specific language governing permissions and 14 | * limitations under the License. 15 | */ 16 | 17 | package com.android.cts.tradefed.testtype; 18 | 19 | import com.android.cts.tradefed.targetprep.SettingsToggler; 20 | import com.android.tradefed.device.DeviceNotAvailableException; 21 | import com.android.tradefed.result.ITestInvocationListener; 22 | 23 | /** 24 | * Running the display tests requires modification of secure settings to create an overlay display. 25 | * Secure settings cannot be changed from device CTS tests since system signature permission is 26 | * required. Such settings can be modified by the shell user, so a host side test is used. 27 | */ 28 | public class DisplayTestRunner extends InstrumentationApkTest { 29 | private static final String OVERLAY_DISPLAY_DEVICES_SETTING_NAME = "overlay_display_devices"; 30 | 31 | // Use a non-standard pattern, must match values in tests/tests/display/.../DisplayTest.java 32 | private static final String OVERLAY_DISPLAY_DEVICES_SETTING_VALUE = "1281x721/214"; 33 | 34 | @Override 35 | public void run(ITestInvocationListener listener) throws DeviceNotAvailableException { 36 | // CLog.e("run: About to enable overlay display."); 37 | SettingsToggler.setGlobalString(getDevice(), OVERLAY_DISPLAY_DEVICES_SETTING_NAME, 38 | OVERLAY_DISPLAY_DEVICES_SETTING_VALUE); 39 | 40 | super.run(listener); 41 | 42 | // Tear down overlay display. 43 | // CLog.e("run: About to disable overlay display."); 44 | SettingsToggler.setGlobalString(getDevice(), OVERLAY_DISPLAY_DEVICES_SETTING_NAME, 45 | ""); 46 | } 47 | } 48 | -------------------------------------------------------------------------------- /src/com/android/cts/tradefed/testtype/GeeTest.java: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright (C) 2011 The Android Open Source Project 3 | * 4 | * Licensed under the Apache License, Version 2.0 (the "License"); 5 | * you may not use this file except in compliance with the License. 6 | * You may obtain a copy of the License at 7 | * 8 | * http://www.apache.org/licenses/LICENSE-2.0 9 | * 10 | * Unless required by applicable law or agreed to in writing, software 11 | * distributed under the License is distributed on an "AS IS" BASIS, 12 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 | * See the License for the specific language governing permissions and 14 | * limitations under the License. 15 | */ 16 | 17 | package com.android.cts.tradefed.testtype; 18 | 19 | import com.android.cts.tradefed.build.CtsBuildHelper; 20 | import com.android.ddmlib.testrunner.ITestRunListener; 21 | import com.android.tradefed.build.IBuildInfo; 22 | import com.android.tradefed.device.DeviceNotAvailableException; 23 | import com.android.tradefed.device.ITestDevice; 24 | import com.android.tradefed.log.LogUtil.CLog; 25 | import com.android.tradefed.result.ITestInvocationListener; 26 | import com.android.tradefed.testtype.IBuildReceiver; 27 | import com.android.tradefed.testtype.IDeviceTest; 28 | import com.android.tradefed.testtype.IRemoteTest; 29 | 30 | import java.io.File; 31 | 32 | /** 33 | * Test runner for native gTests. 34 | * 35 | * TODO: This is similar to Tradefed's existing GTest, but it doesn't confirm 36 | * each directory segment exists using ddmlib's file service. This was 37 | * a problem since /data is not visible on a user build, but it is 38 | * executable. It's also a lot more verbose when it comes to errors. 39 | */ 40 | public class GeeTest implements IBuildReceiver, IDeviceTest, IRemoteTest { 41 | 42 | private static final String NATIVE_TESTS_DIRECTORY = "/data/local/tmp/cts-native-tests"; 43 | 44 | private int mMaxTestTimeMs = 1 * 60 * 1000; 45 | 46 | private CtsBuildHelper mCtsBuild; 47 | private ITestDevice mDevice; 48 | 49 | private final String mPackageName; 50 | private final String mExeName; 51 | 52 | public GeeTest(String packageName, String exeName) { 53 | mPackageName = packageName; 54 | mExeName = exeName; 55 | } 56 | 57 | @Override 58 | public void run(ITestInvocationListener listener) throws DeviceNotAvailableException { 59 | if (installTest()) { 60 | runTest(listener); 61 | } else { 62 | CLog.e("Failed to install native tests"); 63 | } 64 | } 65 | 66 | private boolean installTest() throws DeviceNotAvailableException { 67 | if (!createRemoteDir(NATIVE_TESTS_DIRECTORY)) { 68 | CLog.e("Could not create directory for native tests: " + NATIVE_TESTS_DIRECTORY); 69 | return false; 70 | } 71 | 72 | File nativeExe = new File(mCtsBuild.getTestCasesDir(), mExeName); 73 | if (!nativeExe.exists()) { 74 | CLog.e("Native test not found: " + nativeExe); 75 | return false; 76 | } 77 | 78 | File devicePath = new File(NATIVE_TESTS_DIRECTORY, mExeName); 79 | if (!mDevice.pushFile(nativeExe, devicePath.toString())) { 80 | CLog.e("Failed to push native test to device"); 81 | return false; 82 | } 83 | return true; 84 | } 85 | 86 | private boolean createRemoteDir(String remoteFilePath) throws DeviceNotAvailableException { 87 | if (mDevice.doesFileExist(remoteFilePath)) { 88 | return true; 89 | } 90 | File remoteFile = new File(remoteFilePath); 91 | String parentPath = remoteFile.getParent(); 92 | if (parentPath != null) { 93 | if (!createRemoteDir(parentPath)) { 94 | return false; 95 | } 96 | } 97 | mDevice.executeShellCommand(String.format("mkdir %s", remoteFilePath)); 98 | return mDevice.doesFileExist(remoteFilePath); 99 | } 100 | 101 | void runTest(ITestRunListener listener) throws DeviceNotAvailableException { 102 | GeeTestResultParser resultParser = new GeeTestResultParser(mPackageName, listener); 103 | resultParser.setFakePackagePrefix(mPackageName + "."); 104 | 105 | String fullPath = NATIVE_TESTS_DIRECTORY + File.separator + mExeName; 106 | String flags = ""; 107 | CLog.v("Running gtest %s %s on %s", fullPath, flags, mDevice.getSerialNumber()); 108 | // force file to be executable 109 | CLog.v("%s", mDevice.executeShellCommand(String.format("chmod 755 %s", fullPath))); 110 | 111 | try { 112 | mDevice.executeShellCommand(String.format("%s %s", fullPath, flags), resultParser, 113 | mMaxTestTimeMs /* maxTimeToShellOutputResponse */, 114 | 0 /* retryAttempts */); 115 | } catch (DeviceNotAvailableException e) { 116 | resultParser.flush(); 117 | throw e; 118 | } catch (RuntimeException e) { 119 | resultParser.flush(); 120 | throw e; 121 | } 122 | } 123 | 124 | 125 | @Override 126 | public void setBuild(IBuildInfo buildInfo) { 127 | mCtsBuild = CtsBuildHelper.createBuildHelper(buildInfo); 128 | } 129 | 130 | @Override 131 | public void setDevice(ITestDevice device) { 132 | mDevice = device; 133 | } 134 | 135 | @Override 136 | public ITestDevice getDevice() { 137 | return mDevice; 138 | } 139 | } 140 | -------------------------------------------------------------------------------- /src/com/android/cts/tradefed/testtype/ITestPackageDef.java: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright (C) 2010 The Android Open Source Project 3 | * 4 | * Licensed under the Apache License, Version 2.0 (the "License"); 5 | * you may not use this file except in compliance with the License. 6 | * You may obtain a copy of the License at 7 | * 8 | * http://www.apache.org/licenses/LICENSE-2.0 9 | * 10 | * Unless required by applicable law or agreed to in writing, software 11 | * distributed under the License is distributed on an "AS IS" BASIS, 12 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 | * See the License for the specific language governing permissions and 14 | * limitations under the License. 15 | */ 16 | 17 | package com.android.cts.tradefed.testtype; 18 | 19 | import com.android.ddmlib.testrunner.TestIdentifier; 20 | import com.android.tradefed.testtype.IRemoteTest; 21 | 22 | import java.io.File; 23 | import java.util.Collection; 24 | 25 | /** 26 | * Container for CTS test info. 27 | *

28 | * Knows how to translate this info into a runnable {@link IRemoteTest}. 29 | */ 30 | public interface ITestPackageDef { 31 | 32 | /** 33 | * Get the unique URI, aka the appPackageName, of the test package. 34 | * @return the {@link String} uri 35 | */ 36 | public String getUri(); 37 | 38 | /** 39 | * Creates a runnable {@link IRemoteTest} from info stored in this definition. 40 | * 41 | * @param testCaseDir {@link File} representing directory of test case data 42 | * @param className the test class to restrict this run to or null to run all tests 43 | * in package 44 | * @param methodName the optional test method to restrict this run to, or null to 45 | * run all tests in class/package 46 | * @return a {@link IRemoteTest} with all necessary data populated to run the test or 47 | * null if test could not be created 48 | */ 49 | public IRemoteTest createTest(File testCaseDir); 50 | 51 | /** 52 | * Determine if given test is defined in this package. 53 | * 54 | * @param testDef the {@link TestIdentifier} 55 | * @return true if test is defined 56 | */ 57 | public boolean isKnownTest(TestIdentifier testDef); 58 | 59 | /** 60 | * Determine if given test class is defined in this package. 61 | * 62 | * @param testClassName the fully qualified test class name 63 | * @return true if test class is defined 64 | */ 65 | public boolean isKnownTestClass(String testClassName); 66 | 67 | /** 68 | * Get the collection of tests in this test package. 69 | */ 70 | public Collection getTests(); 71 | 72 | /** 73 | * Return the sha1sum of the binary file for this test package. 74 | *

75 | * Will only return a valid value after {@link #createTest(File, String, String)} has been 76 | * called. 77 | * 78 | * @return the sha1sum in {@link String} form 79 | */ 80 | public String getDigest(); 81 | 82 | /** 83 | * @return the name of this test package. 84 | */ 85 | public String getName(); 86 | 87 | /** 88 | * Set the filter to use to exclude tests 89 | * 90 | * @param excludedTestFilter 91 | */ 92 | public void setExcludedTestFilter(TestFilter excludedTestFilter); 93 | 94 | /** 95 | * Restrict this test package to run a specific class and method name 96 | * 97 | * @param className the test class to restrict this run to 98 | * @param methodName the optional test method to restrict this run to, or null to 99 | * run all tests in class 100 | */ 101 | public void setClassName(String className, String methodName); 102 | 103 | /** 104 | * Return the file name of this package's instrumentation target apk. 105 | * 106 | * @return the file name or null if not applicable. 107 | */ 108 | public String getTargetApkName(); 109 | 110 | /** 111 | * Return the Android package name of this package's instrumentation target, or 112 | * null if not applicable. 113 | */ 114 | public String getTargetPackageName(); 115 | 116 | } 117 | -------------------------------------------------------------------------------- /src/com/android/cts/tradefed/testtype/ITestPackageRepo.java: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright (C) 2010 The Android Open Source Project 3 | * 4 | * Licensed under the Apache License, Version 2.0 (the "License"); 5 | * you may not use this file except in compliance with the License. 6 | * You may obtain a copy of the License at 7 | * 8 | * http://www.apache.org/licenses/LICENSE-2.0 9 | * 10 | * Unless required by applicable law or agreed to in writing, software 11 | * distributed under the License is distributed on an "AS IS" BASIS, 12 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 | * See the License for the specific language governing permissions and 14 | * limitations under the License. 15 | */ 16 | 17 | package com.android.cts.tradefed.testtype; 18 | 19 | import java.util.Collection; 20 | 21 | 22 | /** 23 | * Interface for accessing tests from the CTS repository. 24 | */ 25 | public interface ITestPackageRepo { 26 | 27 | /** 28 | * Get a {@link TestPackageDef} given a uri 29 | * 30 | * @param testUri the string uris 31 | * @return a {@link TestPackageDef} or null if the uri cannot be found in repo 32 | */ 33 | public ITestPackageDef getTestPackage(String testUri); 34 | 35 | /** 36 | * Attempt to find the package uri for a given test class name 37 | * 38 | * @param testClassName the test class name 39 | * @return the package uri or null if the package cannot be found 40 | */ 41 | public String findPackageForTest(String testClassName); 42 | 43 | /** 44 | * Return a sorted {@link Collection} of all package names found in repo. 45 | */ 46 | public Collection getPackageNames(); 47 | 48 | } 49 | -------------------------------------------------------------------------------- /src/com/android/cts/tradefed/testtype/ITestPlan.java: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright (C) 2010 The Android Open Source Project 3 | * 4 | * Licensed under the Apache License, Version 2.0 (the "License"); 5 | * you may not use this file except in compliance with the License. 6 | * You may obtain a copy of the License at 7 | * 8 | * http://www.apache.org/licenses/LICENSE-2.0 9 | * 10 | * Unless required by applicable law or agreed to in writing, software 11 | * distributed under the License is distributed on an "AS IS" BASIS, 12 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 | * See the License for the specific language governing permissions and 14 | * limitations under the License. 15 | */ 16 | 17 | package com.android.cts.tradefed.testtype; 18 | 19 | import com.android.ddmlib.testrunner.TestIdentifier; 20 | import com.android.tradefed.util.xml.AbstractXmlParser.ParseException; 21 | 22 | import java.io.IOException; 23 | import java.io.InputStream; 24 | import java.io.OutputStream; 25 | import java.util.Collection; 26 | 27 | /** 28 | * Interface for accessing test plan data. 29 | */ 30 | public interface ITestPlan { 31 | 32 | /** 33 | * Populates the test plan data from given XML stream. 34 | * 35 | * @param xmlStream the {@link InputStream} that contains the test plan xml. 36 | */ 37 | public void parse(InputStream xmlStream) throws ParseException; 38 | 39 | /** 40 | * Gets the list of test uris contained in this plan. 41 | */ 42 | public Collection getTestUris(); 43 | 44 | /** 45 | * Gets the {@link TestFilter} that should be used to exclude tests from given package. 46 | */ 47 | public TestFilter getExcludedTestFilter(String uri); 48 | 49 | /** 50 | * Add a package to this test plan 51 | * @param uri 52 | */ 53 | public void addPackage(String uri); 54 | 55 | /** 56 | * Add a excluded test to this test plan 57 | * 58 | * @param uri the package uri 59 | * @param testToExclude the test to exclude for given package 60 | */ 61 | public void addExcludedTest(String uri, TestIdentifier testToExclude); 62 | 63 | /** 64 | * Adds the list of excluded tests for given package 65 | * 66 | * @param pkgUri 67 | * @param excludedTests 68 | */ 69 | public void addExcludedTests(String uri, Collection excludedTests); 70 | 71 | /** 72 | * Serialize the contents of this test plan. 73 | * 74 | * @param xmlOutStream the {@link OutputStream} to serialize test plan contents to 75 | * @throws IOException 76 | */ 77 | public void serialize(OutputStream xmlOutStream) throws IOException; 78 | 79 | /** 80 | * @return the test plan name 81 | */ 82 | public String getName(); 83 | 84 | } 85 | -------------------------------------------------------------------------------- /src/com/android/cts/tradefed/testtype/InstrumentationApkTest.java: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright (C) 2011 The Android Open Source Project 3 | * 4 | * Licensed under the Apache License, Version 2.0 (the "License"); 5 | * you may not use this file except in compliance with the License. 6 | * You may obtain a copy of the License at 7 | * 8 | * http://www.apache.org/licenses/LICENSE-2.0 9 | * 10 | * Unless required by applicable law or agreed to in writing, software 11 | * distributed under the License is distributed on an "AS IS" BASIS, 12 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 | * See the License for the specific language governing permissions and 14 | * limitations under the License. 15 | */ 16 | package com.android.cts.tradefed.testtype; 17 | 18 | import com.android.cts.tradefed.build.CtsBuildHelper; 19 | import com.android.ddmlib.Log; 20 | import com.android.tradefed.build.IBuildInfo; 21 | import com.android.tradefed.device.DeviceNotAvailableException; 22 | import com.android.tradefed.result.ITestInvocationListener; 23 | import com.android.tradefed.testtype.IBuildReceiver; 24 | import com.android.tradefed.testtype.InstrumentationTest; 25 | 26 | import java.io.FileNotFoundException; 27 | import java.util.ArrayList; 28 | import java.util.Collection; 29 | 30 | import junit.framework.Assert; 31 | 32 | /** 33 | * A {@link InstrumentationTest] that will install CTS apks before test execution, 34 | * and uninstall on execution completion. 35 | */ 36 | public class InstrumentationApkTest extends InstrumentationTest implements IBuildReceiver { 37 | 38 | private static final String LOG_TAG = "InstrumentationApkTest"; 39 | 40 | /** the file names of the CTS apks to install */ 41 | private Collection mInstallFileNames = new ArrayList(); 42 | private Collection mUninstallPackages = new ArrayList(); 43 | 44 | private CtsBuildHelper mCtsBuild = null; 45 | 46 | /** 47 | * {@inheritDoc} 48 | */ 49 | @Override 50 | public void setBuild(IBuildInfo build) { 51 | mCtsBuild = CtsBuildHelper.createBuildHelper(build); 52 | } 53 | 54 | /** 55 | * Add an apk to install. 56 | * 57 | * @param apkFileName the apk file name 58 | * @param packageName the apk's Android package name 59 | */ 60 | public void addInstallApk(String apkFileName, String packageName) { 61 | mInstallFileNames.add(apkFileName); 62 | mUninstallPackages.add(packageName); 63 | } 64 | 65 | /** 66 | * {@inheritDoc} 67 | */ 68 | @Override 69 | public void run(final ITestInvocationListener listener) 70 | throws DeviceNotAvailableException { 71 | Assert.assertNotNull("missing device", getDevice()); 72 | Assert.assertNotNull("missing build", mCtsBuild); 73 | 74 | for (String apkFileName : mInstallFileNames) { 75 | Log.d(LOG_TAG, String.format("Installing %s on %s", apkFileName, 76 | getDevice().getSerialNumber())); 77 | try { 78 | String installCode = getDevice().installPackage(mCtsBuild.getTestApp(apkFileName), 79 | true); 80 | Assert.assertNull(String.format("Failed to install %s on %s. Reason: %s", 81 | apkFileName, getDevice().getSerialNumber(), installCode), installCode); 82 | 83 | } catch (FileNotFoundException e) { 84 | Assert.fail(String.format("Could not find file %s", apkFileName)); 85 | } 86 | } 87 | super.run(listener); 88 | for (String packageName : mUninstallPackages) { 89 | Log.d(LOG_TAG, String.format("Uninstalling %s on %s", packageName, 90 | getDevice().getSerialNumber())); 91 | getDevice().uninstallPackage(packageName); 92 | } 93 | } 94 | } 95 | -------------------------------------------------------------------------------- /src/com/android/cts/tradefed/testtype/ResultFilter.java: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright (C) 2010 The Android Open Source Project 3 | * 4 | * Licensed under the Apache License, Version 2.0 (the "License"); 5 | * you may not use this file except in compliance with the License. 6 | * You may obtain a copy of the License at 7 | * 8 | * http://www.apache.org/licenses/LICENSE-2.0 9 | * 10 | * Unless required by applicable law or agreed to in writing, software 11 | * distributed under the License is distributed on an "AS IS" BASIS, 12 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 | * See the License for the specific language governing permissions and 14 | * limitations under the License. 15 | */ 16 | package com.android.cts.tradefed.testtype; 17 | 18 | import java.util.Collection; 19 | import java.util.HashMap; 20 | import java.util.HashSet; 21 | import java.util.LinkedHashMap; 22 | import java.util.LinkedHashSet; 23 | import java.util.List; 24 | import java.util.Map; 25 | 26 | import junit.framework.TestFailure; 27 | 28 | import com.android.cts.tradefed.testtype.CtsTest.TestPackage; 29 | import com.android.ddmlib.testrunner.TestIdentifier; 30 | import com.android.tradefed.log.LogUtil.CLog; 31 | import com.android.tradefed.result.ITestInvocationListener; 32 | import com.android.tradefed.result.ResultForwarder; 33 | 34 | /** 35 | * A {@link ITestInvocationListener} that filters test results based on the set of expected tests 36 | * in CTS test package xml files. 37 | *

38 | * It will only report test results for expected tests, and at end of invocation, will report the 39 | * set of expected tests that were not executed. 40 | */ 41 | class ResultFilter extends ResultForwarder { 42 | 43 | private final Map> mKnownTestsMap; 44 | private final Map> mRemainingTestsMap; 45 | private String mCurrentTestRun = null; 46 | 47 | /** 48 | * Create a {@link ResultFilter}. 49 | * 50 | * @param listener the real {@link ITestInvocationListener} to forward results to 51 | */ 52 | ResultFilter(ITestInvocationListener listener, List testPackages) { 53 | super(listener); 54 | 55 | mKnownTestsMap = new HashMap>(); 56 | // use LinkedHashMap for predictable test order 57 | mRemainingTestsMap = new LinkedHashMap>(); 58 | 59 | for (TestPackage testPkg : testPackages) { 60 | mKnownTestsMap.put(testPkg.getTestRunName(), new HashSet( 61 | testPkg.getKnownTests())); 62 | mRemainingTestsMap.put(testPkg.getTestRunName(), new LinkedHashSet( 63 | testPkg.getKnownTests())); 64 | } 65 | } 66 | 67 | /** 68 | * {@inheritDoc} 69 | */ 70 | @Override 71 | public void testRunStarted(String runName, int testCount) { 72 | super.testRunStarted(runName, testCount); 73 | mCurrentTestRun = runName; 74 | } 75 | 76 | /** 77 | * {@inheritDoc} 78 | */ 79 | @Override 80 | public void testStarted(TestIdentifier test) { 81 | if (isKnownTest(test)) { 82 | super.testStarted(test); 83 | } else { 84 | CLog.d("Skipping reporting unknown test %s", test); 85 | } 86 | } 87 | 88 | /** 89 | * {@inheritDoc} 90 | */ 91 | 92 | 93 | /** 94 | * {@inheritDoc} 95 | */ 96 | @Override 97 | public void testEnded(TestIdentifier test, Map testMetrics) { 98 | if (isKnownTest(test)) { 99 | super.testEnded(test, testMetrics); 100 | removeExecutedTest(test); 101 | } 102 | } 103 | 104 | /** 105 | * @param test 106 | * @return 107 | */ 108 | private boolean isKnownTest(TestIdentifier test) { 109 | if (mCurrentTestRun != null && mKnownTestsMap.containsKey(mCurrentTestRun)) { 110 | return mKnownTestsMap.get(mCurrentTestRun).contains(test); 111 | } 112 | return false; 113 | } 114 | 115 | /** 116 | * Remove given test from the 'remaining tests' data structure. 117 | * @param test 118 | */ 119 | private void removeExecutedTest(TestIdentifier test) { 120 | if (mCurrentTestRun != null && mRemainingTestsMap.containsKey(mCurrentTestRun)) { 121 | mRemainingTestsMap.get(mCurrentTestRun).remove(test); 122 | } 123 | } 124 | 125 | /** 126 | * Report the set of expected tests that were not executed 127 | */ 128 | public void reportUnexecutedTests() { 129 | for (Map.Entry> entry : mRemainingTestsMap.entrySet()) { 130 | if (!entry.getValue().isEmpty()) { 131 | super.testRunStarted(entry.getKey(), entry.getValue().size()); 132 | for (TestIdentifier test : entry.getValue()) { 133 | // an unexecuted test is currently reported as a 'testStarted' event without a 134 | // 'testEnded'. TODO: consider adding an explict API for reporting an unexecuted 135 | // test 136 | super.testStarted(test); 137 | } 138 | super.testRunEnded(0, new HashMap()); 139 | } 140 | } 141 | } 142 | } 143 | -------------------------------------------------------------------------------- /src/com/android/cts/tradefed/testtype/TestFilter.java: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright (C) 2011 The Android Open Source Project 3 | * 4 | * Licensed under the Apache License, Version 2.0 (the "License"); 5 | * you may not use this file except in compliance with the License. 6 | * You may obtain a copy of the License at 7 | * 8 | * http://www.apache.org/licenses/LICENSE-2.0 9 | * 10 | * Unless required by applicable law or agreed to in writing, software 11 | * distributed under the License is distributed on an "AS IS" BASIS, 12 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 | * See the License for the specific language governing permissions and 14 | * limitations under the License. 15 | */ 16 | 17 | package com.android.cts.tradefed.testtype; 18 | 19 | import com.android.ddmlib.testrunner.TestIdentifier; 20 | 21 | import java.util.ArrayList; 22 | import java.util.Collection; 23 | import java.util.Collections; 24 | import java.util.Comparator; 25 | import java.util.HashSet; 26 | import java.util.List; 27 | import java.util.Set; 28 | 29 | /** 30 | * Filter for {@link TestIdentifier}s. 31 | */ 32 | public class TestFilter { 33 | 34 | private final Set mExcludedClasses; 35 | private final Set mExcludedTests; 36 | private String mIncludedClass = null; 37 | private String mIncludedMethod = null; 38 | 39 | /** 40 | * Creates a {@link TestFilter} 41 | */ 42 | public TestFilter() { 43 | mExcludedClasses = new HashSet(); 44 | mExcludedTests = new HashSet(); 45 | } 46 | 47 | /** 48 | * Adds a test class to the filter. 49 | *

50 | * All tests in this class should be filtered. 51 | */ 52 | public void addExcludedClass(String className) { 53 | mExcludedClasses.add(className); 54 | } 55 | 56 | /** 57 | * Adds a test class to the filter. All tests in this class should be excluded. 58 | */ 59 | public void addExcludedTest(TestIdentifier test) { 60 | mExcludedTests.add(test); 61 | } 62 | 63 | /** 64 | * Get the test classes to exclude. 65 | *

66 | * Exposed for unit testing 67 | */ 68 | Set getExcludedClasses() { 69 | return mExcludedClasses; 70 | } 71 | 72 | /** 73 | * Get the tests to exclude. 74 | *

75 | * Exposed for unit testing 76 | */ 77 | Set getExcludedTests() { 78 | return mExcludedTests; 79 | } 80 | 81 | /** 82 | * Sets the class name and optionally method that should pass this filter. If non-null, all 83 | * other tests will be excluded. 84 | * 85 | * @param className the test class name to exclusively include 86 | * @param method the test method name to exclusively include 87 | */ 88 | public void setTestInclusion(String className, String method) { 89 | mIncludedClass = className; 90 | mIncludedMethod = method; 91 | } 92 | 93 | /** 94 | * Filter the list of tests based on rules in this filter 95 | * 96 | * @param tests the list of tests to filter 97 | * @return a new sorted list of tests that passed the filter 98 | */ 99 | public Collection filter(Collection tests) { 100 | List filteredTests = new ArrayList(tests.size()); 101 | for (TestIdentifier test : tests) { 102 | if (mIncludedClass != null && !test.getClassName().equals(mIncludedClass)) { 103 | // skip 104 | continue; 105 | } 106 | if (mIncludedMethod != null && !test.getTestName().equals(mIncludedMethod)) { 107 | // skip 108 | continue; 109 | } 110 | if (mExcludedClasses.contains(test.getClassName())) { 111 | // skip 112 | continue; 113 | } 114 | if (mExcludedTests.contains(test)) { 115 | // skip 116 | continue; 117 | } 118 | filteredTests.add(test); 119 | } 120 | Collections.sort(filteredTests, new TestIdComparator()); 121 | return filteredTests; 122 | } 123 | 124 | /** 125 | * Return true if there are exclusions rules defined. 126 | */ 127 | public boolean hasExclusion() { 128 | return !mExcludedClasses.isEmpty() || !mExcludedTests.isEmpty(); 129 | } 130 | 131 | /** 132 | * A {@link Comparator} for {@link TestIdentifier} that compares using 133 | * {@link TestIdentifier#toString()} 134 | */ 135 | private class TestIdComparator implements Comparator { 136 | 137 | @Override 138 | public int compare(TestIdentifier o1, TestIdentifier o2) { 139 | return o1.toString().compareTo(o2.toString()); 140 | } 141 | } 142 | } 143 | -------------------------------------------------------------------------------- /src/com/android/cts/tradefed/testtype/TestPackageRepo.java: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright (C) 2010 The Android Open Source Project 3 | * 4 | * Licensed under the Apache License, Version 2.0 (the "License"); 5 | * you may not use this file except in compliance with the License. 6 | * You may obtain a copy of the License at 7 | * 8 | * http://www.apache.org/licenses/LICENSE-2.0 9 | * 10 | * Unless required by applicable law or agreed to in writing, software 11 | * distributed under the License is distributed on an "AS IS" BASIS, 12 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 | * See the License for the specific language governing permissions and 14 | * limitations under the License. 15 | */ 16 | package com.android.cts.tradefed.testtype; 17 | 18 | import com.android.ddmlib.Log; 19 | import com.android.tradefed.util.xml.AbstractXmlParser.ParseException; 20 | 21 | import java.io.BufferedInputStream; 22 | import java.io.File; 23 | import java.io.FileInputStream; 24 | import java.io.FileNotFoundException; 25 | import java.io.FilenameFilter; 26 | import java.io.InputStream; 27 | import java.util.ArrayList; 28 | import java.util.Collection; 29 | import java.util.Collections; 30 | import java.util.Hashtable; 31 | import java.util.List; 32 | import java.util.Map; 33 | 34 | /** 35 | * Retrieves CTS test package definitions from the repository. 36 | */ 37 | public class TestPackageRepo implements ITestPackageRepo { 38 | 39 | private static final String LOG_TAG = "TestCaseRepo"; 40 | 41 | private final File mTestCaseDir; 42 | 43 | /** mapping of uri to test definition */ 44 | private final Map mTestMap; 45 | 46 | private final boolean mIncludeKnownFailures; 47 | 48 | /** 49 | * Creates a {@link TestPackageRepo}, initialized from provided repo files 50 | * 51 | * @param testCaseDir directory containing all test case definition xml and build files 52 | */ 53 | public TestPackageRepo(File testCaseDir, boolean includeKnownFailures) { 54 | mTestCaseDir = testCaseDir; 55 | mTestMap = new Hashtable(); 56 | mIncludeKnownFailures = includeKnownFailures; 57 | parse(mTestCaseDir); 58 | } 59 | 60 | /** 61 | * Builds mTestMap based on directory contents 62 | */ 63 | private void parse(File dir) { 64 | File[] xmlFiles = dir.listFiles(new XmlFilter()); 65 | for (File xmlFile : xmlFiles) { 66 | parseTestFromXml(xmlFile); 67 | } 68 | } 69 | 70 | private void parseTestFromXml(File xmlFile) { 71 | TestPackageXmlParser parser = new TestPackageXmlParser(mIncludeKnownFailures); 72 | try { 73 | parser.parse(createStreamFromFile(xmlFile)); 74 | TestPackageDef def = parser.getTestPackageDef(); 75 | if (def != null) { 76 | mTestMap.put(def.getUri(), def); 77 | } else { 78 | Log.w(LOG_TAG, String.format("Could not find test package info in xml file %s", 79 | xmlFile.getAbsolutePath())); 80 | } 81 | } catch (FileNotFoundException e) { 82 | Log.e(LOG_TAG, String.format("Could not find test case xml file %s", 83 | xmlFile.getAbsolutePath())); 84 | Log.e(LOG_TAG, e); 85 | } catch (ParseException e) { 86 | Log.e(LOG_TAG, String.format("Failed to parse test case xml file %s", 87 | xmlFile.getAbsolutePath())); 88 | Log.e(LOG_TAG, e); 89 | } 90 | } 91 | 92 | /** 93 | * Helper method to create a stream to read data from given file 94 | *

95 | * Exposed for unit testing 96 | * 97 | * @param xmlFile 98 | * @return stream to read data 99 | * 100 | */ 101 | InputStream createStreamFromFile(File xmlFile) throws FileNotFoundException { 102 | return new BufferedInputStream(new FileInputStream(xmlFile)); 103 | } 104 | 105 | private static class XmlFilter implements FilenameFilter { 106 | 107 | /** 108 | * {@inheritDoc} 109 | */ 110 | @Override 111 | public boolean accept(File dir, String name) { 112 | return name.endsWith(".xml"); 113 | } 114 | } 115 | 116 | /** 117 | * {@inheritDoc} 118 | */ 119 | @Override 120 | public ITestPackageDef getTestPackage(String testUri) { 121 | return mTestMap.get(testUri); 122 | } 123 | 124 | /** 125 | * {@inheritDoc} 126 | */ 127 | @Override 128 | public String findPackageForTest(String testClassName) { 129 | for (Map.Entry entry : mTestMap.entrySet()) { 130 | if (entry.getValue().isKnownTestClass(testClassName)) { 131 | return entry.getKey(); 132 | } 133 | } 134 | return null; 135 | } 136 | 137 | /** 138 | * @return list of all package names found in repo 139 | */ 140 | @Override 141 | public Collection getPackageNames() { 142 | List packageNames = new ArrayList(); 143 | packageNames.addAll(mTestMap.keySet()); 144 | Collections.sort(packageNames); 145 | return packageNames; 146 | } 147 | } 148 | -------------------------------------------------------------------------------- /src/com/android/cts/tradefed/testtype/TestPackageXmlParser.java: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright (C) 2010 The Android Open Source Project 3 | * 4 | * Licensed under the Apache License, Version 2.0 (the "License"); 5 | * you may not use this file except in compliance with the License. 6 | * You may obtain a copy of the License at 7 | * 8 | * http://www.apache.org/licenses/LICENSE-2.0 9 | * 10 | * Unless required by applicable law or agreed to in writing, software 11 | * distributed under the License is distributed on an "AS IS" BASIS, 12 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 | * See the License for the specific language governing permissions and 14 | * limitations under the License. 15 | */ 16 | package com.android.cts.tradefed.testtype; 17 | 18 | import com.android.ddmlib.Log; 19 | import com.android.ddmlib.testrunner.TestIdentifier; 20 | import com.android.tradefed.util.xml.AbstractXmlParser; 21 | 22 | import org.xml.sax.Attributes; 23 | import org.xml.sax.helpers.DefaultHandler; 24 | 25 | import java.util.Iterator; 26 | import java.util.Stack; 27 | 28 | /** 29 | * Parser for CTS test case XML. 30 | *

31 | * Dumb parser that just retrieves data from in the test case xml and stuff it into a 32 | * {@link TestPackageDef}. Currently performs limited error checking. 33 | */ 34 | public class TestPackageXmlParser extends AbstractXmlParser { 35 | 36 | private static final String LOG_TAG = "TestPackageXmlParser"; 37 | 38 | private final boolean mIncludeKnownFailures; 39 | 40 | private TestPackageDef mPackageDef; 41 | 42 | public TestPackageXmlParser(boolean includeKnownFailures) { 43 | mIncludeKnownFailures = includeKnownFailures; 44 | } 45 | 46 | /** 47 | * SAX callback object. Handles parsing data from the xml tags. 48 | *

49 | * Expected structure: 50 | * 51 | * 52 | * 53 | * 54 | */ 55 | private class TestPackageHandler extends DefaultHandler { 56 | 57 | private static final String TEST_PACKAGE_TAG = "TestPackage"; 58 | private static final String TEST_SUITE_TAG = "TestSuite"; 59 | private static final String TEST_CASE_TAG = "TestCase"; 60 | private static final String TEST_TAG = "Test"; 61 | 62 | // holds current class name segments 63 | private Stack mClassNameStack = new Stack(); 64 | 65 | @Override 66 | public void startElement(String uri, String localName, String name, Attributes attributes) { 67 | if (TEST_PACKAGE_TAG.equals(localName)) { 68 | // appPackageName is used as the uri 69 | final String entryUriValue = attributes.getValue("appPackageName"); 70 | final String testPackageNameSpace = attributes.getValue("appNameSpace"); 71 | final String packageName = attributes.getValue("name"); 72 | final String runnerName = attributes.getValue("runner"); 73 | final String jarPath = attributes.getValue("jarPath"); 74 | final String signatureCheck = attributes.getValue("signatureCheck"); 75 | final String javaPackageFilter = attributes.getValue("javaPackageFilter"); 76 | final String targetBinaryName = attributes.getValue("targetBinaryName"); 77 | final String targetNameSpace = attributes.getValue("targetNameSpace"); 78 | 79 | mPackageDef = new TestPackageDef(); 80 | mPackageDef.setUri(entryUriValue); 81 | mPackageDef.setAppNameSpace(testPackageNameSpace); 82 | mPackageDef.setName(packageName); 83 | mPackageDef.setRunner(runnerName); 84 | mPackageDef.setTestType(getTestType(attributes)); 85 | mPackageDef.setJarPath(jarPath); 86 | mPackageDef.setIsSignatureCheck(parseBoolean(signatureCheck)); 87 | mPackageDef.setTestPackageName(javaPackageFilter); 88 | mPackageDef.setTargetBinaryName(targetBinaryName); 89 | mPackageDef.setTargetNameSpace(targetNameSpace); 90 | 91 | // reset the class name 92 | mClassNameStack = new Stack(); 93 | } else if (TEST_SUITE_TAG.equals(localName)) { 94 | String packageSegment = attributes.getValue("name"); 95 | if (packageSegment != null) { 96 | mClassNameStack.push(packageSegment); 97 | } else { 98 | Log.e(LOG_TAG, String.format("Invalid XML: missing 'name' attribute for '%s'", 99 | TEST_SUITE_TAG)); 100 | } 101 | } else if (TEST_CASE_TAG.equals(localName)) { 102 | String classSegment = attributes.getValue("name"); 103 | if (classSegment != null) { 104 | mClassNameStack.push(classSegment); 105 | } else { 106 | Log.e(LOG_TAG, String.format("Invalid XML: missing 'name' attribute for '%s'", 107 | TEST_CASE_TAG)); 108 | } 109 | } else if (TEST_TAG.equals(localName)) { 110 | String methodName = attributes.getValue("name"); 111 | if (mPackageDef == null) { 112 | Log.e(LOG_TAG, String.format( 113 | "Invalid XML: encountered a '%s' tag not enclosed within a '%s' tag", 114 | TEST_TAG, TEST_PACKAGE_TAG)); 115 | } else if (methodName == null) { 116 | Log.e(LOG_TAG, String.format("Invalid XML: missing 'name' attribute for '%s'", 117 | TEST_TAG)); 118 | } else { 119 | // build class name from package segments 120 | StringBuilder classNameBuilder = new StringBuilder(); 121 | for (Iterator iter = mClassNameStack.iterator(); iter.hasNext(); ) { 122 | classNameBuilder.append(iter.next()); 123 | if (iter.hasNext()) { 124 | classNameBuilder.append("."); 125 | } 126 | } 127 | int timeout = -1; 128 | String timeoutStr = attributes.getValue("timeout"); 129 | if (timeoutStr != null) { 130 | timeout = Integer.parseInt(timeoutStr); 131 | } 132 | TestIdentifier testId = new TestIdentifier(classNameBuilder.toString(), 133 | methodName); 134 | boolean isKnownFailure = "failure".equals(attributes.getValue("expectation")); 135 | if (!isKnownFailure || mIncludeKnownFailures) { 136 | mPackageDef.addTest(testId, timeout); 137 | } 138 | } 139 | } 140 | 141 | } 142 | 143 | private String getTestType(Attributes attributes) { 144 | if (parseBoolean(attributes.getValue("hostSideOnly"))) { 145 | return TestPackageDef.HOST_SIDE_ONLY_TEST; 146 | } else if (parseBoolean(attributes.getValue("vmHostTest"))) { 147 | return TestPackageDef.VM_HOST_TEST; 148 | } else { 149 | return attributes.getValue("testType"); 150 | } 151 | } 152 | 153 | @Override 154 | public void endElement (String uri, String localName, String qName) { 155 | if (TEST_SUITE_TAG.equals(localName) || TEST_CASE_TAG.equals(localName)) { 156 | mClassNameStack.pop(); 157 | } 158 | } 159 | 160 | /** 161 | * Parse a boolean attribute value 162 | */ 163 | private boolean parseBoolean(final String stringValue) { 164 | return stringValue != null && 165 | Boolean.parseBoolean(stringValue); 166 | } 167 | } 168 | 169 | /** 170 | * {@inheritDoc} 171 | */ 172 | @Override 173 | protected DefaultHandler createXmlHandler() { 174 | return new TestPackageHandler(); 175 | } 176 | 177 | /** 178 | * @returns the {@link TestPackageDef} containing data parsed from xml or null if 179 | * xml did not contain the correct information. 180 | */ 181 | public TestPackageDef getTestPackageDef() { 182 | return mPackageDef; 183 | } 184 | } 185 | -------------------------------------------------------------------------------- /src/com/android/cts/tradefed/testtype/TestPlan.java: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright (C) 2010 The Android Open Source Project 3 | * 4 | * Licensed under the Apache License, Version 2.0 (the "License"); 5 | * you may not use this file except in compliance with the License. 6 | * You may obtain a copy of the License at 7 | * 8 | * http://www.apache.org/licenses/LICENSE-2.0 9 | * 10 | * Unless required by applicable law or agreed to in writing, software 11 | * distributed under the License is distributed on an "AS IS" BASIS, 12 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 | * See the License for the specific language governing permissions and 14 | * limitations under the License. 15 | */ 16 | 17 | package com.android.cts.tradefed.testtype; 18 | 19 | import com.android.ddmlib.testrunner.TestIdentifier; 20 | import com.android.tradefed.util.ArrayUtil; 21 | import com.android.tradefed.util.xml.AbstractXmlParser; 22 | 23 | import org.kxml2.io.KXmlSerializer; 24 | import org.xml.sax.Attributes; 25 | import org.xml.sax.SAXException; 26 | import org.xml.sax.helpers.DefaultHandler; 27 | 28 | import java.io.IOException; 29 | import java.io.OutputStream; 30 | import java.util.ArrayList; 31 | import java.util.Collection; 32 | import java.util.LinkedHashMap; 33 | import java.util.List; 34 | import java.util.Map; 35 | 36 | /** 37 | * Implementation of {@link TestPlan}. 38 | */ 39 | public class TestPlan extends AbstractXmlParser implements ITestPlan { 40 | 41 | /** 42 | * Map of uri names found in plan, and their excluded tests 43 | */ 44 | private Map mUriExcludedTestsMap; 45 | 46 | private static final String ENTRY_TAG = "Entry"; 47 | private static final String TEST_DELIM = ";"; 48 | private static final String METHOD_DELIM = "#"; 49 | private static final String EXCLUDE_ATTR = "exclude"; 50 | private static final String URI_ATTR = "uri"; 51 | 52 | private final String mName; 53 | 54 | /** 55 | * SAX callback object. Handles parsing data from the xml tags. 56 | */ 57 | private class EntryHandler extends DefaultHandler { 58 | 59 | @Override 60 | public void startElement(String uri, String localName, String name, Attributes attributes) 61 | throws SAXException { 62 | if (ENTRY_TAG.equals(localName)) { 63 | final String entryUriValue = attributes.getValue(URI_ATTR); 64 | TestFilter filter = parseExcludedTests(attributes.getValue(EXCLUDE_ATTR)); 65 | mUriExcludedTestsMap.put(entryUriValue, filter); 66 | } 67 | } 68 | 69 | /** 70 | * Parse the semi colon separated list of tests to exclude. 71 | *

72 | * Expected format: 73 | * testClassName[#testMethodName][;testClassName2...] 74 | * 75 | * @param excludedString the excluded string list 76 | * @return 77 | */ 78 | private TestFilter parseExcludedTests(String excludedString) { 79 | TestFilter filter = new TestFilter(); 80 | if (excludedString != null) { 81 | String[] testStrings = excludedString.split(TEST_DELIM); 82 | for (String testString : testStrings) { 83 | String[] classMethodPair = testString.split(METHOD_DELIM); 84 | if (classMethodPair.length == 2) { 85 | filter.addExcludedTest(new TestIdentifier(classMethodPair[0], 86 | classMethodPair[1])); 87 | } else { 88 | filter.addExcludedClass(testString); 89 | } 90 | } 91 | } 92 | return filter; 93 | } 94 | } 95 | 96 | public TestPlan(String name) { 97 | mName = name; 98 | // Uses a LinkedHashMap to have predictable iteration order 99 | mUriExcludedTestsMap = new LinkedHashMap(); 100 | } 101 | 102 | /** 103 | * {@inheritDoc} 104 | */ 105 | @Override 106 | public String getName() { 107 | return mName; 108 | } 109 | 110 | /** 111 | * {@inheritDoc} 112 | */ 113 | @Override 114 | public Collection getTestUris() { 115 | return mUriExcludedTestsMap.keySet(); 116 | } 117 | 118 | /** 119 | * {@inheritDoc} 120 | */ 121 | @Override 122 | public TestFilter getExcludedTestFilter(String uri) { 123 | return mUriExcludedTestsMap.get(uri); 124 | } 125 | 126 | /** 127 | * {@inheritDoc} 128 | */ 129 | @Override 130 | public void addPackage(String uri) { 131 | mUriExcludedTestsMap.put(uri, new TestFilter()); 132 | } 133 | 134 | /** 135 | * {@inheritDoc} 136 | */ 137 | @Override 138 | protected DefaultHandler createXmlHandler() { 139 | return new EntryHandler(); 140 | } 141 | 142 | /** 143 | * {@inheritDoc} 144 | */ 145 | @Override 146 | public void addExcludedTest(String uri, TestIdentifier testToExclude) { 147 | TestFilter filter = mUriExcludedTestsMap.get(uri); 148 | if (filter != null) { 149 | filter.addExcludedTest(testToExclude); 150 | } else { 151 | throw new IllegalArgumentException(String.format("Could not find package %s", uri)); 152 | } 153 | } 154 | 155 | /** 156 | * {@inheritDoc} 157 | */ 158 | @Override 159 | public void addExcludedTests(String uri, Collection excludedTests) { 160 | TestFilter filter = mUriExcludedTestsMap.get(uri); 161 | if (filter != null) { 162 | filter.getExcludedTests().addAll(excludedTests); 163 | } else { 164 | throw new IllegalArgumentException(String.format("Could not find package %s", uri)); 165 | } 166 | } 167 | 168 | /** 169 | * {@inheritDoc} 170 | */ 171 | @Override 172 | public void serialize(OutputStream stream) throws IOException { 173 | KXmlSerializer serializer = new KXmlSerializer(); 174 | serializer.setOutput(stream, "UTF-8"); 175 | serializer.startDocument("UTF-8", false); 176 | serializer.setFeature( 177 | "http://xmlpull.org/v1/doc/features.html#indent-output", true); 178 | serializer.startTag(null, "TestPlan"); 179 | serializer.attribute(null, "version", "1.0"); 180 | for (Map.Entry packageEntry : mUriExcludedTestsMap.entrySet()) { 181 | serializer.startTag(null, ENTRY_TAG); 182 | serializer.attribute(null, "uri", packageEntry.getKey()); 183 | serializeFilter(serializer, packageEntry.getValue()); 184 | serializer.endTag(null, ENTRY_TAG); 185 | } 186 | serializer.endTag(null, "TestPlan"); 187 | serializer.endDocument(); 188 | } 189 | 190 | /** 191 | * Adds an xml attribute containing {@link TestFilter} contents. 192 | *

193 | * If {@link TestFilter} is empty, no data will be outputted. 194 | * 195 | * @param serializer 196 | * @param value 197 | * @throws IOException 198 | */ 199 | private void serializeFilter(KXmlSerializer serializer, TestFilter testFilter) 200 | throws IOException { 201 | if (!testFilter.hasExclusion()) { 202 | return; 203 | } 204 | List exclusionStrings = new ArrayList(); 205 | exclusionStrings.addAll(testFilter.getExcludedClasses()); 206 | for (TestIdentifier test : testFilter.getExcludedTests()) { 207 | // TODO: this relies on TestIdentifier.toString() using METHOD_DELIM. 208 | exclusionStrings.add(test.toString()); 209 | } 210 | String exclusionAttrValue = ArrayUtil.join(TEST_DELIM, exclusionStrings); 211 | serializer.attribute(null, EXCLUDE_ATTR, exclusionAttrValue); 212 | } 213 | } 214 | -------------------------------------------------------------------------------- /src/com/android/cts/tradefed/testtype/TestTimeoutException.java: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright (C) 2010 The Android Open Source Project 3 | * 4 | * Licensed under the Apache License, Version 2.0 (the "License"); 5 | * you may not use this file except in compliance with the License. 6 | * You may obtain a copy of the License at 7 | * 8 | * http://www.apache.org/licenses/LICENSE-2.0 9 | * 10 | * Unless required by applicable law or agreed to in writing, software 11 | * distributed under the License is distributed on an "AS IS" BASIS, 12 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 | * See the License for the specific language governing permissions and 14 | * limitations under the License. 15 | */ 16 | package com.android.cts.tradefed.testtype; 17 | 18 | /** 19 | * An exception that indicates a test has timed out. 20 | * TODO: consider moving this to tradefed proper 21 | */ 22 | public class TestTimeoutException extends Exception { 23 | 24 | private static final long serialVersionUID = 941691916057121118L; 25 | 26 | } 27 | -------------------------------------------------------------------------------- /src/com/android/cts/tradefed/testtype/UiAutomatorJarTest.java: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright (C) 2011 The Android Open Source Project 3 | * 4 | * Licensed under the Apache License, Version 2.0 (the "License"); 5 | * you may not use this file except in compliance with the License. 6 | * You may obtain a copy of the License at 7 | * 8 | * http://www.apache.org/licenses/LICENSE-2.0 9 | * 10 | * Unless required by applicable law or agreed to in writing, software 11 | * distributed under the License is distributed on an "AS IS" BASIS, 12 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 | * See the License for the specific language governing permissions and 14 | * limitations under the License. 15 | */ 16 | package com.android.cts.tradefed.testtype; 17 | 18 | import com.android.cts.tradefed.build.CtsBuildHelper; 19 | import com.android.tradefed.build.IBuildInfo; 20 | import com.android.tradefed.device.DeviceNotAvailableException; 21 | import com.android.tradefed.log.LogUtil.CLog; 22 | import com.android.tradefed.result.ITestInvocationListener; 23 | import com.android.tradefed.testtype.IBuildReceiver; 24 | import com.android.tradefed.testtype.UiAutomatorTest; 25 | 26 | import java.io.FileNotFoundException; 27 | import java.util.Arrays; 28 | 29 | import junit.framework.Assert; 30 | 31 | /** 32 | * A {@link UiAutomatorTest} that will install a uiautomator jar before test 33 | * execution, and uninstall on execution completion. 34 | */ 35 | public class UiAutomatorJarTest extends UiAutomatorTest implements IBuildReceiver { 36 | 37 | // TODO: expose this in parent 38 | private static final String SHELL_EXE_BASE = "/data/local/tmp/"; 39 | 40 | /** the file names of the CTS jar to install */ 41 | private String mTestJarFileName; 42 | 43 | private CtsBuildHelper mCtsBuild = null; 44 | 45 | /** 46 | * {@inheritDoc} 47 | */ 48 | @Override 49 | public void setBuild(IBuildInfo build) { 50 | mCtsBuild = CtsBuildHelper.createBuildHelper(build); 51 | } 52 | 53 | /** 54 | * Setter for CTS build files needed to perform the test. 55 | * 56 | * @param testJarName the file name of the jar containing the uiautomator tests 57 | */ 58 | public void setInstallArtifacts(String testJarName) { 59 | mTestJarFileName = testJarName; 60 | } 61 | 62 | /** 63 | * {@inheritDoc} 64 | */ 65 | @Override 66 | public void run(final ITestInvocationListener listener) 67 | throws DeviceNotAvailableException { 68 | Assert.assertNotNull("missing device", getDevice()); 69 | Assert.assertNotNull("missing build", mCtsBuild); 70 | Assert.assertNotNull("missing jar to install", mTestJarFileName); 71 | 72 | installJar(); 73 | 74 | super.run(listener); 75 | 76 | uninstallJar(); 77 | } 78 | 79 | private void installJar() throws DeviceNotAvailableException { 80 | CLog.d("Installing %s on %s", mTestJarFileName, getDevice().getSerialNumber()); 81 | String fullJarPath = String.format("%s%s", SHELL_EXE_BASE, mTestJarFileName); 82 | try { 83 | boolean result = getDevice().pushFile(mCtsBuild.getTestApp(mTestJarFileName), 84 | fullJarPath); 85 | Assert.assertTrue(String.format("Failed to push file to %s", fullJarPath), result); 86 | setTestJarPaths(Arrays.asList(fullJarPath)); 87 | } catch (FileNotFoundException e) { 88 | Assert.fail(String.format("Could not find file %s", mTestJarFileName)); 89 | } 90 | } 91 | 92 | private void uninstallJar() throws DeviceNotAvailableException { 93 | CLog.d("Uninstalling %s on %s", mTestJarFileName, getDevice().getSerialNumber()); 94 | String fullJarPath = String.format("%s%s", SHELL_EXE_BASE, mTestJarFileName); 95 | getDevice().executeShellCommand(String.format("rm %s", fullJarPath)); 96 | } 97 | } 98 | -------------------------------------------------------------------------------- /src/com/android/cts/tradefed/testtype/VMHostTest.java: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright (C) 2011 The Android Open Source Project 3 | * 4 | * Licensed under the Apache License, Version 2.0 (the "License"); 5 | * you may not use this file except in compliance with the License. 6 | * You may obtain a copy of the License at 7 | * 8 | * http://www.apache.org/licenses/LICENSE-2.0 9 | * 10 | * Unless required by applicable law or agreed to in writing, software 11 | * distributed under the License is distributed on an "AS IS" BASIS, 12 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 | * See the License for the specific language governing permissions and 14 | * limitations under the License. 15 | */ 16 | package com.android.cts.tradefed.testtype; 17 | 18 | import com.android.cts.tradefed.build.CtsBuildHelper; 19 | import com.android.tradefed.device.DeviceNotAvailableException; 20 | import com.android.tradefed.device.ITestDevice; 21 | import com.android.tradefed.log.LogUtil.CLog; 22 | import com.android.tradefed.result.ITestInvocationListener; 23 | import com.android.tradefed.util.FileUtil; 24 | 25 | import java.io.File; 26 | import java.io.IOException; 27 | import java.util.zip.ZipFile; 28 | 29 | /** 30 | * A wrapper around {@link JarHostTest} that includes additional device setup and clean up. 31 | * 32 | */ 33 | public class VMHostTest extends JarHostTest { 34 | 35 | private static final String VM_TEST_TEMP_DIR = "/data/local/tmp/vm-tests"; 36 | 37 | /** 38 | * {@inheritDoc} 39 | */ 40 | @Override 41 | @SuppressWarnings("unchecked") 42 | public void run(ITestInvocationListener listener) throws DeviceNotAvailableException { 43 | if (!installVmPrereqs(getDevice(), getBuildHelper())) { 44 | throw new RuntimeException(String.format( 45 | "Failed to install vm-tests prereqs on device %s", 46 | getDevice().getSerialNumber())); 47 | } 48 | super.run(listener); 49 | cleanupDeviceFiles(getDevice()); 50 | } 51 | 52 | /** 53 | * Install pre-requisite jars for running vm-tests, creates temp directories for test. 54 | * 55 | * @param device the {@link ITestDevice} 56 | * @param ctsBuild the {@link CtsBuildHelper} 57 | * @throws DeviceNotAvailableException 58 | * @return true if test jar files are extracted and pushed to device successfully 59 | */ 60 | private boolean installVmPrereqs(ITestDevice device, CtsBuildHelper ctsBuild) 61 | throws DeviceNotAvailableException { 62 | cleanupDeviceFiles(device); 63 | // Creates temp directory recursively. We also need to create the dalvik-cache directory 64 | // which is used by the dalvikvm to optimize things. Without the dalvik-cache, there will be 65 | // a sigsev thrown by the vm. 66 | CLog.d("Creating device temp directory, including dalvik-cache."); 67 | createRemoteDir(device, VM_TEST_TEMP_DIR + "/dalvik-cache" ); 68 | try { 69 | File localTmpDir = FileUtil.createTempDir("cts-vm", new File("/tmp/")); 70 | CLog.d("Creating host temp dir %s", localTmpDir.getPath()); 71 | File jarFile = new File(ctsBuild.getTestCasesDir(), getJarFileName()); 72 | if (!jarFile.exists()) { 73 | CLog.e("Missing jar file %s", jarFile.getPath()); 74 | return false; 75 | } 76 | CLog.d("Extracting jar file %s to host temp directory %s.", 77 | jarFile.getPath(), localTmpDir.getPath()); 78 | ZipFile zipFile = new ZipFile(jarFile); 79 | FileUtil.extractZip(zipFile, localTmpDir); 80 | File localTestTmpDir = new File(localTmpDir, "tests"); 81 | CLog.d("Syncing host dir %s to device dir %s", 82 | localTestTmpDir.getPath(), VM_TEST_TEMP_DIR); 83 | if (!device.pushDir(localTestTmpDir, VM_TEST_TEMP_DIR)) { 84 | CLog.e("Failed to push vm test files"); 85 | return false; 86 | } 87 | CLog.d("Cleaning up host temp dir %s", localTmpDir.getPath()); 88 | FileUtil.recursiveDelete(localTmpDir); 89 | } catch (IOException e) { 90 | CLog.e("Failed to extract jar file %s and sync it to device %s.", 91 | getJarFileName(), device.getSerialNumber()); 92 | return false; 93 | } 94 | return true; 95 | } 96 | 97 | /** 98 | * Removes temporary file directory from device 99 | * 100 | * @param device 101 | * @throws DeviceNotAvailableException 102 | */ 103 | private void cleanupDeviceFiles(ITestDevice device) throws DeviceNotAvailableException { 104 | if (device.doesFileExist(VM_TEST_TEMP_DIR)) { 105 | CLog.d("Removing device's temp dir %s from previous runs.", VM_TEST_TEMP_DIR); 106 | device.executeShellCommand(String.format("rm -r %s", VM_TEST_TEMP_DIR)); 107 | } 108 | } 109 | 110 | /** 111 | * Creates the file directory recursively in the device. 112 | * 113 | * @param device the {@link ITestDevice} 114 | * @param remoteFilePath the absolute path. 115 | * @throws DeviceNotAvailableException 116 | */ 117 | private void createRemoteDir(ITestDevice device, String remoteFilePath) 118 | throws DeviceNotAvailableException { 119 | if (device.doesFileExist(remoteFilePath)) { 120 | return; 121 | } 122 | File f = new File(remoteFilePath); 123 | String parentPath = f.getParent(); 124 | if (parentPath != null) { 125 | createRemoteDir(device, parentPath); 126 | } 127 | device.executeShellCommand(String.format("mkdir %s", remoteFilePath)); 128 | } 129 | } 130 | -------------------------------------------------------------------------------- /src/com/android/cts/tradefed/testtype/WrappedGTest.java: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright 2012 The Android Open Source Project 3 | * 4 | * Licensed under the Apache License, Version 2.0 (the "License"); 5 | * you may not use this file except in compliance with the License. 6 | * You may obtain a copy of the License at 7 | * 8 | * http://www.apache.org/licenses/LICENSE-2.0 9 | * 10 | * Unless required by applicable law or agreed to in writing, software 11 | * distributed under the License is distributed on an "AS IS" BASIS, 12 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 | * See the License for the specific language governing permissions and 14 | * limitations under the License. 15 | */ 16 | 17 | package com.android.cts.tradefed.testtype; 18 | 19 | import com.android.cts.tradefed.build.CtsBuildHelper; 20 | import com.android.ddmlib.testrunner.ITestRunListener; 21 | import com.android.tradefed.build.IBuildInfo; 22 | import com.android.tradefed.device.DeviceNotAvailableException; 23 | import com.android.tradefed.device.ITestDevice; 24 | import com.android.tradefed.log.LogUtil.CLog; 25 | import com.android.tradefed.result.ITestInvocationListener; 26 | import com.android.tradefed.testtype.IBuildReceiver; 27 | import com.android.tradefed.testtype.IDeviceTest; 28 | import com.android.tradefed.testtype.IRemoteTest; 29 | 30 | import java.io.File; 31 | import java.io.FileNotFoundException; 32 | 33 | /** 34 | * Test runner for wrapped (native) GTests 35 | */ 36 | public class WrappedGTest implements IBuildReceiver, IDeviceTest, IRemoteTest { 37 | 38 | private int mMaxTestTimeMs = 1 * 60 * 1000; 39 | 40 | private CtsBuildHelper mCtsBuild; 41 | private ITestDevice mDevice; 42 | 43 | private final String mAppNameSpace; 44 | private final String mRunner; 45 | private final String mName; 46 | private final String mUri; 47 | 48 | 49 | public WrappedGTest(String appNameSpace, String uri, String name, String runner) { 50 | mAppNameSpace = appNameSpace; 51 | mRunner = runner; 52 | mName = name; 53 | mUri = uri; 54 | } 55 | 56 | @Override 57 | public void setBuild(IBuildInfo buildInfo) { 58 | mCtsBuild = CtsBuildHelper.createBuildHelper(buildInfo); 59 | } 60 | 61 | @Override 62 | public void setDevice(ITestDevice device) { 63 | mDevice = device; 64 | } 65 | 66 | @Override 67 | public ITestDevice getDevice() { 68 | return mDevice; 69 | } 70 | 71 | @Override 72 | public void run(ITestInvocationListener listener) throws DeviceNotAvailableException { 73 | if (installTest()) { 74 | runTest(listener); 75 | uninstallTest(); 76 | } else { 77 | CLog.e("Failed to install test"); 78 | } 79 | } 80 | 81 | private boolean installTest() throws DeviceNotAvailableException { 82 | try { 83 | File testApp = mCtsBuild.getTestApp(String.format("%s.apk", mName)); 84 | String installCode = mDevice.installPackage(testApp, true); 85 | 86 | if (installCode != null) { 87 | CLog.e("Failed to install %s.apk on %s. Reason: %s", mName, 88 | mDevice.getSerialNumber(), installCode); 89 | return false; 90 | } 91 | } 92 | catch (FileNotFoundException e) { 93 | CLog.e("Package %s.apk not found", mName); 94 | return false; 95 | } 96 | return true; 97 | } 98 | 99 | private void runTest(ITestRunListener listener) throws DeviceNotAvailableException { 100 | WrappedGTestResultParser resultParser = new WrappedGTestResultParser(mUri, listener); 101 | resultParser.setFakePackagePrefix(mUri + "."); 102 | try { 103 | String command = String.format("am instrument -w %s/.%s", mAppNameSpace, mRunner); 104 | mDevice.executeShellCommand(command, resultParser, mMaxTestTimeMs, 0); 105 | } catch (DeviceNotAvailableException e) { 106 | resultParser.flush(); 107 | throw e; 108 | } catch (RuntimeException e) { 109 | resultParser.flush(); 110 | throw e; 111 | } 112 | } 113 | 114 | private void uninstallTest() throws DeviceNotAvailableException { 115 | mDevice.uninstallPackage(mAppNameSpace); 116 | } 117 | } 118 | -------------------------------------------------------------------------------- /src/com/android/cts/tradefed/testtype/WrappedGTestResultParser.java: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright 2012 The Android Open Source Project 3 | * 4 | * Licensed under the Apache License, Version 2.0 (the "License"); 5 | * you may not use this file except in compliance with the License. 6 | * You may obtain a copy of the License at 7 | * 8 | * http://www.apache.org/licenses/LICENSE-2.0 9 | * 10 | * Unless required by applicable law or agreed to in writing, software 11 | * distributed under the License is distributed on an "AS IS" BASIS, 12 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 | * See the License for the specific language governing permissions and 14 | * limitations under the License. 15 | */ 16 | package com.android.cts.tradefed.testtype; 17 | 18 | import com.android.ddmlib.testrunner.ITestRunListener; 19 | import com.android.tradefed.log.LogUtil.CLog; 20 | 21 | import java.util.Collection; 22 | import java.util.List; 23 | import java.util.ArrayList; 24 | 25 | public class WrappedGTestResultParser extends GeeTestResultParser { 26 | 27 | private boolean mInstrumentationError; 28 | 29 | /** 30 | * Creates the WrappedGTestResultParser. 31 | * 32 | * @param testRunName the test run name to provide to 33 | * {@link ITestRunListener#testRunStarted(String, int)} 34 | * @param listeners informed of test results as the tests are executing 35 | */ 36 | public WrappedGTestResultParser(String testRunName, Collection listeners) { 37 | super(testRunName, listeners); 38 | } 39 | 40 | /** 41 | * Creates the WrappedGTestResultParser for a single listener. 42 | * 43 | * @param testRunName the test run name to provide to 44 | * {@link ITestRunListener#testRunStarted(String, int)} 45 | * @param listener informed of test results as the tests are executing 46 | */ 47 | public WrappedGTestResultParser(String testRunName, ITestRunListener listener) { 48 | super(testRunName, listener); 49 | } 50 | 51 | /** 52 | * Strips the instrumentation information and then forwards 53 | * the raw gtest output to the {@link GeeTestResultParser}. 54 | */ 55 | @Override 56 | public void processNewLines(String[] lines) { 57 | if (mInstrumentationError) { 58 | return; 59 | } 60 | 61 | String[] gtestOutput = parseInstrumentation(lines); 62 | super.processNewLines(gtestOutput); 63 | } 64 | 65 | /** 66 | * Parses raw instrumentation output and returns the 67 | * contained gtest output 68 | * 69 | * @param lines the raw instrumentation output 70 | * @return the gtest output 71 | */ 72 | public String[] parseInstrumentation(String[] lines) { 73 | List output = new ArrayList(); 74 | boolean readMultiLine = false; 75 | for (String line : lines) { 76 | 77 | if (line.startsWith("INSTRUMENTATION_RESULT: ")) { 78 | CLog.e("Instrumentation Error:"); 79 | mInstrumentationError = true; 80 | } 81 | 82 | if (mInstrumentationError) { 83 | CLog.e(line); 84 | continue; 85 | } 86 | 87 | if (line.startsWith("INSTRUMENTATION_STATUS: gtest=")) { 88 | output.add(line.replace("INSTRUMENTATION_STATUS: gtest=", "")); 89 | readMultiLine = true; 90 | continue; 91 | } 92 | 93 | if (line.startsWith("INSTRUMENTATION_")) { 94 | readMultiLine = false; 95 | continue; 96 | } 97 | 98 | if (readMultiLine) { 99 | output.add(line); 100 | } 101 | } 102 | 103 | return output.toArray(new String[output.size()]); 104 | } 105 | } 106 | 107 | -------------------------------------------------------------------------------- /src/com/android/cts/tradefed/testtype/monkey/Monkey.java: -------------------------------------------------------------------------------- 1 | package com.android.cts.tradefed.testtype.monkey; 2 | 3 | import java.security.SecureRandom; 4 | import java.util.ArrayList; 5 | import java.util.Random; 6 | 7 | import com.android.chimpchat.adb.AdbChimpDevice; 8 | import com.android.cts.tradefed.result.CtsXmlResultReporter; 9 | 10 | public class Monkey { 11 | 12 | private String testPackage; 13 | 14 | private MonkeySourceRandom mEventSource; 15 | 16 | /** Categories we are allowed to launch **/ 17 | private ArrayList mMainCategories = new ArrayList(); 18 | /** Applications we can switch to. */ 19 | 20 | private long mThrottle = 300; 21 | private int mVerbose = 1; 22 | private boolean mRandomizeThrottle = false; 23 | private AdbChimpDevice mDevice; 24 | private Rectangle mRectangle = null; 25 | 26 | public Monkey(String testPackage, AdbChimpDevice device, float[] factors) { 27 | this.testPackage = testPackage; 28 | this.mDevice = device; 29 | init(factors); 30 | } 31 | 32 | /** 33 | * Fire next random event 34 | */ 35 | public void nextRandomEvent(CtsXmlResultReporter ctsXmlResultReporter) { 36 | MonkeyEvent ev = mEventSource.getNextEvent(); 37 | 38 | // System.out.println("Firing Monkey Event:" + ev.toString()); 39 | if (ev != null) { 40 | ctsXmlResultReporter.addMonkeyEvent(ev); 41 | ev.fireEvent(mDevice); 42 | } 43 | 44 | } 45 | 46 | public void setY(int y){ 47 | mRectangle.setY(y); 48 | } 49 | 50 | /** 51 | * Initiate the monkey 52 | */ 53 | private void init(float[] factors) { 54 | Random mRandom = new SecureRandom(); 55 | mRandom.setSeed(-1); 56 | 57 | mRectangle = new Rectangle(Integer.parseInt(mDevice 58 | .getProperty("display.width")), Integer.parseInt(mDevice 59 | .getProperty("display.height"))); 60 | mEventSource = new MonkeySourceRandom(mRandom, mThrottle, 61 | mRandomizeThrottle, mRectangle); 62 | mEventSource.setVerbose(mVerbose); 63 | for (int i = 0; i < factors.length; i++) { 64 | if (factors[i] > 0) { 65 | mEventSource.setFactors(i, -factors[i]); 66 | } 67 | } 68 | 69 | mEventSource.validate(); 70 | 71 | // start a random activity 72 | // mEventSource.generateActivity(); 73 | } 74 | 75 | } 76 | -------------------------------------------------------------------------------- /src/com/android/cts/tradefed/testtype/monkey/MonkeyDragEvent.java: -------------------------------------------------------------------------------- 1 | package com.android.cts.tradefed.testtype.monkey; 2 | 3 | import java.awt.Point; 4 | import java.util.ArrayList; 5 | import java.util.List; 6 | 7 | import com.android.chimpchat.adb.AdbChimpDevice; 8 | 9 | public class MonkeyDragEvent extends MonkeyEvent { 10 | 11 | private Point downPoint = null; 12 | private Point upPoint = null; 13 | 14 | public MonkeyDragEvent() { 15 | super(EVENT_TYPE_DRAG); 16 | // TODO Auto-generated constructor stub 17 | } 18 | 19 | public void setDownPoint(Point point) { 20 | downPoint = point; 21 | } 22 | 23 | public void setUpPoint(Point point) { 24 | upPoint = point; 25 | } 26 | 27 | public Point getDownPoint() { 28 | return downPoint; 29 | } 30 | 31 | public Point getUpPoint() { 32 | return upPoint; 33 | } 34 | 35 | @Override 36 | public int fireEvent(AdbChimpDevice acDevice) { 37 | // TODO Auto-generated method stub 38 | 39 | acDevice.drag(downPoint.x, downPoint.y, upPoint.x, upPoint.y, 10, 1000); 40 | 41 | return 0; 42 | } 43 | 44 | } 45 | -------------------------------------------------------------------------------- /src/com/android/cts/tradefed/testtype/monkey/MonkeyEvent.java: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright (C) 2008 The Android Open Source Project 3 | * 4 | * Licensed under the Apache License, Version 2.0 (the "License"); 5 | * you may not use this file except in compliance with the License. 6 | * You may obtain a copy of the License at 7 | * 8 | * http://www.apache.org/licenses/LICENSE-2.0 9 | * 10 | * Unless required by applicable law or agreed to in writing, software 11 | * distributed under the License is distributed on an "AS IS" BASIS, 12 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 | * See the License for the specific language governing permissions and 14 | * limitations under the License. 15 | */ 16 | 17 | package com.android.cts.tradefed.testtype.monkey; 18 | 19 | import com.android.chimpchat.adb.AdbChimpDevice; 20 | 21 | /** 22 | * abstract class for monkey event 23 | */ 24 | public abstract class MonkeyEvent{ 25 | protected int eventType; 26 | public static final int EVENT_TYPE_KEY = 0; 27 | public static final int EVENT_TYPE_TAP = 1; 28 | public static final int EVENT_TYPE_MOTION = 2; 29 | public static final int EVENT_TYPE_DRAG = 3; 30 | 31 | 32 | 33 | 34 | public MonkeyEvent(int type) { 35 | eventType = type; 36 | } 37 | 38 | /* (non-Javadoc) 39 | * @see com.android.cts.tradefed.testtype.monkey.IMonkeyEvent#getEventType() 40 | */ 41 | public int getEventType() { 42 | return eventType; 43 | } 44 | 45 | /* (non-Javadoc) 46 | * @see com.android.cts.tradefed.testtype.monkey.IMonkeyEvent#isThrottlable() 47 | */ 48 | public boolean isThrottlable() { 49 | return true; 50 | } 51 | 52 | /* (non-Javadoc) 53 | * @see com.android.cts.tradefed.testtype.monkey.IMonkeyEvent#fireEvent(com.android.chimpchat.adb.AdbChimpDevice) 54 | */ 55 | public abstract int fireEvent(AdbChimpDevice acDevice); 56 | } 57 | -------------------------------------------------------------------------------- /src/com/android/cts/tradefed/testtype/monkey/MonkeyEventQueue.java: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright (C) 2008 The Android Open Source Project 3 | * 4 | * Licensed under the Apache License, Version 2.0 (the "License"); 5 | * you may not use this file except in compliance with the License. 6 | * You may obtain a copy of the License at 7 | * 8 | * http://www.apache.org/licenses/LICENSE-2.0 9 | * 10 | * Unless required by applicable law or agreed to in writing, software 11 | * distributed under the License is distributed on an "AS IS" BASIS, 12 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 | * See the License for the specific language governing permissions and 14 | * limitations under the License. 15 | */ 16 | 17 | package com.android.cts.tradefed.testtype.monkey; 18 | 19 | import java.util.LinkedList; 20 | import java.util.Random; 21 | 22 | 23 | /** 24 | * class for keeping a monkey event queue 25 | */ 26 | @SuppressWarnings("serial") 27 | public class MonkeyEventQueue extends LinkedList { 28 | 29 | private Random mRandom; 30 | private long mThrottle; 31 | private boolean mRandomizeThrottle; 32 | 33 | public MonkeyEventQueue(Random random, long throttle, boolean randomizeThrottle) { 34 | super(); 35 | mRandom = random; 36 | mThrottle = throttle; 37 | mRandomizeThrottle = randomizeThrottle; 38 | } 39 | 40 | @Override 41 | public void addLast(MonkeyEvent e) { 42 | super.add(e); 43 | if (e.isThrottlable()) { 44 | long throttle = mThrottle; 45 | if (mRandomizeThrottle && (mThrottle > 0)) { 46 | throttle = mRandom.nextLong(); 47 | if (throttle < 0) { 48 | throttle = -throttle; 49 | } 50 | throttle %= mThrottle; 51 | ++throttle; 52 | } 53 | // super.add(new MonkeyThrottleEvent(throttle)); 54 | } 55 | } 56 | } 57 | -------------------------------------------------------------------------------- /src/com/android/cts/tradefed/testtype/monkey/MonkeyEventSource.java: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright (C) 2008 The Android Open Source Project 3 | * 4 | * Licensed under the Apache License, Version 2.0 (the "License"); 5 | * you may not use this file except in compliance with the License. 6 | * You may obtain a copy of the License at 7 | * 8 | * http://www.apache.org/licenses/LICENSE-2.0 9 | * 10 | * Unless required by applicable law or agreed to in writing, software 11 | * distributed under the License is distributed on an "AS IS" BASIS, 12 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 | * See the License for the specific language governing permissions and 14 | * limitations under the License. 15 | */ 16 | 17 | package com.android.cts.tradefed.testtype.monkey; 18 | 19 | 20 | /** 21 | * event source interface 22 | */ 23 | public interface MonkeyEventSource { 24 | /** 25 | * @return the next monkey event from the source 26 | */ 27 | public MonkeyEvent getNextEvent(); 28 | 29 | /** 30 | * set verbose to allow different level of log 31 | * 32 | * @param verbose output mode? 1= verbose, 2=very verbose 33 | */ 34 | public void setVerbose(int verbose); 35 | 36 | /** 37 | * check whether precondition is satisfied 38 | * 39 | * @return false if something fails, e.g. factor failure in random source or 40 | * file can not open from script source etc 41 | */ 42 | public boolean validate(); 43 | } 44 | -------------------------------------------------------------------------------- /src/com/android/cts/tradefed/testtype/monkey/MonkeyKeyEvent.java: -------------------------------------------------------------------------------- 1 | package com.android.cts.tradefed.testtype.monkey; 2 | 3 | import java.io.IOException; 4 | 5 | import com.android.chimpchat.adb.AdbChimpDevice; 6 | 7 | 8 | //设备按键事件,包括:基本的导航事件(nav),主要导航事件(majornav)和系统事件(syskeys) 9 | public class MonkeyKeyEvent extends MonkeyEvent { 10 | public String getKeyCode() { 11 | return mKeyCode; 12 | } 13 | 14 | public void setKeyCode(String mKeyCode) { 15 | this.mKeyCode = mKeyCode; 16 | } 17 | 18 | private String mKeyCode; 19 | 20 | public MonkeyKeyEvent(String keyCode) { 21 | super(EVENT_TYPE_KEY); 22 | this.mKeyCode = keyCode; 23 | } 24 | 25 | @Override 26 | public int fireEvent(AdbChimpDevice acDevice) { 27 | try { 28 | acDevice.getManager().press(mKeyCode); 29 | } catch (IOException e) { 30 | // TODO Auto-generated catch block 31 | e.printStackTrace(); 32 | } 33 | return 0; 34 | } 35 | 36 | } 37 | -------------------------------------------------------------------------------- /src/com/android/cts/tradefed/testtype/monkey/MonkeyMotionEvent.java: -------------------------------------------------------------------------------- 1 | package com.android.cts.tradefed.testtype.monkey; 2 | 3 | import java.awt.Point; 4 | import java.io.IOException; 5 | import java.sql.Time; 6 | import java.util.ArrayList; 7 | import java.util.List; 8 | import java.util.concurrent.TimeUnit; 9 | 10 | import com.android.chimpchat.adb.AdbChimpDevice; 11 | 12 | //motion事件是由屏幕上某处一个down事件、一系列伪随机的移动事件和一个up事件组成 13 | public class MonkeyMotionEvent extends MonkeyEvent { 14 | 15 | private List movePoints = new ArrayList(); 16 | private Point downPoint = null; 17 | private Point upPoint = null; 18 | 19 | public MonkeyMotionEvent() { 20 | super(EVENT_TYPE_MOTION); 21 | // TODO Auto-generated constructor stub 22 | } 23 | 24 | public void addMovePoint(Point point) { 25 | movePoints.add(point); 26 | } 27 | 28 | public void setDownPoint(Point point) { 29 | downPoint = point; 30 | } 31 | 32 | public void setUpPoint(Point point) { 33 | upPoint = point; 34 | } 35 | 36 | public List getMovePoints() { 37 | return movePoints; 38 | } 39 | 40 | public void setMovePoints(List movePoints) { 41 | this.movePoints = movePoints; 42 | } 43 | 44 | public Point getDownPoint() { 45 | return downPoint; 46 | } 47 | 48 | public Point getUpPoint() { 49 | return upPoint; 50 | } 51 | 52 | @Override 53 | public int fireEvent(AdbChimpDevice acDevice) { 54 | // TODO Auto-generated method stub 55 | 56 | // try { 57 | // acDevice.getManager().touchDown(downPoint.x, downPoint.y); 58 | // for (Point point : movePoints) { 59 | // acDevice.getManager().touchMove(point.x, point.y); 60 | // } 61 | // 62 | // acDevice.getManager().touchUp(upPoint.x, upPoint.y); 63 | // 64 | // } catch (IOException e) { 65 | // // TODO Auto-generated catch block 66 | // e.printStackTrace(); 67 | // } 68 | acDevice.drag(downPoint.x, downPoint.y, upPoint.x, upPoint.y, 1, 1000); 69 | 70 | return 0; 71 | } 72 | 73 | } 74 | -------------------------------------------------------------------------------- /src/com/android/cts/tradefed/testtype/monkey/MonkeyTapEvent.java: -------------------------------------------------------------------------------- 1 | package com.android.cts.tradefed.testtype.monkey; 2 | 3 | import java.awt.Point; 4 | import java.io.IOException; 5 | 6 | import com.android.chimpchat.adb.AdbChimpDevice; 7 | 8 | //触摸事件是指在屏幕中的一个down-up事件,即在屏幕某处按下并抬起的操作 9 | 10 | public class MonkeyTapEvent extends MonkeyEvent { 11 | 12 | public static final int ACTION_UP = 0; 13 | public static final int ACTION_MOVE = 1; 14 | public static final int ACTION_DOWN = 2; 15 | private Point mPoint; 16 | 17 | public MonkeyTapEvent(Point point) { 18 | super(EVENT_TYPE_TAP); 19 | this.mPoint = point; 20 | } 21 | 22 | public Point getPoint() { 23 | return mPoint; 24 | } 25 | 26 | public void setPoint(Point mPoint) { 27 | this.mPoint = mPoint; 28 | } 29 | 30 | @Override 31 | public int fireEvent(AdbChimpDevice acDevice) { 32 | // TODO Auto-generated method stub 33 | try { 34 | acDevice.getManager().tap(mPoint.x, mPoint.y); 35 | 36 | } catch (IOException e) { 37 | // TODO Auto-generated catch block 38 | e.printStackTrace(); 39 | } 40 | return 0; 41 | } 42 | 43 | } 44 | -------------------------------------------------------------------------------- /src/com/android/cts/tradefed/testtype/monkey/Rectangle.java: -------------------------------------------------------------------------------- 1 | package com.android.cts.tradefed.testtype.monkey; 2 | 3 | public class Rectangle { 4 | 5 | private int x; 6 | private int y; 7 | private int width; 8 | private int height; 9 | 10 | public Rectangle(int width, int height) { 11 | this.width = width; 12 | this.height = height; 13 | } 14 | 15 | public int getX() { 16 | return x; 17 | } 18 | 19 | public void setX(int x) { 20 | this.x = x; 21 | } 22 | 23 | public int getY() { 24 | return y; 25 | } 26 | 27 | public void setY(int y) { 28 | this.y = y; 29 | } 30 | 31 | public int getWidth() { 32 | return width; 33 | } 34 | 35 | public void setWidth(int width) { 36 | this.width = width; 37 | } 38 | 39 | public int getHeight() { 40 | return height; 41 | } 42 | 43 | public void setHeight(int height) { 44 | this.height = height; 45 | } 46 | 47 | } 48 | --------------------------------------------------------------------------------