You are viewing a plain text version of this content. The canonical link for it is here.
Posted to commits@cordova.apache.org by an...@apache.org on 2016/03/01 09:45:44 UTC

android commit: CB-10729 Move plugin handlers tests for into platform's repo

Repository: cordova-android
Updated Branches:
  refs/heads/master aa2b3d3da -> 8ab75e710


CB-10729 Move plugin handlers tests for into platform's repo


Project: http://git-wip-us.apache.org/repos/asf/cordova-android/repo
Commit: http://git-wip-us.apache.org/repos/asf/cordova-android/commit/8ab75e71
Tree: http://git-wip-us.apache.org/repos/asf/cordova-android/tree/8ab75e71
Diff: http://git-wip-us.apache.org/repos/asf/cordova-android/diff/8ab75e71

Branch: refs/heads/master
Commit: 8ab75e710980703d6b4427ea8ca3a4ea1b87e307
Parents: aa2b3d3
Author: Vladimir Kotikov <v-...@microsoft.com>
Authored: Mon Feb 29 18:49:17 2016 +0300
Committer: Vladimir Kotikov <v-...@microsoft.com>
Committed: Tue Mar 1 11:31:14 2016 +0300

----------------------------------------------------------------------
 .gitignore                                      |   4 +
 .jshintignore                                   |   1 +
 bin/templates/cordova/lib/pluginHandlers.js     |  30 ++-
 node_modules/.bin/istanbul                      |  15 ++
 node_modules/.bin/istanbul.cmd                  |   7 +
 package.json                                    |   2 +
 .../android_project/AndroidManifest.xml         |  69 +++++
 .../android_project/assets/www/.gitkeep         |   0
 .../fixtures/android_project/res/xml/config.xml |  54 ++++
 spec/fixtures/android_project/src/.gitkeep      |   0
 .../android-resource.xml                        |   1 +
 .../org.test.plugins.dummyplugin/extra.gradle   |   1 +
 .../plugin-lib/AndroidManifest.xml              |   5 +
 .../plugin-lib/libFile                          |   1 +
 .../plugin-lib/project.properties               |   1 +
 .../plugin-lib2/AndroidManifest.xml             |   5 +
 .../plugin-lib2/libFile                         |   1 +
 .../plugin-lib2/project.properties              |   1 +
 .../org.test.plugins.dummyplugin/plugin.xml     |  75 ++++++
 .../src/android/DummyPlugin.java                |   1 +
 .../src/android/TestLib.jar                     |   1 +
 .../www/dummyplugin.js                          |   1 +
 .../www/dummyplugin/image.jpg                   |   1 +
 .../org.test.plugins.faultyplugin/plugin.xml    |  63 +++++
 .../src/android/FaultyPlugin.java               |   1 +
 spec/unit/AndroidProject.spec.js                |  31 +++
 spec/unit/Api.spec.js                           |  77 ++++++
 spec/unit/plugin.spec.js                        |  77 ------
 spec/unit/pluginHandlers/common.spec.js         | 170 ++++++++++++
 spec/unit/pluginHandlers/handlers.spec.js       | 258 +++++++++++++++++++
 30 files changed, 863 insertions(+), 91 deletions(-)
----------------------------------------------------------------------


http://git-wip-us.apache.org/repos/asf/cordova-android/blob/8ab75e71/.gitignore
----------------------------------------------------------------------
diff --git a/.gitignore b/.gitignore
index 23195f3..65636c0 100644
--- a/.gitignore
+++ b/.gitignore
@@ -35,6 +35,7 @@ Desktop.ini
 *.swp
 *.class
 *.jar
+!/spec/fixtures/org.test.plugins.dummyplugin/src/android/TestLib.jar
 # IntelliJ IDEA files
 *.iml
 .idea
@@ -43,3 +44,6 @@ npm-debug.log
 node_modules/jshint
 node_modules/promise-matchers
 node_modules/jasmine-node
+node_modules/rewire
+node_modules/istanbul
+/coverage

