1 /* 2 * Copyright (C) 2009 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.monkeyrunner; 18 19 import java.io.File; 20 import java.io.FileInputStream; 21 import java.io.FileOutputStream; 22 import java.io.FileWriter; 23 import java.io.IOException; 24 import java.text.SimpleDateFormat; 25 import java.util.List; 26 import java.util.ArrayList; 27 import java.util.Calendar; 28 import java.util.zip.ZipEntry; 29 import java.util.zip.ZipOutputStream; 30 31 import org.jheer.XMLWriter; 32 33 /** 34 * MonkeyRecorder is a host side class that records the output of scripts that are run. 35 * It creates a unique directory, puts in an xml file that records each cmd and result. 36 * It stores every screenshot in this directory. 37 * When finished, it zips this all up. 38 * 39 * Calling Sequence: 40 * mr = new MonkeyRecorder(scriptName); 41 * mr.startCommand(); 42 * [mr.addAttribute(name, value);] 43 * ... 44 * [mr.addInput(cmd);] 45 * [mr.addResults(result, filename);] // filename = "" if no screenshot 46 * mr.endCommand(); 47 * mr.addComment(comment); 48 * mr.startCommand(); 49 * ... 50 * mr.endCommand(); 51 * ... 52 * mr.close(); 53 * 54 * With MonkeyRunner this should output an xml file, <script_name>-yyyyMMdd-HH:mm:ss.xml, into the 55 * directory out/<script_name>-yyyyMMdd-HH:mm:ss with the contents like: 56 * 57 * <?xml version="1.0" encoding='UTF-8'?> 58 * <!-- Monkey Script Results --> 59 * <script_run script_name="filename" monkeyRunnerVersion="0.2"> 60 * <!-- Device specific variables --> 61 * <device_var var_name="name" var_value="value" /> 62 * <device_var name="build.display" value="opal-userdebug 1.6 DRC79 14207 test-keys"/> 63 * ... 64 * <!-- Script commands --> 65 * <command> 66 * dateTime="20090921-17:08:43" 67 * <input cmd="Pressing: menu"/> 68 * <response result="OK" dateTime="20090921-17:08:43"/> 69 * </command> 70 * ... 71 * <command> 72 * dateTime="20090921-17:09:44" 73 * <input cmd="grabscreen"/> 74 * <response result="OK" dateTime="20090921-17:09:45" screenshot="home_screen-20090921-17:09:45.png"/> 75 * </command> 76 * ... 77 * </script_run> 78 * 79 * And then zip it up with all the screenshots in the file: <script_name>-yyyyMMdd-HH:mm:ss.zip. 80 */ 81 82 public class MonkeyRecorder { 83 84 // xml file to store output results in 85 private static String mXmlFilename; 86 private static FileWriter mXmlFile; 87 private static XMLWriter mXmlWriter; 88 89 // unique subdirectory to put results in (screenshots and xml file) 90 private static String mDirname; 91 private static List<String> mScreenShotNames = new ArrayList<String>(); 92 93 // where we store all the results for all the script runs 94 private static final String ROOT_DIR = "out"; 95 96 // for getting the date and time in now() 97 private static final SimpleDateFormat SIMPLE_DATE_TIME_FORMAT = 98 new SimpleDateFormat("yyyyMMdd-HH:mm:ss"); 99 100 /** 101 * Create a new MonkeyRecorder that records commands and zips up screenshots for submittal 102 * 103 * @param scriptName filepath of the monkey script we are running 104 */ MonkeyRecorder(String scriptName, String version)105 public MonkeyRecorder(String scriptName, String version) throws IOException { 106 // Create directory structure to store xml file, images and zips 107 File scriptFile = new File(scriptName); 108 scriptName = scriptFile.getName(); // Get rid of path 109 mDirname = ROOT_DIR + "/" + stripType(scriptName) + "-" + now(); 110 new File(mDirname).mkdirs(); 111 112 // Initialize xml file 113 mXmlFilename = stampFilename(stripType(scriptName) + ".xml"); 114 initXmlFile(scriptName, version); 115 } 116 117 // Get the current date and time in a simple string format (used for timestamping filenames) now()118 private static String now() { 119 return SIMPLE_DATE_TIME_FORMAT.format(Calendar.getInstance().getTime()); 120 } 121 122 /** 123 * Initialize the xml file writer 124 * 125 * @param scriptName filename (not path) of the monkey script, stored as attribute in the xml file 126 * @param version of the monkey runner test system 127 */ initXmlFile(String scriptName, String version)128 private static void initXmlFile(String scriptName, String version) throws IOException { 129 String[] names = new String[] { "script_name", "monkeyRunnerVersion" }; 130 String[] values = new String[] { scriptName, version }; 131 mXmlFile = new FileWriter(mDirname + "/" + mXmlFilename); 132 mXmlWriter = new XMLWriter(mXmlFile); 133 mXmlWriter.begin(); 134 mXmlWriter.comment("Monkey Script Results"); 135 mXmlWriter.start("script_run", names, values, names.length); 136 } 137 138 /** 139 * Add a comment to the xml file. 140 * 141 * @param comment comment to add to the xml file 142 */ addComment(String comment)143 public static void addComment(String comment) throws IOException { 144 mXmlWriter.comment(comment); 145 } 146 147 /** 148 * Begin writing a command xml element 149 */ startCommand()150 public static void startCommand() throws IOException { 151 mXmlWriter.start("command", "dateTime", now()); 152 } 153 154 /** 155 * Write a command name attribute in a command xml element. 156 * It's add as a sinlge script command could be multiple monkey commands. 157 * 158 * @param cmd command sent to the monkey 159 */ addInput(String cmd)160 public static void addInput(String cmd) throws IOException { 161 String name = "cmd"; 162 String value = cmd; 163 mXmlWriter.tag("input", name, value); 164 } 165 166 /** 167 * Write a response xml element in a command. 168 * Attributes include the monkey result, datetime, and possibly screenshot filename 169 * 170 * @param result response of the monkey to the command 171 * @param filename filename of the screen shot (or other file to be included) 172 */ addResult(String result, String filename)173 public static void addResult(String result, String filename) throws IOException { 174 int num_args = 2; 175 String[] names = new String[3]; 176 String[] values = new String[3]; 177 names[0] = "result"; 178 values[0] = result; 179 names[1] = "dateTime"; 180 values[1] = now(); 181 if (filename.length() != 0) { 182 names[2] = "screenshot"; 183 values[2] = stampFilename(filename); 184 addScreenShot(filename); 185 num_args = 3; 186 } 187 mXmlWriter.tag("response", names, values, num_args); 188 } 189 190 /** 191 * Add an attribut to an open xml element. name="escaped_value" 192 * 193 * @param name name of the attribute 194 * @param value value of the attribute 195 */ addAttribute(String name, String value)196 public static void addAttribute(String name, String value) throws IOException { 197 mXmlWriter.addAttribute(name, value); 198 } 199 200 /** 201 * Add an xml device variable element. name="escaped_value" 202 * 203 * @param name name of the variable 204 * @param value value of the variable 205 */ addDeviceVar(String name, String value)206 public static void addDeviceVar(String name, String value) throws IOException { 207 String[] names = {"name", "value"}; 208 String[] values = {name, value}; 209 mXmlWriter.tag("device_var", names, values, names.length); 210 } 211 212 /** 213 * Move the screenshot to storage and remember you did it so it can be zipped up later. 214 * 215 * @param filename file name of the screenshot to be stored (Not path name) 216 */ addScreenShot(String filename)217 private static void addScreenShot(String filename) { 218 File file = new File(filename); 219 String screenShotName = stampFilename(filename); 220 file.renameTo(new File(mDirname, screenShotName)); 221 mScreenShotNames.add(screenShotName); 222 } 223 224 /** 225 * Finish writing a command xml element 226 */ endCommand()227 public static void endCommand() throws IOException { 228 mXmlWriter.end(); 229 } 230 231 /** 232 * Add datetime in front of filetype (the stuff after and including the last infamous '.') 233 * 234 * @param filename path of file to be stamped 235 */ stampFilename(String filename)236 private static String stampFilename(String filename) { 237 // 238 int typeIndex = filename.lastIndexOf('.'); 239 if (typeIndex == -1) { 240 return filename + "-" + now(); 241 } 242 return filename.substring(0, typeIndex) + "-" + now() + filename.substring(typeIndex); 243 } 244 245 /** 246 * Strip out the file type (the stuff after and including the last infamous '.') 247 * 248 * @param filename path of file to be stripped of type information 249 */ stripType(String filename)250 private static String stripType(String filename) { 251 // 252 int typeIndex = filename.lastIndexOf('.'); 253 if (typeIndex == -1) 254 return filename; 255 return filename.substring(0, typeIndex); 256 } 257 258 /** 259 * Close the monkeyRecorder by closing the xml file and zipping it up with the screenshots. 260 * 261 * @param filename path of file to be stripped of type information 262 */ close()263 public static void close() throws IOException { 264 // zip up xml file and screenshots into ROOT_DIR. 265 byte[] buf = new byte[1024]; 266 String zipFileName = mXmlFilename + ".zip"; 267 endCommand(); 268 mXmlFile.close(); 269 FileOutputStream zipFile = new FileOutputStream(ROOT_DIR + "/" + zipFileName); 270 ZipOutputStream out = new ZipOutputStream(zipFile); 271 272 // add the xml file 273 addFileToZip(out, mDirname + "/" + mXmlFilename, buf); 274 275 // Add the screenshots 276 for (String filename : mScreenShotNames) { 277 addFileToZip(out, mDirname + "/" + filename, buf); 278 } 279 out.close(); 280 } 281 282 /** 283 * Helper function to zip up a file into an open zip archive. 284 * 285 * @param zip the stream of the zip archive 286 * @param filepath the filepath of the file to be added to the zip archive 287 * @param buf storage place to stage reads of file before zipping 288 */ addFileToZip(ZipOutputStream zip, String filepath, byte[] buf)289 private static void addFileToZip(ZipOutputStream zip, String filepath, byte[] buf) throws IOException { 290 FileInputStream in = new FileInputStream(filepath); 291 zip.putNextEntry(new ZipEntry(filepath)); 292 int len; 293 while ((len = in.read(buf)) > 0) { 294 zip.write(buf, 0, len); 295 } 296 zip.closeEntry(); 297 in.close(); 298 } 299 } 300