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 2013/07/11 23:50:02 UTC

[02/43] git commit: redid core method specs so they dont touch FS

redid core method specs so they dont touch FS


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

Branch: refs/heads/plugman-registry
Commit: cf8887eaf1be9f55ac3b6bf81ae885d6bbed2a91
Parents: 59c684a
Author: Fil Maj <ma...@gmail.com>
Authored: Wed Jun 26 17:29:22 2013 -0700
Committer: Fil Maj <ma...@gmail.com>
Committed: Wed Jun 26 17:34:21 2013 -0700

----------------------------------------------------------------------
 spec/fetch.spec.js                     |  50 ++++---
 spec/install.spec.js                   | 217 +++++++---------------------
 spec/plugins/dependencies/A/plugin.xml |   2 +-
 spec/prepare.spec.js                   | 100 +++++++------
 spec/uninstall.spec.js                 | 133 ++++++-----------
 src/fetch.js                           |  11 +-
 src/install.js                         |  32 +++-
 src/prepare.js                         |  10 +-
 src/uninstall.js                       |   4 +-
 9 files changed, 214 insertions(+), 345 deletions(-)
----------------------------------------------------------------------


http://git-wip-us.apache.org/repos/asf/cordova-plugman/blob/cf8887ea/spec/fetch.spec.js
----------------------------------------------------------------------
diff --git a/spec/fetch.spec.js b/spec/fetch.spec.js
index 88a1e20..1fc1aaf 100644
--- a/spec/fetch.spec.js
+++ b/spec/fetch.spec.js
@@ -3,59 +3,61 @@ var fetch   = require('../src/fetch'),
     os      = require('osenv'),
     path    = require('path'),
     shell   = require('shelljs'),
+    xml_helpers = require('../src/util/xml-helpers'),
+    metadata = require('../src/util/metadata'),
     temp    = path.join(os.tmpdir(), 'plugman'),
     test_plugin = path.join(__dirname, 'plugins', 'ChildBrowser'),
     plugins = require('../src/util/plugins');
 
 describe('fetch', function() {
-    var copied_plugin_path = path.join(temp, 'com.phonegap.plugins.childbrowser');
-
-    beforeEach(function() {
-        shell.mkdir('-p', temp);
-    });
-    afterEach(function() {
-        try{shell.rm('-rf', temp);}catch(e){}
-    });
-
     describe('local plugins', function() {
+        var xml, rm, sym, mkdir, cp, save_metadata;
+        beforeEach(function() {
+            xml = spyOn(xml_helpers, 'parseElementtreeSync').andReturn({
+                getroot:function() { return {attrib:{id:'id'}};}
+            });
+            rm = spyOn(shell, 'rm');
+            sym = spyOn(fs, 'symlinkSync');
+            mkdir = spyOn(shell, 'mkdir');
+            cp = spyOn(shell, 'cp');
+            save_metadata = spyOn(metadata, 'save_fetch_metadata');
+        });
         it('should copy locally-available plugin to plugins directory', function() {
             fetch(test_plugin, temp);
-            expect(fs.existsSync(copied_plugin_path)).toBe(true);
+            expect(cp).toHaveBeenCalledWith('-R', path.join(test_plugin, '*'), path.join(temp, 'id'));
         });
-       // it('should copy locally-available plugin to plugins directory when specified with a trailing slash', function() {
-       //     fetch(test_plugin+'/', temp, false);
-       //     expect(fs.existsSync(copied_plugin_path)).toBe(true);
-       // });
         it('should create a symlink if used with `link` param', function() {
             fetch(test_plugin, temp, { link: true });
-            expect(fs.lstatSync(copied_plugin_path).isSymbolicLink()).toBe(true);
+            expect(sym).toHaveBeenCalledWith(test_plugin, path.join(temp, 'id'), 'dir');
         });
     });
     describe('remote plugins', function() {
+        var clone;
+        beforeEach(function() {
+            clone = spyOn(plugins, 'clonePluginGitRepo');
+        });
         it('should call clonePluginGitRepo for https:// and git:// based urls', function() {
-            var s = spyOn(plugins, 'clonePluginGitRepo');
-            fetch("https://github.com/bobeast/GAPlugin.git", temp);
-            expect(s).toHaveBeenCalled();
+            var url = "https://github.com/bobeast/GAPlugin.git";
+            fetch(url, temp);
+            expect(clone).toHaveBeenCalledWith(url, temp, '.', undefined, jasmine.any(Function));
         });
         it('should call clonePluginGitRepo with subdir if applicable', function() {
-            var s = spyOn(plugins, 'clonePluginGitRepo');
             var url = "https://github.com/bobeast/GAPlugin.git";
             var dir = 'fakeSubDir';
             fetch(url, temp, { subdir: dir });
-            expect(s).toHaveBeenCalledWith(url, temp, dir, undefined, jasmine.any(Function));
+            expect(clone).toHaveBeenCalledWith(url, temp, dir, undefined, jasmine.any(Function));
         });
         it('should call clonePluginGitRepo with subdir and git ref if applicable', function() {
-            var s = spyOn(plugins, 'clonePluginGitRepo');
             var url = "https://github.com/bobeast/GAPlugin.git";
             var dir = 'fakeSubDir';
             var ref = 'fakeGitRef';
             fetch(url, temp, { subdir: dir, git_ref: ref });
-            expect(s).toHaveBeenCalledWith(url, temp, dir, ref, jasmine.any(Function));
+            expect(clone).toHaveBeenCalledWith(url, temp, dir, ref, jasmine.any(Function));
         });
         it('should throw if used with url and `link` param', function() {
             expect(function() {
-                fetch("https://github.com/bobeast/GAPlugin.git", temp, true);
-            }).toThrow();
+                fetch("https://github.com/bobeast/GAPlugin.git", temp, {link:true});
+            }).toThrow('--link is not supported for git URLs');
         });
     });
 });