http://git-wip-us.apache.org/repos/asf/cordova-android/blob/8ab75e71/.jshintignore
----------------------------------------------------------------------
diff --git a/.jshintignore b/.jshintignore
index e87aa4b..dc77ccc 100644
--- a/.jshintignore
+++ b/.jshintignore
@@ -1,2 +1,3 @@
 bin/node_modules/*
 bin/templates/project/*
+spec/fixtures/*

http://git-wip-us.apache.org/repos/asf/cordova-android/blob/8ab75e71/bin/templates/cordova/lib/pluginHandlers.js
----------------------------------------------------------------------
diff --git a/bin/templates/cordova/lib/pluginHandlers.js b/bin/templates/cordova/lib/pluginHandlers.js
index efbeda8..1ee6b1f 100644
--- a/bin/templates/cordova/lib/pluginHandlers.js
+++ b/bin/templates/cordova/lib/pluginHandlers.js
@@ -31,7 +31,7 @@ var handlers = {
             if (!obj.src) throw new CordovaError('<source-file> element is missing "src" attribute for plugin: ' + plugin.id);
             if (!obj.targetDir) throw new CordovaError('<source-file> element is missing "target-dir" attribute for plugin: ' + plugin.id);
             var dest = path.join(obj.targetDir, path.basename(obj.src));
-            copyNewFile(plugin.dir, obj.src, project.projectDir, dest, options && options.link);
+            copyNewFile(plugin.dir, obj.src, project.projectDir, dest, !!(options && options.link));
         },
         uninstall:function(obj, plugin, project, options) {
             var dest = path.join(obj.targetDir, path.basename(obj.src));
@@ -41,7 +41,7 @@ var handlers = {
     'lib-file':{
         install:function(obj, plugin, project, options) {
             var dest = path.join('libs', path.basename(obj.src));
-            copyFile(plugin.dir, obj.src, project.projectDir, dest, options && options.link);
+            copyFile(plugin.dir, obj.src, project.projectDir, dest, !!(options && options.link));
         },
         uninstall:function(obj, plugin, project, options) {
             var dest = path.join('libs', path.basename(obj.src));
@@ -50,7 +50,7 @@ var handlers = {
     },
     'resource-file':{
         install:function(obj, plugin, project, options) {
-            copyFile(plugin.dir, obj.src, project.projectDir, path.normalize(obj.target), options && options.link);
+            copyFile(plugin.dir, obj.src, project.projectDir, path.normalize(obj.target), !!(options && options.link));
         },
         uninstall:function(obj, plugin, project, options) {
             removeFile(project.projectDir, path.normalize(obj.target));
@@ -65,22 +65,24 @@ var handlers = {
             var parentDir = obj.parent ? path.resolve(project.projectDir, obj.parent) : project.projectDir;
             var subDir;
 
-            if (obj.custom) {
-                var subRelativeDir = project.getCustomSubprojectRelativeDir(plugin.id, src);
-                copyNewFile(plugin.dir, src, project.projectDir, subRelativeDir, options && options.link);
-                subDir = path.resolve(project.projectDir, subRelativeDir);
-            } else {
-                obj.type = 'sys';
-                subDir = src;
+             if (obj.custom) {
+                 var subRelativeDir = project.getCustomSubprojectRelativeDir(plugin.id, src);
+                 copyNewFile(plugin.dir, src, project.projectDir, subRelativeDir, !!(options && options.link));
+                 subDir = path.resolve(project.projectDir, subRelativeDir);
+             } else {
+                 obj.type = 'sys';
+                 subDir = src;
             }
 
             if (obj.type == 'gradleReference') {
                 project.addGradleReference(parentDir, subDir);
-            } else if (obj.type == 'sys') {
-                project.addSystemLibrary(parentDir, subDir);
-            } else {
-                project.addSubProject(parentDir, subDir);
+             } else if (obj.type == 'sys') {
+                 project.addSystemLibrary(parentDir, subDir);
+             } else {
+                 project.addSubProject(parentDir, subDir);
             }
+
+            project.addSubProject(parentDir, subDir);
         },
         uninstall:function(obj, plugin, project, options) {
             var src = obj.src;

http://git-wip-us.apache.org/repos/asf/cordova-android/blob/8ab75e71/node_modules/.bin/istanbul
----------------------------------------------------------------------
diff --git a/node_modules/.bin/istanbul b/node_modules/.bin/istanbul
new file mode 100644
index 0000000..6d0d614
--- /dev/null
+++ b/node_modules/.bin/istanbul
@@ -0,0 +1,15 @@
+#!/bin/sh
+basedir=`dirname "$0"`
+
+case `uname` in
+    *CYGWIN*) basedir=`cygpath -w "$basedir"`;;
+esac
+
+if [ -x "$basedir/node" ]; then
+  "$basedir/node"  "$basedir/../istanbul/lib/cli.js" "$@"
+  ret=$?
+else 
+  node  "$basedir/../istanbul/lib/cli.js" "$@"
+  ret=$?
+fi
+exit $ret

http://git-wip-us.apache.org/repos/asf/cordova-android/blob/8ab75e71/node_modules/.bin/istanbul.cmd
----------------------------------------------------------------------
diff --git a/node_modules/.bin/istanbul.cmd b/node_modules/.bin/istanbul.cmd
new file mode 100644
index 0000000..6635d19
--- /dev/null
+++ b/node_modules/.bin/istanbul.cmd
@@ -0,0 +1,7 @@
+@IF EXIST "%~dp0\node.exe" (
+  "%~dp0\node.exe"  "%~dp0\..\istanbul\lib\cli.js" %*
+) ELSE (
+  @SETLOCAL
+  @SET PATHEXT=%PATHEXT:;.JS;=;%
+  node  "%~dp0\..\istanbul\lib\cli.js" %*
+)
\ No newline at end of file

http://git-wip-us.apache.org/repos/asf/cordova-android/blob/8ab75e71/package.json
----------------------------------------------------------------------
diff --git a/package.json b/package.json
index c9cc19c..f9f57ff 100644
--- a/package.json
+++ b/package.json
@@ -17,6 +17,7 @@
     ],
     "scripts": {
         "test": "npm run jshint && jasmine-node --color spec/unit",
+        "cover": "istanbul cover --root bin/templates/cordova --print detail node_modules/jasmine-node/bin/jasmine-node -- spec/unit",
         "test-build": "jasmine-node --captureExceptions --color spec/e2e",
         "jshint": "node node_modules/jshint/bin/jshint bin && node node_modules/jshint/bin/jshint spec"
     },
@@ -39,6 +40,7 @@
         "shelljs"
     ],
     "devDependencies": {
+        "istanbul": "^0.4.2",
         "jasmine-node": "^1.14.5",
         "jshint": "^2.6.0",
         "promise-matchers": "~0",

http://git-wip-us.apache.org/repos/asf/cordova-android/blob/8ab75e71/spec/fixtures/android_project/AndroidManifest.xml
----------------------------------------------------------------------
diff --git a/spec/fixtures/android_project/AndroidManifest.xml b/spec/fixtures/android_project/AndroidManifest.xml
new file mode 100644
index 0000000..17489ca
--- /dev/null
+++ b/spec/fixtures/android_project/AndroidManifest.xml
@@ -0,0 +1,69 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!--
+
+ Copyright 2013 Anis Kadri
+
+ 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.
+
+-->
+
+<manifest xmlns:android="http://schemas.android.com/apk/res/android" android:windowSoftInputMode="adjustPan"
+      package="com.alunny.childapp" android:versionName="1.1" android:versionCode="5">
+    <supports-screens
+    	android:largeScreens="true"
+    	android:normalScreens="true"
+    	android:smallScreens="true"
+    	android:xlargeScreens="true"
+    	android:resizeable="true"
+    	android:anyDensity="true"
+    	/>
+
+    <uses-permission android:name="android.permission.CAMERA" />
+    <uses-permission android:name="android.permission.VIBRATE" />
+    <uses-permission android:name="android.permission.ACCESS_COARSE_LOCATION" />
+    <uses-permission android:name="android.permission.ACCESS_FINE_LOCATION" />
+    <uses-permission android:name="android.permission.ACCESS_LOCATION_EXTRA_COMMANDS" />
+    <uses-permission android:name="android.permission.INTERNET" />
+    <uses-permission android:name="android.permission.RECEIVE_SMS" />
+    <uses-permission android:name="android.permission.RECORD_AUDIO" />
+    <uses-permission android:name="android.permission.RECORD_VIDEO"/>
+    <uses-permission android:name="android.permission.MODIFY_AUDIO_SETTINGS" />
+    <uses-permission android:name="android.permission.READ_CONTACTS" />
+    <uses-permission android:name="android.permission.WRITE_CONTACTS" />   
+    <uses-permission android:name="android.permission.WRITE_EXTERNAL_STORAGE" />   
+    <uses-permission android:name="android.permission.ACCESS_NETWORK_STATE" />
+    <uses-permission android:name="android.permission.GET_ACCOUNTS" />
+    <uses-permission android:name="android.permission.BROADCAST_STICKY" />
+
+    <uses-feature android:name="android.hardware.camera" />
+    <uses-feature android:name="android.hardware.camera.autofocus" />
+
+    <application android:icon="@drawable/icon" android:label="@string/app_name"
+    	android:debuggable="true">
+		<activity android:name="ChildApp" android:label="@string/app_name" 
+				  android:configChanges="orientation|keyboardHidden">
+			<intent-filter>
+				<action android:name="android.intent.action.MAIN" />
+				<category android:name="android.intent.category.LAUNCHER" />
+			</intent-filter>
+        </activity>
+        <activity android:name="org.test.DroidGap" android:label="@string/app_name" 
+            	  android:configChanges="orientation|keyboardHidden">
+        	<intent-filter>
+        	</intent-filter>
+        </activity>
+    </application>
+
+	<uses-sdk android:minSdkVersion="5" />
+</manifest> 

http://git-wip-us.apache.org/repos/asf/cordova-android/blob/8ab75e71/spec/fixtures/android_project/assets/www/.gitkeep
----------------------------------------------------------------------
diff --git a/spec/fixtures/android_project/assets/www/.gitkeep b/spec/fixtures/android_project/assets/www/.gitkeep
new file mode 100644
index 0000000..e69de29

http://git-wip-us.apache.org/repos/asf/cordova-android/blob/8ab75e71/spec/fixtures/android_project/res/xml/config.xml
----------------------------------------------------------------------
diff --git a/spec/fixtures/android_project/res/xml/config.xml b/spec/fixtures/android_project/res/xml/config.xml
new file mode 100644
index 0000000..4f087a9
--- /dev/null
+++ b/spec/fixtures/android_project/res/xml/config.xml
@@ -0,0 +1,54 @@
+<?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.
+-->
+<cordova>
+    <!--
+    access elements control the Android whitelist.
+    Domains are assumed blocked unless set otherwise
+     -->
+
+    <access origin="http://127.0.0.1*"/> <!-- allow local pages -->
+
+    <!-- <access origin="https://example.com" /> allow any secure requests to example.com -->
+    <!-- <access origin="https://example.com" subdomains="true" /> such as above, but including subdomains, such as www -->
+    <!-- <access origin=".*"/> Allow all domains, suggested development use only -->
+
+    <log level="DEBUG"/>
+    <preference name="useBrowserHistory" value="false" />
+<plugins>
+    <plugin name="App" value="org.apache.cordova.App"/>
+    <plugin name="Geolocation" value="org.apache.cordova.GeoBroker"/>
+    <plugin name="Device" value="org.apache.cordova.Device"/>
+    <plugin name="Accelerometer" value="org.apache.cordova.AccelListener"/>
+    <plugin name="Compass" value="org.apache.cordova.CompassListener"/>
+    <plugin name="Media" value="org.apache.cordova.AudioHandler"/>
+    <plugin name="Camera" value="org.apache.cordova.CameraLauncher"/>
+    <plugin name="org.apache.cordova.core.contacts" value="org.apache.cordova.ContactManager"/>
+    <plugin name="File" value="org.apache.cordova.FileUtils"/>
+    <plugin name="NetworkStatus" value="org.apache.cordova.NetworkManager"/>
+    <plugin name="Notification" value="org.apache.cordova.Notification"/>
+    <plugin name="Storage" value="org.apache.cordova.Storage"/>
+    <plugin name="Temperature" value="org.apache.cordova.TempListener"/>
+    <plugin name="FileTransfer" value="org.apache.cordova.FileTransfer"/>
+    <plugin name="Capture" value="org.apache.cordova.Capture"/>
+    <plugin name="Battery" value="org.apache.cordova.BatteryListener"/>
+    <plugin name="SplashScreen" value="org.apache.cordova.SplashScreen"/>
+</plugins>
+</cordova>
+

http://git-wip-us.apache.org/repos/asf/cordova-android/blob/8ab75e71/spec/fixtures/android_project/src/.gitkeep
----------------------------------------------------------------------
diff --git a/spec/fixtures/android_project/src/.gitkeep b/spec/fixtures/android_project/src/.gitkeep
new file mode 100644
index 0000000..e69de29

http://git-wip-us.apache.org/repos/asf/cordova-android/blob/8ab75e71/spec/fixtures/org.test.plugins.dummyplugin/android-resource.xml
----------------------------------------------------------------------
diff --git a/spec/fixtures/org.test.plugins.dummyplugin/android-resource.xml b/spec/fixtures/org.test.plugins.dummyplugin/android-resource.xml
new file mode 100644
index 0000000..9046e6e
--- /dev/null
+++ b/spec/fixtures/org.test.plugins.dummyplugin/android-resource.xml
@@ -0,0 +1 @@
+./org.test.plugins.dummyplugin/android-resource.xml

http://git-wip-us.apache.org/repos/asf/cordova-android/blob/8ab75e71/spec/fixtures/org.test.plugins.dummyplugin/extra.gradle
----------------------------------------------------------------------
diff --git a/spec/fixtures/org.test.plugins.dummyplugin/extra.gradle b/spec/fixtures/org.test.plugins.dummyplugin/extra.gradle
new file mode 100644
index 0000000..5b828a9
--- /dev/null
+++ b/spec/fixtures/org.test.plugins.dummyplugin/extra.gradle
@@ -0,0 +1 @@
+extra.gradle

http://git-wip-us.apache.org/repos/asf/cordova-android/blob/8ab75e71/spec/fixtures/org.test.plugins.dummyplugin/plugin-lib/AndroidManifest.xml
----------------------------------------------------------------------
diff --git a/spec/fixtures/org.test.plugins.dummyplugin/plugin-lib/AndroidManifest.xml b/spec/fixtures/org.test.plugins.dummyplugin/plugin-lib/AndroidManifest.xml
new file mode 100644
index 0000000..3c9499c
--- /dev/null
+++ b/spec/fixtures/org.test.plugins.dummyplugin/plugin-lib/AndroidManifest.xml
@@ -0,0 +1,5 @@
+<manifest xmlns:android="http://schemas.android.com/apk/res/android"
+  android:versionCode="1" package="com.test.somelib">
+    <uses-sdk android:minSdkVersion="9"/>
+    <application/>
+</manifest>

http://git-wip-us.apache.org/repos/asf/cordova-android/blob/8ab75e71/spec/fixtures/org.test.plugins.dummyplugin/plugin-lib/libFile
----------------------------------------------------------------------
diff --git a/spec/fixtures/org.test.plugins.dummyplugin/plugin-lib/libFile b/spec/fixtures/org.test.plugins.dummyplugin/plugin-lib/libFile
new file mode 100644
index 0000000..c79df8e
--- /dev/null
+++ b/spec/fixtures/org.test.plugins.dummyplugin/plugin-lib/libFile
@@ -0,0 +1 @@
+libFile contents

http://git-wip-us.apache.org/repos/asf/cordova-android/blob/8ab75e71/spec/fixtures/org.test.plugins.dummyplugin/plugin-lib/project.properties
----------------------------------------------------------------------
diff --git a/spec/fixtures/org.test.plugins.dummyplugin/plugin-lib/project.properties b/spec/fixtures/org.test.plugins.dummyplugin/plugin-lib/project.properties
new file mode 100644
index 0000000..c4a5b63
--- /dev/null
+++ b/spec/fixtures/org.test.plugins.dummyplugin/plugin-lib/project.properties
@@ -0,0 +1 @@
+target=android-11

http://git-wip-us.apache.org/repos/asf/cordova-android/blob/8ab75e71/spec/fixtures/org.test.plugins.dummyplugin/plugin-lib2/AndroidManifest.xml
----------------------------------------------------------------------
diff --git a/spec/fixtures/org.test.plugins.dummyplugin/plugin-lib2/AndroidManifest.xml b/spec/fixtures/org.test.plugins.dummyplugin/plugin-lib2/AndroidManifest.xml
new file mode 100644
index 0000000..3c9499c
--- /dev/null
+++ b/spec/fixtures/org.test.plugins.dummyplugin/plugin-lib2/AndroidManifest.xml
@@ -0,0 +1,5 @@
+<manifest xmlns:android="http://schemas.android.com/apk/res/android"
+  android:versionCode="1" package="com.test.somelib">
+    <uses-sdk android:minSdkVersion="9"/>
+    <application/>
+</manifest>

http://git-wip-us.apache.org/repos/asf/cordova-android/blob/8ab75e71/spec/fixtures/org.test.plugins.dummyplugin/plugin-lib2/libFile
----------------------------------------------------------------------
diff --git a/spec/fixtures/org.test.plugins.dummyplugin/plugin-lib2/libFile b/spec/fixtures/org.test.plugins.dummyplugin/plugin-lib2/libFile
new file mode 100644
index 0000000..c79df8e
--- /dev/null
+++ b/spec/fixtures/org.test.plugins.dummyplugin/plugin-lib2/libFile
@@ -0,0 +1 @@
+libFile contents

http://git-wip-us.apache.org/repos/asf/cordova-android/blob/8ab75e71/spec/fixtures/org.test.plugins.dummyplugin/plugin-lib2/project.properties
----------------------------------------------------------------------
diff --git a/spec/fixtures/org.test.plugins.dummyplugin/plugin-lib2/project.properties b/spec/fixtures/org.test.plugins.dummyplugin/plugin-lib2/project.properties
new file mode 100644
index 0000000..c4a5b63
--- /dev/null
+++ b/spec/fixtures/org.test.plugins.dummyplugin/plugin-lib2/project.properties
@@ -0,0 +1 @@
+target=android-11

http://git-wip-us.apache.org/repos/asf/cordova-android/blob/8ab75e71/spec/fixtures/org.test.plugins.dummyplugin/plugin.xml
----------------------------------------------------------------------
diff --git a/spec/fixtures/org.test.plugins.dummyplugin/plugin.xml b/spec/fixtures/org.test.plugins.dummyplugin/plugin.xml
new file mode 100644
index 0000000..a40df2b
--- /dev/null
+++ b/spec/fixtures/org.test.plugins.dummyplugin/plugin.xml
@@ -0,0 +1,75 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<!--
+
+ Copyright 2013 Anis Kadri
+
+ 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.
+
+-->
+
+<plugin xmlns="http://cordova.apache.org/ns/plugins/1.0"
+    xmlns:android="http://schemas.android.com/apk/res/android"
+    id="org.test.plugins.dummyplugin"
+    version="0.6.0">
+
+    <!-- new requirement: NO SPACES -->
+    <name>dummyplugin</name>
+    <!-- These are going to be required by plugman-registry -->
+    <description>my description</description>
+    <author>Jackson Badman</author>
+    <keywords>dummy,plugin</keywords>
+    <license>BSD</license>
+    <!-- end plugman-registry requirements -->
+
+    <asset src="www/dummyplugin.js" target="dummyplugin.js" />
+    <asset src="www/dummyplugin" target="dummyplugin" />
+
+    <config-file target="config.xml" parent="/*">
+        <access origin="build.phonegap.com" />
+        <access origin="s3.amazonaws.com" />
+    </config-file>
+
+    <!-- android -->
+    <platform name="android">
+        <config-file target="AndroidManifest.xml" parent="/manifest/application">
+            <activity android:name="DummyPlugin.org.test.plugins.dummyplugin"
+                      android:label="@string/app_name">
+                <intent-filter>
+                </intent-filter>
+            </activity>
+        </config-file>
+
+        <framework src="plugin-lib" custom="true" />
+        <framework src="plugin-lib2" custom="true" parent="plugin-lib" />
+        <framework src="extras/android/support/v7/appcompat" />
+        <framework src="extra.gradle" type="gradleReference" />
+        <resource-file src="android-resource.xml" target="res/xml/dummy.xml" />
+
+        <!-- CDV < 2.0 -->
+        <config-file target="res/xml/plugins.xml" parent="/plugins">
+            <plugin name="org.test.plugins.dummyplugin"
+                value="DummyPlugin.org.test.plugins.dummyplugin"/>
+        </config-file>
+
+        <!-- CDV 2.0+ (for now) -->
+        <config-file target="res/xml/config.xml" parent="/cordova/plugins">
+            <plugin name="org.test.plugins.dummyplugin"
+                value="DummyPlugin.org.test.plugins.dummyplugin"/>
+        </config-file>
+
+        <source-file src="src/android/DummyPlugin.java"
+                target-dir="src/com/phonegap/plugins/dummyplugin" />
+        <lib-file src="src/android/TestLib.jar" />
+    </platform>
+</plugin>

http://git-wip-us.apache.org/repos/asf/cordova-android/blob/8ab75e71/spec/fixtures/org.test.plugins.dummyplugin/src/android/DummyPlugin.java
----------------------------------------------------------------------
diff --git a/spec/fixtures/org.test.plugins.dummyplugin/src/android/DummyPlugin.java b/spec/fixtures/org.test.plugins.dummyplugin/src/android/DummyPlugin.java
new file mode 100644
index 0000000..90e4f5a
--- /dev/null
+++ b/spec/fixtures/org.test.plugins.dummyplugin/src/android/DummyPlugin.java
@@ -0,0 +1 @@
+./org.test.plugins.dummyplugin/src/android/DummyPlugin.java

http://git-wip-us.apache.org/repos/asf/cordova-android/blob/8ab75e71/spec/fixtures/org.test.plugins.dummyplugin/src/android/TestLib.jar
----------------------------------------------------------------------
diff --git a/spec/fixtures/org.test.plugins.dummyplugin/src/android/TestLib.jar b/spec/fixtures/org.test.plugins.dummyplugin/src/android/TestLib.jar
new file mode 100644
index 0000000..089ae3b
--- /dev/null
+++ b/spec/fixtures/org.test.plugins.dummyplugin/src/android/TestLib.jar
@@ -0,0 +1 @@
+./org.test.plugins.dummyplugin/src/android/TestLib.jar

http://git-wip-us.apache.org/repos/asf/cordova-android/blob/8ab75e71/spec/fixtures/org.test.plugins.dummyplugin/www/dummyplugin.js
----------------------------------------------------------------------
diff --git a/spec/fixtures/org.test.plugins.dummyplugin/www/dummyplugin.js b/spec/fixtures/org.test.plugins.dummyplugin/www/dummyplugin.js
new file mode 100644
index 0000000..631d6da
--- /dev/null
+++ b/spec/fixtures/org.test.plugins.dummyplugin/www/dummyplugin.js
@@ -0,0 +1 @@
+./org.test.plugins.dummyplugin/www/dummyplugin.js

http://git-wip-us.apache.org/repos/asf/cordova-android/blob/8ab75e71/spec/fixtures/org.test.plugins.dummyplugin/www/dummyplugin/image.jpg
----------------------------------------------------------------------
diff --git a/spec/fixtures/org.test.plugins.dummyplugin/www/dummyplugin/image.jpg b/spec/fixtures/org.test.plugins.dummyplugin/www/dummyplugin/image.jpg
new file mode 100644
index 0000000..219c78a
--- /dev/null
+++ b/spec/fixtures/org.test.plugins.dummyplugin/www/dummyplugin/image.jpg
@@ -0,0 +1 @@
+./org.test.plugins.dummyplugin/www/dummyplugin/image.jpg

http://git-wip-us.apache.org/repos/asf/cordova-android/blob/8ab75e71/spec/fixtures/org.test.plugins.faultyplugin/plugin.xml
----------------------------------------------------------------------
diff --git a/spec/fixtures/org.test.plugins.faultyplugin/plugin.xml b/spec/fixtures/org.test.plugins.faultyplugin/plugin.xml
new file mode 100644
index 0000000..7d2a2d2
--- /dev/null
+++ b/spec/fixtures/org.test.plugins.faultyplugin/plugin.xml
@@ -0,0 +1,63 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<!--
+
+ Copyright 2013 Anis Kadri
+
+ 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.
+
+-->
+
+<plugin xmlns="http://cordova.apache.org/ns/plugins/1.0"
+    xmlns:android="http://schemas.android.com/apk/res/android"
+    id="org.test.plugins.faultyplugin"
+    version="0.6.0">
+
+    <name>Faulty Plugin</name>
+
+    <access origin="build.phonegap.com" />
+    <access origin="s3.amazonaws.com" />
+    <!-- file doesn't exist -->
+
+    <config-file target="config.xml" parent="/widget">
+        <asset src="www/main.js" target="faultyplugin/main.js" />
+        <asset src="www/index.js" target="faultyplugin/index.js" />
+    </config-file>
+
+    <!-- android -->
+    <platform name="android">
+        <config-file target="AndroidManifest.xml" parent="/manifest/application">
+            <activity android:name="org.test.plugins.faultyplugin.org.test.plugins.faultyplugin"
+                      android:label="@string/app_name">
+                <intent-filter>
+                </intent-filter>
+            </activity>
+        </config-file>
+
+        <!-- CDV < 2.0 -->
+        <config-file target="res/xml/plugins.xml" parent="/plugins">
+            <plugin name="org.test.plugins.faultyplugin"
+                value="org.test.plugins.faultyplugin.org.test.plugins.faultyplugin"/>
+        </config-file>
+
+        <!-- CDV 2.0+ (for now) -->
+        <config-file target="res/xml/config.xml" parent="/cordova/plugins">
+            <plugin name="org.test.plugins.faultyplugin"
+                value="org.test.plugins.faultyplugin.org.test.plugins.faultyplugin"/>
+        </config-file>
+
+        <!-- this file doesn't exist -->
+        <source-file src="src/android/NotHere.java"
+                target-dir="src/com/phonegap/plugins/faultyplugin" />
+    </platform>
+</plugin>

http://git-wip-us.apache.org/repos/asf/cordova-android/blob/8ab75e71/spec/fixtures/org.test.plugins.faultyplugin/src/android/FaultyPlugin.java
----------------------------------------------------------------------
diff --git a/spec/fixtures/org.test.plugins.faultyplugin/src/android/FaultyPlugin.java b/spec/fixtures/org.test.plugins.faultyplugin/src/android/FaultyPlugin.java
new file mode 100644
index 0000000..dba5492
--- /dev/null
+++ b/spec/fixtures/org.test.plugins.faultyplugin/src/android/FaultyPlugin.java
@@ -0,0 +1 @@
+./org.test.plugins.faultyplugin/src/android/org.test.plugins.faultyplugin.java

http://git-wip-us.apache.org/repos/asf/cordova-android/blob/8ab75e71/spec/unit/AndroidProject.spec.js
----------------------------------------------------------------------
diff --git a/spec/unit/AndroidProject.spec.js b/spec/unit/AndroidProject.spec.js
new file mode 100644
index 0000000..8aec35a
--- /dev/null
+++ b/spec/unit/AndroidProject.spec.js
@@ -0,0 +1,31 @@
+/**
+    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 AndroidProject = require('../../bin/templates/cordova/lib/AndroidProject');
+var android_project = path.join(__dirname, '../fixtures/android_project');
+
+describe('AndroidProject class', function() {
+    describe('getPackageName method', function() {
+        it('should return an android project\'s proper package name', function() {
+            expect(AndroidProject.getProjectFile(android_project).getPackageName())
+                .toEqual('com.alunny.childapp');
+        });
+    });
+});

http://git-wip-us.apache.org/repos/asf/cordova-android/blob/8ab75e71/spec/unit/Api.spec.js
----------------------------------------------------------------------
diff --git a/spec/unit/Api.spec.js b/spec/unit/Api.spec.js
new file mode 100644
index 0000000..59fb709
--- /dev/null
+++ b/spec/unit/Api.spec.js
@@ -0,0 +1,77 @@
+/**
+    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.
+*/
+
+/* jshint node:true */
+
+var Q = require('q');
+var os = require('os');
+var path = require('path');
+var common = require('cordova-common');
+
+var AndroidProject = require('../../bin/templates/cordova/lib/AndroidProject');
+var builders = require('../../bin/templates/cordova/lib/builders/builders');
+
+var PluginInfo = common.PluginInfo;
+
+var FIXTURES = path.join(__dirname, '../e2e/fixtures');
+var FAKE_PROJECT_DIR = path.join(os.tmpdir(), 'plugin-test-project');
+
+describe('addPlugin method', function () {
+    var api, fail, gradleBuilder;
+
+    beforeEach(function() {
+        var ActionStack = jasmine.createSpyObj('ActionStack', ['createAction', 'push', 'process']);
+        ActionStack.process.andReturn(Q());
+        spyOn(common, 'ActionStack').andReturn(ActionStack);
+
+        spyOn(AndroidProject, 'getProjectFile')
+            .andReturn(jasmine.createSpyObj('AndroidProject', ['getPackageName', 'write']));
+
+        var Api = require('../../bin/templates/cordova/Api');
+        api = new Api('android', FAKE_PROJECT_DIR);
+
+        spyOn(api, '_addModulesInfo');
+        spyOn(api._munger, 'add_plugin_changes')
+            .andReturn(jasmine.createSpyObj('munger', ['save_all']));
+
+        fail = jasmine.createSpy('fail');
+        gradleBuilder = jasmine.createSpyObj('gradleBuilder', ['prepBuildFiles']);
+        spyOn(builders, 'getBuilder').andReturn(gradleBuilder);
+    });
+
+    it('should call gradleBuilder.prepBuildFiles for every plugin with frameworks', function(done) {
+        api.addPlugin(new PluginInfo(path.join(FIXTURES, 'cordova-plugin-fake')))
+        .catch(fail)
+        .fin(function () {
+            expect(fail).not.toHaveBeenCalled();
+            expect(gradleBuilder.prepBuildFiles).toHaveBeenCalled();
+            done();
+        });
+    });
+
+    it('shouldn\'t trigger gradleBuilder.prepBuildFiles for plugins without android frameworks', function(done) {
+        api.addPlugin(new PluginInfo(path.join(FIXTURES, 'cordova-plugin-fake-ios-frameworks')))
+        .catch(fail)
+        .fin(function () {
+            expect(fail).not.toHaveBeenCalled();
+            expect(gradleBuilder.prepBuildFiles).not.toHaveBeenCalled();
+            done();
+        });
+    });
+});

