tsm
Version:
A manager for Titanium SDK versions
613 lines (533 loc) • 19 kB
JavaScript
var EventEmitter = require("events").EventEmitter;
// simple child process mocking
var oldspawn = require('child_process').spawn;
var spawned;
var spawnerror = false;
require('child_process').spawn = function (name, args) {
spawned = {name: name, args: args};
var emitter = new EventEmitter();
emitter.stdout = {pipe: function () {}};
emitter.stderr = {pipe: function () {}};
process.nextTick(function () {
if (spawnerror) emitter.emit('end', 127);
else emitter.emit('exit', 0);
});
return emitter;
};
var assert = require("assert");
var tsm;
if (process.env.TSM_COV) tsm = require("./lib-cov/index");
else tsm = require('./index');
var path = require('path');
var EventEmitter = require('events').EventEmitter;
var nock = require('nock');
var fs = require('fs');
if (!fs.exists) fs.exists = path.exists;
var ncp = require('ncp').ncp;
var rimraf = require('rimraf');
function assertInPath (expected, actual) {
// fix slashes in path for windows
if (process.platform === 'win32')
expected = expected.split('/').join('\\');
assert(actual.indexOf(expected) != -1, "correct path");
}
suite('gitCheck', function () {
var hash0 = "e721f1820f65368794aee29e2da8ebc03de804fd";
var hash1 = "e2a219dceb3026e73be3afab4591aa3ba627d58b";
test('matches properly', function () {
assert(tsm.gitCheck('e721', hash0) === true);
assert(tsm.gitCheck('e2', hash0) === false);
assert(tsm.gitCheck('e2', hash1) === true);
});
test('handles invalid input', function () {
assert(tsm.gitCheck(false, hash0) === false);
assert(tsm.gitCheck('1', hash0) === false);
assert(tsm.gitCheck({}, hash0) === false);
});
});
suite('getBranches', function () {
test('functional', function (done) {
var scope = nock('http://builds.appcelerator.com.s3.amazonaws.com')
.get('/mobile/branches.json')
.replyWithFile(200, __dirname + "/fixtures/branches.json");
tsm.getBranches(function (error, data) {
try {
assert(error === null);
assert(Array.isArray(data));
assert(data.indexOf('master') != -1, "master is in branch list");
assert(data.indexOf('2_0_X') != -1, "2_0_X is in branch list");
scope.done();
done();
} catch (e) { done(e); }
});
});
test('handles 404', function (done) {
var scope = nock('http://builds.appcelerator.com.s3.amazonaws.com')
.get('/mobile/branches.json')
.reply(404);
tsm.getBranches(function (error, data) {
try {
assert(error instanceof Error, "got error");
scope.done();
done();
} catch (e) { done(e); }
});
});
});
suite('parseDate', function () {
var date0 = "20120827132447";
test('parses properly', function () {
var date = tsm.parseDate(date0);
// Months are zero indexed so we're really testing if the month is August
assert(date.getMonth() === 7, "check month");
assert(date.getDate() === 27, "check day of month");
assert(date.getHours() === 13, "check hour");
});
});
suite('parseBuildList', function () {
var list = [
{
"sha1": "2a60ec1e0b693047e6fa9112fe93944c5432a3c3",
"build_url": "http://jenkins.appcelerator.org/job/titanium_mobile_master/1277/",
"git_revision": "c63b0d947da94e2cdfb3fb06e95106cb803c3f22",
"filename": "mobilesdk-2.1.0.v20120827132447-win32.zip",
"git_branch": "master",
"build_type": "mobile",
"size": 26961576
},
{
"sha1": "7be939ed222691536b8b32ad0782e4729a9d447c",
"build_url": "http://jenkins.appcelerator.org/job/titanium_mobile_master/1278/",
"git_revision": "79e9c73d5070fc4306d37bc1cf8cbabb2ad67ae8",
"filename": "mobilesdk-2.2.0.v20120827143312-osx.zip",
"git_branch": "master",
"build_type": "mobile",
"size": 80186079
},
{
"sha1": "a0c6a6cbf334752cf9e2f23be37ee11e63af0a1b",
"build_url": "http://jenkins.appcelerator.org/job/titanium_mobile_master/1278/",
"git_revision": "79e9c73d5070fc4306d37bc1cf8cbabb2ad67ae8",
"filename": "mobilesdk-2.2.0.v20120827143312-linux.zip",
"git_branch": "master",
"build_type": "mobile",
"size": 26960385
},
{
"sha1": "92ffcd16454b38d8eca203a4b36352ec140504ec",
"build_url": "http://jenkins.appcelerator.org/job/titanium_mobile_master/1278/",
"git_revision": "79e9c73d5070fc4306d37bc1cf8cbabb2ad67ae8",
"filename": "mobilesdk-2.2.0.v20120827143312-win32.zip",
"git_branch": "master",
"build_type": "mobile",
"size": 26961706
}
];
test('parses out correct os', function () {
var ret = tsm.parseBuildList(null, 'osx', list);
ret.forEach(function (build) {
assert(build.filename.indexOf('win32') === -1, 'no win32 buids');
assert(build.filename.indexOf('linux') === -1, 'no linux buids');
});
});
test('matches git hashes properly', function () {
var ret = tsm.parseBuildList('79e9', 'linux', list);
assert(ret.length === 1, "only one result");
assert(
ret[0].sha1 === "a0c6a6cbf334752cf9e2f23be37ee11e63af0a1b",
"correct build matched"
);
});
test('matches versions properly', function () {
var ret = tsm.parseBuildList('2.1', 'win32', list);
assert(ret.length === 1, "only one result");
assert(
ret[0].sha1 === "2a60ec1e0b693047e6fa9112fe93944c5432a3c3",
"correct build matched"
);
});
test('handles malformed filename', function () {
var malformed = [
{
"sha1": "a0c6a6cbf334752cf9e2f23be37ee11e63af0a1b",
"build_url": "http://jenkins.appcelerator.org/job/titanium_mobile_master/1278/",
"git_revision": "79e9c73d5070fc4306d37bc1cf8cbabb2ad67ae8",
"filename": "mobilessdf92jfjlllssssssssssfoooooo312-linux.zip",
"git_branch": "master",
"build_type": "mobile",
"size": 26960385
}
];
// just want to make sure it doesn't throw
var ret = tsm.parseBuildList(undefined, 'linux', malformed);
});
});
suite('getBuilds', function () {
test('functional: retrieval of branch master', function (done) {
var scope = nock('http://builds.appcelerator.com.s3.amazonaws.com')
.get('/mobile/master/index.json')
.replyWithFile(200, __dirname + '/fixtures/master-index.json');
tsm.getBuilds('master', function (error, data) {
try {
assert(error === null, "no error");
assert(Array.isArray(data), "return is an array");
assert(data.length > 4, "more than 4 items");
data.forEach(function (item) {
assert(item.sha1);
assert(item.filename);
assert(typeof item.size === "number");
});
done();
} catch (e) { done(e); }
});
});
test('handles 404', function (done) {
var scope = nock('http://builds.appcelerator.com.s3.amazonaws.com')
.get('/mobile/master/index.json')
.reply(404);
tsm.getBuilds('master', function (error, data) {
try {
assert(error instanceof Error, "expected error");
scope.done();
done();
} catch (e) { done(e); }
});
});
test('handles malformed json', function (done) {
var scope = nock('http://builds.appcelerator.com.s3.amazonaws.com')
.get('/mobile/master/index.json')
.reply(200, "}}}[[[[");
tsm.getBuilds('master', function (error, data) {
try {
assert(error instanceof SyntaxError, "expected error");
scope.done();
done();
} catch (e) { done(e); }
});
});
});
suite('getAllBuilds', function () {
test('functional', function (done) {
var scope = nock('http://builds.appcelerator.com.s3.amazonaws.com')
.get('/mobile/branches.json')
.replyWithFile(200, __dirname + "/fixtures/branches.json");
var branches = ["master", "1_4_X", "1_5_X", "1_6_X", "1_7_X", "1_8_X", "2_0_X", "2_1_X"];
branches.forEach(function (b) {
scope
.get('/mobile/'+b+'/index.json')
.replyWithFile(200, __dirname + '/fixtures/' + b + '-index.json');
});
tsm.getAllBuilds('2.1.x', 'linux', function (error, builds) {
try {
if (error) throw error;
assert(Array.isArray(builds), "return is an array");
assert(builds.length > 4, "more than 4 items");
for (var i = 0; i < builds.length; i++) {
var b = builds[i];
assert(b.sha1, "build has a sha1");
assert(b.date.getDay, "build has a date object");
assert(b.zip, "build has a zip url");
}
scope.done();
done();
} catch (e) { done(e); }
});
});
});
suite('parseVersionFile', function () {
var file = "version=2.1.0\n"+
"module_apiversion=2\n"+
"timestamp=05/02/12 14:18\n" +
"githash=cde5b27";
var malformedFile = "======asdf== ==\n\n\nsfoij3===!!!!!!!";
test('parses', function () {
var res = tsm.parseVersionFile(file);
assert(res.version === '2.1.0');
assert(res.timestamp === '05/02/12 14:18');
assert(res.githash === 'cde5b27');
});
test("doesn't die with invalid input", function () {
var res = tsm.parseVersionFile(malformedFile);
});
});
suite('examineDir', function () {
test('handles a dir w/o version.txt', function (done) {
var dir = path.join(__dirname, 'fixtures', '1', '.DS_Store');
tsm.examineDir(dir, function (error, data) {
if (error instanceof Error) done();
else done(new Error("expected error"));
});
});
test('handles dir with malformed version.txt', function (done) {
var dir = path.join(__dirname, 'fixtures', '1', 'malformed');
tsm.examineDir(dir, function (error, data) {
if (error instanceof SyntaxError) done();
else done(new Error("expected error"));
});
});
test('handles a valid dir', function (done) {
var dir = path.join(__dirname, 'fixtures', '1', '2.1.0');
tsm.examineDir(dir, function (error, data) {
try {
if (error) throw error;
assert(data.version === '2.1.0');
assert(data.timestamp === '05/02/12 14:18');
assert(data.githash === 'cde5b27');
done();
} catch (e) { done(e); }
});
});
});
suite('findInstalled', function () {
test('functional', function (done) {
var dir = path.join(__dirname, 'fixtures', '1');
var e = tsm.findInstalled(dir, undefined, function (error, builds) {
try {
assert(builds.length === 2, "2 results");
assert(builds[1].version === '2.1.0');
assert(builds[1].githash === 'cde5b27');
assert(builds[1].date.getDay, "has a valid date object");
done();
} catch (e) { done(e); }
});
assert(e instanceof EventEmitter);
});
});
suite('mergeBuilds', function () {
var installed = [
{ githash: '0a43607',
version: '2.1.1',
date: new Date("Tue, 17 Jul 2012 02:46:00 GMT") },
{ githash: 'c63b0d9',
version: '2.1.0',
date: new Date("Fri, 27 Jul 2012 18:01:00 GMT") },
{ githash: '61078b0',
version: '2.2.0',
date: new Date("Fri, 10 Aug 2012 23:41:00 GMT") }
];
var available = [
{
"sha1": "2a60ec1e0b693047e6fa9112fe93944c5432a3c3",
"build_url": "http://jenkins.appcelerator.org/job/titanium_mobile_master/1277/",
"git_revision": "c63b0d947da94e2cdfb3fb06e95106cb803c3f22",
"githash": "c63b0d9",
"filename": "mobilesdk-2.1.0.v20120827132447-osx.zip",
"git_branch": "master",
"build_type": "mobile",
"date": new Date(),
"size": 26961576
},
{
"sha1": "7be939ed222691536b8b32ad0782e4729a9d447c",
"build_url": "http://jenkins.appcelerator.org/job/titanium_mobile_master/1278/",
"git_revision": "79e9c73d5070fc4306d37bc1cf8cbabb2ad67ae8",
"githash": "79e9c73",
"filename": "mobilesdk-2.2.0.v20120827143312-osx.zip",
"git_branch": "master",
"build_type": "mobile",
"date": new Date(),
"size": 80186079
},
{
"sha1": "a0c6a6cbf334752cf9e2f23be37ee11e63af0a1b",
"build_url": "http://jenkins.appcelerator.org/job/titanium_mobile_master/1278/",
"git_revision": "39bc239d5070fc4306d37bc1cf8cbabb2ad67ae8",
"githash": "39bc239",
"filename": "mobilesdk-2.2.0.v20120827143312-osx.zip",
"git_branch": "master",
"build_type": "mobile",
"date": new Date(),
"size": 26960385
}
];
test('merges correctly', function () {
var merged = tsm.mergeBuilds(available, installed);
assert(merged.length === 5);
// pull out the one that should've been merged
var mergedEntry = merged.filter(function (item) {
return item.githash === 'c63b0d9';
});
assert(mergedEntry.length === 1);
assert(mergedEntry[0].installed === true, "merged entry marked as installed");
var availableEntry = merged.filter(function (item) {
return item.githash === '79e9c73';
});
assert(availableEntry.length === 1);
assert(availableEntry[0].installed === false);
// pull out the one that should've been merged
var installedEntry = merged.filter(function (item) {
return item.githash === '61078b0';
});
assert(installedEntry.length === 1);
assert(installedEntry[0].installed === true);
});
});
suite('list', function () {
test('handles missing / malformed os parameter', function () {
try {
tsm.list({available: true, os: 7}, function (error, data) {});
} catch (e) {
assert(e instanceof TypeError);
}
try {
tsm.list({available: true}, function (error, data) {});
} catch (e) {
assert(e instanceof TypeError);
}
});
test('functional: installed', function (done) {
tsm.list({
installed: true,
os: 'osx',
dir: __dirname + "/fixtures/1"
}, function (error, data) {
try {
assert(error === null);
assert(data.length === 2);
assert(data[1].githash === 'cde5b27');
assert(data[1].installed === true);
assert(data[0].installed === true);
done();
} catch (e) { done(e); }
});
});
test('functional: installed and available, input: 2', function (done) {
var scope = nock('http://builds.appcelerator.com.s3.amazonaws.com')
.get('/mobile/branches.json')
.replyWithFile(200, __dirname + "/fixtures/branches.json");
var branches = ["master", "1_4_X", "1_5_X", "1_6_X", "1_7_X", "1_8_X", "2_0_X", "2_1_X"];
branches.forEach(function (b) {
scope
.get('/mobile/'+b+'/index.json')
.replyWithFile(200, __dirname + '/fixtures/' + b + '-index.json');
});
tsm.list({
installed: true,
available: true,
os: 'osx',
dir: __dirname + "/fixtures/1",
input: '2'
}, function (error, data) {
try {
assert(error === null, "no error");
var installed = data.filter(function (item) {
return item.installed;
});
assert(installed.length > 0, "at least one marked as installed");
data.forEach(function (item) {
assert(item.installed !== undefined);
assert(item.githash);
assert(typeof item.date.getDay === 'function');
});
scope.done();
done();
} catch (e) { done(e); }
});
});
test('handles missing / malformed dir argument', function () {
try {
tsm.list({os: 'osx', installed: true}, function (error, data) {});
} catch (e) {
assert(e instanceof TypeError);
}
try {
tsm.list({os: 'osx', installed: true, dir: 7}, function (error, data) {});
} catch (e) {
assert(e instanceof TypeError);
}
});
});
suite('unzip', function () {
test('extracts', function (done) {
var zip = path.join(__dirname, "fixtures", "test.zip");
var output = path.join(__dirname, "fixtures", "2");
var p = path.join(__dirname, "fixtures", "2", "index.js");
tsm.unzip(zip, output, function (error) {
if (error) done(error);
fs.exists(p, function (exists) {
if (!exists) done(new Error("file was not extracted properly"));
fs.unlink(p, done);
});
});
});
});
suite('install', function () {
test('functional', function (done) {
var zip = __dirname + "/fixtures/test.zip";
var output = __dirname + "/fixtures/2/";
var zipurl = "/mobile/master/mobilesdk-2.2.0.v20120828153312-osx.zip";
var path = __dirname + "/1.8.2/version.txt";
var scope = nock('http://builds.appcelerator.com.s3.amazonaws.com')
.get('/mobile/branches.json')
.replyWithFile(200, __dirname + "/fixtures/branches-simple.json")
.get('/mobile/master/index.json')
.replyWithFile(200, __dirname + '/fixtures/master-index.json')
.get(zipurl)
.replyWithFile(200, __dirname + '/fixtures/sdk.zip');
var emitter = tsm.install({
dir: output,
input: '2',
os: 'osx'
}, function (error) {
try {
assert(error === null);
scope.done();
fs.exists(path, function (exists) {
if (!exists) done(new Error("file was not extracted properly"));
rimraf(__dirname + "/1.8.2", done);
});
} catch (e) { done(e); }
});
});
});
suite('delete', function () {
test('functional', function (done) {
var src = __dirname + "/fixtures/1/";
var sdkdir = __dirname + "/fixtures/2";
ncp(src, sdkdir, function (err) {
if (err) return done(err);
tsm.remove({dir: sdkdir, input: "1.8.x"}, function (error) {
if (error) return done(error);
fs.exists(sdkdir + "/1.8.2", function (exists) {
if (exists) return done(new Error("sdk wasn't deleted"));
fs.exists(sdkdir + "/2.1.0", function (exists) {
if (!exists) return done(new Error("wrong sdk deleted"));
rimraf(sdkdir, function () {
fs.mkdir(sdkdir, done);
});
});
});
});
});
});
});
suite('builder', function () {
test('functional: iphone', function (done) {
var sdkdir = __dirname + "/fixtures/1/";
var options = {dir: sdkdir, input: '1.8', os: 'iphone', args: ['foo', 'bar']};
tsm.builder(options, function (error) {
try {
assert(spawned.name === "python");
assertInPath("fixtures/1/1.8.2/iphone/builder.py", spawned.args[0]);
assert(spawned.args[1] === "foo");
assert(spawned.args[2] === "bar");
done();
} catch (e) { done(e); }
});
});
});
suite('titanium', function () {
test('functional', function (done) {
var sdkdir = __dirname + "/fixtures/1/";
var options = {dir: sdkdir, input: '1.8', args: ['foo', 'bar']};
tsm.titanium(options, function (error) {
try {
assert(spawned.name === "python");
assertInPath("fixtures/1/1.8.2/titanium.py", spawned.args[0]);
assert(spawned.args[1] === "foo");
assert(spawned.args[2] === "bar");
done();
} catch (e) { done(e); }
});
});
});