http://git-wip-us.apache.org/repos/asf/cordova-plugman/blob/cf8887ea/spec/install.spec.js
----------------------------------------------------------------------
diff --git a/spec/install.spec.js b/spec/install.spec.js
index 4bda6a5..644834a 100644
--- a/spec/install.spec.js
+++ b/spec/install.spec.js
@@ -1,186 +1,98 @@
 var install = require('../src/install'),
-
-    android = require('../src/platforms/android'),
-    blackberry = require('../src/platforms/blackberry10'),
-    common = require('../src/platforms/common'),
     actions = require('../src/util/action-stack'),
-    //ios     = require('../src/platforms/ios'),
-    //blackberry10 = require('../src/platforms/blackberry10'),
     config_changes = require('../src/util/config-changes'),
+    xml_helpers = require('../src/util/xml-helpers'),
     plugman = require('../plugman'),
     fs      = require('fs'),
     os      = require('osenv'),
     path    = require('path'),
     shell   = require('shelljs'),
     semver  = require('semver'),
-    temp    = path.join(os.tmpdir(), 'plugman'),
-    childbrowser = path.join(__dirname, 'plugins', 'ChildBrowser'),
-    dep_a = path.join(__dirname, 'plugins', 'dependencies', 'A'),
-    dep_b = path.join(__dirname, 'plugins', 'dependencies', 'B'),
-    dep_c = path.join(__dirname, 'plugins', 'dependencies', 'C'),
-    dep_d = path.join(__dirname, 'plugins', 'dependencies', 'D'),
-    dep_e = path.join(__dirname, 'plugins', 'dependencies', 'E'),
-    dummyplugin = path.join(__dirname, 'plugins', 'DummyPlugin'),
+    temp    = __dirname,
+    dummyplugin = 'DummyPlugin',
     dummy_id = 'com.phonegap.plugins.dummyplugin',
