You are viewing a plain text version of this content. The canonical link for it is here.
Posted to commits@cordova.apache.org by ag...@apache.org on 2014/03/10 21:30:47 UTC
[1/4] git commit: Improve dependencies tests by grouping with
beforeStart() Fix for dependency cycle / throw error.
Repository: cordova-plugman
Updated Branches:
refs/heads/master 7e26c88ab -> a4a51d7ce
Improve dependencies tests by grouping with beforeStart() Fix for dependency cycle / throw error.
Project: http://git-wip-us.apache.org/repos/asf/cordova-plugman/repo
Commit: http://git-wip-us.apache.org/repos/asf/cordova-plugman/commit/f58db0a9
Tree: http://git-wip-us.apache.org/repos/asf/cordova-plugman/tree/f58db0a9
Diff: http://git-wip-us.apache.org/repos/asf/cordova-plugman/diff/f58db0a9
Branch: refs/heads/master
Commit: f58db0a998022b1da39158d4c219a419fa38e532
Parents: ae2ce7a
Author: jbondc <jb...@openmv.com>
Authored: Fri Feb 28 18:44:46 2014 -0500
Committer: Andrew Grieve <ag...@chromium.org>
Committed: Mon Mar 10 16:29:27 2014 -0400
----------------------------------------------------------------------
spec/install.spec.js | 98 +++++++++++++++++++----------------------------
src/install.js | 6 +--
2 files changed, 43 insertions(+), 61 deletions(-)
----------------------------------------------------------------------
http://git-wip-us.apache.org/repos/asf/cordova-plugman/blob/f58db0a9/spec/install.spec.js
----------------------------------------------------------------------
diff --git a/spec/install.spec.js b/spec/install.spec.js
index bff78c2..5a40de3 100644
--- a/spec/install.spec.js
+++ b/spec/install.spec.js
@@ -36,7 +36,7 @@ var install = require('../src/install'),
dummy_id = 'com.phonegap.plugins.dummyplugin';
function installPromise(f) {
- f.then(function() { done = true; }, function(err) { done = err; });
+ f.then(function(res) { done = true; }, function(err) { done = err; });
}
var existsSync = fs.existsSync;
@@ -259,7 +259,7 @@ describe('install', function() {
// <engine name="mega-fun-plugin" version=">=1.0.0" scriptSrc="megaFunVersion" platform="*" />
// <engine name="mega-boring-plugin" version=">=3.0.0" scriptSrc="megaBoringVersion" platform="ios|android" />
- var plugmanVersion = require('../package.json').version;
+ var plugmanVersion = require('../package.json').version;
expect(spy.calls.length).toBe(4);
expect(spy.calls[0].args).toEqual([ '', '>=2.3.0' ]);
@@ -280,15 +280,17 @@ describe('install', function() {
});
describe('with dependencies', function() {
- it('should install any dependent plugins if missing', function() {
+ var emit;
+ beforeEach(function() {
spyOn(fs, 'existsSync').andCallFake( fake['existsSync']['noPlugins'] );
fetchSpy.andCallFake( fake['fetch']['dependencies'] );
emit = spyOn(events, 'emit');
-
exec.andCallFake(function(cmd, cb) {
cb(null, '9.0.0\n');
});
-
+ });
+
+ it('should install any dependent plugins if missing', function() {
runs(function() {
installPromise( install('android', project, plugins['A']) );
});
@@ -304,14 +306,8 @@ describe('install', function() {
]);
});
});
- it('should install any dependent plugins from registry when url is not defined', function() {
- spyOn(fs, 'existsSync').andCallFake( fake['existsSync']['noPlugins'] );
- fetchSpy.andCallFake( fake['fetch']['dependencies'] );
- emit = spyOn(events, 'emit');
- exec.andCallFake(function(cmd, cb) {
- cb(null, '9.0.0\n', '');
- });
+ it('should install any dependent plugins from registry when url is not defined', function() {
// Plugin A depends on C & D
runs(function() {
installPromise( install('android', project, plugins['A']) );
@@ -328,53 +324,39 @@ describe('install', function() {
]);;
});
});
+
+ it('should process all dependent plugins with alternate routes to the same plugin', function() {
+ // Plugin F depends on A, C, D and E
+ runs(function () {
+ installPromise(install('android', project, plugins['F']));
+ });
+ waitsFor(function () { return done; }, 'install promise never resolved', 500);
+ runs(function () {
+ var install = common.spy.getInstall(emit);
+
+ expect(install).toEqual([
+ 'Install start for "C" on android.',
+ 'Install start for "D" on android.',
+ 'Install start for "A" on android.',
+ 'Install start for "D" on android.',
+ 'Install start for "F" on android.'
+ ]);
+ });
+ });
+
+ it('should throw if there is a cyclic dependency', function() {
+ runs(function () {
+ installPromise( install('android', project, plugins['G']) );
+ });
+ waitsFor(function () { return done; }, 'install promise never resolved', 500);
+ runs(function () {
+ var install = common.spy.getInstall(emit);
+
+ expect(done.message).toEqual('Cyclic dependency from G to H');
+ });
+ });
});
- it('should process all dependent plugins with alternate routes to the same plugin', function() {
- spyOn(fs, 'existsSync').andCallFake( fake['existsSync']['noPlugins'] );
- fetchSpy.andCallFake( fake['fetch']['dependencies'] );
- emit = spyOn(events, 'emit');
- exec.andCallFake(function(cmd, cb) {
- cb(null, '9.0.0\n', '');
- });
-
- // Plugin F depends on A, C, D and E
- runs(function () {
- installPromise(install('android', project, plugins['F']));
- });
- waitsFor(function () { return done; }, 'install promise never resolved', 500);
- runs(function () {
- var install = common.spy.getInstall(emit);
-
- expect(install).toEqual([
- 'Install start for "C" on android.',
- 'Install start for "D" on android.',
- 'Install start for "A" on android.',
- 'Install start for "D" on android.',
- 'Install start for "F" on android.'
- ]);
- });
- });
- /*
- it('should throw if there is a cyclic dependency', function() {
- // TODO: group this into BeforeStart() for all dependency tests
- spyOn(fs, 'existsSync').andCallFake( fake['existsSync']['noPlugins'] );
- fetchSpy.andCallFake( fake['fetch']['dependencies'] );
- emit = spyOn(events, 'emit');
- exec.andCallFake(function(cmd, cb) {
- cb(null, '9.0.0\n', '');
- });
-
- runs(function () {
- installPromise(install('android', project, plugins['G']));
- });
- waitsFor(function () { return done; }, 'install promise never resolved', 500);
- runs(function () {
- var install = common.spy.getInstall(emit);
-
- console.log(install);
- expect(done.message).toEqual('Cyclic dependency from G to H');
- });
- });*/
+
});
xdescribe('failure', function() {
http://git-wip-us.apache.org/repos/asf/cordova-plugman/blob/f58db0a9/src/install.js
----------------------------------------------------------------------
diff --git a/src/install.js b/src/install.js
index 1a82f65..3fea119 100644
--- a/src/install.js
+++ b/src/install.js
@@ -285,9 +285,9 @@ var runInstall = module.exports.runInstall = function runInstall(actions, platfo
// Check for dependencies
var dependencies = plugin_et.findall('dependency') || [];
dependencies = dependencies.concat(plugin_et.findall('./platform[@name="'+platform+'"]/dependency'));
- if(dependencies && dependencies.length)
+ if(dependencies && dependencies.length) {
return installDependencies(install, dependencies, options);
-
+ }
return Q(true);
}
).then(
@@ -304,7 +304,7 @@ var runInstall = module.exports.runInstall = function runInstall(actions, platfo
).fail(
function (error) {
events.emit('warn', "Failed to install '"+plugin_id+"':"+ error.stack);
- return Q(false);
+ throw error;
}
);
}
[2/4] Refactoring of install & uninstall tests
Posted by ag...@apache.org.
http://git-wip-us.apache.org/repos/asf/cordova-plugman/blob/ae2ce7ac/src/install.js
----------------------------------------------------------------------
diff --git a/src/install.js b/src/install.js
index 0949068..1a82f65 100644
--- a/src/install.js
+++ b/src/install.js
@@ -9,7 +9,11 @@ var path = require('path'),
Q = require('q'),
platform_modules = require('./platforms'),
os = require('os'),
- isWindows = (os.platform() === 'win32');
+ underscore = require('underscore'),
+ shell = require('shelljs'),
+ events = require('./events'),
+ plugman = require('../plugman'),
+ isWindows = (os.platform().substr(0,3) === 'win');
/* INSTALL FLOW
------------
@@ -36,11 +40,15 @@ var path = require('path'),
// Returns a promise.
module.exports = function installPlugin(platform, project_dir, id, plugins_dir, options) {
options = options || {};
+ options.is_top_level = true;
+ plugins_dir = plugins_dir || path.join(project_dir, 'cordova', 'plugins');
+
if (!platform_modules[platform]) {
return Q.reject(new Error(platform + " not supported."));
}
+
var current_stack = new action_stack();
- options.is_top_level = true;
+
return possiblyFetch(id, plugins_dir, options)
.then(function(plugin_dir) {
return runInstall(current_stack, platform, project_dir, plugin_dir, plugins_dir, options);
@@ -50,26 +58,31 @@ module.exports = function installPlugin(platform, project_dir, id, plugins_dir,
// possible options: subdir, cli_variables, www_dir, git_ref, is_top_level
// Returns a promise.
function possiblyFetch(id, plugins_dir, options) {
- var plugin_dir = path.join(plugins_dir, id);
+
+ // if plugin is a relative path, check if it already exists
+ var plugin_src_dir = path.join(plugins_dir, id);
+ if( isAbsolutePath(id) )
+ plugin_src_dir = id;
// Check that the plugin has already been fetched.
- if (fs.existsSync(plugin_dir)) {
- return Q(plugin_dir);
+ if (fs.existsSync(plugin_src_dir)) {
+ return Q(plugin_src_dir);
}
- var opts = {
- link: false,
- subdir: options.subdir,
- git_ref: options.git_ref,
- client: 'plugman',
- expected_id: options.expected_id,
- searchpath: options.searchpath
- };
- return require('../plugman').raw.fetch(id, plugins_dir, opts);
+
+ var opts = underscore.extend({}, options, {
+ link: false,
+ client: 'plugman'
+ });
+
+ // if plugin doesnt exist, use fetch to get it.
+ return plugman.raw.fetch(id, plugins_dir, opts);
}
function checkEngines(engines) {
+
for(var i = 0; i < engines.length; i++) {
var engine = engines[i];
+
if(semver.satisfies(engine.currentVersion, engine.minVersion) || engine.currentVersion === null){
// engine ok!
}else{
@@ -88,15 +101,13 @@ function cleanVersionOutput(version, name){
out = out.substr(0, rc_index) + '-' + out.substr(rc_index);
}
- // strip out the -dev and put a warning about using the dev branch
+ // put a warning about using the dev branch
if (dev_index > -1) {
// some platform still lists dev branches as just dev, set to null and continue
if(out=="dev"){
out = null;
- }else{
- out = out.substr(0, dev_index-1);
}
- require('../plugman').emit('verbose', name+' has been detected as using a development branch. Attemping to install anyways.');
+ events.emit('verbose', name+' has been detected as using a development branch. Attemping to install anyways.');
}
// add extra period/digits to conform to semver - some version scripts will output
@@ -124,29 +135,36 @@ function callEngineScripts(engines) {
return Q.all(
engines.map(function(engine){
// CB-5192; on Windows scriptSrc doesn't have file extension so we shouldn't check whether the script exists
- if(isWindows || fs.existsSync(engine.scriptSrc)){
+ var scriptPath = engine.scriptSrc ? '"' + engine.scriptSrc + '"' : null;
+
+ if(scriptPath && (isWindows || fs.existsSync(engine.scriptSrc)) ) {
+
+ var d = Q.defer();
if(!isWindows) { // not required on Windows
fs.chmodSync(engine.scriptSrc, '755');
}
- var d = Q.defer();
- child_process.exec('"' + engine.scriptSrc + '"', function(error, stdout, stderr) {
+ child_process.exec(scriptPath, function(error, stdout, stderr) {
if (error) {
- require('../plugman').emit('log', 'Cordova project '+ engine.scriptSrc +' script failed, continuing anyways.');
+ events.emit('warn', engine.name +' version check failed ('+ scriptPath +'), continuing anyways.');
engine.currentVersion = null;
- d.resolve(engine); // Yes, resolve. We're trying to continue despite the error.
} else {
- var version = cleanVersionOutput(stdout, engine.name);
- engine.currentVersion = version;
- d.resolve(engine);
+ engine.currentVersion = cleanVersionOutput(stdout, engine.name);
}
+
+ d.resolve(engine);
});
return d.promise;
- }else if(engine.currentVersion){
- return cleanVersionOutput(engine.currentVersion, engine.name)
- }else{
- require('../plugman').emit('verbose', 'Cordova project '+ engine.scriptSrc +' not detected (lacks a '+ engine.scriptSrc +' script), continuing.');
- return null;
+
+ } else {
+
+ if(engine.currentVersion) {
+ engine.currentVersion = cleanVersionOutput(engine.currentVersion, engine.name)
+ } else {
+ events.emit('warn', engine.name +' version not detected (lacks script '+ scriptPath +' ), continuing.');
+ }
+
+ return Q(engine);
}
})
);
@@ -159,6 +177,7 @@ function getEngines(pluginElement, platform, project_dir, plugin_dir){
var uncheckedEngines = [];
var cordovaEngineIndex, cordovaPlatformEngineIndex, theName, platformIndex, defaultPlatformIndex;
// load in known defaults and update when necessary
+
engines.forEach(function(engine){
theName = engine.attrib["name"];
@@ -210,7 +229,7 @@ function isPluginInstalled(plugins_dir, platform, plugin_id) {
// possible options: cli_variables, www_dir, is_top_level
// Returns a promise.
-var runInstall = module.exports.runInstall = function runInstall(actions, platform, project_dir, plugin_dir, plugins_dir, options, graph) {
+var runInstall = module.exports.runInstall = function runInstall(actions, platform, project_dir, plugin_dir, plugins_dir, options) {
var xml_path = path.join(plugin_dir, 'plugin.xml')
, plugin_et = xml_helpers.parseElementtreeSync(xml_path)
, filtered_variables = {};
@@ -218,146 +237,228 @@ var runInstall = module.exports.runInstall = function runInstall(actions, platfo
var plugin_id = plugin_et.getroot().attrib['id'];
options = options || {};
- graph = graph || new dep_graph();
+ options.graph = options.graph || new dep_graph();
if (isPluginInstalled(plugins_dir, platform, plugin_id)) {
if (options.is_top_level) {
- require('../plugman').emit('results', 'Plugin "' + plugin_id + '" already installed on ' + platform + '.');
+ events.emit('results', 'Plugin "' + plugin_id + '" already installed on ' + platform + '.');
} else {
- require('../plugman').emit('verbose', 'Dependent plugin "' + plugin_id + '" already installed on ' + platform + '.');
+ events.emit('verbose', 'Dependent plugin "' + plugin_id + '" already installed on ' + platform + '.');
}
return Q();
}
- require('../plugman').emit('log', 'Installing ' + plugin_id + ' (' + platform + ')');
-
- var plugin_basename = path.basename(plugin_dir);
+ events.emit('log', 'Installing "' + plugin_id + '" for ' + platform);
var theEngines = getEngines(plugin_et, platform, project_dir, plugin_dir);
+
+ var install = {
+ actions: actions,
+ platform: platform,
+ project_dir: project_dir,
+ plugins_dir: plugins_dir,
+ top_plugin_id: plugin_id,
+ top_plugin_dir: plugin_dir
+ }
+
return callEngineScripts(theEngines)
.then(checkEngines)
- .then(function() {
- // checking preferences, if certain variables are not provided, we should throw.
- var prefs = plugin_et.findall('./preference') || [];
- prefs = prefs.concat(plugin_et.findall('./platform[@name="'+platform+'"]/preference'));
- var missing_vars = [];
- prefs.forEach(function (pref) {
- var key = pref.attrib["name"].toUpperCase();
- options.cli_variables = options.cli_variables || {};
- if (options.cli_variables[key] === undefined)
- missing_vars.push(key)
- else
- filtered_variables[key] = options.cli_variables[key]
- });
- if (missing_vars.length > 0) {
- return Q.reject(new Error('Variable(s) missing: ' + missing_vars.join(", ")));
+ .then(
+ function() {
+ // checking preferences, if certain variables are not provided, we should throw.
+ var prefs = plugin_et.findall('./preference') || [];
+ prefs = prefs.concat(plugin_et.findall('./platform[@name="'+platform+'"]/preference'));
+ var missing_vars = [];
+ prefs.forEach(function (pref) {
+ var key = pref.attrib["name"].toUpperCase();
+ options.cli_variables = options.cli_variables || {};
+ if (options.cli_variables[key] === undefined)
+ missing_vars.push(key)
+ else
+ filtered_variables[key] = options.cli_variables[key]
+ });
+ install.filtered_variables = filtered_variables;
+
+ if (missing_vars.length > 0) {
+ throw new Error('Variable(s) missing: ' + missing_vars.join(", "));
+ }
+
+ // Check for dependencies
+ var dependencies = plugin_et.findall('dependency') || [];
+ dependencies = dependencies.concat(plugin_et.findall('./platform[@name="'+platform+'"]/dependency'));
+ if(dependencies && dependencies.length)
+ return installDependencies(install, dependencies, options);
+
+ return Q(true);
+ }
+ ).then(
+ function(){
+ var install_plugin_dir = path.join(plugins_dir, plugin_id);
+
+ // may need to copy to destination...
+ if ( !fs.existsSync(install_plugin_dir) ) {
+ copyPlugin(plugin_dir, plugins_dir, options.link);
+ }
+
+ return handleInstall(actions, plugin_id, plugin_et, platform, project_dir, plugins_dir, install_plugin_dir, filtered_variables, options.www_dir, options.is_top_level);
+ }
+ ).fail(
+ function (error) {
+ events.emit('warn', "Failed to install '"+plugin_id+"':"+ error.stack);
+ return Q(false);
}
+ );
+}
- // Check for dependencies, (co)recurse to install each one
- var dependencies = plugin_et.findall('dependency') || [];
- dependencies = dependencies.concat(plugin_et.findall('./platform[@name="'+platform+'"]/dependency'));
- if (dependencies && dependencies.length) {
- require('../plugman').emit('verbose', 'Dependencies detected, iterating through them...');
- var dep_plugin_id, dep_subdir, dep_git_ref;
- return dependencies.reduce(function (soFar, dep) {
+function installDependencies(install, dependencies, options) {
+ events.emit('verbose', 'Dependencies detected, iterating through them...');
+
+ var top_plugins = path.join(install.top_plugin_dir, '..');
+
+ // Add directory of top-level plugin to search path
+ options.searchpath = options.searchpath || [];
+ if( top_plugins != install.plugins_dir && options.searchpath.indexOf(top_plugins) == -1 )
+ options.searchpath.push(top_plugins);
+
+ // Search for dependency by Id is:
+ // a) Look for {$top_plugins}/{$depId} directory
+ // b) Scan the top level plugin directory {$top_plugins} for matching id (searchpath)
+ // c) Fetch from registry
+
+ return dependencies.reduce(function(soFar, depXml) {
+ return soFar.then(
+ function() {
+ var dep = {
+ id: depXml.attrib.id,
+ subdir: depXml.attrib.subdir,
+ url: depXml.attrib.url || '',
+ git_ref: depXml.attrib.commit
+ }
+
+ if (dep.subdir) {
+ dep.subdir = path.join(dep.subdir.split('/'));
+ }
// We build the dependency graph only to be able to detect cycles, getChain will throw an error if it detects one
- graph.add(plugin_id, dep.attrib.id);
- graph.getChain(plugin_id);
- return soFar.then(function() {
- dep_plugin_id = dep.attrib.id;
- dep_subdir = dep.attrib.subdir;
- var dep_url = dep.attrib.url;
- dep_git_ref = dep.attrib.commit;
- if (dep_subdir) {
- dep_subdir = path.join.apply(null, dep_subdir.split('/'));
+ options.graph.add(install.top_plugin_id, dep.id);
+ options.graph.getChain(install.top_plugin_id);
+
+ return tryFetchDependency(dep, install)
+ .then(
+ function(url){
+ dep.url = url;
+ return installDependency(dep, install, options);
}
+ );
+ }
+ );
- // Handle relative dependency paths by expanding and resolving them.
- // The easy case of relative paths is to have a URL of '.' and a different subdir.
- // TODO: Implement the hard case of different repo URLs, rather than the special case of
- // same-repo-different-subdir.
- if (dep_url == '.') {
- // Look up the parent plugin's fetch metadata and determine the correct URL.
- var fetchdata = require('./util/metadata').get_fetch_metadata(plugin_dir);
-
- if (!fetchdata || !(fetchdata.source && fetchdata.source.type)) {
- return Q.reject(new Error('No fetch metadata found for ' + plugin_id + '. Cannot install relative dependencies.'));
- }
-
- // Now there are two cases here: local directory, and git URL.
- if (fetchdata.source.type === 'local') {
- dep_url = fetchdata.source.path;
-
- var d = Q.defer();
- child_process.exec('git rev-parse --show-toplevel', { cwd:dep_url }, function(err, stdout, stderr) {
- if (err) {
- if (err.code == 128) {
- return d.reject(new Error('Plugin ' + plugin_id + ' is not in git repository. All plugins must be in a git repository.'));
- } else {
- return d.reject(new Error('Failed to locate git repository for ' + plugin_id + ' plugin.'));
- }
- }
-
- return d.resolve(stdout.trim());
- });
- return d.promise.then(function(git_repo) {
- //Clear out the subdir since the url now contains it
- var url = path.join(git_repo, dep_subdir);
- dep_subdir = "";
- return url;
- });
- } else if (fetchdata.source.type === 'git') {
- return Q(fetchdata.source.url);
- }
- } else {
- return Q(dep_url);
- }
- })
- .then(function(dep_url) {
- var dep_plugin_dir = path.join(plugins_dir, dep_plugin_id);
- if (fs.existsSync(dep_plugin_dir)) {
- require('../plugman').emit('verbose', 'Dependent plugin "' + dep_plugin_id + '" already fetched, using that version.');
- var opts = {
- cli_variables: filtered_variables,
- www_dir: options.www_dir,
- is_top_level: false
- };
- return runInstall(actions, platform, project_dir, dep_plugin_dir, plugins_dir, opts, graph);
+ }, Q(true));
+}
+
+function tryFetchDependency(dep, install) {
+
+ // Handle relative dependency paths by expanding and resolving them.
+ // The easy case of relative paths is to have a URL of '.' and a different subdir.
+ // TODO: Implement the hard case of different repo URLs, rather than the special case of
+ // same-repo-different-subdir.
+ if ( dep.url == '.' ) {
+
+ // Look up the parent plugin's fetch metadata and determine the correct URL.
+ var fetchdata = require('./util/metadata').get_fetch_metadata(install.top_plugin_dir);
+ if (!fetchdata || !(fetchdata.source && fetchdata.source.type)) {
+ throw new Error('No fetch metadata found for plugin ' + install.top_plugin_id + '. Cannot install relative dependency --> ' + dep.id);
+ }
+
+ // Now there are two cases here: local directory, and git URL.
+ var d = Q.defer();
+
+ if (fetchdata.source.type === 'local') {
+
+ dep.url = fetchdata.source.path;
+
+ child_process.exec('git rev-parse --show-toplevel', { cwd:dep.url }, function(err, stdout, stderr) {
+ if (err) {
+ if (err.code == 128) {
+ return d.reject(new Error('Plugin ' + dep.id + ' is not in git repository. All plugins must be in a git repository.'));
} else {
- require('../plugman').emit('verbose', 'Dependent plugin "' + dep_plugin_id + '" not fetched, retrieving then installing.');
- var opts = {
- cli_variables: filtered_variables,
- www_dir: options.www_dir,
- is_top_level: false,
- subdir: dep_subdir,
- git_ref: dep_git_ref,
- expected_id: dep_plugin_id,
- searchpath: options.searchpath
- };
-
- // CB-4770: registry fetching
- if(dep_url === undefined) {
- dep_url = dep_plugin_id;
- }
-
- return possiblyFetch(dep_url, plugins_dir, opts)
- .then(function(plugin_dir) {
- return runInstall(actions, platform, project_dir, plugin_dir, plugins_dir, opts, graph);
- });
+ return d.reject(new Error('Failed to locate git repository for ' + dep.id + ' plugin.'));
}
- });
- }, Q())
- .then(function() {
- return handleInstall(actions, plugin_id, plugin_et, platform, project_dir, plugins_dir, plugin_basename, plugin_dir, filtered_variables, options.www_dir, options.is_top_level);
+ }
+ return d.resolve(stdout.trim());
});
- } else {
- return handleInstall(actions, plugin_id, plugin_et, platform, project_dir, plugins_dir, plugin_basename, plugin_dir, filtered_variables, options.www_dir, options.is_top_level);
+
+ return d.promise.then(function(git_repo) {
+ //Clear out the subdir since the url now contains it
+ var url = path.join(git_repo, dep.subdir);
+ dep.subdir = "";
+ return Q(url);
+ }).fail(function(error){
+//console.log("Failed to resolve url='.': " + error);
+ return Q(dep.url);
+ });
+
+ } else if (fetchdata.source.type === 'git') {
+ return Q(fetchdata.source.url);
}
- });
+ }
+
+ // Test relative to parent folder
+ if( dep.url && isRelativePath(dep.url) ) {
+ var relativePath = path.resolve(install.top_plugin_dir, '../' + dep.url);
+
+ if( fs.existsSync(relativePath) ) {
+ dep.url = relativePath;
+ }
+ }
+
+ // CB-4770: registry fetching
+ if(dep.url === undefined) {
+ dep.url = dep.plugin_id;
+ }
+
+ return Q(dep.url);
}
-function handleInstall(actions, plugin_id, plugin_et, platform, project_dir, plugins_dir, plugin_basename, plugin_dir, filtered_variables, www_dir, is_top_level) {
- require('../plugman').emit('verbose', 'Installing plugin ' + plugin_id);
+function installDependency(dep, install, options) {
+
+ dep.install_dir = path.join(install.plugins_dir, dep.id);
+
+ if ( fs.existsSync(dep.install_dir) ) {
+ events.emit('verbose', 'Dependent plugin "' + dep.id + '" already fetched, using that version.');
+ var opts = underscore.extend({}, options, {
+ cli_variables: install.filtered_variables,
+ is_top_level: false
+ });
+
+ return runInstall(install.actions, install.platform, install.project_dir, dep.install_dir, install.plugins_dir, opts);
+
+ } else {
+ events.emit('verbose', 'Dependent plugin "' + dep.id + '" not fetched, retrieving then installing.');
+
+ var opts = underscore.extend({}, options, {
+ cli_variables: install.filtered_variables,
+ is_top_level: false,
+ subdir: dep.subdir,
+ git_ref: dep.git_ref,
+ expected_id: dep.id
+ });
+
+ var dep_src = dep.url.length ? dep.url : dep.id;
+
+ return possiblyFetch(dep_src, install.plugins_dir, opts)
+ .then(
+ function(plugin_dir) {
+ return runInstall(install.actions, install.platform, install.project_dir, plugin_dir, install.plugins_dir, opts);
+ }
+ );
+ };
+}
+
+function handleInstall(actions, plugin_id, plugin_et, platform, project_dir, plugins_dir, plugin_dir, filtered_variables, www_dir, is_top_level) {
+
+ // @tests - important this event is checked spec/install.spec.js
+ events.emit('verbose', 'Install start for "' + plugin_id + '" on ' + platform + '.');
+
var handler = platform_modules[platform];
www_dir = www_dir || handler.www_dir(project_dir);
@@ -415,20 +516,20 @@ function handleInstall(actions, plugin_id, plugin_et, platform, project_dir, plu
return actions.process(platform, project_dir)
.then(function(err) {
// queue up the plugin so prepare knows what to do.
- config_changes.add_installed_plugin_to_prepare_queue(plugins_dir, plugin_basename, platform, filtered_variables, is_top_level);
+ config_changes.add_installed_plugin_to_prepare_queue(plugins_dir, plugin_id, platform, filtered_variables, is_top_level);
// call prepare after a successful install
- require('./../plugman').prepare(project_dir, platform, plugins_dir, www_dir);
+ plugman.prepare(project_dir, platform, plugins_dir, www_dir);
- require('../plugman').emit('verbose', 'Install complete for ' + plugin_id + ' on ' + platform + '.');
+ events.emit('verbose', 'Install complete for ' + plugin_id + ' on ' + platform + '.');
// WIN!
// Log out plugin INFO element contents in case additional install steps are necessary
var info = plugin_et.findall('./info');
if(info.length) {
- require('../plugman').emit('results', interp_vars(filtered_variables, info[0].text));
+ events.emit('results', interp_vars(filtered_variables, info[0].text));
}
info = (platformTag ? platformTag.findall('./info') : []);
if(info.length) {
- require('../plugman').emit('results', interp_vars(filtered_variables, info[0].text));
+ events.emit('results', interp_vars(filtered_variables, info[0].text));
}
});
}
@@ -440,3 +541,52 @@ function interp_vars(vars, text) {
});
return text;
}
+
+function isAbsolutePath(path) {
+ return path && (path[0] === '/' || path[0] === '\\' || path.indexOf(':\\') > 0 );
+}
+
+function isRelativePath(path) {
+ return !isAbsolutePath();
+}
+
+function readId(plugin_dir) {
+ var xml_path = path.join(plugin_dir, 'plugin.xml');
+ events.emit('verbose', 'Fetch is reading plugin.xml from location "' + xml_path + '"...');
+ var et = xml_helpers.parseElementtreeSync(xml_path);
+
+ return et.getroot().attrib.id;
+}
+
+// Copy or link a plugin from plugin_dir to plugins_dir/plugin_id.
+function copyPlugin(plugin_src_dir, plugins_dir, link) {
+ var plugin_id = readId(plugin_src_dir);
+ var dest = path.join(plugins_dir, plugin_id);
+ shell.rm('-rf', dest);
+
+ if (link) {
+ events.emit('verbose', 'Symlinking from location "' + plugin_src_dir + '" to location "' + dest + '"');
+ fs.symlinkSync(plugin_src_dir, dest, 'dir');
+ } else {
+ shell.mkdir('-p', dest);
+ events.emit('verbose', 'Copying from location "' + plugin_src_dir + '" to location "' + dest + '"');
+ shell.cp('-R', path.join(plugin_src_dir, '*') , dest);
+ }
+
+ return dest;
+}
+
+function isPluginInstalled(plugins_dir, platform, plugin_id) {
+ var platform_config = config_changes.get_platform_json(plugins_dir, platform);
+ for (var installed_plugin_id in platform_config.installed_plugins) {
+ if (installed_plugin_id == plugin_id) {
+ return true;
+ }
+ }
+ for (var installed_plugin_id in platform_config.dependent_plugins) {
+ if (installed_plugin_id == plugin_id) {
+ return true;
+ }
+ }
+ return false;
+}
http://git-wip-us.apache.org/repos/asf/cordova-plugman/blob/ae2ce7ac/src/platforms/android.js
----------------------------------------------------------------------
diff --git a/src/platforms/android.js b/src/platforms/android.js
index 038f92d..e396680 100644
--- a/src/platforms/android.js
+++ b/src/platforms/android.js
@@ -37,9 +37,8 @@ module.exports = {
"source-file":{
install:function(source_el, plugin_dir, project_dir, plugin_id) {
var dest = path.join(source_el.attrib['target-dir'], path.basename(source_el.attrib['src']));
- var target_path = common.resolveTargetPath(project_dir, dest);
- if (fs.existsSync(target_path)) throw new Error('"' + target_path + '" already exists!');
- common.copyFile(plugin_dir, source_el.attrib['src'], project_dir, dest);
+
+ common.copyNewFile(plugin_dir, source_el.attrib['src'], project_dir, dest);
},
uninstall:function(source_el, project_dir, plugin_id) {
var dest = path.join(source_el.attrib['target-dir'], path.basename(source_el.attrib['src']));
http://git-wip-us.apache.org/repos/asf/cordova-plugman/blob/ae2ce7ac/src/platforms/blackberry10.js
----------------------------------------------------------------------
diff --git a/src/platforms/blackberry10.js b/src/platforms/blackberry10.js
index d3709c0..d9e93ad 100644
--- a/src/platforms/blackberry10.js
+++ b/src/platforms/blackberry10.js
@@ -39,9 +39,8 @@ module.exports = {
var target = source_el.attrib['target-dir'] || plugin_id;
TARGETS.forEach(function(arch) {
var dest = path.join("native", arch, "chrome", "plugin", target, path.basename(src));
- var target_path = common.resolveTargetPath(project_dir, dest);
- if (fs.existsSync(target_path)) throw new Error('"' + target_path + '" already exists!');
- common.copyFile(plugin_dir, src, project_dir, dest);
+
+ common.copyNewFile(plugin_dir, src, project_dir, dest);
});
},
uninstall:function(source_el, project_dir, plugin_id) {
http://git-wip-us.apache.org/repos/asf/cordova-plugman/blob/ae2ce7ac/src/platforms/common.js
----------------------------------------------------------------------
diff --git a/src/platforms/common.js b/src/platforms/common.js
index 671e722..8fe1730 100644
--- a/src/platforms/common.js
+++ b/src/platforms/common.js
@@ -1,8 +1,9 @@
var shell = require('shelljs'),
path = require('path'),
- fs = require('fs');
+ fs = require('fs'),
+ common;
-module.exports = {
+module.exports = common = {
// helper for resolving source paths from plugin.xml
resolveSrcPath:function(plugin_dir, relative_path) {
var full_path = path.resolve(plugin_dir, relative_path);
@@ -22,11 +23,19 @@ module.exports = {
// XXX sheljs decides to create a directory when -R|-r is used which sucks. http://goo.gl/nbsjq
if(fs.statSync(src).isDirectory()) {
- shell.cp('-R', src+'/*', dest);
+ shell.cp('-Rf', src+'/*', dest);
} else {
shell.cp('-f', src, dest);
}
},
+ // Same as copy file but throws error if target exists
+ copyNewFile:function(plugin_dir, src, project_dir, dest) {
+ var target_path = common.resolveTargetPath(project_dir, dest);
+ if (fs.existsSync(target_path))
+ throw new Error('"' + target_path + '" already exists!');
+
+ common.copyFile(plugin_dir, src, project_dir, dest);
+ },
// checks if file exists and then deletes. Error if doesn't exist
removeFile:function(project_dir, src) {
var file = module.exports.resolveSrcPath(project_dir, src);
@@ -41,7 +50,7 @@ module.exports = {
var file = path.resolve(project_dir, destFile);
if (!fs.existsSync(file)) return;
- module.exports.removeFileF(file);
+ common.removeFileF(file);
// check if directory is empty
var curDir = path.dirname(file);
@@ -69,7 +78,7 @@ module.exports = {
throw new Error('<asset> tag without required "target" attribute');
}
- module.exports.copyFile(plugin_dir, src, www_dir, target);
+ common.copyFile(plugin_dir, src, www_dir, target);
},
uninstall:function(asset_el, www_dir, plugin_id) {
var target = asset_el.attrib.target || asset_el.attrib.src;
@@ -78,8 +87,8 @@ module.exports = {
throw new Error('<asset> tag without required "target" attribute');
}
- module.exports.removeFile(www_dir, target);
- module.exports.removeFileF(path.resolve(www_dir, 'plugins', plugin_id));
+ common.removeFile(www_dir, target);
+ common.removeFileF(path.resolve(www_dir, 'plugins', plugin_id));
}
}
};
http://git-wip-us.apache.org/repos/asf/cordova-plugman/blob/ae2ce7ac/src/platforms/windows8.js
----------------------------------------------------------------------
diff --git a/src/platforms/windows8.js b/src/platforms/windows8.js
index 287c048..2232192 100644
--- a/src/platforms/windows8.js
+++ b/src/platforms/windows8.js
@@ -47,9 +47,8 @@ module.exports = {
install:function(source_el, plugin_dir, project_dir, plugin_id, project_file) {
var targetDir = source_el.attrib['target-dir'] || '';
var dest = path.join('www', 'plugins', plugin_id, targetDir, path.basename(source_el.attrib['src']));
- var target_path = common.resolveTargetPath(project_dir, dest);
- if (fs.existsSync(target_path)) throw new Error('"' + target_path + '" already exists!');
- common.copyFile(plugin_dir, source_el.attrib['src'], project_dir, dest);
+
+ common.copyNewFile(plugin_dir, source_el.attrib['src'], project_dir, dest);
// add reference to this file to jsproj.
project_file.addSourceFile(dest);
},
http://git-wip-us.apache.org/repos/asf/cordova-plugman/blob/ae2ce7ac/src/platforms/wp7.js
----------------------------------------------------------------------
diff --git a/src/platforms/wp7.js b/src/platforms/wp7.js
index b60697c..ebbd862 100644
--- a/src/platforms/wp7.js
+++ b/src/platforms/wp7.js
@@ -43,9 +43,8 @@ module.exports = {
"source-file":{
install:function(source_el, plugin_dir, project_dir, plugin_id, project_file) {
var dest = path.join('Plugins', plugin_id, source_el.attrib['target-dir'] ? source_el.attrib['target-dir'] : '', path.basename(source_el.attrib['src']));
- var target_path = common.resolveTargetPath(project_dir, dest);
- if (fs.existsSync(target_path)) throw new Error('"' + target_path + '" already exists!');
- common.copyFile(plugin_dir, source_el.attrib['src'], project_dir, dest);
+
+ common.copyNewFile(plugin_dir, source_el.attrib['src'], project_dir, dest);
// add reference to this file to csproj.
project_file.addSourceFile(dest);
},
http://git-wip-us.apache.org/repos/asf/cordova-plugman/blob/ae2ce7ac/src/platforms/wp8.js
----------------------------------------------------------------------
diff --git a/src/platforms/wp8.js b/src/platforms/wp8.js
index 4a74a1f..8eb62c2 100644
--- a/src/platforms/wp8.js
+++ b/src/platforms/wp8.js
@@ -43,9 +43,8 @@ module.exports = {
"source-file":{
install:function(source_el, plugin_dir, project_dir, plugin_id, project_file) {
var dest = path.join('Plugins', plugin_id, source_el.attrib['target-dir'] ? source_el.attrib['target-dir'] : '', path.basename(source_el.attrib['src']));
- var target_path = common.resolveTargetPath(project_dir, dest);
- if (fs.existsSync(target_path)) throw new Error('"' + target_path + '" already exists!');
- common.copyFile(plugin_dir, source_el.attrib['src'], project_dir, dest);
+
+ common.copyNewFile(plugin_dir, source_el.attrib['src'], project_dir, dest);
// add reference to this file to csproj.
project_file.addSourceFile(dest);
},
http://git-wip-us.apache.org/repos/asf/cordova-plugman/blob/ae2ce7ac/src/prepare.js
----------------------------------------------------------------------
diff --git a/src/prepare.js b/src/prepare.js
index 46e9487..3fbb7c0 100644
--- a/src/prepare.js
+++ b/src/prepare.js
@@ -30,6 +30,7 @@ var platform_modules = require('./platforms'),
fs = require('fs'),
shell = require('shelljs'),
util = require('util'),
+ events = require('./events'),
plugman = require('../plugman'),
et = require('elementtree');
@@ -47,7 +48,7 @@ module.exports = function handlePrepare(project_dir, platform, plugins_dir, www_
// - For each js-module (general first, then platform) build up an object storing the path and any clobbers, merges and runs for it.
// - Write this object into www/cordova_plugins.json.
// - Cordova.js contains code to load them at runtime from that file.
- plugman.emit('verbose', 'Preparing ' + platform + ' project');
+ events.emit('verbose', 'Preparing ' + platform + ' project');
var platform_json = config_changes.get_platform_json(plugins_dir, platform);
var wwwDir = www_dir || platform_modules[platform].www_dir(project_dir);
@@ -60,17 +61,15 @@ module.exports = function handlePrepare(project_dir, platform, plugins_dir, www_
plugins_to_uninstall.forEach(function(plug) {
var id = plug.id;
var plugin_modules = path.join(plugins_www, id);
- if (!fs.existsSync(plugin_modules)) {
- plugman.emit('verbose', 'There is no directory "'+plugin_modules+'"');
- return;
+ if (fs.existsSync(plugin_modules)) {
+ events.emit('verbose', 'Removing plugins directory from www "'+plugin_modules+'"');
+ shell.rm('-rf', plugin_modules);
}
- plugman.emit('verbose', 'Removing plugins directory from www "'+plugin_modules+'"');
- shell.rm('-rf', plugin_modules);
});
}
}
- plugman.emit('verbose', 'Processing configuration changes for plugins.');
+ events.emit('verbose', 'Processing configuration changes for plugins.');
config_changes.process(plugins_dir, project_dir, platform);
// for windows phone plaform we need to add all www resources to the .csproj file
@@ -123,7 +122,7 @@ module.exports = function handlePrepare(project_dir, platform, plugins_dir, www_
var plugins = Object.keys(platform_json.installed_plugins).concat(Object.keys(platform_json.dependent_plugins));
var moduleObjects = [];
var pluginMetadata = {};
- plugman.emit('verbose', 'Iterating over installed plugins:', plugins);
+ events.emit('verbose', 'Iterating over installed plugins:', plugins);
plugins && plugins.forEach(function(plugin) {
var pluginDir = path.join(plugins_dir, plugin),
@@ -155,7 +154,7 @@ module.exports = function handlePrepare(project_dir, platform, plugins_dir, www_
// Copy www assets described in <asset> tags.
assets = assets || [];
- assets.forEach(function(asset) {
+ assets.forEach(function(asset) {
common.asset.install(asset, pluginDir, wwwDir);
});
@@ -223,7 +222,7 @@ module.exports = function handlePrepare(project_dir, platform, plugins_dir, www_
final_contents += '// BOTTOM OF METADATA\n';
final_contents += '});'; // Close cordova.define.
- plugman.emit('verbose', 'Writing out cordova_plugins.js...');
+ events.emit('verbose', 'Writing out cordova_plugins.js...');
fs.writeFileSync(path.join(wwwDir, 'cordova_plugins.js'), final_contents, 'utf-8');
if(platform == 'wp7' || platform == 'wp8' || platform == "windows8") {
http://git-wip-us.apache.org/repos/asf/cordova-plugman/blob/ae2ce7ac/src/uninstall.js
----------------------------------------------------------------------
diff --git a/src/uninstall.js b/src/uninstall.js
index 0d55670..18d7a18 100644
--- a/src/uninstall.js
+++ b/src/uninstall.js
@@ -1,3 +1,4 @@
+
var path = require('path'),
fs = require('fs'),
et = require('elementtree'),
@@ -9,85 +10,124 @@ var path = require('path'),
underscore = require('underscore'),
Q = require('q'),
plugins = require('./util/plugins'),
- platform_modules = require('./platforms');
+ underscore = require('underscore'),
+ events = require('./events'),
+ platform_modules = require('./platforms'),
+ plugman = require('../plugman');
// possible options: cli_variables, www_dir
// Returns a promise.
module.exports = function(platform, project_dir, id, plugins_dir, options) {
+ options = options || {};
+ options.is_top_level = true;
+ plugins_dir = plugins_dir || path.join(project_dir, 'cordova', 'plugins');
+
+ // Allow path to file to grab an ID
+ var xml_path = path.join(id, 'plugin.xml');
+ if ( fs.existsSync(xml_path) ) {
+ var plugin_et = xml_helpers.parseElementtreeSync(xml_path),
+ id = plugin_et._root.attrib['id'];
+ }
+
return module.exports.uninstallPlatform(platform, project_dir, id, plugins_dir, options)
- .then(function(uninstalled) {
- return module.exports.uninstallPlugin(id, plugins_dir);
+ .then(function() {
+ return module.exports.uninstallPlugin(id, plugins_dir, options);
});
}
// Returns a promise.
module.exports.uninstallPlatform = function(platform, project_dir, id, plugins_dir, options) {
options = options || {};
+ options.is_top_level = true;
+ plugins_dir = plugins_dir || path.join(project_dir, 'cordova', 'plugins');
+
if (!platform_modules[platform]) {
return Q.reject(new Error(platform + " not supported."));
}
var plugin_dir = path.join(plugins_dir, id);
-
if (!fs.existsSync(plugin_dir)) {
return Q.reject(new Error('Plugin "' + id + '" not found. Already uninstalled?'));
}
var current_stack = new action_stack();
- options.is_top_level = true;
- return runUninstall(current_stack, platform, project_dir, plugin_dir, plugins_dir, options);
+ return runUninstallPlatform(current_stack, platform, project_dir, plugin_dir, plugins_dir, options);
};
// Returns a promise.
-module.exports.uninstallPlugin = function(id, plugins_dir) {
+module.exports.uninstallPlugin = function(id, plugins_dir, options) {
+ options = options || {};
+
var plugin_dir = path.join(plugins_dir, id);
+
// If already removed, skip.
if (!fs.existsSync(plugin_dir)) {
return Q();
}
- var xml_path = path.join(plugin_dir, 'plugin.xml')
- , plugin_et = xml_helpers.parseElementtreeSync(xml_path);
- require('../plugman').emit('log', 'Deleting plugin ' + id);
+ var xml_path = path.join(plugin_dir, 'plugin.xml')
+ , plugin_et = xml_helpers.parseElementtreeSync(xml_path);
+
+ events.emit('log', 'Deleting "'+ id +'"');
var doDelete = function(id) {
var plugin_dir = path.join(plugins_dir, id);
- if (!fs.existsSync(plugin_dir)) return;
+ if ( !fs.existsSync(plugin_dir) )
+ return;
+
shell.rm('-rf', plugin_dir);
- require('../plugman').emit('verbose', id + ' deleted.');
+ events.emit('verbose', '"'+ id +'" deleted.');
};
// We've now lost the metadata for the plugins that have been uninstalled, so we can't use that info.
// Instead, we list all dependencies of the target plugin, and check the remaining metadata to see if
// anything depends on them, or if they're listed as top-level.
// If neither, they can be deleted.
+ var top_plugin_id = id;
var toDelete = plugin_et.findall('dependency');
toDelete = toDelete && toDelete.length ? toDelete.map(function(p) { return p.attrib.id; }) : [];
- toDelete.push(id);
+ toDelete.push(top_plugin_id);
// Okay, now we check if any of these are depended on, or top-level.
// Find the installed platforms by whether they have a metadata file.
- var platforms = Object.keys(platform_modules).filter(function(plat) {
- return fs.existsSync(path.join(plugins_dir, plat + '.json'));
+ var platforms = Object.keys(platform_modules).filter(function(platform) {
+ return fs.existsSync(path.join(plugins_dir, platform + '.json'));
});
- var found = [];
- platforms.forEach(function(plat) {
- var tlps = dependencies.generate_dependency_info(plugins_dir, plat).top_level_plugins;
+ var dependList = {};
+ platforms.forEach(function(platform) {
+ var depsInfo = dependencies.generate_dependency_info(plugins_dir, platform);
+ var tlps = depsInfo.top_level_plugins,
+ deps, i;
+
toDelete.forEach(function(plugin) {
- if (tlps.indexOf(plugin) >= 0 || dependencies.dependents(plugin, plugins_dir, plat).length) {
- found.push(plugin);
+ deps = dependencies.dependents(plugin, depsInfo);
+ var i = deps.indexOf(top_plugin_id);
+ if(i >= 0)
+ deps.splice(i, 1); // remove current/top-level plugin as blocking uninstall
+
+ if(deps.length) {
+ dependList[plugin] = deps.join(', ');
}
});
});
- var danglers = underscore.difference(toDelete, found);
- if (danglers && danglers.length) {
- require('../plugman').emit('log', 'Found ' + danglers.length + ' removable plugins. Deleting them.');
- danglers.forEach(doDelete);
- } else {
- require('../plugman').emit('log', 'No dangling plugins to remove.');
+ var i, plugin_id, msg;
+ for(i in toDelete) {
+ plugin_id = toDelete[i];
+
+ if( dependList[plugin_id] ) {
+ msg = '"' + plugin_id + '" is required by ('+ dependList[plugin_id] + ')';
+ if(options.force) {
+ events.emit('log', msg +' but forcing removal.');
+ } else {
+ events.emit('warn', msg +' and cannot be removed (hint: use -f or --force)');
+ continue;
+ }
+ }
+
+ doDelete(plugin_id);
}
return Q();
@@ -95,36 +135,47 @@ module.exports.uninstallPlugin = function(id, plugins_dir) {
// possible options: cli_variables, www_dir, is_top_level
// Returns a promise
-function runUninstall(actions, platform, project_dir, plugin_dir, plugins_dir, options) {
- var xml_path = path.join(plugin_dir, 'plugin.xml')
- , plugin_et = xml_helpers.parseElementtreeSync(xml_path);
- var plugin_id = plugin_et._root.attrib['id'];
+function runUninstallPlatform(actions, platform, project_dir, plugin_dir, plugins_dir, options) {
options = options || {};
+ var xml_path = path.join(plugin_dir, 'plugin.xml');
+ var plugin_et = xml_helpers.parseElementtreeSync(xml_path);
+ var plugin_id = plugin_et._root.attrib['id'];
+
+ // deps info can be passed recusively
+ var depsInfo = options.depsInfo || dependencies.generate_dependency_info(plugins_dir, platform, 'remove');
+
// Check that this plugin has no dependents.
- var dependents = dependencies.dependents(plugin_id, plugins_dir, platform);
+ var dependents = dependencies.dependents(plugin_id, depsInfo, platform);
+
if(options.is_top_level && dependents && dependents.length > 0) {
- require('../plugman').emit('verbose', 'Other top-level plugins (' + dependents.join(', ') + ') depend on ' + plugin_id + ', skipping uninstallation.');
- return Q();
+ var msg = "The plugin '"+ plugin_id +"' is required by (" + dependents.join(', ') + ")";
+ if(options.force) {
+ events.emit("info", msg + " but forcing removal");
+ } else {
+ return Q.reject( new Error(msg + ", skipping uninstallation.") );
+ }
}
// Check how many dangling dependencies this plugin has.
- var dependency_info = dependencies.generate_dependency_info(plugins_dir, platform);
- var deps = dependency_info.graph.getChain(plugin_id);
- var danglers = dependencies.danglers(plugin_id, plugins_dir, platform);
+ var deps = depsInfo.graph.getChain(plugin_id);
+ var danglers = dependencies.danglers(plugin_id, depsInfo, platform);
var promise;
if (deps && deps.length && danglers && danglers.length) {
- require('../plugman').emit('log', 'Uninstalling ' + danglers.length + ' dangling dependent plugins.');
+
+ // @tests - important this event is checked spec/uninstall.spec.js
+ events.emit('log', 'Uninstalling ' + danglers.length + ' dependent plugins.');
promise = Q.all(
danglers.map(function(dangler) {
- var dependent_path = path.join(plugins_dir, dangler);
- var opts = {
- www_dir: options.www_dir,
- cli_variables: options.cli_variables,
- is_top_level: dependency_info.top_level_plugins.indexOf(dangler) > -1
- };
- return runUninstall(actions, platform, project_dir, dependent_path, plugins_dir, opts);
+ var dependent_path = dependencies.resolvePath(dangler, plugins_dir);
+
+ var opts = underscore.extend({}, options, {
+ is_top_level: depsInfo.top_level_plugins.indexOf(dangler) > -1,
+ depsInfo: depsInfo
+ });
+
+ return runUninstallPlatform(actions, platform, project_dir, dependent_path, plugins_dir, opts);
})
);
} else {
@@ -142,7 +193,7 @@ function handleUninstall(actions, platform, plugin_id, plugin_et, project_dir, w
var handler = platform_modules[platform];
var platformTag = plugin_et.find('./platform[@name="'+platform+'"]');
www_dir = www_dir || handler.www_dir(project_dir);
- require('../plugman').emit('log', 'Uninstalling ' + plugin_id + ' from ' + platform);
+ events.emit('log', 'Uninstalling ' + plugin_id + ' from ' + platform);
var assets = plugin_et.findall('./asset');
if (platformTag) {
@@ -201,11 +252,10 @@ function handleUninstall(actions, platform, plugin_id, plugin_et, project_dir, w
return actions.process(platform, project_dir)
.then(function() {
// WIN!
- require('../plugman').emit('verbose', plugin_id + ' uninstalled from ' + platform + '.');
+ events.emit('verbose', plugin_id + ' uninstalled from ' + platform + '.');
// queue up the plugin so prepare can remove the config changes
- config_changes.add_uninstalled_plugin_to_prepare_queue(plugins_dir, path.basename(plugin_dir), platform, is_top_level);
+ config_changes.add_uninstalled_plugin_to_prepare_queue(plugins_dir, plugin_id, platform, is_top_level);
// call prepare after a successful uninstall
- require('./../plugman').prepare(project_dir, platform, plugins_dir, www_dir);
+ plugman.prepare(project_dir, platform, plugins_dir, www_dir);
});
}
-
http://git-wip-us.apache.org/repos/asf/cordova-plugman/blob/ae2ce7ac/src/util/default-engines.js
----------------------------------------------------------------------
diff --git a/src/util/default-engines.js b/src/util/default-engines.js
index 2ca8b13..926c206 100644
--- a/src/util/default-engines.js
+++ b/src/util/default-engines.js
@@ -3,10 +3,9 @@ var path = require('path');
module.exports = function(project_dir){
return {
'cordova':
- { 'platform':'*', 'scriptSrc': path.join(project_dir,'cordova','version') },
- // no location needed for plugman as this should be the calling process
+ { 'platform':'*', 'scriptSrc': path.join(project_dir,'cordova','version') },
'cordova-plugman':
- { 'platform':'*', 'currentVersion': process.version },
+ { 'platform':'*', 'currentVersion': require('../../package.json').version },
'cordova-android':
{ 'platform':'android', 'scriptSrc': path.join(project_dir,'cordova','version') },
'cordova-ios':
http://git-wip-us.apache.org/repos/asf/cordova-plugman/blob/ae2ce7ac/src/util/dependencies.js
----------------------------------------------------------------------
diff --git a/src/util/dependencies.js b/src/util/dependencies.js
index acd7816..8abbc5b 100644
--- a/src/util/dependencies.js
+++ b/src/util/dependencies.js
@@ -4,33 +4,43 @@ var dep_graph = require('dep-graph'),
plugman = require('../../plugman'),
config_changes = require('./config-changes'),
underscore = require('underscore'),
- xml_helpers = require('./xml-helpers');
+ xml_helpers = require('./xml-helpers'),
+ package;
+
+module.exports = package = {
+
+ resolvePath: function(plugin_id, plugins_dir)
+ {
+ return path.join(plugins_dir, plugin_id);
+ },
+
+ resolveConfig: function(plugin_id, plugins_dir)
+ {
+ return path.join(plugins_dir, plugin_id, 'plugin.xml');
+ },
-module.exports = {
generate_dependency_info:function(plugins_dir, platform) {
var json = config_changes.get_platform_json(plugins_dir, platform);
+
+ // TODO: store whole dependency tree in plugins/[platform].json
+ // in case plugins are forcefully removed...
var tlps = [];
var graph = new dep_graph();
- Object.keys(json.installed_plugins).forEach(function(tlp) {
- tlps.push(tlp);
- var xml = xml_helpers.parseElementtreeSync(path.join(plugins_dir, tlp, 'plugin.xml'));
+ Object.keys(json.installed_plugins).forEach(function(plugin_id) {
+ tlps.push(plugin_id);
+
+ var xml = xml_helpers.parseElementtreeSync( package.resolveConfig(plugin_id, plugins_dir) );
var deps = xml.findall('dependency');
+
deps && deps.forEach(function(dep) {
- var id = dep.attrib.id;
- graph.add(tlp, id);
+ graph.add(plugin_id, dep.attrib.id);
});
});
- Object.keys(json.dependent_plugins).forEach(function(plug) {
- var pluginXML = path.join(plugins_dir, plug, 'plugin.xml');
- if (!fs.existsSync(pluginXML)) {
- plugman.emit('verbose', 'Could not find "' + pluginXML + '"');
- return;
- }
- var xml = xml_helpers.parseElementtreeSync(pluginXML);
+ Object.keys(json.dependent_plugins).forEach(function(plugin_id) {
+ var xml = xml_helpers.parseElementtreeSync( package.resolveConfig(plugin_id, plugins_dir) );
var deps = xml.findall('dependency');
deps && deps.forEach(function(dep) {
- var id = dep.attrib.id;
- graph.add(plug, id);
+ graph.add(plugin_id, dep.attrib.id);
});
});
@@ -42,10 +52,13 @@ module.exports = {
// Returns a list of top-level plugins which are (transitively) dependent on the given plugin.
dependents: function(plugin_id, plugins_dir, platform) {
- var dependency_info = module.exports.generate_dependency_info(plugins_dir, platform);
- var graph = dependency_info.graph;
+ if(typeof plugins_dir == 'object')
+ var depsInfo = plugins_dir;
+ else
+ var depsInfo = package.generate_dependency_info(plugins_dir, platform);
- var tlps = dependency_info.top_level_plugins;
+ var graph = depsInfo.graph;
+ var tlps = depsInfo.top_level_plugins;
var dependents = tlps.filter(function(tlp) {
return tlp != plugin_id && graph.getChain(tlp).indexOf(plugin_id) >= 0;
});
@@ -56,11 +69,15 @@ module.exports = {
// Returns a list of plugins which the given plugin depends on, for which it is the only dependent.
// In other words, if the given plugin were deleted, these dangling dependencies should be deleted too.
danglers: function(plugin_id, plugins_dir, platform) {
- var dependency_info = module.exports.generate_dependency_info(plugins_dir, platform);
- var graph = dependency_info.graph;
+ if(typeof plugins_dir == 'object')
+ var depsInfo = plugins_dir;
+ else
+ var depsInfo = package.generate_dependency_info(plugins_dir, platform);
+
+ var graph = depsInfo.graph;
var dependencies = graph.getChain(plugin_id);
- var tlps = dependency_info.top_level_plugins;
+ var tlps = depsInfo.top_level_plugins;
var diff_arr = [];
tlps.forEach(function(tlp) {
if (tlp != plugin_id) {
@@ -76,4 +93,4 @@ module.exports = {
danglers = danglers && danglers.filter(function(x) { return tlps.indexOf(x) < 0; });
return danglers;
}
-};
+};
\ No newline at end of file
[4/4] git commit: Fix to never remove top-level plugins that are
dependencies + tests.
Posted by ag...@apache.org.
Fix to never remove top-level plugins that are dependencies + tests.
github: close #58
Project: http://git-wip-us.apache.org/repos/asf/cordova-plugman/repo
Commit: http://git-wip-us.apache.org/repos/asf/cordova-plugman/commit/a4a51d7c
Tree: http://git-wip-us.apache.org/repos/asf/cordova-plugman/tree/a4a51d7c
Diff: http://git-wip-us.apache.org/repos/asf/cordova-plugman/diff/a4a51d7c
Branch: refs/heads/master
Commit: a4a51d7ce8200594bf90ecc524afbfc899c4e361
Parents: f58db0a
Author: jbondc <jb...@openmv.com>
Authored: Sun Mar 9 18:31:01 2014 -0400
Committer: Andrew Grieve <ag...@chromium.org>
Committed: Mon Mar 10 16:30:30 2014 -0400
----------------------------------------------------------------------
spec/common.js | 41 ++++++++++++++++-------
spec/install.spec.js | 9 ++---
spec/uninstall.spec.js | 80 ++++++++++++++++++++++++++++++++++++---------
src/uninstall.js | 49 ++++++++++++++++++++-------
4 files changed, 135 insertions(+), 44 deletions(-)
----------------------------------------------------------------------
http://git-wip-us.apache.org/repos/asf/cordova-plugman/blob/a4a51d7c/spec/common.js
----------------------------------------------------------------------
diff --git a/spec/common.js b/spec/common.js
index 2991806..6a87e23 100644
--- a/spec/common.js
+++ b/spec/common.js
@@ -29,15 +29,34 @@ if(opt.debug) {
plugman.on('log', console.log);
}
-module.exports = {
- spy: {
- getInstall: function(emitSpy){
- var install = [], i;
- for(i in emitSpy.argsForCall) {
- if(emitSpy.argsForCall[i][1].substr(0, 13) === 'Install start')
- install.push(emitSpy.argsForCall[i][1]);
- }
- return install;
- }
- }
+module.exports = common = {
+ spy: {
+ getInstall: function(emitSpy){
+ return common.spy.startsWith(emitSpy, 'Install start');
+ },
+
+ getDeleted: function(emitSpy){
+ return common.spy.startsWith(emitSpy, 'Deleted');
+ },
+
+ startsWith: function(emitSpy, string)
+ {
+ var match = [], i;
+ for(i in emitSpy.argsForCall) {
+ if(emitSpy.argsForCall[i][1].substr(0, string.length) === string)
+ match.push(emitSpy.argsForCall[i][1]);
+ }
+ return match;
+ },
+
+ contains: function(emitSpy, string)
+ {
+ var match = [], i;
+ for(i in emitSpy.argsForCall) {
+ if(emitSpy.argsForCall[i][1].indexOf(string) >= 0)
+ match.push(emitSpy.argsForCall[i][1]);
+ }
+ return match;
+ }
+ }
};
http://git-wip-us.apache.org/repos/asf/cordova-plugman/blob/a4a51d7c/spec/install.spec.js
----------------------------------------------------------------------
diff --git a/spec/install.spec.js b/spec/install.spec.js
index 5a40de3..f552c49 100644
--- a/spec/install.spec.js
+++ b/spec/install.spec.js
@@ -411,17 +411,14 @@ describe('end', function() {
it('end', function() {
done = false;
- var finish = function(err){
+
+ promise.fin(function(err){
if(err)
events.emit('error', err);
shell.rm('-rf', project);
done = true;
- }
-
- promise.then(
-
- ).fin(finish);
+ });
waitsFor(function() { return done; }, 'promise never resolved', 500);
});
http://git-wip-us.apache.org/repos/asf/cordova-plugman/blob/a4a51d7c/spec/uninstall.spec.js
----------------------------------------------------------------------
diff --git a/spec/uninstall.spec.js b/spec/uninstall.spec.js
index 7ae8a14..bf9aa7f 100644
--- a/spec/uninstall.spec.js
+++ b/spec/uninstall.spec.js
@@ -13,9 +13,12 @@ var uninstall = require('../src/uninstall'),
done = false,
srcProject = path.join(spec, 'projects', 'android_uninstall'),
project = path.join(spec, 'projects', 'android_uninstall.test'),
+ project2 = path.join(spec, 'projects', 'android_uninstall.test2'),
plugins_dir = path.join(spec, 'plugins'),
plugins_install_dir = path.join(project, 'cordova', 'plugins'),
+ plugins_install_dir2 = path.join(project2, 'cordova', 'plugins'),
+
plugins = {
'DummyPlugin' : path.join(plugins_dir, 'DummyPlugin'),
'A' : path.join(plugins_dir, 'dependencies', 'A'),
@@ -32,7 +35,9 @@ describe('start', function() {
it('start', function() {
shell.rm('-rf', project);
+ shell.rm('-rf', project2);
shell.cp('-R', path.join(srcProject, '*'), project);
+ shell.cp('-R', path.join(srcProject, '*'), project2);
done = false;
promise = Q()
@@ -41,6 +46,10 @@ describe('start', function() {
).then(
function(){ return install('android', project, plugins['A']) }
).then(
+ function(){ return install('android', project2, plugins['C']) }
+ ).then(
+ function(){ return install('android', project2, plugins['A']) }
+ ).then(
function(){ done = true; }
);
waitsFor(function() { return done; }, 'promise never resolved', 500);
@@ -134,28 +143,67 @@ describe('uninstallPlatform', function() {
});
describe('uninstallPlugin', function() {
- var rm, fsWrite, rmstack = [];
+ var rm, fsWrite, rmstack = [], emit;
beforeEach(function() {
fsWrite = spyOn(fs, 'writeFileSync').andReturn(true);
rm = spyOn(shell, 'rm').andCallFake(function(f,p) { rmstack.push(p); return true});
rmstack = [];
+ emit = spyOn(events, 'emit');
done = false;
});
describe('with dependencies', function() {
- it('should delete all dangling plugins', function() {
-
+ it('should delete all dependent plugins', function() {
runs(function() {
uninstallPromise( uninstall.uninstallPlugin('A', plugins_install_dir) );
});
waitsFor(function() { return done; }, 'promise never resolved', 200);
runs(function() {
- expect(done).toEqual(true);
- expect(rmstack[0]).toBe( path.join(plugins_install_dir, 'C') );
- expect(rmstack[1]).toBe( path.join(plugins_install_dir, 'D') );
- expect(rmstack[2]).toBe( path.join(plugins_install_dir, 'A') );
- expect(rm.calls.length).toBe(3);
+ var del = common.spy.getDeleted(emit);
+
+ expect(del).toEqual([
+ 'Deleted "C"',
+ 'Deleted "D"',
+ 'Deleted "A"'
+ ]);
+ });
+ });
+
+ it("should fail if plugin is a required dependency", function() {
+ runs(function() {
+ uninstallPromise( uninstall.uninstallPlugin('C', plugins_install_dir) );
+ });
+ waitsFor(function() { return done; }, 'promise never resolved', 200);
+ runs(function() {
+ expect(done.message).toBe('"C" is required by (A) and cannot be removed (hint: use -f or --force)');
+ });
+ });
+
+ it("allow forcefully removing a plugin", function() {
+ runs(function() {
+ uninstallPromise( uninstall.uninstallPlugin('C', plugins_install_dir, {force: true}) );
+ });
+ waitsFor(function() { return done; }, 'promise never resolved', 200);
+ runs(function() {
+ expect(done).toBe(true);
+ var del = common.spy.getDeleted(emit);
+ expect(del).toEqual(['Deleted "C"']);
+ });
+ });
+
+ it("never remove top level plugins if they are a dependency", function() {
+ runs(function() {
+ uninstallPromise( uninstall.uninstallPlugin('A', plugins_install_dir2) );
+ });
+ waitsFor(function() { return done; }, 'promise never resolved', 200);
+ runs(function() {
+ var del = common.spy.getDeleted(emit);
+
+ expect(del).toEqual([
+ 'Deleted "D"',
+ 'Deleted "A"'
+ ]);
});
});
});
@@ -208,13 +256,6 @@ describe('end', function() {
it('end', function() {
done = false;
- var finish = function(err){
- if(err)
- plugman.emit('error', err);
-
- shell.rm('-rf', project);
- done = true;
- }
promise.then(
function(){
@@ -234,7 +275,14 @@ describe('end', function() {
// dependencies on C,D ... should this only work with --recursive? prompt user..?
return uninstall('android', project, plugins['A'])
}
- ).fin(finish);
+ ).fin(function(err){
+ if(err)
+ plugman.emit('error', err);
+
+ shell.rm('-rf', project);
+ shell.rm('-rf', project2);
+ done = true;
+ });
waitsFor(function() { return done; }, 'promise never resolved', 500);
});
http://git-wip-us.apache.org/repos/asf/cordova-plugman/blob/a4a51d7c/src/uninstall.js
----------------------------------------------------------------------
diff --git a/src/uninstall.js b/src/uninstall.js
index 18d7a18..98c57ad 100644
--- a/src/uninstall.js
+++ b/src/uninstall.js
@@ -61,23 +61,27 @@ module.exports.uninstallPlugin = function(id, plugins_dir, options) {
var plugin_dir = path.join(plugins_dir, id);
+ // @tests - important this event is checked spec/uninstall.spec.js
+ events.emit('log', 'Removing "'+ id +'"');
+
// If already removed, skip.
- if (!fs.existsSync(plugin_dir)) {
+ if ( !fs.existsSync(plugin_dir) ) {
+ events.emit('verbose', 'Plugin "'+ id +'" already removed ('+ plugin_dir +')');
return Q();
}
var xml_path = path.join(plugin_dir, 'plugin.xml')
, plugin_et = xml_helpers.parseElementtreeSync(xml_path);
- events.emit('log', 'Deleting "'+ id +'"');
-
var doDelete = function(id) {
var plugin_dir = path.join(plugins_dir, id);
- if ( !fs.existsSync(plugin_dir) )
- return;
+ if ( !fs.existsSync(plugin_dir) ) {
+ events.emit('verbose', 'Plugin "'+ id +'" already removed ('+ plugin_dir +')');
+ return Q();
+ }
shell.rm('-rf', plugin_dir);
- events.emit('verbose', '"'+ id +'" deleted.');
+ events.emit('verbose', 'Deleted "'+ id +'"');
};
// We've now lost the metadata for the plugins that have been uninstalled, so we can't use that info.
@@ -85,7 +89,11 @@ module.exports.uninstallPlugin = function(id, plugins_dir, options) {
// anything depends on them, or if they're listed as top-level.
// If neither, they can be deleted.
var top_plugin_id = id;
- var toDelete = plugin_et.findall('dependency');
+
+ // Recursively remove plugins which were installed as dependents (that are not top-level)
+ // optional?
+ var recursive = true;
+ var toDelete = recursive ? plugin_et.findall('dependency') : [];
toDelete = toDelete && toDelete.length ? toDelete.map(function(p) { return p.attrib.id; }) : [];
toDelete.push(top_plugin_id);
@@ -95,14 +103,26 @@ module.exports.uninstallPlugin = function(id, plugins_dir, options) {
return fs.existsSync(path.join(plugins_dir, platform + '.json'));
});
+ // Can have missing plugins on some platforms when not supported..
var dependList = {};
- platforms.forEach(function(platform) {
+ platforms.forEach(function(platform) {
var depsInfo = dependencies.generate_dependency_info(plugins_dir, platform);
var tlps = depsInfo.top_level_plugins,
deps, i;
+ // Top-level deps must always be explicitely asked to remove by user
+ tlps.forEach(function(plugin_id){
+ if(top_plugin_id == plugin_id)
+ return;
+
+ var i = toDelete.indexOf(plugin_id);
+ if(i >= 0)
+ toDelete.splice(i, 1);
+ });
+
toDelete.forEach(function(plugin) {
deps = dependencies.dependents(plugin, depsInfo);
+
var i = deps.indexOf(top_plugin_id);
if(i >= 0)
deps.splice(i, 1); // remove current/top-level plugin as blocking uninstall
@@ -122,8 +142,15 @@ module.exports.uninstallPlugin = function(id, plugins_dir, options) {
if(options.force) {
events.emit('log', msg +' but forcing removal.');
} else {
- events.emit('warn', msg +' and cannot be removed (hint: use -f or --force)');
- continue;
+ // @tests - error and event message is checked spec/uninstall.spec.js
+ msg += ' and cannot be removed (hint: use -f or --force)';
+
+ if(plugin_id == top_plugin_id) {
+ return Q.reject( new Error(msg) );
+ } else {
+ events.emit('warn', msg +' and cannot be removed (hint: use -f or --force)');
+ continue;
+ }
}
}
@@ -163,7 +190,7 @@ function runUninstallPlatform(actions, platform, project_dir, plugin_dir, plugin
var promise;
if (deps && deps.length && danglers && danglers.length) {
-
+
// @tests - important this event is checked spec/uninstall.spec.js
events.emit('log', 'Uninstalling ' + danglers.length + ' dependent plugins.');
promise = Q.all(
[3/4] git commit: Refactoring of install & uninstall tests
Posted by ag...@apache.org.
Refactoring of install & uninstall tests
Project: http://git-wip-us.apache.org/repos/asf/cordova-plugman/repo
Commit: http://git-wip-us.apache.org/repos/asf/cordova-plugman/commit/ae2ce7ac
Tree: http://git-wip-us.apache.org/repos/asf/cordova-plugman/tree/ae2ce7ac
Diff: http://git-wip-us.apache.org/repos/asf/cordova-plugman/diff/ae2ce7ac
Branch: refs/heads/master
Commit: ae2ce7ac6325d1c6558365b4be82e5a8ff0983b9
Parents: 7e26c88
Author: jbondc <jb...@openmv.com>
Authored: Fri Feb 28 12:44:00 2014 -0500
Committer: Andrew Grieve <ag...@chromium.org>
Committed: Mon Mar 10 16:29:27 2014 -0400
----------------------------------------------------------------------
plugman.js | 11 +-
spec/common.js | 43 ++
spec/install.spec.js | 513 +++++++++++--------
.../android_install/AndroidManifest.xml | 20 +
.../android_install/cordova/android_sdk_version | 1 +
spec/projects/android_install/cordova/version | 1 +
.../android_install/cordova/version.bat | 2 +
.../android_uninstall/AndroidManifest.xml | 20 +
spec/projects/android_uninstall/cordova/version | 1 +
.../android_uninstall/cordova/version.bat | 2 +
spec/uninstall.spec.js | 338 +++++-------
src/events.js | 3 +-
src/install.js | 458 +++++++++++------
src/platforms/android.js | 5 +-
src/platforms/blackberry10.js | 5 +-
src/platforms/common.js | 23 +-
src/platforms/windows8.js | 5 +-
src/platforms/wp7.js | 5 +-
src/platforms/wp8.js | 5 +-
src/prepare.js | 19 +-
src/uninstall.js | 148 ++++--
src/util/default-engines.js | 5 +-
src/util/dependencies.js | 63 ++-
23 files changed, 1017 insertions(+), 679 deletions(-)
----------------------------------------------------------------------
http://git-wip-us.apache.org/repos/asf/cordova-plugman/blob/ae2ce7ac/plugman.js
----------------------------------------------------------------------
diff --git a/plugman.js b/plugman.js
index 5ceac60..4aa1843 100644
--- a/plugman.js
+++ b/plugman.js
@@ -19,7 +19,7 @@
// copyright (c) 2013 Andrew Lunny, Adobe Systems
-var emitter = require('./src/events');
+var events = require('./src/events');
var Q = require('q');
function addProperty(o, symbol, modulePath, doWrap) {
@@ -53,12 +53,13 @@ function addProperty(o, symbol, modulePath, doWrap) {
}
plugman = {
- on: emitter.addListener,
- off: emitter.removeListener,
- removeAllListeners: emitter.removeAllListeners,
- emit: emitter.emit,
+ on: events.on.bind(events),
+ off: events.removeListener.bind(events),
+ removeAllListeners: events.removeAllListeners.bind(events),
+ emit: events.emit.bind(events),
raw: {}
};
+
addProperty(plugman, 'help', './src/help');
addProperty(plugman, 'install', './src/install', true);
addProperty(plugman, 'uninstall', './src/uninstall', true);
http://git-wip-us.apache.org/repos/asf/cordova-plugman/blob/ae2ce7ac/spec/common.js
----------------------------------------------------------------------
diff --git a/spec/common.js b/spec/common.js
new file mode 100644
index 0000000..2991806
--- /dev/null
+++ b/spec/common.js
@@ -0,0 +1,43 @@
+
+var plugman = require('../plugman'),
+ nopt = require('nopt');
+
+var known_opts = {
+ 'verbose' : Boolean,
+ 'debug' : Number
+}, shortHands = { 'd' : ['--debug'] };
+
+var opt = nopt(known_opts, shortHands);
+var mapNames = {
+ 'verbose' : 7,
+ 'info' : 6,
+ 'notice' : 5,
+ 'warn' : 4,
+ 'error' : 3
+}
+
+if(opt.verbose)
+ opt.debug = 7;
+
+if(opt.debug) {
+ for(var i in mapNames) {
+ if(mapNames[i] <= opt.debug)
+ plugman.on(i, console.log);
+ }
+
+ if(opt.debug >= 6)
+ plugman.on('log', console.log);
+}
+
+module.exports = {
+ spy: {
+ getInstall: function(emitSpy){
+ var install = [], i;
+ for(i in emitSpy.argsForCall) {
+ if(emitSpy.argsForCall[i][1].substr(0, 13) === 'Install start')
+ install.push(emitSpy.argsForCall[i][1]);
+ }
+ return install;
+ }
+ }
+};
http://git-wip-us.apache.org/repos/asf/cordova-plugman/blob/ae2ce7ac/spec/install.spec.js
----------------------------------------------------------------------
diff --git a/spec/install.spec.js b/spec/install.spec.js
index 8401526..bff78c2 100644
--- a/spec/install.spec.js
+++ b/spec/install.spec.js
@@ -2,324 +2,407 @@ var install = require('../src/install'),
actions = require('../src/util/action-stack'),
config_changes = require('../src/util/config-changes'),
xml_helpers = require('../src/util/xml-helpers'),
+ events = require('../src/events'),
plugman = require('../plugman'),
+ platforms = require('../src/platforms/common'),
+ common = require('./common'),
fs = require('fs'),
- os = require('osenv'),
+ os = require('os'),
path = require('path'),
shell = require('shelljs'),
child_process = require('child_process'),
semver = require('semver'),
- temp = __dirname,
- dummyplugin = 'DummyPlugin',
- dummy_id = 'com.phonegap.plugins.dummyplugin',
- variableplugin = 'VariablePlugin',
- engineplugin = 'EnginePlugin',
- childplugin = 'ChildBrowser',
Q = require('q'),
- plugins_dir = path.join(temp, 'plugins');
+ spec = __dirname,
+ done = false,
+ srcProject = path.join(spec, 'projects', 'android_install'),
+ project = path.join(os.tmpdir(), 'plugman-test', 'android_install'),
-describe('install', function() {
- var exists, get_json, chmod, exec, proc, add_to_queue, prepare, actions_push, c_a, mkdir, done;
+ plugins_dir = path.join(spec, 'plugins'),
+ plugins_install_dir = path.join(project, 'cordova', 'plugins'),
+ plugins = {
+ 'DummyPlugin' : path.join(plugins_dir, 'DummyPlugin'),
+ 'EnginePlugin' : path.join(plugins_dir, 'EnginePlugin'),
+ 'EnginePluginAndroid' : path.join(plugins_dir, 'EnginePluginAndroid'),
+ 'ChildBrowser' : path.join(plugins_dir, 'ChildBrowser'),
+ 'VariablePlugin' : path.join(plugins_dir, 'VariablePlugin'),
+ 'A' : path.join(plugins_dir, 'dependencies', 'A'),
+ 'C' : path.join(plugins_dir, 'dependencies', 'C'),
+ 'F' : path.join(plugins_dir, 'dependencies', 'F'),
+ 'G' : path.join(plugins_dir, 'dependencies', 'G')
+ },
+ promise,
+ results = {},
+ dummy_id = 'com.phonegap.plugins.dummyplugin';
+
+function installPromise(f) {
+ f.then(function() { done = true; }, function(err) { done = err; });
+}
+
+var existsSync = fs.existsSync;
+
+// Mocked functions for tests
+var fake = {
+ 'existsSync' : {
+ 'noPlugins' : function(path){
+ // fake installed plugin directories as 'not found'
+ if( path.slice(-5) !== '.json' && path.indexOf(plugins_install_dir) >= 0) {
+ return false;
+ }
- function installPromise(f) {
- f.then(function() { done = true; }, function(err) { done = err; });
+ return existsSync(path);
+ }
+ },
+ 'fetch' : {
+ 'dependencies' : function(id, dir) {
+ if(id == plugins['A'])
+ return Q(id); // full path to plugin
+
+ return Q( path.join(plugins_dir, 'dependencies', id) );
+ }
}
+}
+
+describe('start', function() {
+ var prepare, config_queue_add, proc, actions_push, ca, emit;
beforeEach(function() {
- proc = spyOn(actions.prototype, 'process').andCallFake(function(platform, proj) {
- return Q();
- });
- mkdir = spyOn(shell, 'mkdir');
- actions_push = spyOn(actions.prototype, 'push');
- c_a = spyOn(actions.prototype, 'createAction');
prepare = spyOn(plugman, 'prepare');
- exec = spyOn(child_process, 'exec').andCallFake(function(cmd, options, cb) {
- if (!cb) cb = options;
+ config_queue_add = spyOn(config_changes, 'add_installed_plugin_to_prepare_queue');
+ proc = spyOn(actions.prototype, 'process').andReturn( Q(true) );
+ actions_push = spyOn(actions.prototype, 'push');
+ ca = spyOn(actions.prototype, 'createAction');
+ });
+ it('start', function() {
+ shell.rm('-rf', project);
+ shell.cp('-R', path.join(srcProject, '*'), project);
+
+ done = false;
+ promise = Q()
+ .then(
+ function(){ return install('android', project, plugins['DummyPlugin']) }
+ ).then(
+ function(){
+ results['actions_callCount'] = actions_push.callCount;
+ results['actions_create'] = ca.argsForCall[0];
+ results['config_add'] = config_queue_add.argsForCall[0];
+
+ return Q();
+ }
+ ).then(
+ function(){ return install('android', project, plugins['EnginePlugin']) }
+ ).then(
+ function(){
+ emit = spyOn(events, 'emit');
+ return install('android', project, plugins['ChildBrowser'])
+ }
+ ).then(
+ function(){
+ return install('android', project, plugins['VariablePlugin'], plugins_install_dir, { cli_variables:{API_KEY:'batman'} })
+ }
+ ).then(
+ function(){
+ done = true;
+ results['prepareCount'] = prepare.callCount;
+ results['emit_results'] = [];
+
+ for(var i in emit.calls) {
+ if(emit.calls[i].args[0] === 'results')
+ results['emit_results'].push(emit.calls[i].args[1]);
+ }
+
+ events.emit("verbose", "***** DONE START *****");
+ }
+ );
+ waitsFor(function() { return done; }, 'promise never resolved', 500);
+ });
+});
+
+describe('install', function() {
+ var chmod, exec, proc, add_to_queue, prepare, actions_push, c_a, mkdir, cp, rm, fetchSpy, emit;
+
+ beforeEach(function() {
+ prepare = spyOn(plugman, 'prepare').andReturn( Q(true) );
+
+ exec = spyOn(child_process, 'exec').andCallFake(function(cmd, cb) {
cb(false, '', '');
});
- chmod = spyOn(fs, 'chmodSync');
- exists = spyOn(fs, 'existsSync').andReturn(true);
- get_json = spyOn(config_changes, 'get_platform_json').andReturn({
- installed_plugins:{},
- dependent_plugins:{}
- });
+ spyOn(fs, 'mkdirSync').andReturn(true);
+ spyOn(shell, 'mkdir').andReturn(true);
+ spyOn(platforms, 'copyFile').andReturn(true);
+
+ fetchSpy = spyOn(plugman.raw, 'fetch').andReturn( Q( plugins['EnginePlugin'] ) );
+ chmod = spyOn(fs, 'chmodSync').andReturn(true);
+ fsWrite = spyOn(fs, 'writeFileSync').andReturn(true);
+ cp = spyOn(shell, 'cp').andReturn(true);
+ rm = spyOn(shell, 'rm').andReturn(true);
add_to_queue = spyOn(config_changes, 'add_installed_plugin_to_prepare_queue');
done = false;
});
+
describe('success', function() {
it('should call prepare after a successful install', function() {
- runs(function() {
- installPromise(install('android', temp, dummyplugin, plugins_dir, {}));
- });
- waitsFor(function() { return done; }, 'install promise never resolved', 500);
- runs(function() {
- expect(done).toBe(true);
- expect(prepare).toHaveBeenCalled();
- });
+ expect(results['prepareCount']).toBe(4);
});
+
+ it('should emit a results event with platform-agnostic <info>', function() {
+ // ChildBrowser
+ expect(results['emit_results'][0]).toBe('No matter what platform you are installing to, this notice is very important.');
+ });
+ it('should emit a results event with platform-specific <info>', function() {
+ // ChildBrowser
+ expect(results['emit_results'][1]).toBe('Please make sure you read this because it is very important to complete the installation of your plugin.');
+ });
+ it('should interpolate variables into <info> tags', function() {
+ // VariableBrowser
+ expect(results['emit_results'][2]).toBe('Remember that your api key is batman!');
+ });
+
it('should call fetch if provided plugin cannot be resolved locally', function() {
- fetchSpy = spyOn(plugman.raw, 'fetch').andReturn(Q(path.join(plugins_dir, dummyplugin)));
- exists.andReturn(false);
+ fetchSpy.andReturn( Q( plugins['DummyPlugin'] ) );
+ spyOn(fs, 'existsSync').andCallFake( fake['existsSync']['noPlugins'] );
+
runs(function() {
- installPromise(install('android', temp, 'CLEANYOURSHORTS', plugins_dir, {}));
+ installPromise(install('android', project, 'CLEANYOURSHORTS' ));
});
- waitsFor(function() { return done; }, 'install promise never resolved', 500);
+ waitsFor(function() { return done; }, 'install promise never resolved', 200);
runs(function() {
expect(done).toBe(true);
expect(fetchSpy).toHaveBeenCalled();
});
});
- it('should call the config-changes module\'s add_installed_plugin_to_prepare_queue method after processing an install', function() {
- runs(function() {
- installPromise(install('android', temp, dummyplugin, plugins_dir, {}));
- });
- waitsFor(function() { return done; }, 'install promise never resolved', 500);
- runs(function() {
- expect(add_to_queue).toHaveBeenCalledWith(plugins_dir, 'DummyPlugin', 'android', {}, true);
- });
+
+ it('should call the config-changes module\'s add_installed_plugin_to_prepare_queue method after processing an install', function() {
+ expect(results['config_add']).toEqual([plugins_install_dir, dummy_id, 'android', {}, true]);
});
- it('should notify if plugin is already installed into project', function() {
- var spy = spyOn(plugman, 'emit');
- get_json.andReturn({
- installed_plugins:{
- 'com.phonegap.plugins.dummyplugin':{}
- },
- dependent_plugins:{}
- });
- installPromise(install('android', temp, dummyplugin, plugins_dir, {}));
- waitsFor(function() { return done; }, 'install promise never resolved', 500);
- runs(function() {
- expect(spy).toHaveBeenCalledWith('results', 'Plugin "'+dummy_id+'" already installed on android.');
- });
+ it('should queue up actions as appropriate for that plugin and call process on the action stack',
+ function() { expect(results['actions_callCount']).toEqual(3);
+ expect(results['actions_create']).toEqual([jasmine.any(Function), [jasmine.any(Object), path.join(plugins_install_dir, dummy_id), project, dummy_id], jasmine.any(Function), [jasmine.any(Object), project, dummy_id]]);
});
+
it('should check version if plugin has engine tag', function(){
- var spy = spyOn(semver, 'satisfies').andReturn(true);
+ var satisfies = spyOn(semver, 'satisfies').andReturn(true);
exec.andCallFake(function(cmd, cb) {
- cb(null, '2.5.0\n', '');
+ cb(null, '2.5.0\n');
});
+
runs(function() {
- installPromise(install('android', temp, engineplugin, plugins_dir, {}));
+ installPromise( install('android', project, plugins['EnginePlugin']) );
});
- waitsFor(function() { return done; }, 'install promise never resolved', 500);
+ waitsFor(function() { return done; }, 'install promise never resolved', 200);
runs(function() {
- expect(spy).toHaveBeenCalledWith('2.5.0','>=2.3.0');
+ expect(satisfies).toHaveBeenCalledWith('2.5.0','>=2.3.0');
});
});
it('should check version and munge it a little if it has "rc" in it so it plays nice with semver (introduce a dash in it)', function() {
- var spy = spyOn(semver, 'satisfies').andReturn(true);
+ var satisfies = spyOn(semver, 'satisfies').andReturn(true);
exec.andCallFake(function(cmd, cb) {
cb(null, '3.0.0rc1\n');
});
+
runs(function() {
- installPromise(install('android', temp, engineplugin, plugins_dir, {}));
+ installPromise( install('android', project, plugins['EnginePlugin']) );
});
- waitsFor(function() { return done; }, 'install promise never resolved', 500);
+ waitsFor(function() { return done; }, 'install promise never resolved', 200);
runs(function() {
- expect(spy).toHaveBeenCalledWith('3.0.0-rc1','>=2.3.0');
+ expect(satisfies).toHaveBeenCalledWith('3.0.0-rc1','>=2.3.0');
});
});
it('should check specific platform version over cordova version if specified', function() {
var spy = spyOn(semver, 'satisfies').andReturn(true);
exec.andCallFake(function(cmd, cb) {
- cb(null, '3.1.0\n', '');
+ cb(null, '3.1.0\n');
});
+ fetchSpy.andReturn( Q( plugins['EnginePluginAndroid'] ) );
+
runs(function() {
- installPromise(install('android', temp, 'EnginePluginAndroid', plugins_dir, {}));
+ installPromise( install('android', project, plugins['EnginePluginAndroid']) );
});
- waitsFor(function() { return done; }, 'install promise never resolved', 500);
+ waitsFor(function() { return done; }, 'install promise never resolved', 200);
runs(function() {
expect(spy).toHaveBeenCalledWith('3.1.0','>=3.1.0');
});
});
it('should check platform sdk version if specified', function() {
var spy = spyOn(semver, 'satisfies').andReturn(true);
+ fetchSpy.andReturn( Q( plugins['EnginePluginAndroid'] ) );
exec.andCallFake(function(cmd, cb) {
- cb(null, '18\n', '');
- });
- runs(function() {
- installPromise(install('android', temp, 'EnginePluginAndroid', plugins_dir, {}));
- });
- waitsFor(function() { return done; }, 'install promise never resolved', 500);
- runs(function() {
- expect(spy).toHaveBeenCalledWith('18.0.0','>=18');
- });
- });
- it('should check plugmans version', function() {
- var spy = spyOn(semver, 'satisfies').andReturn(true);
- runs(function() {
- installPromise(install('android', temp, engineplugin, plugins_dir, {}));
- });
- waitsFor(function() { return done; }, 'install promise never resolved', 500);
- runs(function() {
- expect(spy).toHaveBeenCalledWith('','>=0.10.0');
+ cb(null, '18\n');
});
- });
- it('should check custom engine version', function() {
- var spy = spyOn(semver, 'satisfies').andReturn(true);
+
runs(function() {
- installPromise(install('android', temp, engineplugin, plugins_dir, {}));
+ installPromise( install('android', project, 'EnginePluginAndroid') );
});
- waitsFor(function() { return done; }, 'install promise never resolved', 500);
+ waitsFor(function() { return done; }, 'install promise never resolved', 200);
runs(function() {
- expect(spy).toHaveBeenCalledWith('','>=1.0.0');
+ // <engine name="cordova" VERSION=">=3.0.0"/>
+ // <engine name="cordova-android" VERSION=">=3.1.0"/>
+ // <engine name="android-sdk" VERSION=">=18"/>
+
+ expect(spy.calls.length).toBe(3);
+ expect(spy.calls[0].args).toEqual([ '18.0.0', '>=3.0.0' ]);
+ expect(spy.calls[1].args).toEqual([ '18.0.0', '>=3.1.0' ]);
+ expect(spy.calls[2].args).toEqual([ '18.0.0','>=18' ]);
});
});
- it('should check custom engine version that supports multiple platforms', function() {
+ it('should check engine versions', function() {
var spy = spyOn(semver, 'satisfies').andReturn(true);
+ fetchSpy.andReturn( Q( plugins['EnginePlugin'] ) );
+
runs(function() {
- installPromise(install('android', temp, engineplugin, plugins_dir, {}));
+ installPromise( install('android', project, plugins['EnginePlugin']) );
});
- waitsFor(function() { return done; }, 'install promise never resolved', 500);
+ waitsFor(function() { return done; }, 'install promise never resolved', 200);
runs(function() {
- expect(spy).toHaveBeenCalledWith('','>=3.0.0');
+ // <engine name="cordova" version=">=2.3.0"/>
+ // <engine name="cordova-plugman" version=">=0.10.0" />
+ // <engine name="mega-fun-plugin" version=">=1.0.0" scriptSrc="megaFunVersion" platform="*" />
+ // <engine name="mega-boring-plugin" version=">=3.0.0" scriptSrc="megaBoringVersion" platform="ios|android" />
+
+ var plugmanVersion = require('../package.json').version;
+
+ expect(spy.calls.length).toBe(4);
+ expect(spy.calls[0].args).toEqual([ '', '>=2.3.0' ]);
+ expect(spy.calls[1].args).toEqual([ plugmanVersion, '>=0.10.0' ]);
+ expect(spy.calls[2].args).toEqual([ '', '>=1.0.0' ]);
+ expect(spy.calls[3].args).toEqual([ '', '>=3.0.0' ]);
});
});
it('should not check custom engine version that is not supported for platform', function() {
var spy = spyOn(semver, 'satisfies').andReturn(true);
runs(function() {
- installPromise(install('blackberry10', temp, engineplugin, plugins_dir, {}));
+ installPromise( install('blackberry10', project, plugins['EnginePlugin']) );
});
- waitsFor(function() { return done; }, 'install promise never resolved', 500);
+ waitsFor(function() { return done; }, 'install promise never resolved', 200);
runs(function() {
expect(spy).not.toHaveBeenCalledWith('','>=3.0.0');
});
});
- it('should queue up actions as appropriate for that plugin and call process on the action stack', function() {
- runs(function() {
- installPromise(install('android', temp, dummyplugin, plugins_dir, {}));
- });
- waitsFor(function() { return done; }, 'install promise never resolved', 500);
- runs(function() {
- 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();
- });
- });
- it('should emit a results event with platform-agnostic <info>', function() {
- var emit = spyOn(plugman, 'emit');
- runs(function() {
- installPromise(install('android', temp, childplugin, plugins_dir, {}));
- });
- waitsFor(function() { return done; }, 'install promise never resolved', 500);
- runs(function() {
- expect(emit).toHaveBeenCalledWith('results', 'No matter what platform you are installing to, this notice is very important.');
- });
- });
- it('should emit a results event with platform-specific <info>', function() {
- var emit = spyOn(plugman, 'emit');
- runs(function() {
- installPromise(install('android', temp, childplugin, plugins_dir, {}));
- });
- waitsFor(function() { return done; }, 'install promise never resolved', 500);
- runs(function() {
- expect(emit).toHaveBeenCalledWith('results', 'Please make sure you read this because it is very important to complete the installation of your plugin.');
- });
- });
- it('should interpolate variables into <info> tags', function() {
- var emit = spyOn(plugman, 'emit');
- runs(function() {
- installPromise(install('android', temp, variableplugin, plugins_dir, {cli_variables:{API_KEY:'batman'}}));
- });
- waitsFor(function() { return done; }, 'install promise never resolved', 500);
- runs(function() {
- expect(emit).toHaveBeenCalledWith('results', 'Remember that your api key is batman!');
- });
- });
describe('with dependencies', function() {
- it('should process all dependent plugins', function() {
- // Plugin A depends on C & D
- runs(function() {
- installPromise(install('android', temp, 'A', path.join(plugins_dir, 'dependencies'), {}));
- });
- waitsFor(function() { return done; }, 'install promise never resolved', 500);
- runs(function() {
- // So process should be called 3 times
- expect(proc.calls.length).toEqual(3);
+ it('should install any dependent plugins if missing', function() {
+ spyOn(fs, 'existsSync').andCallFake( fake['existsSync']['noPlugins'] );
+ fetchSpy.andCallFake( fake['fetch']['dependencies'] );
+ emit = spyOn(events, 'emit');
+
+ exec.andCallFake(function(cmd, cb) {
+ cb(null, '9.0.0\n');
});
- });
- it('should fetch any dependent plugins if missing', function() {
- var deps_dir = path.join(plugins_dir, 'dependencies'),
- s = spyOn(plugman.raw, 'fetch').andCallFake(function(id, dir, opts) {
- return Q(path.join(dir, id));
- });
+
runs(function() {
- exists.andReturn(false);
- // Plugin A depends on C & D
- install('android', temp, 'A', deps_dir, {});
+ installPromise( install('android', project, plugins['A']) );
});
- waits(100);
- runs(function() {
- expect(s).toHaveBeenCalledWith('C', deps_dir, { link: false, subdir: undefined, git_ref: undefined, client: 'plugman', expected_id: 'C' });
- expect(s.calls.length).toEqual(3);
+ waitsFor(function() { return done; }, 'install promise never resolved', 200);
+ runs(function() {
+ // Look for 'Installing plugin ...' in events
+ var install = common.spy.getInstall(emit);
+
+ expect(install).toEqual([
+ 'Install start for "C" on android.',
+ 'Install start for "D" on android.',
+ 'Install start for "A" on android.'
+ ]);
});
});
- it('should try to fetch any dependent plugins from registry when url is not defined', function() {
- var deps_dir = path.join(plugins_dir, 'dependencies'),
- s = spyOn(plugman.raw, 'fetch').andCallFake(function(id, dir) {
- return Q(path.join(dir,id));
- });
- exists.andReturn(false);
+ it('should install any dependent plugins from registry when url is not defined', function() {
+ spyOn(fs, 'existsSync').andCallFake( fake['existsSync']['noPlugins'] );
+ fetchSpy.andCallFake( fake['fetch']['dependencies'] );
+ emit = spyOn(events, 'emit');
+ exec.andCallFake(function(cmd, cb) {
+ cb(null, '9.0.0\n', '');
+ });
+
// Plugin A depends on C & D
runs(function() {
- installPromise(install('android', temp, 'E', deps_dir, {}));
+ installPromise( install('android', project, plugins['A']) );
});
- waitsFor(function() { return done; }, 'promise never resolved', 500);
+ waitsFor(function() { return done; }, 'promise never resolved', 200);
runs(function() {
- expect(s).toHaveBeenCalledWith('D', deps_dir, { link: false, subdir: undefined, git_ref: undefined, client: 'plugman', expected_id: 'D' });
- expect(s.calls.length).toEqual(2);
- });
- });
- it('should process all dependent plugins with alternate routes to the same plugin', function() {
- // Plugin F depends on A, C, D and E
- runs(function () {
- installPromise(install('android', temp, 'F', path.join(plugins_dir, 'dependencies'), {}));
- });
- waitsFor(function () { return done; }, 'install promise never resolved', 500);
- runs(function () {
+ // TODO: this is same test as above? Need test other dependency with url=?
+ var install = common.spy.getInstall(emit);
- // So process should be called 5 times
- expect(proc.calls.length).toEqual(5);
- });
- });
- it('should throw if there is a cyclic dependency', function() {
- runs(function () {
- installPromise(install('android', temp, 'G', path.join(plugins_dir, 'dependencies'), {}));
- });
- waitsFor(function () { return done; }, 'install promise never resolved', 500);
- runs(function () {
- expect(done.message).toEqual('Cyclic dependency from G to H');
+ expect(install).toEqual([
+ 'Install start for "C" on android.',
+ 'Install start for "D" on android.',
+ 'Install start for "A" on android.'
+ ]);;
});
});
});
+ it('should process all dependent plugins with alternate routes to the same plugin', function() {
+ spyOn(fs, 'existsSync').andCallFake( fake['existsSync']['noPlugins'] );
+ fetchSpy.andCallFake( fake['fetch']['dependencies'] );
+ emit = spyOn(events, 'emit');
+ exec.andCallFake(function(cmd, cb) {
+ cb(null, '9.0.0\n', '');
+ });
+
+ // Plugin F depends on A, C, D and E
+ runs(function () {
+ installPromise(install('android', project, plugins['F']));
+ });
+ waitsFor(function () { return done; }, 'install promise never resolved', 500);
+ runs(function () {
+ var install = common.spy.getInstall(emit);
+
+ expect(install).toEqual([
+ 'Install start for "C" on android.',
+ 'Install start for "D" on android.',
+ 'Install start for "A" on android.',
+ 'Install start for "D" on android.',
+ 'Install start for "F" on android.'
+ ]);
+ });
+ });
+ /*
+ it('should throw if there is a cyclic dependency', function() {
+ // TODO: group this into BeforeStart() for all dependency tests
+ spyOn(fs, 'existsSync').andCallFake( fake['existsSync']['noPlugins'] );
+ fetchSpy.andCallFake( fake['fetch']['dependencies'] );
+ emit = spyOn(events, 'emit');
+ exec.andCallFake(function(cmd, cb) {
+ cb(null, '9.0.0\n', '');
+ });
+
+ runs(function () {
+ installPromise(install('android', project, plugins['G']));
+ });
+ waitsFor(function () { return done; }, 'install promise never resolved', 500);
+ runs(function () {
+ var install = common.spy.getInstall(emit);
+
+ console.log(install);
+ expect(done.message).toEqual('Cyclic dependency from G to H');
+ });
+ });*/
});
xdescribe('failure', function() {
it('should throw if platform is unrecognized', function() {
runs(function() {
- installPromise(install('atari', temp, 'SomePlugin', plugins_dir, {}));
+ installPromise( install('atari', project, 'SomePlugin') );
});
- waitsFor(function() { return done; }, 'install promise never resolved', 500);
+ waitsFor(function() { return done; }, 'install promise never resolved', 200);
runs(function() {
expect(done).toEqual(new Error('atari not supported.'));
});
});
it('should throw if variables are missing', function() {
runs(function() {
- installPromise(install('android', temp, variableplugin, plugins_dir, {}));
+ installPromise( install('android', project, plugins['VariablePlugin']) );
});
- waitsFor(function(){ return done; }, 'install promise never resolved', 500);
+ waitsFor(function(){ return done; }, 'install promise never resolved', 200);
runs(function() {
expect(done).toEqual(new Error('Variable(s) missing: API_KEY'));
});
});
it('should throw if git is not found on the path and a remote url is requested', function() {
- exists.andReturn(false);
+ spyOn(fs, 'existsSync').andCallFake( fake['existsSync']['noPlugins'] );
var which_spy = spyOn(shell, 'which').andReturn(null);
runs(function() {
- installPromise(install('android', temp, 'https://git-wip-us.apache.org/repos/asf/cordova-plugin-camera.git', plugins_dir, {}));
+ installPromise( install('android', project, 'https://git-wip-us.apache.org/repos/asf/cordova-plugin-camera.git') );
});
- waitsFor(function(){ return done; }, 'install promise never resolved', 500);
+ waitsFor(function(){ return done; }, 'install promise never resolved', 200);
runs(function() {
expect(done).toEqual(new Error('"git" command line tool is not installed: make sure it is accessible on your PATH.'));
});
@@ -327,15 +410,37 @@ describe('install', function() {
it('should throw if plugin version is less than the minimum requirement', function(){
var spy = spyOn(semver, 'satisfies').andReturn(false);
exec.andCallFake(function(cmd, cb) {
- cb(null, '0.0.1\n', '');
+ cb(null, '0.0.1\n');
});
runs(function() {
- installPromise(install('android', temp, engineplugin, plugins_dir, {}));
+ installPromise( install('android', project, plugins['EnginePlugin']) );
});
- waitsFor(function(){ return done; }, 'install promise never resolved', 500);
+ waitsFor(function(){ return done; }, 'install promise never resolved', 200);
runs(function() {
expect(done).toEqual(new Error('Plugin doesn\'t support this project\'s cordova version. cordova: 0.0.1, failed version requirement: >=2.3.0'));
});
});
});
+
+});
+
+
+describe('end', function() {
+
+ it('end', function() {
+ done = false;
+ var finish = function(err){
+ if(err)
+ events.emit('error', err);
+
+ shell.rm('-rf', project);
+ done = true;
+ }
+
+ promise.then(
+
+ ).fin(finish);
+
+ waitsFor(function() { return done; }, 'promise never resolved', 500);
+ });
});
http://git-wip-us.apache.org/repos/asf/cordova-plugman/blob/ae2ce7ac/spec/projects/android_install/AndroidManifest.xml
----------------------------------------------------------------------
diff --git a/spec/projects/android_install/AndroidManifest.xml b/spec/projects/android_install/AndroidManifest.xml
new file mode 100644
index 0000000..b5fea9d
--- /dev/null
+++ b/spec/projects/android_install/AndroidManifest.xml
@@ -0,0 +1,20 @@
+<?xml version='1.0' encoding='utf-8'?>
+<manifest android:versionCode="5" android:versionName="1.1" android:windowSoftInputMode="adjustPan" package="cordova.test.uninstall" xmlns:android="http://schemas.android.com/apk/res/android">
+ <supports-screens android:anyDensity="true" android:largeScreens="true" android:normalScreens="true" android:resizeable="true" android:smallScreens="true" android:xlargeScreens="true" />
+ <application android:debuggable="true" android:icon="@drawable/icon" android:label="@string/app_name">
+ <activity android:configChanges="orientation|keyboardHidden" android:label="@string/app_name" android:name="ChildApp">
+ <intent-filter>
+ <action android:name="android.intent.action.MAIN" />
+ <category android:name="android.intent.category.LAUNCHER" />
+ </intent-filter>
+ </activity>
+ <activity android:configChanges="orientation|keyboardHidden" android:label="@string/app_name" android:name="com.phonegap.DroidGap">
+ <intent-filter>
+ </intent-filter>
+ </activity>
+ <activity android:label="@string/app_name" android:name="com.phonegap.plugins.dummyplugin.DummyPlugin">
+ <intent-filter />
+ </activity>
+ </application>
+ <uses-sdk android:minSdkVersion="5" />
+</manifest>
http://git-wip-us.apache.org/repos/asf/cordova-plugman/blob/ae2ce7ac/spec/projects/android_install/cordova/android_sdk_version
----------------------------------------------------------------------
diff --git a/spec/projects/android_install/cordova/android_sdk_version b/spec/projects/android_install/cordova/android_sdk_version
new file mode 100644
index 0000000..0ab155c
--- /dev/null
+++ b/spec/projects/android_install/cordova/android_sdk_version
@@ -0,0 +1 @@
+echo 18.0.9
\ No newline at end of file
http://git-wip-us.apache.org/repos/asf/cordova-plugman/blob/ae2ce7ac/spec/projects/android_install/cordova/version
----------------------------------------------------------------------
diff --git a/spec/projects/android_install/cordova/version b/spec/projects/android_install/cordova/version
new file mode 100644
index 0000000..01f68fd
--- /dev/null
+++ b/spec/projects/android_install/cordova/version
@@ -0,0 +1 @@
+echo 9.0.0
\ No newline at end of file
http://git-wip-us.apache.org/repos/asf/cordova-plugman/blob/ae2ce7ac/spec/projects/android_install/cordova/version.bat
----------------------------------------------------------------------
diff --git a/spec/projects/android_install/cordova/version.bat b/spec/projects/android_install/cordova/version.bat
new file mode 100644
index 0000000..c637d7c
--- /dev/null
+++ b/spec/projects/android_install/cordova/version.bat
@@ -0,0 +1,2 @@
+@ECHO OFF
+echo 9.0.0
\ No newline at end of file
http://git-wip-us.apache.org/repos/asf/cordova-plugman/blob/ae2ce7ac/spec/projects/android_uninstall/AndroidManifest.xml
----------------------------------------------------------------------
diff --git a/spec/projects/android_uninstall/AndroidManifest.xml b/spec/projects/android_uninstall/AndroidManifest.xml
new file mode 100644
index 0000000..b5fea9d
--- /dev/null
+++ b/spec/projects/android_uninstall/AndroidManifest.xml
@@ -0,0 +1,20 @@
+<?xml version='1.0' encoding='utf-8'?>
+<manifest android:versionCode="5" android:versionName="1.1" android:windowSoftInputMode="adjustPan" package="cordova.test.uninstall" xmlns:android="http://schemas.android.com/apk/res/android">
+ <supports-screens android:anyDensity="true" android:largeScreens="true" android:normalScreens="true" android:resizeable="true" android:smallScreens="true" android:xlargeScreens="true" />
+ <application android:debuggable="true" android:icon="@drawable/icon" android:label="@string/app_name">
+ <activity android:configChanges="orientation|keyboardHidden" android:label="@string/app_name" android:name="ChildApp">
+ <intent-filter>
+ <action android:name="android.intent.action.MAIN" />
+ <category android:name="android.intent.category.LAUNCHER" />
+ </intent-filter>
+ </activity>
+ <activity android:configChanges="orientation|keyboardHidden" android:label="@string/app_name" android:name="com.phonegap.DroidGap">
+ <intent-filter>
+ </intent-filter>
+ </activity>
+ <activity android:label="@string/app_name" android:name="com.phonegap.plugins.dummyplugin.DummyPlugin">
+ <intent-filter />
+ </activity>
+ </application>
+ <uses-sdk android:minSdkVersion="5" />
+</manifest>
http://git-wip-us.apache.org/repos/asf/cordova-plugman/blob/ae2ce7ac/spec/projects/android_uninstall/cordova/version
----------------------------------------------------------------------
diff --git a/spec/projects/android_uninstall/cordova/version b/spec/projects/android_uninstall/cordova/version
new file mode 100644
index 0000000..01f68fd
--- /dev/null
+++ b/spec/projects/android_uninstall/cordova/version
@@ -0,0 +1 @@
+echo 9.0.0
\ No newline at end of file
http://git-wip-us.apache.org/repos/asf/cordova-plugman/blob/ae2ce7ac/spec/projects/android_uninstall/cordova/version.bat
----------------------------------------------------------------------
diff --git a/spec/projects/android_uninstall/cordova/version.bat b/spec/projects/android_uninstall/cordova/version.bat
new file mode 100644
index 0000000..c637d7c
--- /dev/null
+++ b/spec/projects/android_uninstall/cordova/version.bat
@@ -0,0 +1,2 @@
+@ECHO OFF
+echo 9.0.0
\ No newline at end of file
http://git-wip-us.apache.org/repos/asf/cordova-plugman/blob/ae2ce7ac/spec/uninstall.spec.js
----------------------------------------------------------------------
diff --git a/spec/uninstall.spec.js b/spec/uninstall.spec.js
index 432e9f4..7ae8a14 100644
--- a/spec/uninstall.spec.js
+++ b/spec/uninstall.spec.js
@@ -1,131 +1,112 @@
var uninstall = require('../src/uninstall'),
+ install = require('../src/install'),
actions = require('../src/util/action-stack'),
config_changes = require('../src/util/config-changes'),
- dependencies = require('../src/util/dependencies'),
- xml_helpers = require('../src/util/xml-helpers'),
- plugins = require('../src/util/plugins.js'),
+ events = require('../src/events'),
plugman = require('../plugman'),
+ common = require('./common'),
fs = require('fs'),
- os = require('osenv'),
path = require('path'),
shell = require('shelljs'),
- semver = require('semver'),
- temp = __dirname,
- dummyplugin = 'DummyPlugin',
- dummy_id = 'com.phonegap.plugins.dummyplugin',
- variableplugin = 'VariablePlugin',
- engineplugin = 'EnginePlugin',
- Q = require('q'),
- plugins_dir = path.join(temp, 'plugins');
+ Q = require('q'),
+ spec = __dirname,
+ done = false,
+ srcProject = path.join(spec, 'projects', 'android_uninstall'),
+ project = path.join(spec, 'projects', 'android_uninstall.test'),
+
+ plugins_dir = path.join(spec, 'plugins'),
+ plugins_install_dir = path.join(project, 'cordova', 'plugins'),
+ plugins = {
+ 'DummyPlugin' : path.join(plugins_dir, 'DummyPlugin'),
+ 'A' : path.join(plugins_dir, 'dependencies', 'A'),
+ 'C' : path.join(plugins_dir, 'dependencies', 'C')
+ },
+ promise,
+ dummy_id = 'com.phonegap.plugins.dummyplugin';
+
+function uninstallPromise(f) {
+ return f.then(function() { done = true; }, function(err) { done = err; });
+}
+
+describe('start', function() {
+
+ it('start', function() {
+ shell.rm('-rf', project);
+ shell.cp('-R', path.join(srcProject, '*'), project);
-describe('uninstallPlatform', function() {
- var exists, get_json, chmod, proc, add_to_queue, prepare, actions_push, c_a, rm, done;
- var gen_deps, get_chain;
+ done = false;
+ promise = Q()
+ .then(
+ function(){ return install('android', project, plugins['DummyPlugin']) }
+ ).then(
+ function(){ return install('android', project, plugins['A']) }
+ ).then(
+ function(){ done = true; }
+ );
+ waitsFor(function() { return done; }, 'promise never resolved', 500);
+ });
+});
- function uninstallPromise(f) {
- return f.then(function() { done = true; }, function(err) { done = err; });
- }
+describe('uninstallPlatform', function() {
+ var proc, prepare, actions_push, add_to_queue, c_a, rm;
+ var fsWrite;
- beforeEach(function() {
+ var plat_common = require('../src/platforms/common');
+
+ beforeEach(function() {
proc = spyOn(actions.prototype, 'process').andReturn(Q());
actions_push = spyOn(actions.prototype, 'push');
c_a = spyOn(actions.prototype, 'createAction');
prepare = spyOn(plugman, 'prepare');
- 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');
+ fsWrite = spyOn(fs, 'writeFileSync').andReturn(true);
+ rm = spyOn(shell, 'rm').andReturn(true);
+ spyOn(shell, 'cp').andReturn(true);
add_to_queue = spyOn(config_changes, 'add_uninstalled_plugin_to_prepare_queue');
- dependents = jasmine.createSpy('getChain').andReturn([]),
- gen_deps = spyOn(dependencies, 'generate_dependency_info').andReturn({
- graph:{
- getChain:dependents
- },
- top_level_plugins:[]
- });
done = false;
});
describe('success', function() {
it('should call prepare after a successful uninstall', function() {
runs(function() {
- uninstallPromise(uninstall.uninstallPlatform('android', temp, dummyplugin, plugins_dir, {}));
+ uninstallPromise(uninstall.uninstallPlatform('android', project, dummy_id));
});
- waitsFor(function() { return done; }, 'promise never resolved', 500);
+ waitsFor(function() { return done; }, 'promise never resolved', 200);
runs(function() {
expect(prepare).toHaveBeenCalled();
});
});
it('should call the config-changes module\'s add_uninstalled_plugin_to_prepare_queue method after processing an install', function() {
runs(function() {
- uninstallPromise(uninstall.uninstallPlatform('android', temp, dummyplugin, plugins_dir, {}));
+ uninstallPromise(uninstall.uninstallPlatform('android', project, dummy_id));
});
- waitsFor(function() { return done; }, 'promise never resolved', 500);
- runs(function() {
- expect(add_to_queue).toHaveBeenCalledWith(plugins_dir, 'DummyPlugin', 'android', true);
+ waitsFor(function() { return done; }, 'promise never resolved', 200);
+ runs(function() {
+ expect(add_to_queue).toHaveBeenCalledWith(plugins_install_dir, dummy_id, 'android', true);
});
});
it('should queue up actions as appropriate for that plugin and call process on the action stack', function() {
runs(function() {
- uninstallPromise(uninstall.uninstallPlatform('android', temp, dummyplugin, plugins_dir, {}));
+ uninstallPromise(uninstall.uninstallPlatform('android', project, dummy_id));
});
- waitsFor(function() { return done; }, 'promise never resolved', 500);
+ waitsFor(function() { return done; }, 'promise never resolved', 200);
runs(function() {
expect(actions_push.calls.length).toEqual(5);
- 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() {
- var parseET, emit, danglers, dependents;
+ var emit;
beforeEach(function() {
- emit = spyOn(plugman, 'emit');
- parseET = spyOn(xml_helpers, 'parseElementtreeSync').andReturn({
- _root:{
- attrib:{}
- },
- find:function() { return null },
- findall:function() { return [] }
- });
- danglers = spyOn(dependencies, 'danglers');
- dependents = spyOn(dependencies, 'dependents').andReturn([]);
+ emit = spyOn(events, 'emit');
});
- it('should uninstall "dangling" dependencies', function(done) {
- // TODO: this is a terrible way to do conditional mocking, i am sorry.
- // if you have a better idea on how to mock out this recursive function, plz patch it
- var counter = 0;
- gen_deps.andCallFake(function() {
- var obj;
- if (counter === 0) {
- counter++;
- obj = {
- graph:{
- getChain:function() { return ['one','two'] }
- },
- top_level_plugins:[]
- };
- } else {
- obj = {
- graph:{
- getChain:dependents
- },
- top_level_plugins:[]
- };
- }
- return obj;
+ it('should uninstall "dangling" dependencies', function() {
+ runs(function() {
+ uninstallPromise(uninstall.uninstallPlatform('android', project, 'A'));
+ });
+ waitsFor(function() { return done; }, 'promise never resolved', 200);
+ runs(function() {
+ expect(emit).toHaveBeenCalledWith('log', 'Uninstalling 2 dependent plugins.');
});
-
- danglers.andReturn(['one', 'two']);
-
- uninstall.uninstallPlatform('android', temp, dummyplugin, plugins_dir, {})
- .then(function() {
- expect(emit).toHaveBeenCalledWith('log', 'Uninstalling 2 dangling dependent plugins.');
- }, function(err) {
- expect(err).toBeUndefined();
- }).done(done);
});
});
});
@@ -133,19 +114,18 @@ describe('uninstallPlatform', function() {
describe('failure', function() {
it('should throw if platform is unrecognized', function() {
runs(function() {
- uninstallPromise(uninstall.uninstallPlatform('atari', temp, 'SomePlugin', plugins_dir, {}));
+ uninstallPromise( uninstall.uninstallPlatform('atari', project, 'SomePlugin') );
});
- waitsFor(function() { return done; }, 'promise never resolved', 500);
+ waitsFor(function() { return done; }, 'promise never resolved', 200);
runs(function() {
expect(done).toEqual(new Error('atari not supported.'));
});
});
it('should throw if plugin is missing', function() {
- exists.andReturn(false);
runs(function() {
- uninstallPromise(uninstall.uninstallPlatform('android', temp, 'SomePluginThatDoesntExist', plugins_dir, {}));
+ uninstallPromise( uninstall.uninstallPlatform('android', project, 'SomePluginThatDoesntExist') );
});
- waitsFor(function() { return done; }, 'promise never resolved', 500);
+ waitsFor(function() { return done; }, 'promise never resolved', 200);
runs(function() {
expect(done).toEqual(new Error('Plugin "SomePluginThatDoesntExist" not found. Already uninstalled?'));
});
@@ -153,165 +133,109 @@ describe('uninstallPlatform', function() {
});
});
-describe('uninstallPlugin', function() {
- var exists, get_json, chmod, proc, add_to_queue, prepare, actions_push, c_a, rm, uninstall_plugin, done;
-
- function uninstallPromise(f) {
- return f.then(function() { done = true; }, function(err) { done = err; });
- }
+describe('uninstallPlugin', function() {
+ var rm, fsWrite, rmstack = [];
beforeEach(function() {
- uninstall_plugin = spyOn(uninstall, 'uninstallPlugin').andCallThrough();
- proc = spyOn(actions.prototype, 'process').andReturn(Q());
- actions_push = spyOn(actions.prototype, 'push');
- c_a = spyOn(actions.prototype, 'createAction');
- prepare = spyOn(plugman, 'prepare');
- 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');
+ fsWrite = spyOn(fs, 'writeFileSync').andReturn(true);
+ rm = spyOn(shell, 'rm').andCallFake(function(f,p) { rmstack.push(p); return true});
+ rmstack = [];
done = false;
});
- describe('success', function() {
- it('should remove the plugin directory', function() {
- runs(function() {
- uninstallPromise(uninstall.uninstallPlugin(dummyplugin, plugins_dir));
- });
- waitsFor(function() { return done; }, 'promise never resolved', 500);
- runs(function() {
- expect(rm).toHaveBeenCalled();
- });
- });
- describe('with dependencies', function() {
- var parseET, emit;
- beforeEach(function() {
- emit = spyOn(plugman, 'emit');
- var counter = 0;
- parseET = spyOn(xml_helpers, 'parseElementtreeSync').andCallFake(function(p) {
- return {
- _root:{
- attrib:{}
- },
- find:function() { return null },
- findall:function() {
- if (counter === 0) {
- counter++;
- return [{attrib:{id:'somedependent'}}]
- }
- else return [];
- }
- }
- });
- });
- it('should delete all dangling plugins', function(done) {
- uninstall.uninstallPlugin(dummyplugin, plugins_dir)
- .then(function() {
- expect(rm).toHaveBeenCalledWith('-rf', path.join(plugins_dir, dummyplugin));
- expect(rm).toHaveBeenCalledWith('-rf', path.join(plugins_dir, 'somedependent'));
- expect(rm.calls.length).toBe(2);
- }, function(err) {
- expect(err).toBeUndefined();
- }).fin(done);
- });
- });
- });
+ describe('with dependencies', function() {
- describe('failure', function() {
- it('should throw if plugin is missing', function() {
- exists.andReturn(false);
+ it('should delete all dangling plugins', function() {
+
runs(function() {
- uninstallPromise(uninstall('android', temp, 'SomePluginThatDoesntExist', plugins_dir, {}));
+ uninstallPromise( uninstall.uninstallPlugin('A', plugins_install_dir) );
});
- waitsFor(function() { return done; }, 'promise never resolved', 500);
+ waitsFor(function() { return done; }, 'promise never resolved', 200);
runs(function() {
- expect(done).toEqual(new Error('Plugin "SomePluginThatDoesntExist" not found. Already uninstalled?'));
+ expect(done).toEqual(true);
+ expect(rmstack[0]).toBe( path.join(plugins_install_dir, 'C') );
+ expect(rmstack[1]).toBe( path.join(plugins_install_dir, 'D') );
+ expect(rmstack[2]).toBe( path.join(plugins_install_dir, 'A') );
+ expect(rm.calls.length).toBe(3);
});
});
});
});
describe('uninstall', function() {
- var exists, get_json, chmod, proc, add_to_queue, prepare, actions_push, c_a, rm, done;
-
- function uninstallPromise(f) {
- return f.then(function() { done = true; }, function(err) { done = err; });
- }
+ var fsWrite, rm, add_to_queue;
beforeEach(function() {
- proc = spyOn(actions.prototype, 'process').andReturn(Q());
- actions_push = spyOn(actions.prototype, 'push');
- c_a = spyOn(actions.prototype, 'createAction');
- prepare = spyOn(plugman, 'prepare');
- 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');
+ fsWrite = spyOn(fs, 'writeFileSync').andReturn(true);
+ rm = spyOn(shell, 'rm').andReturn(true);
add_to_queue = spyOn(config_changes, 'add_uninstalled_plugin_to_prepare_queue');
done = false;
});
describe('success', function() {
- it('should call prepare after a successful uninstall', function() {
- runs(function() {
- uninstallPromise(uninstall('android', temp, dummyplugin, plugins_dir, {}));
- });
- waitsFor(function() { return done; }, 'promise never resolved', 500);
- runs(function() {
- expect(prepare).toHaveBeenCalled();
- });
- });
it('should call the config-changes module\'s add_uninstalled_plugin_to_prepare_queue method after processing an install', function() {
runs(function() {
- uninstallPromise(uninstall('android', temp, dummyplugin, plugins_dir, {}));
+ uninstallPromise( uninstall('android', project, plugins['DummyPlugin']) );
});
- waitsFor(function() { return done; }, 'promise never resolved', 500);
+ waitsFor(function() { return done; }, 'promise never resolved', 200);
runs(function() {
- expect(add_to_queue).toHaveBeenCalledWith(plugins_dir, 'DummyPlugin', 'android', true);
+ expect(add_to_queue).toHaveBeenCalledWith(plugins_install_dir, dummy_id, 'android', true);
});
});
- it('should queue up actions as appropriate for that plugin and call process on the action stack', function() {
- runs(function() {
- uninstallPromise(uninstall('android', temp, dummyplugin, plugins_dir, {}));
- });
- waitsFor(function() { return done; }, 'promise never resolved', 500);
- runs(function() {
- expect(actions_push.calls.length).toEqual(5);
- 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 "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() {
runs(function() {
- uninstallPromise(uninstall('atari', temp, 'SomePlugin', plugins_dir, {}));
+ uninstallPromise(uninstall('atari', project, 'SomePlugin'));
});
- waitsFor(function() { return done; }, 'promise never resolved', 500);
+ waitsFor(function() { return done; }, 'promise never resolved', 200);
runs(function() {
expect(done).toEqual(new Error('atari not supported.'));
});
});
it('should throw if plugin is missing', function() {
- exists.andReturn(false);
runs(function() {
- uninstallPromise(uninstall('android', temp, 'SomePluginThatDoesntExist', plugins_dir, {}));
+ uninstallPromise(uninstall('android', project, 'SomePluginThatDoesntExist'));
});
- waitsFor(function() { return done; }, 'promise never resolved', 500);
+ waitsFor(function() { return done; }, 'promise never resolved', 200);
runs(function() {
expect(done).toEqual(new Error('Plugin "SomePluginThatDoesntExist" not found. Already uninstalled?'));
});
});
});
});
+
+describe('end', function() {
+
+ it('end', function() {
+ done = false;
+ var finish = function(err){
+ if(err)
+ plugman.emit('error', err);
+
+ shell.rm('-rf', project);
+ done = true;
+ }
+
+ promise.then(
+ function(){
+ return uninstall('android', project, plugins['DummyPlugin'])
+ }
+ ).then(
+ function(){
+ // Fails... A depends on
+ return uninstall('android', project, plugins['C'])
+ }
+ ).fail(
+ function(err) {
+ expect(err.message).toBe("The plugin 'C' is required by (A), skipping uninstallation.");
+ }
+ ).then(
+ function(){
+ // dependencies on C,D ... should this only work with --recursive? prompt user..?
+ return uninstall('android', project, plugins['A'])
+ }
+ ).fin(finish);
+
+ waitsFor(function() { return done; }, 'promise never resolved', 500);
+ });
+});
http://git-wip-us.apache.org/repos/asf/cordova-plugman/blob/ae2ce7ac/src/events.js
----------------------------------------------------------------------
diff --git a/src/events.js b/src/events.js
index 1cec85a..ed78839 100644
--- a/src/events.js
+++ b/src/events.js
@@ -1,3 +1,2 @@
-var emitter = new (require('events').EventEmitter)();
-module.exports = emitter;
+module.exports = new (require('events').EventEmitter)();