diff options
Diffstat (limited to 'tools/addon-sdk-1.4/packages/api-utils/tests')
90 files changed, 9284 insertions, 0 deletions
diff --git a/tools/addon-sdk-1.4/packages/api-utils/tests/commonjs-test-adapter/asserts.js b/tools/addon-sdk-1.4/packages/api-utils/tests/commonjs-test-adapter/asserts.js new file mode 100644 index 0000000..83abd23 --- /dev/null +++ b/tools/addon-sdk-1.4/packages/api-utils/tests/commonjs-test-adapter/asserts.js @@ -0,0 +1,50 @@ +"use strict"; + +const AssertBase = require("test/assert").Assert; + +/** + * Generates custom assertion constructors that may be bundled with a test + * suite. + * @params {String} + * names of assertion function to be added to the generated Assert. + */ +function Assert() { + let assertDescriptor = {}; + Array.forEach(arguments, function(name) { + assertDescriptor[name] = { value: function(message) { + this.pass(message); + }} + }); + + return function Assert() { + return Object.create(AssertBase.apply(null, arguments), assertDescriptor); + }; +} + +exports["test suite"] = { + Assert: Assert("foo"), + "test that custom assertor is passed to test function": function(assert) { + assert.ok("foo" in assert, "custom assertion function `foo` is defined"); + assert.foo("custom assertion function `foo` is called"); + }, + "test sub suite": { + "test that `Assert` is inherited by sub suits": function(assert) { + assert.ok("foo" in assert, "assertion function `foo` is not defined"); + }, + "test sub sub suite": { + Assert: Assert("bar"), + "test that custom assertor is passed to test function": function(assert) { + assert.ok("bar" in assert, + "custom assertion function `bar` is defined"); + assert.bar("custom assertion function `bar` is called"); + }, + "test that `Assert` is not inherited by sub sub suits": function(assert) { + assert.ok(!("foo" in assert), + "assertion function `foo` is not defined"); + } + } + } +}; + +if (module == require.main) + require("test").run(exports); diff --git a/tools/addon-sdk-1.4/packages/api-utils/tests/fixtures/es5.js b/tools/addon-sdk-1.4/packages/api-utils/tests/fixtures/es5.js new file mode 100644 index 0000000..6e5b1ea --- /dev/null +++ b/tools/addon-sdk-1.4/packages/api-utils/tests/fixtures/es5.js @@ -0,0 +1,4 @@ +"use strict"; +exports.frozen = Object.freeze({}); +exports.sealed = Object.seal({}); +exports.inextensible = Object.preventExtensions({}); diff --git a/tools/addon-sdk-1.4/packages/api-utils/tests/helpers.js b/tools/addon-sdk-1.4/packages/api-utils/tests/helpers.js new file mode 100644 index 0000000..bbfe911 --- /dev/null +++ b/tools/addon-sdk-1.4/packages/api-utils/tests/helpers.js @@ -0,0 +1,19 @@ +"use strict"; + +const { Loader } = require("@loader"); + +exports.Loader = function(module, globals) { + var options = JSON.parse(JSON.stringify(require("@packaging"))); + options.globals = globals; + let loader = Loader.new(options); + return Object.create(loader, { + require: { value: Loader.require.bind(loader, module.path) }, + sandbox: { value: function sandbox(id) { + let path = options.manifest[module.path].requirements[id].path; + return loader.sandboxes[path].sandbox; + }}, + unload: { value: function unload(reason, callback) { + loader.unload(reason, callback); + }} + }) +}; diff --git a/tools/addon-sdk-1.4/packages/api-utils/tests/loader/fixture.js b/tools/addon-sdk-1.4/packages/api-utils/tests/loader/fixture.js new file mode 100644 index 0000000..d097dea --- /dev/null +++ b/tools/addon-sdk-1.4/packages/api-utils/tests/loader/fixture.js @@ -0,0 +1,2 @@ +exports.foo = foo; +console.log('testing', 1, [2, 3, 4]); diff --git a/tools/addon-sdk-1.4/packages/api-utils/tests/modules/add.js b/tools/addon-sdk-1.4/packages/api-utils/tests/modules/add.js new file mode 100644 index 0000000..5825e08 --- /dev/null +++ b/tools/addon-sdk-1.4/packages/api-utils/tests/modules/add.js @@ -0,0 +1,5 @@ +define('modules/add', function () { + return function (a, b) { + return a + b; + }; +}); diff --git a/tools/addon-sdk-1.4/packages/api-utils/tests/modules/async1.js b/tools/addon-sdk-1.4/packages/api-utils/tests/modules/async1.js new file mode 100644 index 0000000..cb51500 --- /dev/null +++ b/tools/addon-sdk-1.4/packages/api-utils/tests/modules/async1.js @@ -0,0 +1,10 @@ +define(['./traditional2', './async2'], function () { + var traditional2 = require('./traditional2'); + return { + name: 'async1', + traditional1Name: traditional2.traditional1Name, + traditional2Name: traditional2.name, + async2Name: require('./async2').name, + async2Traditional2Name: require('./async2').traditional2Name + }; +}); diff --git a/tools/addon-sdk-1.4/packages/api-utils/tests/modules/async2.js b/tools/addon-sdk-1.4/packages/api-utils/tests/modules/async2.js new file mode 100644 index 0000000..c0281e5 --- /dev/null +++ b/tools/addon-sdk-1.4/packages/api-utils/tests/modules/async2.js @@ -0,0 +1,4 @@ +define(['./traditional2', 'exports'], function (traditional2, exports) { + exports.name = 'async2'; + exports.traditional2Name = traditional2.name; +}); diff --git a/tools/addon-sdk-1.4/packages/api-utils/tests/modules/badExportAndReturn.js b/tools/addon-sdk-1.4/packages/api-utils/tests/modules/badExportAndReturn.js new file mode 100644 index 0000000..0844be1 --- /dev/null +++ b/tools/addon-sdk-1.4/packages/api-utils/tests/modules/badExportAndReturn.js @@ -0,0 +1,6 @@ +// This is a bad module, it asks for exports but also returns a value from +// the define defintion function. +define(['exports'], function (exports) { + return 'badExportAndReturn'; +}); + diff --git a/tools/addon-sdk-1.4/packages/api-utils/tests/modules/badFirst.js b/tools/addon-sdk-1.4/packages/api-utils/tests/modules/badFirst.js new file mode 100644 index 0000000..c3e4c36 --- /dev/null +++ b/tools/addon-sdk-1.4/packages/api-utils/tests/modules/badFirst.js @@ -0,0 +1,5 @@ +define(['./badSecond'], function (badSecond) { + return { + name: 'badFirst' + }; +}); diff --git a/tools/addon-sdk-1.4/packages/api-utils/tests/modules/badSecond.js b/tools/addon-sdk-1.4/packages/api-utils/tests/modules/badSecond.js new file mode 100644 index 0000000..213c7b8 --- /dev/null +++ b/tools/addon-sdk-1.4/packages/api-utils/tests/modules/badSecond.js @@ -0,0 +1,4 @@ +var first = require('./badFirst'); + +exports.name = 'badSecond'; +exports.badFirstName = first.name; diff --git a/tools/addon-sdk-1.4/packages/api-utils/tests/modules/blue.js b/tools/addon-sdk-1.4/packages/api-utils/tests/modules/blue.js new file mode 100644 index 0000000..af3a193 --- /dev/null +++ b/tools/addon-sdk-1.4/packages/api-utils/tests/modules/blue.js @@ -0,0 +1,5 @@ +define(function () { + return { + name: 'blue' + }; +}); diff --git a/tools/addon-sdk-1.4/packages/api-utils/tests/modules/castor.js b/tools/addon-sdk-1.4/packages/api-utils/tests/modules/castor.js new file mode 100644 index 0000000..c2d40b7 --- /dev/null +++ b/tools/addon-sdk-1.4/packages/api-utils/tests/modules/castor.js @@ -0,0 +1,6 @@ +define(['exports', './pollux'], function(exports, pollux) { + exports.name = 'castor'; + exports.getPolluxName = function () { + return pollux.name; + }; +}); diff --git a/tools/addon-sdk-1.4/packages/api-utils/tests/modules/cheetah.js b/tools/addon-sdk-1.4/packages/api-utils/tests/modules/cheetah.js new file mode 100644 index 0000000..ad24e3a --- /dev/null +++ b/tools/addon-sdk-1.4/packages/api-utils/tests/modules/cheetah.js @@ -0,0 +1,5 @@ +define(function () { + return function () { + return 'cheetah'; + }; +}); diff --git a/tools/addon-sdk-1.4/packages/api-utils/tests/modules/color.js b/tools/addon-sdk-1.4/packages/api-utils/tests/modules/color.js new file mode 100644 index 0000000..e1fe374 --- /dev/null +++ b/tools/addon-sdk-1.4/packages/api-utils/tests/modules/color.js @@ -0,0 +1,3 @@ +define({ + type: 'color' +}); diff --git a/tools/addon-sdk-1.4/packages/api-utils/tests/modules/dupe.js b/tools/addon-sdk-1.4/packages/api-utils/tests/modules/dupe.js new file mode 100644 index 0000000..f5ce8c9 --- /dev/null +++ b/tools/addon-sdk-1.4/packages/api-utils/tests/modules/dupe.js @@ -0,0 +1,11 @@ +define({ + name: 'dupe' +}); + +// This is wrong and should not be allowed. Only one call to +// define per file. +define([], function () { + return { + name: 'dupe2' + }; +}); diff --git a/tools/addon-sdk-1.4/packages/api-utils/tests/modules/dupeNested.js b/tools/addon-sdk-1.4/packages/api-utils/tests/modules/dupeNested.js new file mode 100644 index 0000000..85ecb8d --- /dev/null +++ b/tools/addon-sdk-1.4/packages/api-utils/tests/modules/dupeNested.js @@ -0,0 +1,11 @@ + +define(function () { + // This is wrong and should not be allowed. + define('dupeNested2', { + name: 'dupeNested2' + }); + + return { + name: 'dupeNested' + }; +}); diff --git a/tools/addon-sdk-1.4/packages/api-utils/tests/modules/dupeSetExports.js b/tools/addon-sdk-1.4/packages/api-utils/tests/modules/dupeSetExports.js new file mode 100644 index 0000000..8ad3417 --- /dev/null +++ b/tools/addon-sdk-1.4/packages/api-utils/tests/modules/dupeSetExports.js @@ -0,0 +1,4 @@ +define({name: "dupeSetExports"}); + +// so this should cause a failure +module.setExports("no no no"); diff --git a/tools/addon-sdk-1.4/packages/api-utils/tests/modules/exportsEquals.js b/tools/addon-sdk-1.4/packages/api-utils/tests/modules/exportsEquals.js new file mode 100644 index 0000000..a9bbdd8 --- /dev/null +++ b/tools/addon-sdk-1.4/packages/api-utils/tests/modules/exportsEquals.js @@ -0,0 +1 @@ +module.exports = 4; diff --git a/tools/addon-sdk-1.4/packages/api-utils/tests/modules/green.js b/tools/addon-sdk-1.4/packages/api-utils/tests/modules/green.js new file mode 100644 index 0000000..8bca33c --- /dev/null +++ b/tools/addon-sdk-1.4/packages/api-utils/tests/modules/green.js @@ -0,0 +1,6 @@ +define('modules/green', ['./color'], function (color) { + return { + name: 'green', + parentType: color.type + }; +}); diff --git a/tools/addon-sdk-1.4/packages/api-utils/tests/modules/lion.js b/tools/addon-sdk-1.4/packages/api-utils/tests/modules/lion.js new file mode 100644 index 0000000..f3962c1 --- /dev/null +++ b/tools/addon-sdk-1.4/packages/api-utils/tests/modules/lion.js @@ -0,0 +1,3 @@ +define(function(require) { + return 'lion'; +}); diff --git a/tools/addon-sdk-1.4/packages/api-utils/tests/modules/orange.js b/tools/addon-sdk-1.4/packages/api-utils/tests/modules/orange.js new file mode 100644 index 0000000..d983a35 --- /dev/null +++ b/tools/addon-sdk-1.4/packages/api-utils/tests/modules/orange.js @@ -0,0 +1,6 @@ +define(['./color'], function (color) { + return { + name: 'orange', + parentType: color.type + }; +}); diff --git a/tools/addon-sdk-1.4/packages/api-utils/tests/modules/pollux.js b/tools/addon-sdk-1.4/packages/api-utils/tests/modules/pollux.js new file mode 100644 index 0000000..e49370b --- /dev/null +++ b/tools/addon-sdk-1.4/packages/api-utils/tests/modules/pollux.js @@ -0,0 +1,6 @@ +define(['exports', './castor'], function(exports, castor) { + exports.name = 'pollux'; + exports.getCastorName = function () { + return castor.name; + }; +}); diff --git a/tools/addon-sdk-1.4/packages/api-utils/tests/modules/red.js b/tools/addon-sdk-1.4/packages/api-utils/tests/modules/red.js new file mode 100644 index 0000000..eb58660 --- /dev/null +++ b/tools/addon-sdk-1.4/packages/api-utils/tests/modules/red.js @@ -0,0 +1,12 @@ +define(function (require) { + // comment fake-outs for require finding. + // require('bad1'); + return { + name: 'red', + parentType: require('./color').type + }; + + /* + require('bad2'); + */ +}); diff --git a/tools/addon-sdk-1.4/packages/api-utils/tests/modules/setExports.js b/tools/addon-sdk-1.4/packages/api-utils/tests/modules/setExports.js new file mode 100644 index 0000000..290a3cb --- /dev/null +++ b/tools/addon-sdk-1.4/packages/api-utils/tests/modules/setExports.js @@ -0,0 +1 @@ +module.setExports(5); diff --git a/tools/addon-sdk-1.4/packages/api-utils/tests/modules/subtract.js b/tools/addon-sdk-1.4/packages/api-utils/tests/modules/subtract.js new file mode 100644 index 0000000..2743132 --- /dev/null +++ b/tools/addon-sdk-1.4/packages/api-utils/tests/modules/subtract.js @@ -0,0 +1,5 @@ +define(function () { + return function (a, b) { + return a - b; + } +}); diff --git a/tools/addon-sdk-1.4/packages/api-utils/tests/modules/tiger.js b/tools/addon-sdk-1.4/packages/api-utils/tests/modules/tiger.js new file mode 100644 index 0000000..9a98b76 --- /dev/null +++ b/tools/addon-sdk-1.4/packages/api-utils/tests/modules/tiger.js @@ -0,0 +1,4 @@ +define(function (require, exports) { + exports.name = 'tiger'; + exports.type = require('modules/types/cat').type; +}); diff --git a/tools/addon-sdk-1.4/packages/api-utils/tests/modules/traditional1.js b/tools/addon-sdk-1.4/packages/api-utils/tests/modules/traditional1.js new file mode 100644 index 0000000..d2e720d --- /dev/null +++ b/tools/addon-sdk-1.4/packages/api-utils/tests/modules/traditional1.js @@ -0,0 +1,8 @@ +exports.name = 'traditional1' + +var async1 = require('./async1'); + +exports.traditional2Name = async1.traditional2Name; +exports.traditional1Name = async1.traditional1Name; +exports.async2Name = async1.async2Name; +exports.async2Traditional2Name = async1.async2Traditional2Name; diff --git a/tools/addon-sdk-1.4/packages/api-utils/tests/modules/traditional2.js b/tools/addon-sdk-1.4/packages/api-utils/tests/modules/traditional2.js new file mode 100644 index 0000000..8363404 --- /dev/null +++ b/tools/addon-sdk-1.4/packages/api-utils/tests/modules/traditional2.js @@ -0,0 +1,2 @@ +exports.name = 'traditional2'; +exports.traditional1Name = require('./traditional1').name; diff --git a/tools/addon-sdk-1.4/packages/api-utils/tests/modules/types/cat.js b/tools/addon-sdk-1.4/packages/api-utils/tests/modules/types/cat.js new file mode 100644 index 0000000..24a1c59 --- /dev/null +++ b/tools/addon-sdk-1.4/packages/api-utils/tests/modules/types/cat.js @@ -0,0 +1 @@ +exports.type = 'cat'; diff --git a/tools/addon-sdk-1.4/packages/api-utils/tests/test-api-utils.js b/tools/addon-sdk-1.4/packages/api-utils/tests/test-api-utils.js new file mode 100644 index 0000000..fc072bb --- /dev/null +++ b/tools/addon-sdk-1.4/packages/api-utils/tests/test-api-utils.js @@ -0,0 +1,274 @@ +/* -*- Mode: Java; tab-width: 2; indent-tabs-mode: nil; c-basic-offset: 2 -*- */ +/* vim:set ts=2 sw=2 sts=2 et: */ +/* ***** BEGIN LICENSE BLOCK ***** + * Version: MPL 1.1/GPL 2.0/LGPL 2.1 + * + * The contents of this file are subject to the Mozilla Public License Version + * 1.1 (the "License"); you may not use this file except in compliance with + * the License. You may obtain a copy of the License at + * http://www.mozilla.org/MPL/ + * + * Software distributed under the License is distributed on an "AS IS" basis, + * WITHOUT WARRANTY OF ANY KIND, either express or implied. See the License + * for the specific language governing rights and limitations under the + * License. + * + * The Original Code is Jetpack. + * + * The Initial Developer of the Original Code is + * the Mozilla Foundation. + * Portions created by the Initial Developer are Copyright (C) 2010 + * the Initial Developer. All Rights Reserved. + * + * Contributor(s): + * Drew Willcoxon <adw@mozilla.com> (Original Author) + * + * Alternatively, the contents of this file may be used under the terms of + * either the GNU General Public License Version 2 or later (the "GPL"), or + * the GNU Lesser General Public License Version 2.1 or later (the "LGPL"), + * in which case the provisions of the GPL or the LGPL are applicable instead + * of those above. If you wish to allow use of your version of this file only + * under the terms of either the GPL or the LGPL, and not to allow others to + * use your version of this file under the terms of the MPL, indicate your + * decision by deleting the provisions above and replace them with the notice + * and other provisions required by the GPL or the LGPL. If you do not delete + * the provisions above, a recipient may use your version of this file under + * the terms of any one of the MPL, the GPL or the LGPL. + * + * ***** END LICENSE BLOCK ***** */ + +const apiUtils = require("api-utils/api-utils"); + +exports.testPublicConstructor = function (test) { + function PrivateCtor() {} + PrivateCtor.prototype = {}; + + let PublicCtor = apiUtils.publicConstructor(PrivateCtor); + test.assert( + PrivateCtor.prototype.isPrototypeOf(PublicCtor.prototype), + "PrivateCtor.prototype should be prototype of PublicCtor.prototype" + ); + + function testObj(useNew) { + let obj = useNew ? new PublicCtor() : PublicCtor(); + test.assert(obj instanceof PublicCtor, + "Object should be instance of PublicCtor"); + test.assert(obj instanceof PrivateCtor, + "Object should be instance of PrivateCtor"); + test.assert(PublicCtor.prototype.isPrototypeOf(obj), + "PublicCtor's prototype should be prototype of object"); + test.assertEqual(obj.constructor, PublicCtor, + "Object constructor should be PublicCtor"); + } + testObj(true); + testObj(false); +}; + +exports.testValidateOptionsEmpty = function (test) { + let val = apiUtils.validateOptions(null, {}); + assertObjsEqual(test, val, {}); + + val = apiUtils.validateOptions(null, { foo: {} }); + assertObjsEqual(test, val, {}); + + val = apiUtils.validateOptions({}, {}); + assertObjsEqual(test, val, {}); + + val = apiUtils.validateOptions({}, { foo: {} }); + assertObjsEqual(test, val, {}); +}; + +exports.testValidateOptionsNonempty = function (test) { + let val = apiUtils.validateOptions({ foo: 123 }, {}); + assertObjsEqual(test, val, {}); + + val = apiUtils.validateOptions({ foo: 123, bar: 456 }, + { foo: {}, bar: {}, baz: {} }); + assertObjsEqual(test, val, { foo: 123, bar: 456 }); +}; + +exports.testValidateOptionsMap = function (test) { + let val = apiUtils.validateOptions({ foo: 3, bar: 2 }, { + foo: { map: function (v) v * v }, + bar: { map: function (v) undefined } + }); + assertObjsEqual(test, val, { foo: 9, bar: undefined }); +}; + +exports.testValidateOptionsMapException = function (test) { + let val = apiUtils.validateOptions({ foo: 3 }, { + foo: { map: function () { throw new Error(); }} + }); + assertObjsEqual(test, val, { foo: 3 }); +}; + +exports.testValidateOptionsOk = function (test) { + let val = apiUtils.validateOptions({ foo: 3, bar: 2, baz: 1 }, { + foo: { ok: function (v) v }, + bar: { ok: function (v) v } + }); + assertObjsEqual(test, val, { foo: 3, bar: 2 }); + + test.assertRaises( + function () apiUtils.validateOptions({ foo: 2, bar: 2 }, { + bar: { ok: function (v) v > 2 } + }), + 'The option "bar" is invalid.', + "ok should raise exception on invalid option" + ); + + test.assertRaises( + function () apiUtils.validateOptions(null, { foo: { ok: function (v) v }}), + 'The option "foo" is invalid.', + "ok should raise exception on invalid option" + ); +}; + +exports.testValidateOptionsIs = function (test) { + let opts = { + array: [], + boolean: true, + func: function () {}, + nul: null, + number: 1337, + object: {}, + string: "foo", + undef1: undefined + }; + let requirements = { + array: { is: ["array"] }, + boolean: { is: ["boolean"] }, + func: { is: ["function"] }, + nul: { is: ["null"] }, + number: { is: ["number"] }, + object: { is: ["object"] }, + string: { is: ["string"] }, + undef1: { is: ["undefined"] }, + undef2: { is: ["undefined"] } + }; + let val = apiUtils.validateOptions(opts, requirements); + assertObjsEqual(test, val, opts); + + test.assertRaises( + function () apiUtils.validateOptions(null, { + foo: { is: ["object", "number"] } + }), + 'The option "foo" must be one of the following types: object, number', + "Invalid type should raise exception" + ); +}; + +exports.testValidateOptionsMapIsOk = function (test) { + let [map, is, ok] = [false, false, false]; + let val = apiUtils.validateOptions({ foo: 1337 }, { + foo: { + map: function (v) v.toString(), + is: ["string"], + ok: function (v) v.length > 0 + } + }); + assertObjsEqual(test, val, { foo: "1337" }); + + let requirements = { + foo: { + is: ["object"], + ok: function () test.fail("is should have caused us to throw by now") + } + }; + test.assertRaises( + function () apiUtils.validateOptions(null, requirements), + 'The option "foo" must be one of the following types: object', + "is should be used before ok is called" + ); +}; + +exports.testValidateOptionsErrorMsg = function (test) { + test.assertRaises( + function () apiUtils.validateOptions(null, { + foo: { ok: function (v) v, msg: "foo!" } + }), + "foo!", + "ok should raise exception with customized message" + ); +}; + +exports.testValidateMapWithMissingKey = function (test) { + let val = apiUtils.validateOptions({ }, { + foo: { + map: function (v) v || "bar" + } + }); + assertObjsEqual(test, val, { foo: "bar" }); + + val = apiUtils.validateOptions({ }, { + foo: { + map: function (v) { throw "bar" } + } + }); + assertObjsEqual(test, val, { }); +}; + +exports.testAddIterator = function testAddIterator(test) { + let obj = {}; + let keys = ["foo", "bar", "baz"]; + let vals = [1, 2, 3]; + let keysVals = [["foo", 1], ["bar", 2], ["baz", 3]]; + apiUtils.addIterator( + obj, + function keysValsGen() { + for each (let keyVal in keysVals) + yield keyVal; + } + ); + + let keysItr = []; + for (let key in obj) + keysItr.push(key); + test.assertEqual(keysItr.length, keys.length, + "the keys iterator returns the correct number of items"); + for (let i = 0; i < keys.length; i++) + test.assertEqual(keysItr[i], keys[i], "the key is correct"); + + let valsItr = []; + for each (let val in obj) + valsItr.push(val); + test.assertEqual(valsItr.length, vals.length, + "the vals iterator returns the correct number of items"); + for (let i = 0; i < vals.length; i++) + test.assertEqual(valsItr[i], vals[i], "the val is correct"); + + let keysValsItr = []; + for (let keyVal in Iterator(obj)) + keysValsItr.push(keyVal); + test.assertEqual(keysValsItr.length, keysVals.length, "the keys/vals " + + "iterator returns the correct number of items"); + for (let i = 0; i < keysVals.length; i++) { + test.assertEqual(keysValsItr[i][0], keysVals[i][0], "the key is correct"); + test.assertEqual(keysValsItr[i][1], keysVals[i][1], "the val is correct"); + } + + let keysOnlyItr = []; + for (let key in Iterator(obj, true /* keysonly */)) + keysOnlyItr.push(key); + test.assertEqual(keysOnlyItr.length, keysVals.length, "the keys only " + + "iterator returns the correct number of items"); + for (let i = 0; i < keysVals.length; i++) + test.assertEqual(keysOnlyItr[i], keysVals[i][0], "the key is correct"); +}; + +function assertObjsEqual(test, obj1, obj2) { + var items = 0; + for (let [key, val] in Iterator(obj1)) { + items++; + test.assert(key in obj2, "obj1 key should be present in obj2"); + test.assertEqual(obj2[key], val, "obj1 value should match obj2 value"); + } + for (let [key, val] in Iterator(obj2)) { + items++; + test.assert(key in obj1, "obj2 key should be present in obj1"); + test.assertEqual(obj1[key], val, "obj2 value should match obj1 value"); + } + if (!items) + test.assertEqual(JSON.stringify(obj1), JSON.stringify(obj2), + "obj1 should have same JSON representation as obj2"); +} diff --git a/tools/addon-sdk-1.4/packages/api-utils/tests/test-app-strings.js b/tools/addon-sdk-1.4/packages/api-utils/tests/test-app-strings.js new file mode 100644 index 0000000..3c41929 --- /dev/null +++ b/tools/addon-sdk-1.4/packages/api-utils/tests/test-app-strings.js @@ -0,0 +1,58 @@ +const { Cc,Ci } = require("chrome"); + +let StringBundle = require("app-strings").StringBundle; +exports.testStringBundle = function(test) { + let url = "chrome://global/locale/security/caps.properties"; + + let strings = StringBundle(url); + + test.assertEqual(strings.url, url, + "'url' property contains correct URL of string bundle"); + + let appLocale = Cc["@mozilla.org/intl/nslocaleservice;1"]. + getService(Ci.nsILocaleService). + getApplicationLocale(); + + let stringBundle = Cc["@mozilla.org/intl/stringbundle;1"]. + getService(Ci.nsIStringBundleService). + createBundle(url, appLocale); + + let (name = "Yes") { + test.assertEqual(strings.get(name), stringBundle.GetStringFromName(name), + "getting a string returns the string"); + } + + let (name = "ExtensionCapability", args = ["foo"]) { + test.assertEqual(strings.get(name, args), + stringBundle.formatStringFromName(name, args, args.length), + "getting a formatted string returns the formatted string"); + } + + test.assertRaises(function () strings.get("nonexistentString"), + "String 'nonexistentString' could not be retrieved from " + + "the bundle due to an unknown error (it doesn't exist?).", + "retrieving a nonexistent string throws an exception"); + + let a = [], b = []; + let enumerator = stringBundle.getSimpleEnumeration(); + while (enumerator.hasMoreElements()) { + let elem = enumerator.getNext().QueryInterface(Ci.nsIPropertyElement); + a.push([elem.key, elem.value]); + } + for (let keyVal in Iterator(strings)) + b.push(keyVal); + + // Sort the arrays, because we don't assume enumeration has a set order. + // Sort compares [key, val] as string "key,val", which sorts the way we want + // it to, so there is no need to provide a custom compare function. + a.sort(); + b.sort(); + + test.assertEqual(a.length, b.length, + "the iterator returns the correct number of items"); + for (let i = 0; i < a.length; i++) { + test.assertEqual(a[i][0], b[i][0], "the iterated string's name is correct"); + test.assertEqual(a[i][1], b[i][1], + "the iterated string's value is correct"); + } +}; diff --git a/tools/addon-sdk-1.4/packages/api-utils/tests/test-array.js b/tools/addon-sdk-1.4/packages/api-utils/tests/test-array.js new file mode 100644 index 0000000..88aed24 --- /dev/null +++ b/tools/addon-sdk-1.4/packages/api-utils/tests/test-array.js @@ -0,0 +1,36 @@ +var array = require("array"); + +exports.testHas = function(test) { + var testAry = [1, 2, 3]; + test.assertEqual(array.has([1, 2, 3], 1), true); + test.assertEqual(testAry.length, 3); + test.assertEqual(testAry[0], 1); + test.assertEqual(testAry[1], 2); + test.assertEqual(testAry[2], 3); + test.assertEqual(array.has(testAry, 2), true); + test.assertEqual(array.has(testAry, 3), true); + test.assertEqual(array.has(testAry, 4), false); + test.assertEqual(array.has(testAry, "1"), false); +}; + +exports.testAdd = function(test) { + var testAry = [1]; + test.assertEqual(array.add(testAry, 1), false); + test.assertEqual(testAry.length, 1); + test.assertEqual(testAry[0], 1); + test.assertEqual(array.add(testAry, 2), true); + test.assertEqual(testAry.length, 2); + test.assertEqual(testAry[0], 1); + test.assertEqual(testAry[1], 2); +}; + +exports.testRemove = function(test) { + var testAry = [1, 2]; + test.assertEqual(array.remove(testAry, 3), false); + test.assertEqual(testAry.length, 2); + test.assertEqual(testAry[0], 1); + test.assertEqual(testAry[1], 2); + test.assertEqual(array.remove(testAry, 2), true); + test.assertEqual(testAry.length, 1); + test.assertEqual(testAry[0], 1); +}; diff --git a/tools/addon-sdk-1.4/packages/api-utils/tests/test-byte-streams.js b/tools/addon-sdk-1.4/packages/api-utils/tests/test-byte-streams.js new file mode 100644 index 0000000..4eae428 --- /dev/null +++ b/tools/addon-sdk-1.4/packages/api-utils/tests/test-byte-streams.js @@ -0,0 +1,203 @@ +/* -*- Mode: Java; tab-width: 2; indent-tabs-mode: nil; c-basic-offset: 2 -*- + * vim:set ts=2 sw=2 sts=2 et filetype=javascript + * ***** BEGIN LICENSE BLOCK ***** + * Version: MPL 1.1/GPL 2.0/LGPL 2.1 + * + * The contents of this file are subject to the Mozilla Public License Version + * 1.1 (the "License"); you may not use this file except in compliance with + * the License. You may obtain a copy of the License at + * http://www.mozilla.org/MPL/ + * + * Software distributed under the License is distributed on an "AS IS" basis, + * WITHOUT WARRANTY OF ANY KIND, either express or implied. See the License + * for the specific language governing rights and limitations under the + * License. + * + * The Original Code is Jetpack. + * + * The Initial Developer of the Original Code is + * the Mozilla Foundation. + * Portions created by the Initial Developer are Copyright (C) 2010 + * the Initial Developer. All Rights Reserved. + * + * Contributor(s): + * Drew Willcoxon <adw@mozilla.com> (Original Author) + * + * Alternatively, the contents of this file may be used under the terms of + * either the GNU General Public License Version 2 or later (the "GPL"), or + * the GNU Lesser General Public License Version 2.1 or later (the "LGPL"), + * in which case the provisions of the GPL or the LGPL are applicable instead + * of those above. If you wish to allow use of your version of this file only + * under the terms of either the GPL or the LGPL, and not to allow others to + * use your version of this file under the terms of the MPL, indicate your + * decision by deleting the provisions above and replace them with the notice + * and other provisions required by the GPL or the LGPL. If you do not delete + * the provisions above, a recipient may use your version of this file under + * the terms of any one of the MPL, the GPL or the LGPL. + * + * ***** END LICENSE BLOCK ***** */ + +const byteStreams = require("byte-streams"); +const file = require("file"); +const url = require("url"); +const { Loader } = require("./helpers"); + +const STREAM_CLOSED_ERROR = "The stream is closed and cannot be used."; + +// This should match the constant of the same name in byte-streams.js. +const BUFFER_BYTE_LEN = 0x8000; + +exports.testWriteRead = function (test) { + let fname = dataFileFilename(); + + // Write a small string less than the stream's buffer size... + let str = "exports.testWriteRead data!"; + let stream = open(test, fname, true); + test.assert(!stream.closed, "stream.closed after open should be false"); + stream.write(str); + stream.close(); + test.assert(stream.closed, "Stream should be closed after stream.close"); + test.assertRaises(function () stream.write("This shouldn't be written!"), + STREAM_CLOSED_ERROR, + "stream.write after close should raise error"); + + // ... and read it. + stream = open(test, fname); + test.assertEqual(stream.read(), str, + "stream.read should return string written"); + test.assertEqual(stream.read(), "", + "stream.read at EOS should return empty string"); + stream.close(); + test.assert(stream.closed, "Stream should be closed after stream.close"); + test.assertRaises(function () stream.read(), + STREAM_CLOSED_ERROR, + "stream.read after close should raise error"); + + file.remove(fname); +}; + +// Write a big string many times the size of the stream's buffer and read it. +exports.testWriteReadBig = function (test) { + let str = ""; + let bufLen = BUFFER_BYTE_LEN; + let fileSize = bufLen * 10; + for (let i = 0; i < fileSize; i++) + str += i % 10; + let fname = dataFileFilename(); + let stream = open(test, fname, true); + stream.write(str); + stream.close(); + stream = open(test, fname); + test.assertEqual(stream.read(), str, + "stream.read should return string written"); + stream.close(); + file.remove(fname); +}; + +// The same, but write and read in chunks. +exports.testWriteReadChunks = function (test) { + let str = ""; + let bufLen = BUFFER_BYTE_LEN; + let fileSize = bufLen * 10; + for (let i = 0; i < fileSize; i++) + str += i % 10; + let fname = dataFileFilename(); + let stream = open(test, fname, true); + let i = 0; + while (i < str.length) { + // Use a chunk length that spans buffers. + let chunk = str.substr(i, bufLen + 1); + stream.write(chunk); + i += bufLen + 1; + } + stream.close(); + stream = open(test, fname); + let readStr = ""; + bufLen = BUFFER_BYTE_LEN; + let readLen = bufLen + 1; + do { + var frag = stream.read(readLen); + readStr += frag; + } while (frag); + stream.close(); + test.assertEqual(readStr, str, + "stream.write and read in chunks should work as expected"); + file.remove(fname); +}; + +exports.testReadLengths = function (test) { + let fname = dataFileFilename(); + let str = "exports.testReadLengths data!"; + let stream = open(test, fname, true); + stream.write(str); + stream.close(); + + stream = open(test, fname); + test.assertEqual(stream.read(str.length * 1000), str, + "stream.read with big byte length should return string " + + "written"); + stream.close(); + + stream = open(test, fname); + test.assertEqual(stream.read(0), "", + "string.read with zero byte length should return empty " + + "string"); + stream.close(); + + stream = open(test, fname); + test.assertEqual(stream.read(-1), "", + "string.read with negative byte length should return " + + "empty string"); + stream.close(); + + file.remove(fname); +}; + +exports.testTruncate = function (test) { + let fname = dataFileFilename(); + let str = "exports.testReadLengths data!"; + let stream = open(test, fname, true); + stream.write(str); + stream.close(); + + stream = open(test, fname); + test.assertEqual(stream.read(), str, + "stream.read should return string written"); + stream.close(); + + stream = open(test, fname, true); + stream.close(); + + stream = open(test, fname); + test.assertEqual(stream.read(), "", + "stream.read after truncate should be empty"); + stream.close(); + + file.remove(fname); +}; + +exports.testUnload = function (test) { + let loader = Loader(module); + let file = loader.require("file"); + + let filename = url.toFilename(module.uri); + let stream = file.open(filename, "b"); + + loader.unload(); + test.assert(stream.closed, "Stream should be closed after module unload"); +}; + +// Returns the name of a file that should be used to test writing and reading. +function dataFileFilename() { + let dir = file.dirname(url.toFilename(module.uri)); + return file.join(dir, "test-byte-streams-data"); +} + +// Opens and returns the given file and ensures it's of the correct class. +function open(test, filename, forWriting) { + let stream = file.open(filename, forWriting ? "wb" : "b"); + let klass = forWriting ? "ByteWriter" : "ByteReader"; + test.assert(stream instanceof byteStreams[klass], + "Opened stream should be a " + klass); + return stream; +} diff --git a/tools/addon-sdk-1.4/packages/api-utils/tests/test-collection.js b/tools/addon-sdk-1.4/packages/api-utils/tests/test-collection.js new file mode 100644 index 0000000..9db638e --- /dev/null +++ b/tools/addon-sdk-1.4/packages/api-utils/tests/test-collection.js @@ -0,0 +1,160 @@ +/* -*- Mode: Java; tab-width: 2; indent-tabs-mode: nil; c-basic-offset: 2 -*- */ +/* vim:set ts=2 sw=2 sts=2 et: */ +/* ***** BEGIN LICENSE BLOCK ***** + * Version: MPL 1.1/GPL 2.0/LGPL 2.1 + * + * The contents of this file are subject to the Mozilla Public License Version + * 1.1 (the "License"); you may not use this file except in compliance with + * the License. You may obtain a copy of the License at + * http://www.mozilla.org/MPL/ + * + * Software distributed under the License is distributed on an "AS IS" basis, + * WITHOUT WARRANTY OF ANY KIND, either express or implied. See the License + * for the specific language governing rights and limitations under the + * License. + * + * The Original Code is Jetpack. + * + * The Initial Developer of the Original Code is + * the Mozilla Foundation. + * Portions created by the Initial Developer are Copyright (C) 2010 + * the Initial Developer. All Rights Reserved. + * + * Contributor(s): + * Drew Willcoxon <adw@mozilla.com> (Original Author) + * + * Alternatively, the contents of this file may be used under the terms of + * either the GNU General Public License Version 2 or later (the "GPL"), or + * the GNU Lesser General Public License Version 2.1 or later (the "LGPL"), + * in which case the provisions of the GPL or the LGPL are applicable instead + * of those above. If you wish to allow use of your version of this file only + * under the terms of either the GPL or the LGPL, and not to allow others to + * use your version of this file under the terms of the MPL, indicate your + * decision by deleting the provisions above and replace them with the notice + * and other provisions required by the GPL or the LGPL. If you do not delete + * the provisions above, a recipient may use your version of this file under + * the terms of any one of the MPL, the GPL or the LGPL. + * + * ***** END LICENSE BLOCK ***** */ + +const collection = require("collection"); + +exports.testAddRemove = function (test) { + let coll = new collection.Collection(); + compare(test, coll, []); + addRemove(test, coll, [], false); +}; + +exports.testAddRemoveBackingArray = function (test) { + let items = ["foo"]; + let coll = new collection.Collection(items); + compare(test, coll, items); + addRemove(test, coll, items, true); + + items = ["foo", "bar"]; + coll = new collection.Collection(items); + compare(test, coll, items); + addRemove(test, coll, items, true); +}; + +exports.testProperty = function (test) { + let obj = makeObjWithCollProp(); + compare(test, obj.coll, []); + addRemove(test, obj.coll, [], false); + + // Test single-value set. + let items = ["foo"]; + obj.coll = items[0]; + compare(test, obj.coll, items); + addRemove(test, obj.coll, items, false); + + // Test array set. + items = ["foo", "bar"]; + obj.coll = items; + compare(test, obj.coll, items); + addRemove(test, obj.coll, items, false); +}; + +exports.testPropertyBackingArray = function (test) { + let items = ["foo"]; + let obj = makeObjWithCollProp(items); + compare(test, obj.coll, items); + addRemove(test, obj.coll, items, true); + + items = ["foo", "bar"]; + obj = makeObjWithCollProp(items); + compare(test, obj.coll, items); + addRemove(test, obj.coll, items, true); +}; + +// Adds some values to coll and then removes them. initialItems is an array +// containing coll's initial items. isBacking is true if initialItems is coll's +// backing array; the point is that updates to coll should affect initialItems +// if that's the case. +function addRemove(test, coll, initialItems, isBacking) { + let items = isBacking ? initialItems : initialItems.slice(0); + let numInitialItems = items.length; + + // Test add(val). + let numInsertions = 5; + for (let i = 0; i < numInsertions; i++) { + compare(test, coll, items); + coll.add(i); + if (!isBacking) + items.push(i); + } + compare(test, coll, items); + + // Add the items we just added to make sure duplicates aren't added. + for (let i = 0; i < numInsertions; i++) + coll.add(i); + compare(test, coll, items); + + // Test remove(val). Do a kind of shuffled remove. Remove item 1, then + // item 0, 3, 2, 5, 4, ... + for (let i = 0; i < numInsertions; i++) { + let val = i % 2 ? i - 1 : + i === numInsertions - 1 ? i : i + 1; + coll.remove(val); + if (!isBacking) + items.splice(items.indexOf(val), 1); + compare(test, coll, items); + } + test.assertEqual(coll.length, numInitialItems, + "All inserted items should be removed"); + + // Remove the items we just removed. coll should be unchanged. + for (let i = 0; i < numInsertions; i++) + coll.remove(i); + compare(test, coll, items); + + // Test add and remove([val1, val2]). + let newItems = [0, 1]; + coll.add(newItems); + compare(test, coll, isBacking ? items : items.concat(newItems)); + coll.remove(newItems); + compare(test, coll, items); + test.assertEqual(coll.length, numInitialItems, + "All inserted items should be removed"); +} + +// Asserts that the items in coll are the items of array. +function compare(test, coll, array) { + test.assertEqual(coll.length, array.length, + "Collection length should be correct"); + let numItems = 0; + for (let item in coll) { + test.assertEqual(item, array[numItems], "Items should be equal"); + numItems++; + } + test.assertEqual(numItems, array.length, + "Number of items in iteration should be correct"); +} + +// Returns a new object with a collection property named "coll". backingArray, +// if defined, will create the collection with that backing array. +function makeObjWithCollProp(backingArray) { + let obj = {}; + collection.addCollectionProperty(obj, "coll", backingArray); + return obj; +} diff --git a/tools/addon-sdk-1.4/packages/api-utils/tests/test-commonjs-test-adapter.js b/tools/addon-sdk-1.4/packages/api-utils/tests/test-commonjs-test-adapter.js new file mode 100644 index 0000000..71fc4a0 --- /dev/null +++ b/tools/addon-sdk-1.4/packages/api-utils/tests/test-commonjs-test-adapter.js @@ -0,0 +1,7 @@ +"use strict"; + +exports["test custom `Assert`'s"] = require("./commonjs-test-adapter/asserts"); + +// Disabling this check since it is not yet supported by jetpack. +// if (module == require.main) + require("test").run(exports); diff --git a/tools/addon-sdk-1.4/packages/api-utils/tests/test-content-loader.js b/tools/addon-sdk-1.4/packages/api-utils/tests/test-content-loader.js new file mode 100644 index 0000000..0e91ee5 --- /dev/null +++ b/tools/addon-sdk-1.4/packages/api-utils/tests/test-content-loader.js @@ -0,0 +1,223 @@ +"use strict"; +const { Loader } = require('content/loader'); +const self = require("self"); + +exports['test:contentURL'] = function(test) { + let loader = Loader(), + value, emitted = 0, changes = 0; + + test.assertRaises( + function() loader.contentURL = undefined, + 'The `contentURL` option must be a valid URL.', + 'Must throw an exception if `contentURL` is not URL.' + ); + test.assertRaises( + function() loader.contentURL = null, + 'The `contentURL` option must be a valid URL.', + 'Must throw an exception if `contentURL` is not URL.' + ); + test.assertRaises( + function() loader.contentURL = 4, + 'The `contentURL` option must be a valid URL.', + 'Must throw an exception if `contentURL` is not URL.' + ); + test.assertRaises( + function() loader.contentURL = { toString: function() 'Oops' }, + 'The `contentURL` option must be a valid URL.', + 'Must throw an exception if `contentURL` is not URL.' + ); + + function listener(e) { + emitted ++; + test.assert( + 'contentURL' in e, + 'emitted event must contain "content" property' + ); + test.assert( + value, + '' + e.contentURL, + 'content property of an event must match value' + ); + } + loader.on('propertyChange', listener); + + test.assertEqual( + null, + loader.contentURL, + 'default value is `null`' + ); + loader.contentURL = value = 'data:text/html,<html><body>Hi</body><html>'; + test.assertEqual( + value, + '' + loader.contentURL, + 'data uri is ok' + ); + test.assertEqual( + ++changes, + emitted, + 'had to emit `propertyChange`' + ); + loader.contentURL = value; + test.assertEqual( + changes, + emitted, + 'must not emit `propertyChange` if same value is set' + ); + + loader.contentURL = value = 'http://google.com/'; + test.assertEqual( + value, + '' + loader.contentURL, + 'value must be set' + ); + test.assertEqual( + ++ changes, + emitted, + 'had to emit `propertyChange`' + ); + loader.contentURL = value; + test.assertEqual( + changes, + emitted, + 'must not emit `propertyChange` if same value is set' + ); + + loader.removeListener('propertyChange', listener); + loader.contentURL = value = 'about:blank'; + test.assertEqual( + value, + '' + loader.contentURL, + 'contentURL must be an actual value' + ); + test.assertEqual( + changes, + emitted, + 'listener had to be romeved' + ); +}; + +exports['test:contentScriptWhen'] = function(test) { + let loader = Loader(); + test.assertEqual( + 'end', + loader.contentScriptWhen, + '`contentScriptWhen` defaults to "end"' + ); + loader.contentScriptWhen = "end"; + test.assertEqual( + "end", + loader.contentScriptWhen + ); + try { + loader.contentScriptWhen = 'boom'; + test.fail('must throw when wrong value is set'); + } catch(e) { + test.assertEqual( + 'The `contentScriptWhen` option must be either "start", "ready" or "end".', + e.message + ); + } + loader.contentScriptWhen = null; + test.assertEqual( + 'end', + loader.contentScriptWhen, + '`contentScriptWhen` defaults to "end"' + ); + loader.contentScriptWhen = "ready"; + test.assertEqual( + "ready", + loader.contentScriptWhen + ); + loader.contentScriptWhen = "start"; + test.assertEqual( + 'start', + loader.contentScriptWhen + ); +}; + +exports['test:contentScript'] = function(test) { + let loader = Loader(), value; + test.assertEqual( + null, + loader.contentScript, + '`contentScript` defaults to `null`' + ); + loader.contentScript = value = 'let test = {};'; + test.assertEqual( + value, + loader.contentScript + ); + try { + loader.contentScript = { 1: value } + test.fail('must throw when wrong value is set'); + } catch(e) { + test.assertEqual( + 'The script option must be a string or an array of strings.', + e.message + ); + } + try { + loader.contentScript = ['oue', 2] + test.fail('must throw when wrong value is set'); + } catch(e) { + test.assertEqual( + 'The script option must be a string or an array of strings.', + e.message + ); + } + loader.contentScript = undefined; + test.assertEqual( + null, + loader.contentScript + ); + loader.contentScript = value = ["1;", "2;"]; + test.assertEqual( + value, + loader.contentScript + ); +}; + +exports['test:contentScriptFile'] = function(test) { + let loader = Loader(), value, uri = self.data.url("test-content-loader.js"); + test.assertEqual( + null, + loader.contentScriptFile, + '`contentScriptFile` defaults to `null`' + ); + loader.contentScriptFile = value = uri; + test.assertEqual( + value, + loader.contentScriptFile + ); + try { + loader.contentScriptFile = { 1: uri } + test.fail('must throw when wrong value is set'); + } catch(e) { + test.assertEqual( + 'The `contentScriptFile` option must be a local file URL or an array of' + + 'URLs.', + e.message + ); + } + try { + loader.contentScriptFile = ['oue', uri] + test.fail('must throw when wrong value is set'); + } catch(e) { + test.assertEqual( + 'The `contentScriptFile` option must be a local file URL or an array of' + + 'URLs.', + e.message + ); + } + loader.contentScriptFile = undefined; + test.assertEqual( + null, + loader.contentScriptFile + ); + loader.contentScriptFile = value = [uri]; + test.assertEqual( + value, + loader.contentScriptFile + ); +}; + diff --git a/tools/addon-sdk-1.4/packages/api-utils/tests/test-content-proxy.js b/tools/addon-sdk-1.4/packages/api-utils/tests/test-content-proxy.js new file mode 100644 index 0000000..1689c81 --- /dev/null +++ b/tools/addon-sdk-1.4/packages/api-utils/tests/test-content-proxy.js @@ -0,0 +1,754 @@ +const hiddenFrames = require("hidden-frame"); +const xulApp = require("xul-app"); + +const { Loader } = require('./helpers'); + +/* + * Utility function that allow to easily run a proxy test with a clean + * new HTML document. See first unit test for usage. + */ +function createProxyTest(html, callback) { + return function (test) { + test.waitUntilDone(); + + let url = 'data:text/html,' + encodeURI(html); + + let hiddenFrame = hiddenFrames.add(hiddenFrames.HiddenFrame({ + onReady: function () { + + function onDOMReady() { + hiddenFrame.element.removeEventListener("DOMContentLoaded", onDOMReady, + false); + + let xrayWindow = hiddenFrame.element.contentWindow; + let rawWindow = xrayWindow.wrappedJSObject; + + let done = false; + let helper = { + xrayWindow: xrayWindow, + rawWindow: rawWindow, + createWorker: function (contentScript) { + return createWorker(test, xrayWindow, contentScript, helper.done); + }, + done: function () { + if (done) + return; + done = true; + hiddenFrames.remove(hiddenFrame); + test.done(); + } + } + callback(helper, test); + } + + hiddenFrame.element.addEventListener("DOMContentLoaded", onDOMReady, false); + hiddenFrame.element.setAttribute("src", url); + + } + })); + }; +} + +function createWorker(test, xrayWindow, contentScript, done) { + // We have to use Sandboxed loader in order to get access to the private + // unlock key `PRIVATE_KEY`. This key should not be used anywhere else. + // See `PRIVATE_KEY` definition in worker.js + let loader = Loader(module); + let Worker = loader.require("api-utils/content/worker").Worker; + let key = loader.sandbox("api-utils/content/worker").PRIVATE_KEY; + let worker = Worker({ + exposeUnlockKey : key, + window: xrayWindow, + contentScript: [ + 'new ' + function () { + assert = function assert(v, msg) { + self.port.emit("assert", {assertion:v, msg:msg}); + } + done = function done() { + self.port.emit("done"); + } + }, + contentScript + ] + }); + + worker.port.on("done", done); + worker.port.on("assert", function (data) { + test.assert(data.assertion, data.msg); + }); + + return worker; +} + +/* Examples for the `createProxyTest` uses */ + +let html = "<script>var documentGlobal = true</script>"; +exports.testCreateProxyTest = createProxyTest(html, function (helper, test) { + // You can get access to regular `test` object in second argument of + // `createProxyTest` method: + test.assert(helper.rawWindow.documentGlobal, + "You have access to a raw window reference via `helper.rawWindow`"); + test.assert(!("documentGlobal" in helper.xrayWindow), + "You have access to an XrayWrapper reference via `helper.xrayWindow`"); + + // If you do not create a Worker, you have to call helper.done(), + // in order to say when your test is finished + helper.done(); +}); + +exports.testCreateProxyTestWithWorker = createProxyTest("", function (helper) { + + helper.createWorker( + "new " + function WorkerScope() { + assert(true, "You can do assertions in your content script"); + // And if you create a worker, you either have to call `done` + // from content script or helper.done() + done(); + } + ); + +}); + +exports.testCreateProxyTestWithEvents = createProxyTest("", function (helper, test) { + + let worker = helper.createWorker( + "new " + function WorkerScope() { + self.port.emit("foo"); + } + ); + + worker.port.on("foo", function () { + test.pass("You can use events"); + // And terminate your test with helper.done: + helper.done(); + }); + +}); + +// Verify that the attribute `exposeUnlockKey`, that allow this test +// to identify proxies, works correctly. +// See `PRIVATE_KEY` definition in worker.js +exports.testKeyAccess = createProxyTest("", function(helper) { + + helper.createWorker( + 'new ' + function ContentScriptScope() { + assert("UNWRAP_ACCESS_KEY" in window, "have access to `UNWRAP_ACCESS_KEY`"); + done(); + } + ); + +}); + + +// Bug 714778: There was some issue around `toString` functions +// that ended up being shared between content scripts +exports.testSharedToStringProxies = createProxyTest("", function(helper) { + + let worker = helper.createWorker( + 'new ' + function ContentScriptScope() { + assert(document.location.toString() == "data:text/html,", + "document.location.toString()"); + self.postMessage("next"); + } + ); + worker.on("message", function () { + helper.createWorker( + 'new ' + function ContentScriptScope2() { + assert(document.location.toString() == "data:text/html,", + "document.location.toString()"); + done(); + } + ); + }); +}); + + +// Ensure that postMessage is working correctly across documents with an iframe +let html = '<iframe id="iframe" name="test" src="data:text/html," />'; +exports.testPostMessage = createProxyTest(html, function (helper, test) { + let ifWindow = helper.xrayWindow.document.getElementById("iframe").contentWindow; + // Listen without proxies, to check that it will work in regular case + // simulate listening from a web document. + ifWindow.addEventListener("message", function listener(event) { + //if (event.source.wrappedJSObject == helper.rawWindow) return; + ifWindow.removeEventListener("message", listener, false); + // As we are in system principal, event is an XrayWrapper + test.assertEqual(event.source, ifWindow, + "event.source is the iframe window"); + test.assertEqual(event.origin, "null", "origin is null"); + + test.assertEqual(event.data, "{\"foo\":\"bar\\n \\\"escaped\\\".\"}", + "message data is correct"); + + helper.done(); + }, false); + + helper.createWorker( + 'new ' + function ContentScriptScope() { + assert(postMessage === postMessage, + "verify that we doesn't generate multiple functions for the same method"); + + var json = JSON.stringify({foo : "bar\n \"escaped\"."}); + + document.getElementById("iframe").contentWindow.postMessage(json, "*"); + } + ); +}); + +let html = '<input id="input2" type="checkbox" />'; +exports.testObjectListener = createProxyTest(html, function (helper) { + + helper.createWorker( + 'new ' + function ContentScriptScope() { + // Test objects being given as event listener + let input = document.getElementById("input2"); + let myClickListener = { + called: false, + handleEvent: function(event) { + assert(this === myClickListener, "`this` is the original object"); + assert(!this.called, "called only once"); + this.called = true; + assert(event.valueOf() !== event.valueOf(UNWRAP_ACCESS_KEY), "event is wrapped"); + assert(event.target, input, "event.target is the wrapped window"); + done(); + } + }; + + window.addEventListener("click", myClickListener, true); + input.click(); + window.removeEventListener("click", myClickListener, true); + } + ); + +}); + +exports.testObjectListener2 = createProxyTest("", function (helper) { + + helper.createWorker( + 'new ' + function ContentScriptScope() { + // Verify object as DOM event listener + let myMessageListener = { + called: false, + handleEvent: function(event) { + window.removeEventListener("message", myMessageListener, true); + + assert(this == myMessageListener, "`this` is the original object"); + assert(!this.called, "called only once"); + this.called = true; + assert(event.valueOf() !== event.valueOf(UNWRAP_ACCESS_KEY), "event is wrapped"); + assert(event.target == document.defaultView, "event.target is the wrapped window"); + assert(event.source == document.defaultView, "event.source is the wrapped window"); + assert(event.origin == "null", "origin is null"); + assert(event.data == "ok", "message data is correct"); + done(); + } + }; + + window.addEventListener("message", myMessageListener, true); + document.defaultView.postMessage("ok", '*'); + } + ); + +}); + +let html = '<input id="input" type="text" /><input id="input3" type="checkbox" />' + + '<input id="input2" type="checkbox" />'; +exports.testStringOverload = createProxyTest(html, function (helper, test) { + // Proxy - toString error + let originalString = "string"; + let p = Proxy.create({ + get: function(receiver, name) { + if (name == "binded") + return originalString.toString.bind(originalString); + return originalString[name]; + } + }); + test.assertRaises(function () { + p.toString(); + }, + /String.prototype.toString called on incompatible Proxy/, + "toString can't be called with this being the proxy"); + test.assertEqual(p.binded(), "string", "but it works if we bind this to the original string"); + + helper.createWorker( + 'new ' + function ContentScriptScope() { + // RightJS is hacking around String.prototype, and do similar thing: + // Pass `this` from a String prototype method. + // It is funny because typeof this == "object"! + // So that when we pass `this` to a native method, + // our proxy code can fail on another even more crazy thing. + // See following test to see what fails around proxies. + String.prototype.update = function () { + assert(typeof this == "object", "in update, `this` is an object"); + assert(this.toString() == "input", "in update, `this.toString works"); + return document.querySelectorAll(this); + }; + assert("input".update().length == 3, "String.prototype overload works"); + done(); + } + ); +}); + +exports.testMozMatchedSelector = createProxyTest("", function (helper) { + helper.createWorker( + 'new ' + function ContentScriptScope() { + // Check mozMatchesSelector XrayWrappers bug: + // mozMatchesSelector returns bad results when we are not calling it from the node itself + // SEE BUG 658909: mozMatchesSelector returns incorrect results with XrayWrappers + assert(document.createElement( "div" ).mozMatchesSelector("div"), + "mozMatchesSelector works while being called from the node"); + assert(document.documentElement.mozMatchesSelector.call( + document.createElement( "div" ), + "div" + ), + "mozMatchesSelector works while being called from a " + + "function reference to " + + "document.documentElement.mozMatchesSelector.call"); + done(); + } + ); +}); + +exports.testEventsOverload = createProxyTest("", function (helper) { + + helper.createWorker( + 'new ' + function ContentScriptScope() { + // If we add a "____proxy" attribute on XrayWrappers in order to store + // the related proxy to create an unique proxy for each wrapper; + // we end up setting this attribute to prototype objects :x + // And so, instances created with such prototype will be considered + // as equal to the prototype ... + // // Internal method that return the proxy for a given XrayWrapper + // function proxify(obj) { + // if (obj._proxy) return obj._proxy; + // return obj._proxy = Proxy.create(...); + // } + // + // // Get a proxy of an XrayWrapper prototype object + // let proto = proxify(xpcProto); + // + // // Use this proxy as a prototype + // function Constr() {} + // Constr.proto = proto; + // + // // Try to create an instance using this prototype + // let xpcInstance = new Constr(); + // let wrapper = proxify(xpcInstance) + // + // xpcProto._proxy = proto and as xpcInstance.__proto__ = xpcProto, + // xpcInstance._proxy = proto ... and profixy(xpcInstance) = proto :( + // + let proto = window.document.createEvent('HTMLEvents').__proto__; + window.Event.prototype = proto; + let event = document.createEvent('HTMLEvents'); + assert(event !== proto, "Event should not be equal to its prototype"); + event.initEvent('dataavailable', true, true); + assert(event.type === 'dataavailable', "Events are working fine"); + done(); + } + ); + +}); + +exports.testNestedAttributes = createProxyTest("", function (helper) { + + helper.createWorker( + 'new ' + function ContentScriptScope() { + // XrayWrappers has a bug when you set an attribute on it, + // in some cases, it creates an unnecessary wrapper that introduces + // a different object that refers to the same original object + // Check that our wrappers don't reproduce this bug + // SEE BUG 658560: Fix identity problem with CrossOriginWrappers + let o = {sandboxObject:true}; + window.nested = o; + o.foo = true; + assert(o === window.nested, "Nested attribute to sandbox object should not be proxified"); + window.nested = document; + assert(window.nested === document, "Nested attribute to proxy should not be double proxified"); + done(); + } + ); + +}); + +exports.testFormNodeName = createProxyTest("", function (helper) { + + helper.createWorker( + 'new ' + function ContentScriptScope() { + let body = document.body; + // Check form[nodeName] + let form = document.createElement("form"); + let input = document.createElement("input"); + input.setAttribute("name", "test"); + form.appendChild(input); + body.appendChild(form); + assert(form.test == input, "form[nodeName] is valid"); + body.removeChild(form); + done(); + } + ); + +}); + +exports.testLocalStorage = createProxyTest("", function (helper, test) { + + let worker = helper.createWorker( + 'new ' + function ContentScriptScope() { + // Check localStorage: + assert(window.localStorage, "has access to localStorage"); + window.localStorage.name = 1; + assert(window.localStorage.name == 1, "localStorage appears to work"); + + self.port.on("step2", function () { + window.localStorage.clear(); + assert(window.localStorage.name == undefined, "localStorage really, really works"); + done(); + }); + self.port.emit("step1"); + } + ); + + worker.port.on("step1", function () { + test.assertEqual(helper.rawWindow.localStorage.name, 1, "localStorage really works"); + worker.port.emit("step2"); + }); + +}); + +exports.testAutoUnwrapCustomAttributes = createProxyTest("", function (helper) { + + helper.createWorker( + 'new ' + function ContentScriptScope() { + let body = document.body; + // Setting a custom object to a proxy attribute is not wrapped when we get it afterward + let object = {custom: true, enumerable: false}; + body.customAttribute = object; + assert(body.customAttribute.valueOf() === body.customAttribute.valueOf(UNWRAP_ACCESS_KEY), "custom JS attributes are not wrapped"); + assert(object === body.customAttribute, "custom JS attributes are not wrapped"); + done(); + } + ); + +}); + +exports.testObjectTag = createProxyTest("", function (helper) { + + helper.createWorker( + 'new ' + function ContentScriptScope() { + // <object>, <embed> and other tags return typeof 'function' + let flash = document.createElement("object"); + assert(typeof flash == "function", "<object> is typeof 'function'"); + assert(flash.toString().match(/\[object HTMLObjectElement.*\]/), "<object> is HTMLObjectElement"); + assert("setAttribute" in flash, "<object> has a setAttribute method"); + done(); + } + ); + +}); + +exports.testHighlightToStringBehavior = createProxyTest("", function (helper, test) { + // We do not have any workaround this particular use of toString + // applied on <object> elements. So disable this test until we found one! + //test.assertEqual(helper.rawWindow.Object.prototype.toString.call(flash), "[object HTMLObjectElement]", "<object> is HTMLObjectElement"); + function f() {}; + test.assertMatches(Object.prototype.toString.call(f), /\[object Function.*\]/, "functions are functions 1"); + // This is how jquery call toString: + test.assertMatches(helper.rawWindow.Object.prototype.toString.call(""), /\[object String.*\]/, "strings are strings"); + test.assertMatches(helper.rawWindow.Object.prototype.toString.call({}), /\[object Object.*\]/, "objects are objects"); + + // Make sure to pass a function from the same compartments + // or toString will return [object Object] on FF8+ + let f2 = helper.rawWindow.eval("(function () {})"); + test.assertMatches(helper.rawWindow.Object.prototype.toString.call(f2), /\[object Function.*\]/, "functions are functions 2"); + + helper.done(); +}); + +exports.testDocumentTagName = createProxyTest("", function (helper) { + + helper.createWorker( + 'new ' + function ContentScriptScope() { + let body = document.body; + // Check document[tagName] + let div = document.createElement("div"); + div.setAttribute("name", "test"); + body.appendChild(div); + assert(!document.test, "document[divName] is undefined"); + body.removeChild(div); + + let form = document.createElement("form"); + form.setAttribute("name", "test"); + body.appendChild(form); + assert(document.test == form, "document[formName] is valid"); + body.removeChild(form); + + let img = document.createElement("img"); + img.setAttribute("name", "test"); + body.appendChild(img); + assert(document.test == img, "document[imgName] is valid"); + body.removeChild(img); + done(); + } + ); + +}); + +let html = '<iframe id="iframe" name="test" src="data:text/html," />'; +exports.testWindowFrames = createProxyTest(html, function (helper) { + + helper.createWorker( + 'let glob = this; new ' + function ContentScriptScope() { + // Check window[frameName] and window.frames[i] + let iframe = document.getElementById("iframe"); + //assert(window.frames.length == 1, "The iframe is reported in window.frames check1"); + //assert(window.frames[0] == iframe.contentWindow, "The iframe is reported in window.frames check2"); + //console.log(window.test+ "-"+iframe.contentWindow); + //console.log(window); + assert(window.test == iframe.contentWindow, "window[frameName] is valid"); + done(); + } + ); + +}); + +exports.testCollections = createProxyTest("", function (helper) { + + helper.createWorker( + 'new ' + function ContentScriptScope() { + // Highlight XPCNativeWrapper bug with HTMLCollection + // tds[0] is only defined on first access :o + let body = document.body; + let div = document.createElement("div"); + body.appendChild(div); + div.innerHTML = "<table><tr><td style='padding:0;border:0;display:none'></td><td>t</td></tr></table>"; + let tds = div.getElementsByTagName("td"); + assert(tds[0] == tds[0], "We can get array element multiple times"); + body.removeChild(div); + done(); + } + ); + +}); + +let html = '<input id="input" type="text" /><input id="input3" type="checkbox" />' + + '<input id="input2" type="checkbox" />'; +exports.testCollections2 = createProxyTest(html, function (helper) { + + helper.createWorker( + 'new ' + function ContentScriptScope() { + // Verify that NodeList/HTMLCollection are working fine + let body = document.body; + let inputs = body.getElementsByTagName("input"); + assert(body.childNodes.length == 3, "body.childNodes length is correct"); + assert(inputs.length == 3, "inputs.length is correct"); + assert(body.childNodes[0] == inputs[0], "body.childNodes[0] is correct"); + assert(body.childNodes[1] == inputs[1], "body.childNodes[1] is correct"); + assert(body.childNodes[2] == inputs[2], "body.childNodes[2] is correct"); + let count = 0; + for(let i in body.childNodes) { + count++; + } + assert(count == 3, "body.childNodes is iterable"); + done(); + } + ); + +}); + +exports.testValueOf = createProxyTest("", function (helper) { + + helper.createWorker( + 'new ' + function ContentScriptScope() { + // Check internal use of valueOf() + assert(window.valueOf().toString().match(/\[object Window.*\]/), "proxy.valueOf() returns the wrapped version"); + assert(window.valueOf({}).toString().match(/\[object Window.*\]/), "proxy.valueOf({}) returns the wrapped version"); + assert(window.valueOf(UNWRAP_ACCESS_KEY).toString().match(/\[object XrayWrapper \[object Window.*\].*\]/), "proxy.valueOf(UNWRAP_ACCESS_KEY) returns the unwrapped version"); + done(); + } + ); + +}); + +exports.testXMLHttpRequest = createProxyTest("", function (helper) { + + helper.createWorker( + 'new ' + function ContentScriptScope() { + // XMLHttpRequest doesn't support XMLHttpRequest.apply, + // that may break our proxy code + assert(window.XMLHttpRequest(), "we are able to instantiate XMLHttpRequest object"); + done(); + } + ); + +}); + +exports.testXPathResult = createProxyTest("", function (helper, test) { + + // Check XPathResult bug with constants being undefined on + // XPCNativeWrapper + let value = helper.rawWindow.XPathResult.UNORDERED_NODE_SNAPSHOT_TYPE; + let xpcXPathResult = helper.xrayWindow.XPathResult; + test.assertEqual(xpcXPathResult.wrappedJSObject. + UNORDERED_NODE_SNAPSHOT_TYPE, + value, + "XPathResult's constants are valid on unwrapped node"); + + if (xulApp.versionInRange(xulApp.platformVersion, "10.0a1", "*")) { + test.assertEqual(xpcXPathResult.UNORDERED_NODE_SNAPSHOT_TYPE, 6, + "XPathResult's constants are defined on " + + "XPCNativeWrapper (platform bug #)"); + } + else { + test.assertEqual(xpcXPathResult.UNORDERED_NODE_SNAPSHOT_TYPE, + undefined, + "XPathResult's constants are undefined on " + + "XPCNativeWrapper (platform bug #665279)"); + } + + let worker = helper.createWorker( + 'new ' + function ContentScriptScope() { + self.port.on("value", function (value) { + // Check that our work around is working: + assert(window.XPathResult.UNORDERED_NODE_SNAPSHOT_TYPE === value, + "XPathResult works correctly on Proxies"); + done(); + }); + } + ); + worker.port.emit("value", value); + +}); + +exports.testPrototypeInheritance = createProxyTest("", function (helper) { + + helper.createWorker( + 'new ' + function ContentScriptScope() { + // Verify that inherited prototype function like initEvent + // are handled correctly. (e2.type will return an error if it's not the case) + let event1 = document.createEvent( 'MouseEvents' ); + event1.initEvent( "click", true, true ); + let event2 = document.createEvent( 'MouseEvents' ); + event2.initEvent( "click", true, true ); + assert(event2.type == "click", "We are able to create an event"); + done(); + } + ); + +}); + +exports.testFunctions = createProxyTest("", function (helper) { + + helper.rawWindow.callFunction = function (f) f(); + helper.rawWindow.isEqual = function (a, b) a == b; + + helper.createWorker( + 'new ' + function ContentScriptScope() { + // Check basic usage of functions + let closure2 = function () {return "ok";}; + assert(window.wrappedJSObject.callFunction(closure2) == "ok", "Function references work"); + + // Ensure that functions are cached when being wrapped to native code + let closure = function () {}; + assert(window.wrappedJSObject.isEqual(closure, closure), "Function references are cached before being wrapped to native"); + done(); + } + ); + +}); + +let html = '<input id="input2" type="checkbox" />'; +exports.testListeners = createProxyTest(html, function (helper) { + + helper.createWorker( + 'new ' + function ContentScriptScope() { + // Verify listeners: + let input = document.getElementById("input2"); + assert(input, "proxy.getElementById works"); + + function onclick() {}; + input.onclick = onclick; + assert(input.onclick === onclick, "on* attributes are equal to original function set"); + + let addEventListenerCalled = false; + let expandoCalled = false; + input.addEventListener("click", function onclick(event) { + input.removeEventListener("click", onclick, true); + + assert(!addEventListenerCalled, "closure given to addEventListener is called once"); + if (addEventListenerCalled) + return; + addEventListenerCalled = true; + + assert(!event.target.ownerDocument.defaultView.documentGlobal, "event object is still wrapped and doesn't expose document globals"); + assert("__isWrappedProxy" in event.target, "event object is a proxy"); + + let input2 = document.getElementById("input2"); + + input.onclick = function (event) { + input.onclick = null; + assert(!expandoCalled, "closure set to expando is called once"); + if (expandoCalled) return; + expandoCalled = true; + + assert(!event.target.ownerDocument.defaultView.documentGlobal, "event object is still wrapped and doesn't expose document globals"); + assert("__isWrappedProxy" in event.target, "event object is a proxy"); + + setTimeout(function () { + input.click(); + done(); + }, 0); + + } + + setTimeout(function () { + input.click(); + }, 0); + + }, true); + + input.click(); + } + ); + +}); + +exports.testMozRequestAnimationFrame = createProxyTest("", function (helper) { + + helper.createWorker( + 'new ' + function ContentScriptScope() { + window.mozRequestAnimationFrame(function callback() { + assert(callback == this, "callback is equal to `this`"); + done(); + }); + } + ); + +}); + +exports.testGlobalScope = createProxyTest("", function (helper) { + + helper.createWorker( + 'let toplevelScope = true;' + + 'assert(window.toplevelScope, "variables in toplevel scope are set to `window` object");' + + 'assert(this.toplevelScope, "variables in toplevel scope are set to `this` object");' + + 'done();' + ); + +}); + +// Bug 671016: Typed arrays should not be proxified +exports.testTypedArrays = createProxyTest("", function (helper) { + + helper.createWorker( + 'new ' + function ContentScriptScope() { + let canvas = document.createElement("canvas"); + let context = canvas.getContext("2d"); + let imageData = context.getImageData(0,0, 1, 1); + let unwrappedData = imageData.valueOf(UNWRAP_ACCESS_KEY).data; + let data = imageData.data; + assert(unwrappedData === data, "Typed array isn't proxified") + done(); + } + ); + +}); diff --git a/tools/addon-sdk-1.4/packages/api-utils/tests/test-content-symbiont.js b/tools/addon-sdk-1.4/packages/api-utils/tests/test-content-symbiont.js new file mode 100644 index 0000000..3bd2890 --- /dev/null +++ b/tools/addon-sdk-1.4/packages/api-utils/tests/test-content-symbiont.js @@ -0,0 +1,186 @@ +"use strict"; + +const { Cc, Ci } = require('chrome'); +const { Symbiont } = require('content/symbiont'); +const self = require("self"); + +function makeWindow() { + let content = + '<?xml version="1.0"?>' + + '<window ' + + 'xmlns="http://www.mozilla.org/keymaster/gatekeeper/there.is.only.xul">' + + '<iframe id="content" type="content"/>' + + '</window>'; + var url = "data:application/vnd.mozilla.xul+xml," + + encodeURIComponent(content); + var features = ["chrome", "width=10", "height=10"]; + + return Cc["@mozilla.org/embedcomp/window-watcher;1"]. + getService(Ci.nsIWindowWatcher). + openWindow(null, url, null, features.join(","), null); +} + +exports['test:constructing symbiont && validating API'] = function(test) { + let window = makeWindow(); + window.addEventListener("load", function onLoad() { + window.removeEventListener("load", onLoad, false); + let frame = window.document.getElementById("content"); + // TODO: support arrays ?? + let contentScripts = ["1;", "2;"]; + let contentSymbiont = Symbiont({ + frame: frame, + contentScriptFile: self.data.url("test-content-symbiont.js"), + contentScript: contentScripts, + contentScriptWhen: "start" + }); + + test.assertEqual( + self.data.url("test-content-symbiont.js"), + contentSymbiont.contentScriptFile, + "There is one contentScriptFile, as specified in options." + ); + test.assertEqual( + contentScripts.length, + contentSymbiont.contentScript.length, + "There are two contentScripts, as specified in options." + ); + test.assertEqual( + contentScripts[0], + contentSymbiont.contentScript[0], + "There are two contentScripts, as specified in options." + ); + test.assertEqual( + contentScripts[1], + contentSymbiont.contentScript[1], + "There are two contentScripts, as specified in options." + ) + test.assertEqual( + contentSymbiont.contentScriptWhen, + "start", + "contentScriptWhen is as specified in options." + ); + + test.done(); + window.close(); + frame.setAttribute("src", "data:text/html,<html><body></body></html>"); + }, false); + test.waitUntilDone(); +}; + +exports["test:communication with worker global scope"] = function(test) { + let window = makeWindow(); + let contentSymbiont; + + function onMessage1(message) { + test.assertEqual(message, 1, "Program gets message via onMessage."); + contentSymbiont.removeListener('message', onMessage1); + contentSymbiont.on('message', onMessage2); + contentSymbiont.postMessage(2); + }; + + function onMessage2(message) { + if (5 == message) { + test.done(); + } else { + test.assertEqual(message, 3, "Program gets message via onMessage2."); + contentSymbiont.postMessage(4) + } + } + + window.addEventListener("load", function onLoad() { + window.removeEventListener("load", onLoad, false); + let frame = window.document.getElementById("content"); + contentSymbiont = Symbiont({ + frame: frame, + contentScript: 'new ' + function() { + self.postMessage(1); + self.on("message", function onMessage(message) { + if (message === 2) + self.postMessage(3); + if (message === 4) + self.postMessage(5); + }); + } + '()', + contentScriptWhen: 'ready', + onMessage: onMessage1 + }); + + frame.setAttribute("src", "data:text/html,<html><body></body></html>"); + }, false); + test.waitUntilDone(); +}; + +exports['test:pageWorker'] = function(test) { + test.waitUntilDone(); + let worker = Symbiont({ + contentURL: 'about:buildconfig', + contentScript: 'new ' + function WorkerScope() { + self.on('message', function(data) { + if (data.valid) + self.postMessage('bye!'); + }) + self.postMessage(window.location.toString()); + }, + onMessage: function(msg) { + if (msg == 'bye!') { + test.done() + } else { + test.assertEqual( + worker.contentURL + '', + msg + ); + worker.postMessage({ valid: true }); + } + } + }); +}; + +exports["test:document element present on 'start'"] = function(test) { + test.waitUntilDone(); + let xulApp = require("xul-app"); + let worker = Symbiont({ + contentURL: "about:buildconfig", + contentScript: "self.postMessage(!!document.documentElement)", + contentScriptWhen: "start", + onMessage: function(message) { + if (xulApp.versionInRange(xulApp.platformVersion, "2.0b6", "*")) + test.assert(message, "document element present on 'start'"); + else + test.pass("document element not necessarily present on 'start'"); + test.done(); + } + }); +}; + +exports["test:direct communication with trusted document"] = function(test) { + test.waitUntilDone(); + + let worker = Symbiont({ + contentURL: require("self").data.url("test-trusted-document.html") + }); + + worker.port.on('document-to-addon', function (arg) { + test.assertEqual(arg, "ok", "Received an event from the document"); + worker.destroy(); + test.done(); + }); + worker.port.emit('addon-to-document', 'ok'); +}; + +exports["test:`addon` is not available when a content script is set"] = function(test) { + test.waitUntilDone(); + + let worker = Symbiont({ + contentURL: require("self").data.url("test-trusted-document.html"), + contentScript: "new " + function ContentScriptScope() { + self.port.emit("cs-to-addon", "addon" in unsafeWindow); + } + }); + + worker.port.on('cs-to-addon', function (hasAddon) { + test.assertEqual(hasAddon, false, + "`addon` is not available"); + worker.destroy(); + test.done(); + }); +}; diff --git a/tools/addon-sdk-1.4/packages/api-utils/tests/test-content-worker.js b/tools/addon-sdk-1.4/packages/api-utils/tests/test-content-worker.js new file mode 100644 index 0000000..ced0b60 --- /dev/null +++ b/tools/addon-sdk-1.4/packages/api-utils/tests/test-content-worker.js @@ -0,0 +1,374 @@ +"use stirct"; + +const { Cc, Ci } = require('chrome'); +const timer = require('timer'); + +function makeWindow(contentURL) { + let content = + '<?xml version="1.0"?>' + + '<window ' + + 'xmlns="http://www.mozilla.org/keymaster/gatekeeper/there.is.only.xul">' + + '<iframe id="content" type="content" src="' + + encodeURIComponent(contentURL) + '"/>' + + '<script>var documentValue=true;</script>' + + '</window>'; + var url = "data:application/vnd.mozilla.xul+xml," + + encodeURIComponent(content); + var features = ["chrome", "width=10", "height=10"]; + + return Cc["@mozilla.org/embedcomp/window-watcher;1"]. + getService(Ci.nsIWindowWatcher). + openWindow(null, url, null, features.join(","), null); +} + +const { Worker } = require('content/worker'); +exports['test:sample'] = function(test) { + let window = makeWindow(); + test.waitUntilDone(); + + // As window has just being created, its document is still loading, + // and we have about:blank document before the expected one + test.assertEqual(window.document.location.href, "about:blank", + "window starts by loading about:blank"); + + // We need to wait for the load/unload of temporary about:blank + // or our worker is going to be automatically destroyed + window.addEventListener("load", function onload() { + window.removeEventListener("load", onload, true); + + test.assertNotEqual(window.document.location.href, "about:blank", + "window is now on the right document"); + + let worker = Worker({ + window: window, + contentScript: 'new ' + function WorkerScope() { + // window is accessible + let myLocation = window.location.toString(); + self.on('message', function(data) { + if (data == 'hi!') + self.postMessage('bye!'); + }); + }, + contentScriptWhen: 'ready', + onMessage: function(msg) { + test.assertEqual('bye!', msg); + test.assertEqual(worker.url, window.document.location.href, + "worker.url still works"); + test.done(); + } + }); + + test.assertEqual(worker.url, window.document.location.href, + "worker.url works"); + worker.postMessage('hi!'); + + }, true); + +} + +exports['test:emit'] = function(test) { + let window = makeWindow(); + test.waitUntilDone(); + + let worker = Worker({ + window: window, + contentScript: 'new ' + function WorkerScope() { + // Validate self.on and self.emit + self.port.on('addon-to-content', function (data) { + self.port.emit('content-to-addon', data); + }); + + // Check for global pollution + //if (typeof on != "undefined") + // self.postMessage("`on` is in globals"); + if (typeof once != "undefined") + self.postMessage("`once` is in globals"); + if (typeof emit != "undefined") + self.postMessage("`emit` is in globals"); + + }, + onMessage: function(msg) { + test.fail("Got an unexpected message : "+msg); + } + }); + + // Validate worker.port + worker.port.on('content-to-addon', function (data) { + test.assertEqual(data, "event data"); + test.done(); + }); + worker.port.emit('addon-to-content', 'event data'); + +} + +exports['test:emit hack message'] = function(test) { + let window = makeWindow(); + test.waitUntilDone(); + + let worker = Worker({ + window: window, + contentScript: 'new ' + function WorkerScope() { + // Validate self.port + self.port.on('message', function (data) { + self.port.emit('message', data); + }); + // We should not receive message on self, but only on self.port + self.on('message', function (data) { + self.postMessage('message', data); + }); + }, + onError: function(e) { + test.fail("Got exception: "+e); + } + }); + + worker.port.on('message', function (data) { + test.assertEqual(data, "event data"); + test.done(); + }); + worker.on('message', function (data) { + test.fail("Got an unexpected message : "+msg); + }); + worker.port.emit('message', 'event data'); + +} + +exports['test:n-arguments emit'] = function(test) { + let window = makeWindow(); + test.waitUntilDone(); + + let worker = Worker({ + window: window, + contentScript: 'new ' + function WorkerScope() { + // Validate self.on and self.emit + self.port.on('addon-to-content', function (a1, a2, a3) { + self.port.emit('content-to-addon', a1, a2, a3); + }); + } + }); + + // Validate worker.port + worker.port.on('content-to-addon', function (arg1, arg2, arg3) { + test.assertEqual(arg1, "first argument"); + test.assertEqual(arg2, "second"); + test.assertEqual(arg3, "third"); + test.done(); + }); + worker.port.emit('addon-to-content', 'first argument', 'second', 'third'); +} + +exports['test:post-json-values-only'] = function(test) { + let window = makeWindow("data:text/html,"); + test.waitUntilDone(); + + window.addEventListener("load", function onload() { + window.removeEventListener("load", onload, true); + + let worker = Worker({ + window: window.document.getElementById("content").contentWindow, + contentScript: 'new ' + function WorkerScope() { + self.on('message', function (message) { + self.postMessage([ message.fun === undefined, + typeof message.w, + message.w && "port" in message.w, + message.w.url, + Array.isArray(message.array), + JSON.stringify(message.array)]); + }); + } + }); + + // Validate worker.onMessage + let array = [1, 2, 3]; + worker.on('message', function (message) { + test.assert(message[0], "function becomes undefined"); + test.assertEqual(message[1], "object", "object stays object"); + test.assert(message[2], "object's attributes are enumerable"); + test.assertEqual(message[3], "about:blank", "jsonable attributes are accessible"); + // See bug 714891, Arrays may be broken over compartements: + test.assert(message[4], "Array keeps being an array"); + test.assertEqual(message[5], JSON.stringify(array), + "Array is correctly serialized"); + test.done(); + }); + worker.postMessage({ fun: function () {}, w: worker, array: array }); + + }, true); + +}; + +exports['test:emit-json-values-only'] = function(test) { + let window = makeWindow("data:text/html,"); + test.waitUntilDone(); + + window.addEventListener("load", function onload() { + window.removeEventListener("load", onload, true); + + let win = window.document.getElementById("content").contentWindow; + let worker = Worker({ + window: win, + contentScript: 'new ' + function WorkerScope() { + // Validate self.on and self.emit + self.port.on('addon-to-content', function (fun, w, obj, array) { + self.port.emit('content-to-addon', [ + fun === null, + typeof w, + "port" in w, + w.url, + "fun" in obj, + Object.keys(obj.dom).length, + Array.isArray(array), + JSON.stringify(array) + ]); + }); + } + }); + + // Validate worker.port + let array = [1, 2, 3]; + worker.port.on('content-to-addon', function (result) { + test.assert(result[0], "functions become null"); + test.assertEqual(result[1], "object", "objects stay objects"); + test.assert(result[2], "object's attributes are enumerable"); + test.assertEqual(result[3], "about:blank", "json attribute is accessible"); + test.assert(!result[4], "function as object attribute is removed"); + test.assertEqual(result[5], 0, "DOM nodes are converted into empty object"); + // See bug 714891, Arrays may be broken over compartements: + test.assert(result[6], "Array keeps being an array"); + test.assertEqual(result[7], JSON.stringify(array), + "Array is correctly serialized"); + test.done(); + }); + let obj = { + fun: function () {}, + dom: win.document.documentElement + }; + worker.port.emit('addon-to-content', function () {}, worker, obj, array); + + }, true); + +} + +exports['test:content is wrapped'] = function(test) { + let contentURL = 'data:text/html,<script>var documentValue=true;</script>'; + let window = makeWindow(contentURL); + test.waitUntilDone(); + + window.addEventListener("load", function onload() { + window.removeEventListener("load", onload, true); + + let worker = Worker({ + window: window.document.getElementById("content").contentWindow, + contentScript: 'new ' + function WorkerScope() { + self.postMessage(!window.documentValue); + }, + contentScriptWhen: 'ready', + onMessage: function(msg) { + test.assert(msg, + "content script has a wrapped access to content document"); + test.done(); + } + }); + + }, true); + +} + +exports['test:chrome is unwrapped'] = function(test) { + let window = makeWindow(); + test.waitUntilDone(); + + window.addEventListener("load", function onload() { + window.removeEventListener("load", onload, true); + + let worker = Worker({ + window: window, + contentScript: 'new ' + function WorkerScope() { + self.postMessage(window.documentValue); + }, + contentScriptWhen: 'ready', + onMessage: function(msg) { + test.assert(msg, + "content script has an unwrapped access to chrome document"); + test.done(); + } + }); + + }, true); + +} + +exports['test:setTimeout can\'t be cancelled by content'] = function(test) { + let contentURL = 'data:text/html,<script>var documentValue=true;</script>'; + let window = makeWindow(contentURL); + test.waitUntilDone(); + + window.addEventListener("load", function onload() { + window.removeEventListener("load", onload, true); + + let worker = Worker({ + window: window.document.getElementById("content").contentWindow, + contentScript: 'new ' + function WorkerScope() { + let id = setTimeout(function () { + self.postMessage("timeout"); + }, 100); + unsafeWindow.eval("clearTimeout("+id+");"); + }, + contentScriptWhen: 'ready', + onMessage: function(msg) { + test.assert(msg, + "content didn't managed to cancel our setTimeout"); + test.done(); + } + }); + + }, true); + +} + +exports['test:setTimeout are unregistered on content unload'] = function(test) { + let contentURL = 'data:text/html,foo'; + let window = makeWindow(contentURL); + test.waitUntilDone(); + + window.addEventListener("load", function onload() { + window.removeEventListener("load", onload, true); + + let iframe = window.document.getElementById("content"); + let originalDocument = iframe.contentDocument; + let worker = Worker({ + window: iframe.contentWindow, + contentScript: 'new ' + function WorkerScope() { + document.title = "ok"; + let i = 0; + setInterval(function () { + document.title = i++; + }, 10); + }, + contentScriptWhen: 'ready' + }); + + // Change location so that content script is destroyed, + // and all setTimeout/setInterval should be unregistered. + // Wait some cycles in order to execute some intervals. + timer.setTimeout(function () { + // Bug 689621: Wait for the new document load so that we are sure that + // previous document cancelled its intervals + iframe.addEventListener("load", function onload() { + iframe.removeEventListener("load", onload, true); + let titleAfterLoad = originalDocument.title; + // Wait additional cycles to verify that intervals are really cancelled + timer.setTimeout(function () { + test.assertEqual(iframe.contentDocument.title, "final", + "New document has not been modified"); + test.assertEqual(originalDocument.title, titleAfterLoad, + "Nor previous one"); + test.done(); + }, 100); + }, true); + iframe.setAttribute("src", "data:text/html,<title>final</title>"); + }, 100); + + }, true); + +} diff --git a/tools/addon-sdk-1.4/packages/api-utils/tests/test-cortex.js b/tools/addon-sdk-1.4/packages/api-utils/tests/test-cortex.js new file mode 100644 index 0000000..11ed397 --- /dev/null +++ b/tools/addon-sdk-1.4/packages/api-utils/tests/test-cortex.js @@ -0,0 +1,118 @@ +// vim:set ts=2 sw=2 sts=2 + +"use strict"; + +var Cortex = require("cortex").Cortex; + +exports["test property changes propagate"] = function (assert) { + var source = { + _foo: "secret", + get foo() { + return this._foo; + }, + set foo(value) { + this._foo = value; + }, + get getOnly() { + return this._foo; + }, + set setOnly(value) { + this._setOnly = value; + }, + bar: "public", + method: function method(a, b) { + return this._foo + a + b + } + }; + var fixture = Cortex(source); + + assert.ok(!('_foo' in fixture), + "properties that start with `_` are omitted"); + assert.equal(fixture.foo, "secret", "get accessor alias works"); + fixture.foo = "new secret"; + assert.equal(fixture.foo, "new secret", "set accessor alias works"); + assert.equal(source.foo, "new secret", "accessor delegates to the source"); + assert.equal(fixture.bar, "public", "data property alias works"); + fixture.bar = "bar"; + assert.equal(source.bar, "bar", "data property change propagates"); + source.bar = "foo" + assert.equal(fixture.bar, "foo", "data property change probagets back"); + assert.equal(fixture.method("a", "b"), "new secretab", + "public methods are callable"); + assert.equal(fixture.method.call({ _foo: "test" }, " a,", "b"), + "new secret a,b", + "`this` pseudo-variable can not be passed through call."); + assert.equal(fixture.method.apply({ _foo: "test" }, [" a,", "b"]), + "new secret a,b", + "`this` pseudo-variable can not be passed through apply."); + assert.equal(fixture.getOnly, source._foo, + "getter returned property of wrapped object"); + fixture.setOnly = 'bar' + assert.equal(source._setOnly, 'bar', "setter modified wrapped object") +}; + + +exports["test immunity of inheritance"] = function(assert) { + function Type() {} + Type.prototype = { + constructor: Type, + _bar: 2, + bar: 3, + get_Foo: function getFoo() { + return this._foo; + } + } + var source = Object.create(Type.prototype, { + _foo: { value: 'secret' }, + getBar: { value: function get_Bar() { + return this.bar + }}, + get_Bar: { value: function getBar() { + return this._bar + }} + }); + + var fixture = Cortex(source); + + assert.ok(Cortex({}, null, Type.prototype) instanceof Type, + "if custom prototype is providede cortex will inherit from it"); + assert.ok(fixture instanceof Type, + "if no prototype is given cortex inherits from object's prototype"); + + source.bar += 1; + assert.notEqual(fixture.bar, source.bar, + "chages of properties don't propagate to non-aliases"); + assert.equal(fixture.getBar(), source.bar, + "methods accessing public properties are bound to the source"); + + fixture._bar += 1; + assert.notEqual(fixture._bar, source._bar, + "changes of non aliased properties don't propagate"); + assert.equal(fixture.get_Bar(), source._bar, + "methods accessing privates are bound to the source"); + assert.notEqual(fixture.get_Foo(), source._foo, + "prototoype methods are not bound to the source"); +} + +exports["test customized public properties"] = function(assert) { + var source = { + _a: 'a', + b: 'b', + get: function get(name) { + return this[name]; + } + }; + + var fixture = Cortex(source, ['_a', 'get']); + fixture._a += "#change"; + + + assert.ok(!("b" in fixture), "non-public own property is not defined"); + assert.equal(fixture.get("b"), source.b, + "public methods preserve access to the private properties"); + assert.equal(fixture._a, source._a, + "custom public property changes propagate"); +} + +//if (require.main == module) + require("test").run(exports); diff --git a/tools/addon-sdk-1.4/packages/api-utils/tests/test-dom.js b/tools/addon-sdk-1.4/packages/api-utils/tests/test-dom.js new file mode 100644 index 0000000..57b1afe --- /dev/null +++ b/tools/addon-sdk-1.4/packages/api-utils/tests/test-dom.js @@ -0,0 +1,84 @@ +"use strict"; + +const events = require("dom/events"); +const { activeBrowserWindow: { document } } = require("window-utils"); +const window = document.window; + +exports["test on / emit"] = function (assert, done) { + let element = document.createElement("div"); + events.on(element, "click", function listener(event) { + assert.equal(event.target, element, "event has correct target"); + events.removeListener(element, "click", listener); + done(); + }); + + events.emit(element, "click", { + category: "MouseEvents", + settings: [ + true, true, window, 0, 0, 0, 0, 0, false, false, false, false, 0, null + ] + }); +}; + +exports["test remove"] = function (assert, done) { + let element = document.createElement("span"); + let l1 = 0; + let l2 = 0; + let options = { + category: "MouseEvents", + settings: [ + true, true, window, 0, 0, 0, 0, 0, false, false, false, false, 0, null + ] + }; + + events.on(element, "click", function listener1(event) { + l1 ++; + assert.equal(event.target, element, "event has correct target"); + events.removeListener(element, "click", listener1); + }); + + events.on(element, "click", function listener2(event) { + l2 ++; + if (l1 < l2) { + assert.equal(l1, 1, "firs listener was called and then romeved"); + events.removeListener(element, "click", listener2); + done(); + } + events.emit(element, "click", options); + }); + + events.emit(element, "click", options); +}; + +exports["test once"] = function (assert, done) { + let element = document.createElement("h1"); + let l1 = 0; + let l2 = 0; + let options = { + category: "MouseEvents", + settings: [ + true, true, window, 0, 0, 0, 0, 0, false, false, false, false, 0, null + ] + }; + + + events.once(element, "click", function listener(event) { + assert.equal(event.target, element, "event target is a correct element"); + l1 ++; + }); + + events.on(element, "click", function listener(event) { + l2 ++; + if (l2 > 3) { + events.removeListener(element, "click", listener); + assert.equal(event.target, element, "event has correct target"); + assert.equal(l1, 1, "once was called only once"); + done(); + } + events.emit(element, "click", options); + }); + + events.emit(element, "click", options); +} + +require("test").run(exports); diff --git a/tools/addon-sdk-1.4/packages/api-utils/tests/test-e10s-porting.js b/tools/addon-sdk-1.4/packages/api-utils/tests/test-e10s-porting.js new file mode 100644 index 0000000..3dc4d32 --- /dev/null +++ b/tools/addon-sdk-1.4/packages/api-utils/tests/test-e10s-porting.js @@ -0,0 +1,73 @@ +// This file just runs all test suites we've white-listed as being +// compatible with E10s. Once we're done with the porting effort, +// we'll just enable cfx's '--e10s' option by default and remove +// this file. + +// This is just to serve as an indicator not to run these tests in +// the addon process. +require("chrome"); + +const E10S_COMPATIBLE_TEST_SUITES = [ + 'test-api-utils.js', + 'test-traits-core.js', + 'test-traits.js', + 'test-list.js', + 'test-self.js' +]; + +exports.runE10SCompatibleTestSuites = function(test) { + var xulApp = require("xul-app"); + if (xulApp.is("Firefox") && + xulApp.versionInRange(xulApp.version, "4.0b7", "4.0b8pre")) { + test.pass("Due to bug 609066, Firefox 4.0b7 will never pass this test, " + + "so we'll skip it."); + return; + } + + // If the "jetpack/service" XPCOM component is not present, then the host + // application does not support e10s, so we can't run any e10s-compatible + // test suites under e10s mode. + if (!require("chrome").Cc["@mozilla.org/jetpack/service;1"]) { + test.pass("This application does not support e10s."); + return; + } + + if (packaging.enableE10s) { + // Don't worry about running these E10S-compatible test + // suites, cfx will find them by default because its + // '--e10s' option is enabled. + test.pass("'cfx --e10s' detected, skipping this test."); + return; + } + + var {TestFinder} = require("unit-test-finder"); + var {TestRunner} = require("unit-test"); + var url = require("url"); + + var thisDir = url.toFilename(url.URL('./', __url__)); + var finder = new TestFinder({ + dirs: [thisDir], + filter: function(name) { + return E10S_COMPATIBLE_TEST_SUITES.indexOf(name) != -1; + }, + testInProcess: false, + testOutOfProcess: true + }); + var runner = new TestRunner(); + finder.findTests(function(tests) { + test.assert(tests.length >= 1, "must find at least one test"); + runner.startMany({ + tests: tests, + onDone: function(runner) { + test.assertEqual(runner.failed, 0, + "No tests in addon process should have failed"); + test.assert(runner.passed > 0, + "Some tests in addon process must have been run"); + test.failed += runner.failed; + test.passed += runner.passed; + test.done(); + } + }); + }); + test.waitUntilDone(); +}; diff --git a/tools/addon-sdk-1.4/packages/api-utils/tests/test-environment.js b/tools/addon-sdk-1.4/packages/api-utils/tests/test-environment.js new file mode 100644 index 0000000..24077a6 --- /dev/null +++ b/tools/addon-sdk-1.4/packages/api-utils/tests/test-environment.js @@ -0,0 +1,46 @@ +'use strict'; + +const { env } = require('api-utils/environment'); +const { Cc, Ci } = require('chrome'); +const { get, set, exists } = Cc['@mozilla.org/process/environment;1']. + getService(Ci.nsIEnvironment); + +exports['test exists'] = function(assert) { + assert.equal('PATH' in env, exists('PATH'), + 'PATH environment variable is defined'); + assert.equal('FOO1' in env, exists('FOO1'), + 'FOO1 environment variable is not defined'); + set('FOO1', 'foo'); + assert.equal('FOO1' in env, true, + 'FOO1 environment variable was set'); + set('FOO1', null); + assert.equal('FOO1' in env, false, + 'FOO1 environment variable was unset'); +}; + +exports['test get'] = function(assert) { + assert.equal(env.PATH, get('PATH'), 'PATH env variable matches'); + assert.equal(env.BAR2, undefined, 'BAR2 env variable is not defined'); + set('BAR2', 'bar'); + assert.equal(env.BAR2, 'bar', 'BAR2 env variable was set'); + set('BAR2', null); + assert.equal(env.BAR2, undefined, 'BAR2 env variable was unset'); +}; + +exports['test set'] = function(assert) { + assert.equal(get('BAZ3'), '', 'BAZ3 env variable is not set'); + assert.equal(env.BAZ3, undefined, 'BAZ3 is not set'); + env.BAZ3 = 'baz'; + assert.equal(env.BAZ3, get('BAZ3'), 'BAZ3 env variable is set'); + assert.equal(get('BAZ3'), 'baz', 'BAZ3 env variable was set to "baz"'); +}; + +exports['test unset'] = function(assert) { + env.BLA4 = 'bla'; + assert.equal(env.BLA4, 'bla', 'BLA4 env varibale is set'); + delete env.BLA4; + assert.equal(env.BLA4, undefined, 'BLA4 env variable is unset'); + assert.equal('BLA4' in env, false, 'BLA4 env variable no longer exists' ); +}; + +require('test').run(exports); diff --git a/tools/addon-sdk-1.4/packages/api-utils/tests/test-errors.js b/tools/addon-sdk-1.4/packages/api-utils/tests/test-errors.js new file mode 100644 index 0000000..6db9c87 --- /dev/null +++ b/tools/addon-sdk-1.4/packages/api-utils/tests/test-errors.js @@ -0,0 +1,66 @@ +var errors = require("errors"); + +exports.testCatchAndLog = function(test) { + var caught = []; + function dummyLog(e) { caught.push(e); } + + var wrapped = errors.catchAndLog(function(x) { + throw Error("blah" + x + this); + }, + "boop", + dummyLog); + test.assertEqual(wrapped.call("hi", 1), "boop", + "exceptions should be trapped, def. resp. returned"); + test.assertEqual(caught.length, 1, + "logging function should be called"); + test.assertEqual(caught[0].message, "blah1hi", + "args and this should be passed to wrapped func"); +}; + +exports.testCatchAndLogProps = function(test) { + var caught = []; + function dummyLog(e) { caught.push(e); } + + var thing = { + foo: function(x) { throw Error("nowai" + x); }, + bar: function() { throw Error("blah"); }, + baz: function() { throw Error("fnarg"); } + }; + + errors.catchAndLogProps(thing, "foo", "ugh", dummyLog); + + test.assertEqual(thing.foo(1), "ugh", + "props should be wrapped"); + test.assertEqual(caught.length, 1, + "logging function should be called"); + test.assertEqual(caught[0].message, "nowai1", + "args should be passed to wrapped func"); + test.assertRaises(function() { thing.bar(); }, + "blah", + "non-wrapped props should be wrapped"); + + errors.catchAndLogProps(thing, ["bar", "baz"], "err", dummyLog); + test.assert((thing.bar() == thing.baz()) && + (thing.bar() == "err"), + "multiple props should be wrapped if array passed in"); +}; + +exports.testCatchAndReturn = function(test) { + var wrapped = errors.catchAndReturn(function(x) { + if (x == 1) + return "one"; + if (x == 2) + throw new Error("two"); + return this + x; + }); + + test.assertEqual(wrapped(1).returnValue, "one", + "arg should be passed; return value should be returned"); + test.assert(wrapped(2).exception, "exception should be returned"); + test.assertEqual(wrapped(2).exception.message, "two", "message is correct"); + test.assert(wrapped(2).exception.fileName.indexOf("test-errors.js") != -1, + "filename is present"); + test.assert(wrapped(2).exception.stack, "stack is available"); + test.assertEqual(wrapped.call("hi", 3).returnValue, "hi3", + "`this` should be set correctly"); +}; diff --git a/tools/addon-sdk-1.4/packages/api-utils/tests/test-events.js b/tools/addon-sdk-1.4/packages/api-utils/tests/test-events.js new file mode 100644 index 0000000..8bd846b --- /dev/null +++ b/tools/addon-sdk-1.4/packages/api-utils/tests/test-events.js @@ -0,0 +1,182 @@ +'use strict'; + +// Exposing private methods as public in order to test +const EventEmitter = require('events').EventEmitter.compose({ + listeners: function(type) this._listeners(type), + emit: function() this._emit.apply(this, arguments), + emitOnObject: function() this._emitOnObject.apply(this, arguments), + removeAllListeners: function(type) this._removeAllListeners(type) +}); + +exports['test:add listeners'] = function(test) { + let e = new EventEmitter(); + + let events_new_listener_emited = []; + let times_hello_emited = 0; + + e.on("newListener", function (event, listener) { + events_new_listener_emited.push(event) + }) + + e.on("hello", function (a, b) { + times_hello_emited += 1 + test.assertEqual("a", a) + test.assertEqual("b", b) + test.assertEqual(this, e, '`this` pseudo-variable is bound to instance'); + }) + + e.emit("hello", "a", "b") +}; + +exports['test:remove listeners'] = function(test) { + let count = 0; + + function listener1 () { + count++; + } + function listener2 () { + count++; + } + function listener3 () { + count++; + } + + let e1 = new EventEmitter(); + e1.on("hello", listener1); + test.assertEqual(1, e1.listeners('hello').length); + e1.removeListener("hello", listener1); + test.assertEqual(0, e1.listeners('hello').length); + + let e2 = new EventEmitter(); + e2.on("hello", listener1); + test.assertEqual(1, e2.listeners('hello').length); + e2.removeListener("hello", listener2); + test.assertEqual(1, e2.listeners('hello').length); + test.assertEqual(listener1, e2.listeners('hello')[0]); + + let e3 = new EventEmitter(); + e3.on("hello", listener1); + test.assertEqual(1, e3.listeners('hello').length); + e3.on("hello", listener2); + test.assertEqual(2, e3.listeners('hello').length); + e3.removeListener("hello", listener1); + test.assertEqual(1, e3.listeners('hello').length); + test.assertEqual(listener2, e3.listeners('hello')[0]); +}; + +exports['test: modify in emit'] = function(test) { + let callbacks_called = [ ]; + let e = new EventEmitter(); + + function callback1() { + callbacks_called.push("callback1"); + e.on("foo", callback2); + e.on("foo", callback3); + e.removeListener("foo", callback1); + } + function callback2() { + callbacks_called.push("callback2"); + e.removeListener("foo", callback2); + } + function callback3() { + callbacks_called.push("callback3"); + e.removeListener("foo", callback3); + } + + e.on("foo", callback1); + test.assertEqual(1, e.listeners("foo").length); + + e.emit("foo"); + test.assertEqual(2, e.listeners("foo").length); + test.assertEqual(1, callbacks_called.length); + test.assertEqual('callback1', callbacks_called[0]); + + e.emit("foo"); + test.assertEqual(0, e.listeners("foo").length); + test.assertEqual(3, callbacks_called.length); + test.assertEqual('callback1', callbacks_called[0]); + test.assertEqual('callback2', callbacks_called[1]); + test.assertEqual('callback3', callbacks_called[2]); + + e.emit("foo"); + test.assertEqual(0, e.listeners("foo").length); + test.assertEqual(3, callbacks_called.length); + test.assertEqual('callback1', callbacks_called[0]); + test.assertEqual('callback2', callbacks_called[1]); + test.assertEqual('callback3', callbacks_called[2]); + + e.on("foo", callback1); + e.on("foo", callback2); + test.assertEqual(2, e.listeners("foo").length); + e.removeAllListeners("foo"); + test.assertEqual(0, e.listeners("foo").length); + + // Verify that removing callbacks while in emit allows emits to propagate to + // all listeners + callbacks_called = [ ]; + + e.on("foo", callback2); + e.on("foo", callback3); + test.assertEqual(2, e.listeners("foo").length); + e.emit("foo"); + test.assertEqual(2, callbacks_called.length); + test.assertEqual('callback2', callbacks_called[0]); + test.assertEqual('callback3', callbacks_called[1]); + test.assertEqual(0, e.listeners("foo").length); +}; + +exports['test:adding same listener'] = function(test) { + function foo() {} + let e = new EventEmitter(); + e.on("foo", foo); + e.on("foo", foo); + test.assertEqual( + 1, + e.listeners("foo").length, + "listener reregistration is ignored" + ); +} + +exports['test:errors are reported if listener throws'] = function(test) { + let e = new EventEmitter(), + reported = false; + e.on('error', function(e) reported = true) + e.on('boom', function() { throw new Error('Boom!') }); + e.emit('boom', 3); + test.assert(reported, 'error should be reported through event'); +}; + +exports['test:emitOnObject'] = function(test) { + let e = new EventEmitter(); + + e.on("foo", function() { + test.assertEqual(this, e, "`this` should be emitter"); + }); + e.emitOnObject(e, "foo"); + + e.on("bar", function() { + test.assertEqual(this, obj, "`this` should be other object"); + }); + let obj = {}; + e.emitOnObject(obj, "bar"); +}; + +exports['test:once'] = function(test) { + let e = new EventEmitter(); + let called = false; + + e.once('foo', function(value) { + test.assert(!called, "listener called only once"); + test.assertEqual(value, "bar", "correct argument was passed"); + }); + + e.emit('foo', 'bar'); + e.emit('foo', 'baz'); +}; + +exports["test romeving once"] = function(test) { + let e = require("events").EventEmitterTrait.create(); + e.once("foo", function() { test.pass("listener was called"); }); + e.once("error", function() { test.fail("error event was emitted"); }); + e._emit("foo", "bug-656684"); +}; diff --git a/tools/addon-sdk-1.4/packages/api-utils/tests/test-file.js b/tools/addon-sdk-1.4/packages/api-utils/tests/test-file.js new file mode 100644 index 0000000..5b1314a --- /dev/null +++ b/tools/addon-sdk-1.4/packages/api-utils/tests/test-file.js @@ -0,0 +1,276 @@ +var file = require("file"); +var url = require("url"); +var byteStreams = require("byte-streams"); +var textStreams = require("text-streams"); + +const ERRORS = { + FILE_NOT_FOUND: /^path does not exist: .+$/, + NOT_A_DIRECTORY: /^path is not a directory: .+$/, + NOT_A_FILE: /^path is not a file: .+$/, +}; + +var myurl = module.uri; +var mydir = myurl.slice(0, -("test-file.js".length)); +var otherdir = mydir + "modules/"; + +exports.testDirName = function(test) { + var aDir = url.toFilename(otherdir); + test.assertEqual(file.dirname(aDir), + aDir.slice(0, aDir.lastIndexOf("modules")-1), + "file.dirname() of dir should return parent dir"); + + aDir = url.toFilename(myurl); + test.assertEqual(file.dirname(aDir), + aDir.slice(0, aDir.lastIndexOf("test-file")-1), + "file.dirname() of file should return its dir"); + + while (aDir) + aDir = file.dirname(aDir); + test.assertEqual(aDir, "", + "dirname should return empty string when dir has no parent"); +}; + +exports.testBasename = function(test) { + // Get the top-most path -- the path with no basename. E.g., on Unix-like + // systems this will be /. We'll use it below to build up some test paths. + // We have to go to this trouble because file.join() needs a legal path as a + // base case; join("foo", "bar") doesn't work unfortunately. + var topPath = url.toFilename(myurl); + var parentPath = file.dirname(topPath); + while (parentPath) { + topPath = parentPath; + parentPath = file.dirname(topPath); + } + + var path = topPath; + test.assertEqual(file.basename(path), "", + "basename should work on paths with no components"); + + path = file.join(path, "foo"); + test.assertEqual(file.basename(path), "foo", + "basename should work on paths with a single component"); + + path = file.join(path, "bar"); + test.assertEqual(file.basename(path), "bar", + "basename should work on paths with multiple components"); +}; + +exports.testList = function(test) { + var list = file.list(url.toFilename(otherdir)); + var found = [true for each (name in list) + if (name == "add.js")]; + if (found.length > 1) + test.fail("a dir can't contain two files of the same name!"); + test.assertEqual(found[0], true, "file.list() should work"); + + test.assertRaises( + function() { file.list(url.toFilename(module.uri)); }, + ERRORS.NOT_A_DIRECTORY, + "file.list() on non-dir should raise error" + ); + + test.assertRaises( + function() { file.list(url.toFilename(mydir + "foo/")); }, + ERRORS.FILE_NOT_FOUND, + "file.list() on nonexistent dir should raise error" + ); +}; + +exports.testRead = function(test) { + var filename = url.toFilename(module.uri); + var contents = file.read(filename); + test.assertMatches(contents, /file\.read\(\) should work/, + "file.read() should work"); + + test.assertRaises( + function() { file.read(filename + "blah"); }, + ERRORS.FILE_NOT_FOUND, + "file.read() on nonexistent file should raise error" + ); + + test.assertRaises( + function() { file.read(url.toFilename(otherdir)); }, + ERRORS.NOT_A_FILE, + "file.read() on dir should raise error" + ); +}; + +exports.testJoin = function(test) { + var filename = url.toFilename(myurl); + var baseDir = file.dirname(filename); + + test.assertEqual(file.join(baseDir, "test-file.js"), + filename, + "file.join() should work"); +}; + +exports.testOpenNonexistentForRead = function (test) { + var filename = dataFileFilename(test); + test.assertRaises(function () file.open(filename), + ERRORS.FILE_NOT_FOUND, + "file.open() on nonexistent file should raise error"); + test.assertRaises(function () file.open(filename, "r"), + ERRORS.FILE_NOT_FOUND, + "file.open('r') on nonexistent file should raise error"); + test.assertRaises(function () file.open(filename, "zzz"), + ERRORS.FILE_NOT_FOUND, + "file.open('zzz') on nonexistent file should raise error"); +}; + +exports.testOpenNonexistentForWrite = function (test) { + var filename = dataFileFilename(test); + + var stream = file.open(filename, "w"); + stream.close(); + + test.assert(file.exists(filename), + "file.exists() should return true after file.open('w')"); + file.remove(filename); + test.assert(!file.exists(filename), + "file.exists() should return false after file.remove()"); + + stream = file.open(filename, "rw"); + stream.close(); + + test.assert(file.exists(filename), + "file.exists() should return true after file.open('rw')"); + file.remove(filename); + test.assert(!file.exists(filename), + "file.exists() should return false after file.remove()"); +}; + +exports.testOpenDirectory = function (test) { + var dir = file.dirname(url.toFilename(module.uri)); + test.assertRaises(function () file.open(dir), + ERRORS.NOT_A_FILE, + "file.open() on directory should raise error"); + test.assertRaises(function () file.open(dir, "w"), + ERRORS.NOT_A_FILE, + "file.open('w') on directory should raise error"); +}; + +exports.testOpenTypes = function (test) { + var filename = dataFileFilename(test); + + // Do the opens first to create the data file. + var stream = file.open(filename, "w"); + test.assert(stream instanceof textStreams.TextWriter, + "open(w) should return a TextWriter"); + stream.close(); + + stream = file.open(filename, "wb"); + test.assert(stream instanceof byteStreams.ByteWriter, + "open(wb) should return a ByteWriter"); + stream.close(); + + stream = file.open(filename); + test.assert(stream instanceof textStreams.TextReader, + "open() should return a TextReader"); + stream.close(); + + stream = file.open(filename, "r"); + test.assert(stream instanceof textStreams.TextReader, + "open(r) should return a TextReader"); + stream.close(); + + stream = file.open(filename, "b"); + test.assert(stream instanceof byteStreams.ByteReader, + "open(b) should return a ByteReader"); + stream.close(); + + stream = file.open(filename, "rb"); + test.assert(stream instanceof byteStreams.ByteReader, + "open(rb) should return a ByteReader"); + stream.close(); + + file.remove(filename); +}; + +exports.testMkpathRmdir = function (test) { + var basePath = file.dirname(url.toFilename(module.uri)); + var dirs = []; + for (var i = 0; i < 3; i++) + dirs.push("test-file-dir"); + var paths = []; + for (var i = 0; i < dirs.length; i++) { + var args = [basePath].concat(dirs.slice(0, i + 1)); + paths.unshift(file.join.apply(null, args)); + } + for (i = 0; i < paths.length; i++) { + test.assert(!file.exists(paths[i]), + "Sanity check: path should not exist: " + paths[i]); + } + file.mkpath(paths[0]); + test.assert(file.exists(paths[0]), "mkpath should create path: " + paths[0]); + for (i = 0; i < paths.length; i++) { + file.rmdir(paths[i]); + test.assert(!file.exists(paths[i]), + "rmdir should remove path: " + paths[i]); + } +}; + +exports.testMkpathTwice = function (test) { + var dir = file.dirname(url.toFilename(module.uri)); + var path = file.join(dir, "test-file-dir"); + test.assert(!file.exists(path), + "Sanity check: path should not exist: " + path); + file.mkpath(path); + test.assert(file.exists(path), "mkpath should create path: " + path); + file.mkpath(path); + test.assert(file.exists(path), + "After second mkpath, path should still exist: " + path); + file.rmdir(path); + test.assert(!file.exists(path), "rmdir should remove path: " + path); +}; + +exports.testMkpathExistingNondirectory = function (test) { + var fname = dataFileFilename(test); + file.open(fname, "w").close(); + test.assert(file.exists(fname), "File should exist"); + test.assertRaises(function () file.mkpath(fname), + /^The path already exists and is not a directory: .+$/, + "mkpath on file should raise error"); + file.remove(fname); +}; + +exports.testRmdirNondirectory = function (test) { + var fname = dataFileFilename(test); + file.open(fname, "w").close(); + test.assert(file.exists(fname), "File should exist"); + test.assertRaises(function () file.rmdir(fname), + ERRORS.NOT_A_DIRECTORY, + "rmdir on file should raise error"); + file.remove(fname); + test.assert(!file.exists(fname), "File should not exist"); + test.assertRaises(function () file.rmdir(fname), + ERRORS.FILE_NOT_FOUND, + "rmdir on non-existing file should raise error"); +}; + +exports.testRmdirNonempty = function (test) { + var dir = file.dirname(url.toFilename(module.uri)); + var path = file.join(dir, "test-file-dir"); + test.assert(!file.exists(path), + "Sanity check: path should not exist: " + path); + file.mkpath(path); + var filePath = file.join(path, "file"); + file.open(filePath, "w").close(); + test.assert(file.exists(filePath), + "Sanity check: path should exist: " + filePath); + test.assertRaises(function () file.rmdir(path), + /^The directory is not empty: .+$/, + "rmdir on non-empty directory should raise error"); + file.remove(filePath); + file.rmdir(path); + test.assert(!file.exists(path), "Path should not exist"); +}; + +// Returns the name of a file that should be used to test writing and reading. +function dataFileFilename(test) { + var dir = file.dirname(url.toFilename(module.uri)); + var fname = file.join(dir, "test-file-data"); + test.assert(!file.exists(fname), + "Sanity check: the file that this test assumes does not " + + "exist should really not exist!"); + return fname; +} diff --git a/tools/addon-sdk-1.4/packages/api-utils/tests/test-function-utils.js b/tools/addon-sdk-1.4/packages/api-utils/tests/test-function-utils.js new file mode 100644 index 0000000..292380a --- /dev/null +++ b/tools/addon-sdk-1.4/packages/api-utils/tests/test-function-utils.js @@ -0,0 +1,23 @@ +const { invoke, Enqueued } = require('utils/function'); + +exports['test forwardApply'] = function(test) { + function sum(b, c) this.a + b + c + test.assertEqual(invoke(sum, [2, 3], { a: 1 }), 6, + 'passed arguments and pseoude-variable are used'); + test.assertEqual(invoke(sum.bind({ a: 2 }), [2, 3], { a: 1 }), 7, + 'bounded `this` pseoudo variable is used') +} + +exports['test enqueued function'] = function(test) { + test.waitUntilDone(); + let nextTurn = false; + function sum(b, c) { + test.assert(nextTurn, 'enqueued is called in next turn of event loop'); + test.assertEqual(this.a + b + c, 6, + 'passed arguments an pseoude-variable are used'); + test.done(); + } + let fixture = { a: 1, method: Enqueued(sum) } + fixture.method(2, 3); + nextTurn = true; +} diff --git a/tools/addon-sdk-1.4/packages/api-utils/tests/test-globals.js b/tools/addon-sdk-1.4/packages/api-utils/tests/test-globals.js new file mode 100644 index 0000000..97cb774 --- /dev/null +++ b/tools/addon-sdk-1.4/packages/api-utils/tests/test-globals.js @@ -0,0 +1,21 @@ +Object.defineProperty(this, "global", { value: this }); + +exports.testGlobals = function(test) { + // the only globals in module scope should be: + // module, exports, require, dump, console + test.assertObject(module, "have 'module', good"); + test.assertObject(exports, "have 'exports', good"); + test.assertFunction(require, "have 'require', good"); + test.assertFunction(dump, "have 'dump', good"); + test.assertObject(console, "have 'console', good"); + + // in particular, these old globals should no longer be present + test.assert(!('packaging' in global), "no 'packaging', good"); + // this will be fixed by bug 620559 + test.expectFail(function() { + test.assert(!('memory' in global), "no 'memory', good"); + }); + + test.assertMatches(module.uri, /test-globals\.js$/, + 'should contain filename'); +}; diff --git a/tools/addon-sdk-1.4/packages/api-utils/tests/test-hidden-frame.js b/tools/addon-sdk-1.4/packages/api-utils/tests/test-hidden-frame.js new file mode 100644 index 0000000..c92046b --- /dev/null +++ b/tools/addon-sdk-1.4/packages/api-utils/tests/test-hidden-frame.js @@ -0,0 +1,47 @@ +let tests = {}, hiddenFrames, HiddenFrame; + +tests.testFrame = function(test) { + let url = "data:text/html,<!DOCTYPE%20html>"; + test.waitUntilDone(); + let hiddenFrame = hiddenFrames.add(HiddenFrame({ + onReady: function () { + test.assertEqual(this.element.contentWindow.location, "about:blank", + "HiddenFrame loads about:blank by default."); + + function onDOMReady() { + hiddenFrame.element.removeEventListener("DOMContentLoaded", onDOMReady, + false); + test.assertEqual(hiddenFrame.element.contentWindow.location, url, + "HiddenFrame loads the specified content."); + test.done(); + } + this.element.addEventListener("DOMContentLoaded", onDOMReady, false); + this.element.setAttribute("src", url); + } + })); +}; + +let hiddenFrameSupported = true; + +try { + hiddenFrames = require("hidden-frame"); + HiddenFrame = hiddenFrames.HiddenFrame; +} +catch(ex if ex.message == [ + "The hidden-frame module currently supports only Firefox and Thunderbird. ", + "In the future, we would like it to support other applications, however. ", + "Please see https://bugzilla.mozilla.org/show_bug.cgi?id=546740 for more ", + "information." + ].join("")) { + hiddenFrameSupported = false; +} + +if (hiddenFrameSupported) { + for (let test in tests) + exports[test] = tests[test]; +} +else { + exports.testHiddenFrameNotSupported = function(test) { + test.pass("The hidden-frame module is not supported on this app."); + } +} diff --git a/tools/addon-sdk-1.4/packages/api-utils/tests/test-httpd.js b/tools/addon-sdk-1.4/packages/api-utils/tests/test-httpd.js new file mode 100644 index 0000000..42734c1 --- /dev/null +++ b/tools/addon-sdk-1.4/packages/api-utils/tests/test-httpd.js @@ -0,0 +1,28 @@ +exports.testBasicHTTPServer = function(test) { + var port = 8080; + var data = require("self").data; + var testFilePath = require("url").toFilename(data.url("test-httpd.txt")); + var basePath = require("file").dirname(testFilePath); + var {startServerAsync} = require("httpd"); + + var srv = startServerAsync(port, basePath); + + test.waitUntilDone(); + + // Request this very file. + var Request = require('request').Request; + Request({ + url: "http://localhost:" + port + "/test-httpd.txt", + onComplete: function (response) { + test.assertEqual(response.text, "This is the HTTPD test file.\n"); + done(); + } + }).get(); + + + function done() { + srv.stop(function() { + test.done(); + }); + } +}; diff --git a/tools/addon-sdk-1.4/packages/api-utils/tests/test-keyboard-observer.js b/tools/addon-sdk-1.4/packages/api-utils/tests/test-keyboard-observer.js new file mode 100644 index 0000000..27d4276 --- /dev/null +++ b/tools/addon-sdk-1.4/packages/api-utils/tests/test-keyboard-observer.js @@ -0,0 +1,32 @@ +"use strict"; + +const { keyPress } = require("api-utils/dom/events/keys"); +const { Loader } = require("./helpers"); + +exports["test unload keyboard observer"] = function(assert, done) { + let loader = Loader(module); + let element = loader.require("api-utils/window-utils"). + activeBrowserWindow.document.documentElement; + let observer = loader.require("api-utils/keyboard/observer"). + observer; + let called = 0; + + observer.on("keypress", function () { called++; }); + + // dispatching "keypress" event to trigger observer listeners. + keyPress(element, "accel-%"); + + // Unload the module. + loader.unload(); + + // dispatching "keypress" even once again. + keyPress(element, "accel-%"); + + // Enqueuing asserts to make sure that assertion is not performed early. + require("timer").setTimeout(function () { + assert.equal(called, 1, "observer was called before unload only."); + done(); + }, 0); +}; + +require("test").run(exports); diff --git a/tools/addon-sdk-1.4/packages/api-utils/tests/test-keyboard-utils.js b/tools/addon-sdk-1.4/packages/api-utils/tests/test-keyboard-utils.js new file mode 100644 index 0000000..137e6b1 --- /dev/null +++ b/tools/addon-sdk-1.4/packages/api-utils/tests/test-keyboard-utils.js @@ -0,0 +1,58 @@ +"use strict"; + +const utils = require("keyboard/utils"); +const runtime = require("runtime"); + +const isMac = runtime.OS === "Darwin"; + +exports["test toString"] = function(assert) { + assert.equal(utils.toString({ + key: "B", + modifiers: [ "Shift", "Ctrl" ] + }), "Shift-Ctrl-B", "toString does not normalizes JSON"); + + assert.equal(utils.toString({ + key: "C", + modifiers: [], + }), "C", "Works with objects with empty array of modifiers"); + + assert.equal(utils.toString(Object.create((function Type() {}).prototype, { + key: { value: "d" }, + modifiers: { value: [ "alt" ] }, + method: { value: function() {} } + })), "alt-d", "Works with non-json objects"); + + assert.equal(utils.toString({ + modifiers: [ "shift", "alt" ] + }), "shift-alt-", "works with only modifiers"); +}; + +exports["test toJSON"] = function(assert) { + assert.deepEqual(utils.toJSON("Shift-Ctrl-B"), { + key: "b", + modifiers: [ "control", "shift" ] + }, "toJSON normalizes input"); + + assert.deepEqual(utils.toJSON("Meta-Alt-option-C"), { + key: "c", + modifiers: [ "alt", "meta" ] + }, "removes dublicates"); + + assert.deepEqual(utils.toJSON("AccEl+sHiFt+Z", "+"), { + key: "z", + modifiers: isMac ? [ "meta", "shift" ] : [ "control", "shift" ] + }, "normalizes OS specific keys and adjustes seperator"); +}; + +exports["test normalize"] = function assert(assert) { + assert.equal(utils.normalize("Shift Ctrl A control ctrl", " "), + "control shift a", "removes reapeted modifiers"); + assert.equal(utils.normalize("shift-ctrl-left"), "control-shift-left", + "normilizes non printed characters"); + + assert.throws(function() { + utils.normalize("shift-alt-b-z"); + }, "throws if contains more then on non-modifier key"); +}; + +require("test").run(exports); diff --git a/tools/addon-sdk-1.4/packages/api-utils/tests/test-light-traits.js b/tools/addon-sdk-1.4/packages/api-utils/tests/test-light-traits.js new file mode 100644 index 0000000..4ad97b5 --- /dev/null +++ b/tools/addon-sdk-1.4/packages/api-utils/tests/test-light-traits.js @@ -0,0 +1,7 @@ +"use strict"; + +exports["test traits from objects"] = require("./traits/object-tests"); +exports["test traits from descriptors"] = require("./traits/descriptor-tests"); +exports["test inheritance"] = require("./traits/inheritance-tests"); + +require("test").run(exports); diff --git a/tools/addon-sdk-1.4/packages/api-utils/tests/test-list.js b/tools/addon-sdk-1.4/packages/api-utils/tests/test-list.js new file mode 100644 index 0000000..29abe1a --- /dev/null +++ b/tools/addon-sdk-1.4/packages/api-utils/tests/test-list.js @@ -0,0 +1,203 @@ +"use strict"; + +function assertList(test, array, list) { + for (let i = 0, ii = array.length; i < ii; i < ii, i++) { + test.assertEqual( + array.length, + list.length, + 'list must contain same amount of elements as array' + ); + test.assertEqual( + 'List(' + array + ')', + list + '', + 'toString must output array like result' + ); + test.assert( + i in list, + 'must contain element with index: ' + i + ); + test.assertEqual( + array[i], + list[i], + 'element with index: ' + i + ' should match' + ); + } +} + +const { List } = require('api-utils/list'); + +exports['test:test for'] = function(test) { + let fixture = List(3, 2, 1); + + test.assertEqual(3, fixture.length, 'length is 3'); + let i = 0; + for (let key in fixture) { + test.assertEqual(i++, key, 'key should match'); + } +}; + +exports['test:test for each'] = function(test) { + let fixture = new List(3, 2, 1); + + test.assertEqual(3, fixture.length, 'length is 3'); + let i = 3; + for each (let value in fixture) { + test.assertEqual(i--, value, 'value should match'); + } +}; + +exports['test: for each using Iterator'] = function(test) { + let fixture = new List(3, 2, 1); + + test.assertEqual(3, fixture.length, 'length is 3'); + let v = 3, k = 0; + for each (let [key, value] in Iterator(fixture)) { + test.assertEqual(k++, key, 'key should match'); + test.assertEqual(v--, value, 'value should match'); + } +}; + +exports['test:test toString'] = function(test) { + let fixture = List(3, 2, 1); + + test.assertEqual( + 'List(3,2,1)', + fixture + '', + 'toString must output array like result' + ) +}; + +exports['test:test constructor with apply'] = function(test) { + let array = ['a', 'b', 'c']; + let fixture = List.apply(null, array); + + test.assertEqual( + 3, + fixture.length, + 'should have applied arguments' + ); +}; + +exports['test:direct element access'] = function(test) { + let array = [1, 'foo', 2, 'bar', {}, 'bar', function a() {}, test, 1]; + let fixture = List.apply(null, array); + array.splice(5, 1); + array.splice(7, 1); + + test.assertEqual( + array.length, + fixture.length, + 'list should omit duplicate elements' + ); + + test.assertEqual( + 'List(' + array + ')', + fixture.toString(), + 'elements should not be rearranged' + ); + + for (let key in array) { + test.assert(key in fixture,'should contain key for index:' + key); + test.assertEqual( + array[key], + fixture[key], + 'values should match for: ' + key + ); + } +}; + +exports['test:removing adding elements'] = function(test) { + let array = [1, 'foo', 2, 'bar', {}, 'bar', function a() {}, test, 1]; + let fixture = List.compose({ + add: function() this._add.apply(this, arguments), + remove: function() this._remove.apply(this, arguments), + clear: function() this._clear() + }).apply(null, array); + array.splice(5, 1); + array.splice(7, 1); + + assertList(test, array, fixture); + + array.splice(array.indexOf(2), 1); + fixture.remove(2); + assertList(test, array, fixture); + + array.splice(array.indexOf('foo'), 1); + fixture.remove('foo'); + array.splice(array.indexOf(1), 1); + fixture.remove(1); + array.push('foo'); + fixture.add('foo'); + assertList(test, array, fixture); + + array.splice(0); + fixture.clear(0); + assertList(test, array, fixture); + + array.push(1, 'foo', 2, 'bar', 3); + fixture.add(1); + fixture.add('foo'); + fixture.add(2); + fixture.add('bar'); + fixture.add(3); + + assertList(test, array, fixture); +}; + +exports['test: remove does not leave invalid numerical properties'] = function(test) { + let fixture = List.compose({ + remove: function() this._remove.apply(this, arguments), + }).apply(null, [1, 2, 3]); + + fixture.remove(1); + test.assertEqual(fixture[fixture.length], undefined); +} + +exports['test:add list item from Iterator'] = function(test) { + let array = [1, 2, 3, 4], sum = 0, added = false; + + let fixture = List.compose({ + add: function() this._add.apply(this, arguments), + }).apply(null, array); + + for each (let item in fixture) { + sum += item; + + if (!added) { + fixture.add(5); + added = true; + } + } + + test.assertEqual(sum, 1 + 2 + 3 + 4); +}; + +exports['test:remove list item from Iterator'] = function(test) { + let array = [1, 2, 3, 4], sum = 0; + + let fixture = List.compose({ + remove: function() this._remove.apply(this, arguments), + }).apply(null, array); + + for each (let item in fixture) { + sum += item; + fixture.remove(item); + } + + test.assertEqual(sum, 1 + 2 + 3 + 4); +}; + +exports['test:clear list from Iterator'] = function(test) { + let array = [1, 2, 3, 4], sum = 0; + + let fixture = List.compose({ + clear: function() this._clear() + }).apply(null, array); + + for each (let item in fixture) { + sum += item; + fixture.clear(); + } + + test.assertEqual(sum, 1 + 2 + 3 + 4); +}; diff --git a/tools/addon-sdk-1.4/packages/api-utils/tests/test-loader.js b/tools/addon-sdk-1.4/packages/api-utils/tests/test-loader.js new file mode 100644 index 0000000..796d1cb --- /dev/null +++ b/tools/addon-sdk-1.4/packages/api-utils/tests/test-loader.js @@ -0,0 +1,31 @@ +const { Loader } = require("./helpers"); + +exports.testLoader = function(test) { + var prints = []; + function print(message) { + prints.push(message); + } + + var loader = Loader(module, { dump: print, foo: 1 }); + + var fixture = loader.require('./loader/fixture'); + + test.assertEqual(fixture.foo, 1, "custom globals must work."); + + test.assertEqual(prints[0], "info: testing 1 2,3,4\n", + "global console must work."); + + var unloadsCalled = ''; + + loader.require("unload").when(function() { + unloadsCalled += 'a'; + }); + loader.require("unload.js").when(function() { + unloadsCalled += 'b'; + }); + + loader.unload(); + + test.assertEqual(unloadsCalled, 'ba', + "loader.unload() must call cb's in LIFO order."); +}; diff --git a/tools/addon-sdk-1.4/packages/api-utils/tests/test-match-pattern.js b/tools/addon-sdk-1.4/packages/api-utils/tests/test-match-pattern.js new file mode 100644 index 0000000..0352ba9 --- /dev/null +++ b/tools/addon-sdk-1.4/packages/api-utils/tests/test-match-pattern.js @@ -0,0 +1,161 @@ +/* -*- Mode: Java; tab-width: 2; indent-tabs-mode: nil; c-basic-offset: 2 -*- */ +/* vim:set ts=2 sw=2 sts=2 et: */ +/* ***** BEGIN LICENSE BLOCK ***** + * Version: MPL 1.1/GPL 2.0/LGPL 2.1 + * + * The contents of this file are subject to the Mozilla Public License Version + * 1.1 (the "License"); you may not use this file except in compliance with + * the License. You may obtain a copy of the License at + * http://www.mozilla.org/MPL/ + * + * Software distributed under the License is distributed on an "AS IS" basis, + * WITHOUT WARRANTY OF ANY KIND, either express or implied. See the License + * for the specific language governing rights and limitations under the + * License. + * + * The Original Code is Jetpack. + * + * The Initial Developer of the Original Code is + * the Mozilla Foundation. + * Portions created by the Initial Developer are Copyright (C) 2010 + * the Initial Developer. All Rights Reserved. + * + * Contributor(s): + * Irakli Gozalishvili <gozala@mozilla.com> (Original Author) + * Drew Willcoxon <adw@mozilla.com> + * + * Alternatively, the contents of this file may be used under the terms of + * either the GNU General Public License Version 2 or later (the "GPL"), or + * the GNU Lesser General Public License Version 2.1 or later (the "LGPL"), + * in which case the provisions of the GPL or the LGPL are applicable instead + * of those above. If you wish to allow use of your version of this file only + * under the terms of either the GPL or the LGPL, and not to allow others to + * use your version of this file under the terms of the MPL, indicate your + * decision by deleting the provisions above and replace them with the notice + * and other provisions required by the GPL or the LGPL. If you do not delete + * the provisions above, a recipient may use your version of this file under + * the terms of any one of the MPL, the GPL or the LGPL. + * + * ***** END LICENSE BLOCK ***** */ + +const { MatchPattern } = require("match-pattern"); + +exports.testMatchPatternTestTrue = function(test) { + function ok(pattern, url) { + let mp = new MatchPattern(pattern); + test.assert(mp.test(url), pattern + " should match " + url); + } + + ok("*", "http://example.com"); + ok("*", "https://example.com"); + ok("*", "ftp://example.com"); + + ok("*.example.com", "http://example.com"); + ok("*.example.com", "http://hamburger.example.com"); + ok("*.example.com", "http://hotdog.hamburger.example.com"); + + ok("http://example.com*", "http://example.com"); + ok("http://example.com*", "http://example.com/"); + ok("http://example.com/*", "http://example.com/"); + ok("http://example.com/*", "http://example.com/potato-salad"); + ok("http://example.com/pickles/*", "http://example.com/pickles/"); + ok("http://example.com/pickles/*", "http://example.com/pickles/lemonade"); + + ok("http://example.com", "http://example.com"); + ok("http://example.com/ice-cream", "http://example.com/ice-cream"); + + ok(/.*zilla.*/, "https://bugzilla.redhat.com/show_bug.cgi?id=569753"); + ok(/https:.*zilla.*/, "https://bugzilla.redhat.com/show_bug.cgi?id=569753"); +}; + +exports.testMatchPatternTestFalse = function(test) { + function ok(pattern, url) { + let mp = new MatchPattern(pattern); + test.assert(!mp.test(url), pattern + " should not match " + url); + } + + ok("*", null); + ok("*", ""); + ok("*", "bogus"); + ok("*", "chrome://browser/content/browser.xul"); + ok("*", "nttp://example.com"); + + ok("*.example.com", null); + ok("*.example.com", ""); + ok("*.example.com", "bogus"); + ok("*.example.com", "http://example.net"); + ok("*.example.com", "http://foo.com"); + ok("*.example.com", "http://example.com.foo"); + ok("*.example2.com", "http://example.com"); + + ok("http://example.com/*", null); + ok("http://example.com/*", ""); + ok("http://example.com/*", "bogus"); + ok("http://example.com/*", "http://example.com"); + ok("http://example.com/*", "http://foo.com/"); + + ok("http://example.com", null); + ok("http://example.com", ""); + ok("http://example.com", "bogus"); + ok("http://example.com", "http://example.com/"); + + ok(/zilla.*/, "https://bugzilla.redhat.com/show_bug.cgi?id=569753"); + ok(/.*zilla/, "https://bugzilla.redhat.com/show_bug.cgi?id=569753"); + ok(/https:.*zilla/, "https://bugzilla.redhat.com/show_bug.cgi?id=569753"); +}; + +exports.testMatchPatternErrors = function(test) { + test.assertRaises( + function() new MatchPattern("*.google.com/*"), + /There can be at most one/, + "MatchPattern throws when supplied multiple '*'" + ); + + test.assertRaises( + function() new MatchPattern("google.com"), + /expected to be either an exact URL/, + "MatchPattern throws when the wildcard doesn't use '*' and doesn't " + + "look like a URL" + ); + + test.assertRaises( + function() new MatchPattern("http://google*.com"), + /expected to be the first or the last/, + "MatchPattern throws when a '*' is in the middle of the wildcard" + ); + + test.assertRaises( + function() new MatchPattern(/ /g), + /^A RegExp match pattern cannot be set to `global` \(i\.e\. \/\/g\)\.$/, + "MatchPattern throws on a RegExp set to `global` (i.e. //g)." + ); + + test.assertRaises( + function() new MatchPattern(/ /i), + /^A RegExp match pattern cannot be set to `ignoreCase` \(i\.e\. \/\/i\)\.$/, + "MatchPattern throws on a RegExp set to `ignoreCase` (i.e. //i)." + ); + + test.assertRaises( + function() new MatchPattern( / /m ), + /^A RegExp match pattern cannot be set to `multiline` \(i\.e\. \/\/m\)\.$/, + "MatchPattern throws on a RegExp set to `multiline` (i.e. //m)." + ); +}; + +exports.testMatchPatternInternals = function(test) { + test.assertEqual( + new MatchPattern("http://google.com/test").exactURL, + "http://google.com/test" + ); + + test.assertEqual( + new MatchPattern("http://google.com/test/*").urlPrefix, + "http://google.com/test/" + ); + + test.assertEqual( + new MatchPattern("*.example.com").domain, + "example.com" + ); +}; diff --git a/tools/addon-sdk-1.4/packages/api-utils/tests/test-memory.js b/tools/addon-sdk-1.4/packages/api-utils/tests/test-memory.js new file mode 100644 index 0000000..7e172bf --- /dev/null +++ b/tools/addon-sdk-1.4/packages/api-utils/tests/test-memory.js @@ -0,0 +1,15 @@ +var memory = require("memory"); + +exports.testMemory = function(test) { + test.pass("Skipping this test until Gecko memory debugging issues " + + "are resolved (see bug 592774)."); + return; + + var obj = {}; + memory.track(obj, "testMemory.testObj"); + var objs = memory.getObjects("testMemory.testObj"); + test.assertEqual(objs[0].weakref.get(), obj); + obj = null; + memory.gc(); + test.assertEqual(objs[0].weakref.get(), null); +}; diff --git a/tools/addon-sdk-1.4/packages/api-utils/tests/test-modules.js b/tools/addon-sdk-1.4/packages/api-utils/tests/test-modules.js new file mode 100644 index 0000000..99297bc --- /dev/null +++ b/tools/addon-sdk-1.4/packages/api-utils/tests/test-modules.js @@ -0,0 +1,144 @@ +exports.testDefine = function(test) { + let tiger = require('./modules/tiger'); + test.assertEqual(tiger.name, 'tiger', 'name proprety was exported properly'); + test.assertEqual(tiger.type, 'cat', 'property form other module exported'); +}; + +exports.testDefineInoresNonFactory = function(test) { + let mod = require('./modules/async2'); + test.assertEqual(mod.name, 'async2', 'name proprety was exported properly'); + test.assertNotEqual(mod.traditional2Name, 'traditional2', '1st is ignored'); +}; +/* Disable test that require AMD specific functionality: + +// define() that exports a function as the module value, +// specifying a module name. +exports.testDefExport = function(test) { + var add = require('modules/add'); + test.assertEqual(add(1, 1), 2, 'Named define() exporting a function'); +}; + +// define() that exports function as a value, but is anonymous +exports.testAnonDefExport = function (test) { + var subtract = require('modules/subtract'); + test.assertEqual(subtract(4, 2), 2, + 'Anonymous define() exporting a function'); +} + +// using require([], function () {}) to load modules. +exports.testSimpleRequire = function (test) { + require(['modules/blue', 'modules/orange'], function (blue, orange) { + test.assertEqual(blue.name, 'blue', 'Simple require for blue'); + test.assertEqual(orange.name, 'orange', 'Simple require for orange'); + test.assertEqual(orange.parentType, 'color', + 'Simple require dependency check for orange'); + }); +} + +// using nested require([]) calls. +exports.testSimpleRequireNested = function (test) { + require(['modules/blue', 'modules/orange', 'modules/green'], + function (blue, orange, green) { + + require(['modules/orange', 'modules/red'], function (orange, red) { + test.assertEqual(red.name, 'red', 'Simple require for red'); + test.assertEqual(red.parentType, 'color', + 'Simple require dependency check for red'); + test.assertEqual(blue.name, 'blue', 'Simple require for blue'); + test.assertEqual(orange.name, 'orange', 'Simple require for orange'); + test.assertEqual(orange.parentType, 'color', + 'Simple require dependency check for orange'); + test.assertEqual(green.name, 'green', 'Simple require for green'); + test.assertEqual(green.parentType, 'color', + 'Simple require dependency check for green'); + }); + + }); +} + +// requiring a traditional module, that uses async, that use traditional and +// async, with a circular reference +exports.testMixedCircular = function (test) { + var t = require('modules/traditional1'); + test.assertEqual(t.name, 'traditional1', 'Testing name'); + test.assertEqual(t.traditional2Name, 'traditional2', + 'Testing dependent name'); + test.assertEqual(t.traditional1Name, 'traditional1', 'Testing circular name'); + test.assertEqual(t.async2Name, 'async2', 'Testing async2 name'); + test.assertEqual(t.async2Traditional2Name, 'traditional2', + 'Testing nested traditional2 name'); +} + +// Testing define()(function(require) {}) with some that use exports, +// some that use return. +exports.testAnonExportsReturn = function (test) { + var lion = require('modules/lion'); + require(['modules/tiger', 'modules/cheetah'], function (tiger, cheetah) { + test.assertEqual('lion', lion, 'Check lion name'); + test.assertEqual('tiger', tiger.name, 'Check tiger name'); + test.assertEqual('cat', tiger.type, 'Check tiger type'); + test.assertEqual('cheetah', cheetah(), 'Check cheetah name'); + }); +} + +// circular dependency +exports.testCircular = function (test) { + var pollux = require('modules/pollux'), + castor = require('modules/castor'); + + test.assertEqual(pollux.name, 'pollux', 'Pollux\'s name'); + test.assertEqual(pollux.getCastorName(), + 'castor', 'Castor\'s name from Pollux.'); + test.assertEqual(castor.name, 'castor', 'Castor\'s name'); + test.assertEqual(castor.getPolluxName(), 'pollux', + 'Pollux\'s name from Castor.'); +} + +// test a bad module that asks for exports but also does a define() return +exports.testBadExportAndReturn = function (test) { + var passed = false; + try { + var bad = require('modules/badExportAndReturn'); + } catch(e) { + passed = /cannot use exports and also return/.test(e.toString()); + } + test.assertEqual(passed, true, 'Make sure exports and return fail'); +} + +// test a bad circular dependency, where an exported value is needed, but +// the return value happens too late, a module already asked for the exported +// value. +exports.testBadExportAndReturnCircular = function (test) { + var passed = false; + try { + var bad = require('modules/badFirst'); + } catch(e) { + passed = /after another module has referenced its exported value/ + .test(e.toString()); + } + test.assertEqual(passed, true, 'Make sure return after an exported ' + + 'value is grabbed by another module fails.'); +} + +// only allow one define call per file. +exports.testOneDefine = function (test) { + var passed = false; + try { + var dupe = require('modules/dupe'); + } catch(e) { + passed = /Only one call to define/.test(e.toString()); + } + test.assertEqual(passed, true, 'Only allow one define call per module'); +} + +// only allow one define call per file, testing a bad nested define call. +exports.testOneDefineNested = function (test) { + var passed = false; + try { + var dupe = require('modules/dupeNested'); + } catch(e) { + passed = /Only one call to define/.test(e.toString()); + } + test.assertEqual(passed, true, 'Only allow one define call per module'); +} +*/ diff --git a/tools/addon-sdk-1.4/packages/api-utils/tests/test-namespace.js b/tools/addon-sdk-1.4/packages/api-utils/tests/test-namespace.js new file mode 100644 index 0000000..60ba23d --- /dev/null +++ b/tools/addon-sdk-1.4/packages/api-utils/tests/test-namespace.js @@ -0,0 +1,70 @@ +"use strict"; + +let { Namespace } = require("api-utils/namespace"); + +exports["test namsepace basics"] = function(assert) { + var privates = Namespace(); + var object = { foo: function foo() { return "hello foo"; } }; + + assert.notEqual(privates(object), object, + "namespaced object is not the same"); + assert.ok(!('foo' in privates(object)), + "public properties are not in the namespace"); + + assert.equal(privates(object), privates(object), + "same namespaced object is returned on each call"); +}; + +exports["test namespace overlays"] = function(assert) { + var _ = new Namespace(); + var object = { foo: 'foo' }; + + _(object).foo = 'bar'; + + assert.equal(_(object).foo, "bar", + "namespaced property `foo` changed value"); + + assert.equal(object.foo, "foo", + "public property `foo` has original value"); + + object.foo = "baz"; + assert.equal(_(object).foo, "bar", + "property changes do not affect namespaced properties"); + + object.bar = "foo"; + assert.ok(!("bar" in _(object)), + "new public properties are not reflected in namespace"); +}; + +exports["test shared namespaces"] = function(assert) { + var _ = new Namespace({ hello: 'hello world' }); + + var f1 = { hello: 1 }; + var f2 = { foo: 'foo' }; + + assert.equal(_(f1).hello, _(f2).hello, "namespace can be shared"); + assert.notEqual(f1.hello, _(f1).hello, "shared namespace can overlay"); + assert.notEqual(f2.hello, _(f2).hello, "target is not affected"); + + _(f1).hello = 2; + + assert.notEqual(_(f1).hello, _(f2).hello, + "namespaced property can be overided"); + assert.equal(_(f2).hello, _({}).hello, "namespace does not change"); +}; + +exports["test multi namespace"] = function(assert) { + var n1 = new Namespace(); + var n2 = new Namespace(); + var object = { baz: 1 }; + n1(object).foo = 1; + n2(object).foo = 2; + n1(object).bar = n2(object).bar = 3; + + assert.notEqual(n1(object).foo, n2(object).foo, + "object can have multiple namespaces"); + assert.equal(n1(object).bar, n2(object).bar, + "object can have matching props in diff namespaces"); +}; + +require("test").run(exports); diff --git a/tools/addon-sdk-1.4/packages/api-utils/tests/test-observer-service.js b/tools/addon-sdk-1.4/packages/api-utils/tests/test-observer-service.js new file mode 100644 index 0000000..68cda74 --- /dev/null +++ b/tools/addon-sdk-1.4/packages/api-utils/tests/test-observer-service.js @@ -0,0 +1,73 @@ +var observers = require("api-utils/observer-service"); +var {Cc,Ci} = require("chrome"); +const { Loader } = require("./helpers"); + +exports.testUnloadAndErrorLogging = function(test) { + var prints = []; + var loader = Loader(module, { dump: function print(message) { + prints.push(message); + }}); + var sbobsvc = loader.require("api-utils/observer-service"); + + var timesCalled = 0; + var cb = function(subject, data) { + timesCalled++; + }; + var badCb = function(subject, data) { + throw new Error("foo"); + }; + sbobsvc.add("blarg", cb); + observers.notify("blarg", "yo yo"); + test.assertEqual(timesCalled, 1); + sbobsvc.add("narg", badCb); + observers.notify("narg", "yo yo"); + var lines = prints[0].split("\n"); + test.assertEqual(lines[0], "error: An exception occurred."); + test.assertEqual(lines[1], "Traceback (most recent call last):"); + test.assertEqual(lines.slice(-2)[0], "Error: foo"); + + loader.unload(); + observers.notify("blarg", "yo yo"); + test.assertEqual(timesCalled, 1); +}; + +exports.testObserverService = function(test) { + var ios = Cc['@mozilla.org/network/io-service;1'] + .getService(Ci.nsIIOService); + var service = Cc["@mozilla.org/observer-service;1"]. + getService(Ci.nsIObserverService); + var uri = ios.newURI("http://www.foo.com", null, null); + var timesCalled = 0; + var lastSubject = null; + var lastData = null; + + var cb = function(subject, data) { + timesCalled++; + lastSubject = subject; + lastData = data; + }; + + observers.add("blarg", cb); + service.notifyObservers(uri, "blarg", "some data"); + test.assertEqual(timesCalled, 1, + "observer-service.add() should call callback"); + test.assertEqual(lastSubject, uri, + "observer-service.add() should pass subject"); + test.assertEqual(lastData, "some data", + "observer-service.add() should pass data"); + + function customSubject() {} + function customData() {} + observers.notify("blarg", customSubject, customData); + test.assertEqual(timesCalled, 2, + "observer-service.notify() should work"); + test.assertEqual(lastSubject, customSubject, + "observer-service.notify() should pass+wrap subject"); + test.assertEqual(lastData, customData, + "observer-service.notify() should pass data"); + + observers.remove("blarg", cb); + service.notifyObservers(null, "blarg", "some data"); + test.assertEqual(timesCalled, 2, + "observer-service.remove() should work"); +}; diff --git a/tools/addon-sdk-1.4/packages/api-utils/tests/test-passwords-utils.js b/tools/addon-sdk-1.4/packages/api-utils/tests/test-passwords-utils.js new file mode 100644 index 0000000..651296f --- /dev/null +++ b/tools/addon-sdk-1.4/packages/api-utils/tests/test-passwords-utils.js @@ -0,0 +1,138 @@ +"use strict"; + +const { store, search, remove } = require("passwords/utils"); + +exports["test store requires `password` field"] = function(assert) { + assert.throws(function() { + store({ username: "foo", realm: "bar" }); + }, "`password` is required"); +}; + +exports["test store requires `username` field"] = function(assert) { + assert.throws(function() { + store({ password: "foo", realm: "bar" }); + }, "`username` is required"); +}; + +exports["test store requires `realm` field"] = function(assert) { + assert.throws(function() { + store({ username: "foo", password: "bar" }); + }, "`password` is required"); +}; + +exports["test can't store same login twice"] = function(assert) { + let options = { username: "user", password: "pass", realm: "realm" }; + store(options); + assert.throws(function() { + store(options); + }, "can't store same pass twice"); + remove(options); +}; + +exports["test remove throws if no login found"] = function(assert) { + assert.throws(function() { + remove({ username: "foo", password: "bar", realm: "baz" }); + }, "can't remove unstored credentials"); +}; + +exports["test addon associated credentials"] = function(assert) { + let options = { username: "foo", password: "bar", realm: "baz" }; + store(options); + + assert.ok(search().length, "credential was stored"); + assert.ok(search(options).length, "stored credential found"); + assert.ok(search({ username: options.username }).length, "found by username"); + assert.ok(search({ password: options.password }).length, "found by password"); + assert.ok(search({ realm: options.realm }).length, "found by realm"); + + let credential = search(options)[0]; + assert.equal(credential.url.indexOf("addon:"), 0, + "`addon:` uri is used for add-on associated credentials"); + assert.equal(credential.username, options.username, "username matches"); + assert.equal(credential.password, options.password, "password matches"); + assert.equal(credential.realm, options.realm, "realm matches"); + assert.equal(credential.formSubmitURL, null, + "`formSubmitURL` is `null` for add-on associated credentials"); + assert.equal(credential.usernameField, "", "usernameField is empty"); + assert.equal(credential.passwordField, "", "passwordField is empty"); + + remove(search(options)[0]); + assert.ok(!search(options).length, "remove worked"); +}; + +exports["test web page associated credentials"] = function(assert) { + let options = { + url: "http://www.example.com", + formSubmitURL: "http://login.example.com", + username: "user", + password: "pass", + usernameField: "user-f", + passwordField: "pass-f" + }; + store({ + url: "http://www.example.com/login", + formSubmitURL: "http://login.example.com/foo/authenticate.cgi", + username: options.username, + password: options.password, + usernameField: options.usernameField, + passwordField: options.passwordField + }); + + assert.ok(search().length, "credential was stored"); + assert.ok(search(options).length, "stored credential found"); + assert.ok(search({ username: options.username }).length, "found by username"); + assert.ok(search({ password: options.password }).length, "found by password"); + assert.ok(search({ formSubmitURL: options.formSubmitURL }).length, + "found by formSubmitURL"); + assert.ok(search({ usernameField: options.usernameField }).length, + "found by usernameField"); + assert.ok(search({ passwordField: options.passwordField }).length, + "found by passwordField"); + + let credential = search(options)[0]; + assert.equal(credential.url, options.url, "url matches"); + assert.equal(credential.username, options.username, "username matches"); + assert.equal(credential.password, options.password, "password matches"); + assert.equal(credential.realm, null, "realm is "); + assert.equal(credential.formSubmitURL, options.formSubmitURL, + "`formSubmitURL` matches"); + assert.equal(credential.usernameField, options.usernameField, + "usernameField matches"); + assert.equal(credential.passwordField, options.passwordField, + "passwordField matches"); + + remove(search(options)[0]); + assert.ok(!search(options).length, "remove worked"); +}; + +exports["test site authentication credentials"] = function(assert) { + let options = { + url: "http://test.authentication.com", + username: "u", + password: "p", + realm: "r" + }; + + store(options); + assert.ok(search().length, "credential was stored"); + assert.ok(search(options).length, "stored credential found"); + assert.ok(search({ username: options.username }).length, "found by username"); + assert.ok(search({ password: options.password }).length, "found by password"); + assert.ok(search({ realm: options.realm }).length, "found by realm"); + assert.ok(search({ url: options.url }).length, "found by url"); + + let credential = search(options)[0]; + assert.equal(credential.url, options.url, "url matches"); + assert.equal(credential.username, options.username, "username matches"); + assert.equal(credential.password, options.password, "password matches"); + assert.equal(credential.realm, options.realm, "realm matches"); + assert.equal(credential.formSubmitURL, null, + "`formSubmitURL` is `null` for site authentication credentials"); + assert.equal(credential.usernameField, "", "usernameField is empty"); + assert.equal(credential.passwordField, "", "passwordField is empty"); + + remove(search(options)[0]); + assert.ok(!search(options).length, "remove worked"); +}; + +require("test").run(exports); diff --git a/tools/addon-sdk-1.4/packages/api-utils/tests/test-plain-text-console.js b/tools/addon-sdk-1.4/packages/api-utils/tests/test-plain-text-console.js new file mode 100644 index 0000000..a0c9dda --- /dev/null +++ b/tools/addon-sdk-1.4/packages/api-utils/tests/test-plain-text-console.js @@ -0,0 +1,64 @@ +exports.testPlainTextConsole = function(test) { + var prints = []; + function print(message) { + prints.push(message); + } + function lastPrint() { + var last = prints.slice(-1)[0]; + prints = []; + return last; + } + + var Console = require("plain-text-console").PlainTextConsole; + var con = new Console(print); + + test.pass("PlainTextConsole instantiates"); + + con.log('testing', 1, [2, 3, 4]); + test.assertEqual(lastPrint(), "info: testing 1 2,3,4\n", + "PlainTextConsole.log() must work."); + + con.info('testing', 1, [2, 3, 4]); + test.assertEqual(lastPrint(), "info: testing 1 2,3,4\n", + "PlainTextConsole.info() must work."); + + con.warn('testing', 1, [2, 3, 4]); + test.assertEqual(lastPrint(), "warning: testing 1 2,3,4\n", + "PlainTextConsole.warn() must work."); + + con.error('testing', 1, [2, 3, 4]); + test.assertEqual(lastPrint(), "error: testing 1 2,3,4\n", + "PlainTextConsole.error() must work."); + + con.debug('testing', 1, [2, 3, 4]); + test.assertEqual(lastPrint(), "debug: testing 1 2,3,4\n", + "PlainTextConsole.debug() must work."); + + con.log('testing', undefined); + test.assertEqual(lastPrint(), "info: testing undefined\n", + "PlainTextConsole.log() must stringify undefined."); + + con.log('testing', null); + test.assertEqual(lastPrint(), "info: testing null\n", + "PlainTextConsole.log() must stringify null."); + + con.log("testing", { toString: function() "obj.toString()" }); + test.assertEqual(lastPrint(), "info: testing obj.toString()\n", + "PlainTextConsole.log() must stringify custom toString."); + + con.log("testing", { toString: function() { throw "fail!"; } }); + test.assertEqual(lastPrint(), "info: testing <toString() error>\n", + "PlainTextConsole.log() must stringify custom bad toString."); + + con.exception(new Error("blah")); + var tbLines = prints[0].split("\n"); + test.assertEqual(tbLines[0], "error: An exception occurred."); + test.assertEqual(tbLines[1], "Traceback (most recent call last):"); + test.assertEqual(tbLines.slice(-2)[0], "Error: blah"); + + prints = []; + con.trace(); + tbLines = prints[0].split("\n"); + test.assertEqual(tbLines[0], "info: Traceback (most recent call last):"); + test.assertEqual(tbLines.slice(-2)[0].trim(), "con.trace();"); +}; diff --git a/tools/addon-sdk-1.4/packages/api-utils/tests/test-preferences-service.js b/tools/addon-sdk-1.4/packages/api-utils/tests/test-preferences-service.js new file mode 100644 index 0000000..dd0de38 --- /dev/null +++ b/tools/addon-sdk-1.4/packages/api-utils/tests/test-preferences-service.js @@ -0,0 +1,87 @@ +var prefs = require("preferences-service"); +var {Cc,Ci} = require("chrome"); + +exports.testReset = function(test) { + prefs.reset("test_reset_pref"); + test.assertEqual(prefs.has("test_reset_pref"), false); + test.assertEqual(prefs.isSet("test_reset_pref"), false); + prefs.set("test_reset_pref", 5); + test.assertEqual(prefs.has("test_reset_pref"), true); + test.assertEqual(prefs.isSet("test_reset_pref"), true); +}; + +exports.testGetAndSet = function(test) { + let svc = Cc["@mozilla.org/preferences-service;1"]. + getService(Ci.nsIPrefService). + getBranch(null); + svc.setCharPref("test_get_string_pref", "a normal string"); + test.assertEqual(prefs.get("test_get_string_pref"), "a normal string", + "preferences-service should read from " + + "application-wide preferences service"); + + prefs.set("test_set_get_pref.integer", 1); + test.assertEqual(prefs.get("test_set_get_pref.integer"), 1, + "set/get integer preference should work"); + + prefs.set("test_set_get_number_pref", 42); + test.assertRaises( + function() { prefs.set("test_set_get_number_pref", 3.14159); }, + "cannot store non-integer number: 3.14159", + "setting a float preference should raise an error" + ); + test.assertEqual(prefs.get("test_set_get_number_pref"), 42, + "bad-type write attempt should not overwrite"); + + // 0x80000000 (no), 0x7fffffff (yes), -0x80000000 (yes), -0x80000001 (no) + test.assertRaises( + function() { prefs.set("test_set_get_number_pref", Math.pow(2, 31)); }, + ("you cannot set the test_set_get_number_pref pref to the number " + + "2147483648, as number pref values must be in the signed 32-bit " + + "integer range -(2^31) to 2^31-1. To store numbers outside that " + + "range, store them as strings."), + "setting an int pref outside the range -(2^31) to 2^31-1 shouldn't work" + ); + test.assertEqual(prefs.get("test_set_get_number_pref"), 42, + "out-of-range write attempt should not overwrite 1"); + prefs.set("test_set_get_number_pref", Math.pow(2, 31)-1); + test.assertEqual(prefs.get("test_set_get_number_pref"), 0x7fffffff, + "in-range write attempt should work 1"); + prefs.set("test_set_get_number_pref", -Math.pow(2, 31)); + test.assertEqual(prefs.get("test_set_get_number_pref"), -0x80000000, + "in-range write attempt should work 2"); + test.assertRaises( + function() { prefs.set("test_set_get_number_pref", -0x80000001); }, + ("you cannot set the test_set_get_number_pref pref to the number " + + "-2147483649, as number pref values must be in the signed 32-bit " + + "integer range -(2^31) to 2^31-1. To store numbers outside that " + + "range, store them as strings."), + "setting an int pref outside the range -(2^31) to 2^31-1 shouldn't work" + ); + test.assertEqual(prefs.get("test_set_get_number_pref"), -0x80000000, + "out-of-range write attempt should not overwrite 2"); + + + prefs.set("test_set_get_pref.string", "foo"); + test.assertEqual(prefs.get("test_set_get_pref.string"), "foo", + "set/get string preference should work"); + + prefs.set("test_set_get_pref.boolean", true); + test.assertEqual(prefs.get("test_set_get_pref.boolean"), true, + "set/get boolean preference should work"); + + prefs.set("test_set_get_unicode_pref", String.fromCharCode(960)); + test.assertEqual(prefs.get("test_set_get_unicode_pref"), + String.fromCharCode(960), + "set/get unicode preference should work"); + + var unsupportedValues = [null, [], undefined]; + unsupportedValues.forEach( + function(value) { + test.assertRaises( + function() { prefs.set("test_set_pref", value); }, + ("can't set pref test_set_pref to value '" + value + "'; " + + "it isn't a string, integer, or boolean"), + "Setting a pref to " + uneval(value) + " should raise error" + ); + }); +}; diff --git a/tools/addon-sdk-1.4/packages/api-utils/tests/test-registry.js b/tools/addon-sdk-1.4/packages/api-utils/tests/test-registry.js new file mode 100644 index 0000000..8a251f7 --- /dev/null +++ b/tools/addon-sdk-1.4/packages/api-utils/tests/test-registry.js @@ -0,0 +1,76 @@ +'use strict'; + +exports['test:add'] = function(test) { + function Class() {} + let fixture = require('utils/registry').Registry(Class); + let isAddEmitted = false; + fixture.on('add', function(item) { + test.assert( + item instanceof Class, + 'if object added is not an instance should construct instance from it' + ); + test.assert( + fixture.has(item), + 'callback is called after instance is added' + ); + test.assert( + !isAddEmitted, + 'callback should be called for the same item only once' + ); + isAddEmitted = true; + }); + + let object = fixture.add({}); + fixture.add(object); +}; + +exports['test:remove'] = function(test) { + function Class() {} + let fixture = require('utils/registry').Registry(Class); + fixture.on('remove', function(item) { + test.assert( + item instanceof Class, + 'if object removed can be only instance of Class' + ); + test.assert( + fixture.has(item), + 'callback is called before instance is removed' + ); + test.assert( + !isRemoveEmitted, + 'callback should be called for the same item only once' + ); + isRemoveEmitted = true; + }); + + fixture.remove({}); + let object = fixture.add({}); + fixture.remove(object); + fixture.remove(object); +}; + +exports['test:items'] = function(test) { + function Class() {} + let fixture = require('utils/registry').Registry(Class), + actual, + times = 0; + + function testItem(item) { + times ++; + test.assertEqual( + actual, + item, + 'item should match actual item being added/removed' + ); + } + + actual = fixture.add({}); + + fixture.on('add', testItem); + fixture.on('remove', testItem); + + fixture.remove(actual); + fixture.remove(fixture.add(actual = new Class())); + test.assertEqual(3, times, 'should notify listeners on each call'); +} + diff --git a/tools/addon-sdk-1.4/packages/api-utils/tests/test-require.js b/tools/addon-sdk-1.4/packages/api-utils/tests/test-require.js new file mode 100644 index 0000000..986a53c --- /dev/null +++ b/tools/addon-sdk-1.4/packages/api-utils/tests/test-require.js @@ -0,0 +1,25 @@ +const traceback = require("traceback"); + +exports.test_no_args = function(test) { + var passed = false; + try { + var oops = require(); // leave this on line 6! + } catch(e) { + let msg = e.toString(); + test.assertEqual(msg.indexOf("Error: you must provide a module name when calling require() from "), 0); + test.assertNotEqual(msg.indexOf("test-require"), -1, msg); + // we'd also like to assert that the right filename and linenumber is in + // the stack trace, but this currently doesn't work (see bugs 679591 and + // 551604) + if (0) { + let tb = traceback.fromException(e); + let lastFrame = tb[tb.length-1]; + test.assertNotEqual(lastFrame.filename.indexOf("test-require.js"), -1, + lastFrame.filename); + test.assertEqual(lastFrame.lineNo, 6); + test.assertEqual(lastFrame.funcName, "??"); + } + passed = true; + } + test.assert(passed, 'require() with no args should raise helpful error'); +}; diff --git a/tools/addon-sdk-1.4/packages/api-utils/tests/test-self.js b/tools/addon-sdk-1.4/packages/api-utils/tests/test-self.js new file mode 100644 index 0000000..3cc6df5 --- /dev/null +++ b/tools/addon-sdk-1.4/packages/api-utils/tests/test-self.js @@ -0,0 +1,29 @@ +exports.testSelf = function(test) { + var self = require("self"); + // We can't assert anything about the ID inside the unit test right now, + // because the ID we get depends upon how the test was invoked. The idea + // is that it is supposed to come from the main top-level package's + // package.json file, from the "id" key. + test.assertEqual(typeof(self.id), "string", "self.id is a string"); + test.assert(self.id.length > 0); + + var source = self.data.load("test-content-symbiont.js"); + test.assert(source.match(/test-content-symbiont/), "self.data.load() works"); + + // Likewise, we can't assert anything about the full URL, because that + // depends on self.id . We can only assert that it ends in the right + // thing. + var url = self.data.url("test-content-symbiont.js"); + test.assertEqual(typeof(url), "string", "self.data.url('x') returns string"); + test.assertEqual(/\/test-content-symbiont\.js$/.test(url), true); + + // Make sure 'undefined' is not in url when no string is provided. + url = self.data.url(); + test.assertEqual(typeof(url), "string", "self.data.url() returns string"); + test.assertEqual(/\/undefined$/.test(url), false); + + // When tests are run on just the api-utils package, self.name is + // api-utils. When they're run as 'cfx testall', self.name is testpkgs. + test.assert((self.name == "api-utils") || (self.name == "testpkgs"), + "self.name is api-utils or testpkgs"); +}; diff --git a/tools/addon-sdk-1.4/packages/api-utils/tests/test-set-exports.js b/tools/addon-sdk-1.4/packages/api-utils/tests/test-set-exports.js new file mode 100644 index 0000000..be89305 --- /dev/null +++ b/tools/addon-sdk-1.4/packages/api-utils/tests/test-set-exports.js @@ -0,0 +1,31 @@ +let four = require("./modules/exportsEquals"); +exports.testExportsEquals = function(test) { + test.assertEqual(four, 4); +} + +/* TODO: Discuss idea of dropping support for this feature that was alternative + to `module.exports = ..` that failed. +let five = require("./modules/setExports"); +exports.testSetExports = function(test) { + test.assertEqual(five, 5); +} + +exports.testDupeSetExports = function(test) { + var passed = false; + try { + var dupe = require('./modules/dupeSetExports'); + } catch(e) { + passed = /define\(\) was used, so module\.exports= and module\.setExports\(\) may not be used/.test(e.toString()); + } + test.assertEqual(passed, true, 'define() or setExports(), not both'); +} +*/ + +exports.testModule = function(test) { + // module.id is not cast in stone yet. In the future, it may include the + // package name, or may possibly be a/ URL of some sort. For now, it's a + // URL that starts with resource: and ends with this module name, but the + // part in between varies depending upon how the test is run. + var found = /test-set-exports$/.test(module.id); + test.assertEqual(found, true, module.id+" ends with test-set-exports.js"); +} diff --git a/tools/addon-sdk-1.4/packages/api-utils/tests/test-tab-browser.js b/tools/addon-sdk-1.4/packages/api-utils/tests/test-tab-browser.js new file mode 100644 index 0000000..10d9614 --- /dev/null +++ b/tools/addon-sdk-1.4/packages/api-utils/tests/test-tab-browser.js @@ -0,0 +1,525 @@ +/* ***** BEGIN LICENSE BLOCK ***** + * Version: MPL 1.1/GPL 2.0/LGPL 2.1 + * + * The contents of this file are subject to the Mozilla Public License Version + * 1.1 (the "License"); you may not use this file except in compliance with + * the License. You may obtain a copy of the License at + * http://www.mozilla.org/MPL/ + * + * Software distributed under the License is distributed on an "AS IS" basis, + * WITHOUT WARRANTY OF ANY KIND, either express or implied. See the License + * for the specific language governing rights and limitations under the + * License. + * + * The Original Code is Jetpack. + * + * The Initial Developer of the Original Code is Mozilla. + * Portions created by the Initial Developer are Copyright (C) 2010 + * the Initial Developer. All Rights Reserved. + * + * Contributor(s): + * Atul Varma <atul@mozilla.com> + * + * Alternatively, the contents of this file may be used under the terms of + * either the GNU General Public License Version 2 or later (the "GPL"), or + * the GNU Lesser General Public License Version 2.1 or later (the "LGPL"), + * in which case the provisions of the GPL or the LGPL are applicable instead + * of those above. If you wish to allow use of your version of this file only + * under the terms of either the GPL or the LGPL, and not to allow others to + * use your version of this file under the terms of the MPL, indicate your + * decision by deleting the provisions above and replace them with the notice + * and other provisions required by the GPL or the LGPL. If you do not delete + * the provisions above, a recipient may use your version of this file under + * the terms of any one of the MPL, the GPL or the LGPL. + * + * ***** END LICENSE BLOCK ***** */ + +var timer = require("timer"); +var {Cc,Ci} = require("chrome"); + +function onBrowserLoad(callback, event) { + if (event.target && event.target.defaultView == this) { + this.removeEventListener("load", onBrowserLoad, true); + let browsers = this.document.getElementsByTagName("tabbrowser"); + try { + require("timer").setTimeout(function (window) { + callback(window, browsers[0]); + }, 10, this); + } catch (e) { console.exception(e); } + } +} +// Utility function to open a new browser window. +function openBrowserWindow(callback, url) { + let ww = Cc["@mozilla.org/embedcomp/window-watcher;1"]. + getService(Ci.nsIWindowWatcher); + let urlString = Cc["@mozilla.org/supports-string;1"]. + createInstance(Ci.nsISupportsString); + urlString.data = url; + let window = ww.openWindow(null, "chrome://browser/content/browser.xul", + "_blank", "chrome,all,dialog=no", urlString); + if (callback) + window.addEventListener("load", onBrowserLoad.bind(window, callback), true); + + return window; +} + +// Helper for calling code at window close +function closeBrowserWindow(window, callback) { + require("timer").setTimeout(function() { + window.addEventListener("unload", function onUnload() { + window.removeEventListener("unload", onUnload, false); + callback(); + }, false); + window.close(); + }, 0); +} + +// Helper for opening two windows at once +function openTwoWindows(callback) { + openBrowserWindow(function (window1) { + openBrowserWindow(function (window2) { + callback(window1, window2); + }); + }); +} + +// Helper for closing two windows at once +function closeTwoWindows(window1, window2, callback) { + closeBrowserWindow(window1, function() { + closeBrowserWindow(window2, callback); + }); +} + +exports.testAddTab = function(test) { + test.waitUntilDone(); + openBrowserWindow(function(window, browser) { + const tabBrowser = require("tab-browser"); + + let cache = []; + let windowUtils = require("window-utils"); + new windowUtils.WindowTracker({ + onTrack: function(win) { + cache.push(win); + }, + onUntrack: function(win) { + cache.splice(cache.indexOf(win), 1) + } + }); + let startWindowCount = cache.length; + + // Test 1: add a tab + let firstUrl = "data:text/html,one"; + tabBrowser.addTab(firstUrl, { + onLoad: function(e) { + let win1 = cache[startWindowCount - 1]; + test.assertEqual(win1.content.location, firstUrl, "URL of new tab in first window matches"); + + // Test 2: add a tab in a new window + let secondUrl = "data:text/html,two"; + tabBrowser.addTab(secondUrl, { + inNewWindow: true, + onLoad: function(e) { + test.assertEqual(cache.length, startWindowCount + 1, "a new window was opened"); + let win2 = cache[startWindowCount]; + let gBrowser = win2.gBrowser; + gBrowser.addEventListener("DOMContentLoaded", function onLoad(e) { + gBrowser.removeEventListener("DOMContentLoaded", onLoad, false); + test.assertEqual(win2.content.location, secondUrl, "URL of new tab in the new window matches"); + + closeBrowserWindow(win2, function() { + closeBrowserWindow(win1, function() { + test.done(); + }); + }); + }, false); + } + }); + } + }); + }); +}; + +exports.testTrackerWithDelegate = function(test) { + test.waitUntilDone(); + const tabBrowser = require("tab-browser"); + + var delegate = { + state: "initializing", + onTrack: function onTrack(browser) { + if (this.state == "initializing") { + this.state = "waiting for browser window to open"; + } + else if (this.state == "waiting for browser window to open") { + this.state = "waiting for browser window to close"; + require("timer").setTimeout(function() { + closeBrowserWindow(browser.ownerDocument.defaultView, function() { + test.assertEqual(delegate.state, "deinitializing"); + tb.unload(); + test.done(); + }); + }, 0); + } + else + test.fail("invalid state"); + }, + onUntrack: function onUntrack(browser) { + if (this.state == "waiting for browser window to close") { + test.pass("proper state in onUntrack"); + this.state = "deinitializing"; + } + else if (this.state != "deinitializing") + test.fail("invalid state"); + } + }; + var tb = new tabBrowser.Tracker(delegate); + + delegate.state = "waiting for browser window to open"; + + openBrowserWindow(); +}; + +exports.testWhenContentLoaded = function(test) { + test.waitUntilDone(); + const tabBrowser = require("tab-browser"); + + var tracker = tabBrowser.whenContentLoaded( + function(window) { + var item = window.document.getElementById("foo"); + test.assertEqual(item.textContent, "bar", + "whenContentLoaded() works."); + tracker.unload(); + closeBrowserWindow(activeWindow(), function() { + test.done(); + }); + }); + + openBrowserWindow(function(browserWindow, browser) { + var html = '<div id="foo">bar</div>'; + browser.addTab("data:text/html," + html); + }); +}; + +exports.testTrackerWithoutDelegate = function(test) { + test.waitUntilDone(); + const tabBrowser = require("tab-browser"); + + openBrowserWindow(function(browserWindow, browser) { + var tb = new tabBrowser.Tracker(); + + if (tb.length == 0) + test.fail("expect at least one tab browser to exist."); + + for (var i = 0; i < tb.length; i++) + test.assertEqual(tb.get(i).nodeName, "tabbrowser", + "get() method and length prop should work"); + for (var b in tb) + test.assertEqual(b.nodeName, "tabbrowser", + "iterator should work"); + + var matches = [b for (b in tb) + if (b == browser)]; + test.assertEqual(matches.length, 1, + "New browser should be in tracker."); + tb.unload(); + + closeBrowserWindow(browserWindow, function() { + test.done(); + }); + }); +}; + +exports.testTabTracker = function(test) { + test.waitUntilDone(); + const tabBrowser = require("tab-browser"); + + openBrowserWindow(function(browserWindow, browser) { + var delegate = { + tracked: 0, + onTrack: function(tab) { + this.tracked++; + }, + onUntrack: function(tab) { + this.tracked--; + } + }; + + let tabTracker = tabBrowser.TabTracker(delegate); + + let tracked = delegate.tracked; + let url1 = "data:text/html,1"; + let url2 = "data:text/html,2"; + let url3 = "data:text/html,3"; + let tabCount = 0; + + function tabLoadListener(e) { + let loadedURL = e.target.defaultView.location; + if (loadedURL == url1) + tabCount++; + else if (loadedURL == url2) + tabCount++; + else if (loadedURL == url3) + tabCount++; + + if (tabCount == 3) { + test.assertEqual(delegate.tracked, tracked + 3, "delegate tracked tabs matched count"); + tabTracker.unload(); + closeBrowserWindow(browserWindow, function() { + require("timer").setTimeout(function() test.done(), 0); + }); + } + } + + tabBrowser.addTab(url1, { + onLoad: tabLoadListener + }); + tabBrowser.addTab(url2, { + onLoad: tabLoadListener + }); + tabBrowser.addTab(url3, { + onLoad: tabLoadListener + }); + }); +}; + +exports.testActiveTab = function(test) { + test.waitUntilDone(); + openBrowserWindow(function(browserWindow, browser) { + const tabBrowser = require("tab-browser"); + const TabModule = require("tab-browser").TabModule; + let tm = new TabModule(browserWindow); + test.assertEqual(tm.length, 1); + let url1 = "data:text/html,foo"; + let url2 = "data:text/html,bar"; + + function tabURL(tab) tab.ownerDocument.defaultView.content.location.toString() + + tabBrowser.addTab(url1, { + onLoad: function(e) { + // make sure we're running in the right window. + test.assertEqual(tabBrowser.activeTab.ownerDocument.defaultView, browserWindow, "active window matches"); + browserWindow.focus(); + + test.assertEqual(tabURL(tabBrowser.activeTab), url1, "url1 matches"); + let tabIndex = browser.getBrowserIndexForDocument(e.target); + let tabAtIndex = browser.tabContainer.getItemAtIndex(tabIndex); + test.assertEqual(tabAtIndex, tabBrowser.activeTab, "activeTab element matches"); + + tabBrowser.addTab(url2, { + inBackground: true, + onLoad: function() { + test.assertEqual(tabURL(tabBrowser.activeTab), url1, "url1 still matches"); + let tabAtIndex = browser.tabContainer.getItemAtIndex(tabIndex); + test.assertEqual(tabAtIndex, tabBrowser.activeTab, "activeTab element matches"); + closeBrowserWindow(browserWindow, function() { + test.done() + }); + } + }); + } + }); + }); +}; + +// TabModule tests +exports.testEventsAndLengthStayInModule = function(test) { + test.waitUntilDone(); + let TabModule = require("tab-browser").TabModule; + + openTwoWindows(function(window1, window2) { + let tm1 = new TabModule(window1); + let tm2 = new TabModule(window2); + + let counter1 = 0, counter2 = 0; + let counterTabs = 0; + + function onOpenListener() { + ++counterTabs; + if (counterTabs < 5) + return; + test.assertEqual(counter1, 2, "Correct number of events fired from window 1"); + test.assertEqual(counter2, 3, "Correct number of events fired from window 2"); + test.assertEqual(counterTabs, 5, "Correct number of events fired from all windows"); + test.assertEqual(tm1.length, 3, "Correct number of tabs in window 1"); + test.assertEqual(tm2.length, 4, "Correct number of tabs in window 2"); + closeTwoWindows(window1, window2, function() test.done()); + } + + tm1.onOpen = function() ++counter1 && onOpenListener(); + tm2.onOpen = function() ++counter2 && onOpenListener(); + + let url = "data:text/html,default"; + tm1.open(url); + tm1.open(url); + + tm2.open(url); + tm2.open(url); + tm2.open(url); + }); +} + +exports.testTabModuleActiveTab_getterAndSetter = function(test) { + test.waitUntilDone(); + let TabModule = require("tab-browser").TabModule; + + openTwoWindows(function(window1, window2) { + let tm1 = new TabModule(window1); + let tm2 = new TabModule(window2); + + let tab1 = null; + tm1.open({ + url: "data:text/html,<title>window1,tab1</title>", + onOpen: function(tab) tab1 = tab, + }); + tm1.open("data:text/html,<title>window1,tab2</title>"); + + tm1.onActivate = function onActivate() { + tm1.onActivate.remove(onActivate); + require("timer").setTimeout(function() { + test.assertEqual(tm1.activeTab.title, "window1,tab1", "activeTab setter works"); + closeTwoWindows(window1, window2, function() test.done()); + }, 1000); + } + + tm2.open("data:text/html,<title>window2,tab1</title>"); + tm2.open({ + url: "data:text/html,<title>window2,tab2</title>", + onOpen: function(tab4) { + test.assertEqual(tm1.activeTab.title, "window1,tab2", "Correct active tab on window 1"); + test.assertEqual(tm2.activeTab.title, "window2,tab2", "Correct active tab on window 2"); + + tm1.activeTab = tab1; + tm1.activeTab = tab4; // Setting activeTab from another window should have no effect + } + }); + }); +} + +// test tabs iterator +exports.testTabModuleTabsIterator = function(test) { + test.waitUntilDone(); + let TabModule = require("tab-browser").TabModule; + + openBrowserWindow(function(window) { + let tm1 = new TabModule(window); + let url = "data:text/html,default"; + tm1.open(url); + tm1.open(url); + tm1.open({ + url: url, + onOpen: function(tab) { + let count = 0; + for each (let t in tm1) count++; + test.assertEqual(count, 4, "iterated tab count matches"); + test.assertEqual(count, tm1.length, "length tab count matches"); + closeBrowserWindow(window, function() test.done()); + } + }); + }); +}; + +// inNewWindow parameter is ignored on single-window modules +exports.testTabModuleCantOpenInNewWindow = function(test) { + test.waitUntilDone(); + let TabModule = require("tab-browser").TabModule; + + openBrowserWindow(function(window) { + let tm = new TabModule(window); + let url = "data:text/html,default"; + tm.open({ + url: url, + inNewWindow: true, + onOpen: function() { + test.assertEqual(tm.length, 2, "Tab was open on same window"); + closeBrowserWindow(window, function() test.done()); + } + }); + }); +}; + +// Test that having two modules attached to the same +// window won't duplicate events fired on each module +exports.testModuleListenersDontInteract = function(test) { + test.waitUntilDone(); + let TabModule = require("tab-browser").TabModule; + + openBrowserWindow(function(window) { + let tm1 = new TabModule(window); + let tm2 = new TabModule(window); + + let url = "data:text/html,foo"; + let eventCount = 0, eventModule1 = 0, eventModule2 = 0; + + + let listener1 = function() { + // this should be called twice: when tab is open and when + // the url location is changed + eventCount++; + eventModule1++; + } + tm1.onReady = listener1; + + tm2.open({ + url: "about:blank", + onOpen: function(tab) { + // add listener via property assignment + let listener2 = function() { + eventCount++; + eventModule2++; + }; + tab.onReady = listener2; + + // add listener via collection add + let listener3 = function() { + eventCount++; + eventModule2++; + }; + tab.onReady.add(listener3); + + tab.location = url; + + test.waitUntilEqual(function () eventCount, 4, + "Correct global number of events") + .then(function () { + test.assertEqual(eventModule1, 2, + "Correct number of events on module 1"); + test.assertEqual(eventModule2, 2, + "Correct number of events on module 2"); + + tm1.onReady.remove(listener1); + tab.onReady.remove(listener2); + tab.onReady.remove(listener3); + closeBrowserWindow(window, function() test.done()); + }); + } + }); + }); +}; + +/******************* helpers *********************/ + +// Helper for getting the active window +function activeWindow() { + return Cc["@mozilla.org/appshell/window-mediator;1"]. + getService(Ci.nsIWindowMediator). + getMostRecentWindow("navigator:browser"); +}; + +// If the module doesn't support the app we're being run in, require() will +// throw. In that case, remove all tests above from exports, and add one dummy +// test that passes. +try { + require("tab-browser"); +} +catch (err) { + // This bug should be mentioned in the error message. + let bug = "https://bugzilla.mozilla.org/show_bug.cgi?id=560716"; + if (err.message.indexOf(bug) < 0) + throw err; + for (let [prop, val] in Iterator(exports)) { + if (/^test/.test(prop) && typeof(val) === "function") + delete exports[prop]; + } + exports.testAppNotSupported = function (test) { + test.pass("the tab-browser module does not support this application."); + }; +} diff --git a/tools/addon-sdk-1.4/packages/api-utils/tests/test-tab-observer.js b/tools/addon-sdk-1.4/packages/api-utils/tests/test-tab-observer.js new file mode 100644 index 0000000..b56b1aa --- /dev/null +++ b/tools/addon-sdk-1.4/packages/api-utils/tests/test-tab-observer.js @@ -0,0 +1,35 @@ +"use strict"; + +const { openTab, closeTab } = require("api-utils/tabs/utils"); +const { Loader } = require("./helpers"); +const { setTimeout } = require("timer"); + +exports["test unload tab observer"] = function(assert, done) { + let loader = Loader(module); + + let window = loader.require("api-utils/window-utils").activeBrowserWindow; + let observer = loader.require("api-utils/tabs/observer").observer; + let opened = 0; + let closed = 0; + + observer.on("open", function onOpen(window) { opened++; }); + observer.on("close", function onClose(window) { closed++; }); + + // Open and close tab to trigger observers. + closeTab(openTab(window, "data:text/html,tab-1")); + + // Unload the module so that all listeners set by observer are removed. + loader.unload(); + + // Open and close tab once again. + closeTab(openTab(window, "data:text/html,tab-2")); + + // Enqueuing asserts to make sure that assertion is not performed early. + setTimeout(function () { + assert.equal(1, opened, "observer open was called before unload only"); + assert.equal(1, closed, "observer close was called before unload only"); + done(); + }, 0); +}; + +require("test").run(exports); diff --git a/tools/addon-sdk-1.4/packages/api-utils/tests/test-tab.js b/tools/addon-sdk-1.4/packages/api-utils/tests/test-tab.js new file mode 100644 index 0000000..4015faa --- /dev/null +++ b/tools/addon-sdk-1.4/packages/api-utils/tests/test-tab.js @@ -0,0 +1,106 @@ +const tabAPI = require("tabs/tab"); +const tabs = require("tabs"); // From addon-kit +const windowUtils = require("window-utils"); + +// The primary test tab +var primaryTab; + +// We have an auxiliary tab to test background tabs. +var auxTab; + +// The window for the outer iframe in the primary test page +var iframeWin; + +exports.testGetTabForWindow = function(test) { + test.waitUntilDone(); + + test.assertEqual(tabAPI.getTabForWindow(windowUtils.activeWindow), null, + "getTabForWindow return null on topwindow"); + test.assertEqual(tabAPI.getTabForWindow(windowUtils.activeBrowserWindow), null, + "getTabForWindow return null on topwindow"); + + let subSubDocument = encodeURIComponent( + 'Sub iframe<br/>'+ + '<iframe id="sub-sub-iframe" src="data:text/html,SubSubIframe" />'); + let subDocument = encodeURIComponent( + 'Iframe<br/>'+ + '<iframe id="sub-iframe" src="data:text/html,'+subSubDocument+'" />'); + let url = 'data:text/html,' + encodeURIComponent( + 'Content<br/><iframe id="iframe" src="data:text/html,'+subDocument+'" />'); + + // Open up a new tab in the background. + // + // This lets us test whether GetTabForWindow works even when the tab in + // question is not active. + tabs.open({ + inBackground: true, + url: "about:mozilla", + onReady: function(tab) { auxTab = tab; step2(url, test);}, + onActivate: function(tab) { step3(test); } + }); +} + +function step2(url, test) { + + tabs.open({ + url: url, + onReady: function(tab) { + primaryTab = tab; + let window = windowUtils.activeBrowserWindow.content; + + let matchedTab = tabAPI.getTabForWindow(window); + test.assertEqual(matchedTab, tab, + "We are able to find the tab with his content window object"); + + let timer = require("timer"); + function waitForFrames() { + let iframe = window.document.getElementById("iframe"); + if (!iframe) { + timer.setTimeout(waitForFrames, 100); + return; + } + iframeWin = iframe.contentWindow; + let subIframe = iframeWin.document.getElementById("sub-iframe"); + if (!subIframe) { + timer.setTimeout(waitForFrames, 100); + return; + } + let subIframeWin = subIframe.contentWindow; + let subSubIframe = subIframeWin.document.getElementById("sub-sub-iframe"); + if (!subSubIframe) { + timer.setTimeout(waitForFrames, 100); + return; + } + let subSubIframeWin = subSubIframe.contentWindow; + + matchedTab = tabAPI.getTabForWindow(iframeWin); + test.assertEqual(matchedTab, tab, + "We are able to find the tab with an iframe window object"); + + matchedTab = tabAPI.getTabForWindow(subIframeWin); + test.assertEqual(matchedTab, tab, + "We are able to find the tab with a sub-iframe window object"); + + matchedTab = tabAPI.getTabForWindow(subSubIframeWin); + test.assertEqual(matchedTab, tab, + "We are able to find the tab with a sub-sub-iframe window object"); + + // Put our primary tab in the background and test again. + // The onActivate listener will take us to step3. + auxTab.activate(); + } + waitForFrames(); + } + }); +} + +function step3(test) { + + let matchedTab = tabAPI.getTabForWindow(iframeWin); + test.assertEqual(matchedTab, primaryTab, + "We get the correct tab even when it's in the background"); + + primaryTab.close(function () { + auxTab.close(function () { test.done();}); + }); +} diff --git a/tools/addon-sdk-1.4/packages/api-utils/tests/test-text-streams.js b/tools/addon-sdk-1.4/packages/api-utils/tests/test-text-streams.js new file mode 100644 index 0000000..2c1c80d --- /dev/null +++ b/tools/addon-sdk-1.4/packages/api-utils/tests/test-text-streams.js @@ -0,0 +1,190 @@ +/* -*- Mode: Java; tab-width: 2; indent-tabs-mode: nil; c-basic-offset: 2 -*- + * vim:set ts=2 sw=2 sts=2 et filetype=javascript + * ***** BEGIN LICENSE BLOCK ***** + * Version: MPL 1.1/GPL 2.0/LGPL 2.1 + * + * The contents of this file are subject to the Mozilla Public License Version + * 1.1 (the "License"); you may not use this file except in compliance with + * the License. You may obtain a copy of the License at + * http://www.mozilla.org/MPL/ + * + * Software distributed under the License is distributed on an "AS IS" basis, + * WITHOUT WARRANTY OF ANY KIND, either express or implied. See the License + * for the specific language governing rights and limitations under the + * License. + * + * The Original Code is Jetpack. + * + * The Initial Developer of the Original Code is + * the Mozilla Foundation. + * Portions created by the Initial Developer are Copyright (C) 2010 + * the Initial Developer. All Rights Reserved. + * + * Contributor(s): + * Drew Willcoxon <adw@mozilla.com> (Original Author) + * + * Alternatively, the contents of this file may be used under the terms of + * either the GNU General Public License Version 2 or later (the "GPL"), or + * the GNU Lesser General Public License Version 2.1 or later (the "LGPL"), + * in which case the provisions of the GPL or the LGPL are applicable instead + * of those above. If you wish to allow use of your version of this file only + * under the terms of either the GPL or the LGPL, and not to allow others to + * use your version of this file under the terms of the MPL, indicate your + * decision by deleting the provisions above and replace them with the notice + * and other provisions required by the GPL or the LGPL. If you do not delete + * the provisions above, a recipient may use your version of this file under + * the terms of any one of the MPL, the GPL or the LGPL. + * + * ***** END LICENSE BLOCK ***** */ + +const file = require("file"); +const url = require("url"); +const { Loader } = require("./helpers"); + +const STREAM_CLOSED_ERROR = "The stream is closed and cannot be used."; + +// This should match the constant of the same name in text-streams.js. +const BUFFER_BYTE_LEN = 0x8000; + +exports.testWriteRead = function (test) { + let fname = dataFileFilename(); + + // Write a small string less than the stream's buffer size... + let str = "exports.testWriteRead data!"; + let stream = file.open(fname, "w"); + test.assert(!stream.closed, "stream.closed after open should be false"); + stream.write(str); + stream.close(); + test.assert(stream.closed, "stream.closed after close should be true"); + test.assertRaises(function () stream.close(), + STREAM_CLOSED_ERROR, + "stream.close after already closed should raise error"); + test.assertRaises(function () stream.write("This shouldn't be written!"), + STREAM_CLOSED_ERROR, + "stream.write after close should raise error"); + + // ... and read it. + stream = file.open(fname); + test.assert(!stream.closed, "stream.closed after open should be false"); + test.assertEqual(stream.read(), str, + "stream.read should return string written"); + test.assertEqual(stream.read(), "", + "stream.read at EOS should return empty string"); + stream.close(); + test.assert(stream.closed, "stream.closed after close should be true"); + test.assertRaises(function () stream.close(), + STREAM_CLOSED_ERROR, + "stream.close after already closed should raise error"); + test.assertRaises(function () stream.read(), + STREAM_CLOSED_ERROR, + "stream.read after close should raise error"); + + // Write a big string many times the size of the stream's buffer and read it. + // Since it comes after the previous test, this also ensures that the file is + // truncated when it's opened for writing. + str = ""; + let bufLen = BUFFER_BYTE_LEN; + let fileSize = bufLen * 10; + for (let i = 0; i < fileSize; i++) + str += i % 10; + stream = file.open(fname, "w"); + stream.write(str); + stream.close(); + stream = file.open(fname); + test.assertEqual(stream.read(), str, + "stream.read should return string written"); + stream.close(); + + // The same, but write and read in chunks. + stream = file.open(fname, "w"); + let i = 0; + while (i < str.length) { + // Use a chunk length that spans buffers. + let chunk = str.substr(i, bufLen + 1); + stream.write(chunk); + i += bufLen + 1; + } + stream.close(); + stream = file.open(fname); + let readStr = ""; + bufLen = BUFFER_BYTE_LEN; + let readLen = bufLen + 1; + do { + var frag = stream.read(readLen); + readStr += frag; + } while (frag); + stream.close(); + test.assertEqual(readStr, str, + "stream.write and read in chunks should work as expected"); + + // Read the same file, passing in strange numbers of bytes to read. + stream = file.open(fname); + test.assertEqual(stream.read(fileSize * 100), str, + "stream.read with big byte length should return string " + + "written"); + stream.close(); + + stream = file.open(fname); + test.assertEqual(stream.read(0), "", + "string.read with zero byte length should return empty " + + "string"); + stream.close(); + + stream = file.open(fname); + test.assertEqual(stream.read(-1), "", + "string.read with negative byte length should return " + + "empty string"); + stream.close(); + + file.remove(fname); +}; + +exports.testWriteAsync = function (test) { + test.waitUntilDone(); + + let fname = dataFileFilename(); + let str = "exports.testWriteAsync data!"; + let stream = file.open(fname, "w"); + test.assert(!stream.closed, "stream.closed after open should be false"); + + // Write. + stream.writeAsync(str, function (err) { + test.assertEqual(this, stream, "|this| should be the stream object"); + test.assertEqual(err, undefined, + "stream.writeAsync should not cause error"); + test.assert(stream.closed, "stream.closed after write should be true"); + test.assertRaises(function () stream.close(), + STREAM_CLOSED_ERROR, + "stream.close after already closed should raise error"); + test.assertRaises(function () stream.writeAsync("This shouldn't work!"), + STREAM_CLOSED_ERROR, + "stream.writeAsync after close should raise error"); + + // Read. + stream = file.open(fname, "r"); + test.assert(!stream.closed, "stream.closed after open should be false"); + let readStr = stream.read(); + test.assertEqual(readStr, str, + "string.read should yield string written"); + stream.close(); + file.remove(fname); + test.done(); + }); +}; + +exports.testUnload = function (test) { + let loader = Loader(module); + let file = loader.require("file"); + + let filename = url.toFilename(module.uri); + let stream = file.open(filename); + + loader.unload(); + test.assert(stream.closed, "stream should be closed after module unload"); +}; + +// Returns the name of a file that should be used to test writing and reading. +function dataFileFilename() { + let dir = file.dirname(url.toFilename(module.uri)); + return file.join(dir, "test-text-streams-data"); +} diff --git a/tools/addon-sdk-1.4/packages/api-utils/tests/test-timer.js b/tools/addon-sdk-1.4/packages/api-utils/tests/test-timer.js new file mode 100644 index 0000000..7c995e2 --- /dev/null +++ b/tools/addon-sdk-1.4/packages/api-utils/tests/test-timer.js @@ -0,0 +1,127 @@ +var timer = require("timer"); +const { Loader } = require("./helpers"); + +exports.testSetTimeout = function(test) { + timer.setTimeout(function() { + test.pass("testSetTimeout passed"); + test.done(); + }, 1); + test.waitUntilDone(); +}; + +exports.testParamedSetTimeout = function(test) { + let params = [1, 'foo', { bar: 'test' }, null, undefined]; + timer.setTimeout.apply(null, [function() { + test.assertEqual(arguments.length, params.length); + for (let i = 0, ii = params.length; i < ii; i++) + test.assertEqual(params[i], arguments[i]); + test.done(); + }, 1].concat(params)); + test.waitUntilDone(); +}; + +exports.testClearTimeout = function(test) { + var myFunc = function myFunc() { + test.fail("myFunc() should not be called in testClearTimeout"); + }; + var id = timer.setTimeout(myFunc, 1); + timer.setTimeout(function() { + test.pass("testClearTimeout passed"); + test.done(); + }, 2); + timer.clearTimeout(id); + test.waitUntilDone(); +}; + +exports.testParamedClearTimeout = function(test) { + let params = [1, 'foo', { bar: 'test' }, null, undefined]; + var myFunc = function myFunc() { + test.fail("myFunc() should not be called in testClearTimeout"); + }; + var id = timer.setTimeout(myFunc, 1); + timer.setTimeout.apply(null, [function() { + test.assertEqual(arguments.length, params.length); + for (let i = 0, ii = params.length; i < ii; i++) + test.assertEqual(params[i], arguments[i]); + test.done(); + }, 1].concat(params)); + timer.clearTimeout(id); + test.waitUntilDone(); +}; + +exports.testSetInterval = function (test) { + var count = 0; + var id = timer.setInterval(function () { + count++; + if (count >= 5) { + timer.clearInterval(id); + test.pass("testSetInterval passed"); + test.done(); + } + }, 1); + test.waitUntilDone(); +}; + +exports.testParamedSetInerval = function(test) { + let params = [1, 'foo', { bar: 'test' }, null, undefined]; + let count = 0; + let id = timer.setInterval.apply(null, [function() { + count ++; + if (count < 5) { + test.assertEqual(arguments.length, params.length); + for (let i = 0, ii = params.length; i < ii; i++) + test.assertEqual(params[i], arguments[i]); + } else { + timer.clearInterval(id); + test.done(); + } + }, 1].concat(params)); + test.waitUntilDone(); +}; + +exports.testClearInterval = function (test) { + timer.clearInterval(timer.setInterval(function () { + test.fail("setInterval callback should not be called"); + }, 1)); + var id = timer.setInterval(function () { + timer.clearInterval(id); + test.pass("testClearInterval passed"); + test.done(); + }, 2); + test.waitUntilDone(); +}; + +exports.testParamedClearInterval = function(test) { + timer.clearInterval(timer.setInterval(function () { + test.fail("setInterval callback should not be called"); + }, 1, timer, {}, null)); + + let id = timer.setInterval(function() { + timer.clearInterval(id); + test.assertEqual(3, arguments.length); + test.done(); + }, 2, undefined, 'test', {}); + test.waitUntilDone(); +}; + + +exports.testUnload = function(test) { + var loader = Loader(module); + var sbtimer = loader.require("timer"); + + var myFunc = function myFunc() { + test.fail("myFunc() should not be called in testUnload"); + }; + + sbtimer.setTimeout(myFunc, 1); + sbtimer.setTimeout(myFunc, 1, 'foo', 4, {}, undefined); + sbtimer.setInterval(myFunc, 1); + sbtimer.setInterval(myFunc, 1, {}, null, 'bar', undefined, 87); + loader.unload(); + timer.setTimeout(function() { + test.pass("timer testUnload passed"); + test.done(); + }, 2); + test.waitUntilDone(); +}; + diff --git a/tools/addon-sdk-1.4/packages/api-utils/tests/test-traceback.js b/tools/addon-sdk-1.4/packages/api-utils/tests/test-traceback.js new file mode 100644 index 0000000..6cf50f0 --- /dev/null +++ b/tools/addon-sdk-1.4/packages/api-utils/tests/test-traceback.js @@ -0,0 +1,114 @@ +var traceback = require("traceback"); +var {Cc,Ci,Cr,Cu} = require("chrome"); + +function throwNsIException() { + var ios = Cc['@mozilla.org/network/io-service;1'] + .getService(Ci.nsIIOService); + ios.newURI("i'm a malformed URI", null, null); +} + +function throwError() { + throw new Error("foob"); +} + +exports.testFormatDoesNotFetchRemoteFiles = function(test) { + var observers = require("observer-service"); + ["http", "https"].forEach( + function(scheme) { + var httpRequests = 0; + function onHttp() { + httpRequests++; + } + + observers.add("http-on-modify-request", onHttp); + + try { + var tb = [{filename: scheme + "://www.mozilla.org/", + lineNo: 1, + funcName: "blah"}]; + traceback.format(tb); + } catch (e) { + test.exception(e); + } + + observers.remove("http-on-modify-request", onHttp); + + test.assertEqual(httpRequests, 0, + "traceback.format() does not make " + + scheme + " request"); + }); +}; + +exports.testFromExceptionWithString = function(test) { + try { + throw "foob"; + test.fail("an exception should've been thrown"); + } catch (e if e == "foob") { + var tb = traceback.fromException(e); + test.assertEqual(tb.length, 0); + } +}; + +exports.testFormatWithString = function(test) { + // This can happen if e.g. a thrown exception was + // a string instead of an Error instance. + test.assertEqual(traceback.format("blah"), + "Traceback (most recent call last):"); +}; + +exports.testFromExceptionWithError = function(test) { + try { + throwError(); + test.fail("an exception should've been thrown"); + } catch (e if e instanceof Error) { + var tb = traceback.fromException(e); + var xulApp = require("xul-app"); + test.assertEqual(tb.slice(-1)[0].funcName, "throwError"); + } +}; + +exports.testFromExceptionWithNsIException = function(test) { + try { + throwNsIException(); + test.fail("an exception should've been thrown"); + } catch (e if e.result == Cr.NS_ERROR_MALFORMED_URI) { + var tb = traceback.fromException(e); + test.assertEqual(tb.slice(-1)[0].funcName, + "throwNsIException"); + } +}; + +exports.testFormat = function(test) { + function getTraceback() { + return traceback.format(); + } + + var formatted = getTraceback(); + test.assertEqual(typeof(formatted), "string"); + var lines = formatted.split("\n"); + test.assertEqual(lines.slice(-2)[0].indexOf("getTraceback") > 0, + true, + "formatted traceback should include function name"); + test.assertEqual(lines.slice(-1)[0].trim(), + "return traceback.format();", + "formatted traceback should include source code"); +}; + +exports.testExceptionsWithEmptyStacksAreLogged = function(test) { + // Ensures that our fix to bug 550368 works. + var sandbox = Cu.Sandbox("http://www.foo.com"); + var excRaised = false; + try { + Cu.evalInSandbox("returns 1 + 2;", sandbox, "1.8", + "blah.js", 25); + } catch (e) { + excRaised = true; + var stack = traceback.fromException(e); + test.assertEqual(stack.length, 1, "stack should have one frame"); + test.assert(stack[0].filename, "blah.js", "frame should have filename"); + test.assert(stack[0].lineNo, 25, "frame should have line no"); + test.assertEqual(stack[0].funcName, null, "frame should have null function name"); + } + if (!excRaised) + test.fail("Exception should have been raised."); +}; diff --git a/tools/addon-sdk-1.4/packages/api-utils/tests/test-traits-core.js b/tools/addon-sdk-1.4/packages/api-utils/tests/test-traits-core.js new file mode 100644 index 0000000..ecc2c51 --- /dev/null +++ b/tools/addon-sdk-1.4/packages/api-utils/tests/test-traits-core.js @@ -0,0 +1,834 @@ +"use strict"; + +const ERR_CONFLICT = 'Remaining conflicting property: ', + ERR_REQUIRED = 'Missing required property: '; + +function assertSametrait(test, trait1, trait2) { + let names1 = Object.getOwnPropertyNames(trait1), + names2 = Object.getOwnPropertyNames(trait2); + + test.assertEqual( + names1.length, + names2.length, + 'equal traits must have same amount of properties' + ); + + for (let i = 0; i < names1.length; i++) { + let name = names1[i]; + test.assertNotEqual( + -1, + names2.indexOf(name), + 'equal traits must contain same named properties: ' + name + ); + assertSameDescriptor(test, name, trait1[name], trait2[name]); + } +} + +function assertSameDescriptor(test, name, desc1, desc2) { + if (desc1.conflict || desc2.conflict) { + test.assertEqual( + desc1.conflict, + desc2.conflict, + 'if one of same descriptors has `conflict` another must have it: ' + + name + ); + } else if (desc1.required || desc2.required) { + test.assertEqual( + desc1.required, + desc2.required, + 'if one of same descriptors is has `required` another must have it: ' + + name + ); + } else { + test.assertEqual( + desc1.get, + desc2.get, + 'get must be the same on both descriptors: ' + name + ); + test.assertEqual( + desc1.set, + desc2.set, + 'set must be the same on both descriptors: ' + name + ); + test.assertEqual( + desc1.value, + desc2.value, + 'value must be the same on both descriptors: ' + name + ); + test.assertEqual( + desc1.enumerable, + desc2.enumerable, + 'enumerable must be the same on both descriptors: ' + name + ); + test.assertEqual( + desc1.required, + desc2.required, + 'value must be the same on both descriptors: ' + name + ); + } +} + +function Data(value, enumerable, confligurable, writable) { + return { + value: value, + enumerable: false !== enumerable, + confligurable: false !== confligurable, + writable: false !== writable + }; +} + +function Method(method, enumerable, confligurable, writable) { + return { + value: method, + enumerable: false !== enumerable, + confligurable: false !== confligurable, + writable: false !== writable + }; +} + +function Accessor(get, set, enumerable, confligurable) { + return { + get: get, + set: set, + enumerable: false !== enumerable, + confligurable: false !== confligurable, + }; +} + +function Required(name) { + function required() { throw new Error(ERR_REQUIRED + name) } + return { + get: required, + set: required, + required: true + }; +} + +function Conflict(name) { + function conflict() { throw new Error(ERR_CONFLICT + name) } + return { + get: conflict, + set: conflict, + conflict: true + }; +} + +function testMethod() {}; + +const { trait, compose, resolve, required, override, create } = + require('traits/core'); + + +exports['test:empty trait'] = function(test) { + assertSametrait( + test, + trait({}), + {} + ); +}; + +exports['test:simple trait'] = function(test) { + assertSametrait( + test, + trait({ + a: 0, + b: testMethod + }), + { + a: Data(0, true, true, true), + b: Method(testMethod, true, true, true) + } + ); +}; + +exports['test:simple trait with required prop'] = function(test) { + assertSametrait( + test, + trait({ + a: required, + b: 1 + }), + { + a: Required('a'), + b: Data(1) + } + ); +}; + +exports['test:ordering of trait properties is irrelevant'] = function(test) { + assertSametrait(test, + trait({ a: 0, b: 1, c: required }), + trait({ b: 1, c: required, a: 0 }) + ); +}; + +exports['test:trait with accessor property'] = function(test) { + let record = { get a() {}, set a(v) {} }; + let get = Object.getOwnPropertyDescriptor(record,'a').get; + let set = Object.getOwnPropertyDescriptor(record,'a').set; + assertSametrait(test, + trait(record), + { a: Accessor(get, set ) } + ); +}; + +exports['test:simple composition'] = function(test) { + assertSametrait(test, + compose( + trait({ a: 0, b: 1 }), + trait({ c: 2, d: testMethod }) + ), + { + a: Data(0), + b: Data(1), + c: Data(2), + d: Method(testMethod) + } + ); +}; + +exports['test:composition with conflict'] = function(test) { + assertSametrait(test, + compose( + trait({ a: 0, b: 1 }), + trait({ a: 2, c: testMethod }) + ), + { + a: Conflict('a'), + b: Data(1), + c: Method(testMethod) + } + ); +}; + +exports['test:composition of identical props does not cause conflict'] = +function(test) { + assertSametrait(test, + compose( + trait({ a: 0, b: 1 }), + trait({ a: 0, c: testMethod }) + ), + { + a: Data(0), + b: Data(1), + c: Method(testMethod) } + ) +}; + +exports['test:composition with identical required props'] = +function(test) { + assertSametrait(test, + compose( + trait({ a: required, b: 1 }), + trait({ a: required, c: testMethod }) + ), + { + a: Required(), + b: Data(1), + c: Method(testMethod) + } + ); +}; + +exports['test:composition satisfying a required prop'] = function (test) { + assertSametrait(test, + compose( + trait({ a: required, b: 1 }), + trait({ a: testMethod }) + ), + { + a: Method(testMethod), + b: Data(1) + } + ); +}; + +exports['test:compose is neutral wrt conflicts'] = function (test) { + assertSametrait(test, + compose( + compose( + trait({ a: 1 }), + trait({ a: 2 }) + ), + trait({ b: 0 }) + ), + { + a: Conflict('a'), + b: Data(0) + } + ); +}; + +exports['test:conflicting prop overrides required prop'] = function (test) { + assertSametrait(test, + compose( + compose( + trait({ a: 1 }), + trait({ a: 2 }) + ), + trait({ a: required }) + ), + { + a: Conflict('a') + } + ); +}; + +exports['test:compose is commutative'] = function (test) { + assertSametrait(test, + compose( + trait({ a: 0, b: 1 }), + trait({ c: 2, d: testMethod }) + ), + compose( + trait({ c: 2, d: testMethod }), + trait({ a: 0, b: 1 }) + ) + ); +}; + +exports['test:compose is commutative, also for required/conflicting props'] = +function (test) { + assertSametrait(test, + compose( + trait({ a: 0, b: 1, c: 3, e: required }), + trait({ c: 2, d: testMethod }) + ), + compose( + trait({ c: 2, d: testMethod }), + trait({ a: 0, b: 1, c: 3, e: required }) + ) + ); +}; +exports['test:compose is associative'] = function (test) { + assertSametrait(test, + compose( + trait({ a: 0, b: 1, c: 3, d: required }), + compose( + trait({ c: 3, d: required }), + trait({ c: 2, d: testMethod, e: 'foo' }) + ) + ), + compose( + compose( + trait({ a: 0, b: 1, c: 3, d: required }), + trait({ c: 3, d: required }) + ), + trait({ c: 2, d: testMethod, e: 'foo' }) + ) + ); +}; + +exports['test:diamond import of same prop does not generate conflict'] = +function (test) { + assertSametrait(test, + compose( + compose( + trait({ b: 2 }), + trait({ a: 1 }) + ), + compose( + trait({ c: 3 }), + trait({ a: 1 }) + ), + trait({ d: 4 }) + ), + { + a: Data(1), + b: Data(2), + c: Data(3), + d: Data(4) + } + ); +}; + +exports['test:resolve with empty resolutions has no effect'] = +function (test) { + assertSametrait(test, resolve({}, trait({ + a: 1, + b: required, + c: testMethod + })), { + a: Data(1), + b: Required(), + c: Method(testMethod) + }); +}; + +exports['test:resolve: renaming'] = function (test) { + assertSametrait(test, + resolve( + { a: 'A', c: 'C' }, + trait({ a: 1, b: required, c: testMethod }) + ), + { + A: Data(1), + b: Required(), + C: Method(testMethod), + a: Required(), + c: Required() + } + ); +}; + +exports['test:resolve: renaming to conflicting name causes conflict, order 1'] += function (test) { + assertSametrait(test, + resolve( + { a: 'b'}, + trait({ a: 1, b: 2 }) + ), + { + b: Conflict('b'), + a: Required() + } + ); +}; + +exports['test:resolve: renaming to conflicting name causes conflict, order 2'] += function (test) { + assertSametrait(test, + resolve( + { a: 'b' }, + trait({ b: 2, a: 1 }) + ), + { + b: Conflict('b'), + a: Required() + } + ); +}; + +exports['test:resolve: simple exclusion'] = function (test) { + assertSametrait(test, + resolve( + { a: undefined }, + trait({ a: 1, b: 2 }) + ), + { + a: Required(), + b: Data(2) + } + ); +}; + +exports['test:resolve: exclusion to "empty" trait'] = function (test) { + assertSametrait(test, + resolve( + { a: undefined, b: undefined }, + trait({ a: 1, b: 2 }) + ), + { + a: Required(), + b: Required() + } + ); +}; + +exports['test:resolve: exclusion and renaming of disjoint props'] = +function (test) { + assertSametrait(test, + resolve( + { a: undefined, b: 'c' }, + trait({ a: 1, b: 2 }) + ), + { + a: Required(), + c: Data(2), + b: Required() + } + ); +}; + +exports['test:resolve: exclusion and renaming of overlapping props'] = +function (test) { + assertSametrait(test, + resolve( + { a: undefined, b: 'a' }, + trait({ a: 1, b: 2 }) + ), + { + a: Data(2), + b: Required() + } + ); +}; + +exports['test:resolve: renaming to a common alias causes conflict'] = +function (test) { + assertSametrait(test, + resolve( + { a: 'c', b: 'c' }, + trait({ a: 1, b: 2 }) + ), + { + c: Conflict('c'), + a: Required(), + b: Required() + } + ); +}; + +exports['test:resolve: renaming overrides required target'] = +function (test) { + assertSametrait(test, + resolve( + { b: 'a' }, + trait({ a: required, b: 2 }) + ), + { + a: Data(2), + b: Required() + } + ); +}; + +exports['test:resolve: renaming required properties has no effect'] = +function (test) { + assertSametrait(test, + resolve( + { b: 'a' }, + trait({ a: 2, b: required }) + ), + { + a: Data(2), + b: Required() + } + ); +}; + +exports['test:resolve: renaming of non-existent props has no effect'] = +function (test) { + assertSametrait(test, + resolve( + { a: 'c', d: 'c' }, + trait({ a: 1, b: 2 }) + ), + { + c: Data(1), + b: Data(2), + a: Required() + } + ); +}; + +exports['test:resolve: exclusion of non-existent props has no effect'] = +function (test) { + assertSametrait(test, + resolve( + { b: undefined }, + trait({ a: 1 }) + ), + { + a: Data(1) + } + ); +}; + +exports['test:resolve is neutral w.r.t. required properties'] = +function (test) { + assertSametrait(test, + resolve( + { a: 'c', b: undefined }, + trait({ a: required, b: required, c: 'foo', d: 1 }) + ), + { + a: Required(), + b: Required(), + c: Data('foo'), + d: Data(1) + } + ); +}; + +exports['test:resolve supports swapping of property names, ordering 1'] = +function (test) { + assertSametrait(test, + resolve( + { a: 'b', b: 'a' }, + trait({ a: 1, b: 2 }) + ), + { + a: Data(2), + b: Data(1) + } + ); +}; + +exports['test:resolve supports swapping of property names, ordering 2'] = +function (test) { + assertSametrait(test, + resolve( + { b: 'a', a: 'b' }, + trait({ a: 1, b: 2 }) + ), + { + a: Data(2), + b: Data(1) + } + ); +}; + +exports['test:resolve supports swapping of property names, ordering 3'] = +function (test) { + assertSametrait(test, + resolve( + { b: 'a', a: 'b' }, + trait({ b: 2, a: 1 }) + ), + { + a: Data(2), + b: Data(1) + } + ); +}; + +exports['test:resolve supports swapping of property names, ordering 4'] = +function (test) { + assertSametrait(test, + resolve( + { a: 'b', b: 'a' }, + trait({ b: 2, a: 1 }) + ), + { + a: Data(2), + b: Data(1) + } + ); +}; + +exports['test:override of mutually exclusive traits'] = function (test) { + assertSametrait(test, + override( + trait({ a: 1, b: 2 }), + trait({ c: 3, d: testMethod }) + ), + { + a: Data(1), + b: Data(2), + c: Data(3), + d: Method(testMethod) + } + ); +}; + +exports['test:override of mutually exclusive traits is compose'] = +function (test) { + assertSametrait(test, + override( + trait({ a: 1, b: 2 }), + trait({ c: 3, d: testMethod }) + ), + compose( + trait({ d: testMethod, c: 3 }), + trait({ b: 2, a: 1 }) + ) + ); +}; + +exports['test:override of overlapping traits'] = function (test) { + assertSametrait(test, + override( + trait({ a: 1, b: 2 }), + trait({ a: 3, c: testMethod }) + ), + { + a: Data(1), + b: Data(2), + c: Method(testMethod) + } + ); +}; + +exports['test:three-way override of overlapping traits'] = function (test) { + assertSametrait(test, + override( + trait({ a: 1, b: 2 }), + trait({ b: 4, c: 3 }), + trait({ a: 3, c: testMethod, d: 5 }) + ), + { + a: Data(1), + b: Data(2), + c: Data(3), + d: Data(5) + } + ); +}; + +exports['test:override replaces required properties'] = function (test) { + assertSametrait(test, + override( + trait({ a: required, b: 2 }), + trait({ a: 1, c: testMethod }) + ), + { + a: Data(1), + b: Data(2), + c: Method(testMethod) + } + ); +}; + +exports['test:override is not commutative'] = function (test) { + assertSametrait(test, + override( + trait({ a: 1, b: 2 }), + trait({ a: 3, c: 4 }) + ), + { + a: Data(1), + b: Data(2), + c: Data(4) + } + ); + + assertSametrait(test, + override( + trait({ a: 3, c: 4 }), + trait({ a: 1, b: 2 }) + ), + { + a: Data(3), + b: Data(2), + c: Data(4) + } + ); +}; + +exports['test:override is associative'] = function (test) { + assertSametrait(test, + override( + override( + trait({ a: 1, b: 2 }), + trait({ a: 3, c: 4, d: 5 }) + ), + trait({ a: 6, c: 7, e: 8 }) + ), + override( + trait({ a: 1, b: 2 }), + override( + trait({ a: 3, c: 4, d: 5 }), + trait({ a: 6, c: 7, e: 8 }) + ) + ) + ); +}; + +exports['test:create simple'] = function(test) { + let o1 = create( + Object.prototype, + trait({ a: 1, b: function() { return this.a; } }) + ); + + test.assertEqual( + Object.prototype, + Object.getPrototypeOf(o1), + 'o1 prototype' + ); + test.assertEqual(1, o1.a, 'o1.a'); + test.assertEqual(1, o1.b(), 'o1.b()'); + test.assertEqual( + 2, + Object.getOwnPropertyNames(o1).length, + 'Object.keys(o1).length === 2' + ); +}; + +exports['test:create with Array.prototype'] = function(test) { + let o2 = create(Array.prototype, trait({})); + test.assertEqual( + Array.prototype, + Object.getPrototypeOf(o2), + "o2 prototype" + ); +}; + +exports['test:exception for incomplete required properties'] = +function(test) { + try { + create(Object.prototype, trait({ foo: required })); + test.fail('expected create to complain about missing required props'); + } catch(e) { + test.assertEqual( + 'Error: Missing required property: foo', + e.toString(), + 'required prop error' + ); + } +}; + +exports['test:exception for unresolved conflicts'] = function(test) { + try { + create({}, compose(trait({ a: 0 }), trait({ a: 1 }))); + test.fail('expected create to complain about unresolved conflicts'); + } catch(e) { + test.assertEqual( + 'Error: Remaining conflicting property: a', + e.toString(), + 'conflicting prop error' + ); + } +}; + +exports['test:verify that required properties are present but undefined'] = +function(test) { + try { + let o4 = Object.create(Object.prototype, trait({ foo: required })); + test.assertEqual(true, 'foo' in o4, 'required property present'); + try { + let foo = o4.foo; + test.fail('access to required property must throw'); + } catch(e) { + test.assertEqual( + 'Error: Missing required property: foo', + e.toString(), + 'required prop error' + ) + } + } catch(e) { + test.fail('did not expect create to complain about required props'); + } +}; + +exports['test:verify that conflicting properties are present'] = +function(test) { + try { + let o5 = Object.create( + Object.prototype, + compose(trait({ a: 0 }), trait({ a: 1 })) + ); + test.assertEqual(true, 'a' in o5, 'conflicting property present'); + try { + let a = o5.a; // accessors or data prop + test.fail('expected conflicting prop to cause exception'); + } catch (e) { + test.assertEqual( + 'Error: Remaining conflicting property: a', + e.toString(), + 'conflicting prop access error' + ); + } + } catch(e) { + test.fail('did not expect create to complain about conflicting props'); + } +}; + +exports['test diamond with conflicts'] = function(test) { + function makeT1(x) trait({ m: function() { return x; } }) + function makeT2(x) compose(trait({ t2: 'foo' }), makeT1(x)) + function makeT3(x) compose(trait({ t3: 'bar' }), makeT1(x)) + + let T4 = compose(makeT2(5), makeT3(5)); + try { + let o = create(Object.prototype, T4); + test.fail('expected diamond prop to cause exception'); + } catch(e) { + test.assertEqual( + 'Error: Remaining conflicting property: m', + e.toString(), + 'diamond prop conflict' + ); + } +}; + diff --git a/tools/addon-sdk-1.4/packages/api-utils/tests/test-traits.js b/tools/addon-sdk-1.4/packages/api-utils/tests/test-traits.js new file mode 100644 index 0000000..6940616 --- /dev/null +++ b/tools/addon-sdk-1.4/packages/api-utils/tests/test-traits.js @@ -0,0 +1,394 @@ +"use strict"; + +const { Trait } = require('traits'); + +exports['test:simple compose'] = function(test) { + let List = Trait.compose({ + _list: null, + constructor: function List() { + this._list = []; + }, + list: function list() this._list.slice(0), + add: function add(item) this._list.push(item), + remove: function remove(item) { + let list = this._list; + let index = list.indexOf(item); + if (0 <= index) list.slice(index, 1); + } + }); + + test.assertNotEqual(undefined, List, 'should not be undefined'); + test.assertEqual('function', typeof List, 'type should be function'); + test.assertEqual( + Trait.compose, + List.compose, + 'should inherit static compose' + ); + test.assertEqual( + Trait.override, + List.override, + 'should inherit static override' + ); + test.assertEqual( + Trait.required, + List.required, + 'should inherit static required' + ); + test.assertEqual( + Trait.resolve, + List.resolve, + 'should inherit static resolve' + ); + + test.assert( + !('_list' in List.prototype), + 'should not expose private API' + ); +} +exports['test: compose trait instance and create instance'] = function(test) { + let List = Trait.compose({ + constructor: function List(options) { + this._list = []; + this._public.publicMember = options.publicMember; + }, + _privateMember: true, + get privateMember() this._privateMember, + get list() this._list.slice(0), + add: function add(item) this._list.push(item), + remove: function remove(item) { + let list = this._list + let index = list.indexOf(item) + if (0 <= index) list.slice(index, 1) + } + }); + let list = List({ publicMember: true }); + + test.assertEqual('object', typeof list, 'should return an object') + test.assertEqual( + true, + list instanceof List, + 'should be instance of a List' + ); + + test.assertEqual( + undefined, + list._privateMember, + 'instance should not expose private API' + ); + + test.assertEqual( + true, + list.privateMember, + 'privates are accessible by public API' + ); + + list._privateMember = false; + + test.assertEqual( + true, + list.privateMember, + 'property changes on instance must not affect privates' + ); + + test.assert( + !('_list' in list), + 'instance should not expose private members' + ); + + test.assertEqual( + true, + list.publicMember, + 'public members are exposed' + ) + test.assertEqual( + 'function', + typeof list.add, + 'should be function' + ) + test.assertEqual( + 'function', + typeof list.remove, + 'should be function' + ); + + list.add(1); + test.assertEqual( + 1, + list.list[0], + 'exposed public API should be able of modifying privates' + ) +}; + + +exports['test:instances must not be hackable'] = function(test) { + let SECRET = 'There is no secret!', + secret = null; + + let Class = Trait.compose({ + _secret: null, + protect: function(data) this._secret = data + }); + + let i1 = Class(); + i1.protect(SECRET); + + test.assertEqual( + undefined, + (function() this._secret).call(i1), + 'call / apply can\'t access private state' + ); + + let proto = Object.getPrototypeOf(i1); + try { + proto.reveal = function() this._secret; + secret = i1.reveal(); + } catch(e) {} + test.assertNotEqual( + SECRET, + secret, + 'public __proto__ changes should not affect privates' + ); + secret = null; + + let Class2 = Trait.compose({ + _secret: null, + protect: function(data) this._secret = data + }); + let i2 = Class2(); + i2.protect(SECRET); + try { + Object.prototype.reveal = function() this._secret; + secret = i2.reveal(); + } catch(e) {} + test.assertNotEqual( + SECRET, + secret, + 'Object.prototype changes must not affect instances' + ); +} + +exports['test:instanceof'] = function(test) { + const List = Trait.compose({ + // private API: + _list: null, + // public API + constructor: function List() { + this._list = [] + }, + get length() this._list.length, + add: function add(item) this._list.push(item), + remove: function remove(item) { + let list = this._list; + let index = list.indexOf(item); + if (0 <= index) list.slice(index, 1); + } + }); + + test.assert(List() instanceof List, 'Must be instance of List'); + test.assert(new List() instanceof List, 'Must be instance of List'); +}; + +exports['test:privates are unaccessible'] = function(test) { + const List = Trait.compose({ + // private API: + _list: null, + // public API + constructor: function List() { + this._list = []; + }, + get length() this._list.length, + add: function add(item) this._list.push(item), + remove: function remove(item) { + let list = this._list; + let index = list.indexOf(item); + if (0 <= index) list.slice(index, 1); + } + }); + + let list = List(); + test.assert(!('_list' in list), 'no privates on instance'); + test.assert( + !('_list' in List.prototype), + 'no privates on prototype' + ); +}; + +exports['test:public API can access private API'] = function(test) { + const List = Trait.compose({ + // private API: + _list: null, + // public API + constructor: function List() { + this._list = []; + }, + get length() this._list.length, + add: function add(item) this._list.push(item), + remove: function remove(item) { + let list = this._list; + let index = list.indexOf(item); + if (0 <= index) list.slice(index, 1); + } + }); + let list = List(); + + list.add('test'); + + test.assertEqual( + 1, + list.length, + 'should be able to add element and access it from public getter' + ); +}; + +exports['test:required'] = function(test) { + const Enumerable = Trait.compose({ + list: Trait.required, + forEach: function forEach(consumer) { + return this.list.forEach(consumer); + } + }); + + try { + let i = Enumerable(); + test.fail('should throw when creating instance with required properties'); + } catch(e) { + test.assertEqual( + 'Error: Missing required property: list', + e.toString(), + 'required prop error' + ); + } +}; + +exports['test:compose with required'] = function(test) { + const List = Trait.compose({ + // private API: + _list: null, + // public API + constructor: function List() { + this._list = []; + }, + get length() this._list.length, + add: function add(item) this._list.push(item), + remove: function remove(item) { + let list = this._list; + let index = list.indexOf(item); + if (0 <= index) list.slice(index, 1); + } + }); + + const Enumerable = Trait.compose({ + list: Trait.required, + forEach: function forEach(consumer) { + return this.list.forEach(consumer); + } + }); + + const EnumerableList = Enumerable.compose({ + get list() this._list.slice(0) + }, List); + + let array = [1,2, 'ab'] + let l = EnumerableList(array); + array.forEach(function(element) l.add(element)); + let number = 0; + l.forEach(function(element, index) { + number ++; + test.assertEqual(array[index], element, 'should mach array element') + }); + test.assertEqual( + array.length, + number, + 'should perform as many asserts as elements in array' + ); +}; + +exports['test:resolve'] = function(test) { + const List = Trait.compose({ + // private API: + _list: null, + // public API + constructor: function List() { + this._list = []; + }, + get length() this._list.length, + add: function add(item) this._list.push(item), + remove: function remove(item) { + let list = this._list; + let index = list.indexOf(item); + if (0 <= index) list.slice(index, 1); + } + }); + + const Range = List.resolve({ + constructor: null, + add: '_add', + }).compose({ + min: null, + max: null, + get list() this._list.slice(0), + constructor: function Range(min, max) { + this.min = min; + this.max = max; + this._list = []; + }, + add: function(item) { + if (item <= this.max && item >= this.min) + this._add(item) + } + }); + + let r = Range(0, 10); + + test.assertEqual( + 0, + r.min, + 'constructor must have set min' + ); + test.assertEqual( + 10, + r.max, + 'constructor must have set max' + ); + + test.assertEqual( + 0, + r.length, + 'should not contain any elements' + ); + + r.add(5); + + test.assertEqual( + 1, + r.length, + 'should add `5` to list' + ); + + r.add(12); + + test.assertEqual( + 1, + r.length, + 'should not add `12` to list' + ); +}; + +exports['test:custom iterator'] = function(test) { + let Sub = Trait.compose({ + foo: "foo", + bar: "bar", + baz: "baz", + __iterator__: function() { + yield 1; + yield 2; + yield 3; + } + }); + + let (i = 0, sub = Sub()) { + for (let item in sub) + test.assertEqual(++i, item, "iterated item has the right value"); + }; +}; + diff --git a/tools/addon-sdk-1.4/packages/api-utils/tests/test-type.js b/tools/addon-sdk-1.4/packages/api-utils/tests/test-type.js new file mode 100644 index 0000000..072075c --- /dev/null +++ b/tools/addon-sdk-1.4/packages/api-utils/tests/test-type.js @@ -0,0 +1,88 @@ +"use strict" + +var utils = require("type"); + +exports["test function"] = function (assert) { + assert.ok(utils.isFunction(function(){}), "value is function"); + assert.ok(utils.isFunction(Object), "Object is function"); + assert.ok(utils.isFunction(new Function("")), "Genertaed value is function"); + assert.ok(!utils.isFunction({}), "object is not a function"); + assert.ok(!utils.isFunction(4), "number is not a function"); +}; + +exports["test atoms"] = function (assert) { + assert.ok(utils.isPrimitive(2), "number is primitive"); + assert.ok(utils.isPrimitive(NaN), "`NaN` is primitve"); + assert.ok(utils.isPrimitive(undefined), "`undefined` is primitive"); + assert.ok(utils.isPrimitive(null), "`null` is primitive"); + assert.ok(utils.isPrimitive(Infinity), "`Infinity` is primitive"); + assert.ok(utils.isPrimitive("foo"), "strings are primitive"); + assert.ok(utils.isPrimitive(true) && utils.isPrimitive(false), + "booleans are primitive"); +}; + +exports["test object"] = function (assert) { + assert.ok(utils.isObject({}), "`{}` is object"); + assert.ok(!utils.isObject(null), "`null` is not an object"); + assert.ok(!utils.isObject(Object), "functions is not an object"); +}; + +exports["test flat objects"] = function (assert) { + assert.ok(utils.isFlat({}), "`{}` is a flat object"); + assert.ok(!utils.isFlat([]), "`[]` is not a flat object"); + assert.ok(!utils.isFlat(new function() {}), "derived objects are not flat"); + assert.ok(utils.isFlat(Object.prototype), "Object.prototype is flat"); +}; + +exports["test json atoms"] = function (assert) { + assert.ok(utils.isJSON(null), "`null` is JSON"); + assert.ok(utils.isJSON(undefined), "`undefined` is JSON"); + assert.ok(utils.isJSON(NaN), "`NaN` is JSON"); + assert.ok(utils.isJSON(Infinity), "`Infinity` is JSON"); + assert.ok(utils.isJSON(true) && utils.isJSON(false), "booleans are JSON"); + assert.ok(utils.isJSON(4), utils.isJSON(0), "numbers are JSON"); + assert.ok(utils.isJSON("foo bar"), "strings are JSON"); +}; + +exports["test instanceOf"] = function (assert) { + assert.ok(utils.instanceOf(assert, Object), + "assert is object from other sandbox"); + assert.ok(utils.instanceOf(new Date(), Date), "instance of date"); + assert.ok(!utils.instanceOf(null, Object), "null is not an instance"); +}; + +exports["test json"] = function (assert) { + assert.ok(!utils.isJSON(function(){}), "functions are not json"); + assert.ok(utils.isJSON({}), "`{}` is JSON"); + assert.ok(utils.isJSON({ + a: "foo", + b: 3, + c: undefined, + d: null, + e: { + f: { + g: "bar", + p: [{}, "oueou", 56] + }, + q: { nan: NaN, infinity: Infinity }, + "non standard name": "still works" + } + }), "JSON can contain nested objects"); + + var foo = {}; + var bar = { foo: foo }; + foo.bar = bar; + assert.ok(!utils.isJSON(foo), "recursive objects are not json"); + + + assert.ok(!utils.isJSON({ get foo() { return 5 } }), + "json can not have getter"); + + assert.ok(!utils.isJSON({ foo: "bar", baz: function () {} }), + "json can not contain functions"); + + assert.ok(!utils.isJSON(Object.create({})), + "json must be direct descendant of `Object.prototype`"); +}; + +require("test").run(exports); diff --git a/tools/addon-sdk-1.4/packages/api-utils/tests/test-unit-test.js b/tools/addon-sdk-1.4/packages/api-utils/tests/test-unit-test.js new file mode 100644 index 0000000..eeef994 --- /dev/null +++ b/tools/addon-sdk-1.4/packages/api-utils/tests/test-unit-test.js @@ -0,0 +1,247 @@ +const timer = require("timer"); +const { Loader } = require("./helpers"); + +var setupCalled = false, teardownCalled = false; + +exports.setup = function() { + setupCalled = true; +}; + +exports.teardown = function() { + teardownCalled = true; + setupCalled = false; +}; + +// Important note - unit tests are run in alphabetical order. The following +// unit tests for setup/teardown are order dependent, sometimes the result of +// one test is checked in the next test (testing for teardown does this). When +// tests are cohesively a single unit, they are named <test_name> - partN where +// N is their order in the sequence. Secondly, because these tests should be +// run before all others, they start with an A. +exports.testASetupTeardownSyncTestPart1 = function(test) { + test.assertEqual(true, setupCalled, 'setup function was called before this'); + test.assertEqual(false, teardownCalled, 'teardown function was not called before this'); +}; + +exports.testASetupTeardownSyncTestPart2 = function(test) { + test.assertEqual(true, setupCalled, 'setup was re-called before this'); + test.assertEqual(true, teardownCalled, 'teardown was called after first function'); +}; + +exports.testATeardownAsyncTestPart1 = function(test) { + teardownCalled = false; + + timer.setTimeout(function() { + test.assertEqual(false, teardownCalled, "teardown not called until done"); + test.done(); + }, 200); + test.waitUntilDone(); +}; + +exports.testATeardownAsyncTestPart2 = function(test) { + test.assertEqual(true, teardownCalled, "teardown called after done"); +}; + +exports.testWaitUntilInstant = function(test) { + test.waitUntilDone(); + + test.waitUntil(function () true, "waitUntil with instant true pass") + .then(function () test.done()); +} + +exports.testWaitUntil = function(test) { + test.waitUntilDone(); + let succeed = false; + + test.waitUntil(function () succeed, "waitUntil pass") + .then(function () test.done()); + + timer.setTimeout(function () { + succeed = true; + }, 200); +} + +exports.testWaitUntilEqual = function(test) { + test.waitUntilDone(); + let succeed = false; + + test.waitUntilEqual("foo", function () succeed ? "foo" : "bar", + "waitUntilEqual pass") + .then(function () test.done()); + + timer.setTimeout(function () { + succeed = true; + }, 200); +} + +exports.testWaitUntilNotEqual = function(test) { + test.waitUntilDone(); + let succeed = false; + + test.waitUntilNotEqual("foo", function () succeed ? "bar" : "foo", + "waitUntilNotEqual pass") + .then(function () test.done()); + + timer.setTimeout(function () { + succeed = true; + }, 200); +} + +exports.testWaitUntilMatches = function(test) { + test.waitUntilDone(); + let succeed = false; + + test.waitUntilMatches(function () succeed ? "foo" : "bar", + /foo/, "waitUntilEqual pass") + .then(function () test.done()); + + timer.setTimeout(function () { + succeed = true; + }, 200); +} + +exports.testWaitUntilErrorInCallback = function(test) { + test.waitUntilDone(); + + test.expectFail(function() { + test.waitUntil(function () {throw "oops"}, "waitUntil pass") + .then(function () test.done()); + }); +} + +exports.testWaitUntilTimeoutInCallback = function(test) { + test.waitUntilDone(1000); + + let runner = new (require("unit-test").TestRunner)({ + console: { + calls: 0, + error: function(msg) { + this.calls++; + if (this.calls == 1) + test.assertEqual(arguments[0], "TEST FAILED: wait4ever (timed out)"); + else if (this.calls == 2) { + test.assertEqual(arguments[0], "test assertion never became true:\n"); + test.assertEqual(arguments[1], "assertion failed, value is false\n"); + // We could additionally check that arguments[1] contains the correct + // stack, but it would be difficult to do so given that it contains + // resource: URLs with a randomly generated string embedded in them + // (the ID of the test addon created to run the tests). And in any + // case, checking the arguments seems sufficient. + + test.done(); + } + else { + test.fail("We got unexpected console.error() calls from waitUntil" + + " assertion callback: '" + arguments[1] + "'"); + } + }, + trace: function () {} + } + }); + + runner.start({ + test: { + name: "wait4ever", + testFunction: function(test) { + test.waitUntilDone(100); + test.waitUntil(function() false); + } + }, + onDone: function() {} + }); +}; + +exports.testExpectFail = function(test) { + test.expectFail(function() { + test.fail('expectFail masking .fail'); + }); + + test.expectFail(function() { + test.assert(false, 'expectFail masking .assert'); + }); + + test.assert(true, 'assert should pass with no expectFail'); +/* + test.expectFail(function() { + test.expectFail(function() { + test.fail('this should blow up'); + }); + }); +*/ +}; + +exports.testAssertFunction = function(test) { + test.assertFunction(function() {}, 'assertFunction with function'); + test.expectFail(function() { + test.assertFunction(null, 'assertFunction with non-function'); + }); +}; + +exports.testAssertUndefined = function(test) { + test.assertUndefined(undefined, 'assertUndefined with undefined'); + test.expectFail(function() { + test.assertUndefined(null, 'assertUndefined with null'); + }); + test.expectFail(function() { + test.assertUndefined(false, 'assertUndefined with false'); + }); + test.expectFail(function() { + test.assertUndefined(0, 'assertUndefined with 0'); + }); +}; + +exports.testAssertNotUndefined = function(test) { + test.expectFail(function() { + test.assertNotUndefined(undefined, 'assertNotUndefined with undefined'); + }); + test.assertNotUndefined(null, 'assertNotUndefined with null'); + test.assertNotUndefined(false, 'assertNotUndefined with false'); + test.assertNotUndefined(0, 'assertNotUndefined with 0'); +}; + +exports.testAssertNull = function(test) { + test.assertNull(null, 'assertNull with null'); + test.expectFail(function() { + test.assertNull(undefined, 'assertNull with undefined'); + }); + test.expectFail(function() { + test.assertNull(false, 'assertNull with false'); + }); + test.expectFail(function() { + test.assertNull(0, 'assertNull with 0'); + }); +}; + +exports.testAssertNotNull = function(test) { + test.assertNotNull(undefined, 'assertNotNull with undefined'); + test.assertNotNull(false, 'assertNotNull with false'); + test.assertNotNull(0, 'assertNotNull with 0'); + + test.expectFail(function() { + test.assertNotNull(null, 'testAssertNotNull with null'); + }); +}; + +exports.testAssertObject = function(test) { + test.assertObject({}, 'assertObject with {}' ); + test.assertObject(new Object(), 'assertObject with new Object'); + test.expectFail(function() { + test.assertObject('fail', 'assertObject with string'); + }); +}; + +exports.testAssertString = function(test) { + test.assertString('', 'assertString with ""'); + test.assertString(new String(), 'assertString with new String'); +}; + +exports.testAssertArray = function(test) { + test.assertArray([], 'assertArray with []'); + test.assertArray(new Array(), 'assertArray with new Array'); +}; + +exports.testNumber = function(test) { + test.assertNumber(1, 'assertNumber with 1'); + test.assertNumber(new Number('2'), 'assertNumber with new Number("2")' ); +}; + diff --git a/tools/addon-sdk-1.4/packages/api-utils/tests/test-unload.js b/tools/addon-sdk-1.4/packages/api-utils/tests/test-unload.js new file mode 100644 index 0000000..33e84d0 --- /dev/null +++ b/tools/addon-sdk-1.4/packages/api-utils/tests/test-unload.js @@ -0,0 +1,196 @@ +/* -*- Mode: Java; tab-width: 2; indent-tabs-mode: nil; c-basic-offset: 2 -*- */ +/* vim:set ts=2 sw=2 sts=2 et: */ +/* ***** BEGIN LICENSE BLOCK ***** + * Version: MPL 1.1/GPL 2.0/LGPL 2.1 + * + * The contents of this file are subject to the Mozilla Public License Version + * 1.1 (the "License"); you may not use this file except in compliance with + * the License. You may obtain a copy of the License at + * http://www.mozilla.org/MPL/ + * + * Software distributed under the License is distributed on an "AS IS" basis, + * WITHOUT WARRANTY OF ANY KIND, either express or implied. See the License + * for the specific language governing rights and limitations under the + * License. + * + * The Original Code is Jetpack. + * + * The Initial Developer of the Original Code is + * the Mozilla Foundation. + * Portions created by the Initial Developer are Copyright (C) 2010 + * the Initial Developer. All Rights Reserved. + * + * Contributor(s): + * Atul Varma <atul@mozilla.com> (Original Author) + * Drew Willcoxon <adw@mozilla.com> + * Erik Vold <erikvvold@gmail.com> + * + * Alternatively, the contents of this file may be used under the terms of + * either the GNU General Public License Version 2 or later (the "GPL"), or + * the GNU Lesser General Public License Version 2.1 or later (the "LGPL"), + * in which case the provisions of the GPL or the LGPL are applicable instead + * of those above. If you wish to allow use of your version of this file only + * under the terms of either the GPL or the LGPL, and not to allow others to + * use your version of this file under the terms of the MPL, indicate your + * decision by deleting the provisions above and replace them with the notice + * and other provisions required by the GPL or the LGPL. If you do not delete + * the provisions above, a recipient may use your version of this file under + * the terms of any one of the MPL, the GPL or the LGPL. + * + * ***** END LICENSE BLOCK ***** */ + +var unload = require("unload"); +var { Loader } = require("./helpers"); + +exports.testUnloading = function(test) { + var loader = Loader(module); + var ul = loader.require("unload"); + var unloadCalled = 0; + var errorsReported = 0; + function unload() { + unloadCalled++; + throw new Error("error"); + } + ul.when(unload); + + // This should be ignored, as we already registered it + ul.when(unload); + + function unload2() { unloadCalled++; } + ul.when(unload2); + loader.unload(undefined, function onError() { errorsReported++; }); + test.assertEqual(unloadCalled, 2, + "Unloader functions are called on unload."); + test.assertEqual(errorsReported, 1, + "One unload handler threw exception"); +}; + +exports.testEnsure = function(test) { + test.assertRaises(function() { unload.ensure({}); }, + "object has no 'unload' property", + "passing obj with no unload prop should fail"); + test.assertRaises(function() { unload.ensure({}, "destroy"); }, + "object has no 'destroy' property", + "passing obj with no custom unload prop should fail"); + + var called = 0; + var obj = {unload: function() { called++; }}; + + unload.ensure(obj); + obj.unload(); + test.assertEqual(called, 1, + "unload() should be called"); + obj.unload(); + test.assertEqual(called, 1, + "unload() should be called only once"); +}; + +/** + * Check that destructors are called only once with Traits. + * - check that public API is calling the destructor and unregister it, + * - check that composed traits with multiple ensure calls, leads to only + * one destructor call. + */ +exports.testEnsureWithTraits = function(test) { + + let { Trait } = require("traits"); + let loader = Loader(module); + let ul = loader.require("unload"); + + let called = 0; + let composedCalled = 0; + let composedTrait = Trait.compose({ + constructor: function () { + // We have to give "public interface" of this trait, as we want to + // call public `unload` method and ensure that we call it only once, + // either when we call this public function manually or on add-on unload + ul.ensure(this._public); + }, + unload: function unload() { + composedCalled++; + } + }); + let obj = Trait.compose( + composedTrait.resolve({ + constructor: "_constructor", + unload : "_unload" + }), { + constructor: function constructor() { + // Same thing applies here, we need to pass public interface + ul.ensure(this._public); + this._constructor(); + }, + unload: function unload() { + called++; + this._unload(); + } + })(); + + obj.unload(); + test.assertEqual(called, 1, + "unload() should be called"); + + test.assertEqual(composedCalled, 1, + "composed object unload() should be called"); + + obj.unload(); + test.assertEqual(called, 1, + "unload() should be called only once"); + test.assertEqual(composedCalled, 1, + "composed object unload() should be called only once"); + + loader.unload(); + test.assertEqual(called, 1, + "unload() should be called only once, after addon unload"); + test.assertEqual(composedCalled, 1, + "composed object unload() should be called only once, " + + "after addon unload"); +}; + +exports.testEnsureWithTraitsPrivate = function(test) { + + let { Trait } = require("traits"); + let loader = Loader(module); + let ul = loader.require("unload"); + + let called = 0; + let privateObj = null; + let obj = Trait.compose({ + constructor: function constructor() { + // This time wa don't have to give public interface, + // as we want to call a private method: + ul.ensure(this, "_unload"); + privateObj = this; + }, + _unload: function unload() { + called++; + this._unload(); + } + })(); + + loader.unload(); + test.assertEqual(called, 1, + "unload() should be called"); + + privateObj._unload(); + test.assertEqual(called, 1, + "_unload() should be called only once, after addon unload"); +}; + +exports.testReason = function (test) { + var reason = "Reason doesn't actually have to be anything in particular."; + var loader = Loader(module); + var ul = loader.require("unload"); + ul.when(function (rsn) { + test.assertEqual(rsn, reason, + "when() reason should be reason given to loader"); + }); + var obj = { + unload: function (rsn) { + test.assertEqual(rsn, reason, + "ensure() reason should be reason given to loader"); + } + }; + ul.ensure(obj); + loader.unload(reason); +}; diff --git a/tools/addon-sdk-1.4/packages/api-utils/tests/test-url.js b/tools/addon-sdk-1.4/packages/api-utils/tests/test-url.js new file mode 100644 index 0000000..3307769 --- /dev/null +++ b/tools/addon-sdk-1.4/packages/api-utils/tests/test-url.js @@ -0,0 +1,157 @@ +var url = require("url"); + +exports.testResolve = function(test) { + test.assertEqual(url.URL("bar", "http://www.foo.com/").toString(), + "http://www.foo.com/bar"); + + test.assertEqual(url.URL("bar", "http://www.foo.com"), + "http://www.foo.com/bar"); + + test.assertEqual(url.URL("http://bar.com/", "http://foo.com/"), + "http://bar.com/", + "relative should override base"); + + test.assertRaises(function() { url.URL("blah"); }, + "malformed URI: blah", + "url.resolve() should throw malformed URI on base"); + + test.assertRaises(function() { url.URL("chrome://global"); }, + "invalid URI: chrome://global", + "url.resolve() should throw invalid URI on base"); + + test.assertRaises(function() { url.URL("chrome://foo/bar"); }, + "invalid URI: chrome://foo/bar", + "url.resolve() should throw on bad chrome URI"); + + test.assertEqual(url.URL("", "http://www.foo.com"), + "http://www.foo.com/", + "url.resolve() should add slash to end of domain"); +}; + +exports.testParseHttp = function(test) { + var info = url.URL("http://foo.com/bar"); + test.assertEqual(info.scheme, "http"); + test.assertEqual(info.host, "foo.com"); + test.assertEqual(info.port, null); + test.assertEqual(info.userPass, null); + test.assertEqual(info.path, "/bar"); +}; + +exports.testParseHttpWithPort = function(test) { + var info = url.URL("http://foo.com:5/bar"); + test.assertEqual(info.port, 5); +}; + +exports.testParseChrome = function(test) { + var info = url.URL("chrome://global/content/blah"); + test.assertEqual(info.scheme, "chrome"); + test.assertEqual(info.host, "global"); + test.assertEqual(info.port, null); + test.assertEqual(info.userPass, null); + test.assertEqual(info.path, "/content/blah"); +}; + +exports.testParseAbout = function(test) { + var info = url.URL("about:boop"); + test.assertEqual(info.scheme, "about"); + test.assertEqual(info.host, null); + test.assertEqual(info.port, null); + test.assertEqual(info.userPass, null); + test.assertEqual(info.path, "boop"); +}; + +exports.testParseFTP = function(test) { + var info = url.URL("ftp://1.2.3.4/foo"); + test.assertEqual(info.scheme, "ftp"); + test.assertEqual(info.host, "1.2.3.4"); + test.assertEqual(info.port, null); + test.assertEqual(info.userPass, null); + test.assertEqual(info.path, "/foo"); +}; + +exports.testParseFTPWithUserPass = function(test) { + var info = url.URL("ftp://user:pass@1.2.3.4/foo"); + test.assertEqual(info.userPass, "user:pass"); +}; + +exports.testToFilename = function(test) { + test.assertRaises( + function() { url.toFilename("resource://nonexistent"); }, + "resource does not exist: resource://nonexistent/", + "url.toFilename() on nonexistent resources should throw" + ); + + test.assertMatches(url.toFilename(module.uri), + /.*test-url\.js$/, + "url.toFilename() on resource: URIs should work"); + + test.assertRaises( + function() { url.toFilename("http://foo.com/"); }, + "cannot map to filename: http://foo.com/", + "url.toFilename() on http: URIs should raise error" + ); + + try { + test.assertMatches( + url.toFilename("chrome://global/content/console.xul"), + /.*console\.xul$/, + "url.toFilename() w/ console.xul works when it maps to filesystem" + ); + } catch (e) { + if (/chrome url isn\'t on filesystem/.test(e.message)) + test.pass("accessing console.xul in jar raises exception"); + else + test.fail("accessing console.xul raises " + e); + } + + // TODO: Are there any chrome URLs that we're certain exist on the + // filesystem? + // test.assertMatches(url.toFilename("chrome://myapp/content/main.js"), + // /.*main\.js$/); +}; + +exports.testFromFilename = function(test) { + var fileUrl = url.fromFilename(url.toFilename(module.uri)); + test.assertEqual(url.URL(fileUrl).scheme, 'file', + 'url.toFilename() should return a file: url'); + test.assertEqual(url.fromFilename(url.toFilename(fileUrl)), + fileUrl); +}; + +exports.testURL = function(test) { + let URL = url.URL; + test.assert(URL("h:foo") instanceof URL, "instance is of correct type"); + test.assertRaises(function() URL(), + "malformed URI: undefined", + "url.URL should throw on undefined"); + test.assertRaises(function() URL(""), + "malformed URI: ", + "url.URL should throw on empty string"); + test.assertRaises(function() URL("foo"), + "malformed URI: foo", + "url.URL should throw on invalid URI"); + test.assert(URL("h:foo").scheme, "has scheme"); + test.assertEqual(URL("h:foo").toString(), + "h:foo", + "toString should roundtrip"); + // test relative + base + test.assertEqual(URL("mypath", "http://foo").toString(), + "http://foo/mypath", + "relative URL resolved to base"); + // test relative + no base + test.assertRaises(function() URL("path").toString(), + "malformed URI: path", + "no base for relative URI should throw"); + + let a = URL("h:foo"); + let b = URL(a); + test.assertEqual(b.toString(), + "h:foo", + "a URL can be initialized from another URL"); + test.assertNotStrictEqual(a, b, + "a URL initialized from another URL is not the same object"); + test.assert(a == "h:foo", + "toString is implicit when a URL is compared to a string via =="); + test.assertStrictEqual(a + "", "h:foo", + "toString is implicit when a URL is concatenated to a string"); +}; diff --git a/tools/addon-sdk-1.4/packages/api-utils/tests/test-window-loader.js b/tools/addon-sdk-1.4/packages/api-utils/tests/test-window-loader.js new file mode 100644 index 0000000..fc6f144 --- /dev/null +++ b/tools/addon-sdk-1.4/packages/api-utils/tests/test-window-loader.js @@ -0,0 +1,152 @@ +/* ***** BEGIN LICENSE BLOCK ***** + * Version: MPL 1.1/GPL 2.0/LGPL 2.1 + * + * The contents of this file are subject to the Mozilla Public License Version + * 1.1 (the "License"); you may not use this file except in compliance with + * the License. You may obtain a copy of the License at + * http://www.mozilla.org/MPL/ + * + * Software distributed under the License is distributed on an "AS IS" basis, + * WITHOUT WARRANTY OF ANY KIND, either express or implied. See the License + * for the specific language governing rights and limitations under the + * License. + * + * The Original Code is Jetpack. + * + * The Initial Developer of the Original Code is Mozilla. + * Portions created by the Initial Developer are Copyright (C) 2010 + * the Initial Developer. All Rights Reserved. + * + * Contributor(s): + * Irakli Gozalishvili <gozala@mozilla.com> (Original author) + * + * Alternatively, the contents of this file may be used under the terms of + * either the GNU General Public License Version 2 or later (the "GPL"), or + * the GNU Lesser General Public License Version 2.1 or later (the "LGPL"), + * in which case the provisions of the GPL or the LGPL are applicable instead + * of those above. If you wish to allow use of your version of this file only + * under the terms of either the GPL or the LGPL, and not to allow others to + * use your version of this file under the terms of the MPL, indicate your + * decision by deleting the provisions above and replace them with the notice + * and other provisions required by the GPL or the LGPL. If you do not delete + * the provisions above, a recipient may use your version of this file under + * the terms of any one of the MPL, the GPL or the LGPL. + * + * ***** END LICENSE BLOCK ***** */ +"use strict"; + +const { WindowLoader } = require('windows/loader'), + { Trait } = require('traits'); + +const Loader = Trait.compose( + WindowLoader, + { + constructor: function Loader(options) { + this._onLoad = options.onLoad; + this._onUnload = options.onUnload; + if ('window' in options) + this._window = options.window; + this._load(); + this.window = this._window; + }, + window: null, + _onLoad: null, + _onUnload: null, + _tabOptions: [] + } +); + +exports['test compositions with missing required properties'] = function(test) { + test.assertRaises( + function() WindowLoader.compose({})(), + 'Missing required property: _onLoad', + 'should throw missing required property exception' + ); + test.assertRaises( + function() WindowLoader.compose({ _onLoad: null, _tabOptions: null })(), + 'Missing required property: _onUnload', + 'should throw missing required property `_onUnload`' + ); + test.assertRaises( + function() WindowLoader.compose({ _onUnload: null, _tabOptions: null })(), + 'Missing required property: _onLoad', + 'should throw missing required property `_onLoad`' + ); + test.assertRaises( + function() WindowLoader.compose({ _onUnload: null, _onLoad: null })(), + 'Missing required property: _tabOptions', + 'should throw missing required property `_tabOptions`' + ); +}; + +exports['test `load` events'] = function(test) { + test.waitUntilDone(); + let onLoadCalled = false; + Loader({ + onLoad: function(window) { + onLoadCalled = true; + test.assertEqual( + window, this._window, 'windows should match' + ); + test.assertEqual( + window.document.readyState, 'complete', 'window must be fully loaded' + ); + window.close(); + }, + onUnload: function(window) { + test.assertEqual( + window, this._window, 'windows should match' + ); + test.assertEqual( + window.document.readyState, 'complete', 'window must be fully loaded' + ); + test.assert(onLoadCalled, 'load callback is supposed to be called'); + test.done(); + } + }); +}; + +exports['test removeing listeners'] = function(test) { + test.waitUntilDone(); + Loader({ + onLoad: function(window) { + test.assertEqual( + window, this._window, 'windows should match' + ); + window.close(); + }, + onUnload: function(window) { + test.done(); + } + }); +}; + +exports['test create loader from opened window'] = function(test) { + test.waitUntilDone(); + let onUnloadCalled = false; + Loader({ + onLoad: function(window) { + test.assertEqual( + window, this._window, 'windows should match' + ); + test.assertEqual( + window.document.readyState, 'complete', 'window must be fully loaded' + ); + Loader({ + window: window, + onLoad: function(win) { + test.assertEqual(win, window, 'windows should match'); + window.close(); + }, + onUnload: function(window) { + test.assert(onUnloadCalled, 'first handler should be called already'); + test.done(); + } + }); + }, + onUnload: function(window) { + onUnloadCalled = true; + } + }); +}; + diff --git a/tools/addon-sdk-1.4/packages/api-utils/tests/test-window-observer.js b/tools/addon-sdk-1.4/packages/api-utils/tests/test-window-observer.js new file mode 100644 index 0000000..3f3bfc2 --- /dev/null +++ b/tools/addon-sdk-1.4/packages/api-utils/tests/test-window-observer.js @@ -0,0 +1,44 @@ +"use strict"; + +const { Loader } = require("./helpers"); + +exports["test unload window observer"] = function(assert, done) { + // Hacky way to be able to create unloadable modules via makeSandboxedLoader. + let loader = Loader(module); + + let utils = loader.require("api-utils/window-utils"); + let { isBrowser, activeBrowserWindow: activeWindow } = utils; + let observer = loader.require("api-utils/windows/observer").observer; + let opened = 0; + let closed = 0; + + observer.on("open", function onOpen(window) { + // Ignoring non-browser windows + if (isBrowser(window)) + opened++; + }); + observer.on("close", function onClose(window) { + // Ignore non-browser windows & already opened `activeWindow` (unload will + // emit close on it even though it is not actually closed). + if (isBrowser(window) && window !== activeWindow) + closed++; + }); + + // Open window and close it to trigger observers. + activeWindow.open().close(); + + // Unload the module so that all listeners set by observer are removed. + loader.unload(); + + // Open and close window once again. + activeWindow.open().close(); + + // Enqueuing asserts to make sure that assertion is not performed early. + require("timer").setTimeout(function () { + assert.equal(1, opened, "observer open was called before unload only"); + assert.equal(1, closed, "observer close was called before unload only"); + done(); + }, 0); +}; + +require("test").run(exports); diff --git a/tools/addon-sdk-1.4/packages/api-utils/tests/test-window-utils.js b/tools/addon-sdk-1.4/packages/api-utils/tests/test-window-utils.js new file mode 100644 index 0000000..eff9f2b --- /dev/null +++ b/tools/addon-sdk-1.4/packages/api-utils/tests/test-window-utils.js @@ -0,0 +1,271 @@ +var windowUtils = require("window-utils"); +var timer = require("timer"); +var {Cc,Ci} = require("chrome"); +var { Loader } = require("./helpers"); + +function makeEmptyWindow() { + var xulNs = "http://www.mozilla.org/keymaster/gatekeeper/there.is.only.xul"; + var blankXul = ('<?xml version="1.0"?>' + + '<?xml-stylesheet href="chrome://global/skin/" ' + + ' type="text/css"?>' + + '<window xmlns="' + xulNs + '" windowtype="test:window">' + + '</window>'); + var url = "data:application/vnd.mozilla.xul+xml," + escape(blankXul); + var features = ["chrome", "width=10", "height=10"]; + + var ww = Cc["@mozilla.org/embedcomp/window-watcher;1"] + .getService(Ci.nsIWindowWatcher); + return ww.openWindow(null, url, null, features.join(","), null); +} + +exports.testCloseOnUnload = function(test) { + var timesClosed = 0; + var fakeWindow = { + _listeners: [], + addEventListener: function(name, func, bool) { + this._listeners.push(func); + }, + removeEventListener: function(name, func, bool) { + var index = this._listeners.indexOf(func); + if (index == -1) + throw new Error("event listener not found"); + this._listeners.splice(index, 1); + }, + close: function() { + timesClosed++; + this._listeners.forEach( + function(func) { + func({target: fakeWindow.document}); + }); + }, + document: { + get defaultView() { return fakeWindow; } + } + }; + + let loader = Loader(module); + loader.require("window-utils").closeOnUnload(fakeWindow); + test.assertEqual(fakeWindow._listeners.length, 1, + "unload listener added on closeOnUnload()"); + test.assertEqual(timesClosed, 0, + "window not closed when registered."); + loader.require("unload").send(); + test.assertEqual(timesClosed, 1, + "window closed on module unload."); + test.assertEqual(fakeWindow._listeners.length, 0, + "unload event listener removed on module unload"); + + timesClosed = 0; + loader.require("window-utils").closeOnUnload(fakeWindow); + test.assertEqual(timesClosed, 0, + "window not closed when registered."); + fakeWindow.close(); + test.assertEqual(timesClosed, 1, + "window closed when close() called."); + test.assertEqual(fakeWindow._listeners.length, 0, + "unload event listener removed on window close"); + loader.require("unload").send(); + test.assertEqual(timesClosed, 1, + "window not closed again on module unload."); + loader.unload(); +}; + +exports.testWindowWatcher = function(test) { + var myWindow; + var finished = false; + + var delegate = { + onTrack: function(window) { + if (window == myWindow) { + test.pass("onTrack() called with our test window"); + timer.setTimeout(function() { myWindow.close(); }, 1); + } + }, + onUntrack: function(window) { + if (window == myWindow) { + test.pass("onUntrack() called with our test window"); + timer.setTimeout(function() { + if (!finished) { + finished = true; + myWindow = null; + wt.unload(); + test.done(); + } else + test.fail("finishTest() called multiple times."); + }, 1); + } + } + }; + + var wt = new windowUtils.WindowTracker(delegate); + myWindow = makeEmptyWindow(); + test.waitUntilDone(5000); +}; + +// test that _unregWindow calls _unregLoadingWindow +exports.testWindowWatcherUnregs4LoadingWindows = function(test) { + var myWindow; + var finished = false; + let browserWindow = Cc["@mozilla.org/appshell/window-mediator;1"] + .getService(Ci.nsIWindowMediator) + .getMostRecentWindow("navigator:browser"); + var counter = 0; + + var delegate = { + onTrack: function(window) { + var type = window.document.documentElement.getAttribute("windowtype"); + if (type == "test:window") + test.fail("onTrack shouldn't have been executed."); + } + }; + var wt = new windowUtils.WindowTracker(delegate); + + // make a new window + myWindow = makeEmptyWindow(); + + // make sure that the window hasn't loaded yet + test.assertNotEqual( + myWindow.document.readyState, + "complete", + "window hasn't loaded yet."); + + // unload WindowTracker + wt.unload(); + + // make sure that the window still hasn't loaded, which means that the onTrack + // would have been removed successfully assuming that it doesn't execute. + test.assertNotEqual( + myWindow.document.readyState, + "complete", + "window still hasn't loaded yet."); + + // wait for the window to load and then close it. onTrack wouldn't be called + // until the window loads, so we must let it load before closing it to be + // certain that onTrack was removed. + myWindow.addEventListener("load", function() { + // allow all of the load handles to execute before closing + myWindow.setTimeout(function() { + myWindow.addEventListener("unload", function() { + // once the window unloads test is done + test.done(); + }, false); + myWindow.close(); + }, 0); + }, false); + + test.waitUntilDone(5000); +} + +exports.testWindowWatcherWithoutUntracker = function(test) { + var myWindow; + var finished = false; + + var delegate = { + onTrack: function(window) { + if (window == myWindow) { + test.pass("onTrack() called with our test window"); + timer.setTimeout(function() { + myWindow.close(); + + if (!finished) { + finished = true; + myWindow = null; + wt.unload(); + test.done(); + } else { + test.fail("onTrack() called multiple times."); + } + }, 1); + } + } + }; + + var wt = new windowUtils.WindowTracker(delegate); + myWindow = makeEmptyWindow(); + test.waitUntilDone(5000); +}; + +exports.testActiveWindow = function(test) { + test.waitUntilDone(5000); + + let testRunnerWindow = Cc["@mozilla.org/appshell/window-mediator;1"] + .getService(Ci.nsIWindowMediator) + .getMostRecentWindow("test:runner"); + let browserWindow = Cc["@mozilla.org/appshell/window-mediator;1"] + .getService(Ci.nsIWindowMediator) + .getMostRecentWindow("navigator:browser"); + + test.assertEqual(windowUtils.activeBrowserWindow, browserWindow, + "Browser window is the active browser window."); + + + let testSteps = [ + function() { + windowUtils.activeWindow = browserWindow; + continueAfterFocus(browserWindow); + }, + function() { + test.assertEqual(windowUtils.activeWindow, browserWindow, + "Correct active window [1]"); + continueAfterFocus(windowUtils.activeWindow = testRunnerWindow); + }, + function() { + test.assertEqual(windowUtils.activeWindow, testRunnerWindow, + "Correct active window [2]"); + test.assertEqual(windowUtils.activeBrowserWindow, browserWindow, + "Correct active browser window [3]"); + continueAfterFocus(windowUtils.activeWindow = browserWindow); + }, + function() { + test.assertEqual(windowUtils.activeWindow, browserWindow, + "Correct active window [4]"); + continueAfterFocus(windowUtils.activeWindow = testRunnerWindow); + }, + function() { + test.assertEqual(windowUtils.activeWindow, testRunnerWindow, + "Correct active window [5]"); + test.assertEqual(windowUtils.activeBrowserWindow, browserWindow, + "Correct active browser window [6]"); + testRunnerWindow = null; + browserWindow = null; + test.done() + } + ]; + + let nextTest = function() { + let func = testSteps.shift(); + if (func) { + func(); + } + } + + function continueAfterFocus(targetWindow) { + + // Based on SimpleTest.waitForFocus + var fm = Cc["@mozilla.org/focus-manager;1"]. + getService(Ci.nsIFocusManager); + + var childTargetWindow = {}; + fm.getFocusedElementForWindow(targetWindow, true, childTargetWindow); + childTargetWindow = childTargetWindow.value; + + var focusedChildWindow = {}; + if (fm.activeWindow) { + fm.getFocusedElementForWindow(fm.activeWindow, true, focusedChildWindow); + focusedChildWindow = focusedChildWindow.value; + } + + var focused = (focusedChildWindow == childTargetWindow); + if (focused) { + nextTest(); + } else { + childTargetWindow.addEventListener("focus", function focusListener() { + childTargetWindow.removeEventListener("focus", focusListener, true); + nextTest(); + }, true); + } + + } + + nextTest(); +} diff --git a/tools/addon-sdk-1.4/packages/api-utils/tests/test-xhr.js b/tools/addon-sdk-1.4/packages/api-utils/tests/test-xhr.js new file mode 100644 index 0000000..2912139 --- /dev/null +++ b/tools/addon-sdk-1.4/packages/api-utils/tests/test-xhr.js @@ -0,0 +1,70 @@ +var xhr = require("xhr"); +var timer = require("timer"); +var { Loader } = require("./helpers"); + +/* Test is intentionally disabled until platform bug 707256 is fixed. +exports.testAbortedXhr = function(test) { + var req = new xhr.XMLHttpRequest(); + test.assertEqual(xhr.getRequestCount(), 1); + req.abort(); + test.assertEqual(xhr.getRequestCount(), 0); +}; +*/ + +exports.testLocalXhr = function(test) { + var req = new xhr.XMLHttpRequest(); + req.overrideMimeType("text/plain"); + req.open("GET", module.uri); + req.onreadystatechange = function() { + if (req.readyState == 4 && req.status == 0) { + test.assertMatches(req.responseText, + /onreadystatechange/, + "XMLHttpRequest should get local files"); + timer.setTimeout( + function() { test.assertEqual(xhr.getRequestCount(), 0); + test.done(); }, + 0 + ); + } + }; + req.send(null); + test.assertEqual(xhr.getRequestCount(), 1); + test.waitUntilDone(4000); +}; + +exports.testUnload = function(test) { + var loader = Loader(module); + var sbxhr = loader.require("xhr"); + var req = new sbxhr.XMLHttpRequest(); + req.overrideMimeType("text/plain"); + req.open("GET", module.uri); + req.send(null); + test.assertEqual(sbxhr.getRequestCount(), 1); + loader.unload(); + test.assertEqual(sbxhr.getRequestCount(), 0); +}; + +exports.testDelegatedReturns = function(test) { + var req = new xhr.XMLHttpRequest(); + req.overrideMimeType("text/plain"); + req.open("GET", module.uri); + req.onreadystatechange = function() { + if (req.readyState == 4 && req.status == 0) { + // This response isn't going to have any headers, so the return value + // should be null. Previously it wasn't returning anything, and thus was + // undefined. + + // Depending on whether Bug 608939 has been applied + // to the platform, getAllResponseHeaders() may return + // null or the empty string; accept either. + var headers = req.getAllResponseHeaders(); + test.assert(headers === null || headers === "", + "XHR's delegated methods should return"); + test.done(); + } + }; + req.send(null); + test.assertEqual(xhr.getRequestCount(), 1); + test.waitUntilDone(4000); +} + diff --git a/tools/addon-sdk-1.4/packages/api-utils/tests/test-xpcom.js b/tools/addon-sdk-1.4/packages/api-utils/tests/test-xpcom.js new file mode 100644 index 0000000..fdcb6e8 --- /dev/null +++ b/tools/addon-sdk-1.4/packages/api-utils/tests/test-xpcom.js @@ -0,0 +1,107 @@ +var traceback = require("traceback"); +var xpcom = require("xpcom"); +var {Cc,Ci,Cm,Cr} = require("chrome"); +var { Loader } = require("./helpers"); + +exports.testRegister = function(test, text) { + if (!text) + text = "hai2u"; + + function Component() {} + + Component.prototype = { + newChannel : function(aURI) { + var ios = Cc["@mozilla.org/network/io-service;1"]. + getService(Ci.nsIIOService); + + var channel = ios.newChannel( + "data:text/plain," + text, + null, + null + ); + + channel.originalURI = aURI; + return channel; + }, + getURIFlags: function(aURI) { + return Ci.nsIAboutModule.ALLOW_SCRIPT; + }, + QueryInterface: xpcom.utils.generateQI([Ci.nsIAboutModule]) + }; + + var contractID = "@mozilla.org/network/protocol/about;1?what=boop"; + + var factory = xpcom.register({name: "test about:boop page", + contractID: contractID, + create: Component}); + + var manager = Cm.QueryInterface(Ci.nsIComponentRegistrar); + test.assertEqual(manager.isCIDRegistered(factory.uuid), true); + + // We don't want to use Cc[contractID] here because it's immutable, + // so it can't accept updated versions of a contractID during the + // same application session. + var aboutFactory = xpcom.getClass(contractID, Ci.nsIFactory); + + test.assertNotEqual(aboutFactory.wrappedJSObject, + undefined, + "Factory wrappedJSObject should exist."); + + var about = aboutFactory.createInstance(null, Ci.nsIAboutModule); + var ios = Cc["@mozilla.org/network/io-service;1"]. + getService(Ci.nsIIOService); + test.assertEqual( + about.getURIFlags(ios.newURI("http://foo.com", null, null)), + Ci.nsIAboutModule.ALLOW_SCRIPT + ); + + var aboutURI = ios.newURI("about:boop", null, null); + var channel = ios.newChannelFromURI(aboutURI); + var iStream = channel.open(); + var siStream = Cc['@mozilla.org/scriptableinputstream;1'] + .createInstance(Ci.nsIScriptableInputStream); + siStream.init(iStream); + var data = new String(); + data += siStream.read(-1); + siStream.close(); + iStream.close(); + test.assertEqual(data, text); + + factory.unregister(); + test.assertEqual(manager.isCIDRegistered(factory.uuid), false); +}; + +exports.testReRegister = function(test) { + exports.testRegister(test, "hai2u again"); +}; + +exports.testMakeUuid = function(test) { + var first = xpcom.makeUuid().toString(); + var second = xpcom.makeUuid().toString(); + test.assertMatches(first, /{[0-9a-f\-]+}/); + test.assertMatches(second, /{[0-9a-f\-]+}/); + test.assertNotEqual(first, second); +}; + +exports.testUnload = function(test) { + var loader = Loader(module); + var sbxpcom = loader.require("xpcom"); + + function Component() {} + + Component.prototype = { + QueryInterface: sbxpcom.utils.generateQI([Ci.nsISupports]) + }; + + var contractID = "@mozilla.org/blargle;1"; + var factory = sbxpcom.register({name: "test component", + contractID: contractID, + create: Component}); + + var manager = Cm.QueryInterface(Ci.nsIComponentRegistrar); + test.assertEqual(manager.isCIDRegistered(factory.uuid), true); + + loader.unload(); + + test.assertEqual(manager.isCIDRegistered(factory.uuid), false); +}; diff --git a/tools/addon-sdk-1.4/packages/api-utils/tests/test-xul-app.js b/tools/addon-sdk-1.4/packages/api-utils/tests/test-xul-app.js new file mode 100644 index 0000000..cda4a2e --- /dev/null +++ b/tools/addon-sdk-1.4/packages/api-utils/tests/test-xul-app.js @@ -0,0 +1,41 @@ +var xulApp = require("xul-app"); + +exports.testXulApp = function(test) { + test.assertEqual(typeof(xulApp.ID), "string", + "ID is a string"); + test.assertEqual(typeof(xulApp.name), "string", + "name is a string"); + test.assertEqual(typeof(xulApp.version), "string", + "version is a string"); + test.assertEqual(typeof(xulApp.platformVersion), "string", + "platformVersion is a string"); + + test.assertRaises(function() { xulApp.is("blargy"); }, + "Unkown Mozilla Application: blargy", + "is() throws error on bad app name"); + test.assertRaises(function() { xulApp.isOneOf(["blargy"]); }, + "Unkown Mozilla Application: blargy", + "isOneOf() throws error on bad app name"); + + function testSupport(name) { + var item = xulApp.is(name); + test.assert(item === true || item === false, + "is('" + name + "') is true or false."); + } + + var apps = ["Firefox", "Mozilla", "Sunbird", "SeaMonkey", + "Fennec", "Thunderbird"]; + + apps.forEach(function(name) { testSupport(name); }); + + test.assert(xulApp.isOneOf(apps) == true || + xulApp.isOneOf(apps) == false, + "isOneOf() returns true or false."); + + test.assertEqual(xulApp.versionInRange(xulApp.platformVersion, "1.9", "*"), + true, "platformVersion in range [1.9, *)"); + test.assertEqual(xulApp.versionInRange("3.6.4", "3.6.4", "3.6.*"), + true, "3.6.4 in [3.6.4, 3.6.*)"); + test.assertEqual(xulApp.versionInRange("1.9.3", "1.9.2", "1.9.3"), + false, "1.9.3 not in [1.9.2, 1.9.3)"); +}; diff --git a/tools/addon-sdk-1.4/packages/api-utils/tests/traits/assert.js b/tools/addon-sdk-1.4/packages/api-utils/tests/traits/assert.js new file mode 100644 index 0000000..dd662a4 --- /dev/null +++ b/tools/addon-sdk-1.4/packages/api-utils/tests/traits/assert.js @@ -0,0 +1,94 @@ +"use strict"; + +var BaseAssert = require("test/assert").Assert; + +/** + * Whether or not given property descriptors are equivalent. They are + * equivalent either if both are marked as "conflict" or "required" property + * or if all the properties of descriptors are equal. + * @param {Object} actual + * @param {Object} expected + */ +function equivalentDescriptors(actual, expected) { + return (actual.conflict && expected.conflict) || + (actual.required && expected.required) || + equalDescriptors(actual, expected); +} + +function equalDescriptors(actual, expected) { + return actual.get === expected.get && + actual.set === expected.set && + actual.value === expected.value && + !!actual.enumerable === !!expected.enumerable && + !!actual.configurable === !!expected.configurable && + !!actual.writable === !!expected.writable; +} + +/** + * Whether or not given `target` array contains all the element + * from a given `source` array. + */ +function containsSet(source, target) { + return source.some(function(element) { + return 0 > target.indexOf(element); + }); +} + +/** + * Whether or not given two arrays contain all elements from another. + */ +function equivalentSets(source, target) { + return containsSet(source, target) && containsSet(target, source); +} + +/** + * Finds name of the property from `source` property descriptor map, that + * is not equivalent of the name named property in the `target` property + * descriptor map. If not found `null` is returned instead. + */ +function findNonEquivalentPropertyName(source, target) { + var value = null; + Object.getOwnPropertyNames(source).some(function(key) { + var areEquivalent = false; + if (!equivalentDescriptors(source[key], target[key])) { + value = key; + areEquivalent = true; + } + return areEquivalent; + }); + return value; +} + +var AssertDescriptor = { + equalTraits: { + value: function equivalentTraits(actual, expected, message) { + var difference; + var actualKeys = Object.getOwnPropertyNames(actual); + var expectedKeys = Object.getOwnPropertyNames(expected); + + if (equivalentSets(actualKeys, expectedKeys)) { + this.fail({ + operator: "equalTraits", + message: "Traits define different properties", + actual: actualKeys.sort().join(","), + expected: expectedKeys.sort().join(","), + }); + } + else if ((difference = findNonEquivalentPropertyName(actual, expected))) { + this.fail({ + operator: "equalTraits", + message: "Traits define non-equivalent property `" + difference + "`", + actual: actual[difference], + expected: expected[difference] + }); + } + else { + this.pass(message || "Traits are equivalent."); + } + } + } +}; + +exports.Assert = function Assert() { + return Object.create(BaseAssert.apply(null, arguments), AssertDescriptor); +}; diff --git a/tools/addon-sdk-1.4/packages/api-utils/tests/traits/descriptor-tests.js b/tools/addon-sdk-1.4/packages/api-utils/tests/traits/descriptor-tests.js new file mode 100644 index 0000000..7c27ac4 --- /dev/null +++ b/tools/addon-sdk-1.4/packages/api-utils/tests/traits/descriptor-tests.js @@ -0,0 +1,331 @@ +"use strict"; + +var Trait = require("light-traits").Trait; +var utils = require("./utils"); +var Data = utils.Data; +var Method = utils.Method; +var Accessor = utils.Accessor; +var Required = utils.Required; +var Conflict = utils.Conflict; + +function method() {} + +exports.Assert = require("./assert").Assert +exports["test simple composition"] = function(assert) { + var actual = Trait.compose( + Trait({ a: 0, b: 1 }), + { c: { value: 2 }, d: { value: method, enumerable: true } } + ); + + var expected = { + a: Data(0), + b: Data(1), + c: Data(2, false, false, false), + d: Method(method, true, false, false) + }; + + assert.equalTraits(actual, expected); +}; + +exports["test composition with conflict"] = function(assert) { + var actual = Trait.compose( + Trait({ a: 0, b: 1 }), + { + a: { + value: 2, + writable: true, + configurable: true, + enumerable: true + }, + c: { + value: method, + configurable: true + } + } + ); + + var expected = { + a: Conflict("a"), + b: Data(1), + c: Method(method, false, true, false) + }; + + assert.equalTraits(actual, expected); +}; + +exports["test identical props does not cause conflict"] = function(assert) { + var actual = Trait.compose( + { + a: { + value: 0, + writable: true, + configurable: true, + enumerable: true + }, + b: { + value: 1 + } + }, + Trait({ + a: 0, + c: method + }) + ); + + var expected = { + a: Data(0), + b: Data(1, false, false, false), + c: Method(method) + } + + assert.equalTraits(actual, expected); +}; + +exports["test composition with identical required props"] = function(assert) { + var actual = Trait.compose( + Trait({ a: Trait.required, b: 1 }), + { a: { required: true }, c: { value: method } } + ); + + var expected = { + a: Required(), + b: Data(1), + c: Method(method, false, false, false) + }; + + assert.equalTraits(actual, expected); +}; + +exports["test composition satisfying a required prop"] = function(assert) { + var actual = Trait.compose( + Trait({ a: Trait.required, b: 1 }), + { a: { value: method, enumerable: true } } + ); + + var expected = { + a: Method(method, true, false, false), + b: Data(1) + }; + + assert.equalTraits(actual, expected); +}; + +exports["test compose is neutral wrt conflicts"] = function(assert) { + var actual = Trait.compose( + Trait({ a: { value: 1 } }, Trait({ a: 2 })), + { b: { value: 0, writable: true, configurable: true, enumerable: false } } + ); + + var expected = { a: Conflict("a"), b: Data(0, false) }; + + assert.equalTraits(actual, expected); +}; + +exports["test conflicting prop overrides Trait.required"] = function(assert) { + var actual = Trait.compose( + Trait.compose( + Trait({ a: 1 }), + { a: { value: 2 } } + ), + { a: { value: Trait.required } } + ); + + var expected = { a: Conflict("a") }; + + assert.equalTraits(actual, expected); +}; + +exports["test compose is commutative"] = function(assert) { + var actual = Trait.compose( + Trait({ a: 0, b: 1 }), + { c: { value: 2 }, d: { value: method } } + ); + + var expected = Trait.compose( + { c: { value: 2 }, d: { value: method } }, + Trait({ a: 0, b: 1 }) + ); + + assert.equalTraits(actual, expected); +} + +exports["test compose is commutative, also for required/conflicting props"] = function(assert) { + var actual = Trait.compose( + { + a: { value: 0 }, + b: { value: 1 }, + c: { value: 3 }, + e: { value: Trait.required } + }, + { + c: { value: 2 }, + d: { get: method } + } + ); + + var expected = Trait.compose( + Trait({ c: 3 }), + { + c: { value: 2 }, + d: { get: method }, + a: { value: 0 }, + b: { value: 1 }, + e: { value: Trait.required }, + } + ); + + assert.equalTraits(actual, expected); +}; + +exports["test compose is associative"] = function(assert) { + var actual = Trait.compose( + { + a: { value: 0 }, + b: { value: 1 }, + c: { value: 3 }, + d: { value: Trait.required } + }, + Trait.compose( + { c: { value: 3 }, d: { value: Trait.required } }, + { c: { value: 2 }, d: { value: method }, e: { value: "foo" } } + ) + ); + + var expected = Trait.compose( + Trait.compose( + { + a: { value: 0 }, + b: { value: 1 }, + c: { value: 3 }, + d: { value: Trait.required } + }, + { + c: { value: 3 }, + d: { value: Trait.required } + } + ), + { + c: { value: 2 }, + d: { value: method }, + e: { value: "foo" } + } + ); + + assert.equalTraits(actual, expected); +}; + +exports["test diamond import of same prop do not conflict"] = function(assert) { + var actual = Trait.compose( + Trait.compose( + { b: { value: 2 } }, + { a: { value: 1, enumerable: true, configurable: true, writable: true } } + ), + Trait.compose( + { c: { value: 3 } }, + Trait({ a: 1 }) + ), + Trait({ d: 4 }) + ); + + var expected = { + a: Data(1), + b: Data(2, false, false, false), + c: Data(3, false, false, false), + d: Data(4) + }; + + assert.equalTraits(actual, expected); +}; + +exports["test create simple"] = function(assert) { + var o1 = Trait.compose( + Trait({ a: 1 }), + { + b: { + value: function() { + return this.a; + } + } + } + ).create(Object.prototype); + + assert.equal(Object.getPrototypeOf(o1), Object.prototype, "o1 prototype"); + assert.equal(1, o1.a, "o1.a"); + assert.equal(1, o1.b(), "o1.b()"); + assert.equal(Object.keys(o1).length, 1, "Object.keys(o1).length === 2"); +}; + +exports["test create with Array.prototype"] = function(assert) { + var o2 = Trait.compose({}, {}).create(Array.prototype); + assert.equal(Object.getPrototypeOf(o2), Array.prototype, "o2 prototype"); +}; + +exports["test exception for incomplete required properties"] = function(assert) { + assert.throws(function() { + Trait({ foo: Trait.required }).create(Object.prototype) + }, /Missing required property: `foo`/, "required prop error"); +} + +exports["test exception for unresolved conflicts"] = function(assert) { + assert.throws(function() { + Trait(Trait({ a: 0 }), Trait({ a: 1 })).create({}) + }, /Remaining conflicting property: `a`/, "conflicting prop error"); +} + +exports["test conflicting properties are present"] = function(assert) { + var o5 = Object.create(Object.prototype, Trait.compose( + { a: { value: 0 } }, + { a: { value: 1 } } + )); + + assert.ok("a" in o5, "conflicting property present"); + assert.throws(function() { + o5.a + }, /Remaining conflicting property: `a`/, "conflicting prop access error"); +}; + +exports["test diamond with conflicts"] = function(assert) { + function makeT1(x) { + return { + m: { + value: function() { + return x + } + } + }; + }; + + function makeT2(x) { + return Trait.compose( + Trait({ t2: "foo" }), + makeT1(x) + ); + }; + + function makeT3(x) { + return Trait.compose( + { + t3: { value: "bar" } + }, + makeT1(x) + ); + }; + + var T4 = Trait.compose(makeT2(5), makeT3(5)); + + assert.throws(function() { + T4.create(Object.prototype); + }, /Remaining conflicting property: `m`/, "diamond prop conflict"); +}; + +exports["test providing requirements through proto"] = function(assert) { + var t = Trait.compose( + {}, + { required: { required: true } } + ).create({ required: "test" }); + + assert.equal(t.required, "test", "property from proto is inherited"); +}; + +if (module == require.main) + require("test").run(exports); diff --git a/tools/addon-sdk-1.4/packages/api-utils/tests/traits/inheritance-tests.js b/tools/addon-sdk-1.4/packages/api-utils/tests/traits/inheritance-tests.js new file mode 100644 index 0000000..73a23b7 --- /dev/null +++ b/tools/addon-sdk-1.4/packages/api-utils/tests/traits/inheritance-tests.js @@ -0,0 +1,100 @@ +"use strict"; + +var Trait = require("light-traits").Trait; + +exports["test custom constructor and inherited toString"] = function(assert) { + function Type() { + return Object.create(Type.prototype); + } + Type.prototype = Trait({ + method: function method() { + return 2; + } + }).create(Object.freeze(Type.prototype)); + + var fixture = Type(); + + assert.equal(fixture.constructor, Type, "must override constructor"); + assert.equal(fixture.toString(), "[object Type]", "must inherit toString"); +}; + +exports["test custom toString and inherited constructor"] = function(assert) { + function Type() { + return Object.create(Type.prototype); + } + Type.prototype = Trait({ + toString: function toString() { + return "<toString>"; + } + }).create(); + + var fixture = Type(); + + assert.equal(fixture.constructor, Trait, "must inherit constructor Trait"); + assert.equal(fixture.toString(), "<toString>", "Must override toString"); +}; + +exports["test custom toString and constructor"] = function(assert) { + function Type() { + return TypeTrait.create(Type.prototype); + } + Object.freeze(Type.prototype); + var TypeTrait = Trait({ + toString: function toString() { + return "<toString>"; + } + }); + + var fixture = Type(); + + assert.equal(fixture.constructor, Type, "constructor is provided to create"); + assert.equal(fixture.toString(), "<toString>", "toString was overridden"); +}; + +exports["test resolve constructor"] = function (assert) { + function Type() {} + var T1 = Trait({ constructor: Type }).resolve({ constructor: '_foo' }); + var f1 = T1.create(); + + assert.equal(f1._foo, Type, "constructor was resolved"); + assert.equal(f1.constructor, Trait, "constructor of prototype is inherited"); + assert.equal(f1.toString(), "[object Trait]", "toString is inherited"); +}; + +exports["test compose read-only"] = function (assert) { + function Type() {} + Type.prototype = Trait.compose(Trait({}), { + constructor: { value: Type }, + a: { value: "b", enumerable: true } + }).resolve({ a: "b" }).create({ a: "a" }); + + var f1 = new Type(); + + assert.equal(Object.getPrototypeOf(f1), Type.prototype, "inherits correctly"); + assert.equal(f1.constructor, Type, "constructor was overridden"); + assert.equal(f1.toString(), "[object Type]", "toString was inherited"); + assert.equal(f1.a, "a", "property a was resolved"); + assert.equal(f1.b, "b", "property a was renamed to b"); + assert.ok(!Object.getOwnPropertyDescriptor(Type.prototype, "a"), + "a is not on the prototype of the instance"); + + var proto = Object.getPrototypeOf(Type.prototype); + var dc = Object.getOwnPropertyDescriptor(Type.prototype, "constructor"); + var db = Object.getOwnPropertyDescriptor(Type.prototype, "b"); + var da = Object.getOwnPropertyDescriptor(proto, "a"); + + assert.ok(!dc.writable, "constructor is not writable"); + assert.ok(!dc.enumerable, "constructor is not enumerable"); + assert.ok(dc.configurable, "constructor inherits configurability"); + + assert.ok(!db.writable, "a -> b is not writable"); + assert.ok(db.enumerable, "a -> b is enumerable"); + assert.ok(!db.configurable, "a -> b is not configurable"); + + assert.ok(da.writable, "a is writable"); + assert.ok(da.enumerable, "a is enumerable"); + assert.ok(da.configurable, "a is configurable"); +}; + +if (require.main == module) + require("test").run(exports); diff --git a/tools/addon-sdk-1.4/packages/api-utils/tests/traits/object-tests.js b/tools/addon-sdk-1.4/packages/api-utils/tests/traits/object-tests.js new file mode 100644 index 0000000..afea3ce --- /dev/null +++ b/tools/addon-sdk-1.4/packages/api-utils/tests/traits/object-tests.js @@ -0,0 +1,317 @@ +"use strict"; + +var Trait = require("light-traits").Trait; +var utils = require("./utils"); +var Data = utils.Data; +var Method = utils.Method; +var Accessor = utils.Accessor; +var Required = utils.Required; +var Conflict = utils.Conflict; + +function method() {} + +exports.Assert = require("./assert").Assert; + +exports["test empty trait"] = function (assert) { + assert.equalTraits(Trait({}), {}); +}; + +exports["test simple trait"] = function (assert) { + var expected = { + a: Data(0, true, true, true), + b: Method(method, true, true, true) + }; + + assert.equalTraits(Trait({ a: 0, b: method }), expected); +}; + +exports["test simple trait with Trait.required property"] = function (assert) { + var actual = Trait({ a: Trait.required, b: 1 }); + var expected = { a: Required("a"), b: Data(1) }; + + assert.equalTraits(actual, expected); +}; + +exports["test ordering of trait properties is irrelevant"] = function (assert) { + var actual = Trait({ a: 0, b: 1, c: Trait.required }); + var expected = Trait({ b: 1, c: Trait.required, a: 0 }); + + assert.equalTraits(actual, expected); +}; + +exports["test trait with accessor property"] = function (assert) { + var record = { get a() {}, set a(v) {} }; + var get = Object.getOwnPropertyDescriptor(record, "a").get; + var set = Object.getOwnPropertyDescriptor(record, "a").set; + + assert.equalTraits(Trait(record), { a: Accessor(get, set) }); +}; + +exports["test simple composition"] = function (assert) { + var actual = Trait.compose(Trait({ a: 0, b: 1 }), Trait({ c: 2, d: method })); + var expected = { a: Data(0), b: Data(1), c: Data(2), d: Method(method) }; + + assert.equalTraits(actual, expected); +}; + +exports["test composition with conflict"] = function (assert) { + var actual = Trait.compose(Trait({ a: 0, b: 1 }), Trait({ a: 2, c: method })); + var expected = { a: Conflict("a"), b: Data(1), c: Method(method) }; + + assert.equalTraits(actual, expected); +}; + +exports["test composition of identical props does not cause conflict"] = function (assert) { + var actual = Trait.compose(Trait({ a: 0, b: 1 }), Trait({ a: 0, c: method })); + + assert.equalTraits(actual, { a: Data(0), b: Data(1), c: Method(method) }); +}; + +exports["test composition with identical Trait.required props"] = function (assert) { + var actual = Trait.compose(Trait({ a: Trait.required, b: 1 }), + Trait({ a: Trait.required, c: method })); + + assert.equalTraits(actual, { a: Required(), b: Data(1), c: Method(method) }); +}; + +exports["test composition satisfying a Trait.required prop"] = function (assert) { + var actual = Trait.compose(Trait({ a: Trait.required, b: 1 }), + Trait({ a: method })); + + assert.equalTraits(actual, { a: Method(method), b: Data(1) }); +}; + +exports["test compose is neutral wrt conflicts"] = function (assert) { + var actual = Trait.compose(Trait.compose(Trait({ a: 1 }), Trait({ a: 2 })), + Trait({ b: 0 })); + + assert.equalTraits(actual, { a: Conflict("a"), b: Data(0) }); +}; + +exports["test conflicting prop overrides Trait.required prop"] = function (assert) { + var actual = Trait.compose(Trait.compose(Trait({ a: 1 }), + Trait({ a: 2 })), + Trait({ a: Trait.required })); + + assert.equalTraits(actual, { a: Conflict("a") }); +}; + +exports["test compose is commutative"] = function (assert) { + var actual = Trait.compose(Trait({ a: 0, b: 1 }), Trait({ c: 2, d: method })); + var expected = Trait.compose(Trait({ c: 2, d: method }), + Trait({ a: 0, b: 1 })); + + assert.equalTraits(actual, expected); +}; + +exports["test compose is commutative, also for Trait.required/conflicting props"] = function (assert) { + var actual = Trait.compose(Trait({ a: 0, b: 1, c: 3, e: Trait.required }), + Trait({ c: 2, d: method })); + + var expected = Trait.compose(Trait({ c: 2, d: method }), + Trait({ a: 0, b: 1, c: 3, e: Trait.required })); + + assert.equalTraits(actual, expected); +}; + +exports["test compose is associative"] = function (assert) { + var actual = Trait.compose(Trait({ a: 0, b: 1, c: 3, d: Trait.required }), + Trait.compose(Trait({ c: 3, d: Trait.required }), + Trait({ c: 2, d: method, + e: "foo" }))); + + var expected = Trait.compose( + Trait.compose(Trait({ a: 0, b: 1, c: 3, d: Trait.required }), + Trait({ c: 3, d: Trait.required })), + Trait({ c: 2, d: method, e: "foo" })); + + assert.equalTraits(actual, expected); +}; + +exports["test diamond import of same prop does not generate conflict"] = function (assert) { + var actual = Trait.compose(Trait.compose(Trait({ b: 2 }), Trait({ a: 1 })), + Trait.compose(Trait({ c: 3 }), Trait({ a: 1 })), + Trait({ d: 4 })); + var expected = { a: Data(1), b: Data(2), c: Data(3), d: Data(4) }; + + assert.equalTraits(actual, expected); +}; + +exports["test resolve with empty resolutions has no effect"] = function (assert) { + assert.equalTraits(Trait({ a: 1, b: Trait.required, c: method }).resolve({}), + { a: Data(1), b: Required(), c: Method(method) }); +}; + +exports["test resolve: renaming"] = function (assert) { + var actual = Trait({ a: 1, b: Trait.required, c: method }); + + assert.equalTraits(actual.resolve({ a: "A", c: "C" }), + { A: Data(1), b: Required(), C: Method(method), + a: Required(), c: Required() }); +}; + +exports["test resolve: renaming to conflicting name causes conflict, order 1"] = function (assert) { + assert.equalTraits(Trait({ a: 1, b: 2 }).resolve({ a: "b" }), + { b: Conflict("b"), a: Required() }); +}; + +exports["test resolve: renaming to conflicting name causes conflict, order 2"] = function (assert) { + assert.equalTraits(Trait({ b: 2, a: 1 }).resolve({ a: "b" }), + { b: Conflict("b"), a: Required() }); +}; + +exports["test resolve: simple exclusion"] = function (assert) { + assert.equalTraits(Trait({ a: 1, b: 2 }).resolve({ a: undefined }), + { a: Required(), b: Data(2) }); +}; + +exports["test resolve: exclusion to empty trait"] = function (assert) { + assert.equalTraits(Trait({ a: 1, b: 2 }).resolve({ a: null, b: undefined }), + { a: Required(), b: Required() }); +}; + +exports["test resolve: exclusion and renaming of disjoint props"] = function (assert) { + assert.equalTraits(Trait({ a: 1, b: 2 }).resolve({ a: undefined, b: "c" }), + { a: Required(), c: Data(2), b: Required() }); +}; + +exports["test resolve: exclusion and renaming of overlapping props"] = function (assert) { + assert.equalTraits(Trait({ a: 1, b: 2 }).resolve({ a: undefined, b: "a" }), + { a: Data(2), b: Required() }); +}; + +exports["test resolve: renaming to a common alias causes conflict"] = function (assert) { + assert.equalTraits(Trait({ a: 1, b: 2 }).resolve({ a: "c", b: "c" }), + { c: Conflict("c"), a: Required(), b: Required() }); +}; + +exports["test resolve: renaming overrides Trait.required target"] = function (assert) { + assert.equalTraits(Trait({ a: Trait.required, b: 2 }).resolve({ b: "a" }), + { a: Data(2), b: Required() }); +}; + +exports["test resolve: renaming Trait.required properties has no effect"] = function (assert) { + assert.equalTraits(Trait({ a: 2, b: Trait.required }).resolve({ b: "a" }), + { a: Data(2), b: Required() }); +}; + +exports["test resolve: renaming of non-existent props has no effect"] = function (assert) { + assert.equalTraits(Trait({ a: 1, b: 2 }).resolve({ a: "c", d: "c" }), + { c: Data(1), b: Data(2), a: Required() }); +}; + +exports["test resolve: exclusion of non-existent props has no effect"] = function (assert) { + assert.equalTraits(Trait({ a: 1 }).resolve({ b: undefined }), { a: Data(1) }); +}; + +exports["test resolve is neutral w.r.t. Trait.required properties"] = function (assert) { + var actual = Trait({ a: Trait.required, b: Trait.required, c: "foo", d: 1 }); + var expected = { a: Required(), b: Required(), c: Data("foo"), d: Data(1) }; + assert.equalTraits(actual.resolve({ a: "c", b: undefined }), expected); +}; + +exports["test resolve supports swapping of property names, ordering 1"] = function (assert) { + assert.equalTraits(Trait({ a: 1, b: 2 }).resolve({ a: "b", b: "a" }), + { a: Data(2), b: Data(1) }); +}; + +exports["test resolve supports swapping of property names, ordering 2"] = function (assert) { + assert.equalTraits(Trait({ a: 1, b: 2 }).resolve({ b: "a", a: "b" }), + { a: Data(2), b: Data(1) }); +}; + +exports["test resolve supports swapping of property names, ordering 3"] = function (assert) { + assert.equalTraits(Trait({ b: 2, a: 1 }).resolve({ b: "a", a: "b" }), + { a: Data(2), b: Data(1) }); +}; + +exports["test resolve supports swapping of property names, ordering 4"] = function (assert) { + assert.equalTraits(Trait({ b: 2, a: 1 }).resolve({ a: "b", b: "a" }), + { a: Data(2), b: Data(1) }); +}; + +exports["test create simple"] = function (assert) { + var o1 = Trait({ + a: 1, + b: function () { + return this.a; + } + }).create(Object.prototype); + + assert.equal(Object.getPrototypeOf(o1), Object.prototype, "o1 prototype"); + assert.equal(1, o1.a, "o1.a"); + assert.equal(1, o1.b(), "o1.b()"); + assert.equal(Object.keys(o1).length, 2, "Object.keys(o1).length === 2"); +}; + +exports["test create with Array.prototype"] = function (assert) { + var o2 = Trait({}).create(Array.prototype); + assert.equal(Object.getPrototypeOf(o2), Array.prototype, "o2 prototype"); +}; + +exports["test exception for incomplete required properties"] = function (assert) { + assert.throws(function () { + Trait({ foo: Trait.required }).create(Object.prototype); + }, /Missing required property: `foo`/, "required prop error"); +}; + +exports["test exception for unresolved conflicts"] = function (assert) { + assert.throws(function () { + Trait.compose(Trait({ a: 0 }), Trait({ a: 1 })).create({}); + }, /Remaining conflicting property: `a`/, "conflicting prop error"); +}; + +exports["test verify that required properties are present but undefined"] = function (assert) { + var o4 = Object.create(Object.prototype, Trait({ foo: Trait.required })); + + assert.ok("foo" in o4, "required property present"); + assert.throws(function () { + o4.foo; + }, /Missing required property: `foo`/, "required prop error"); +}; + +exports["test verify that conflicting properties are present"] = function (assert) { + var o5 = Object.create(Object.prototype, Trait.compose(Trait({ a: 0 }), + Trait({ a: 1 }))); + + assert.ok("a" in o5, "conflicting property present"); + assert.throws(function () { + o5.a; + }, /Remaining conflicting property: `a`/, "conflicting prop access error"); +}; + +exports["test diamond with conflicts"] = function (assert) { + function makeT1(x) { + return Trait({ + m: function () { + return x + } + }) + }; + + function makeT2(x) { + return Trait.compose(Trait({ + t2: "foo" + }), makeT1(x)); + }; + + function makeT3(x) { + return Trait.compose(Trait({ + t3: "bar" + }), makeT1(x)); + }; + + var T4 = Trait.compose(makeT2(5), makeT3(5)); + + assert.throws(function () { + T4.create(Object.prototype); + }, /Remaining conflicting property: `m`/, "diamond prop conflict"); +}; + +exports["test providing requirements through proto"] = function (assert) { + var t = Trait({ required: Trait.required }).create({ required: "test" }); + assert.equal(t.required, "test", "property from proto is inherited"); +}; + +if (module == require.main) + require("test").run(exports); diff --git a/tools/addon-sdk-1.4/packages/api-utils/tests/traits/utils.js b/tools/addon-sdk-1.4/packages/api-utils/tests/traits/utils.js new file mode 100644 index 0000000..5647fb9 --- /dev/null +++ b/tools/addon-sdk-1.4/packages/api-utils/tests/traits/utils.js @@ -0,0 +1,52 @@ +"use strict"; + +var ERR_CONFLICT = "Remaining conflicting property: "; +var ERR_REQUIRED = "Missing required property: "; + +exports.Data = function Data(value, enumerable, configurable, writable) { + return ({ + value: value, + enumerable: enumerable !== false, + configurable: configurable !== false, + writable: writable !== false + }); +}; + +exports.Method = function Method(method, enumerable, configurable, writable) { + return ({ + value: method, + enumerable: enumerable !== false, + configurable: configurable !== false, + writable: writable !== false + }); +}; + +exports.Accessor = function Accessor(get, set, enumerable, configurable) { + return ({ + get: get, + set: set, + enumerable: enumerable !== false, + configurable: configurable !== false + }); +}; + +exports.Required = function Required(name) { + function required() { throw new Error(ERR_REQUIRED + name) } + + return ({ + get: required, + set: required, + required: true + }); +}; + +exports.Conflict = function Conflict(name) { + function conflict() { throw new Error(ERR_CONFLICT + name) } + + return ({ + get: conflict, + set: conflict, + conflict: true + }); +}; + |