summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorJean-Baptiste Queru <jbq@google.com>2009-11-23 09:18:45 -0800
committerJean-Baptiste Queru <jbq@google.com>2009-11-23 09:18:45 -0800
commit106ed5a3a4c98b258bc2010a93d8a792c232c698 (patch)
tree67436b7a5b6f53a6e0cdc13f12435dd420ab3c16
parent041c6fc2c6e805c8bd055c2003b687870de57c32 (diff)
parentdd69b12f6e774ac5aecfcb7533884f37276a569b (diff)
downloaddevelopment-donut-plus-aosp.tar.gz
merge from donutdonut-plus-aosp
-rw-r--r--build/sdk.atree21
-rw-r--r--emulator/skins/HVGA/layout10
-rw-r--r--emulator/skins/QVGA/layout3
-rw-r--r--emulator/skins/WQVGA432/layout2
-rw-r--r--emulator/skins/WVGA800/hardware.ini3
-rw-r--r--emulator/skins/WVGA800/layout2
-rw-r--r--emulator/skins/WVGA854/hardware.ini3
-rw-r--r--emulator/skins/WVGA854/layout2
-rw-r--r--testrunner/test_defs.xml7
-rw-r--r--tools/monkeyrunner/src/Android.mk24
-rw-r--r--tools/monkeyrunner/src/com/android/monkeyrunner/MonkeyRecorder.java312
-rw-r--r--tools/monkeyrunner/src/com/android/monkeyrunner/MonkeyRunner.java359
-rw-r--r--tools/scripts/AndroidManifest.template2
-rw-r--r--tools/scripts/AndroidManifest.tests.template2
-rw-r--r--tools/scripts/alias_rules.xml24
-rw-r--r--tools/scripts/android_rules.xml469
-rw-r--r--tools/scripts/android_test_rules.xml104
-rw-r--r--tools/scripts/app_engine_server/memcache_zipserve.py255
-rw-r--r--tools/scripts/build.alias.template8
-rw-r--r--tools/scripts/build.template35
-rw-r--r--tools/scripts/java_file.template2
-rw-r--r--tools/scripts/java_tests_file.template8
-rw-r--r--tools/scripts/layout.template2
-rw-r--r--tools/scripts/platform_source.properties4
-rw-r--r--tools/scripts/strings.template2
25 files changed, 1317 insertions, 348 deletions
diff --git a/build/sdk.atree b/build/sdk.atree
index 16d65de16..bc0a33114 100644
--- a/build/sdk.atree
+++ b/build/sdk.atree
@@ -171,16 +171,21 @@ docs/service_actions.txt platforms/${PLATFORM_NAME}/data/service_actions.txt
docs/categories.txt platforms/${PLATFORM_NAME}/data/categories.txt
docs/widgets.txt platforms/${PLATFORM_NAME}/data/widgets.txt
framework/layoutlib.jar platforms/${PLATFORM_NAME}/data/layoutlib.jar
+
+# framework resources for layoutlib
frameworks/base/core/res/res platforms/${PLATFORM_NAME}/data/res
-frameworks/base/data/fonts/fonts.xml platforms/${PLATFORM_NAME}/data/fonts/fonts.xml
-frameworks/base/data/fonts/DroidSans.ttf platforms/${PLATFORM_NAME}/data/fonts/DroidSans.ttf
-frameworks/base/data/fonts/DroidSans-Bold.ttf platforms/${PLATFORM_NAME}/data/fonts/DroidSans-Bold.ttf
-frameworks/base/data/fonts/DroidSansFallback.ttf platforms/${PLATFORM_NAME}/data/fonts/DroidSansFallback.ttf
-frameworks/base/data/fonts/DroidSansMono.ttf platforms/${PLATFORM_NAME}/data/fonts/DroidSansMono.ttf
-frameworks/base/data/fonts/DroidSerif-Bold.ttf platforms/${PLATFORM_NAME}/data/fonts/DroidSerif-Bold.ttf
+
+# fonts for layoutlib.
+frameworks/base/data/fonts/fonts.xml platforms/${PLATFORM_NAME}/data/fonts/fonts.xml
+frameworks/base/data/fonts/DroidSans.ttf platforms/${PLATFORM_NAME}/data/fonts/DroidSans.ttf
+frameworks/base/data/fonts/DroidSans-Bold.ttf platforms/${PLATFORM_NAME}/data/fonts/DroidSans-Bold.ttf
+frameworks/base/data/fonts/DroidSansMono.ttf platforms/${PLATFORM_NAME}/data/fonts/DroidSansMono.ttf
+frameworks/base/data/fonts/DroidSerif-Bold.ttf platforms/${PLATFORM_NAME}/data/fonts/DroidSerif-Bold.ttf
frameworks/base/data/fonts/DroidSerif-BoldItalic.ttf platforms/${PLATFORM_NAME}/data/fonts/DroidSerif-BoldItalic.ttf
-frameworks/base/data/fonts/DroidSerif-Italic.ttf platforms/${PLATFORM_NAME}/data/fonts/DroidSerif-Italic.ttf
-frameworks/base/data/fonts/DroidSerif-Regular.ttf platforms/${PLATFORM_NAME}/data/fonts/DroidSerif-Regular.ttf
+frameworks/base/data/fonts/DroidSerif-Italic.ttf platforms/${PLATFORM_NAME}/data/fonts/DroidSerif-Italic.ttf
+frameworks/base/data/fonts/DroidSerif-Regular.ttf platforms/${PLATFORM_NAME}/data/fonts/DroidSerif-Regular.ttf
+frameworks/base/data/fonts/DroidSansFallback.ttf platforms/${PLATFORM_NAME}/data/fonts/DroidSansFallback.ttf
+frameworks/base/data/fonts/DroidSansJapanese.ttf platforms/${PLATFORM_NAME}/data/fonts/DroidSansJapanese.ttf
# empty add-on folder with just a readme
development/tools/scripts/README_add-ons.txt add-ons/README.txt
diff --git a/emulator/skins/HVGA/layout b/emulator/skins/HVGA/layout
index 7117824d2..cc82ddeae 100644
--- a/emulator/skins/HVGA/layout
+++ b/emulator/skins/HVGA/layout
@@ -18,7 +18,7 @@ parts {
y 0
}
}
-
+
controls {
background {
image controls.png
@@ -357,7 +357,7 @@ layouts {
height 534
color 0xe0e0e0
event EV_SW:0:1
-
+
part1 {
name portrait
x 0
@@ -394,6 +394,12 @@ layouts {
color 0xe0e0e0
event EV_SW:0:0
+ # the framework _always_ assume that the DPad
+ # has been physically rotated in landscape mode.
+ # however, with this skin, this is not the case
+ #
+ dpad-rotation 3
+
part1 {
name portrait
x 800
diff --git a/emulator/skins/QVGA/layout b/emulator/skins/QVGA/layout
index f98e7bd48..96f213388 100644
--- a/emulator/skins/QVGA/layout
+++ b/emulator/skins/QVGA/layout
@@ -393,6 +393,9 @@ layouts {
color 0xe0e0e0
event EV_SW:0:0
+ dpad-rotation 3
+
+
part1 {
name portrait
x 800
diff --git a/emulator/skins/WQVGA432/layout b/emulator/skins/WQVGA432/layout
index 7e52b5320..006e955d3 100644
--- a/emulator/skins/WQVGA432/layout
+++ b/emulator/skins/WQVGA432/layout
@@ -393,6 +393,8 @@ layouts {
color 0xe0e0e0
event EV_SW:0:0
+ dpad-rotation 3
+
part1 {
name portrait
x 800
diff --git a/emulator/skins/WVGA800/hardware.ini b/emulator/skins/WVGA800/hardware.ini
index 02e9d89ee..9aec915b0 100644
--- a/emulator/skins/WVGA800/hardware.ini
+++ b/emulator/skins/WVGA800/hardware.ini
@@ -1,2 +1,3 @@
# skin-specific hardware values
-hw.lcd.density=240 \ No newline at end of file
+hw.lcd.density=240
+vm.heapSize=24
diff --git a/emulator/skins/WVGA800/layout b/emulator/skins/WVGA800/layout
index 6037ab8b5..63647f3e4 100644
--- a/emulator/skins/WVGA800/layout
+++ b/emulator/skins/WVGA800/layout
@@ -394,6 +394,8 @@ layouts {
color 0xe0e0e0
event EV_SW:0:0
+ dpad-rotation 3
+
part1 {
name portrait
x 900
diff --git a/emulator/skins/WVGA854/hardware.ini b/emulator/skins/WVGA854/hardware.ini
index 02e9d89ee..9aec915b0 100644
--- a/emulator/skins/WVGA854/hardware.ini
+++ b/emulator/skins/WVGA854/hardware.ini
@@ -1,2 +1,3 @@
# skin-specific hardware values
-hw.lcd.density=240 \ No newline at end of file
+hw.lcd.density=240
+vm.heapSize=24
diff --git a/emulator/skins/WVGA854/layout b/emulator/skins/WVGA854/layout
index ab0784d02..b086ddc2f 100644
--- a/emulator/skins/WVGA854/layout
+++ b/emulator/skins/WVGA854/layout
@@ -394,6 +394,8 @@ layouts {
color 0xe0e0e0
event EV_SW:0:0
+ dpad-rotation 3
+
part1 {
name portrait
x 900
diff --git a/testrunner/test_defs.xml b/testrunner/test_defs.xml
index 6f073bbb1..55ddfdfa7 100644
--- a/testrunner/test_defs.xml
+++ b/testrunner/test_defs.xml
@@ -291,6 +291,13 @@ See test_defs.xsd for more information.
coverage_target="framework"
cts="true" />
+<test name="cts-webkit"
+ build_path="cts/tests/tests/webkit"
+ package="com.android.cts.webkit"
+ runner="android.test.InstrumentationCtsTestRunner"
+ coverage_target="framework"
+ cts="true" />
+
<test name="cts-widget"
build_path="cts/tests"
package="com.android.cts.widget"
diff --git a/tools/monkeyrunner/src/Android.mk b/tools/monkeyrunner/src/Android.mk
index 576025a0c..fb6b9c10f 100644
--- a/tools/monkeyrunner/src/Android.mk
+++ b/tools/monkeyrunner/src/Android.mk
@@ -21,9 +21,31 @@ LOCAL_SRC_FILES := $(call all-subdir-java-files)
LOCAL_JAR_MANIFEST := ../etc/manifest.txt
LOCAL_JAVA_LIBRARIES := \
ddmlib \
- jython
+ jython \
+ xmlwriter
LOCAL_MODULE := monkeyrunner
include $(BUILD_HOST_JAVA_LIBRARY)
+
+# Build ext.jar
+# ============================================================
+
+ext_dirs := ../../../../external/xmlwriter/src
+
+ext_src_files := $(call all-java-files-under,$(ext_dirs))
+
+# ==== the library =========================================
+include $(CLEAR_VARS)
+
+LOCAL_SRC_FILES := $(ext_src_files)
+
+LOCAL_NO_STANDARD_LIBRARIES := true
+#LOCAL_JAVA_LIBRARIES := core
+#LOCAL_STATIC_JAVA_LIBRARIES := libgoogleclient
+
+LOCAL_MODULE := xmlwriter
+
+include $(BUILD_HOST_JAVA_LIBRARY)
+
diff --git a/tools/monkeyrunner/src/com/android/monkeyrunner/MonkeyRecorder.java b/tools/monkeyrunner/src/com/android/monkeyrunner/MonkeyRecorder.java
new file mode 100644
index 000000000..efc002b5a
--- /dev/null
+++ b/tools/monkeyrunner/src/com/android/monkeyrunner/MonkeyRecorder.java
@@ -0,0 +1,312 @@
+/*
+ * 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();
+ }
+}
diff --git a/tools/monkeyrunner/src/com/android/monkeyrunner/MonkeyRunner.java b/tools/monkeyrunner/src/com/android/monkeyrunner/MonkeyRunner.java
index cbc881c6c..07a473917 100644
--- a/tools/monkeyrunner/src/com/android/monkeyrunner/MonkeyRunner.java
+++ b/tools/monkeyrunner/src/com/android/monkeyrunner/MonkeyRunner.java
@@ -30,18 +30,21 @@ import java.io.BufferedReader;
import java.io.BufferedWriter;
import java.io.File;
import java.io.IOException;
+import java.io.InputStream;
import java.io.InputStreamReader;
import java.io.OutputStreamWriter;
import java.net.InetAddress;
import java.net.Socket;
import java.net.UnknownHostException;
+import java.util.logging.Level;
+import java.util.logging.Logger;
import javax.imageio.ImageIO;
/**
* MonkeyRunner is a host side application to control a monkey instance on a
- * device. MonkeyRunner provides some useful helper functions to control the
- * device as well as various other methods to help script tests.
+ * device. MonkeyRunner provides some useful helper functions to control the
+ * device as well as various other methods to help script tests.
*/
public class MonkeyRunner {
@@ -50,27 +53,46 @@ public class MonkeyRunner {
static Socket monkeySocket = null;
static IDevice monkeyDevice;
-
+
static BufferedReader monkeyReader;
static BufferedWriter monkeyWriter;
+ static String monkeyResponse;
+
+ static MonkeyRecorder monkeyRecorder;
static String scriptName = null;
+
+ // Obtain a suitable logger.
+ private static Logger logger = Logger.getLogger("com.android.monkeyrunner");
// delay between key events
final static int KEY_INPUT_DELAY = 1000;
+
+ // version of monkey runner
+ final static String monkeyRunnerVersion = "0.31";
+
+ // TODO: interface cmd; class xml tags; fix logger; test class/script
- public static void main(String[] args) {
+ public static void main(String[] args) throws IOException {
+ // haven't figure out how to get below INFO...bad parent. Pass -v INFO to turn on logging
+ logger.setLevel(Level.parse("WARNING"));
processOptions(args);
+ logger.info("initAdb");
initAdbConnection();
+ logger.info("openMonkeyConnection");
openMonkeyConnection();
+ logger.info("start_script");
start_script();
+ logger.info("ScriptRunner.run");
ScriptRunner.run(scriptName);
+ logger.info("end_script");
end_script();
+ logger.info("closeMonkeyConnection");
closeMonkeyConnection();
}
@@ -166,13 +188,15 @@ public class MonkeyRunner {
try {
InetAddress addr = InetAddress.getByName(monkeyServer);
monkeySocket = new Socket(addr, monkeyPort);
+ monkeyWriter = new BufferedWriter(new OutputStreamWriter(monkeySocket.getOutputStream()));
+ monkeyReader = new BufferedReader(new InputStreamReader(monkeySocket.getInputStream()));
} catch (UnknownHostException e) {
e.printStackTrace();
} catch(IOException e) {
e.printStackTrace();
}
}
-
+
/**
* Close tcp session with the monkey on the device
*
@@ -189,47 +213,59 @@ public class MonkeyRunner {
}
/**
- * This is a house cleaning type routine to run before starting a script. Puts
- * the device in a known state.
+ * This is a house cleaning routine to run before starting a script. Puts
+ * the device in a known state and starts recording interesting info.
*/
- public static void start_script() {
- press("menu");
- press("menu");
- press("home");
+ public static void start_script() throws IOException {
+ press("menu", false);
+ press("menu", false);
+ press("home", false);
+
+ // Start recording the script output, might want md5 signature of file for completeness
+ monkeyRecorder = new MonkeyRecorder(scriptName);
+
+ // Record what device and version of software we are running on
+ monkeyRecorder.addAttribute("monkeyRunnerVersion", monkeyRunnerVersion);
+ addDeviceVars();
+ monkeyRecorder.addComment("Script commands");
}
- public static void end_script() {
- String command = "END";
- sendMonkeyEvent(command);
+ /**
+ * This is a house cleaning routine to run after finishing a script.
+ * Puts the monkey server in a known state and closes the recording.
+ */
+ public static void end_script() throws IOException {
+ String command = "done";
+ sendMonkeyEvent(command, false, false);
+
+ // Stop the recording and zip up the results
+ monkeyRecorder.close();
}
/** This is a method for scripts to launch an activity on the device
*
* @param name The name of the activity to launch
*/
- public static void launch_activity(String name) {
- try {
- System.out.println("Launching: " + name);
- monkeyDevice.executeShellCommand("am start -a android.intent.action.MAIN -n "
- + name, new NullOutputReceiver());
- } catch (Exception e) {
- e.printStackTrace();
- }
- }
+ public static void launch_activity(String name) throws IOException {
+ System.out.println("Launching: " + name);
+ recordCommand("Launching: " + name);
+ monkeyDevice.executeShellCommand("am start -a android.intent.action.MAIN -n "
+ + name, new NullOutputReceiver());
+ // void return, so no response given, just close the command element in the xml file.
+ monkeyRecorder.endCommand();
+ }
/**
* Grabs the current state of the screen stores it as a png
*
* @param tag filename or tag descriptor of the screenshot
*/
- public static void grabscreen(String tag) {
+ public static void grabscreen(String tag) throws IOException {
tag += ".png";
try {
Thread.sleep(1000);
getDeviceImage(monkeyDevice, tag, false);
- } catch (IOException e) {
- e.printStackTrace();
} catch (InterruptedException e) {
}
}
@@ -239,9 +275,11 @@ public class MonkeyRunner {
*
* @param msec msecs to sleep for
*/
- public static void sleep(int msec) {
+ public static void sleep(int msec) throws IOException {
try {
+ recordCommand("sleep: " + msec);
Thread.sleep(msec);
+ recordResponse("OK");
} catch (InterruptedException e) {
e.printStackTrace();
}
@@ -253,12 +291,10 @@ public class MonkeyRunner {
* @param x x-coordinate
* @param y y-coordinate
*/
- public static boolean tap(int x, int y) {
- String command = "touch down " + x + " " + y + "\r\n" +
- "touch up " + x + " " + y + "\r\n";
-
- System.out.println("Tapping: " + x + ", " + y);
- return sendMonkeyEvent(command);
+ public static boolean tap(int x, int y) throws IOException {
+ String command = "tap " + x + " " + y;
+ boolean result = sendMonkeyEvent(command);
+ return result;
}
/**
@@ -266,25 +302,33 @@ public class MonkeyRunner {
*
* @param key key to press
*/
- public static boolean press(String key) {
- String command = "key down " + key + "\r\n" +
- "key up " + key + "\r\n";
+ public static boolean press(String key) throws IOException {
+ return press(key, true);
+ }
- System.out.println("Pressing: " + key);
- return sendMonkeyEvent(command);
+ /**
+ * Press function for scripts to call on a particular button or key
+ *
+ * @param key key to press
+ * @param print whether to send output to user
+ */
+ private static boolean press(String key, boolean print) throws IOException {
+ String command = "press " + key;
+ boolean result = sendMonkeyEvent(command, print, true);
+ return result;
}
/**
* dpad down function
*/
- public static boolean down() {
+ public static boolean down() throws IOException {
return press("dpad_down");
}
/**
* dpad up function
*/
- public static boolean up() {
+ public static boolean up() throws IOException {
return press("dpad_up");
}
@@ -293,69 +337,173 @@ public class MonkeyRunner {
*
* @param text text to type
*/
- public static boolean type(String text) {
- System.out.println("Typing: " + text);
- for (int i=0; i<text.length(); i++) {
- String command = "key down ";
- char c = text.charAt(i);
- if(Character.isDigit(c)) {
- command += "KEYCODE_" + c + "\n" + "key up KEYCODE_" + c + "\n";
- } else {
- command = "key down " + c + "\n" + "key up " + c + "\n";
- }
-
- if(!sendMonkeyEvent(command)) {
- System.out.println("\nERROR: Key not set \n");
- }
-
- // lets delay a bit after each input to ensure accuracy
- try {
- Thread.sleep(KEY_INPUT_DELAY);
- } catch (InterruptedException e) {
- }
+ public static boolean type(String text) throws IOException {
+ boolean result = false;
+ // text might have line ends, which signal new monkey command, so we have to eat and reissue
+ String[] lines = text.split("[\\r\\n]+");
+ for (String line: lines) {
+ result = sendMonkeyEvent("type " + line + "\n");
}
+ // return last result. Should never fail..?
+ return result;
+ }
+
+ /**
+ * Function to get a static variable from the device
+ *
+ * @param name name of static variable to get
+ */
+ public static boolean getvar(String name) throws IOException {
+ return sendMonkeyEvent("getvar " + name + "\n");
+ }
- return true;
+ /**
+ * Function to get the list of static variables from the device
+ */
+ public static boolean listvar() throws IOException {
+ return sendMonkeyEvent("listvar \n");
}
/**
* This function is the communication bridge between the host and the device.
* It sends monkey events and waits for responses over the adb tcp socket.
+ * This version if for all scripted events so that they get recorded and reported to user.
*
* @param command the monkey command to send to the device
*/
- private static boolean sendMonkeyEvent(String command) {
- try {
- monkeyWriter = new BufferedWriter(
- new OutputStreamWriter(monkeySocket.getOutputStream()));
- monkeyWriter.write(command);
- monkeyWriter.flush();
-
- monkeyReader = new BufferedReader(
- new InputStreamReader(monkeySocket.getInputStream()));
- String response = monkeyReader.readLine();
-
- sleep(1000);
- System.out.println("MonkeyServer: " + response);
- if(response.equals("OK"))
- return true;
- if(response.equals("ERROR"))
+ private static boolean sendMonkeyEvent(String command) throws IOException {
+ return sendMonkeyEvent(command, true, true);
+ }
+
+ /**
+ * This function allows the communication bridge between the host and the device
+ * to be invisible to the script for internal needs.
+ * It splits a command into monkey events and waits for responses for each over an adb tcp socket.
+ * Returns on an error, else continues and sets up last response.
+ *
+ * @param command the monkey command to send to the device
+ * @param print whether to print out the responses to the user
+ * @param record whether to put the command in the xml file that stores test outputs
+ */
+ private static boolean sendMonkeyEvent(String command, Boolean print, Boolean record) throws IOException {
+ command = command.trim();
+ if (print)
+ System.out.println("MonkeyCommand: " + command);
+ if (record)
+ recordCommand(command);
+ logger.info("Monkey Command: " + command + ".");
+
+ // send a single command and get the response
+ monkeyWriter.write(command + "\n");
+ monkeyWriter.flush();
+ monkeyResponse = monkeyReader.readLine();
+
+ if(monkeyResponse != null) {
+ // if a command returns with a response
+ if (print)
+ System.out.println("MonkeyServer: " + monkeyResponse);
+ if (record)
+ recordResponse(monkeyResponse);
+ logger.info("Monkey Response: " + monkeyResponse + ".");
+
+ // return on error
+ if (monkeyResponse.startsWith("ERROR"))
return false;
- } catch (IOException e) {
- e.printStackTrace();
- }
+ // return on ok
+ if(monkeyResponse.startsWith("OK"))
+ return true;
+ // return on something else?
+ return false;
+ }
+ // didn't get a response...
+ if (print)
+ System.out.println("MonkeyServer: ??no response");
+ if (record)
+ recordResponse("??no response");
+ logger.info("Monkey Response: ??no response.");
+
+ //return on no response
return false;
}
-
+ /**
+ * Record the command in the xml file
+ *
+ * @param command the command sent to the monkey server
+ */
+ private static void recordCommand(String command) throws IOException {
+ if (monkeyRecorder != null) { // don't record setup junk
+ monkeyRecorder.startCommand();
+ monkeyRecorder.addInput(command);
+ }
+ }
+
+ /**
+ * Record the response in the xml file
+ *
+ * @param response the response sent by the monkey server
+ */
+ private static void recordResponse(String response) throws IOException {
+ recordResponse(response, "");
+ }
+
+ /**
+ * Record the response and the filename in the xml file, store the file (to be zipped up later)
+ *
+ * @param response the response sent by the monkey server
+ * @param filename the filename of a file to be time stamped, recorded in the xml file and stored
+ */
+ private static void recordResponse(String response, String filename) throws IOException {
+ if (monkeyRecorder != null) { // don't record setup junk
+ monkeyRecorder.addResult(response, filename); // ignores file if filename empty
+ monkeyRecorder.endCommand();
+ }
+ }
+
+ /**
+ * Add the device variables to the xml file in monkeyRecorder.
+ * The results get added as device_var tags in the script_run tag
+ */
+ private static void addDeviceVars() throws IOException {
+ monkeyRecorder.addComment("Device specific variables");
+ sendMonkeyEvent("listvar \n", false, false);
+ if (monkeyResponse.startsWith("OK:")) {
+ // peel off "OK:" string and get the individual var names
+ String[] varNames = monkeyResponse.substring(3).split("\\s+");
+ // grab all the individual var values
+ for (String name: varNames) {
+ sendMonkeyEvent("getvar " + name, false, false);
+ if(monkeyResponse != null) {
+ if (monkeyResponse.startsWith("OK") ) {
+ if (monkeyResponse.length() > 2) {
+ monkeyRecorder.addDeviceVar(name, monkeyResponse.substring(3));
+ } else {
+ // only got OK - good variable but no value
+ monkeyRecorder.addDeviceVar(name, "null");
+ }
+ } else {
+ // error returned - couldn't get var value for name... include error return
+ monkeyRecorder.addDeviceVar(name, monkeyResponse);
+ }
+ } else {
+ // no monkeyResponse - bad variable with no value
+ monkeyRecorder.addDeviceVar(name, "null");
+ }
+ }
+ } else {
+ // it's an error, can't find variable names...
+ monkeyRecorder.addAttribute("listvar", monkeyResponse);
+ }
+ }
+
/**
* Process the command-line options
*
* @return Returns true if options were parsed with no apparent errors.
*/
- public static void processOptions(String[] args) {
+ private static void processOptions(String[] args) {
// parse command line parameters.
int index = 0;
@@ -364,7 +512,7 @@ public class MonkeyRunner {
if ("-s".equals(argument)) {
if(index == args.length) {
- printAndExit("Missing Server after -s", false);
+ printUsageAndQuit("Missing Server after -s");
}
monkeyServer = args[index++];
@@ -372,18 +520,32 @@ public class MonkeyRunner {
} else if ("-p".equals(argument)) {
// quick check on the next argument.
if (index == args.length) {
- printAndExit("Missing Server IP after -p", false /* terminate */);
+ printUsageAndQuit("Missing Server port after -p");
}
monkeyPort = Integer.parseInt(args[index++]);
+
+ } else if ("-v".equals(argument)) {
+ // quick check on the next argument.
+ if (index == args.length) {
+ printUsageAndQuit("Missing Log Level after -v");
+ }
+
+ Level level = Level.parse(args[index++]);
+ logger.setLevel(level);
+ level = logger.getLevel();
+ System.out.println("Log level set to: " + level + "(" + level.intValue() + ").");
+ System.out.println("Warning: Log levels below INFO(800) not working currently... parent issues");
+
+ } else if (argument.startsWith("-")) {
+ // we have an unrecognized argument.
+ printUsageAndQuit("Unrecognized argument: " + argument + ".");
+
+ monkeyPort = Integer.parseInt(args[index++]);
+
} else {
- // get the filepath of the script to run.
+ // get the filepath of the script to run. This will be the last undashed argument.
scriptName = argument;
-
- // should not be any other device.
- //if (index < args.length) {
- // printAndExit("Too many arguments!", false /* terminate */);
- //}
}
} while (index < args.length);
}
@@ -394,22 +556,29 @@ public class MonkeyRunner {
private static void getDeviceImage(IDevice device, String filepath, boolean landscape)
throws IOException {
RawImage rawImage;
+ recordCommand("grabscreen");
+ System.out.println("Grabbing Screeshot: " + filepath + ".");
try {
rawImage = device.getScreenshot();
}
catch (IOException ioe) {
+ recordResponse("No frame buffer", "");
printAndExit("Unable to get frame buffer: " + ioe.getMessage(), true /* terminate */);
return;
}
// device/adb not available?
- if (rawImage == null)
+ if (rawImage == null) {
+ recordResponse("No image", "");
return;
-
+ }
+
assert rawImage.bpp == 16;
BufferedImage image;
+
+ logger.info("Raw Image - height: " + rawImage.height + ", width: " + rawImage.width);
if (landscape) {
// convert raw data to an Image
@@ -458,16 +627,20 @@ public class MonkeyRunner {
}
if (!ImageIO.write(image, "png", new File(filepath))) {
+ recordResponse("No png writer", "");
throw new IOException("Failed to find png writer");
}
+ recordResponse("OK", filepath);
}
- private static void printUsageAndQuit() {
+ private static void printUsageAndQuit(String message) {
// 80 cols marker: 01234567890123456789012345678901234567890123456789012345678901234567890123456789
+ System.out.println(message);
System.out.println("Usage: monkeyrunner [options] SCRIPT_FILE");
System.out.println("");
System.out.println(" -s MonkeyServer IP Address.");
System.out.println(" -p MonkeyServer TCP Port.");
+ System.out.println(" -v MonkeyServer Logging level (ALL, FINEST, FINER, FINE, CONFIG, INFO, WARNING, SEVERE, OFF)");
System.out.println("");
System.out.println("");
diff --git a/tools/scripts/AndroidManifest.template b/tools/scripts/AndroidManifest.template
index 2b06e76ac..9b07072f5 100644
--- a/tools/scripts/AndroidManifest.template
+++ b/tools/scripts/AndroidManifest.template
@@ -4,7 +4,7 @@
android:versionCode="1"
android:versionName="1.0">
<application android:label="@string/app_name">
- <activity android:name=".ACTIVITY_NAME"
+ <activity android:name="ACTIVITY_ENTRY_NAME"
android:label="@string/app_name">
<intent-filter>
<action android:name="android.intent.action.MAIN" />
diff --git a/tools/scripts/AndroidManifest.tests.template b/tools/scripts/AndroidManifest.tests.template
index 1f7d827fe..c74ff6dbf 100644
--- a/tools/scripts/AndroidManifest.tests.template
+++ b/tools/scripts/AndroidManifest.tests.template
@@ -17,5 +17,5 @@
-->
<instrumentation android:name="android.test.InstrumentationTestRunner"
android:targetPackage="PACKAGE"
- android:label="Tests for ACTIVITY_NAME"/>
+ android:label="Tests for PACKAGE"/>
</manifest>
diff --git a/tools/scripts/alias_rules.xml b/tools/scripts/alias_rules.xml
index 570246134..273da45a6 100644
--- a/tools/scripts/alias_rules.xml
+++ b/tools/scripts/alias_rules.xml
@@ -5,22 +5,22 @@ THIS FILE IS CURRENTLY BROKEN AND SHOULD NOT BE USED.
-->
<project name="alias_rules" default="package">
- <!-- No user servicable parts below. -->
+ <!-- No user serviceable parts below. -->
<!-- Input directories -->
- <property name="resource-dir" value="res" />
+ <property name="resource.dir" value="res" />
<!-- The final package file to generate -->
- <property name="out-package" value="${ant.project.name}.apk" />
+ <property name="out.package" value="${ant.project.name}.apk" />
<!-- Tools -->
- <condition property="aapt" value="${android-tools}/aapt.exe" else="${android-tools}/aapt" >
- <os family="windows"/>
+ <condition property="aapt" value="${android.tools.dir}/aapt.exe" else="${android.tools.dir}/aapt">
+ <os family="windows" />
</condition>
- <condition property="adb" value="${android-tools}/adb.exe" else="${android-tools}/adb" >
- <os family="windows"/>
+ <condition property="adb" value="${android.tools.dir}/adb.exe" else="${android.tools.dir}/adb">
+ <os family="windows" />
</condition>
- <property name="android-jar" value="${sdk-folder}/android.jar" />
+ <property name="android.jar" value="${sdk.dir}/android.jar" />
<!-- Rules -->
@@ -33,11 +33,11 @@ THIS FILE IS CURRENTLY BROKEN AND SHOULD NOT BE USED.
<arg value="-M" />
<arg value="AndroidManifest.xml" />
<arg value="-S" />
- <arg value="${resource-dir}" />
+ <arg value="${resource.dir}" />
<arg value="-I" />
- <arg value="${android-jar}" />
+ <arg value="${android.jar}" />
<arg value="-F" />
- <arg value="${out-package}" />
+ <arg value="${out.package}" />
</exec>
</target>
@@ -49,7 +49,7 @@ THIS FILE IS CURRENTLY BROKEN AND SHOULD NOT BE USED.
<echo>Sending package to default emulator...</echo>
<exec executable="${adb}" failonerror="true">
<arg value="install" />
- <arg value="${out-package}" />
+ <arg value="${out.package}" />
</exec>
</target>
diff --git a/tools/scripts/android_rules.xml b/tools/scripts/android_rules.xml
index ad36cbe72..675017c0b 100644
--- a/tools/scripts/android_rules.xml
+++ b/tools/scripts/android_rules.xml
@@ -6,7 +6,7 @@
com.android.ant.AndroidInitTask
The following properties are put in place by the importing task:
- android-jar, android-aidl, aapt, aidl, and dx
+ android.jar, android.aidl, aapt, aidl, and dx
Additionnaly, the task sets up the following classpath reference:
android.target.classpath
@@ -16,184 +16,291 @@
<!-- Custom tasks -->
<taskdef name="aaptexec"
classname="com.android.ant.AaptExecLoopTask"
- classpathref="android.antlibs"/>
+ classpathref="android.antlibs" />
<taskdef name="apkbuilder"
classname="com.android.ant.ApkBuilderTask"
- classpathref="android.antlibs"/>
+ classpathref="android.antlibs" />
+
+ <taskdef name="xpath"
+ classname="com.android.ant.XPathTask"
+ classpathref="android.antlibs" />
<!-- Properties -->
- <property name="android-tools" value="${sdk-location}/tools" />
+ <!-- Tells adb which device to target. You can change this from the command line
+ by invoking "ant -Dadb.device.arg=-d" for device "ant -Dadb.device.arg=-e" for
+ the emulator. -->
+ <property name="adb.device.arg" value="" />
+
+ <property name="android.tools.dir" location="${sdk.dir}/tools" />
+ <!-- Name of the application package extracted from manifest file -->
+ <xpath input="AndroidManifest.xml" expression="/manifest/@package"
+ output="manifest.package" />
<!-- Input directories -->
- <property name="source-folder" value="src" />
- <property name="gen-folder" value="gen" />
- <property name="resource-folder" value="res" />
- <property name="asset-folder" value="assets" />
- <property name="source-location" value="${basedir}/${source-folder}" />
+ <property name="source.dir" value="src" />
+ <property name="source.absolute.dir" location="${source.dir}" />
+ <property name="gen.dir" value="gen" />
+ <property name="gen.absolute.dir" location="${gen.dir}" />
+ <property name="resource.dir" value="res" />
+ <property name="resource.absolute.dir" location="${resource.dir}" />
+ <property name="asset.dir" value="assets" />
+ <property name="asset.absolute.dir" location="${asset.dir}" />
- <!-- folder for the 3rd party java libraries -->
- <property name="external-libs-folder" value="libs" />
+ <!-- Directory for the third party java libraries -->
+ <property name="external.libs.dir" value="libs" />
+ <property name="external.libs.absolute.dir" location="${external.libs.dir}" />
- <!-- folder for the native libraries -->
- <property name="native-libs-folder" value="libs" />
+ <!-- Directory for the native libraries -->
+ <property name="native.libs.dir" value="libs" />
+ <property name="native.libs.absolute.dir" location="${native.libs.dir}" />
<!-- Output directories -->
- <property name="gen-folder" value="gen" />
- <property name="out-folder" value="bin" />
- <property name="out-classes" value="${out-folder}/classes" />
- <property name="out-classes-location" value="${basedir}/${out-classes}"/>
- <!-- out folders for a parent project if this project is an instrumentation project -->
- <property name="main-out-folder" value="../${out-folder}" />
- <property name="main-out-classes" value="${main-out-folder}/classes"/>
+ <property name="out.dir" value="bin" />
+ <property name="out.absolute.dir" location="${out.dir}" />
+ <property name="out.classes.dir" value="${out.absolute.dir}/classes" />
+ <property name="out.classes.absolute.dir" location="${out.classes.dir}" />
<!-- Intermediate files -->
- <property name="dex-file" value="classes.dex" />
- <property name="intermediate-dex" value="${out-folder}/${dex-file}" />
- <!-- dx does not properly support incorrect / or \ based on the platform
- and Ant cannot convert them because the parameter is not a valid path.
- Because of this we have to compute different paths depending on the platform. -->
- <condition property="intermediate-dex-location"
- value="${basedir}\${intermediate-dex}"
- else="${basedir}/${intermediate-dex}" >
- <os family="windows"/>
- </condition>
+ <property name="dex.file.name" value="classes.dex" />
+ <property name="intermediate.dex.file" location="${out.absolute.dir}/${dex.file.name}" />
<!-- The final package file to generate -->
- <property name="out-debug-unaligned-package" value="${out-folder}/${ant.project.name}-debug-unaligned.apk"/>
- <property name="out-debug-package" value="${out-folder}/${ant.project.name}-debug.apk"/>
- <property name="out-unsigned-package" value="${out-folder}/${ant.project.name}-unsigned.apk"/>
- <property name="out-unaligned-package" value="${out-folder}/${ant.project.name}-unaligned.apk"/>
- <property name="out-release-package" value="${out-folder}/${ant.project.name}-release.apk"/>
+ <property name="out.debug.unaligned.package"
+ location="${out.absolute.dir}/${ant.project.name}-debug-unaligned.apk" />
+ <property name="out.debug.package"
+ location="${out.absolute.dir}/${ant.project.name}-debug.apk" />
+ <property name="out.unsigned.package"
+ location="${out.absolute.dir}/${ant.project.name}-unsigned.apk" />
+ <property name="out.unaligned.package"
+ location="${out.absolute.dir}/${ant.project.name}-unaligned.apk" />
+ <property name="out.release.package"
+ location="${out.absolute.dir}/${ant.project.name}-release.apk" />
+
+ <!-- Verbosity -->
+ <property name="verbose" value="false" />
+ <!-- This is needed by emma as it uses multilevel verbosity instead of simple 'true' or 'false'
+ The property 'verbosity' is not user configurable and depends exclusively on 'verbose'
+ value.-->
+ <condition property="verbosity" value="verbose" else="quiet">
+ <istrue value="${verbose}" />
+ </condition>
+ <!-- This is needed to switch verbosity of zipalign and aapt. Depends exclusively on 'verbose'
+ -->
+ <condition property="v.option" value="-v" else="">
+ <istrue value="${verbose}" />
+ </condition>
+ <!-- This is needed to switch verbosity of dx. Depends exclusively on 'verbose' -->
+ <condition property="verbose.option" value="--verbose" else="">
+ <istrue value="${verbose}" />
+ </condition>
<!-- Tools -->
- <condition property="exe" value=".exe" else=""><os family="windows"/></condition>
- <property name="adb" value="${android-tools}/adb${exe}"/>
- <property name="zipalign" value="${android-tools}/zipalign${exe}" />
+ <condition property="exe" value=".exe" else=""><os family="windows" /></condition>
+ <property name="adb" location="${android.tools.dir}/adb${exe}" />
+ <property name="zipalign" location="${android.tools.dir}/zipalign${exe}" />
+
+ <!-- Emma configuration -->
+ <property name="emma.dir" value="${sdk.dir}/tools/lib" />
+ <path id="emma.lib">
+ <pathelement location="${emma.dir}/emma.jar" />
+ <pathelement location="${emma.dir}/emma_ant.jar" />
+ </path>
+ <taskdef resource="emma_ant.properties" classpathref="emma.lib" />
+ <!-- End of emma configuration -->
+
+ <!-- Macros -->
+
+ <!-- Configurable macro, which allows to pass as parameters output directory,
+ output dex filename and external libraries to dex (optional) -->
+ <macrodef name="dex-helper">
+ <element name="external-libs" optional="yes" />
+ <element name="extra-parameters" optional="yes" />
+ <sequential>
+ <echo>Converting compiled files and external libraries into ${intermediate.dex.file}...
+ </echo>
+ <apply executable="${dx}" failonerror="true" parallel="true">
+ <arg value="--dex" />
+ <arg value="--output=${intermediate.dex.file}" />
+ <extra-parameters />
+ <arg line="${verbose.option}" />
+ <arg path="${out.classes.absolute.dir}" />
+ <fileset dir="${external.libs.absolute.dir}" includes="*.jar" />
+ <external-libs />
+ </apply>
+ </sequential>
+ </macrodef>
- <!-- rules -->
+ <!-- This is macro that enable passing variable list of external jar files to ApkBuilder
+ Example of use:
+ <package-helper>
+ <extra-jars>
+ <jarfolder path="my_jars" />
+ <jarfile path="foo/bar.jar" />
+ <jarfolder path="your_jars" />
+ </extra-jars>
+ </package-helper> -->
+ <macrodef name="package-helper">
+ <attribute name="sign.package" />
+ <element name="extra-jars" optional="yes" />
+ <sequential>
+ <apkbuilder
+ outfolder="${out.absolute.dir}"
+ basename="${ant.project.name}"
+ signed="@{sign.package}"
+ verbose="${verbose}">
+ <file path="${intermediate.dex.file}" />
+ <sourcefolder path="${source.absolute.dir}" />
+ <nativefolder path="${native.libs.absolute.dir}" />
+ <jarfolder path="${external.libs.absolute.dir}" />
+ <extra-jars/>
+ </apkbuilder>
+ </sequential>
+ </macrodef>
- <!-- Create the output directories if they don't exist yet. -->
- <target name="dirs">
+ <!-- This is macro which zipaligns in.package and outputs it to out.package. Used by targets
+ debug, -debug-with-emma and release.-->
+ <macrodef name="zipalign-helper">
+ <attribute name="in.package" />
+ <attribute name="out.package" />
+ <sequential>
+ <echo>Running zip align on final apk...</echo>
+ <exec executable="${zipalign}" failonerror="true">
+ <arg line="${v.option}" />
+ <arg value="-f" />
+ <arg value="4" />
+ <arg path="@{in.package}" />
+ <arg path="@{out.package}" />
+ </exec>
+ </sequential>
+ </macrodef>
+
+ <!-- This is macro used only for sharing code among two targets, -install and
+ -install-with-emma which do exactly the same but differ in dependencies -->
+ <macrodef name="install-helper">
+ <sequential>
+ <echo>Installing ${out.debug.package} onto default emulator or device...</echo>
+ <exec executable="${adb}" failonerror="true">
+ <arg line="${adb.device.arg}" />
+ <arg value="install" />
+ <arg value="-r" />
+ <arg path="${out.debug.package}" />
+ </exec>
+ </sequential>
+ </macrodef>
+
+ <!-- Rules -->
+
+ <!-- Creates the output directories if they don't exist yet. -->
+ <target name="-dirs">
<echo>Creating output directories if needed...</echo>
- <mkdir dir="${resource-folder}" />
- <mkdir dir="${external-libs-folder}" />
- <mkdir dir="${gen-folder}" />
- <mkdir dir="${out-folder}" />
- <mkdir dir="${out-classes}" />
+ <mkdir dir="${resource.absolute.dir}" />
+ <mkdir dir="${external.libs.absolute.dir}" />
+ <mkdir dir="${gen.absolute.dir}" />
+ <mkdir dir="${out.absolute.dir}" />
+ <mkdir dir="${out.classes.absolute.dir}" />
</target>
- <!-- Generate the R.java file for this project's resources. -->
- <target name="resource-src" depends="dirs">
+ <!-- Generates the R.java file for this project's resources. -->
+ <target name="-resource-src" depends="-dirs">
<echo>Generating R.java / Manifest.java from the resources...</echo>
<exec executable="${aapt}" failonerror="true">
<arg value="package" />
+ <arg line="${v.option}" />
<arg value="-m" />
<arg value="-J" />
- <arg path="${gen-folder}" />
+ <arg path="${gen.absolute.dir}" />
<arg value="-M" />
<arg path="AndroidManifest.xml" />
<arg value="-S" />
- <arg path="${resource-folder}" />
+ <arg path="${resource.absolute.dir}" />
<arg value="-I" />
- <arg path="${android-jar}" />
+ <arg path="${android.jar}" />
</exec>
</target>
- <!-- Generate java classes from .aidl files. -->
- <target name="aidl" depends="dirs">
+ <!-- Generates java classes from .aidl files. -->
+ <target name="-aidl" depends="-dirs">
<echo>Compiling aidl files into Java classes...</echo>
<apply executable="${aidl}" failonerror="true">
- <arg value="-p${android-aidl}" />
- <arg value="-I${source-folder}" />
- <arg value="-o${gen-folder}" />
- <fileset dir="${source-folder}">
- <include name="**/*.aidl"/>
+ <arg value="-p${android.aidl}" />
+ <arg value="-I${source.absolute.dir}" />
+ <arg value="-o${gen.absolute.dir}" />
+ <fileset dir="${source.absolute.dir}">
+ <include name="**/*.aidl" />
</fileset>
</apply>
</target>
- <!-- Compile this project's .java files into .class files. -->
- <target name="compile" depends="resource-src, aidl">
+ <!-- Compiles this project's .java files into .class files. -->
+ <target name="compile" depends="-resource-src, -aidl"
+ description="Compiles project's .java files into .class files">
+ <!-- If android rules are used for a test project, its classpath should include
+ tested project's location -->
+ <condition property="extensible.classpath"
+ value="${tested.project.absolute.dir}/bin/classes" else=".">
+ <isset property="tested.project.absolute.dir" />
+ </condition>
<javac encoding="ascii" target="1.5" debug="true" extdirs=""
- destdir="${out-classes}"
- bootclasspathref="android.target.classpath">
- <src path="${source-folder}" />
- <src path="${gen-folder}" />
+ destdir="${out.classes.absolute.dir}"
+ bootclasspathref="android.target.classpath"
+ verbose="${verbose}" classpath="${extensible.classpath}">
+ <src path="${source.absolute.dir}" />
+ <src path="${gen.absolute.dir}" />
<classpath>
- <fileset dir="${external-libs-folder}" includes="*.jar"/>
- <pathelement path="${main-out-classes}"/>
+ <fileset dir="${external.libs.absolute.dir}" includes="*.jar" />
</classpath>
- </javac>
+ </javac>
</target>
- <!-- Convert this project's .class files into .dex files. -->
- <target name="dex" depends="compile">
- <echo>Converting compiled files and external libraries into ${out-folder}/${dex-file}...</echo>
- <apply executable="${dx}" failonerror="true" parallel="true">
- <arg value="--dex" />
- <arg value="--output=${intermediate-dex-location}" />
- <arg path="${out-classes-location}" />
- <fileset dir="${external-libs-folder}" includes="*.jar"/>
- </apply>
+ <!-- Converts this project's .class files into .dex files -->
+ <target name="-dex" depends="compile">
+ <dex-helper />
</target>
- <!-- Put the project's resources into the output package file
+ <!-- Puts the project's resources into the output package file
This actually can create multiple resource package in case
Some custom apk with specific configuration have been
declared in default.properties.
-->
- <target name="package-resources">
+ <target name="-package-resources">
<echo>Packaging resources</echo>
<aaptexec executable="${aapt}"
command="package"
manifest="AndroidManifest.xml"
- resources="${resource-folder}"
- assets="${asset-folder}"
- androidjar="${android-jar}"
- outfolder="${out-folder}"
+ resources="${resource.absolute.dir}"
+ assets="${asset.absolute.dir}"
+ androidjar="${android.jar}"
+ outfolder="${out.absolute.dir}"
basename="${ant.project.name}" />
</target>
- <!-- Package the application and (maybe) sign it with a debug key.
- This requires the property sign.package to be set to true or false. -->
- <target name="package">
- <apkbuilder
- outfolder="${out-folder}"
- basename="${ant.project.name}"
- signed="${sign.package}"
- verbose="true">
- <file path="${intermediate-dex}" />
- <sourcefolder path="${source-folder}" />
- <jarfolder path="${external-libs-folder}" />
- <nativefolder path="${native-libs-folder}" />
- </apkbuilder>
+ <!-- Packages the application and sign it with a debug key. -->
+ <target name="-package-debug-sign" depends="-dex, -package-resources">
+ <package-helper sign.package="true" />
</target>
- <target name="no-sign">
- <property name="sign.package" value="false" />
+ <!-- Packages the application without signing it. -->
+ <target name="-package-no-sign" depends="-dex, -package-resources">
+ <package-helper sign.package="false" />
</target>
- <target name="debug-sign">
- <property name="sign.package" value="true" />
+ <target name="-compile-tested-if-test" if="tested.project.dir" unless="do.not.compile.again">
+ <subant target="compile">
+ <fileset dir="${tested.project.absolute.dir}" includes="build.xml" />
+ </subant>
</target>
- <target name="debug" depends="dex, package-resources, debug-sign, package">
- <echo>Running zip align on final apk...</echo>
- <exec executable="${zipalign}" failonerror="true">
- <arg value="-f" />
- <arg value="4" />
- <arg path="${out-debug-unaligned-package}" />
- <arg path="${out-debug-package}" />
- </exec>
- <echo>Debug Package: ${out-debug-package}</echo>
- </target>
-
- <target name="release-package" depends="dex, package-resources, no-sign, package">
+ <!-- Builds debug output package, provided all the necessary files are already dexed -->
+ <target name="debug" depends="-compile-tested-if-test, -package-debug-sign"
+ description="Builds the application and signs it with a debug key.">
+ <zipalign-helper in.package="${out.debug.unaligned.package}"
+ out.package="${out.debug.package}" />
+ <echo>Debug Package: ${out.debug.package}</echo>
</target>
- <target name="release.check">
+ <target name="-release-check">
<condition property="release.sign">
<and>
<isset property="key.store" />
@@ -201,80 +308,140 @@
</and>
</condition>
</target>
- <target name="release.nosign" depends="release.check" unless="release.sign">
+
+ <target name="-release-nosign" depends="-release-check" unless="release.sign">
<echo>No key.store and key.alias properties found in build.properties.</echo>
- <echo>Please sign ${out-unsigned-package} manually</echo>
+ <echo>Please sign ${out.unsigned.package} manually</echo>
<echo>and run zipalign from the Android SDK tools.</echo>
</target>
- <target name="release" depends="release-package, release.nosign" if="release.sign">
- <!-- get passwords -->
+ <target name="release" depends="-package-no-sign, -release-nosign" if="release.sign"
+ description="Builds the application. The generated apk file must be signed before
+ it is published.">
+ <!-- Gets passwords -->
<input
message="Please enter keystore password (store:${key.store}):"
- addproperty="key.store.password"/>
+ addproperty="key.store.password" />
<input
message="Please enter password for alias '${key.alias}':"
- addproperty="key.alias.password"/>
- <!-- sign the APK -->
+ addproperty="key.alias.password" />
+
+ <!-- Signs the APK -->
<echo>Signing final apk...</echo>
<signjar
- jar="${out-unsigned-package}"
- signedjar="${out-unaligned-package}"
+ jar="${out.unsigned.package}"
+ signedjar="${out.unaligned.package}"
keystore="${key.store}"
storepass="${key.store.password}"
alias="${key.alias}"
- keypass="${key.alias.password}"/>
- <!-- zip align the APK -->
- <echo>Running zip align on final apk...</echo>
- <exec executable="${zipalign}" failonerror="true">
- <arg value="-f" />
- <arg value="4" />
- <arg path="${out-unaligned-package}" />
- <arg path="${out-release-package}" />
- </exec>
- <echo>Release Package: ${out-release-package}</echo>
+ keypass="${key.alias.password}"
+ verbose="${verbose}" />
+
+ <!-- Zip aligns the APK -->
+ <zipalign-helper in.package="${out.unaligned.package}"
+ out.package="${out.release.package}" />
+ <echo>Release Package: ${out.release.package}</echo>
</target>
- <!-- Install the package on the default emulator -->
- <target name="install" depends="debug">
- <echo>Installing ${out-debug-package} onto default emulator...</echo>
- <exec executable="${adb}" failonerror="true">
- <arg value="install" />
- <arg value="-r" />
- <arg path="${out-debug-package}" />
- </exec>
+ <target name="install" depends="debug"
+ description="Installs/reinstalls the debug package onto a running
+ emulator or device. If the application was previously installed,
+ the signatures must match." >
+ <install-helper />
</target>
- <!-- Uinstall the package from the default emulator -->
- <target name="uninstall.check">
+ <target name="-uninstall-check">
<condition property="uninstall.run">
- <isset property="application-package" />
+ <isset property="manifest.package" />
</condition>
</target>
- <target name="uninstall.error" depends="uninstall.check" unless="uninstall.run">
- <echo>Unable to run 'ant unintall', application-package is not defined in build.properties</echo>
+
+ <target name="-uninstall-error" depends="-uninstall-check" unless="uninstall.run">
+ <echo>Unable to run 'ant uninstall', manifest.package property is not defined.
+ </echo>
</target>
- <target name="uninstall" depends="uninstall.error" if="uninstall.run">
- <echo>Uninstalling ${application-package} from the default emulator...</echo>
+
+ <!-- Uninstalls the package from the default emulator/device -->
+ <target name="uninstall" depends="-uninstall-error" if="uninstall.run"
+ description="Uninstalls the application from a running emulator or device.">
+ <echo>Uninstalling ${manifest.package} from the default emulator or device...</echo>
<exec executable="${adb}" failonerror="true">
+ <arg line="${adb.device.arg}" />
<arg value="uninstall" />
- <arg value="${application-package}" />
+ <arg value="${manifest.package}" />
</exec>
</target>
-
+
+ <target name="clean" description="Removes output files created by other targets.">
+ <delete dir="${out.absolute.dir}" verbose="${verbose}" />
+ <delete dir="${gen.absolute.dir}" verbose="${verbose}" />
+ </target>
+
+ <!-- Targets for code-coverage measurement purposes, invoked from external file -->
+
+ <!-- Emma-instruments tested project classes (compiles the tested project if necessary)
+ and writes instrumented classes to ${instrumentation.absolute.dir}/classes -->
+ <target name="-emma-instrument" depends="compile">
+ <echo>Instrumenting classes from ${out.absolute.dir}/classes...</echo>
+ <!-- It only instruments class files, not any external libs -->
+ <emma enabled="true">
+ <instr verbosity="${verbosity}"
+ mode="overwrite"
+ instrpath="${out.absolute.dir}/classes"
+ outdir="${out.absolute.dir}/classes">
+ </instr>
+ <!-- TODO: exclusion filters on R*.class and allowing custom exclusion from
+ user defined file -->
+ </emma>
+ </target>
+
+ <target name="-dex-instrumented" depends="-emma-instrument">
+ <dex-helper>
+ <extra-parameters>
+ <arg value="--no-locals" />
+ </extra-parameters>
+ <external-libs>
+ <fileset file="${emma.dir}/emma_device.jar" />
+ </external-libs>
+ </dex-helper>
+ </target>
+
+ <!-- Invoked from external files for code coverage purposes -->
+ <target name="-package-with-emma" depends="-dex-instrumented, -package-resources">
+ <package-helper sign.package="true">
+ <extra-jars>
+ <!-- Injected from external file -->
+ <jarfile path="${emma.dir}/emma_device.jar" />
+ </extra-jars>
+ </package-helper>
+ </target>
+
+ <target name="-debug-with-emma" depends="-package-with-emma">
+ <zipalign-helper in.package="${out.debug.unaligned.package}"
+ out.package="${out.debug.package}" />
+ </target>
+
+ <target name="-install-with-emma" depends="-debug-with-emma">
+ <install-helper />
+ </target>
+
+ <!-- End of targets for code-coverage measurement purposes -->
+
<target name="help">
<!-- displays starts at col 13
|13 80| -->
<echo>Android Ant Build. Available targets:</echo>
<echo> help: Displays this help.</echo>
- <echo> debug: Builds the application and sign it with a debug key.</echo>
+ <echo> clean: Removes output files created by other targets.</echo>
+ <echo> compile: Compiles project's .java files into .class files.</echo>
+ <echo> debug: Builds the application and signs it with a debug key.</echo>
<echo> release: Builds the application. The generated apk file must be</echo>
<echo> signed before it is published.</echo>
- <echo> install: Installs/reinstall the debug package onto a running</echo>
+ <echo> install: Installs/reinstalls the debug package onto a running</echo>
<echo> emulator or device.</echo>
<echo> If the application was previously installed, the</echo>
<echo> signatures must match.</echo>
- <echo> uninstall: uninstall the application from a running emulator or</echo>
+ <echo> uninstall: Uninstalls the application from a running emulator or</echo>
<echo> device.</echo>
</target>
</project>
diff --git a/tools/scripts/android_test_rules.xml b/tools/scripts/android_test_rules.xml
new file mode 100644
index 000000000..78503aef7
--- /dev/null
+++ b/tools/scripts/android_test_rules.xml
@@ -0,0 +1,104 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<project name="android_test_rules" default="run-tests">
+
+ <import file="android_rules.xml" />
+
+ <property name="tested.project.absolute.dir" location="${tested.project.dir}" />
+ <property name="instrumentation.dir" value="instrumented" />
+ <property name="instrumentation.absolute.dir" location="${instrumentation.dir}" />
+
+ <property name="test.runner" value="android.test.InstrumentationTestRunner" />
+ <!-- Application package of the tested project extracted from its manifest file -->
+ <xpath input="${tested.project.absolute.dir}/AndroidManifest.xml"
+ expression="/manifest/@package" output="tested.manifest.package" />
+
+ <!-- TODO: make it more configurable in the next CL's - now it is default for auto-generated
+ project -->
+ <property name="emma.dump.file"
+ value="/data/data/${tested.manifest.package}/files/coverage.ec" />
+
+ <macrodef name="run-tests-helper">
+ <attribute name="emma.enabled" default="false" />
+ <element name="extra-instrument-args" optional="yes" />
+ <sequential>
+ <echo>Running tests ...</echo>
+ <exec executable="${adb}" failonerror="true">
+ <arg value="shell" />
+ <arg value="am" />
+ <arg value="instrument" />
+ <arg value="-w" />
+ <arg value="-e" />
+ <arg value="coverage" />
+ <arg value="@{emma.enabled}" />
+ <extra-instrument-args />
+ <arg value="${manifest.package}/${test.runner}" />
+ </exec>
+ </sequential>
+ </macrodef>
+
+ <!-- Invoking this target sets the value of extensible.classpath, which is being added to javac
+ classpath in target 'compile' (android_rules.xml) -->
+ <target name="-set-coverage-classpath">
+ <property name="extensible.classpath"
+ location="${instrumentation.absolute.dir}/classes" />
+ </target>
+
+ <!-- Ensures that tested project is installed on the device before we run the tests.
+ Used for ordinary tests, without coverage measurement -->
+ <target name="-install-tested-project">
+ <property name="do.not.compile.again" value="true" />
+ <subant target="install">
+ <fileset dir="${tested.project.absolute.dir}" includes="build.xml" />
+ </subant>
+ </target>
+
+ <target name="run-tests" depends="-install-tested-project, install"
+ description="Runs tests from the package defined in test.package property">
+ <run-tests-helper />
+ </target>
+
+ <target name="-install-instrumented">
+ <property name="do.not.compile.again" value="true" />
+ <subant target="-install-with-emma">
+ <property name="out.absolute.dir" value="${instrumentation.absolute.dir}" />
+ <fileset dir="${tested.project.absolute.dir}" includes="build.xml" />
+ </subant>
+ </target>
+
+ <target name="coverage" depends="-set-coverage-classpath, -install-instrumented, install"
+ description="Runs the tests against the instrumented code and generates
+ code coverage report">
+ <run-tests-helper emma.enabled="true">
+ <extra-instrument-args>
+ <arg value="-e" />
+ <arg value="coverageFile" />
+ <arg value="${emma.dump.file}" />
+ </extra-instrument-args>
+ </run-tests-helper>
+ <echo>Downloading coverage file into project directory...</echo>
+ <exec executable="${adb}" failonerror="true">
+ <arg value="pull" />
+ <arg value="${emma.dump.file}" />
+ <arg value="coverage.ec" />
+ </exec>
+ <echo>Extracting coverage report...</echo>
+ <emma>
+ <report sourcepath="${tested.project.absolute.dir}/${source.dir}"
+ verbosity="${verbosity}">
+ <!-- TODO: report.dir or something like should be introduced if necessary -->
+ <infileset dir=".">
+ <include name="coverage.ec" />
+ <include name="coverage.em" />
+ </infileset>
+ <!-- TODO: reports in other, indicated by user formats -->
+ <html outfile="coverage.html" />
+ </report>
+ </emma>
+ <echo>Cleaning up temporary files...</echo>
+ <delete dir="${instrumentation.absolute.dir}" />
+ <delete file="coverage.ec" />
+ <delete file="coverage.em" />
+ <echo>Saving the report file in ${basedir}/coverage/coverage.html</echo>
+ </target>
+
+</project>
diff --git a/tools/scripts/app_engine_server/memcache_zipserve.py b/tools/scripts/app_engine_server/memcache_zipserve.py
index e11cfc544..43826b00a 100644
--- a/tools/scripts/app_engine_server/memcache_zipserve.py
+++ b/tools/scripts/app_engine_server/memcache_zipserve.py
@@ -37,7 +37,7 @@ import zipfile
from google.appengine.api import memcache
from google.appengine.ext import webapp
from google.appengine.ext.webapp import util
-
+from time import localtime, strftime
def create_handler(zip_files, max_age=None, public=None):
"""Factory method to create a MemcachedZipHandler instance.
@@ -57,7 +57,6 @@ def create_handler(zip_files, max_age=None, public=None):
"""
# verify argument integrity. If the argument is passed in list format,
# convert it to list of lists format
-
if zip_files and type(zip_files).__name__ == 'list':
num_items = len(zip_files)
while num_items > 0:
@@ -72,7 +71,6 @@ def create_handler(zip_files, max_age=None, public=None):
I'm still not sure why this is needed
"""
-
def get(self, name):
self.zipfilenames = zip_files
self.TrueGet(name)
@@ -96,12 +94,15 @@ class MemcachedZipHandler(webapp.RequestHandler):
PUBLIC = True # public cache setting
CACHE_PREFIX = 'cache://' # memcache key prefix for actual URLs
NEG_CACHE_PREFIX = 'noncache://' # memcache key prefix for non-existant URL
-
- def TrueGet(self, name):
+ intlString = 'intl/'
+ validLangs = ['en', 'de', 'es', 'fr','it','ja','zh-CN','zh-TW']
+
+ def TrueGet(self, reqUri):
"""The top-level entry point to serving requests.
Called 'True' get because it does the work when called from the wrapper
- class' get method
+ class' get method. Some logic is applied to the request to serve files
+ from an intl/<lang>/... directory or fall through to the default language.
Args:
name: URL requested
@@ -109,37 +110,88 @@ class MemcachedZipHandler(webapp.RequestHandler):
Returns:
None
"""
- name = self.PreprocessUrl(name)
-
- # see if we have the page in the memcache
- resp_data = self.GetFromCache(name)
- if resp_data is None:
- logging.info('Cache miss for %s', name)
- resp_data = self.GetFromNegativeCache(name)
- if resp_data is None:
- resp_data = self.GetFromStore(name)
+ langName = 'en'
+ resetLangCookie = False
+ urlLangName = None
+ retry = False
+ isValidIntl = False
+
+ # Try to retrieve the user's lang pref from the cookie. If there is no
+ # lang pref cookie in the request, add set-cookie to the response with the
+ # default value of 'en'.
+ try:
+ langName = self.request.cookies['android_developer_pref_lang']
+ except KeyError:
+ resetLangCookie = True
+ #logging.info('==========================EXCEPTION: NO LANG COOKIE FOUND, USING [%s]', langName)
+ logging.info('==========================REQ INIT name [%s] langName [%s]', reqUri, langName)
+
+ # Preprocess the req url. If it references a directory or the domain itself,
+ # append '/index.html' to the url and 302 redirect. Otherwise, continue
+ # processing the request below.
+ name = self.PreprocessUrl(reqUri, langName)
+ if name:
+ # Do some prep for handling intl requests. Parse the url and validate
+ # the intl/lang substring, extract the url lang code (urlLangName) and the
+ # the uri that follows the intl/lang substring(contentUri)
+ sections = name.split("/", 2)
+ contentUri = 0
+ isIntl = len(sections) > 1 and (sections[0] == "intl")
+ if isIntl:
+ isValidIntl = sections[1] in self.validLangs
+ if isValidIntl:
+ urlLangName = sections[1]
+ contentUri = sections[2]
+ if (langName != urlLangName):
+ # if the lang code in the request is different from that in
+ # the cookie, reset the cookie to the url lang value.
+ langName = urlLangName
+ resetLangCookie = True
+ #logging.info('INTL PREP resetting langName to urlLangName [%s]', langName)
+ #else:
+ # logging.info('INTL PREP no need to reset langName')
+
+ # Send for processing
+ if self.isCleanUrl(name, langName, isValidIntl):
+ # handle a 'clean' request.
+ # Try to form a response using the actual request url.
+ if not self.CreateResponse(name, langName, isValidIntl, resetLangCookie):
+ # If CreateResponse returns False, there was no such document
+ # in the intl/lang tree. Before going to 404, see if there is an
+ # English-language version of the doc in the default
+ # default tree and return it, else go to 404.
+ self.CreateResponse(contentUri, langName, False, resetLangCookie)
+
+ elif isIntl:
+ # handle the case where we need to pass through an invalid intl req
+ # for processing (so as to get 404 as appropriate). This is needed
+ # because intl urls are passed through clean and retried in English,
+ # if necessary.
+ logging.info(' Handling an invalid intl request...')
+ self.CreateResponse(name, langName, isValidIntl, resetLangCookie)
- # IF we have the file, put it in the memcache
- # ELSE put it in the negative cache
- if resp_data is not None:
- self.StoreOrUpdateInCache(name, resp_data)
- else:
- logging.info('Adding %s to negative cache, serving 404', name)
- self.StoreInNegativeCache(name)
- self.Write404Error()
- return
else:
- self.Write404Error()
- return
+ # handle the case where we have a non-clean url (usually a non-intl
+ # url) that we need to interpret in the context of any lang pref
+ # that is set. Prepend an intl/lang string to the request url and
+ # send it as a 302 redirect. After the redirect, the subsequent
+ # request will be handled as a clean url.
+ self.RedirToIntl(name, self.intlString, langName)
- content_type, encoding = mimetypes.guess_type(name)
- if content_type:
- self.response.headers['Content-Type'] = content_type
- self.SetCachingHeaders()
- self.response.out.write(resp_data)
+ def isCleanUrl(self, name, langName, isValidIntl):
+ """Determine whether to pass an incoming url straight to processing.
- def PreprocessUrl(self, name):
- """Any preprocessing work on the URL when it comes it.
+ Args:
+ name: The incoming URL
+
+ Returns:
+ boolean: Whether the URL should be sent straight to processing
+ """
+ if (langName == 'en') or isValidIntl or not ('.html' in name) or (not isValidIntl and not langName):
+ return True
+
+ def PreprocessUrl(self, name, langName):
+ """Any preprocessing work on the URL when it comes in.
Put any work related to interpretting the incoming URL here. For example,
this is used to redirect requests for a directory to the index.html file
@@ -150,12 +202,9 @@ class MemcachedZipHandler(webapp.RequestHandler):
name: The incoming URL
Returns:
- The processed URL
+ False if the request was redirected to '/index.html', or
+ The processed URL, otherwise
"""
- # handle special case of requesting the domain itself
- if not name:
- name = 'index.html'
-
# determine if this is a request for a directory
final_path_segment = name
final_slash_offset = name.rfind('/')
@@ -164,12 +213,123 @@ class MemcachedZipHandler(webapp.RequestHandler):
if final_path_segment.find('.') == -1:
name = ''.join([name, '/'])
- # if this is a directory, redirect to index.html
- if name[len(name) - 1:] == '/':
- return '%s%s' % (name, 'index.html')
+ # if this is a directory or the domain itself, redirect to /index.html
+ if not name or (name[len(name) - 1:] == '/'):
+ uri = ''.join(['/', name, 'index.html'])
+ logging.info('--->PREPROCESSING REDIRECT [%s] to [%s] with langName [%s]', name, uri, langName)
+ self.redirect(uri, False)
+ return False
else:
return name
+ def RedirToIntl(self, name, intlString, langName):
+ """Redirect an incoming request to the appropriate intl uri.
+
+ Builds the intl/lang string from a base (en) string
+ and redirects (302) the request to look for a version
+ of the file in the language that matches the client-
+ supplied cookie value.
+
+ Args:
+ name: The incoming, preprocessed URL
+
+ Returns:
+ The lang-specific URL
+ """
+ builtIntlLangUri = ''.join([intlString, langName, '/', name, '?', self.request.query_string])
+ uri = ''.join(['/', builtIntlLangUri])
+ logging.info('-->>REDIRECTING %s to %s', name, uri)
+ self.redirect(uri, False)
+ return uri
+
+ def CreateResponse(self, name, langName, isValidIntl, resetLangCookie):
+ """Process the url and form a response, if appropriate.
+
+ Attempts to retrieve the requested file (name) from cache,
+ negative cache, or store (zip) and form the response.
+ For intl requests that are not found (in the localized tree),
+ returns False rather than forming a response, so that
+ the request can be retried with the base url (this is the
+ fallthrough to default language).
+
+ For requests that are found, forms the headers and
+ adds the content to the response entity. If the request was
+ for an intl (localized) url, also resets the language cookie
+ to the language specified in the url if needed, to ensure that
+ the client language and response data remain harmonious.
+
+ Args:
+ name: The incoming, preprocessed URL
+ langName: The language id. Used as necessary to reset the
+ language cookie in the response.
+ isValidIntl: If present, indicates whether the request is
+ for a language-specific url
+ resetLangCookie: Whether the response should reset the
+ language cookie to 'langName'
+
+ Returns:
+ True: A response was successfully created for the request
+ False: No response was created.
+ """
+ # see if we have the page in the memcache
+ logging.info('PROCESSING %s langName [%s] isValidIntl [%s] resetLang [%s]',
+ name, langName, isValidIntl, resetLangCookie)
+ resp_data = self.GetFromCache(name)
+ if resp_data is None:
+ logging.info(' Cache miss for %s', name)
+ resp_data = self.GetFromNegativeCache(name)
+ if resp_data is None:
+ resp_data = self.GetFromStore(name)
+
+ # IF we have the file, put it in the memcache
+ # ELSE put it in the negative cache
+ if resp_data is not None:
+ self.StoreOrUpdateInCache(name, resp_data)
+ elif isValidIntl:
+ # couldn't find the intl doc. Try to fall through to English.
+ #logging.info(' Retrying with base uri...')
+ return False
+ else:
+ logging.info(' Adding %s to negative cache, serving 404', name)
+ self.StoreInNegativeCache(name)
+ self.Write404Error()
+ return True
+ else:
+ # found it in negative cache
+ self.Write404Error()
+ return True
+
+ # found content from cache or store
+ logging.info('FOUND CLEAN')
+ if resetLangCookie:
+ logging.info(' Resetting android_developer_pref_lang cookie to [%s]',
+ langName)
+ expireDate = time.mktime(localtime()) + 60 * 60 * 24 * 365 * 10
+ self.response.headers.add_header('Set-Cookie',
+ 'android_developer_pref_lang=%s; path=/; expires=%s' %
+ (langName, strftime("%a, %d %b %Y %H:%M:%S", localtime(expireDate))))
+ mustRevalidate = False
+ if ('.html' in name):
+ # revalidate html files -- workaround for cache inconsistencies for
+ # negotiated responses
+ mustRevalidate = True
+ logging.info(' Adding [Vary: Cookie] to response...')
+ self.response.headers.add_header('Vary', 'Cookie')
+ content_type, encoding = mimetypes.guess_type(name)
+ if content_type:
+ self.response.headers['Content-Type'] = content_type
+ self.SetCachingHeaders(mustRevalidate)
+ self.response.out.write(resp_data)
+ elif (name == 'favicon.ico'):
+ self.response.headers['Content-Type'] = 'image/x-icon'
+ self.SetCachingHeaders(mustRevalidate)
+ self.response.out.write(resp_data)
+ elif name.endswith('.psd'):
+ self.response.headers['Content-Type'] = 'application/octet-stream'
+ self.SetCachingHeaders(mustRevalidate)
+ self.response.out.write(resp_data)
+ return True
+
def GetFromStore(self, file_path):
"""Retrieve file from zip files.
@@ -192,7 +352,7 @@ class MemcachedZipHandler(webapp.RequestHandler):
archive_name = self.MapFileToArchive(file_path)
if not archive_name:
archive_name = file_itr.next()[0]
-
+
while resp_data is None and archive_name:
zip_archive = self.LoadZipFile(archive_name)
if zip_archive:
@@ -326,15 +486,17 @@ class MemcachedZipHandler(webapp.RequestHandler):
else:
return 0
- def SetCachingHeaders(self):
+ def SetCachingHeaders(self, revalidate):
"""Set caching headers for the request."""
max_age = self.MAX_AGE
- self.response.headers['Expires'] = email.Utils.formatdate(
- time.time() + max_age, usegmt=True)
- cache_control = []
+ #self.response.headers['Expires'] = email.Utils.formatdate(
+ # time.time() + max_age, usegmt=True)
+ cache_control = []
if self.PUBLIC:
cache_control.append('public')
cache_control.append('max-age=%d' % max_age)
+ if revalidate:
+ cache_control.append('must-revalidate')
self.response.headers['Cache-Control'] = ', '.join(cache_control)
def GetFromCache(self, filename):
@@ -401,7 +563,6 @@ class MemcachedZipHandler(webapp.RequestHandler):
"""
return memcache.get('%s%s' % (self.NEG_CACHE_PREFIX, filename))
-
def main():
application = webapp.WSGIApplication([('/([^/]+)/(.*)',
MemcachedZipHandler)])
diff --git a/tools/scripts/build.alias.template b/tools/scripts/build.alias.template
index 1043af184..d051405ef 100644
--- a/tools/scripts/build.alias.template
+++ b/tools/scripts/build.alias.template
@@ -8,16 +8,16 @@ THIS FILE IS CURRENTLY BROKEN AND SHOULD NOT BE USED.
<!-- The build.properties file can be created by you and is never touched
by activitycreator. If you want to manually set properties, this is
the best place to set them. -->
- <property file="build.properties"/>
+ <property file="build.properties" />
<!-- The default.properties file is created and updated by activitycreator.
It will set any properties not already defined by build.properties. -->
- <property file="default.properties"/>
+ <property file="default.properties" />
<!-- ************************************************************************************* -->
- <!-- Import the default Android build rules.
+ <!-- Import the default Android build rules.
This requires ant 1.6.0 or above. -->
- <import file="${sdk-folder}/tools/lib/alias_rules.xml" />
+ <import file="${sdk.dir}/tools/lib/alias_rules.xml" />
</project>
diff --git a/tools/scripts/build.template b/tools/scripts/build.template
index 1ed385345..3959c570f 100644
--- a/tools/scripts/build.template
+++ b/tools/scripts/build.template
@@ -2,22 +2,22 @@
<project name="PROJECT_NAME" default="help">
<!-- The local.properties file is created and updated by the 'android' tool.
- It contain the path to the SDK. It should *NOT* be checked in in Version
+ It contains the path to the SDK. It should *NOT* be checked in in Version
Control Systems. -->
- <property file="local.properties"/>
+ <property file="local.properties" />
<!-- The build.properties file can be created by you and is never touched
by the 'android' tool. This is the place to change some of the default property values
used by the Ant rules.
Here are some properties you may want to change/update:
- application-package
+ application.package
the name of your application package as defined in the manifest. Used by the
'uninstall' rule.
- source-folder
- the name of the source folder. Default is 'src'.
- out-folder
- the name of the output folder. Default is 'bin'.
+ source.dir
+ the name of the source directory. Default is 'src'.
+ out.dir
+ the name of the output directory. Default is 'bin'.
Properties related to the SDK location or the project target should be updated
using the 'android' tool with the 'update' action.
@@ -26,27 +26,27 @@
should be checked in in Version Control Systems.
-->
- <property file="build.properties"/>
+ <property file="build.properties" />
<!-- The default.properties file is created and updated by the 'android' tool, as well
- as ADT.
+ as ADT.
This file is an integral part of the build system for your application and
should be checked in in Version Control Systems. -->
- <property file="default.properties"/>
+ <property file="default.properties" />
<!-- Custom Android task to deal with the project target, and import the proper rules.
This requires ant 1.6.0 or above. -->
<path id="android.antlibs">
- <pathelement path="${sdk-location}/tools/lib/anttasks.jar" />
- <pathelement path="${sdk-location}/tools/lib/sdklib.jar" />
- <pathelement path="${sdk-location}/tools/lib/androidprefs.jar" />
- <pathelement path="${sdk-location}/tools/lib/apkbuilder.jar" />
- <pathelement path="${sdk-location}/tools/lib/jarutils.jar" />
+ <pathelement path="${sdk.dir}/tools/lib/anttasks.jar" />
+ <pathelement path="${sdk.dir}/tools/lib/sdklib.jar" />
+ <pathelement path="${sdk.dir}/tools/lib/androidprefs.jar" />
+ <pathelement path="${sdk.dir}/tools/lib/apkbuilder.jar" />
+ <pathelement path="${sdk.dir}/tools/lib/jarutils.jar" />
</path>
<taskdef name="setup"
classname="com.android.ant.SetupTask"
- classpathref="android.antlibs"/>
+ classpathref="android.antlibs" />
<!-- Execute the Android Setup task that will setup some properties specific to the target,
and import the build rules files.
@@ -58,9 +58,10 @@
- copy the content of the main node <project> from android_rules.xml
- paste it in this build.xml below the <setup /> task.
- disable the import by changing the setup task below to <setup import="false" />
-
+
This will ensure that the properties are setup correctly but that your customized
build steps are used.
-->
<setup />
+
</project>
diff --git a/tools/scripts/java_file.template b/tools/scripts/java_file.template
index aeb541f7b..19714a8e3 100644
--- a/tools/scripts/java_file.template
+++ b/tools/scripts/java_file.template
@@ -3,7 +3,7 @@ package PACKAGE;
import android.app.Activity;
import android.os.Bundle;
-public class ACTIVITY_NAME extends Activity
+public class ACTIVITY_CLASS_NAME extends Activity
{
/** Called when the activity is first created. */
@Override
diff --git a/tools/scripts/java_tests_file.template b/tools/scripts/java_tests_file.template
index c6fa8736f..08d6f9b5d 100644
--- a/tools/scripts/java_tests_file.template
+++ b/tools/scripts/java_tests_file.template
@@ -9,13 +9,13 @@ import android.test.ActivityInstrumentationTestCase2;
* <p/>
* To run this test, you can type:
* adb shell am instrument -w \
- * -e class PACKAGE.ACTIVITY_NAMETest \
+ * -e class ACTIVITY_FQ_NAME \
* PACKAGE.tests/android.test.InstrumentationTestRunner
*/
-public class ACTIVITY_NAMETest extends ActivityInstrumentationTestCase2<ACTIVITY_NAME> {
+public class ACTIVITY_CLASS_NAME extends ActivityInstrumentationTestCase2<ACTIVITY_TESTED_CLASS_NAME> {
- public ACTIVITY_NAMETest() {
- super("PACKAGE", ACTIVITY_NAME.class);
+ public ACTIVITY_CLASS_NAME() {
+ super("PACKAGE", ACTIVITY_TESTED_CLASS_NAME.class);
}
} \ No newline at end of file
diff --git a/tools/scripts/layout.template b/tools/scripts/layout.template
index 864e99748..f5e367ccf 100644
--- a/tools/scripts/layout.template
+++ b/tools/scripts/layout.template
@@ -7,7 +7,7 @@
<TextView
android:layout_width="fill_parent"
android:layout_height="wrap_content"
- android:text="Hello World, ACTIVITY_NAME"
+ android:text="Hello World, ACTIVITY_ENTRY_NAME"
/>
</LinearLayout>
diff --git a/tools/scripts/platform_source.properties b/tools/scripts/platform_source.properties
index 49dc3ae91..4269133e9 100644
--- a/tools/scripts/platform_source.properties
+++ b/tools/scripts/platform_source.properties
@@ -1,5 +1,5 @@
-Pkg.Desc=Android SDK Platform 1.6_r1
+Pkg.Desc=Android SDK Platform 1.6_r2
Pkg.UserSrc=false
Platform.Version=1.6
-Pkg.Revision=1
+Pkg.Revision=2
AndroidVersion.ApiLevel=4
diff --git a/tools/scripts/strings.template b/tools/scripts/strings.template
index acc28e29d..ee5af40eb 100644
--- a/tools/scripts/strings.template
+++ b/tools/scripts/strings.template
@@ -1,4 +1,4 @@
<?xml version="1.0" encoding="utf-8"?>
<resources>
- <string name="app_name">ACTIVITY_NAME</string>
+ <string name="app_name">ACTIVITY_ENTRY_NAME</string>
</resources>