• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
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