-    variableplugin = path.join(__dirname, 'plugins', 'VariablePlugin'),
-    faultyplugin = path.join(__dirname, 'plugins', 'FaultyPlugin'),
-    engineplugin = path.join(__dirname, 'plugins','EnginePlugin'),
-    android_one_project = path.join(__dirname, 'projects', 'android_one', '*'),
-    //blackberry_project = path.join(__dirname, 'projects', 'blackberry', '*'),
-    //ios_project = path.join(__dirname, 'projects', 'ios-config-xml', '*'),
-    plugins_dir = path.join(temp, 'cordova', 'plugins');
+    variableplugin = 'VariablePlugin',
+    engineplugin = 'EnginePlugin',
+    plugins_dir = path.join(temp, 'plugins');
 
 describe('install', function() {
-    var copied_plugin_path = path.join(temp,'ChildBrowser');
-
+    var exists, get_json, chmod, exec, proc, add_to_queue, prepare, actions_push, c_a;
     beforeEach(function() {
-        shell.mkdir('-p', temp);
-        shell.cp('-rf', android_one_project, temp);
-    });
-    afterEach(function() {
-        shell.rm('-rf', temp);
-    });
-
-    describe('success', function() {
-        it('should properly install assets', function() {
-            var s = spyOn(common, 'copyFile').andCallThrough();
-            install('android', temp, dummyplugin, plugins_dir, {});
-            // making sure the right methods were called
-            expect(s).toHaveBeenCalled();
-            expect(s.calls.length).toEqual(3);
-
-            expect(fs.existsSync(path.join(temp, 'assets', 'www', 'dummyplugin.js'))).toBe(true);
-            expect(fs.existsSync(path.join(temp, 'assets', 'www', 'dummyplugin'))).toBe(true);
-            expect(fs.existsSync(path.join(temp, 'assets', 'www', 'dummyplugin', 'image.jpg'))).toBe(true);
-            expect(fs.statSync(path.join(temp, 'assets', 'www', 'dummyplugin.js')).isFile()).toBe(true);
-            expect(fs.statSync(path.join(temp, 'assets', 'www', 'dummyplugin')).isDirectory()).toBe(true);
-            expect(fs.statSync(path.join(temp, 'assets', 'www', 'dummyplugin', 'image.jpg')).isFile()).toBe(true);
-        });
-        it('should revert all assets on asset install error', function() {
-            var sCopyFile = spyOn(common, 'copyFile').andCallThrough();
-            var sRemoveFile = spyOn(common, 'removeFile').andCallThrough();
-            var sRemoveFileF = spyOn(common, 'removeFileF').andCallThrough();
-            
-            // messing with the plugin
-            shell.mkdir('-p', plugins_dir);
-            shell.cp('-rf', dummyplugin, plugins_dir);
-            shell.rm('-rf', path.join(plugins_dir, 'DummyPlugin', 'www', 'dummyplugin')); 
-            expect(function() {
-                install('android', temp, 'DummyPlugin', plugins_dir, {});
-            }).toThrow();
-            // making sure the right methods were called
-            expect(sCopyFile).toHaveBeenCalled();
-            expect(sCopyFile.calls.length).toEqual(3);
-
-            expect(sRemoveFile).toHaveBeenCalled();
-            expect(sRemoveFile.calls.length).toEqual(1);
-            expect(sRemoveFileF).toHaveBeenCalled();
-            expect(sRemoveFileF.calls.length).toEqual(1);
-           
-            expect(fs.existsSync(path.join(temp, 'assets', 'www', 'dummyplugin.js'))).toBe(false);
-            expect(fs.existsSync(path.join(temp, 'assets', 'www', 'dummyplugin'))).toBe(false);
-        });
-
-        it('should properly install assets into a custom www dir', function() {
-            var s = spyOn(common, 'copyFile').andCallThrough();
-            install('android', temp, dummyplugin, plugins_dir, { www_dir: path.join(temp, 'staging') });
-            // making sure the right methods were called
-            expect(s).toHaveBeenCalled();
-            expect(s.calls.length).toEqual(3);
-
-            expect(fs.existsSync(path.join(temp, 'staging', 'dummyplugin.js'))).toBe(true);
-            expect(fs.existsSync(path.join(temp, 'staging', 'dummyplugin'))).toBe(true);
-            expect(fs.existsSync(path.join(temp, 'staging', 'dummyplugin', 'image.jpg'))).toBe(true);
-            expect(fs.statSync(path.join(temp, 'staging', 'dummyplugin.js')).isFile()).toBe(true);
-            expect(fs.statSync(path.join(temp, 'staging', 'dummyplugin')).isDirectory()).toBe(true);
-            expect(fs.statSync(path.join(temp, 'staging', 'dummyplugin', 'image.jpg')).isFile()).toBe(true);
+        proc = spyOn(actions.prototype, 'process').andCallFake(function(platform, proj, cb) {
+            cb();
         });
-
-        it('should revert all assets on asset install error with a custom www dir', function() {
-            var sCopyFile = spyOn(common, 'copyFile').andCallThrough();
-            var sRemoveFile = spyOn(common, 'removeFile').andCallThrough();
-            var sRemoveFileF = spyOn(common, 'removeFileF').andCallThrough();
-            
-            // messing the plugin
-            shell.mkdir('-p', plugins_dir);
-            shell.cp('-rf', dummyplugin, plugins_dir);
-            shell.rm('-rf', path.join(plugins_dir, 'dummyplugin', 'www', 'dummyplugin')); 
-            expect(function() {
-                install('android', temp, 'DummyPlugin', plugins_dir, { www_dir: path.join(temp, 'staging') });
-            }).toThrow();
-            // making sure the right methods were called
-            expect(sCopyFile).toHaveBeenCalled();
-            expect(sCopyFile.calls.length).toEqual(3);
-
-            expect(sRemoveFile).toHaveBeenCalled();
-            expect(sRemoveFile.calls.length).toEqual(1);
-            expect(sRemoveFileF).toHaveBeenCalled();
-            expect(sRemoveFileF.calls.length).toEqual(1);
-           
-            expect(fs.existsSync(path.join(temp, 'staging', 'dummyplugin.js'))).toBe(false);
-            expect(fs.existsSync(path.join(temp, 'staging', 'dummyplugin'))).toBe(false);
+        actions_push = spyOn(actions.prototype, 'push');
+        c_a = spyOn(actions.prototype, 'createAction');
+        prepare = spyOn(plugman, 'prepare');
+        exec = spyOn(shell, 'exec').andReturn({code:1});
+        chmod = spyOn(fs, 'chmodSync');
+        exists = spyOn(fs, 'existsSync').andReturn(true);
+        get_json = spyOn(config_changes, 'get_platform_json').andReturn({
+            installed_plugins:{},
+            dependent_plugins:{}
         });
-
+        add_to_queue = spyOn(config_changes, 'add_installed_plugin_to_prepare_queue');
+    });
+    describe('success', function() {
         it('should call prepare after a successful install', function() {
-            var s = spyOn(plugman, 'prepare');
             install('android', temp, dummyplugin, plugins_dir, {});
-            expect(s).toHaveBeenCalled();
+            expect(prepare).toHaveBeenCalled();
         });
 
         it('should call fetch if provided plugin cannot be resolved locally', function() {
             var s = spyOn(plugman, 'fetch');
+            exists.andReturn(false);
             install('android', temp, 'CLEANYOURSHORTS', plugins_dir, {});
             expect(s).toHaveBeenCalled();
         });
-        it('should call the config-changes module\'s add_installed_plugin_to_prepare_queue method', function() {
-            var spy = spyOn(config_changes, 'add_installed_plugin_to_prepare_queue');
+        it('should call the config-changes module\'s add_installed_plugin_to_prepare_queue method after processing an install', function() {
             install('android', temp, dummyplugin, plugins_dir, {});
-            expect(spy).toHaveBeenCalledWith(plugins_dir, dummy_id, 'android', {}, true);
+            expect(add_to_queue).toHaveBeenCalledWith(plugins_dir, 'DummyPlugin', 'android', {}, true);
         });
         it('should notify if plugin is already installed into project', function() {
-            expect(function() {
-                install('android', temp, dummyplugin, plugins_dir, {});
-            }).not.toThrow();
             var spy = spyOn(console, 'log');
+            get_json.andReturn({
+                installed_plugins:{
+                    'com.phonegap.plugins.dummyplugin':{}
+                },
+                dependent_plugins:{}
+            });
+            install('android', temp, dummyplugin, plugins_dir, {});
+            expect(spy).toHaveBeenCalledWith('Plugin "'+dummy_id+'" already installed, \'sall good.');
+        });
+        it('should check version if plugin has engine tag', function(){
+            var spy = spyOn(semver, 'satisfies').andReturn(true);
+            exec.andReturn({code:0,output:"2.5.0"});
+            install('android', temp, 'engineplugin', plugins_dir, {});
+            expect(spy).toHaveBeenCalledWith('2.5.0','>=2.3.0');
+        });
+        it('should queue up actions as appropriate for that plugin and call process on the action stack', function() {
             install('android', temp, dummyplugin, plugins_dir, {});
-            expect(spy).toHaveBeenCalledWith('Plugin "com.phonegap.plugins.dummyplugin" already installed, \'sall good.');
+            expect(actions_push.calls.length).toEqual(3);
+            expect(c_a).toHaveBeenCalledWith(jasmine.any(Function), [jasmine.any(Object), path.join(plugins_dir, dummyplugin), temp, dummy_id], jasmine.any(Function), [jasmine.any(Object), temp, dummy_id]);
+            expect(proc).toHaveBeenCalled();
         });
 
         describe('with dependencies', function() {
             it('should process all dependent plugins', function() {
-                var spy = spyOn(actions.prototype, 'process').andCallThrough();
-                shell.mkdir('-p', plugins_dir);
-                shell.cp('-rf', dep_a, plugins_dir);
-                shell.cp('-rf', dep_d, plugins_dir);
-                shell.cp('-rf', dep_c, plugins_dir);
-                install('android', temp, 'A', plugins_dir, {});
-                expect(spy.calls.length).toEqual(3);
+                // Plugin A depends on C & D
+                install('android', temp, 'A', path.join(plugins_dir, 'dependencies'), {});
+                // So process should be called 3 times
+                expect(proc.calls.length).toEqual(3);
             });
             it('should fetch any dependent plugins if missing', function() {
-                var spy = spyOn(plugman, 'fetch');
-                shell.mkdir('-p', plugins_dir);
-                shell.cp('-rf', dep_a, plugins_dir);
-                shell.cp('-rf', dep_c, plugins_dir);
-                install('android', temp, 'A', plugins_dir, {});
-                expect(spy).toHaveBeenCalled();
+                var s = spyOn(plugman, 'fetch').andCallFake(function(id, dir, opts, cb) {
+                    cb(false, path.join(dir, id));
+                });
+                exists.andReturn(false);
+                // Plugin A depends on C & D
+                install('android', temp, 'A', path.join(plugins_dir, 'dependencies'), {});
+                expect(s.calls.length).toEqual(3);
             });
         });
-        it('should check version if plugin has engine tag', function(){
-            shell.cp('-rf', engineplugin, plugins_dir);
-            var spy = spyOn(semver, 'satisfies').andCallThrough();
-            install('android', temp, 'engineplugin', plugins_dir, {});
-            expect(spy).toHaveBeenCalledWith('2.7.0rc1', '>=2.3.0');
-        });
     });
     
     describe('failure', function() {
-        it('should throw if asset target already exists', function() {
-            var target = path.join(temp, 'assets', 'www', 'dummyplugin.js');
-            fs.writeFileSync(target, 'some bs', 'utf-8');
-            expect(function() {
-                install('android', temp, dummyplugin, plugins_dir, {});
-            }).toThrow();
-        });
         it('should throw if platform is unrecognized', function() {
             expect(function() {
                 install('atari', temp, 'SomePlugin', plugins_dir, {});
@@ -191,24 +103,5 @@ describe('install', function() {
                 install('android', temp, variableplugin, plugins_dir, {});
             }).toThrow('Variable(s) missing: API_KEY');
         });
-        it('should throw if a file required for installation cannot be found', function() {
-            shell.mkdir('-p', plugins_dir);
-            shell.cp('-rf', dummyplugin, plugins_dir);
-            shell.rm(path.join(plugins_dir, 'DummyPlugin', 'src', 'android', 'DummyPlugin.java')); 
-            
-            expect(function() {
-                install('android', temp, 'DummyPlugin', plugins_dir, {});
-            }).toThrow();
-        });
-        it('should pass error into specified callback if a file required for installation cannot be found', function(done) {
-            shell.mkdir('-p', plugins_dir);
-            shell.cp('-rf', dummyplugin, plugins_dir);
-            shell.rm(path.join(plugins_dir, 'DummyPlugin', 'src', 'android', 'DummyPlugin.java')); 
-            
-            install('android', temp, 'DummyPlugin', plugins_dir, {}, function(err) {
-                expect(err).toBeDefined();
-                done();
-            });
-        });
     });
 });

http://git-wip-us.apache.org/repos/asf/cordova-plugman/blob/cf8887ea/spec/plugins/dependencies/A/plugin.xml
----------------------------------------------------------------------
diff --git a/spec/plugins/dependencies/A/plugin.xml b/spec/plugins/dependencies/A/plugin.xml
index 615af6e..a8acbfb 100644
--- a/spec/plugins/dependencies/A/plugin.xml
+++ b/spec/plugins/dependencies/A/plugin.xml
@@ -25,7 +25,7 @@
 
     <name>Plugin A</name>
 
-    <dependency id="C" />
+    <dependency id="C" url="C" />
     <dependency id="D" url="D" />
 
     <asset src="www/plugin-a.js" target="plugin-a.js" />

http://git-wip-us.apache.org/repos/asf/cordova-plugman/blob/cf8887ea/spec/prepare.spec.js
----------------------------------------------------------------------
diff --git a/spec/prepare.spec.js b/spec/prepare.spec.js
index 7fa49c0..675444a 100644
--- a/spec/prepare.spec.js
+++ b/spec/prepare.spec.js
@@ -5,65 +5,69 @@ var platforms = require('../src/platforms'),
     path    = require('path'),
     shell   = require('shelljs'),
     config_changes = require('../src/util/config-changes'),
-    temp    = path.join(os.tmpdir(), 'plugman'),
-    childbrowser = path.join(__dirname, 'plugins', 'ChildBrowser'),
-    dummyplugin = path.join(__dirname, 'plugins', 'DummyPlugin'),
-    androidplugin = path.join(__dirname, 'plugins', 'AndroidJS'),
-    android_one_project = path.join(__dirname, 'projects', 'android_one', '*');
-    plugins_dir = path.join(temp, 'cordova', 'plugins');
+    xml_helpers = require('../src/util/xml-helpers'),
+    temp    = __dirname,
+    childbrowser = 'ChildBrowser',
+    dummyplugin = 'DummyPlugin',
+    androidplugin = 'AndroidJS',
+    plugins_dir = path.join(temp, 'plugins');
+var json = path.join(temp, 'assets', 'www', 'cordova_plugins.json');
+var js = path.join(temp, 'assets', 'www', 'cordova_plugins.js');
 
 describe('prepare', function() {
+    var proc, readdir, write, stat, read, parseET, mkdir;
+    var root, findall, find;
     beforeEach(function() {
-        shell.mkdir('-p', temp);
-        shell.mkdir('-p', plugins_dir);
-        shell.cp('-rf', childbrowser, plugins_dir);
-        shell.cp('-rf', android_one_project, temp);
+        mkdir = spyOn(shell, 'mkdir');
+        proc = spyOn(config_changes, 'process');
+        readdir = spyOn(fs, 'readdirSync').andReturn([]);
+        write = spyOn(fs, 'writeFileSync');
+        stat = spyOn(fs, 'statSync').andReturn({isDirectory:function() { return true; }});
+        root = jasmine.createSpy('ElementTree getroot').andReturn({
+            attrib:{
+                id:'someid'
+            }
+        });
+        findall = jasmine.createSpy('ElementTree findall');
+        find = jasmine.createSpy('ElementTree find');
+        parseET = spyOn(xml_helpers, 'parseElementtreeSync').andReturn({
+            getroot:root,
+            findall:findall,
+            find:find
+        });
     });
-    afterEach(function() {
-        shell.rm('-rf', temp);
-    });
-
-    var www = path.join(temp, 'assets', 'www');
-    
     it('should create a cordova_plugins.json file', function() {
         prepare(temp, 'android', plugins_dir);
-        expect(fs.existsSync(path.join(www, 'cordova_plugins.json'))).toBe(true);
-    });
-    it('should create a plugins directory in an application\'s www directory', function() {
-        shell.cp('-rf', dummyplugin, plugins_dir);
-        prepare(temp, 'android', plugins_dir);
-        expect(fs.existsSync(path.join(www, 'plugins'))).toBe(true);
-    });
-    it('should not add code to load platform js in a project for a different platform', function() {
-        shell.cp('-rf', dummyplugin, plugins_dir);
-        prepare(temp, 'android', plugins_dir);
-        var plugins = JSON.parse(fs.readFileSync(path.join(www, 'cordova_plugins.json'), 'utf-8'));
-        expect(plugins.length).toEqual(1);
-        expect(plugins[0].id).not.toMatch(/dummy/);
+        expect(write).toHaveBeenCalledWith(json, jasmine.any(String), 'utf-8');
     });
-    it('should add code to load platform js if platform is applicable', function() {
-        shell.cp('-rf', androidplugin, plugins_dir);
+    it('should create a cordova_plugins.js file', function() {
         prepare(temp, 'android', plugins_dir);
-        var plugins = JSON.parse(fs.readFileSync(path.join(www, 'cordova_plugins.json'), 'utf-8'));
-        expect(plugins.length).toEqual(2);
-        expect(plugins[0].id).toMatch(/android/);
+        expect(write).toHaveBeenCalledWith(js, jasmine.any(String), 'utf-8');
     });
-    it('should parse js modules for multiple plugins added to a single project', function() {
-        shell.cp('-rf', androidplugin, plugins_dir);
-        prepare(temp, 'android', plugins_dir);
-        var plugins = JSON.parse(fs.readFileSync(path.join(www, 'cordova_plugins.json'), 'utf-8'));
-        expect(plugins.length).toEqual(2);
+    describe('handling of js-modules', function() {
+        var read, child_one;
+        var fake_plugins = ['plugin_one', 'plugin_two'];
+        beforeEach(function() {
+            child_one = jasmine.createSpy('getchildren').andReturn([]);
+            read = spyOn(fs, 'readFileSync').andReturn('JAVASCRIPT!');
+            readdir.andReturn(fake_plugins);
+            findall.andReturn([
+                {attrib:{src:'somedir', name:'NAME'}, getchildren:child_one},
+                {attrib:{src:'someotherdir', name:'NAME'}, getchildren:child_one}
+            ]);
+        });
+        it('should create a plugins directory in an application\'s www directory', function() {
+            prepare(temp, 'android', plugins_dir);
+            expect(mkdir).toHaveBeenCalledWith('-p',path.join(temp, 'assets', 'www', 'plugins'));
+        });
+        it('should write out one file per js module', function() {
+            prepare(temp, 'android', plugins_dir);
+            expect(write).toHaveBeenCalledWith(path.join(temp, 'assets', 'www', 'plugins', 'someid', 'somedir'), jasmine.any(String), 'utf-8');
+            expect(write).toHaveBeenCalledWith(path.join(temp, 'assets', 'www', 'plugins', 'someid', 'someotherdir'), jasmine.any(String), 'utf-8');
+        });
     });
-    it('should write out an empty cordova_plugins.json if no plugins are applicable', function() {
-        shell.rm('-rf', path.join(plugins_dir, '*'));
-        prepare(temp, 'android', plugins_dir);
-        var plugins = JSON.parse(fs.readFileSync(path.join(www, 'cordova_plugins.json'), 'utf-8'));
-        expect(plugins.length).toEqual(0);
-    });
-
     it('should call into config-changes\' process method to do config processing', function() {
-        var spy = spyOn(config_changes, 'process');
         prepare(temp, 'android', plugins_dir);
-        expect(spy).toHaveBeenCalledWith(plugins_dir, temp, 'android');
+        expect(proc).toHaveBeenCalledWith(plugins_dir, temp, 'android');
     });
 });

http://git-wip-us.apache.org/repos/asf/cordova-plugman/blob/cf8887ea/spec/uninstall.spec.js
----------------------------------------------------------------------
diff --git a/spec/uninstall.spec.js b/spec/uninstall.spec.js
index 7e51bc1..61e1c81 100644
--- a/spec/uninstall.spec.js
+++ b/spec/uninstall.spec.js
@@ -1,123 +1,72 @@
 var uninstall = require('../src/uninstall'),
-    install = require('../src/install'),
-    common = require('../src/platforms/common'),
     actions = require('../src/util/action-stack'),
-    android = require('../src/platforms/android'),
-    ios     = require('../src/platforms/ios'),
-    blackberry10 = require('../src/platforms/blackberry10'),
-    xml_helpers = require('../src/util/xml-helpers'),
     config_changes = require('../src/util/config-changes'),
+    xml_helpers = require('../src/util/xml-helpers'),
     plugman = require('../plugman'),
     fs      = require('fs'),
-    et      = require('elementtree'),
     os      = require('osenv'),
     path    = require('path'),
     shell   = require('shelljs'),
-    temp    = path.join(os.tmpdir(), 'plugman'),
-    dummyplugin = path.join(__dirname, 'plugins', 'DummyPlugin'),
+    semver  = require('semver'),
+    temp    = __dirname,
+    dummyplugin = 'DummyPlugin',
     dummy_id = 'com.phonegap.plugins.dummyplugin',
-    faultyplugin = path.join(__dirname, 'plugins', 'FaultyPlugin'),
-    childbrowserplugin = path.join(__dirname, 'plugins', 'ChildBrowser'),
-    dep_a = path.join(__dirname, 'plugins', 'dependencies', 'A'),
-    dep_b = path.join(__dirname, 'plugins', 'dependencies', 'B'),
-    dep_c = path.join(__dirname, 'plugins', 'dependencies', 'C'),
-    dep_d = path.join(__dirname, 'plugins', 'dependencies', 'D'),
-    dep_e = path.join(__dirname, 'plugins', 'dependencies', 'E'),
-    android_one_project = path.join(__dirname, 'projects', 'android_one', '*'),
-    ios_project = path.join(__dirname, 'projects', 'ios-config-xml', '*'),
-    plugins_dir = path.join(temp, 'cordova', 'plugins');
+    variableplugin = 'VariablePlugin',
+    engineplugin = 'EnginePlugin',
+    plugins_dir = path.join(temp, 'plugins');
 
 describe('uninstall', function() {
-    var copied_plugin_path = path.join(temp,'ChildBrowser');
-    var dummy_plugin_path = path.join(plugins_dir, dummy_id);
-
+    var exists, get_json, chmod, exec, proc, add_to_queue, prepare, actions_push, c_a, rm;
     beforeEach(function() {
-        shell.mkdir('-p', temp);
-        shell.cp('-rf', android_one_project, temp);
-    });
-    afterEach(function() {
-        shell.rm('-rf', temp);
+        proc = spyOn(actions.prototype, 'process').andCallFake(function(platform, proj, cb) {
+            cb();
+        });
+        actions_push = spyOn(actions.prototype, 'push');
+        c_a = spyOn(actions.prototype, 'createAction');
+        prepare = spyOn(plugman, 'prepare');
+        exec = spyOn(shell, 'exec').andReturn({code:1});
+        chmod = spyOn(fs, 'chmodSync');
+        exists = spyOn(fs, 'existsSync').andReturn(true);
+        get_json = spyOn(config_changes, 'get_platform_json').andReturn({
+            installed_plugins:{},
+            dependent_plugins:{}
+        });
+        rm = spyOn(shell, 'rm');
+        add_to_queue = spyOn(config_changes, 'add_uninstalled_plugin_to_prepare_queue');
     });
-
     describe('success', function() {
-        beforeEach(function() {
-            install('android', temp, dummyplugin, plugins_dir, {});
-        });
-        it('should properly uninstall assets', function() {
-            var s = spyOn(common, 'removeFile').andCallThrough();
-            var s2 = spyOn(common, 'removeFileF').andCallThrough();
-            // making sure the right methods were called
-            uninstall('android', temp, dummy_id, plugins_dir, {});
-            expect(s).toHaveBeenCalled();
-            expect(s.calls.length).toEqual(2);
-            
-            expect(s2).toHaveBeenCalled();
-            expect(s2.calls.length).toEqual(2);
-
-            expect(fs.existsSync(path.join(temp, 'assets', 'www', 'dummyplugin.js'))).toBe(false);
-            expect(fs.existsSync(path.join(temp, 'assets', 'www', 'dummyplugin'))).toBe(false);
+        it('should call prepare after a successful uninstall', function() {
+            uninstall('android', temp, dummyplugin, plugins_dir, {});
+            expect(prepare).toHaveBeenCalled();
         });
-        it('should properly revert all assets on asset uninstall error', function() {
-            var sRemoveFile = spyOn(common, 'removeFile').andCallThrough();
-            var sCopyFile = spyOn(common, 'copyFile').andCallThrough();
-            // making sure the right methods were called
-            
-            shell.rm('-rf', path.join(temp, 'assets', 'www', 'dummyplugin'));
-            
-            expect(function() {
-                uninstall('android', temp, dummy_id, plugins_dir, {});
-            }).toThrow();
-
-            expect(sRemoveFile).toHaveBeenCalled();
-            expect(sRemoveFile.calls.length).toEqual(2);
-            expect(sCopyFile).toHaveBeenCalled();
-            expect(sCopyFile.calls.length).toEqual(2);
-            
-            expect(fs.existsSync(path.join(temp, 'assets', 'www', 'dummyplugin.js'))).toBe(true);
+        it('should call the config-changes module\'s add_uninstalled_plugin_to_prepare_queue method after processing an install', function() {
+            uninstall('android', temp, dummyplugin, plugins_dir, {});
+            expect(add_to_queue).toHaveBeenCalledWith(plugins_dir, 'DummyPlugin', 'android', true);
         });
-        it('should call the config-changes module\'s add_uninstalled_plugin_to_prepare_queue method', function() {
-            var spy = spyOn(config_changes, 'add_uninstalled_plugin_to_prepare_queue');
-            uninstall('android', temp, dummy_id, plugins_dir, {});
-            expect(spy).toHaveBeenCalledWith(plugins_dir, dummy_id, 'android', true);
+        it('should queue up actions as appropriate for that plugin and call process on the action stack', function() {
+            uninstall('android', temp, dummyplugin, plugins_dir, {});
+            expect(actions_push.calls.length).toEqual(3);
+            expect(c_a).toHaveBeenCalledWith(jasmine.any(Function), [jasmine.any(Object), temp, dummy_id], jasmine.any(Function), [jasmine.any(Object), path.join(plugins_dir, dummyplugin), temp, dummy_id]);
+            expect(proc).toHaveBeenCalled();
         });
 
         describe('with dependencies', function() {
-            it('should uninstall any dependent plugins', function() {
-                shell.mkdir('-p', plugins_dir);
-                shell.cp('-rf', dep_a, plugins_dir);
-                shell.cp('-rf', dep_d, plugins_dir);
-                shell.cp('-rf', dep_c, plugins_dir);
-                install('android', temp, 'A', plugins_dir, {});
-                var spy = spyOn(actions.prototype, 'process').andCallThrough();
-                uninstall('android', temp, 'A', plugins_dir, {});
-                expect(spy.calls.length).toEqual(3);
-            });
-            it('should not uninstall any dependent plugins that are required by other top-level plugins', function() {
-                shell.mkdir('-p', plugins_dir);
-                shell.cp('-rf', dep_a, plugins_dir);
-                shell.cp('-rf', dep_b, plugins_dir);
-                shell.cp('-rf', dep_d, plugins_dir);
-                shell.cp('-rf', dep_c, plugins_dir);
-                shell.cp('-rf', dep_e, plugins_dir);
-                install('android', temp, 'A', plugins_dir, {});
-                install('android', temp, 'B', plugins_dir, {});
-                var spy = spyOn(actions.prototype, 'process').andCallThrough();
-                uninstall('android', temp, 'A', plugins_dir, {});
-                expect(spy.calls.length).toEqual(2);
-            });
+            it('should uninstall "dangling" dependencies');
+            it('should not uninstall any dependencies that are relied on by other plugins'); 
         });
     });
-
+    
     describe('failure', function() {
         it('should throw if platform is unrecognized', function() {
             expect(function() {
                 uninstall('atari', temp, 'SomePlugin', plugins_dir, {});
             }).toThrow('atari not supported.');
         });
-        it('should throw if the plugin was not found', function() {
+        it('should throw if plugin is missing', function() {
+            exists.andReturn(false);
             expect(function() {
-                uninstall('android', temp, 'SomePlugin', plugins_dir, {});
-            }).toThrow('Plugin "SomePlugin" not found. Already uninstalled?');
+                uninstall('android', temp, 'SomePluginThatDoesntExist', plugins_dir, {});
+            }).toThrow('Plugin "SomePluginThatDoesntExist" not found. Already uninstalled?');
         });
     });
 });

http://git-wip-us.apache.org/repos/asf/cordova-plugman/blob/cf8887ea/src/fetch.js
----------------------------------------------------------------------
diff --git a/src/fetch.js b/src/fetch.js
index 476d138..c0ce5f1 100644
--- a/src/fetch.js
+++ b/src/fetch.js
@@ -1,5 +1,6 @@
 var shell   = require('shelljs'),
     fs      = require('fs'),
+    url     = require('url'),
     plugins = require('./util/plugins'),
     xml_helpers = require('./util/xml-helpers'),
     metadata = require('./util/metadata'),
@@ -14,10 +15,11 @@ module.exports = function fetchPlugin(plugin_dir, plugins_dir, options, callback
     options.subdir = options.subdir || '.';
 
     // clone from git repository
-    if(plugin_dir.indexOf('https://') == 0 || plugin_dir.indexOf('git://') == 0) {
+    var uri = url.parse(plugin_dir);
+    if (uri.protocol && uri.protocol != 'file:') {
         if (options.link) {
             var err = new Error('--link is not supported for git URLs');
-            if (callback) callback(err);
+            if (callback) return callback(err);
             else throw err;
         } else {
             var data = {
@@ -37,13 +39,10 @@ module.exports = function fetchPlugin(plugin_dir, plugins_dir, options, callback
             });
         }
     } else {
-        if (plugin_dir.lastIndexOf('file://', 0) === 0) {
-            plugin_dir = plugin_dir.substring('file://'.length);
-        }
 
         // Copy from the local filesystem.
         // First, read the plugin.xml and grab the ID.
-        plugin_dir = path.join(plugin_dir, options.subdir);
+        plugin_dir = path.join(uri.path, options.subdir);
         var xml = xml_helpers.parseElementtreeSync(path.join(plugin_dir, 'plugin.xml'));
         var plugin_id = xml.getroot().attrib.id;
 

http://git-wip-us.apache.org/repos/asf/cordova-plugman/blob/cf8887ea/src/install.js
----------------------------------------------------------------------
diff --git a/src/install.js b/src/install.js
index 37f09c8..3f66fc1 100644
--- a/src/install.js
+++ b/src/install.js
@@ -2,13 +2,34 @@ var path = require('path'),
     fs   = require('fs'),
     et   = require('elementtree'),
     n    = require('ncallbacks'),
-    config_changes = require('./util/config-changes'),
     action_stack = require('./util/action-stack'),
     shell = require('shelljs'),
     semver = require('semver'),
     config_changes = require('./util/config-changes'),
+    xml_helpers = require('./util/xml-helpers'),
     platform_modules = require('./platforms');
 
+/* INSTALL FLOW
+   ------------
+   There are four functions install "flows" through. Here is an attempt at
+   providing a high-level logic flow overview.
+   1. module.exports (installPlugin)
+     a) checks that the platform is supported
+     b) invokes possiblyFetch
+   2. possiblyFetch
+     a) checks that the plugin is fetched. if so, calls runInstall
+     b) if not, invokes plugman.fetch, and when done, calls runInstall
+   3. runInstall
+     a) checks if the plugin is already installed. if so, calls back (done).
+     b) if possible, will check the version of the project and make sure it is compatible with the plugin (checks <engine> tags)
+     c) makes sure that any variables required by the plugin are specified
+     d) if dependencies are listed in the plugin, it will recurse for each dependent plugin and call possiblyFetch (2) on each one. When each dependent plugin is successfully installed, it will then proceed to call handleInstall (4)
+   4. handleInstall
+     a) queues up actions into a queue (asset, source-file, headers, etc)
+     b) processes the queue
+     c) calls back (done)
+*/
+
 // possible options: subdir, cli_variables, www_dir
 module.exports = function installPlugin(platform, project_dir, id, plugins_dir, options, callback) {
     if (!platform_modules[platform]) {
@@ -47,11 +68,10 @@ function possiblyFetch(actions, platform, project_dir, id, plugins_dir, options,
 // possible options: cli_variables, www_dir, is_top_level
 function runInstall(actions, platform, project_dir, plugin_dir, plugins_dir, options, callback) {
     var xml_path     = path.join(plugin_dir, 'plugin.xml')
-      , xml_text     = fs.readFileSync(xml_path, 'utf-8')
-      , plugin_et    = new et.ElementTree(et.XML(xml_text))
+      , plugin_et    = xml_helpers.parseElementtreeSync(xml_path)
       , filtered_variables = {};
     var name         = plugin_et.findall('name').text;
-    var plugin_id    = plugin_et._root.attrib['id'];
+    var plugin_id    = plugin_et.getroot().attrib['id'];
 
     // check if platform has plugin installed already.
     var platform_config = config_changes.get_platform_json(plugins_dir, platform);
@@ -82,14 +102,14 @@ function runInstall(actions, platform, project_dir, plugin_dir, plugins_dir, opt
         var versionScript = shell.exec(versionPath, {silent: true});
         // Only check cordova version if the version script was successful.
         if (versionScript.code === 0) {
+            var current_version = versionScript.output.trim();
             var engines = plugin_et.findall('engines/engine');
             engines.forEach(function(engine){
                 if(engine.attrib["name"].toLowerCase() === "cordova"){
                     var engineVersion = engine.attrib["version"];
                     // clean only versionScript.output since semver.clean strips out 
                     // the gt and lt operators
-                    var current_version = versionScript.output.trim();
-                    if(current_version === 'dev' || semver.satisfies(semver.clean(current_version), engineVersion)){
+                    if(current_version == 'dev' || semver.satisfies(current_version, engineVersion)){
                         // engine ok!
                     } else {
                         var err = new Error('Plugin doesn\'t support this project\'s Cordova version. Project version: ' + current_version + ', failed version requirement: ' + engineVersion);

http://git-wip-us.apache.org/repos/asf/cordova-plugman/blob/cf8887ea/src/prepare.js
----------------------------------------------------------------------
diff --git a/src/prepare.js b/src/prepare.js
index e4ad090..969c73e 100644
--- a/src/prepare.js
+++ b/src/prepare.js
@@ -20,9 +20,9 @@
 var platform_modules = require('./platforms'),
     path            = require('path'),
     config_changes  = require('./util/config-changes'),
+    xml_helpers     = require('./util/xml-helpers'),
     fs              = require('fs'),
     shell           = require('shelljs'),
-    ls              = fs.readdirSync,
     util            = require('util'),
     exec            = require('child_process').exec,
     et              = require('elementtree');
@@ -35,7 +35,6 @@ module.exports = function handlePrepare(project_dir, platform, plugins_dir) {
     // Process:
     // - Do config munging by calling into config-changes module
     // - List all plugins in plugins_dir
-    // TODO: should look into platform json file to determine which plugins are properly installed into the project
     // - Load and parse their plugin.xml files.
     // - Skip those without support for this platform. (No <platform> tags means JS-only!)
     // - Build a list of all their js-modules, including platform-specific js-modules.
@@ -46,7 +45,10 @@ module.exports = function handlePrepare(project_dir, platform, plugins_dir) {
     config_changes.process(plugins_dir, project_dir, platform);
 
     var wwwDir = platform_modules[platform].www_dir(project_dir);
-    var plugins = ls(plugins_dir);
+    // TODO: perhaps this should look at platform json files to determine which plugins to prepare?
+    var plugins = fs.readdirSync(plugins_dir).filter(function(p) {
+        return p != '.svn' && p != 'CVS';
+    });
 
     // This array holds all the metadata for each module and ends up in cordova_plugins.json
     var moduleObjects = [];
@@ -54,7 +56,7 @@ module.exports = function handlePrepare(project_dir, platform, plugins_dir) {
     plugins && plugins.forEach(function(plugin) {
         var pluginDir = path.join(plugins_dir, plugin);
         if(fs.statSync(pluginDir).isDirectory()){
-            var xml = new et.ElementTree(et.XML(fs.readFileSync(path.join(pluginDir, 'plugin.xml'), 'utf-8')));
+            var xml = xml_helpers.parseElementtreeSync(path.join(pluginDir, 'plugin.xml'));
     
             var plugin_id = xml.getroot().attrib.id;
     

http://git-wip-us.apache.org/repos/asf/cordova-plugman/blob/cf8887ea/src/uninstall.js
----------------------------------------------------------------------
diff --git a/src/uninstall.js b/src/uninstall.js
index 4426af8..575012d 100644
--- a/src/uninstall.js
+++ b/src/uninstall.js
@@ -3,6 +3,7 @@ var path = require('path'),
     et   = require('elementtree'),
     shell= require('shelljs'),
     config_changes = require('./util/config-changes'),
+    xml_helpers = require('./util/xml-helpers'),
     action_stack = require('./util/action-stack'),
     n = require('ncallbacks'),
     dependencies = require('./util/dependencies'),
@@ -36,8 +37,7 @@ module.exports = function uninstallPlugin(platform, project_dir, id, plugins_dir
 // possible options: cli_variables, www_dir, is_top_level
 function runUninstall(actions, platform, project_dir, plugin_dir, plugins_dir, options, callback) {
     var xml_path     = path.join(plugin_dir, 'plugin.xml')
-      , xml_text     = fs.readFileSync(xml_path, 'utf-8')
-      , plugin_et    = new et.ElementTree(et.XML(xml_text))
+      , plugin_et    = xml_helpers.parseElementtreeSync(xml_path);
     var name         = plugin_et.findall('name').text;
     var plugin_id    = plugin_et._root.attrib['id'];
     options = options || {};