/* * Copyright (C) 2009 The Android Open Source Project * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ package com.android.monkeyrunner; import java.io.File; import java.io.FileInputStream; import java.io.FileOutputStream; import java.io.FileWriter; import java.io.IOException; import java.text.SimpleDateFormat; import java.util.List; import java.util.ArrayList; import java.util.Calendar; import java.util.zip.ZipEntry; import java.util.zip.ZipOutputStream; import org.jheer.XMLWriter; /** * MonkeyRecorder is a host side class that records the output of scripts that are run. * It creates a unique directory, puts in an xml file that records each cmd and result. * It stores every screenshot in this directory. * When finished, it zips this all up. * * Calling Sequence: * mr = new MonkeyRecorder(scriptName); * mr.startCommand(); * [mr.addAttribute(name, value);] * ... * [mr.addInput(cmd);] * [mr.addResults(result, filename);] // filename = "" if no screenshot * mr.endCommand(); * mr.addComment(comment); * mr.startCommand(); * ... * mr.endCommand(); * ... * mr.close(); * * With MonkeyRunner this should output an xml file, <script_name>-yyyyMMdd-HH:mm:ss.xml, into the * directory out/<script_name>-yyyyMMdd-HH:mm:ss with the contents: * * <?xml version="1.0" encoding='UTF-8'?> * <script_run> * script_name="filename" * monkeyRunnerVersion="0.2" * <!-- Device specific variables --> * <device_var var_name="name" var_value="value" /> * <device_var name="build.display" value="opal-userdebug 1.6 DRC79 14207 test-keys"/> * ... * <!-- Script commands --> * <command> * dateTime="20090921-17:08:43" * <input cmd="Pressing: menu"/> * <response result="OK" dateTime="20090921-17:08:43"/> * </command> * ... * <command> * dateTime="20090921-17:09:44" * <input cmd="grabscreen"/> * <response result="OK" dateTime="20090921-17:09:45" screenshot="home_screen-20090921-17:09:45.png"/> * </command> * ... * </script_run> * * And then zip it up with all the screenshots in the file: <script_name>-yyyyMMdd-HH:mm:ss.zip. */ public class MonkeyRecorder { // xml file to store output results in private static String mXmlFilename; private static FileWriter mXmlFile; private static XMLWriter mXmlWriter; // unique subdirectory to put results in (screenshots and xml file) private static String mDirname; private static List<String> mScreenShotNames = new ArrayList<String>(); // where we store all the results for all the script runs private static final String ROOT_DIR = "out"; // for getting the date and time in now() private static final SimpleDateFormat SIMPLE_DATE_TIME_FORMAT = new SimpleDateFormat("yyyyMMdd-HH:mm:ss"); /** * Create a new MonkeyRecorder that records commands and zips up screenshots for submittal * * @param scriptName filepath of the monkey script we are running */ public MonkeyRecorder(String scriptName) throws IOException { // Create directory structure to store xml file, images and zips File scriptFile = new File(scriptName); scriptName = scriptFile.getName(); // Get rid of path mDirname = ROOT_DIR + "/" + stripType(scriptName) + "-" + now(); new File(mDirname).mkdirs(); // Initialize xml file mXmlFilename = stampFilename(stripType(scriptName) + ".xml"); initXmlFile(scriptName); } // Get the current date and time in a simple string format (used for timestamping filenames) private static String now() { return SIMPLE_DATE_TIME_FORMAT.format(Calendar.getInstance().getTime()); } /** * Initialize the xml file writer * * @param scriptName filename (not path) of the monkey script, stored as attribute in the xml file */ private static void initXmlFile(String scriptName) throws IOException { mXmlFile = new FileWriter(mDirname + "/" + mXmlFilename); mXmlWriter = new XMLWriter(mXmlFile); mXmlWriter.begin(); mXmlWriter.comment("Monkey Script Results"); mXmlWriter.start("script_run"); mXmlWriter.addAttribute("script_name", scriptName); } /** * Add a comment to the xml file. * * @param comment comment to add to the xml file */ public static void addComment(String comment) throws IOException { mXmlWriter.comment(comment); } /** * Begin writing a command xml element */ public static void startCommand() throws IOException { mXmlWriter.start("command"); mXmlWriter.addAttribute("dateTime", now()); } /** * Write a command name attribute in a command xml element. * It's add as a sinlge script command could be multiple monkey commands. * * @param cmd command sent to the monkey */ public static void addInput(String cmd) throws IOException { String name = "cmd"; String value = cmd; mXmlWriter.tag("input", name, value); } /** * Write a response xml element in a command. * Attributes include the monkey result, datetime, and possibly screenshot filename * * @param result response of the monkey to the command * @param filename filename of the screen shot (or other file to be included) */ public static void addResult(String result, String filename) throws IOException { int num_args = 2; String[] names = new String[3]; String[] values = new String[3]; names[0] = "result"; values[0] = result; names[1] = "dateTime"; values[1] = now(); if (filename.length() != 0) { names[2] = "screenshot"; values[2] = stampFilename(filename); addScreenShot(filename); num_args = 3; } mXmlWriter.tag("response", names, values, num_args); } /** * Add an attribut to an xml element. name="escaped_value" * * @param name name of the attribute * @param value value of the attribute */ public static void addAttribute(String name, String value) throws IOException { mXmlWriter.addAttribute(name, value); } /** * Add an xml device variable element. name="escaped_value" * * @param name name of the variable * @param value value of the variable */ public static void addDeviceVar(String name, String value) throws IOException { String[] names = {"name", "value"}; String[] values = {name, value}; mXmlWriter.tag("device_var", names, values, names.length); } /** * Move the screenshot to storage and remember you did it so it can be zipped up later. * * @param filename file name of the screenshot to be stored (Not path name) */ private static void addScreenShot(String filename) { File file = new File(filename); String screenShotName = stampFilename(filename); file.renameTo(new File(mDirname, screenShotName)); mScreenShotNames.add(screenShotName); } /** * Finish writing a command xml element */ public static void endCommand() throws IOException { mXmlWriter.end(); } /** * Add datetime in front of filetype (the stuff after and including the last infamous '.') * * @param filename path of file to be stamped */ private static String stampFilename(String filename) { // int typeIndex = filename.lastIndexOf('.'); if (typeIndex == -1) { return filename + "-" + now(); } return filename.substring(0, typeIndex) + "-" + now() + filename.substring(typeIndex); } /** * Strip out the file type (the stuff after and including the last infamous '.') * * @param filename path of file to be stripped of type information */ private static String stripType(String filename) { // int typeIndex = filename.lastIndexOf('.'); if (typeIndex == -1) return filename; return filename.substring(0, typeIndex); } /** * Add a signature element * * @param filename path of file to be signatured */ private static void addMD5Signature(String filename) throws IOException { String signature = ""; // find signature... MD5 sig = new MD5(filename); signature = sig.toString(); String[] names = new String[] { "type", "filename", "signature" }; String[] values = new String[] { "md5", filename, signature }; mXmlWriter.tag("Signature", names, values, values.length); } /** * Close the monkeyRecorder by closing the xml file and zipping it up with the screenshots. * * @param filename path of file to be stripped of type information */ public static void close() throws IOException { // zip up xml file and screenshots into ROOT_DIR. byte[] buf = new byte[1024]; String zipFileName = mXmlFilename + ".zip"; endCommand(); mXmlFile.close(); FileOutputStream zipFile = new FileOutputStream(ROOT_DIR + "/" + zipFileName); ZipOutputStream out = new ZipOutputStream(zipFile); // add the xml file addFileToZip(out, mDirname + "/" + mXmlFilename, buf); // Add the screenshots for (String filename : mScreenShotNames) { addFileToZip(out, mDirname + "/" + filename, buf); } out.close(); } /** * Helper function to zip up a file into an open zip archive. * * @param zip the stream of the zip archive * @param filepath the filepath of the file to be added to the zip archive * @param buf storage place to stage reads of file before zipping */ private static void addFileToZip(ZipOutputStream zip, String filepath, byte[] buf) throws IOException { FileInputStream in = new FileInputStream(filepath); zip.putNextEntry(new ZipEntry(filepath)); int len; while ((len = in.read(buf)) > 0) { zip.write(buf, 0, len); } zip.closeEntry(); in.close(); } }