You are viewing a plain text version of this content. The canonical link for it is here.
Posted to commits@cordova.apache.org by ka...@apache.org on 2014/04/15 02:33:02 UTC

[2/8] CB-6421: Move tests from e2e to spec

http://git-wip-us.apache.org/repos/asf/cordova-cli/blob/f4a9597a/spec/fixtures/platforms/android/build.xml
----------------------------------------------------------------------
diff --git a/spec/fixtures/platforms/android/build.xml b/spec/fixtures/platforms/android/build.xml
new file mode 100644
index 0000000..9674edf
--- /dev/null
+++ b/spec/fixtures/platforms/android/build.xml
@@ -0,0 +1,92 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<project name="TestBase" default="help">
+
+    <!-- The local.properties file is created and updated by the 'android' tool.
+         It contains the path to the SDK. It should *NOT* be checked into
+         Version Control Systems. -->
+    <property file="local.properties" />
+
+    <!-- The ant.properties file can be created by you. It is only edited by the
+         'android' tool to add properties to it.
+         This is the place to change some Ant specific build properties.
+         Here are some properties you may want to change/update:
+
+         source.dir
+             The name of the source directory. Default is 'src'.
+         out.dir
+             The name of the output directory. Default is 'bin'.
+
+         For other overridable properties, look at the beginning of the rules
+         files in the SDK, at tools/ant/build.xml
+
+         Properties related to the SDK location or the project target should
+         be updated using the 'android' tool with the 'update' action.
+
+         This file is an integral part of the build system for your
+         application and should be checked into Version Control Systems.
+
+         -->
+    <property file="ant.properties" />
+
+    <!-- if sdk.dir was not set from one of the property file, then
+         get it from the ANDROID_HOME env var.
+         This must be done before we load project.properties since
+         the proguard config can use sdk.dir -->
+    <property environment="env" />
+    <condition property="sdk.dir" value="${env.ANDROID_HOME}">
+        <isset property="env.ANDROID_HOME" />
+    </condition>
+
+    <!-- The project.properties file is created and updated by the 'android'
+         tool, as well as ADT.
+
+         This contains project specific properties such as project target, and library
+         dependencies. Lower level build properties are stored in ant.properties
+         (or in .classpath for Eclipse projects).
+
+         This file is an integral part of the build system for your
+         application and should be checked into Version Control Systems. -->
+    <loadproperties srcFile="project.properties" />
+
+    <!-- quick check on sdk.dir -->
+    <fail
+            message="sdk.dir is missing. Make sure to generate local.properties using 'android update project' or to inject it through the ANDROID_HOME environment variable."
+            unless="sdk.dir"
+    />
+
+    <!--
+        Import per project custom build rules if present at the root of the project.
+        This is the place to put custom intermediary targets such as:
+            -pre-build
+            -pre-compile
+            -post-compile (This is typically used for code obfuscation.
+                           Compiled code location: ${out.classes.absolute.dir}
+                           If this is not done in place, override ${out.dex.input.absolute.dir})
+            -post-package
+            -post-build
+            -pre-clean
+    -->
+    <import file="custom_rules.xml" optional="true" />
+
+    <!-- Import the actual build file.
+
+         To customize existing targets, there are two options:
+         - Customize only one target:
+             - copy/paste the target into this file, *before* the
+               <import> task.
+             - customize it to your needs.
+         - Customize the whole content of build.xml
+             - copy/paste the content of the rules files (minus the top node)
+               into this file, replacing the <import> task.
+             - customize to your needs.
+
+         ***********************
+         ****** IMPORTANT ******
+         ***********************
+         In all cases you must update the value of version-tag below to read 'custom' instead of an integer,
+         in order to avoid having your file be overridden by tools such as "android update project"
+    -->
+    <!-- version-tag: 1 -->
+    <import file="${sdk.dir}/tools/ant/build.xml" />
+
+</project>

http://git-wip-us.apache.org/repos/asf/cordova-cli/blob/f4a9597a/spec/fixtures/platforms/android/cordova/build
----------------------------------------------------------------------
diff --git a/spec/fixtures/platforms/android/cordova/build b/spec/fixtures/platforms/android/cordova/build
new file mode 100755
index 0000000..7028eb8
--- /dev/null
+++ b/spec/fixtures/platforms/android/cordova/build
@@ -0,0 +1,35 @@
+#!/usr/bin/env node
+
+/*
+       Licensed to the Apache Software Foundation (ASF) under one
+       or more contributor license agreements.  See the NOTICE file
+       distributed with this work for additional information
+       regarding copyright ownership.  The ASF licenses this file
+       to you 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.
+*/
+
+var build = require('./lib/build'),
+    reqs  = require('./lib/check_reqs'),
+    args  = process.argv;
+
+// Support basic help commands
+if(args[2] == '--help' || args[2] == '/?' || args[2] == '-h' ||
+                    args[2] == 'help' || args[2] == '-help' || args[2] == '/help') {
+    build.help();
+} else if(reqs.run()) {
+    build.run(args[2]);
+    process.exit(0);
+} else {
+    process.exit(2);
+}

http://git-wip-us.apache.org/repos/asf/cordova-cli/blob/f4a9597a/spec/fixtures/platforms/android/cordova/check_reqs
----------------------------------------------------------------------
diff --git a/spec/fixtures/platforms/android/cordova/check_reqs b/spec/fixtures/platforms/android/cordova/check_reqs
new file mode 100755
index 0000000..4a8abee
--- /dev/null
+++ b/spec/fixtures/platforms/android/cordova/check_reqs
@@ -0,0 +1,27 @@
+#!/usr/bin/env node
+
+/*
+       Licensed to the Apache Software Foundation (ASF) under one
+       or more contributor license agreements.  See the NOTICE file
+       distributed with this work for additional information
+       regarding copyright ownership.  The ASF licenses this file
+       to you 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.
+*/
+
+var check_reqs = require('./lib/check_reqs');
+
+if(!check_reqs.run()) {
+      process.exit(2);
+}
+

http://git-wip-us.apache.org/repos/asf/cordova-cli/blob/f4a9597a/spec/fixtures/platforms/android/cordova/clean
----------------------------------------------------------------------
diff --git a/spec/fixtures/platforms/android/cordova/clean b/spec/fixtures/platforms/android/cordova/clean
new file mode 100755
index 0000000..70c4ca8
--- /dev/null
+++ b/spec/fixtures/platforms/android/cordova/clean
@@ -0,0 +1,34 @@
+#!/usr/bin/env node
+
+/*
+       Licensed to the Apache Software Foundation (ASF) under one
+       or more contributor license agreements.  See the NOTICE file
+       distributed with this work for additional information
+       regarding copyright ownership.  The ASF licenses this file
+       to you 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.
+*/
+
+var clean = require('./lib/clean'),
+    reqs  = require('./lib/check_reqs'),
+    args  = process.argv;
+
+// Usage support for when args are given
+if(args.length > 2) {
+    clean.help();
+} else if(reqs.run()) {
+    clean.run();
+    process.exit(0);
+} else {
+    process.exit(2);
+}

http://git-wip-us.apache.org/repos/asf/cordova-cli/blob/f4a9597a/spec/fixtures/platforms/android/cordova/defaults.xml
----------------------------------------------------------------------
diff --git a/spec/fixtures/platforms/android/cordova/defaults.xml b/spec/fixtures/platforms/android/cordova/defaults.xml
new file mode 100644
index 0000000..24e5725
--- /dev/null
+++ b/spec/fixtures/platforms/android/cordova/defaults.xml
@@ -0,0 +1,50 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<!--
+ Licensed to the Apache Software Foundation (ASF) under one
+ or more contributor license agreements.  See the NOTICE file
+ distributed with this work for additional information
+ regarding copyright ownership.  The ASF licenses this file
+ to you 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.
+-->
+<widget xmlns     = "http://www.w3.org/ns/widgets"
+        id        = "io.cordova.helloCordova"
+        version   = "2.0.0">
+    <name>Hello Cordova</name>
+
+    <description>
+        A sample Apache Cordova application that responds to the deviceready event.
+    </description>
+
+    <author href="http://cordova.io" email="dev@cordova.apache.org">
+        Apache Cordova Team
+    </author>
+
+    <access origin="*"/>
+
+    <!-- <content src="http://mysite.com/myapp.html" /> for external pages -->
+    <content src="index.html" />
+
+    <preference name="loglevel" value="DEBUG" />
+    <!--
+      <preference name="splashscreen" value="resourceName" />
+      <preference name="backgroundColor" value="0xFFF" />
+      <preference name="loadUrlTimeoutValue" value="20000" />
+      <preference name="InAppBrowserStorageEnabled" value="true" />
+      <preference name="disallowOverscroll" value="true" />
+    -->
+    <!-- This is required for native Android hooks -->
+    <feature name="App">
+        <param name="android-package" value="org.apache.cordova.App" />
+    </feature>
+</widget>