http://git-wip-us.apache.org/repos/asf/cordova-android/blob/8ab75e71/spec/unit/plugin.spec.js
----------------------------------------------------------------------
diff --git a/spec/unit/plugin.spec.js b/spec/unit/plugin.spec.js
deleted file mode 100644
index 59fb709..0000000
--- a/spec/unit/plugin.spec.js
+++ /dev/null
@@ -1,77 +0,0 @@
-/**
-    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.
-*/
-
-/* jshint node:true */
-
-var Q = require('q');
-var os = require('os');
-var path = require('path');
-var common = require('cordova-common');
-
-var AndroidProject = require('../../bin/templates/cordova/lib/AndroidProject');
-var builders = require('../../bin/templates/cordova/lib/builders/builders');
-
-var PluginInfo = common.PluginInfo;
-
-var FIXTURES = path.join(__dirname, '../e2e/fixtures');
-var FAKE_PROJECT_DIR = path.join(os.tmpdir(), 'plugin-test-project');
-
-describe('addPlugin method', function () {
-    var api, fail, gradleBuilder;
-
-    beforeEach(function() {
-        var ActionStack = jasmine.createSpyObj('ActionStack', ['createAction', 'push', 'process']);
-        ActionStack.process.andReturn(Q());
-        spyOn(common, 'ActionStack').andReturn(ActionStack);
-
-        spyOn(AndroidProject, 'getProjectFile')
-            .andReturn(jasmine.createSpyObj('AndroidProject', ['getPackageName', 'write']));
-
-        var Api = require('../../bin/templates/cordova/Api');
-        api = new Api('android', FAKE_PROJECT_DIR);
-
-        spyOn(api, '_addModulesInfo');
-        spyOn(api._munger, 'add_plugin_changes')
-            .andReturn(jasmine.createSpyObj('munger', ['save_all']));
-
-        fail = jasmine.createSpy('fail');
-        gradleBuilder = jasmine.createSpyObj('gradleBuilder', ['prepBuildFiles']);
-        spyOn(builders, 'getBuilder').andReturn(gradleBuilder);
-    });
-
-    it('should call gradleBuilder.prepBuildFiles for every plugin with frameworks', function(done) {
-        api.addPlugin(new PluginInfo(path.join(FIXTURES, 'cordova-plugin-fake')))
-        .catch(fail)
-        .fin(function () {
-            expect(fail).not.toHaveBeenCalled();
-            expect(gradleBuilder.prepBuildFiles).toHaveBeenCalled();
-            done();
-        });
-    });
-
-    it('shouldn\'t trigger gradleBuilder.prepBuildFiles for plugins without android frameworks', function(done) {
-        api.addPlugin(new PluginInfo(path.join(FIXTURES, 'cordova-plugin-fake-ios-frameworks')))
-        .catch(fail)
-        .fin(function () {
-            expect(fail).not.toHaveBeenCalled();
-            expect(gradleBuilder.prepBuildFiles).not.toHaveBeenCalled();
-            done();
-        });
-    });
-});