http://git-wip-us.apache.org/repos/asf/cordova-cli/blob/f4a9597a/spec/fixtures/platforms/android/cordova/lib/appinfo.js
----------------------------------------------------------------------
diff --git a/spec/fixtures/platforms/android/cordova/lib/appinfo.js b/spec/fixtures/platforms/android/cordova/lib/appinfo.js
new file mode 100755
index 0000000..1f8ebe2
--- /dev/null
+++ b/spec/fixtures/platforms/android/cordova/lib/appinfo.js
@@ -0,0 +1,41 @@
+#!/usr/bin/env node
+
+/*
+       Licensed to the Apache Software Foundation (ASF) under one
+       or more contributor license agreements.  See the NOTICE file
+       distributed with this work for additional information
+       regarding copyright ownership.  The ASF licenses this file
+       to you 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.
+*/
+
+var path = require('path');
+var fs = require('fs');
+var cachedAppInfo = null;
+
+function readAppInfoFromManifest() {
+    var manifestPath = path.join(__dirname, '..', '..', 'AndroidManifest.xml');
+    var manifestData = fs.readFileSync(manifestPath, {encoding:'utf8'});
+    var packageName = /\bpackage\s*=\s*"(.+?)"/.exec(manifestData);
+    if (!packageName) throw new Error('Could not find package name within ' + manifestPath);
+    var activityTag = /<activity\b[\s\S]*<\/activity>/.exec(manifestData);
+    if (!activityTag) throw new Error('Could not find <activity> within ' + manifestPath);
+    var activityName = /\bandroid:name\s*=\s*"(.+?)"/.exec(activityTag);
+    if (!activityName) throw new Error('Could not find android:name within ' + manifestPath);
+
+    return packageName[1] + '/.' + activityName[1];
+}
+
+exports.getActivityName = function() {
+    return cachedAppInfo = cachedAppInfo || readAppInfoFromManifest();
+};

http://git-wip-us.apache.org/repos/asf/cordova-cli/blob/f4a9597a/spec/fixtures/platforms/android/cordova/lib/build.js
----------------------------------------------------------------------
diff --git a/spec/fixtures/platforms/android/cordova/lib/build.js b/spec/fixtures/platforms/android/cordova/lib/build.js
new file mode 100755
index 0000000..7bc33ca
--- /dev/null
+++ b/spec/fixtures/platforms/android/cordova/lib/build.js
@@ -0,0 +1,89 @@
+#!/usr/bin/env node
+
+/*
+       Licensed to the Apache Software Foundation (ASF) under one
+       or more contributor license agreements.  See the NOTICE file
+       distributed with this work for additional information
+       regarding copyright ownership.  The ASF licenses this file
+       to you 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.
+*/
+
+var shell   = require('shelljs'),
+    clean   = require('./clean'),
+    path    = require('path'),
+    fs      = require('fs'),
+    ROOT    = path.join(__dirname, '..', '..');
+
+/*
+ * Builds the project with ant.
+ */
+module.exports.run = function(build_type) {
+    //default build type
+    build_type = typeof build_type !== 'undefined' ? build_type : "--debug";
+    var cmd;
+    switch(build_type) {
+        case '--debug' :
+            clean.run();
+            cmd = 'ant debug -f ' + path.join(ROOT, 'build.xml');
+            break;
+        case '--release' :
+            clean.run();
+            cmd = 'ant release -f ' + path.join(ROOT, 'build.xml');
+            break;
+        case '--nobuild' :
+            console.log('Skipping build...');
+            break;
+        default :
+           console.error('Build option \'' + build_type + '\' not recognized.');
+           process.exit(2);
+           break;
+    }
+    if(cmd) {
+        var result = shell.exec(cmd, {silent:false, async:false});
+        if(result.code > 0) {
+            console.error('ERROR: Failed to build android project.');
+            console.error(result.output);
+            process.exit(2);
+        }
+    }
+}
+
+/*
+ * Gets the path to the apk file, if not such file exists then
+ * the script will error out. (should we error or just return undefined?)
+ */
+module.exports.get_apk = function() {
+    if(fs.existsSync(path.join(ROOT, 'bin'))) {
+        var bin_files = fs.readdirSync(path.join(ROOT, 'bin'));
+        for (file in bin_files) {
+            if(path.extname(bin_files[file]) == '.apk') {
+                return path.join(ROOT, 'bin', bin_files[file]);
+            }
+        }
+        console.error('ERROR : No .apk found in \'bin\' folder');
+        process.exit(2);
+    } else {
+        console.error('ERROR : unable to find project bin folder, could not locate .apk');
+        process.exit(2);
+    }
+}
+
+module.exports.help = function() {
+    console.log('Usage: ' + path.relative(process.cwd(), path.join(ROOT, 'cordova', 'build')) + ' [build_type]');
+    console.log('Build Types : ');
+    console.log('    \'--debug\': Default build, will build project in using ant debug');
+    console.log('    \'--release\': will build project using ant release');
+    console.log('    \'--nobuild\': will skip build process (can be used with run command)');
+    process.exit(0);
+}

http://git-wip-us.apache.org/repos/asf/cordova-cli/blob/f4a9597a/spec/fixtures/platforms/android/cordova/lib/check_reqs.js
----------------------------------------------------------------------
diff --git a/spec/fixtures/platforms/android/cordova/lib/check_reqs.js b/spec/fixtures/platforms/android/cordova/lib/check_reqs.js
new file mode 100755
index 0000000..c064499
--- /dev/null
+++ b/spec/fixtures/platforms/android/cordova/lib/check_reqs.js
@@ -0,0 +1,78 @@
+#!/usr/bin/env node
+
+/*
+       Licensed to the Apache Software Foundation (ASF) under one
+       or more contributor license agreements.  See the NOTICE file
+       distributed with this work for additional information
+       regarding copyright ownership.  The ASF licenses this file
+       to you 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.
+*/
+
+var shell = require('shelljs'),
+    path  = require('path'),
+    fs    = require('fs'),
+    ROOT  = path.join(__dirname, '..', '..');
+
+// Get valid target from framework/project.properties
+module.exports.get_target = function() {
+    if(fs.existsSync(path.join(ROOT, 'framework', 'project.properties'))) {
+        var target = shell.grep(/target=android-[\d+]/, path.join(ROOT, 'framework', 'project.properties'));
+        return target.split('=')[1].replace('\n', '').replace('\r', '').replace(' ', '');
+    } else if (fs.existsSync(path.join(ROOT, 'project.properties'))) {
+        // if no target found, we're probably in a project and project.properties is in ROOT.
+        var target = shell.grep(/target=android-[\d+]/, path.join(ROOT, 'project.properties'));
+        return target.split('=')[1].replace('\n', '').replace('\r', '').replace(' ', '');
+    }
+}
+
+module.exports.check_ant = function() {
+    var test = shell.exec('ant -version', {silent:true, async:false});
+    if(test.code > 0) {
+        console.error('ERROR : executing command \'ant\', make sure you have ant installed and added to your path.');
+        return false;
+    }
+    return true;
+}
+
+module.exports.check_java = function() {
+    if(process.env.JAVA_HOME) {
+        var test = shell.exec('java', {silent:true, async:false});
+        if(test.code > 0) {
+            console.error('ERROR : executing command \'java\', make sure you java environment is set up. Including your JDK and JRE.');
+            return false;
+        }
+        return true;
+    } else {
+        console.error('ERROR : Make sure JAVA_HOME is set, as well as paths to your JDK and JRE for java.');
+        return false;
+    }
+}
+
+module.exports.check_android = function() {
+    var valid_target = this.get_target();
+    var targets = shell.exec('android list targets', {silent:true, async:false});
+
+    if(targets.code > 0 && targets.output.match(/command\snot\sfound/)) {
+        console.error('The command \"android\" failed. Make sure you have the latest Android SDK installed, and the \"android\" command (inside the tools/ folder) is added to your path.');
+        return false;
+    } else if(!targets.output.match(valid_target)) {
+        console.error('Please install Android target ' + valid_target.split('-')[1] + ' (the Android newest SDK). Make sure you have the latest Android tools installed as well. Run \"android\" from your command-line to install/update any missing SDKs or tools.');
+        return false;
+    }
+    return true;
+}
+
+module.exports.run = function() {
+    return this.check_ant() && this.check_java && this.check_android();
+}

http://git-wip-us.apache.org/repos/asf/cordova-cli/blob/f4a9597a/spec/fixtures/platforms/android/cordova/lib/clean.js
----------------------------------------------------------------------
diff --git a/spec/fixtures/platforms/android/cordova/lib/clean.js b/spec/fixtures/platforms/android/cordova/lib/clean.js
new file mode 100755
index 0000000..8f14015
--- /dev/null
+++ b/spec/fixtures/platforms/android/cordova/lib/clean.js
@@ -0,0 +1,43 @@
+#!/usr/bin/env node
+
+/*
+       Licensed to the Apache Software Foundation (ASF) under one
+       or more contributor license agreements.  See the NOTICE file
+       distributed with this work for additional information
+       regarding copyright ownership.  The ASF licenses this file
+       to you 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.
+*/
+
+var shell = require('shelljs'),
+    path  = require('path'),
+    ROOT = path.join(__dirname, '..', '..');
+
+/*
+ * Cleans the project using ant
+ */
+module.exports.run = function() {
+    var cmd = 'ant clean -f ' + path.join(ROOT, 'build.xml');
+    var result = shell.exec(cmd, {silent:false, async:false});
+    if (result.code > 0) {
+        console.error('ERROR: Failed to clean android project.');
+        console.error(result.output);
+        process.exit(2);
+    }
+}
+
+module.exports.help = function() {
+    console.log('Usage: ' + path.relative(process.cwd(), path.join(ROOT, 'cordova', 'clean')));
+    console.log('Cleans the project directory.');
+    process.exit(0);
+}

http://git-wip-us.apache.org/repos/asf/cordova-cli/blob/f4a9597a/spec/fixtures/platforms/android/cordova/lib/device.js
----------------------------------------------------------------------
diff --git a/spec/fixtures/platforms/android/cordova/lib/device.js b/spec/fixtures/platforms/android/cordova/lib/device.js
new file mode 100755
index 0000000..363dc2b
--- /dev/null
+++ b/spec/fixtures/platforms/android/cordova/lib/device.js
@@ -0,0 +1,95 @@
+#!/usr/bin/env node
+
+/*
+       Licensed to the Apache Software Foundation (ASF) under one
+       or more contributor license agreements.  See the NOTICE file
+       distributed with this work for additional information
+       regarding copyright ownership.  The ASF licenses this file
+       to you 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.
+*/
+
+var shell = require('shelljs'),
+    path  = require('path'),
+    build = require('./build'),
+    appinfo = require('./appinfo'),
+    exec  = require('child_process').exec,
+    ROOT = path.join(__dirname, '..', '..');
+
+/**
+ * Returns a list of the device ID's found
+ */
+module.exports.list = function() {
+    var cmd = 'adb devices';
+    var result = shell.exec(cmd, {silent:true, async:false});
+    if (result.code > 0) {
+        console.error('Failed to execute android command \'' + cmd + '\'.');
+        process.exit(2);
+    } else {
+        var response = result.output.split('\n');
+        var device_list = [];
+        for (var i = 1; i < response.length; i++) {
+            if (response[i].match(/\w+\tdevice/) && !response[i].match(/emulator/)) {
+                device_list.push(response[i].replace(/\tdevice/, '').replace('\r', ''));
+            }
+        }
+        return device_list;
+    }
+}
+
+/*
+ * Installs a previously built application on the device
+ * and launches it.
+ */
+module.exports.install = function(target) {
+    var device_list = this.list();
+    if (device_list.length > 0) {
+        // default device
+        target = typeof target !== 'undefined' ? target : device_list[0];
+        if (device_list.indexOf(target) > -1) {
+            var apk_path = build.get_apk();
+            var launchName = appinfo.getActivityName();
+            console.log('Installing app on device...');
+            cmd = 'adb -s ' + target + ' install -r ' + apk_path;
+            var install = shell.exec(cmd, {silent:false, async:false});
+            if (install.error || install.output.match(/Failure/)) {
+                console.error('ERROR : Failed to install apk to device : ');
+                console.error(install.output);
+                process.exit(2);
+            }
+
+            //unlock screen
+            cmd = 'adb -s ' + target + ' shell input keyevent 82';
+            shell.exec(cmd, {silent:true, async:false});
+
+            // launch the application
+            console.log('Launching application...');
+            cmd = 'adb -s ' + target + ' shell am start -W -a android.intent.action.MAIN -n ' + launchName;
+            var launch = shell.exec(cmd, {silent:true, async:false});
+            if(launch.code > 0) {
+                console.error('ERROR : Failed to launch application on emulator : ' + launch.error);
+                console.error(launch.output);
+                process.exit(2);
+            } else {
+                console.log('LAUNCH SUCCESS');
+            }
+        } else {
+            console.error('ERROR : Unable to find target \'' + target + '\'.');
+            console.error('Failed to deploy to device.');
+            process.exit(2);
+        }
+    } else {
+        console.error('ERROR : Failed to deploy to device, no devices found.');
+        process.exit(2);
+    }
+}