http://git-wip-us.apache.org/repos/asf/cordova-android/blob/8ab75e71/spec/unit/pluginHandlers/common.spec.js
----------------------------------------------------------------------
diff --git a/spec/unit/pluginHandlers/common.spec.js b/spec/unit/pluginHandlers/common.spec.js
new file mode 100644
index 0000000..7280ffb
--- /dev/null
+++ b/spec/unit/pluginHandlers/common.spec.js
@@ -0,0 +1,170 @@
+/*
+ *
+ *
+ * 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.
+ *
+*/
+
+var rewire = require('rewire');
+var common = rewire('../../../bin/templates/cordova/lib/pluginHandlers');
+var path = require('path');
+var fs = require('fs');
+var osenv = require('os');
+var shell = require('shelljs');
+var test_dir = path.join(osenv.tmpdir(), 'test_plugman');
+var project_dir = path.join(test_dir, 'project');
+var src = path.join(project_dir, 'src');
+var dest = path.join(project_dir, 'dest');
+var java_dir = path.join(src, 'one', 'two', 'three');
+var java_file = path.join(java_dir, 'test.java');
+var symlink_file = path.join(java_dir, 'symlink');
+var non_plugin_file = path.join(osenv.tmpdir(), 'non_plugin_file');
+
+var copyFile = common.__get__('copyFile');
+var deleteJava = common.__get__('deleteJava');
+var copyNewFile = common.__get__('copyNewFile');
+
+describe('common platform handler', function() {
+
+    describe('copyFile', function() {
+        it('should throw if source path not found', function(){
+            shell.rm('-rf', src);
+            expect(function(){copyFile(test_dir, src, project_dir, dest);})
+                .toThrow(new Error('"' + src + '" not found!'));
+        });
+
+        it('should throw if src not in plugin directory', function(){
+            shell.mkdir('-p', project_dir);
+            fs.writeFileSync(non_plugin_file, 'contents', 'utf-8');
+            expect(function(){copyFile(test_dir, '../non_plugin_file', project_dir, dest);}).
+                toThrow(new Error('"' + non_plugin_file + '" not located within plugin!'));
+            shell.rm('-rf', test_dir);
+        });
+
+        it('should allow symlink src, if inside plugin', function(){
+            shell.mkdir('-p', java_dir);
+            fs.writeFileSync(java_file, 'contents', 'utf-8');
+
+            // This will fail on windows if not admin - ignore the error in that case.
+            if (ignoreEPERMonWin32(java_file, symlink_file)) {
+                return;
+            }
+
+            copyFile(test_dir, symlink_file, project_dir, dest);
+            shell.rm('-rf', project_dir);
+        });
+
+        it('should throw if symlink is linked to a file outside the plugin', function(){
+            shell.mkdir('-p', java_dir);
+            fs.writeFileSync(non_plugin_file, 'contents', 'utf-8');
+
+            // This will fail on windows if not admin - ignore the error in that case.
+            if (ignoreEPERMonWin32(non_plugin_file, symlink_file)) {
+                return;
+            }
+
+            expect(function(){copyFile(test_dir, symlink_file, project_dir, dest);}).
+                toThrow(new Error('"' + symlink_file + '" not located within plugin!'));
+            shell.rm('-rf', project_dir);
+        });
+
+        it('should throw if dest is outside the project directory', function(){
+            shell.mkdir('-p', java_dir);
+            fs.writeFileSync(java_file, 'contents', 'utf-8');
+            expect(function(){copyFile(test_dir, java_file, project_dir, non_plugin_file);}).
+                toThrow(new Error('"' + non_plugin_file + '" not located within project!'));
+            shell.rm('-rf', project_dir);
+        });
+
+        it('should call mkdir -p on target path', function(){
+            shell.mkdir('-p', java_dir);
+            fs.writeFileSync(java_file, 'contents', 'utf-8');
+
+            var s = spyOn(shell, 'mkdir').andCallThrough();
+            var resolvedDest = path.resolve(project_dir, dest);
+
+            copyFile(test_dir, java_file, project_dir, dest);
+
+            expect(s).toHaveBeenCalled();
+            expect(s).toHaveBeenCalledWith('-p', path.dirname(resolvedDest));
+            shell.rm('-rf', project_dir);
+        });
+
+        it('should call cp source/dest paths', function(){
+            shell.mkdir('-p', java_dir);
+            fs.writeFileSync(java_file, 'contents', 'utf-8');
+
+            var s = spyOn(shell, 'cp').andCallThrough();
+            var resolvedDest = path.resolve(project_dir, dest);
+
+            copyFile(test_dir, java_file, project_dir, dest);
+
+            expect(s).toHaveBeenCalled();
+            expect(s).toHaveBeenCalledWith('-f', java_file, resolvedDest);
+
+            shell.rm('-rf', project_dir);
+        });
+    });
+
+    describe('copyNewFile', function () {
+        it('should throw if target path exists', function(){
+            shell.mkdir('-p', dest);
+            expect(function(){copyNewFile(test_dir, src, project_dir, dest);}).
+                toThrow(new Error('"' + dest + '" already exists!'));
+            shell.rm('-rf', dest);
+        });
+    });
+
+    describe('deleteJava', function() {
+        beforeEach(function() {
+            shell.mkdir('-p', java_dir);
+            fs.writeFileSync(java_file, 'contents', 'utf-8');
+        });
+
+        afterEach(function() {
+            shell.rm('-rf', java_dir);
+        });
+
+        it('should call fs.unlinkSync on the provided paths', function(){
+            var s = spyOn(fs, 'unlinkSync').andCallThrough();
+            deleteJava(project_dir, java_file);
+            expect(s).toHaveBeenCalled();
+            expect(s).toHaveBeenCalledWith(path.resolve(project_dir, java_file));
+        });
+
+        it('should delete empty directories after removing source code in a java src path hierarchy', function(){
+            deleteJava(project_dir, java_file);
+            expect(fs.existsSync(java_file)).not.toBe(true);
+            expect(fs.existsSync(java_dir)).not.toBe(true);
+            expect(fs.existsSync(path.join(src,'one'))).not.toBe(true);
+        });
+
+        it('should never delete the top-level src directory, even if all plugins added were removed', function(){
+            deleteJava(project_dir, java_file);
+            expect(fs.existsSync(src)).toBe(true);
+        });
+    });
+});
+
+function ignoreEPERMonWin32(symlink_src, symlink_dest) {
+    try {
+        fs.symlinkSync(symlink_src, symlink_dest);
+    } catch (e) {
+        if (process.platform === 'win32' && e.message.indexOf('Error: EPERM, operation not permitted' > -1)) {
+            return true;
+        }
+        throw e;
+    }
+    return false;
+}