http://git-wip-us.apache.org/repos/asf/cordova-cli/blob/f4a9597a/spec/fixtures/platforms/android/cordova/lib/emulator.js
----------------------------------------------------------------------
diff --git a/spec/fixtures/platforms/android/cordova/lib/emulator.js b/spec/fixtures/platforms/android/cordova/lib/emulator.js
new file mode 100755
index 0000000..cc658a9
--- /dev/null
+++ b/spec/fixtures/platforms/android/cordova/lib/emulator.js
@@ -0,0 +1,337 @@
+#!/usr/bin/env node
+
+/*
+       Licensed to the Apache Software Foundation (ASF) under one
+       or more contributor license agreements.  See the NOTICE file
+       distributed with this work for additional information
+       regarding copyright ownership.  The ASF licenses this file
+       to you 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.
+*/
+
+var shell = require('shelljs'),
+    path  = require('path'),
+    appinfo = require('./appinfo'),
+    build = require('./build'),
+    ROOT  = path.join(__dirname, '..', '..'),
+    new_emulator = 'cordova_emulator';
+
+/**
+ * Returns a list of emulator images in the form of objects
+ * {
+       name   : <emulator_name>,
+       path   : <path_to_emulator_image>,
+       target : <api_target>,
+       abi    : <cpu>,
+       skin   : <skin>
+   }
+ */
+module.exports.list_images = function() {
+    var cmd = 'android list avds';
+    var result = shell.exec(cmd, {silent:true, async:false});
+    if (result.code > 0) {
+        console.error('Failed to execute android command \'' + cmd + '\'.');
+        process.exit(2);
+    } else {
+        var response = result.output.split('\n');
+        var emulator_list = [];
+        for (var i = 1; i < response.length; i++) {
+            // To return more detailed information use img_obj
+            var img_obj = {};
+            if (response[i].match(/Name:\s/)) {
+                img_obj['name'] = response[i].split('Name: ')[1].replace('\r', '');
+                if (response[i + 1].match(/Path:\s/)) {
+                    i++;
+                    img_obj['path'] = response[i].split('Path: ')[1].replace('\r', '');
+                }
+                if (response[i + 1].match(/\(API\slevel\s/)) {
+                    i++;
+                    img_obj['target'] = response[i].replace('\r', '');
+                }
+                if (response[i + 1].match(/ABI:\s/)) {
+                    i++;
+                    img_obj['abi'] = response[i].split('ABI: ')[1].replace('\r', '');
+                }
+                if (response[i + 1].match(/Skin:\s/)) {
+                    i++;
+                    img_obj['skin'] = response[i].split('Skin: ')[1].replace('\r', '');
+                }
+
+                emulator_list.push(img_obj);
+            }
+            /* To just return a list of names use this
+            if (response[i].match(/Name:\s/)) {
+                emulator_list.push(response[i].split('Name: ')[1].replace('\r', '');
+            }*/
+
+        }
+        return emulator_list;
+    }
+}
+
+/**
+ * Will return the closest avd to the projects target
+ * or undefined if no avds exist.
+ */
+module.exports.best_image = function() {
+    var project_target = this.get_target().replace('android-', '');
+    var images = this.list_images();
+    var closest = 9999;
+    var best = images[0];
+    for (i in images) {
+        var target = images[i].target;
+        if(target) {
+            var num = target.split('(API level ')[1].replace(')', '');
+            if (num == project_target) {
+                return images[i];
+            } else if (project_target - num < closest && project_target > num) {
+                var closest = project_target - num;
+                best = images[i];
+            }
+        }
+    }
+    return best;
+}
+
+module.exports.list_started = function() {
+    var cmd = 'adb devices';
+    var result = shell.exec(cmd, {silent:true, async:false});
+    if (result.code > 0) {
+        console.error('Failed to execute android command \'' + cmd + '\'.');
+        process.exit(2);
+    } else {
+        var response = result.output.split('\n');
+        var started_emulator_list = [];
+        for (var i = 1; i < response.length; i++) {
+            if (response[i].match(/device/) && response[i].match(/emulator/)) {
+                started_emulator_list.push(response[i].replace(/\tdevice/, '').replace('\r', ''));
+            }
+        }
+        return started_emulator_list;
+    }
+}
+
+module.exports.get_target = function() {
+    var target = shell.grep(/target=android-[\d+]/, path.join(ROOT, 'project.properties'));
+    return target.split('=')[1].replace('\n', '').replace('\r', '').replace(' ', '');
+}
+
+module.exports.list_targets = function() {
+    var target_out = shell.exec('android list targets', {silent:true, async:false}).output.split('\n');
+    var targets = [];
+    for (var i = target_out.length; i >= 0; i--) {
+        if(target_out[i].match(/id:/)) {
+            targets.push(targets[i].split(' ')[1]);
+        }
+    }
+    return targets;
+}
+
+/*
+ * Starts an emulator with the given ID,
+ * and returns the started ID of that emulator.
+ * If no ID is given it will used the first image availible,
+ * if no image is availible it will error out (maybe create one?).
+ */
+module.exports.start = function(emulator_ID) {
+    var started_emulators = this.list_started();
+    var num_started = started_emulators.length;
+    if (typeof emulator_ID === 'undefined') {
+        var emulator_list = this.list_images();
+        if (emulator_list.length > 0) {
+            emulator_ID = this.best_image().name;
+            console.log('WARNING : no emulator specified, defaulting to ' + emulator_ID);
+        } else {
+            console.error('ERROR : No emulator images (avds) found, if you would like to create an');
+            console.error(' avd follow the instructions provided here : ');
+            console.error(' http://developer.android.com/tools/devices/index.html')
+            console.error(' Or run \'android create avd --name <name> --target <targetID>\' ');
+            console.error(' in on the command line.');
+            process.exit(2);
+            /*console.log('WARNING : no emulators availible, creating \'' + new_emulator + '\'.');
+            this.create_image(new_emulator, this.get_target());
+            emulator_ID = new_emulator;*/
+        }
+    }
+
+    var pipe_null = (process.platform == 'win32' || process.platform == 'win64'? '> NUL' : '> /dev/null');
+    var cmd = 'emulator -avd ' + emulator_ID + ' ' + pipe_null + ' &';
+    if(process.platform == 'win32' || process.platform == 'win64') {
+        cmd = '%comspec% /c start cmd /c ' + cmd;
+    }
+    var result = shell.exec(cmd, {silent:true, async:false}, function(code, output) {
+        if (code > 0) {
+            console.error('Failed to execute android command \'' + cmd + '\'.');
+            console.error(output);
+            process.exit(2);
+        }
+    });
+
+    // wait for emulator to start
+    console.log('Waiting for emulator...');
+    var new_started = this.wait_for_emulator(num_started);
+    var emulator_id;
+    if (new_started.length > 1) {
+        for (i in new_started) {
+            console.log(new_started[i]);
+            console.log(started_emulators.indexOf(new_started[i]));
+            if (started_emulators.indexOf(new_started[i]) < 0) {
+                emulator_id = new_started[i];
+            }
+        }
+    } else {
+        emulator_id = new_started[0];
+    }
+    if (!emulator_id) {
+        console.error('ERROR :  Failed to start emulator, could not find new emulator');
+        process.exit(2);
+    }
+
+    //wait for emulator to boot up
+    process.stdout.write('Booting up emulator (this may take a while)...');
+    this.wait_for_boot(emulator_id);
+    console.log('BOOT COMPLETE');
+
+    //unlock screen
+    cmd = 'adb -s ' + emulator_id + ' shell input keyevent 82';
+    shell.exec(cmd, {silent:false, async:false});
+
+    //return the new emulator id for the started emulators
+    return emulator_id;
+}
+
+/*
+ * Waits for the new emulator to apear on the started-emulator list.
+ */
+module.exports.wait_for_emulator = function(num_running) {
+    var new_started = this.list_started();
+    if (new_started.length > num_running) {
+        return new_started;
+    } else {
+        this.sleep(1);
+        return this.wait_for_emulator(num_running);
+    }
+}
+
+/*
+ * Waits for the boot animation property of the emulator to switch to 'stopped'
+ */
+module.exports.wait_for_boot = function(emulator_id) {
+    var cmd;
+    // ShellJS opens a lot of file handles, and the default on OS X is too small.
+    // TODO : This is not working, need to find a better way to increese the ulimit.
+    if(process.platform == 'win32' || process.platform == 'win64') {
+        cmd = 'adb -s ' + emulator_id + ' shell getprop init.svc.bootanim';
+    } else {
+        cmd = 'ulimit -S -n 4096; adb -s ' + emulator_id + ' shell getprop init.svc.bootanim';
+    }
+    var boot_anim = shell.exec(cmd, {silent:true, async:false});
+    if (boot_anim.output.match(/stopped/)) {
+        return;
+    } else {
+        process.stdout.write('.');
+        this.sleep(3);
+        return this.wait_for_boot(emulator_id);
+    }
+}
+
+/*
+ * TODO : find a better way to wait for the emulator (maybe using async methods?)
+ */
+module.exports.sleep = function(time_sec) {
+    if (process.platform == 'win32' || process.platform == 'win64') {
+        shell.exec('ping 127.0.0.1 -n ' + time_sec, {silent:true, async:false});
+    } else {
+        shell.exec('sleep ' + time_sec, {silent:true, async:false});
+    }
+}
+
+/*
+ * Create avd
+ * TODO : Enter the stdin input required to complete the creation of an avd.
+ */
+module.exports.create_image = function(name, target) {
+    console.log('Creating avd named ' + name);
+    if (target) {
+        var cmd = 'android create avd --name ' + name + ' --target ' + target;
+        var create = shell.exec(cmd, {sient:false, async:false});
+        if (create.error) {
+            console.error('ERROR : Failed to create emulator image : ');
+            console.error(' Do you have the latest android targets including ' + target + '?');
+            console.error(create.output);
+            process.exit(2);
+        }
+    } else {
+        console.log('WARNING : Project target not found, creating avd with a different target but the project may fail to install.');
+        var cmd = 'android create avd --name ' + name + ' --target ' + this.list_targets()[0];
+        var create = shell.exec(cmd, {sient:false, async:false});
+        if (create.error) {
+            console.error('ERROR : Failed to create emulator image : ');
+            console.error(create.output);
+            process.exit(2);
+        }
+        console.error('ERROR : Unable to create an avd emulator, no targets found.');
+        console.error('Please insure you have targets availible by runing the "android" command').
+        process.exit(2);
+    }
+}
+
+/*
+ * Installs a previously built application on the emulator and launches it.
+ * If no target is specified, then it picks one.
+ * If no started emulators are found, error out.
+ */
+module.exports.install = function(target) {
+    var emulator_list = this.list_started();
+    if (emulator_list.length < 1) {
+        console.error('ERROR : No started emulators found, please start an emultor before deploying your project.');
+        process.exit(2);
+        /*console.log('WARNING : No started emulators found, attemting to start an avd...');
+        this.start(this.best_image().name);*/
+    }
+    // default emulator
+    target = typeof target !== 'undefined' ? target : emulator_list[0];
+    if (emulator_list.indexOf(target) > -1) {
+        console.log('Installing app on emulator...');
+        var apk_path = build.get_apk();
+        var cmd = 'adb -s ' + target + ' install -r ' + apk_path;
+        var install = shell.exec(cmd, {sient:false, async:false});
+        if (install.error || install.output.match(/Failure/)) {
+            console.error('ERROR : Failed to install apk to emulator : ');
+            console.error(install.output);
+            process.exit(2);
+        }
+
+        //unlock screen
+        cmd = 'adb -s ' + target + ' shell input keyevent 82';
+        shell.exec(cmd, {silent:true, async:false});
+
+        // launch the application
+        console.log('Launching application...');
+        var launchName = appinfo.getActivityName();
+        cmd = 'adb -s ' + target + ' shell am start -W -a android.intent.action.MAIN -n ' + launchName;
+        console.log(cmd);
+        var launch = shell.exec(cmd, {silent:false, async:false});
+        if(launch.code > 0) {
+            console.error('ERROR : Failed to launch application on emulator : ' + launch.error);
+            console.error(launch.output);
+            process.exit(2);
+        } else {
+            console.log('LAUNCH SUCCESS');
+        }
+    } else {
+        console.error('ERROR : Unable to find target \'' + target + '\'.');
+        console.error('Failed to deploy to emulator.');
+        process.exit(2);
+    }
+}

http://git-wip-us.apache.org/repos/asf/cordova-cli/blob/f4a9597a/spec/fixtures/platforms/android/cordova/lib/install-device
----------------------------------------------------------------------
diff --git a/spec/fixtures/platforms/android/cordova/lib/install-device b/spec/fixtures/platforms/android/cordova/lib/install-device
new file mode 100755
index 0000000..679efbf
--- /dev/null
+++ b/spec/fixtures/platforms/android/cordova/lib/install-device
@@ -0,0 +1,38 @@
+#!/usr/bin/env node
+
+/*
+       Licensed to the Apache Software Foundation (ASF) under one
+       or more contributor license agreements.  See the NOTICE file
+       distributed with this work for additional information
+       regarding copyright ownership.  The ASF licenses this file
+       to you 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.
+*/
+
+var device = require('./device'),
+    args   = process.argv;
+
+if(args.length > 2) {
+    var install_target;
+    if (args[2].substring(0, 9) == '--target=') {
+        install_target = args[2].substring(9, args[2].length);
+        device.install(install_target);
+        process.exit(0);
+     } else {
+        console.error('ERROR : argument \'' + args[2] + '\' not recognized.');
+        process.exit(2);
+     }
+} else {
+    device.install();
+    process.exit(0);
+}

http://git-wip-us.apache.org/repos/asf/cordova-cli/blob/f4a9597a/spec/fixtures/platforms/android/cordova/lib/install-emulator
----------------------------------------------------------------------
diff --git a/spec/fixtures/platforms/android/cordova/lib/install-emulator b/spec/fixtures/platforms/android/cordova/lib/install-emulator
new file mode 100755
index 0000000..c006eb2
--- /dev/null
+++ b/spec/fixtures/platforms/android/cordova/lib/install-emulator
@@ -0,0 +1,38 @@
+#!/usr/bin/env node
+
+/*
+       Licensed to the Apache Software Foundation (ASF) under one
+       or more contributor license agreements.  See the NOTICE file
+       distributed with this work for additional information
+       regarding copyright ownership.  The ASF licenses this file
+       to you 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.
+*/
+
+var emulator = require('./emulator'),
+    args     = process.argv;
+
+if(args.length > 2) {
+    var install_target;
+    if (args[2].substring(0, 9) == '--target=') {
+        install_target = args[2].substring(9, args[2].length);
+        emulator.install(install_target);
+        process.exit(0);
+     } else {
+        console.error('ERROR : argument \'' + args[2] + '\' not recognized.');
+        process.exit(2);
+     }
+} else {
+    emulator.install();
+    process.exit(0);
+}

http://git-wip-us.apache.org/repos/asf/cordova-cli/blob/f4a9597a/spec/fixtures/platforms/android/cordova/lib/list-devices
----------------------------------------------------------------------
diff --git a/spec/fixtures/platforms/android/cordova/lib/list-devices b/spec/fixtures/platforms/android/cordova/lib/list-devices
new file mode 100755
index 0000000..3ef4efa
--- /dev/null
+++ b/spec/fixtures/platforms/android/cordova/lib/list-devices
@@ -0,0 +1,28 @@
+#!/usr/bin/env node
+
+/*
+       Licensed to the Apache Software Foundation (ASF) under one
+       or more contributor license agreements.  See the NOTICE file
+       distributed with this work for additional information
+       regarding copyright ownership.  The ASF licenses this file
+       to you 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.
+*/
+
+var devices = require('./device');
+
+// Usage support for when args are given
+var device_list = devices.list();
+for(device in device_list) {
+    console.log(device_list[device]);
+}

http://git-wip-us.apache.org/repos/asf/cordova-cli/blob/f4a9597a/spec/fixtures/platforms/android/cordova/lib/list-emulator-images
----------------------------------------------------------------------
diff --git a/spec/fixtures/platforms/android/cordova/lib/list-emulator-images b/spec/fixtures/platforms/android/cordova/lib/list-emulator-images
new file mode 100755
index 0000000..3230537
--- /dev/null
+++ b/spec/fixtures/platforms/android/cordova/lib/list-emulator-images
@@ -0,0 +1,29 @@
+#!/usr/bin/env node
+
+/*
+       Licensed to the Apache Software Foundation (ASF) under one
+       or more contributor license agreements.  See the NOTICE file
+       distributed with this work for additional information
+       regarding copyright ownership.  The ASF licenses this file
+       to you 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.
+*/
+
+var emulators = require('./emulator');
+
+// Usage support for when args are given
+var emulator_list = emulators.list_images();
+for(emulator in emulator_list) {
+    console.log(emulator_list[emulator].name);
+    process.exit(0);
+}

http://git-wip-us.apache.org/repos/asf/cordova-cli/blob/f4a9597a/spec/fixtures/platforms/android/cordova/lib/list-started-emulators
----------------------------------------------------------------------
diff --git a/spec/fixtures/platforms/android/cordova/lib/list-started-emulators b/spec/fixtures/platforms/android/cordova/lib/list-started-emulators
new file mode 100755
index 0000000..525a64c
--- /dev/null
+++ b/spec/fixtures/platforms/android/cordova/lib/list-started-emulators
@@ -0,0 +1,29 @@
+#!/usr/bin/env node
+
+/*
+       Licensed to the Apache Software Foundation (ASF) under one
+       or more contributor license agreements.  See the NOTICE file
+       distributed with this work for additional information
+       regarding copyright ownership.  The ASF licenses this file
+       to you 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.
+*/
+
+var emulators = require('./emulator');
+
+// Usage support for when args are given
+var emulator_list = emulators.list_started();
+for(emulator in emulator_list) {
+    console.log(emulator_list[emulator]);
+    process.exit(0);
+}

http://git-wip-us.apache.org/repos/asf/cordova-cli/blob/f4a9597a/spec/fixtures/platforms/android/cordova/lib/log.js
----------------------------------------------------------------------
diff --git a/spec/fixtures/platforms/android/cordova/lib/log.js b/spec/fixtures/platforms/android/cordova/lib/log.js
new file mode 100755
index 0000000..b85cf60
--- /dev/null
+++ b/spec/fixtures/platforms/android/cordova/lib/log.js
@@ -0,0 +1,43 @@
+#!/usr/bin/env node
+
+/*
+       Licensed to the Apache Software Foundation (ASF) under one
+       or more contributor license agreements.  See the NOTICE file
+       distributed with this work for additional information
+       regarding copyright ownership.  The ASF licenses this file
+       to you 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.
+*/
+
+var shell = require('shelljs'),
+    path  = require('path'),
+    ROOT = path.join(__dirname, '..', '..');
+
+/*
+ * Starts running logcat in the shell.
+ */
+module.exports.run = function() {
+    var cmd = 'adb logcat | grep -v nativeGetEnabledTags';
+    var result = shell.exec(cmd, {silent:false, async:false});
+    if (result.code > 0) {
+        console.error('ERROR: Failed to run logcat command.');
+        console.error(result.output);
+        process.exit(2);
+    }
+}
+
+module.exports.help = function() {
+    console.log('Usage: ' + path.relative(process.cwd(), path.join(ROOT, 'cordova', 'log')));
+    console.log('Gives the logcat output on the command line.');
+    process.exit(0);
+}

http://git-wip-us.apache.org/repos/asf/cordova-cli/blob/f4a9597a/spec/fixtures/platforms/android/cordova/lib/run.js
----------------------------------------------------------------------
diff --git a/spec/fixtures/platforms/android/cordova/lib/run.js b/spec/fixtures/platforms/android/cordova/lib/run.js
new file mode 100755
index 0000000..787d123
--- /dev/null
+++ b/spec/fixtures/platforms/android/cordova/lib/run.js
@@ -0,0 +1,124 @@
+#!/usr/bin/env node
+
+/*
+       Licensed to the Apache Software Foundation (ASF) under one
+       or more contributor license agreements.  See the NOTICE file
+       distributed with this work for additional information
+       regarding copyright ownership.  The ASF licenses this file
+       to you 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.
+*/
+
+var path  = require('path'),
+    build = require('./build'),
+    emulator = require('./emulator'),
+    device   = require('./device'),
+    ROOT = path.join(__dirname, '..', '..');
+
+/*
+ * Runs the application on a device if availible.
+ * If not device is found, it will use a started emulator.
+ * If no started emulators are found it will attempt to start an avd.
+ * If no avds are found it will error out.
+ */
+ module.exports.run = function(args) {
+    var build_type;
+    var install_target;
+
+    for (var i=2; i<args.length; i++) {
+        if (args[i] == '--debug') {
+            build_type = '--debug';
+        } else if (args[i] == '--release') {
+            build_type = '--release';
+        } else if (args[i] == '--nobuild') {
+            build_type = '--nobuild';
+        } else if (args[i] == '--device') {
+            install_target = '--device';
+        } else if (args[i] == '--emulator') {
+            install_target = '--emulator';
+        } else if (args[i].substring(0, 9) == '--target=') {
+            install_target = args[i].substring(9, args[i].length);
+        } else {
+            console.error('ERROR : Run option \'' + args[i] + '\' not recognized.');
+            process.exit(2);
+        }
+    }
+    build.run(build_type);
+    if (install_target == '--device') {
+        device.install();
+    } else if (install_target == '--emulator') {
+        if (emulator.list_started() == 0) {
+            emulator.start();
+        }
+        emulator.install();
+    } else if (install_target) {
+        var devices = device.list();
+        var started_emulators = emulator.list_started();
+        var avds = emulator.list_images();
+        if (devices.indexOf(install_target) > -1) {
+            device.install(install_target);
+        } else if (started_emulators.indexOf(install_target) > -1) {
+            emulator.install(install_target);
+        } else {
+            // if target emulator isn't started, then start it.
+            var emulator_ID;
+            for(avd in avds) {
+                if(avds[avd].name == install_target) {
+                    emulator_ID = emulator.start(install_target);
+                    emulator.install(emulator_ID);
+                    break;
+                }
+            }
+            if(!emulator_ID) {
+                console.error('ERROR : Target \'' + install_target + '\' not found, unalbe to run project');
+                process.exit(2);
+            }
+        }
+    } else {
+        // no target given, deploy to device if availible, otherwise use the emulator.
+        var device_list = device.list();
+        if (device_list.length > 0) {
+            console.log('WARNING : No target specified, deploying to device \'' + device_list[0] + '\'.');
+            device.install(device_list[0])
+        } else {
+            var emulator_list = emulator.list_started();
+            if (emulator_list.length > 0) {
+                console.log('WARNING : No target specified, deploying to emulator \'' + emulator_list[0] + '\'.');
+                emulator.install(emulator_list[0]);
+            } else {
+                console.log('WARNING : No started emulators found, starting an emulator.');
+                var best_avd = emulator.best_image();
+                if(best_avd) {
+                    var emulator_ID = emulator.start(best_avd.name);
+                    console.log('WARNING : No target specified, deploying to emulator \'' + emulator_ID + '\'.');
+                    emulator.install(emulator_ID);
+                } else {
+                    emulator.start();
+                }
+            }
+        }
+    }
+}
+
+module.exports.help = function() {
+    console.log('Usage: ' + path.relative(process.cwd(), path.join(ROOT, 'cordova', 'run')) + ' [options]');
+    console.log('Build options :');
+    console.log('    --debug : Builds project in debug mode');
+    console.log('    --release : Builds project in release mode');
+    console.log('    --nobuild : Runs the currently built project without recompiling');
+    console.log('Deploy options :');
+    console.log('    --device : Will deploy the built project to a device');
+    console.log('    --emulator : Will deploy the built project to an emulator if one exists');
+    console.log('    --target=<target_id> : Installs to the target with the specified id.');
+    process.exit(0);
+}

http://git-wip-us.apache.org/repos/asf/cordova-cli/blob/f4a9597a/spec/fixtures/platforms/android/cordova/lib/start-emulator
----------------------------------------------------------------------
diff --git a/spec/fixtures/platforms/android/cordova/lib/start-emulator b/spec/fixtures/platforms/android/cordova/lib/start-emulator
new file mode 100755
index 0000000..5d6c4dd
--- /dev/null
+++ b/spec/fixtures/platforms/android/cordova/lib/start-emulator
@@ -0,0 +1,38 @@
+#!/usr/bin/env node
+
+/*
+       Licensed to the Apache Software Foundation (ASF) under one
+       or more contributor license agreements.  See the NOTICE file
+       distributed with this work for additional information
+       regarding copyright ownership.  The ASF licenses this file
+       to you 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.
+*/
+
+var emulator = require('./emulator'),
+      args   = process.argv;
+
+if(args.length > 2) {
+    var install_target;
+    if (args[2].substring(0, 9) == '--target=') {
+        install_target = args[2].substring(9, args[2].length);
+        emulator.start(install_target);
+        process.exit(0);
+     } else {
+        console.error('ERROR : argument \'' + args[2] + '\' not recognized.');
+        process.exit(2);
+     }
+} else {
+    emulator.start();
+    process.exit(0);
+}

http://git-wip-us.apache.org/repos/asf/cordova-cli/blob/f4a9597a/spec/fixtures/platforms/android/cordova/log
----------------------------------------------------------------------
diff --git a/spec/fixtures/platforms/android/cordova/log b/spec/fixtures/platforms/android/cordova/log
new file mode 100755
index 0000000..087325f
--- /dev/null
+++ b/spec/fixtures/platforms/android/cordova/log
@@ -0,0 +1,33 @@
+#!/usr/bin/env node
+
+/*
+       Licensed to the Apache Software Foundation (ASF) under one
+       or more contributor license agreements.  See the NOTICE file
+       distributed with this work for additional information
+       regarding copyright ownership.  The ASF licenses this file
+       to you 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.
+*/
+
+var log  = require('./lib/log'),
+    reqs = require('./lib/check_reqs'),
+    args = process.argv;
+
+// Usage support for when args are given
+if(args.length > 2) {
+    log.help();
+} else if(reqs.run()) {
+    log.run();
+} else {
+    process.exit(2);
+}

http://git-wip-us.apache.org/repos/asf/cordova-cli/blob/f4a9597a/spec/fixtures/platforms/android/cordova/run
----------------------------------------------------------------------
diff --git a/spec/fixtures/platforms/android/cordova/run b/spec/fixtures/platforms/android/cordova/run
new file mode 100755
index 0000000..57d7345
--- /dev/null
+++ b/spec/fixtures/platforms/android/cordova/run
@@ -0,0 +1,35 @@
+#!/usr/bin/env node
+
+/*
+       Licensed to the Apache Software Foundation (ASF) under one
+       or more contributor license agreements.  See the NOTICE file
+       distributed with this work for additional information
+       regarding copyright ownership.  The ASF licenses this file
+       to you 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.
+*/
+
+var run  = require('./lib/run'),
+    reqs = require('./lib/check_reqs'),
+    args = process.argv;
+
+// Support basic help commands
+if (args[2] == '--help' || args[2] == '/?' || args[2] == '-h' ||
+                    args[2] == 'help' || args[2] == '-help' || args[2] == '/help') {
+    run.help();
+} else if(reqs.run()) {
+    run.run(args);
+    process.exit(0);
+} else {
+    process.exit(2);
+}

http://git-wip-us.apache.org/repos/asf/cordova-cli/blob/f4a9597a/spec/fixtures/platforms/android/cordova/version
----------------------------------------------------------------------
diff --git a/spec/fixtures/platforms/android/cordova/version b/spec/fixtures/platforms/android/cordova/version
new file mode 100755
index 0000000..de1a76d
--- /dev/null
+++ b/spec/fixtures/platforms/android/cordova/version
@@ -0,0 +1,25 @@
+#!/usr/bin/env node
+
+/*
+       Licensed to the Apache Software Foundation (ASF) under one
+       or more contributor license agreements.  See the NOTICE file
+       distributed with this work for additional information
+       regarding copyright ownership.  The ASF licenses this file
+       to you 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.
+*/
+
+// Coho updates this line:
+var VERSION = "3.1.0";
+
+console.log(VERSION);

http://git-wip-us.apache.org/repos/asf/cordova-cli/blob/f4a9597a/spec/fixtures/platforms/android/local.properties
----------------------------------------------------------------------
diff --git a/spec/fixtures/platforms/android/local.properties b/spec/fixtures/platforms/android/local.properties
new file mode 100644
index 0000000..d3f5072
--- /dev/null
+++ b/spec/fixtures/platforms/android/local.properties
@@ -0,0 +1,10 @@
+# This file is automatically generated by Android Tools.
+# Do not modify this file -- YOUR CHANGES WILL BE ERASED!
+#
+# This file must *NOT* be checked into Version Control Systems,
+# as it contains information specific to your local configuration.
+
+# location of the SDK. This is only used by Ant
+# For customization when using a Version Control System, please read the
+# header note.
+sdk.dir=/Users/braden/cordova/android/android-sdk-macosx

http://git-wip-us.apache.org/repos/asf/cordova-cli/blob/f4a9597a/spec/fixtures/platforms/android/proguard-project.txt
----------------------------------------------------------------------
diff --git a/spec/fixtures/platforms/android/proguard-project.txt b/spec/fixtures/platforms/android/proguard-project.txt
new file mode 100644
index 0000000..f2fe155
--- /dev/null
+++ b/spec/fixtures/platforms/android/proguard-project.txt
@@ -0,0 +1,20 @@
+# To enable ProGuard in your project, edit project.properties
+# to define the proguard.config property as described in that file.
+#
+# Add project specific ProGuard rules here.
+# By default, the flags in this file are appended to flags specified
+# in ${sdk.dir}/tools/proguard/proguard-android.txt
+# You can edit the include path and order by changing the ProGuard
+# include property in project.properties.
+#
+# For more details, see
+#   http://developer.android.com/guide/developing/tools/proguard.html
+
+# Add any project specific keep options here:
+
+# If your project uses WebView with JS, uncomment the following
+# and specify the fully qualified class name to the JavaScript interface
+# class:
+#-keepclassmembers class fqcn.of.javascript.interface.for.webview {
+#   public *;
+#}

http://git-wip-us.apache.org/repos/asf/cordova-cli/blob/f4a9597a/spec/fixtures/platforms/android/project.properties
----------------------------------------------------------------------
diff --git a/spec/fixtures/platforms/android/project.properties b/spec/fixtures/platforms/android/project.properties
new file mode 100644
index 0000000..a3ee5ab
--- /dev/null
+++ b/spec/fixtures/platforms/android/project.properties
@@ -0,0 +1,14 @@
+# This file is automatically generated by Android Tools.
+# Do not modify this file -- YOUR CHANGES WILL BE ERASED!
+#
+# This file must be checked in Version Control Systems.
+#
+# To customize properties used by the Ant build system edit
+# "ant.properties", and override values to adapt the script to your
+# project structure.
+#
+# To enable ProGuard to shrink and obfuscate your code, uncomment this (available properties: sdk.dir, user.home):
+#proguard.config=${sdk.dir}/tools/proguard/proguard-android.txt:proguard-project.txt
+
+# Project target.
+target=android-17

http://git-wip-us.apache.org/repos/asf/cordova-cli/blob/f4a9597a/spec/fixtures/platforms/android/res/drawable-hdpi/icon.png
----------------------------------------------------------------------
diff --git a/spec/fixtures/platforms/android/res/drawable-hdpi/icon.png b/spec/fixtures/platforms/android/res/drawable-hdpi/icon.png
new file mode 100644
index 0000000..4d27634
Binary files /dev/null and b/spec/fixtures/platforms/android/res/drawable-hdpi/icon.png differ

http://git-wip-us.apache.org/repos/asf/cordova-cli/blob/f4a9597a/spec/fixtures/platforms/android/res/drawable-ldpi/icon.png
----------------------------------------------------------------------
diff --git a/spec/fixtures/platforms/android/res/drawable-ldpi/icon.png b/spec/fixtures/platforms/android/res/drawable-ldpi/icon.png
new file mode 100644
index 0000000..cd5032a
Binary files /dev/null and b/spec/fixtures/platforms/android/res/drawable-ldpi/icon.png differ

http://git-wip-us.apache.org/repos/asf/cordova-cli/blob/f4a9597a/spec/fixtures/platforms/android/res/drawable-mdpi/icon.png
----------------------------------------------------------------------
diff --git a/spec/fixtures/platforms/android/res/drawable-mdpi/icon.png b/spec/fixtures/platforms/android/res/drawable-mdpi/icon.png
new file mode 100644
index 0000000..e79c606
Binary files /dev/null and b/spec/fixtures/platforms/android/res/drawable-mdpi/icon.png differ

http://git-wip-us.apache.org/repos/asf/cordova-cli/blob/f4a9597a/spec/fixtures/platforms/android/res/drawable-xhdpi/icon.png
----------------------------------------------------------------------
diff --git a/spec/fixtures/platforms/android/res/drawable-xhdpi/icon.png b/spec/fixtures/platforms/android/res/drawable-xhdpi/icon.png
new file mode 100644
index 0000000..ec7ffbf
Binary files /dev/null and b/spec/fixtures/platforms/android/res/drawable-xhdpi/icon.png differ

http://git-wip-us.apache.org/repos/asf/cordova-cli/blob/f4a9597a/spec/fixtures/platforms/android/res/drawable/icon.png
----------------------------------------------------------------------
diff --git a/spec/fixtures/platforms/android/res/drawable/icon.png b/spec/fixtures/platforms/android/res/drawable/icon.png
new file mode 100644
index 0000000..ec7ffbf
Binary files /dev/null and b/spec/fixtures/platforms/android/res/drawable/icon.png differ

http://git-wip-us.apache.org/repos/asf/cordova-cli/blob/f4a9597a/spec/fixtures/platforms/android/res/values/strings.xml
----------------------------------------------------------------------
diff --git a/spec/fixtures/platforms/android/res/values/strings.xml b/spec/fixtures/platforms/android/res/values/strings.xml
new file mode 100644
index 0000000..1e706b3
--- /dev/null
+++ b/spec/fixtures/platforms/android/res/values/strings.xml
@@ -0,0 +1,4 @@
+<?xml version='1.0' encoding='utf-8'?>
+<resources>
+    <string name="app_name">TestBase</string>
+</resources>

http://git-wip-us.apache.org/repos/asf/cordova-cli/blob/f4a9597a/spec/fixtures/platforms/android/res/xml/config.xml
----------------------------------------------------------------------
diff --git a/spec/fixtures/platforms/android/res/xml/config.xml b/spec/fixtures/platforms/android/res/xml/config.xml
new file mode 100644
index 0000000..17ca237
--- /dev/null
+++ b/spec/fixtures/platforms/android/res/xml/config.xml
@@ -0,0 +1,18 @@
+<?xml version='1.0' encoding='utf-8'?>
+<widget id="org.testing" version="0.0.1" xmlns="http://www.w3.org/ns/widgets" xmlns:cdv="http://cordova.apache.org/ns/1.0">
+    <name>Hello Cordova</name>
+    <description>
+        A sample Apache Cordova application that responds to the deviceready event.
+    </description>
+    <access origin="*" />
+    <preference name="loglevel" value="DEBUG" />
+    <feature name="App">
+        <param name="android-package" value="org.apache.cordova.App" />
+    </feature>
+    <author email="dev@cordova.apache.org" href="http://cordova.io">
+        Apache Cordova Team
+    </author>
+    <content src="index.html" />
+    <preference name="fullscreen" value="true" />
+    <preference name="webviewbounce" value="true" />
+</widget>

http://git-wip-us.apache.org/repos/asf/cordova-cli/blob/f4a9597a/spec/fixtures/platforms/android/src/org/testing/TestBase.java
----------------------------------------------------------------------
diff --git a/spec/fixtures/platforms/android/src/org/testing/TestBase.java b/spec/fixtures/platforms/android/src/org/testing/TestBase.java
new file mode 100644
index 0000000..928e074
--- /dev/null
+++ b/spec/fixtures/platforms/android/src/org/testing/TestBase.java
@@ -0,0 +1,37 @@
+/*
+       Licensed to the Apache Software Foundation (ASF) under one
+       or more contributor license agreements.  See the NOTICE file
+       distributed with this work for additional information
+       regarding copyright ownership.  The ASF licenses this file
+       to you 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 org.testing;
+
+import android.os.Bundle;
+import org.apache.cordova.*;
+
+public class TestBase extends CordovaActivity 
+{
+    @Override
+    public void onCreate(Bundle savedInstanceState)
+    {
+        super.onCreate(savedInstanceState);
+        super.init();
+        // Set by <content src="index.html" /> in config.xml
+        super.loadUrl(Config.getStartUrl());
+        //super.loadUrl("file:///android_asset/www/index.html")
+    }
+}
+

http://git-wip-us.apache.org/repos/asf/cordova-cli/blob/f4a9597a/spec/fixtures/plugins/fake1/plugin.xml
----------------------------------------------------------------------
diff --git a/spec/fixtures/plugins/fake1/plugin.xml b/spec/fixtures/plugins/fake1/plugin.xml
new file mode 100644
index 0000000..ffdc650
--- /dev/null
+++ b/spec/fixtures/plugins/fake1/plugin.xml
@@ -0,0 +1,10 @@
+<?xml version="1.0" encoding="UTF-8"?>
+
+<plugin xmlns="http://apache.org/cordova/ns/plugins/1.0"
+           id="org.apache.cordova.fakeplugin1"
+      version="0.1.0-dev">
+    <name>Fake1</name>
+    <description>Cordova fake plugin for tests</description>
+    <license>Apache 2.0</license>
+    <keywords>cordova,cli,test</keywords>
+</plugin>

http://git-wip-us.apache.org/repos/asf/cordova-cli/blob/f4a9597a/spec/helpers.js
----------------------------------------------------------------------
diff --git a/spec/helpers.js b/spec/helpers.js
new file mode 100644
index 0000000..3236516
--- /dev/null
+++ b/spec/helpers.js
@@ -0,0 +1,46 @@
+
+var path = require('path'),
+    fs = require('fs'),
+    shell = require('shelljs'),
+    os = require('os');
+
+module.exports.tmpDir = function(subdir) {
+    var dir = path.join(os.tmpdir(), 'e2e-test');
+    if (subdir) {
+        dir = path.join(dir, subdir);
+    }
+    shell.mkdir('-p', dir);
+    return dir;
+};
+
+// Returns the platform that should be used for testing on this host platform.
+/*
+var host = os.platform();
+if (host.match(/win/)) {
+    module.exports.testPlatform = 'wp8';
+} else if (host.match(/darwin/)) {
+    module.exports.testPlatform = 'ios';
+} else {
+    module.exports.testPlatform = 'android';
+}
+*/
+
+// Just use Android everywhere; we're mocking out any calls to the `android` binary.
+module.exports.testPlatform = 'android';
+
+// Add the toExist matcher.
+beforeEach(function() {
+    this.addMatchers({
+        'toExist': function() {
+            var notText = this.isNot ? ' not' : '';
+            var self = this;
+
+            this.message = function() {
+                return 'Expected file ' + self.actual + notText + ' to exist.';
+            };
+
+            return fs.existsSync(this.actual);
+        }
+    });
+});
+

http://git-wip-us.apache.org/repos/asf/cordova-cli/blob/f4a9597a/spec/hooker.spec.js
----------------------------------------------------------------------
diff --git a/spec/hooker.spec.js b/spec/hooker.spec.js
new file mode 100644
index 0000000..c85cc59
--- /dev/null
+++ b/spec/hooker.spec.js
@@ -0,0 +1,261 @@
+ /**
+    Licensed to the Apache Software Foundation (ASF) under one
+    or more contributor license agreements.  See the NOTICE file
+    distributed with this work for additional information
+    regarding copyright ownership.  The ASF licenses this file
+    to you 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.
+**/
+
+
+var cordova = require('../cordova'),
+    hooker = require('../src/hooker'),
+    shell  = require('shelljs'),
+    path   = require('path'),
+    fs     = require('fs'),
+    os     = require('os'),
+    Q      = require('q'),
+    child_process = require('child_process'),
+    helpers = require('./helpers');
+
+var platform = os.platform();
+var tmpDir = helpers.tmpDir('hooks_test');
+var project = path.join(tmpDir, 'project');
+var dotCordova = path.join(project, '.cordova');
+var hooksDir = path.join(project, '.cordova', 'hooks');
+var ext = platform.match(/(win32|win64)/)?'bat':'sh';
+
+
+// copy fixture
+shell.rm('-rf', project);
+shell.mkdir('-p', project);
+shell.cp('-R', path.join(__dirname, 'fixtures', 'base', '*'), project);
+shell.mkdir('-p', dotCordova);
+shell.cp('-R', path.join(__dirname, 'fixtures', 'hooks_' + ext), dotCordova);
+shell.mv(path.join(dotCordova, 'hooks_' + ext), hooksDir);
+shell.chmod('-R', 'ug+x', hooksDir);
+
+
+describe('hooker', function() {
+    it('should throw if provided directory is not a cordova project', function() {
+        expect(function() {
+            new hooker(tmpDir);
+        }).toThrow();
+    });
+});
+
+describe('global (static) fire method', function() {
+    it('should execute listeners serially', function(done) {
+        var test_event = 'foo';
+        var h1_fired = false;
+        var h1 = function() {
+            expect(h2_fired).toBe(false);
+            // Delay 100 ms here to check that h2 is not executed until after
+            // the promise returned by h1 is resolved.
+            var q = Q.delay(100).then(function() {
+                h1_fired = true;
+            });
+            return q;
+        };
+        var h2_fired = false;
+        var h2 = function() {
+            h2_fired = true;
+            expect(h1_fired).toBe(true);
+            return Q();
+        };
+
+        cordova.on(test_event, h1);
+        cordova.on(test_event, h2);
+        hooker.fire(test_event).then(function() {
+            expect(h1_fired).toBe(true);
+            expect(h2_fired).toBe(true);
+            done();
+        });
+    });
+});
+
+describe('module-level hooks', function() {
+    var handler = jasmine.createSpy().andReturn(Q());
+    var test_event = 'before_build';
+    var h;
+
+    beforeEach(function() {
+        h = new hooker(project);
+    });
+
+    afterEach(function() {
+        cordova.removeAllListeners(test_event);
+        handler.reset();
+    });
+
+    it('should fire handlers using cordova.on', function(done) {
+        cordova.on(test_event, handler);
+        h.fire(test_event)
+        .then(function() {
+            expect(handler).toHaveBeenCalled();
+        })
+        .fail(function(err) {
+            expect(err).not.toBeDefined();
+        })
+        .fin(done);
+    });
+
+    it('should pass the project root folder as parameter into the module-level handlers', function(done) {
+        cordova.on(test_event, handler);
+        h.fire(test_event)
+        .then(function() {
+            expect(handler).toHaveBeenCalledWith({root:project});
+        })
+        .fail(function(err) {
+            console.log(err);
+            expect(err).not.toBeDefined();
+        })
+        .fin(done);
+    });
+
+    it('should be able to stop listening to events using cordova.off', function(done) {
+        cordova.on(test_event, handler);
+        cordova.off(test_event, handler);
+        h.fire(test_event)
+        .then(function() {
+            expect(handler).not.toHaveBeenCalled();
+        })
+        .fail(function(err) {
+            console.log(err);
+            expect(err).toBeUndefined();
+        })
+        .fin(done);
+    });
+
+    it('should allow for hook to opt into asynchronous execution and block further hooks from firing using the done callback', function(done) {
+        var h1_fired = false;
+        var h1 = function() {
+            h1_fired = true;
+            expect(h2_fired).toBe(false);
+            return Q();
+        };
+        var h2_fired = false;
+        var h2 = function() {
+            h2_fired = true;
+            expect(h1_fired).toBe(true);
+            return Q();
+        };
+
+        cordova.on(test_event, h1);
+        cordova.on(test_event, h2);
+        h.fire(test_event).then(function() {
+            expect(h1_fired).toBe(true);
+            expect(h2_fired).toBe(true);
+            done();
+        });
+    });
+
+    it('should pass data object that fire calls into async handlers', function(done) {
+        var data = {
+            "hi":"ho",
+            "offtowork":"wego"
+        };
+        var async = function(opts) {
+            data.root = tmpDir;
+            expect(opts).toEqual(data);
+            return Q();
+        };
+        cordova.on(test_event, async);
+        h.fire(test_event, data).then(done);
+    });
+
+    it('should pass data object that fire calls into sync handlers', function(done) {
+        var data = {
+            "hi":"ho",
+            "offtowork":"wego"
+        };
+        var async = function(opts) {
+            data.root = tmpDir;
+            expect(opts).toEqual(data);
+        };
+        cordova.on(test_event, async);
+        h.fire(test_event, data).then(done);
+    });
+});
+
+
+describe('hooks', function() {
+    var h;
+    beforeEach(function() {
+        h = new hooker(project);
+    });
+
+
+    it('should not error if the hook is unrecognized', function(done) {
+        h.fire('CLEAN YOUR SHORTS GODDAMNIT LIKE A BIG BOY!')
+        .fail(function (err) {
+            expect('Call with unrecogized hook ').toBe('successful.\n' + err);
+        })
+        .fin(done);
+    });
+
+    it('should error if any script exits with non-zero code', function(done) {
+        h.fire('fail').then(function() {
+            expect('the call').toBe('a failure');
+        }, function(err) {
+            expect(err).toBeDefined();
+        })
+        .fin(done);
+    });
+
+    it('should execute all scripts in order', function(done) {
+        h.fire('test')
+        .then(function() {
+            var hooksOrderFile = path.join(project, 'hooks_order.txt');
+            var hooksEnvFile = path.join(project, 'hooks_env.json');
+            var hooksParamsFile = path.join(project, 'hooks_params.txt');
+            expect(hooksOrderFile).toExist();
+            expect(hooksEnvFile).toExist();
+            expect(hooksParamsFile).toExist();
+            expect(path.join(project, 'dotted_hook_should_not_fire.txt')).not.toExist();
+
+            var order = fs.readFileSync(hooksOrderFile, 'ascii').replace(/\W/gm, '');
+            expect(order).toBe('ab');
+
+            var params = fs.readFileSync(hooksParamsFile, 'ascii').trim().trim('"');
+            expect(params).toMatch(project.replace(/\\/g, '\\\\'));
+
+            var env = JSON.parse(fs.readFileSync(hooksEnvFile, 'ascii'));
+            expect(env.CORDOVA_VERSION).toEqual(require('../package').version);
+        })
+        .fail(function(err) {
+            console.log(err);
+            expect('Test hook call').toBe('successful');
+        })
+        .fin(done);
+
+    });
+
+    // Cleanup. Must be the last spec. Is there a better place for final cleanup in Jasmine?
+    it('should not fail during cleanup', function() {
+        process.chdir(path.join(__dirname, '..'));  // Non e2e tests assume CWD is repo root.
+        if(ext == 'sh') {
+            //shell.rm('-rf', tmpDir);
+        } else { // Windows:
+            // For some mysterious reason, both shell.rm and RMDIR /S /Q won't
+            // delete the dir on Windows, but they do remove the files leaving
+            // only folders. But the dir is removed just fine by
+            // shell.rm('-rf', tmpDir) at the top of this file with the next
+            // invocation of this test. The benefit of RMDIR /S /Q is that it
+            // doesn't print warnings like shell.rmdir() that look like this:
+            // rm: could not remove directory (code ENOTEMPTY): C:\Users\...
+            var cmd =  'RMDIR /S /Q ' + tmpDir;
+            child_process.exec(cmd);
+        }
+    });
+});