http://git-wip-us.apache.org/repos/asf/cordova-android/blob/8ab75e71/spec/unit/pluginHandlers/handlers.spec.js
----------------------------------------------------------------------
diff --git a/spec/unit/pluginHandlers/handlers.spec.js b/spec/unit/pluginHandlers/handlers.spec.js
new file mode 100644
index 0000000..abeb48b
--- /dev/null
+++ b/spec/unit/pluginHandlers/handlers.spec.js
@@ -0,0 +1,258 @@
+/**
+    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 rewire = require('rewire');
+var common  = rewire('../../../bin/templates/cordova/lib/pluginHandlers');
+var android = common.__get__('handlers');
+var path    = require('path');
+var fs      = require('fs');
+var shell   = require('shelljs');
+var os      = require('os');
+var temp    = path.join(os.tmpdir(), 'plugman');
+var plugins_dir = path.join(temp, 'cordova/plugins');
+var dummyplugin = path.join(__dirname, '../../fixtures/org.test.plugins.dummyplugin');
+var faultyplugin = path.join(__dirname, '../../fixtures/org.test.plugins.faultyplugin');
+var android_project = path.join(__dirname, '../../fixtures/android_project/*');
+
+var PluginInfo = require('cordova-common').PluginInfo;
+var AndroidProject = require('../../../bin/templates/cordova/lib/AndroidProject');
+
+var dummyPluginInfo = new PluginInfo(dummyplugin);
+var valid_source = dummyPluginInfo.getSourceFiles('android'),
+    valid_resources = dummyPluginInfo.getResourceFiles('android'),
+    valid_libs = dummyPluginInfo.getLibFiles('android');
+
+var faultyPluginInfo = new PluginInfo(faultyplugin);
+var invalid_source = faultyPluginInfo.getSourceFiles('android');
+
+describe('android project handler', function() {
+    describe('installation', function() {
+        var copyFileOrig = common.__get__('copyFile');
+        var copyFileSpy = jasmine.createSpy('copyFile');
+        var dummyProject;
+
+        beforeEach(function() {
+            shell.mkdir('-p', temp);
+            dummyProject = AndroidProject.getProjectFile(temp);
+            common.__set__('copyFile', copyFileSpy);
+        });
+
+        afterEach(function() {
+            shell.rm('-rf', temp);
+            common.__set__('copyFile', copyFileOrig);
+        });
+
+        describe('of <lib-file> elements', function() {
+            it('should copy files', function () {
+                android['lib-file'].install(valid_libs[0], dummyPluginInfo, dummyProject);
+                expect(copyFileSpy).toHaveBeenCalledWith(dummyplugin, 'src/android/TestLib.jar', temp, path.join('libs', 'TestLib.jar'), false);
+            });
+        });
+
+        describe('of <resource-file> elements', function() {
+            it('should copy files', function () {
+                android['resource-file'].install(valid_resources[0], dummyPluginInfo, dummyProject);
+                expect(copyFileSpy).toHaveBeenCalledWith(dummyplugin, 'android-resource.xml', temp, path.join('res', 'xml', 'dummy.xml'), false);
+            });
+        });
+
+        describe('of <source-file> elements', function() {
+            beforeEach(function() {
+                shell.cp('-rf', android_project, temp);
+            });
+
+            it('should copy stuff from one location to another by calling common.copyFile', function() {
+                android['source-file'].install(valid_source[0], dummyPluginInfo, dummyProject);
+                expect(copyFileSpy)
+                    .toHaveBeenCalledWith(dummyplugin, 'src/android/DummyPlugin.java', temp, path.join('src/com/phonegap/plugins/dummyplugin/DummyPlugin.java'), false);
+            });
+
+            it('should throw if source file cannot be found', function() {
+                common.__set__('copyFile', copyFileOrig);
+                expect(function() {
+                    android['source-file'].install(invalid_source[0], faultyPluginInfo, dummyProject);
+                }).toThrow('"' + path.resolve(faultyplugin, 'src/android/NotHere.java') + '" not found!');
+            });
+
+            it('should throw if target file already exists', function() {
+                // write out a file
+                var target = path.resolve(temp, 'src/com/phonegap/plugins/dummyplugin');
+                shell.mkdir('-p', target);
+                target = path.join(target, 'DummyPlugin.java');
+                fs.writeFileSync(target, 'some bs', 'utf-8');
+
+                expect(function() {
+                    android['source-file'].install(valid_source[0], dummyPluginInfo, dummyProject);
+                }).toThrow('"' + target + '" already exists!');
+            });
+        });
+
+        describe('of <framework> elements', function() {
+
+            var someString = jasmine.any(String);
+
+            var copyNewFileOrig = common.__get__('copyNewFile');
+            var copyNewFileSpy = jasmine.createSpy('copyNewFile');
+
+            beforeEach(function() {
+                shell.cp('-rf', android_project, temp);
+
+                spyOn(dummyProject, 'addSystemLibrary');
+                spyOn(dummyProject, 'addSubProject');
+                spyOn(dummyProject, 'addGradleReference');
+                common.__set__('copyNewFile', copyNewFileSpy);
+            });
+
+            afterEach(function() {
+                common.__set__('copyNewFile', copyNewFileOrig);
+            });
+
+            it('should throw if framework doesn\'t have "src" attribute', function() {
+                expect(function() { android.framework.install({}, dummyPluginInfo, dummyProject); }).toThrow();
+            });
+
+            it('should install framework without "parent" attribute into project root', function() {
+                var framework = {src: 'plugin-lib'};
+                android.framework.install(framework, dummyPluginInfo, dummyProject);
+                expect(dummyProject.addSystemLibrary).toHaveBeenCalledWith(dummyProject.projectDir, someString);
+            });
+
+            it('should install framework with "parent" attribute into parent framework dir', function() {
+                var childFramework = {src: 'plugin-lib2', parent: 'plugin-lib'};
+                android.framework.install(childFramework, dummyPluginInfo, dummyProject);
+                expect(dummyProject.addSystemLibrary).toHaveBeenCalledWith(path.resolve(dummyProject.projectDir, childFramework.parent), someString);
+            });
+
+            it('should not copy anything if "custom" attribute is not set', function() {
+                var framework = {src: 'plugin-lib'};
+                var cpSpy = spyOn(shell, 'cp');
+                android.framework.install(framework, dummyPluginInfo, dummyProject);
+                expect(dummyProject.addSystemLibrary).toHaveBeenCalledWith(someString, framework.src);
+                expect(cpSpy).not.toHaveBeenCalled();
+            });
+
+            it('should copy framework sources if "custom" attribute is set', function() {
+                var framework = {src: 'plugin-lib', custom: true};
+                android.framework.install(framework, dummyPluginInfo, dummyProject);
+                expect(dummyProject.addSubProject).toHaveBeenCalledWith(dummyProject.projectDir, someString);
+                expect(copyNewFileSpy).toHaveBeenCalledWith(dummyPluginInfo.dir, framework.src, dummyProject.projectDir, someString, false);
+            });
+
+            it('should install gradleReference using project.addGradleReference', function() {
+                var framework = {src: 'plugin-lib', custom: true, type: 'gradleReference'};
+                android.framework.install(framework, dummyPluginInfo, dummyProject);
+                expect(copyNewFileSpy).toHaveBeenCalledWith(dummyPluginInfo.dir, framework.src, dummyProject.projectDir, someString, false);
+                expect(dummyProject.addGradleReference).toHaveBeenCalledWith(dummyProject.projectDir, someString);
+            });
+        });
+    });
+
+    describe('uninstallation', function() {
+
+        var removeFileOrig = common.__get__('removeFile');
+        var deleteJavaOrig = common.__get__('deleteJava');
+
+        var removeFileSpy = jasmine.createSpy('removeFile');
+        var deleteJavaSpy = jasmine.createSpy('deleteJava');
+        var dummyProject;
+
+        beforeEach(function() {
+            shell.mkdir('-p', temp);
+            shell.mkdir('-p', plugins_dir);
+            shell.cp('-rf', android_project, temp);
+            AndroidProject.purgeCache();
+            dummyProject = AndroidProject.getProjectFile(temp);
+            common.__set__('removeFile', removeFileSpy);
+            common.__set__('deleteJava', deleteJavaSpy);
+        });
+
+        afterEach(function() {
+            shell.rm('-rf', temp);
+            common.__set__('removeFile', removeFileOrig);
+            common.__set__('deleteJava', deleteJavaOrig);
+        });
+
+        describe('of <lib-file> elements', function(done) {
+            it('should remove jar files', function () {
+                android['lib-file'].install(valid_libs[0], dummyPluginInfo, dummyProject);
+                android['lib-file'].uninstall(valid_libs[0], dummyPluginInfo, dummyProject);
+                expect(removeFileSpy).toHaveBeenCalledWith(temp, path.join('libs/TestLib.jar'));
+            });
+        });
+
+        describe('of <resource-file> elements', function(done) {
+            it('should remove files', function () {
+                android['resource-file'].install(valid_resources[0], dummyPluginInfo, dummyProject);
+                android['resource-file'].uninstall(valid_resources[0], dummyPluginInfo, dummyProject);
+                expect(removeFileSpy).toHaveBeenCalledWith(temp, path.join('res/xml/dummy.xml'));
+            });
+        });
+
+        describe('of <source-file> elements', function() {
+            it('should remove stuff by calling common.deleteJava', function() {
+                android['source-file'].install(valid_source[0], dummyPluginInfo, dummyProject);
+                android['source-file'].uninstall(valid_source[0], dummyPluginInfo, dummyProject);
+                expect(deleteJavaSpy).toHaveBeenCalledWith(temp, path.join('src/com/phonegap/plugins/dummyplugin/DummyPlugin.java'));
+            });
+        });
+
+        describe('of <framework> elements', function() {
+
+            var someString = jasmine.any(String);
+
+            beforeEach(function() {
+                shell.mkdir(path.join(dummyProject.projectDir, dummyPluginInfo.id));
+
+                spyOn(dummyProject, 'removeSystemLibrary');
+                spyOn(dummyProject, 'removeSubProject');
+                spyOn(dummyProject, 'removeGradleReference');
+            });
+
+            it('should throw if framework doesn\'t have "src" attribute', function() {
+                expect(function() { android.framework.uninstall({}, dummyPluginInfo, dummyProject); }).toThrow();
+            });
+
+            it('should uninstall framework without "parent" attribute into project root', function() {
+                var framework = {src: 'plugin-lib'};
+                android.framework.uninstall(framework, dummyPluginInfo, dummyProject);
+                expect(dummyProject.removeSystemLibrary).toHaveBeenCalledWith(dummyProject.projectDir, someString);
+            });
+
+            it('should uninstall framework with "parent" attribute into parent framework dir', function() {
+                var childFramework = {src: 'plugin-lib2', parent: 'plugin-lib'};
+                android.framework.uninstall(childFramework, dummyPluginInfo, dummyProject);
+                expect(dummyProject.removeSystemLibrary).toHaveBeenCalledWith(path.resolve(dummyProject.projectDir, childFramework.parent), someString);
+            });
+
+            it('should remove framework sources if "custom" attribute is set', function() {
+                var framework = {src: 'plugin-lib', custom: true};
+                android.framework.uninstall(framework, dummyPluginInfo, dummyProject);
+                expect(dummyProject.removeSubProject).toHaveBeenCalledWith(dummyProject.projectDir, someString);
+                expect(removeFileSpy).toHaveBeenCalledWith(dummyProject.projectDir, someString);
+            });
+
+            it('should install gradleReference using project.removeGradleReference', function() {
+                var framework = {src: 'plugin-lib', custom: true, type: 'gradleReference'};
+                android.framework.uninstall(framework, dummyPluginInfo, dummyProject);
+                expect(removeFileSpy).toHaveBeenCalledWith(dummyProject.projectDir, someString);
+                expect(dummyProject.removeGradleReference).toHaveBeenCalledWith(dummyProject.projectDir, someString);
+            });
+        });
+    });
+});


---------------------------------------------------------------------
To unsubscribe, e-mail: commits-unsubscribe@cordova.apache.org
For additional commands, e-mail: commits-help@cordova.apache.org