import FIREFOX_52_6_0esr_RELEASE from mozilla-esr52 hg repo

This commit is contained in:
Roy Tam 2018-01-19 03:59:58 +08:00
commit dcd9973243
150858 changed files with 23884658 additions and 0 deletions

View file

@ -0,0 +1,7 @@
"use strict";
module.exports = {
"extends": [
"../../../../testing/xpcshell/xpcshell.eslintrc.js"
]
};

View file

@ -0,0 +1,42 @@
/* Any copyright is dedicated to the Public Domain.
* http://creativecommons.org/publicdomain/zero/1.0/ */
/*
* Internal module used to test the generation of Integration.jsm getters.
*/
"use strict";
this.EXPORTED_SYMBOLS = [
"TestIntegration",
];
const { classes: Cc, interfaces: Ci, utils: Cu, results: Cr } = Components;
Cu.import("resource://gre/modules/Task.jsm");
this.TestIntegration = {
value: "value",
get valueFromThis() {
return this.value;
},
get property() {
return this._property;
},
set property(value) {
this._property = value;
},
method(argument) {
this.methodArgument = argument;
return "method" + argument;
},
asyncMethod: Task.async(function* (argument) {
this.asyncMethodArgument = argument;
return "asyncMethod" + argument;
}),
};

Binary file not shown.

View file

@ -0,0 +1,28 @@
<?xml version="1.0" encoding="UTF-8"?>
<!DOCTYPE plist PUBLIC "-//Apple//DTD PLIST 1.0//EN" "http://www.apple.com/DTDs/PropertyList-1.0.dtd">
<plist version="1.0">
<dict>
<key>Boolean</key>
<false/>
<key>Array</key>
<array>
<string>abc</string>
<string>aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa</string>
<string>אאא</string>
<string>אאאאאאאאאאאאאאאאאאאאאאאאאאאאאאאאאאאאאאאאאאאאאאאאאאאאאאאאאאאאאאאאאאאאאאאאאאאאאאאאאאאאאאאאאאאאאאאאאאאאאאאאאאאאאאאאאאאאאאאאאאאאאאאאאאאאאאאאאאאאאאאאאאאאאאאאאאאאאאאאאאאאאאאאאאאאאאאאאאאאאאאאאאאאאאאאאאאאאאאאאאאאאאאאאאאאאאאאאאאאאאאאאאאאאאאאאאאאאאאאאאאאאאאאאאאאאאאאאאאאאאאאאאאאאאאאאאאאאאאאאאאאאאאאאאאאאאאאאאאאאאאאאאאאאאאאאאאאאאאאאאאאאאאאאאאאאאאאאאאאאאאאאאאאאאאאאאאאאאאאאאאאאאאאאאאאאאאאאאאאאאאאאאאאאאאאאאאאאאאאאאאאאאאאאאאאאאאאאאאאאאאאאאאאאאאאאאאאאאאאאאאאאאאאאאאאאאאאאאאאאאאאאאאאאאאאאאאאאאאאאאאאאאאאאאאאאאאאאאאאאאאאאאאאאאאאאאאאאאאאאאאאאאאאאאאאאאאאאאאאאאאאאאאאאאאאאאאאאאאאאאאאאאאאאאאאאאאאאאאאאאאאאאאאאאאאאאאאאאאאאאאאאאאאאאאאאאאאאאאאאאאאאאאאאאאאאאאאאאאאאאאאאאאאאאאאאאאאאאאאאאאאאאאאאאאאאאאאאאאאאאאאאאאאאאאאאאאאאאאאאאאאאאאאאאאאאאאאאאאאאאאאאאאאאאאאאאאאאאאאאאאאאאאאאאאאאאאאאאאאאאאאאאאאאאאאאאאאאאאאאאאאאאאאאאאאאאאאאאאאאאאאאאאאאאאאאאאאאאאאאאאאאאאאאאאאאאאאאאאאאאאאאאאאאאאאאאאאאאאאאאאאאאאאאאאאאאאאאאאאאאאאאאאאאאאאאאאאאאאאאאאאאאאאאאאאאאאאאאאאאאאאאאאאאאאאאאאאאאאאאאאאאאאאאאאאאאאאאאאאאאאאאאאאאאאאאאאאאאאאאאאאאאאאאאאאאאאאאאאא</string>
<string>𐀀𐀀𐀀</string>
<date>2011-12-31T11:15:23Z</date>
<data>MjAxMS0xMi0zMVQxMToxNTozM1o=</data>
<dict>
<key>Negative Number</key>
<integer>-400</integer>
<key>Real Number</key>
<real>2.71828183</real>
<key>Big Int</key>
<integer>9007199254740993</integer>
<key>Negative Big Int</key>
<integer>-9007199254740993</integer>
</dict>
</array>
</dict>
</plist>

View file

@ -0,0 +1,81 @@
/* Any copyright is dedicated to the Public Domain.
* http://creativecommons.org/publicdomain/zero/1.0/ */
Components.utils.import("resource://gre/modules/BinarySearch.jsm");
function run_test() {
// empty array
ok([], 1, false, 0);
// one-element array
ok([2], 2, true, 0);
ok([2], 1, false, 0);
ok([2], 3, false, 1);
// two-element array
ok([2, 4], 2, true, 0);
ok([2, 4], 4, true, 1);
ok([2, 4], 1, false, 0);
ok([2, 4], 3, false, 1);
ok([2, 4], 5, false, 2);
// three-element array
ok([2, 4, 6], 2, true, 0);
ok([2, 4, 6], 4, true, 1);
ok([2, 4, 6], 6, true, 2);
ok([2, 4, 6], 1, false, 0);
ok([2, 4, 6], 3, false, 1);
ok([2, 4, 6], 5, false, 2);
ok([2, 4, 6], 7, false, 3);
// duplicates
ok([2, 2], 2, true, 0);
ok([2, 2], 1, false, 0);
ok([2, 2], 3, false, 2);
// duplicates on the left
ok([2, 2, 4], 2, true, 1);
ok([2, 2, 4], 4, true, 2);
ok([2, 2, 4], 1, false, 0);
ok([2, 2, 4], 3, false, 2);
ok([2, 2, 4], 5, false, 3);
// duplicates on the right
ok([2, 4, 4], 2, true, 0);
ok([2, 4, 4], 4, true, 1);
ok([2, 4, 4], 1, false, 0);
ok([2, 4, 4], 3, false, 1);
ok([2, 4, 4], 5, false, 3);
// duplicates in the middle
ok([2, 4, 4, 6], 2, true, 0);
ok([2, 4, 4, 6], 4, true, 1);
ok([2, 4, 4, 6], 6, true, 3);
ok([2, 4, 4, 6], 1, false, 0);
ok([2, 4, 4, 6], 3, false, 1);
ok([2, 4, 4, 6], 5, false, 3);
ok([2, 4, 4, 6], 7, false, 4);
// duplicates all around
ok([2, 2, 4, 4, 6, 6], 2, true, 0);
ok([2, 2, 4, 4, 6, 6], 4, true, 2);
ok([2, 2, 4, 4, 6, 6], 6, true, 4);
ok([2, 2, 4, 4, 6, 6], 1, false, 0);
ok([2, 2, 4, 4, 6, 6], 3, false, 2);
ok([2, 2, 4, 4, 6, 6], 5, false, 4);
ok([2, 2, 4, 4, 6, 6], 7, false, 6);
}
function ok(array, target, expectedFound, expectedIdx) {
let [found, idx] = BinarySearch.search(cmp, array, target);
do_check_eq(found, expectedFound);
do_check_eq(idx, expectedIdx);
idx = expectedFound ? expectedIdx : -1;
do_check_eq(BinarySearch.indexOf(cmp, array, target), idx);
do_check_eq(BinarySearch.insertionIndexOf(cmp, array, target), expectedIdx);
}
function cmp(num1, num2) {
return num1 - num2;
}

View file

@ -0,0 +1,146 @@
const { CanonicalJSON } = Components.utils.import("resource://gre/modules/CanonicalJSON.jsm");
function stringRepresentation(obj) {
const clone = JSON.parse(JSON.stringify(obj));
return JSON.stringify(clone);
}
add_task(function* test_canonicalJSON_should_preserve_array_order() {
const input = ['one', 'two', 'three'];
// No sorting should be done on arrays.
do_check_eq(CanonicalJSON.stringify(input), '["one","two","three"]');
});
add_task(function* test_canonicalJSON_orders_object_keys() {
const input = [{
b: ['two', 'three'],
a: ['zero', 'one']
}];
do_check_eq(
CanonicalJSON.stringify(input),
'[{"a":["zero","one"],"b":["two","three"]}]'
);
});
add_task(function* test_canonicalJSON_orders_nested_object_keys() {
const input = [{
b: {d: 'd', c: 'c'},
a: {b: 'b', a: 'a'}
}];
do_check_eq(
CanonicalJSON.stringify(input),
'[{"a":{"a":"a","b":"b"},"b":{"c":"c","d":"d"}}]'
);
});
add_task(function* test_canonicalJSON_escapes_unicode_values() {
do_check_eq(
CanonicalJSON.stringify([{key: '✓'}]),
'[{"key":"\\u2713"}]'
);
// Unicode codepoints should be output in lowercase.
do_check_eq(
CanonicalJSON.stringify([{key: 'é'}]),
'[{"key":"\\u00e9"}]'
);
});
add_task(function* test_canonicalJSON_escapes_unicode_object_keys() {
do_check_eq(
CanonicalJSON.stringify([{'é': 'check'}]),
'[{"\\u00e9":"check"}]'
);
});
add_task(function* test_canonicalJSON_does_not_alter_input() {
const records = [
{'foo': 'bar', 'last_modified': '12345', 'id': '1'},
{'bar': 'baz', 'last_modified': '45678', 'id': '2'}
];
const serializedJSON = JSON.stringify(records);
CanonicalJSON.stringify(records);
do_check_eq(JSON.stringify(records), serializedJSON);
});
add_task(function* test_canonicalJSON_preserves_data() {
const records = [
{'foo': 'bar', 'last_modified': '12345', 'id': '1'},
{'bar': 'baz', 'last_modified': '45678', 'id': '2'},
]
const serialized = CanonicalJSON.stringify(records);
const expected = '[{"foo":"bar","id":"1","last_modified":"12345"},' +
'{"bar":"baz","id":"2","last_modified":"45678"}]';
do_check_eq(CanonicalJSON.stringify(records), expected);
});
add_task(function* test_canonicalJSON_does_not_add_space_separators() {
const records = [
{'foo': 'bar', 'last_modified': '12345', 'id': '1'},
{'bar': 'baz', 'last_modified': '45678', 'id': '2'},
]
const serialized = CanonicalJSON.stringify(records);
do_check_false(serialized.includes(" "));
});
add_task(function* test_canonicalJSON_serializes_empty_object() {
do_check_eq(CanonicalJSON.stringify({}), "{}");
});
add_task(function* test_canonicalJSON_serializes_empty_array() {
do_check_eq(CanonicalJSON.stringify([]), "[]");
});
add_task(function* test_canonicalJSON_serializes_NaN() {
do_check_eq(CanonicalJSON.stringify(NaN), "null");
});
add_task(function* test_canonicalJSON_serializes_inf() {
// This isn't part of the JSON standard.
do_check_eq(CanonicalJSON.stringify(Infinity), "null");
});
add_task(function* test_canonicalJSON_serializes_empty_string() {
do_check_eq(CanonicalJSON.stringify(""), '""');
});
add_task(function* test_canonicalJSON_escapes_backslashes() {
do_check_eq(CanonicalJSON.stringify("This\\and this"), '"This\\\\and this"');
});
add_task(function* test_canonicalJSON_handles_signed_zeros() {
// do_check_eq doesn't support comparison of -0 and 0 properly.
do_check_true(CanonicalJSON.stringify(-0) === '-0');
do_check_true(CanonicalJSON.stringify(0) === '0');
});
add_task(function* test_canonicalJSON_with_deeply_nested_dicts() {
const records = [{
'a': {
'b': 'b',
'a': 'a',
'c': {
'b': 'b',
'a': 'a',
'c': ['b', 'a', 'c'],
'd': {'b': 'b', 'a': 'a'},
'id': '1',
'e': 1,
'f': [2, 3, 1],
'g': {2: 2, 3: 3, 1: {
'b': 'b', 'a': 'a', 'c': 'c'}}}},
'id': '1'}]
const expected =
'[{"a":{"a":"a","b":"b","c":{"a":"a","b":"b","c":["b","a","c"],' +
'"d":{"a":"a","b":"b"},"e":1,"f":[2,3,1],"g":{' +
'"1":{"a":"a","b":"b","c":"c"},"2":2,"3":3},"id":"1"}},"id":"1"}]';
do_check_eq(CanonicalJSON.stringify(records), expected);
});
function run_test() {
run_next_test();
}

View file

@ -0,0 +1,53 @@
"use strict";
Components.utils.import("resource://gre/modules/Color.jsm");
function run_test() {
testRelativeLuminance();
testIsBright();
testContrastRatio();
testIsContrastRatioAcceptable();
}
function testRelativeLuminance() {
let c = new Color(0, 0, 0);
Assert.equal(c.relativeLuminance, 0, "Black is not illuminating");
c = new Color(255, 255, 255);
Assert.equal(c.relativeLuminance, 1, "White is quite the luminant one");
c = new Color(142, 42, 142);
Assert.equal(c.relativeLuminance, 0.25263952353998204,
"This purple is not that luminant");
}
function testIsBright() {
let c = new Color(0, 0, 0);
Assert.equal(c.isBright, 0, "Black is bright");
c = new Color(255, 255, 255);
Assert.equal(c.isBright, 1, "White is bright");
}
function testContrastRatio() {
let c = new Color(0, 0, 0);
let c2 = new Color(255, 255, 255);
Assert.equal(c.contrastRatio(c2), 21, "Contrast between black and white is max");
Assert.equal(c.contrastRatio(c), 1, "Contrast between equals is min");
let c3 = new Color(142, 42, 142);
Assert.equal(c.contrastRatio(c3), 6.05279047079964, "Contrast between black and purple");
Assert.equal(c2.contrastRatio(c3), 3.469474137806338, "Contrast between white and purple");
}
function testIsContrastRatioAcceptable() {
// Let's assert what browser.js is doing for window frames.
let c = new Color(...[55, 156, 152]);
let c2 = new Color(0, 0, 0);
Assert.equal(c.r, 55, "Reds should match");
Assert.equal(c.g, 156, "Greens should match");
Assert.equal(c.b, 152, "Blues should match");
Assert.ok(c.isContrastRatioAcceptable(c2), "The blue is high contrast enough");
c = new Color(...[35, 65, 100]);
Assert.ok(!c.isContrastRatioAcceptable(c2), "The blue is not high contrast enough");
}

View file

@ -0,0 +1,390 @@
/* Any copyright is dedicated to the Public Domain.
http://creativecommons.org/publicdomain/zero/1.0/ */
/**
* This file tests the DeferredTask.jsm module.
*/
// Globals
var { classes: Cc, interfaces: Ci, utils: Cu, results: Cr } = Components;
Cu.import("resource://gre/modules/XPCOMUtils.jsm");
XPCOMUtils.defineLazyModuleGetter(this, "DeferredTask",
"resource://gre/modules/DeferredTask.jsm");
XPCOMUtils.defineLazyModuleGetter(this, "Promise",
"resource://gre/modules/Promise.jsm");
/**
* Due to the nature of this module, most of the tests are time-dependent. All
* the timeouts are designed to occur at multiples of this granularity value,
* in milliseconds, that should be high enough to prevent intermittent failures,
* but low enough to prevent an excessive overall test execution time.
*/
const T = 100;
/**
* Waits for the specified timeout before resolving the returned promise.
*/
function promiseTimeout(aTimeoutMs)
{
let deferred = Promise.defer();
do_timeout(aTimeoutMs, deferred.resolve);
return deferred.promise;
}
function run_test()
{
run_next_test();
}
// Tests
/**
* Creates a simple DeferredTask and executes it once.
*/
add_test(function test_arm_simple()
{
new DeferredTask(run_next_test, 10).arm();
});
/**
* Checks that the delay set for the task is respected.
*/
add_test(function test_arm_delay_respected()
{
let executed1 = false;
let executed2 = false;
new DeferredTask(function () {
executed1 = true;
do_check_false(executed2);
}, 1*T).arm();
new DeferredTask(function () {
executed2 = true;
do_check_true(executed1);
run_next_test();
}, 2*T).arm();
});
/**
* Checks that calling "arm" again does not introduce further delay.
*/
add_test(function test_arm_delay_notrestarted()
{
let executed = false;
// Create a task that will run later.
let deferredTask = new DeferredTask(() => { executed = true; }, 4*T);
deferredTask.arm();
// Before the task starts, call "arm" again.
do_timeout(2*T, () => deferredTask.arm());
// The "arm" call should not have introduced further delays.
do_timeout(5*T, function () {
do_check_true(executed);
run_next_test();
});
});
/**
* Checks that a task runs only once when armed multiple times synchronously.
*/
add_test(function test_arm_coalesced()
{
let executed = false;
let deferredTask = new DeferredTask(function () {
do_check_false(executed);
executed = true;
run_next_test();
}, 50);
deferredTask.arm();
deferredTask.arm();
});
/**
* Checks that a task runs only once when armed multiple times synchronously,
* even when it has been created with a delay of zero milliseconds.
*/
add_test(function test_arm_coalesced_nodelay()
{
let executed = false;
let deferredTask = new DeferredTask(function () {
do_check_false(executed);
executed = true;
run_next_test();
}, 0);
deferredTask.arm();
deferredTask.arm();
});
/**
* Checks that a task can be armed again while running.
*/
add_test(function test_arm_recursive()
{
let executed = false;
let deferredTask = new DeferredTask(function () {
if (!executed) {
executed = true;
deferredTask.arm();
} else {
run_next_test();
}
}, 50);
deferredTask.arm();
});
/**
* Checks that calling "arm" while an asynchronous task is running waits until
* the task is finished before restarting the delay.
*/
add_test(function test_arm_async()
{
let finishedExecution = false;
let finishedExecutionAgain = false;
// Create a task that will run later.
let deferredTask = new DeferredTask(function* () {
yield promiseTimeout(4*T);
if (!finishedExecution) {
finishedExecution = true;
} else if (!finishedExecutionAgain) {
finishedExecutionAgain = true;
}
}, 2*T);
deferredTask.arm();
// While the task is running, call "arm" again. This will result in a wait
// of 2*T until the task finishes, then another 2*T for the normal task delay
// specified on construction.
do_timeout(4*T, function () {
do_check_true(deferredTask.isRunning);
do_check_false(finishedExecution);
deferredTask.arm();
});
// This will fail in case the task was started without waiting 2*T after it
// has finished.
do_timeout(7*T, function () {
do_check_false(deferredTask.isRunning);
do_check_true(finishedExecution);
});
// This is in the middle of the second execution.
do_timeout(10*T, function () {
do_check_true(deferredTask.isRunning);
do_check_false(finishedExecutionAgain);
});
// Wait enough time to verify that the task was executed as expected.
do_timeout(13*T, function () {
do_check_false(deferredTask.isRunning);
do_check_true(finishedExecutionAgain);
run_next_test();
});
});
/**
* Checks that an armed task can be disarmed.
*/
add_test(function test_disarm()
{
// Create a task that will run later.
let deferredTask = new DeferredTask(function () {
do_throw("This task should not run.");
}, 2*T);
deferredTask.arm();
// Disable execution later, but before the task starts.
do_timeout(1*T, () => deferredTask.disarm());
// Wait enough time to verify that the task did not run.
do_timeout(3*T, run_next_test);
});
/**
* Checks that calling "disarm" allows the delay to be restarted.
*/
add_test(function test_disarm_delay_restarted()
{
let executed = false;
let deferredTask = new DeferredTask(() => { executed = true; }, 4*T);
deferredTask.arm();
do_timeout(2*T, function () {
deferredTask.disarm();
deferredTask.arm();
});
do_timeout(5*T, function () {
do_check_false(executed);
});
do_timeout(7*T, function () {
do_check_true(executed);
run_next_test();
});
});
/**
* Checks that calling "disarm" while an asynchronous task is running does not
* prevent the task to finish.
*/
add_test(function test_disarm_async()
{
let finishedExecution = false;
let deferredTask = new DeferredTask(function* () {
deferredTask.arm();
yield promiseTimeout(2*T);
finishedExecution = true;
}, 1*T);
deferredTask.arm();
do_timeout(2*T, function () {
do_check_true(deferredTask.isRunning);
do_check_true(deferredTask.isArmed);
do_check_false(finishedExecution);
deferredTask.disarm();
});
do_timeout(4*T, function () {
do_check_false(deferredTask.isRunning);
do_check_false(deferredTask.isArmed);
do_check_true(finishedExecution);
run_next_test();
});
});
/**
* Checks that calling "arm" immediately followed by "disarm" while an
* asynchronous task is running does not cause it to run again.
*/
add_test(function test_disarm_immediate_async()
{
let executed = false;
let deferredTask = new DeferredTask(function* () {
do_check_false(executed);
executed = true;
yield promiseTimeout(2*T);
}, 1*T);
deferredTask.arm();
do_timeout(2*T, function () {
do_check_true(deferredTask.isRunning);
do_check_false(deferredTask.isArmed);
deferredTask.arm();
deferredTask.disarm();
});
do_timeout(4*T, function () {
do_check_true(executed);
do_check_false(deferredTask.isRunning);
do_check_false(deferredTask.isArmed);
run_next_test();
});
});
/**
* Checks the isArmed and isRunning properties with a synchronous task.
*/
add_test(function test_isArmed_isRunning()
{
let deferredTask = new DeferredTask(function () {
do_check_true(deferredTask.isRunning);
do_check_false(deferredTask.isArmed);
deferredTask.arm();
do_check_true(deferredTask.isArmed);
deferredTask.disarm();
do_check_false(deferredTask.isArmed);
run_next_test();
}, 50);
do_check_false(deferredTask.isArmed);
deferredTask.arm();
do_check_true(deferredTask.isArmed);
do_check_false(deferredTask.isRunning);
});
/**
* Checks that the "finalize" method executes a synchronous task.
*/
add_test(function test_finalize()
{
let executed = false;
let timePassed = false;
let deferredTask = new DeferredTask(function () {
do_check_false(timePassed);
executed = true;
}, 2*T);
deferredTask.arm();
do_timeout(1*T, () => { timePassed = true; });
// This should trigger the immediate execution of the task.
deferredTask.finalize().then(function () {
do_check_true(executed);
run_next_test();
});
});
/**
* Checks that the "finalize" method executes the task again from start to
* finish in case it is already running.
*/
add_test(function test_finalize_executes_entirely()
{
let executed = false;
let executedAgain = false;
let timePassed = false;
let deferredTask = new DeferredTask(function* () {
// The first time, we arm the timer again and set up the finalization.
if (!executed) {
deferredTask.arm();
do_check_true(deferredTask.isArmed);
do_check_true(deferredTask.isRunning);
deferredTask.finalize().then(function () {
// When we reach this point, the task must be finished.
do_check_true(executedAgain);
do_check_false(timePassed);
do_check_false(deferredTask.isArmed);
do_check_false(deferredTask.isRunning);
run_next_test();
});
// The second execution triggered by the finalization waits 1*T for the
// current task to finish (see the timeout below), but then it must not
// wait for the 2*T specified on construction as normal task delay. The
// second execution will finish after the timeout below has passed again,
// for a total of 2*T of wait time.
do_timeout(3*T, () => { timePassed = true; });
}
yield promiseTimeout(1*T);
// Just before finishing, indicate if we completed the second execution.
if (executed) {
do_check_true(deferredTask.isRunning);
executedAgain = true;
} else {
executed = true;
}
}, 2*T);
deferredTask.arm();
});

View file

@ -0,0 +1,226 @@
/* Any copyright is dedicated to the Public Domain.
http://creativecommons.org/publicdomain/zero/1.0/ */
Components.utils.import("resource://gre/modules/FileUtils.jsm");
function do_check_throws(f, result, stack) {
if (!stack)
stack = Components.stack.caller;
try {
f();
} catch (exc) {
if (exc.result == result)
return;
do_throw("expected result " + result + ", caught " + exc, stack);
}
do_throw("expected result " + result + ", none thrown", stack);
}
const gProfD = do_get_profile();
add_test(function test_getFile() {
let file = FileUtils.getFile("ProfD", ["foobar"]);
do_check_true(file instanceof Components.interfaces.nsIFile);
do_check_false(file.exists());
let other = gProfD.clone();
other.append("foobar");
do_check_true(file.equals(other));
run_next_test();
});
add_test(function test_getFile_nonexistentDir() {
do_check_throws(function () {
let file = FileUtils.getFile("NonexistentD", ["foobar"]);
}, Components.results.NS_ERROR_FAILURE);
run_next_test();
});
add_test(function test_getFile_createDirs() {
let file = FileUtils.getFile("ProfD", ["a", "b", "foobar"]);
do_check_true(file instanceof Components.interfaces.nsIFile);
do_check_false(file.exists());
let other = gProfD.clone();
other.append("a");
do_check_true(other.isDirectory());
other.append("b");
do_check_true(other.isDirectory());
other.append("foobar");
do_check_true(file.equals(other));
run_next_test();
});
add_test(function test_getDir() {
let dir = FileUtils.getDir("ProfD", ["foodir"]);
do_check_true(dir instanceof Components.interfaces.nsIFile);
do_check_false(dir.exists());
let other = gProfD.clone();
other.append("foodir");
do_check_true(dir.equals(other));
run_next_test();
});
add_test(function test_getDir_nonexistentDir() {
do_check_throws(function () {
let file = FileUtils.getDir("NonexistentD", ["foodir"]);
}, Components.results.NS_ERROR_FAILURE);
run_next_test();
});
add_test(function test_getDir_shouldCreate() {
let dir = FileUtils.getDir("ProfD", ["c", "d", "foodir"], true);
do_check_true(dir instanceof Components.interfaces.nsIFile);
do_check_true(dir.exists());
let other = gProfD.clone();
other.append("c");
do_check_true(other.isDirectory());
other.append("d");
do_check_true(other.isDirectory());
other.append("foodir");
do_check_true(dir.equals(other));
run_next_test();
});
var openFileOutputStream_defaultFlags = function (aKind, aFileName) {
let file = FileUtils.getFile("ProfD", [aFileName]);
let fos;
do_check_true(aKind == "atomic" || aKind == "safe" || aKind == "");
if (aKind == "atomic") {
fos = FileUtils.openAtomicFileOutputStream(file);
} else if (aKind == "safe") {
fos = FileUtils.openSafeFileOutputStream(file);
} else {
fos = FileUtils.openFileOutputStream(file);
}
do_check_true(fos instanceof Components.interfaces.nsIFileOutputStream);
if (aKind == "atomic" || aKind == "safe") {
do_check_true(fos instanceof Components.interfaces.nsISafeOutputStream);
}
// FileUtils.openFileOutputStream or FileUtils.openAtomicFileOutputStream()
// or FileUtils.openSafeFileOutputStream() opens the stream with DEFER_OPEN
// which means the file will not be open until we write to it.
do_check_false(file.exists());
let data = "test_default_flags";
fos.write(data, data.length);
do_check_true(file.exists());
// No nsIXULRuntime in xpcshell, so use this trick to determine whether we're
// on Windows.
if ("@mozilla.org/windows-registry-key;1" in Components.classes) {
do_check_eq(file.permissions, 0o666);
} else {
do_check_eq(file.permissions, FileUtils.PERMS_FILE);
}
run_next_test();
};
var openFileOutputStream_modeFlags = function(aKind, aFileName) {
let file = FileUtils.getFile("ProfD", [aFileName]);
let fos;
do_check_true(aKind == "atomic" || aKind == "safe" || aKind == "");
if (aKind == "atomic") {
fos = FileUtils.openAtomicFileOutputStream(file, FileUtils.MODE_WRONLY);
} else if (aKind == "safe") {
fos = FileUtils.openSafeFileOutputStream(file, FileUtils.MODE_WRONLY);
} else {
fos = FileUtils.openFileOutputStream(file, FileUtils.MODE_WRONLY);
}
let data = "test_modeFlags";
do_check_throws(function () {
fos.write(data, data.length);
}, Components.results.NS_ERROR_FILE_NOT_FOUND);
do_check_false(file.exists());
run_next_test();
};
var closeFileOutputStream = function(aKind, aFileName) {
let file = FileUtils.getFile("ProfD", [aFileName]);
let fos;
do_check_true(aKind == "atomic" || aKind == "safe");
if (aKind == "atomic") {
fos = FileUtils.openAtomicFileOutputStream(file);
} else if (aKind == "safe") {
fos = FileUtils.openSafeFileOutputStream(file);
}
// We can write data to the stream just fine while it's open.
let data = "testClose";
fos.write(data, data.length);
// But once we close it, we can't anymore.
if (aKind == "atomic") {
FileUtils.closeAtomicFileOutputStream(fos);
} else if (aKind == "safe") {
FileUtils.closeSafeFileOutputStream(fos);
}
do_check_throws(function () {
fos.write(data, data.length);
}, Components.results.NS_BASE_STREAM_CLOSED);
run_next_test();
};
add_test(function test_openFileOutputStream_defaultFlags() {
openFileOutputStream_defaultFlags("", "george");
});
// openFileOutputStream will uses MODE_WRONLY | MODE_CREATE | MODE_TRUNCATE
// as the default mode flags, but we can pass in our own if we want to.
add_test(function test_openFileOutputStream_modeFlags() {
openFileOutputStream_modeFlags("", "ringo");
});
add_test(function test_openAtomicFileOutputStream_defaultFlags() {
openFileOutputStream_defaultFlags("atomic", "peiyong");
});
// openAtomicFileOutputStream will uses MODE_WRONLY | MODE_CREATE | MODE_TRUNCATE
// as the default mode flags, but we can pass in our own if we want to.
add_test(function test_openAtomicFileOutputStream_modeFlags() {
openFileOutputStream_modeFlags("atomic", "lin");
});
add_test(function test_closeAtomicFileOutputStream() {
closeFileOutputStream("atomic", "peiyonglin");
});
add_test(function test_openSafeFileOutputStream_defaultFlags() {
openFileOutputStream_defaultFlags("safe", "john");
});
// openSafeFileOutputStream will uses MODE_WRONLY | MODE_CREATE | MODE_TRUNCATE
// as the default mode flags, but we can pass in our own if we want to.
add_test(function test_openSafeFileOutputStream_modeFlags() {
openFileOutputStream_modeFlags("safe", "paul");
});
add_test(function test_closeSafeFileOutputStream() {
closeFileOutputStream("safe", "georgee");
});
add_test(function test_newFile() {
let testfile = FileUtils.getFile("ProfD", ["test"]);
let testpath = testfile.path;
let file = new FileUtils.File(testpath);
do_check_true(file instanceof Components.interfaces.nsILocalFile);
do_check_true(file.equals(testfile));
do_check_eq(file.path, testpath);
run_next_test();
});
function run_test() {
run_next_test();
}

View file

@ -0,0 +1,265 @@
const { interfaces: Ci, classes: Cc, utils: Cu } = Components;
const { FinderIterator } = Cu.import("resource://gre/modules/FinderIterator.jsm", {});
Cu.import("resource://gre/modules/Promise.jsm");
var gFindResults = [];
// Stub the method that instantiates nsIFind and does all the interaction with
// the docShell to be searched through.
FinderIterator._iterateDocument = function* (word, window, finder) {
for (let range of gFindResults)
yield range;
};
FinderIterator._rangeStartsInLink = fakeRange => fakeRange.startsInLink;
function FakeRange(textContent, startsInLink = false) {
this.startContainer = {};
this.startsInLink = startsInLink;
this.toString = () => textContent;
}
var gMockWindow = {
setTimeout(cb, delay) {
Cc["@mozilla.org/timer;1"].createInstance(Ci.nsITimer)
.initWithCallback(cb, delay, Ci.nsITimer.TYPE_ONE_SHOT);
}
};
var gMockFinder = {
_getWindow() { return gMockWindow; }
};
function prepareIterator(findText, rangeCount) {
gFindResults = [];
for (let i = rangeCount; --i >= 0;)
gFindResults.push(new FakeRange(findText));
}
add_task(function* test_start() {
let findText = "test";
let rangeCount = 300;
prepareIterator(findText, rangeCount);
let count = 0;
yield FinderIterator.start({
caseSensitive: false,
entireWord: false,
finder: gMockFinder,
listener: {
onIteratorRangeFound(range) {
++count;
Assert.equal(range.toString(), findText, "Text content should match");
}
},
word: findText
});
Assert.equal(rangeCount, count, "Amount of ranges yielded should match!");
Assert.ok(!FinderIterator.running, "Running state should match");
Assert.equal(FinderIterator._previousRanges.length, rangeCount, "Ranges cache should match");
FinderIterator.reset();
});
add_task(function* test_valid_arguments() {
let findText = "foo";
let rangeCount = 20;
prepareIterator(findText, rangeCount);
let count = 0;
yield FinderIterator.start({
caseSensitive: false,
entireWord: false,
finder: gMockFinder,
listener: { onIteratorRangeFound(range) { ++count; } },
word: findText
});
let params = FinderIterator._previousParams;
Assert.ok(!params.linksOnly, "Default for linksOnly is false");
Assert.ok(!params.useCache, "Default for useCache is false");
Assert.equal(params.word, findText, "Words should match");
count = 0;
Assert.throws(() => FinderIterator.start({
entireWord: false,
listener: { onIteratorRangeFound(range) { ++count; } },
word: findText
}), /Missing required option 'caseSensitive'/, "Should throw when missing an argument");
FinderIterator.reset();
Assert.throws(() => FinderIterator.start({
caseSensitive: false,
listener: { onIteratorRangeFound(range) { ++count; } },
word: findText
}), /Missing required option 'entireWord'/, "Should throw when missing an argument");
FinderIterator.reset();
Assert.throws(() => FinderIterator.start({
caseSensitive: false,
entireWord: false,
listener: { onIteratorRangeFound(range) { ++count; } },
word: findText
}), /Missing required option 'finder'/, "Should throw when missing an argument");
FinderIterator.reset();
Assert.throws(() => FinderIterator.start({
caseSensitive: true,
entireWord: false,
finder: gMockFinder,
word: findText
}), /Missing valid, required option 'listener'/, "Should throw when missing an argument");
FinderIterator.reset();
Assert.throws(() => FinderIterator.start({
caseSensitive: false,
entireWord: true,
finder: gMockFinder,
listener: { onIteratorRangeFound(range) { ++count; } },
}), /Missing required option 'word'/, "Should throw when missing an argument");
FinderIterator.reset();
Assert.equal(count, 0, "No ranges should've been counted");
});
add_task(function* test_stop() {
let findText = "bar";
let rangeCount = 120;
prepareIterator(findText, rangeCount);
let count = 0;
let whenDone = FinderIterator.start({
caseSensitive: false,
entireWord: false,
finder: gMockFinder,
listener: { onIteratorRangeFound(range) { ++count; } },
word: findText
});
FinderIterator.stop();
yield whenDone;
Assert.equal(count, 0, "Number of ranges should be 0");
FinderIterator.reset();
});
add_task(function* test_reset() {
let findText = "tik";
let rangeCount = 142;
prepareIterator(findText, rangeCount);
let count = 0;
let whenDone = FinderIterator.start({
caseSensitive: false,
entireWord: false,
finder: gMockFinder,
listener: { onIteratorRangeFound(range) { ++count; } },
word: findText
});
Assert.ok(FinderIterator.running, "Yup, running we are");
Assert.equal(count, 0, "Number of ranges should match 0");
Assert.equal(FinderIterator.ranges.length, 0, "Number of ranges should match 0");
FinderIterator.reset();
Assert.ok(!FinderIterator.running, "Nope, running we are not");
Assert.equal(FinderIterator.ranges.length, 0, "No ranges after reset");
Assert.equal(FinderIterator._previousRanges.length, 0, "No ranges after reset");
yield whenDone;
Assert.equal(count, 0, "Number of ranges should match 0");
});
add_task(function* test_parallel_starts() {
let findText = "tak";
let rangeCount = 2143;
prepareIterator(findText, rangeCount);
// Start off the iterator.
let count = 0;
let whenDone = FinderIterator.start({
caseSensitive: false,
entireWord: false,
finder: gMockFinder,
listener: { onIteratorRangeFound(range) { ++count; } },
word: findText
});
yield new Promise(resolve => gMockWindow.setTimeout(resolve, 120));
Assert.ok(FinderIterator.running, "We ought to be running here");
let count2 = 0;
let whenDone2 = FinderIterator.start({
caseSensitive: false,
entireWord: false,
finder: gMockFinder,
listener: { onIteratorRangeFound(range) { ++count2; } },
word: findText
});
// Let the iterator run for a little while longer before we assert the world.
yield new Promise(resolve => gMockWindow.setTimeout(resolve, 10));
FinderIterator.stop();
Assert.ok(!FinderIterator.running, "Stop means stop");
yield whenDone;
yield whenDone2;
Assert.greater(count, FinderIterator.kIterationSizeMax, "At least one range should've been found");
Assert.less(count, rangeCount, "Not all ranges should've been found");
Assert.greater(count2, FinderIterator.kIterationSizeMax, "At least one range should've been found");
Assert.less(count2, rangeCount, "Not all ranges should've been found");
Assert.equal(count2, count, "The second start was later, but should have caught up");
FinderIterator.reset();
});
add_task(function* test_allowDistance() {
let findText = "gup";
let rangeCount = 20;
prepareIterator(findText, rangeCount);
// Start off the iterator.
let count = 0;
let whenDone = FinderIterator.start({
caseSensitive: false,
entireWord: false,
finder: gMockFinder,
listener: { onIteratorRangeFound(range) { ++count; } },
word: findText
});
let count2 = 0;
let whenDone2 = FinderIterator.start({
caseSensitive: false,
entireWord: false,
finder: gMockFinder,
listener: { onIteratorRangeFound(range) { ++count2; } },
word: "gu"
});
let count3 = 0;
let whenDone3 = FinderIterator.start({
allowDistance: 1,
caseSensitive: false,
entireWord: false,
finder: gMockFinder,
listener: { onIteratorRangeFound(range) { ++count3; } },
word: "gu"
});
yield Promise.all([whenDone, whenDone2, whenDone3]);
Assert.equal(count, rangeCount, "The first iterator invocation should yield all results");
Assert.equal(count2, 0, "The second iterator invocation should yield _no_ results");
Assert.equal(count3, rangeCount, "The first iterator invocation should yield all results");
FinderIterator.reset();
});

View file

@ -0,0 +1,794 @@
/* Any copyright is dedicated to the Public Domain.
* http://creativecommons.org/publicdomain/zero/1.0/ */
var {classes: Cc, interfaces: Ci, results: Cr, utils: Cu, manager: Cm} = Components;
const URL_HOST = "http://localhost";
var GMPScope = Cu.import("resource://gre/modules/GMPInstallManager.jsm");
Cu.import("resource://gre/modules/Services.jsm");
Cu.import("resource://gre/modules/XPCOMUtils.jsm");
Cu.import("resource://gre/modules/FileUtils.jsm");
Cu.import("resource://testing-common/httpd.js");
Cu.import("resource://gre/modules/Promise.jsm");
Cu.import("resource://gre/modules/Preferences.jsm")
Cu.import("resource://gre/modules/UpdateUtils.jsm");
var { computeHash } = Cu.import("resource://gre/modules/addons/ProductAddonChecker.jsm");
var ProductAddonCheckerScope = Cu.import("resource://gre/modules/addons/ProductAddonChecker.jsm");
do_get_profile();
function run_test() { Cu.import("resource://gre/modules/Preferences.jsm")
Preferences.set("media.gmp.log.dump", true);
Preferences.set("media.gmp.log.level", 0);
run_next_test();
}
/**
* Tests that the helper used for preferences works correctly
*/
add_task(function* test_prefs() {
let addon1 = "addon1", addon2 = "addon2";
GMPScope.GMPPrefs.set(GMPScope.GMPPrefs.KEY_URL, "http://not-really-used");
GMPScope.GMPPrefs.set(GMPScope.GMPPrefs.KEY_URL_OVERRIDE, "http://not-really-used-2");
GMPScope.GMPPrefs.set(GMPScope.GMPPrefs.KEY_PLUGIN_LAST_UPDATE, "1", addon1);
GMPScope.GMPPrefs.set(GMPScope.GMPPrefs.KEY_PLUGIN_VERSION, "2", addon1);
GMPScope.GMPPrefs.set(GMPScope.GMPPrefs.KEY_PLUGIN_LAST_UPDATE, "3", addon2);
GMPScope.GMPPrefs.set(GMPScope.GMPPrefs.KEY_PLUGIN_VERSION, "4", addon2);
GMPScope.GMPPrefs.set(GMPScope.GMPPrefs.KEY_PLUGIN_AUTOUPDATE, false, addon2);
GMPScope.GMPPrefs.set(GMPScope.GMPPrefs.KEY_CERT_CHECKATTRS, true);
do_check_eq(GMPScope.GMPPrefs.get(GMPScope.GMPPrefs.KEY_URL), "http://not-really-used");
do_check_eq(GMPScope.GMPPrefs.get(GMPScope.GMPPrefs.KEY_URL_OVERRIDE),
"http://not-really-used-2");
do_check_eq(GMPScope.GMPPrefs.get(GMPScope.GMPPrefs.KEY_PLUGIN_LAST_UPDATE, "", addon1), "1");
do_check_eq(GMPScope.GMPPrefs.get(GMPScope.GMPPrefs.KEY_PLUGIN_VERSION, "", addon1), "2");
do_check_eq(GMPScope.GMPPrefs.get(GMPScope.GMPPrefs.KEY_PLUGIN_LAST_UPDATE, "", addon2), "3");
do_check_eq(GMPScope.GMPPrefs.get(GMPScope.GMPPrefs.KEY_PLUGIN_VERSION, "", addon2), "4");
do_check_eq(GMPScope.GMPPrefs.get(GMPScope.GMPPrefs.KEY_PLUGIN_AUTOUPDATE, undefined, addon2),
false);
do_check_true(GMPScope.GMPPrefs.get(GMPScope.GMPPrefs.KEY_CERT_CHECKATTRS));
GMPScope.GMPPrefs.set(GMPScope.GMPPrefs.KEY_PLUGIN_AUTOUPDATE, true, addon2);
});
/**
* Tests that an uninit without a check works fine
*/
add_task(function* test_checkForAddons_uninitWithoutCheck() {
let installManager = new GMPInstallManager();
installManager.uninit();
});
/**
* Tests that an uninit without an install works fine
*/
add_test(function test_checkForAddons_uninitWithoutInstall() {
overrideXHR(200, "");
let installManager = new GMPInstallManager();
let promise = installManager.checkForAddons();
promise.then(res => {
do_check_true(res.usedFallback);
installManager.uninit();
run_next_test();
});
});
/**
* Tests that no response returned rejects
*/
add_test(function test_checkForAddons_noResponse() {
overrideXHR(200, "");
let installManager = new GMPInstallManager();
let promise = installManager.checkForAddons();
promise.then(res => {
do_check_true(res.usedFallback);
installManager.uninit();
run_next_test();
});
});
/**
* Tests that no addons element returned resolves with no addons
*/
add_task(function* test_checkForAddons_noAddonsElement() {
overrideXHR(200, "<updates></updates>");
let installManager = new GMPInstallManager();
let res = yield installManager.checkForAddons();
do_check_eq(res.gmpAddons.length, 0);
installManager.uninit();
});
/**
* Tests that empty addons element returned resolves with no addons
*/
add_task(function* test_checkForAddons_emptyAddonsElement() {
overrideXHR(200, "<updates><addons/></updates>");
let installManager = new GMPInstallManager();
let res = yield installManager.checkForAddons();
do_check_eq(res.gmpAddons.length, 0);
installManager.uninit();
});
/**
* Tests that a response with the wrong root element rejects
*/
add_test(function test_checkForAddons_wrongResponseXML() {
overrideXHR(200, "<digits_of_pi>3.141592653589793....</digits_of_pi>");
let installManager = new GMPInstallManager();
let promise = installManager.checkForAddons();
promise.then(res => {
do_check_true(res.usedFallback);
installManager.uninit();
run_next_test();
});
});
/**
* Tests that a 404 error works as expected
*/
add_test(function test_checkForAddons_404Error() {
overrideXHR(404, "");
let installManager = new GMPInstallManager();
let promise = installManager.checkForAddons();
promise.then(res => {
do_check_true(res.usedFallback);
installManager.uninit();
run_next_test();
});
});
/**
* Tests that a xhr abort() works as expected
*/
add_test(function test_checkForAddons_abort() {
let overriddenXhr = overrideXHR(200, "", { dropRequest: true} );
let installManager = new GMPInstallManager();
let promise = installManager.checkForAddons();
overriddenXhr.abort();
promise.then(res => {
do_check_true(res.usedFallback);
installManager.uninit();
run_next_test();
});
});
/**
* Tests that a defensive timeout works as expected
*/
add_test(function test_checkForAddons_timeout() {
overrideXHR(200, "", { dropRequest: true, timeout: true });
let installManager = new GMPInstallManager();
let promise = installManager.checkForAddons();
promise.then(res => {
do_check_true(res.usedFallback);
installManager.uninit();
run_next_test();
});
});
/**
* Tests that we throw correctly in case of ssl certification error.
*/
add_test(function test_checkForAddons_bad_ssl() {
//
// Add random stuff that cause CertUtil to require https.
//
let PREF_KEY_URL_OVERRIDE_BACKUP =
Preferences.get(GMPScope.GMPPrefs.KEY_URL_OVERRIDE, undefined);
Preferences.reset(GMPScope.GMPPrefs.KEY_URL_OVERRIDE);
let CERTS_BRANCH_DOT_ONE = GMPScope.GMPPrefs.KEY_CERTS_BRANCH + ".1";
let PREF_CERTS_BRANCH_DOT_ONE_BACKUP =
Preferences.get(CERTS_BRANCH_DOT_ONE, undefined);
Services.prefs.setCharPref(CERTS_BRANCH_DOT_ONE, "funky value");
overrideXHR(200, "");
let installManager = new GMPInstallManager();
let promise = installManager.checkForAddons();
promise.then(res => {
do_check_true(res.usedFallback);
installManager.uninit();
if (PREF_KEY_URL_OVERRIDE_BACKUP) {
Preferences.set(GMPScope.GMPPrefs.KEY_URL_OVERRIDE,
PREF_KEY_URL_OVERRIDE_BACKUP);
}
if (PREF_CERTS_BRANCH_DOT_ONE_BACKUP) {
Preferences.set(CERTS_BRANCH_DOT_ONE,
PREF_CERTS_BRANCH_DOT_ONE_BACKUP);
}
run_next_test();
});
});
/**
* Tests that gettinga a funky non XML response works as expected
*/
add_test(function test_checkForAddons_notXML() {
overrideXHR(200, "3.141592653589793....");
let installManager = new GMPInstallManager();
let promise = installManager.checkForAddons();
promise.then(res => {
do_check_true(res.usedFallback);
installManager.uninit();
run_next_test();
});
});
/**
* Tests that getting a response with a single addon works as expected
*/
add_task(function* test_checkForAddons_singleAddon() {
let responseXML =
"<?xml version=\"1.0\"?>" +
"<updates>" +
" <addons>" +
" <addon id=\"gmp-gmpopenh264\"" +
" URL=\"http://127.0.0.1:8011/gmp-gmpopenh264-1.1.zip\"" +
" hashFunction=\"sha256\"" +
" hashValue=\"1118b90d6f645eefc2b99af17bae396636ace1e33d079c88de715177584e2aee\"" +
" version=\"1.1\"/>" +
" </addons>" +
"</updates>"
overrideXHR(200, responseXML);
let installManager = new GMPInstallManager();
let res = yield installManager.checkForAddons();
do_check_eq(res.gmpAddons.length, 1);
let gmpAddon = res.gmpAddons[0];
do_check_eq(gmpAddon.id, "gmp-gmpopenh264");
do_check_eq(gmpAddon.URL, "http://127.0.0.1:8011/gmp-gmpopenh264-1.1.zip");
do_check_eq(gmpAddon.hashFunction, "sha256");
do_check_eq(gmpAddon.hashValue, "1118b90d6f645eefc2b99af17bae396636ace1e33d079c88de715177584e2aee");
do_check_eq(gmpAddon.version, "1.1");
do_check_eq(gmpAddon.size, undefined);
do_check_true(gmpAddon.isValid);
do_check_false(gmpAddon.isInstalled);
installManager.uninit();
});
/**
* Tests that getting a response with a single addon with the optional size
* attribute parses as expected.
*/
add_task(function* test_checkForAddons_singleAddonWithSize() {
let responseXML =
"<?xml version=\"1.0\"?>" +
"<updates>" +
" <addons>" +
" <addon id=\"openh264-plugin-no-at-symbol\"" +
" URL=\"http://127.0.0.1:8011/gmp-gmpopenh264-1.1.zip\"" +
" hashFunction=\"sha256\"" +
" size=\"42\"" +
" hashValue=\"1118b90d6f645eefc2b99af17bae396636ace1e33d079c88de715177584e2aee\"" +
" version=\"1.1\"/>" +
" </addons>" +
"</updates>"
overrideXHR(200, responseXML);
let installManager = new GMPInstallManager();
let res = yield installManager.checkForAddons();
do_check_eq(res.gmpAddons.length, 1);
let gmpAddon = res.gmpAddons[0];
do_check_eq(gmpAddon.id, "openh264-plugin-no-at-symbol");
do_check_eq(gmpAddon.URL, "http://127.0.0.1:8011/gmp-gmpopenh264-1.1.zip");
do_check_eq(gmpAddon.hashFunction, "sha256");
do_check_eq(gmpAddon.hashValue, "1118b90d6f645eefc2b99af17bae396636ace1e33d079c88de715177584e2aee");
do_check_eq(gmpAddon.size, 42);
do_check_eq(gmpAddon.version, "1.1");
do_check_true(gmpAddon.isValid);
do_check_false(gmpAddon.isInstalled);
installManager.uninit();
});
/**
* Tests that checking for multiple addons work correctly.
* Also tests that invalid addons work correctly.
*/
add_task(function* test_checkForAddons_multipleAddonNoUpdatesSomeInvalid() {
let responseXML =
"<?xml version=\"1.0\"?>" +
"<updates>" +
" <addons>" +
// valid openh264
" <addon id=\"gmp-gmpopenh264\"" +
" URL=\"http://127.0.0.1:8011/gmp-gmpopenh264-1.1.zip\"" +
" hashFunction=\"sha256\"" +
" hashValue=\"1118b90d6f645eefc2b99af17bae396636ace1e33d079c88de715177584e2aee\"" +
" version=\"1.1\"/>" +
// valid not openh264
" <addon id=\"NOT-gmp-gmpopenh264\"" +
" URL=\"http://127.0.0.1:8011/NOT-gmp-gmpopenh264-1.1.zip\"" +
" hashFunction=\"sha512\"" +
" hashValue=\"141592656f645eefc2b99af17bae396636ace1e33d079c88de715177584e2aee\"" +
" version=\"9.1\"/>" +
// noid
" <addon notid=\"NOT-gmp-gmpopenh264\"" +
" URL=\"http://127.0.0.1:8011/NOT-gmp-gmpopenh264-1.1.zip\"" +
" hashFunction=\"sha512\"" +
" hashValue=\"141592656f645eefc2b99af17bae396636ace1e33d079c88de715177584e2aee\"" +
" version=\"9.1\"/>" +
// no URL
" <addon id=\"NOT-gmp-gmpopenh264\"" +
" notURL=\"http://127.0.0.1:8011/NOT-gmp-gmpopenh264-1.1.zip\"" +
" hashFunction=\"sha512\"" +
" hashValue=\"141592656f645eefc2b99af17bae396636ace1e33d079c88de715177584e2aee\"" +
" version=\"9.1\"/>" +
// no hash function
" <addon id=\"NOT-gmp-gmpopenh264\"" +
" URL=\"http://127.0.0.1:8011/NOT-gmp-gmpopenh264-1.1.zip\"" +
" nothashFunction=\"sha512\"" +
" hashValue=\"141592656f645eefc2b99af17bae396636ace1e33d079c88de715177584e2aee\"" +
" version=\"9.1\"/>" +
// no hash function
" <addon id=\"NOT-gmp-gmpopenh264\"" +
" URL=\"http://127.0.0.1:8011/NOT-gmp-gmpopenh264-1.1.zip\"" +
" hashFunction=\"sha512\"" +
" nothashValue=\"141592656f645eefc2b99af17bae396636ace1e33d079c88de715177584e2aee\"" +
" version=\"9.1\"/>" +
// not version
" <addon id=\"NOT-gmp-gmpopenh264\"" +
" URL=\"http://127.0.0.1:8011/NOT-gmp-gmpopenh264-1.1.zip\"" +
" hashFunction=\"sha512\"" +
" hashValue=\"141592656f645eefc2b99af17bae396636ace1e33d079c88de715177584e2aee\"" +
" notversion=\"9.1\"/>" +
" </addons>" +
"</updates>"
overrideXHR(200, responseXML);
let installManager = new GMPInstallManager();
let res = yield installManager.checkForAddons();
do_check_eq(res.gmpAddons.length, 7);
let gmpAddon = res.gmpAddons[0];
do_check_eq(gmpAddon.id, "gmp-gmpopenh264");
do_check_eq(gmpAddon.URL, "http://127.0.0.1:8011/gmp-gmpopenh264-1.1.zip");
do_check_eq(gmpAddon.hashFunction, "sha256");
do_check_eq(gmpAddon.hashValue, "1118b90d6f645eefc2b99af17bae396636ace1e33d079c88de715177584e2aee");
do_check_eq(gmpAddon.version, "1.1");
do_check_true(gmpAddon.isValid);
do_check_false(gmpAddon.isInstalled);
gmpAddon = res.gmpAddons[1];
do_check_eq(gmpAddon.id, "NOT-gmp-gmpopenh264");
do_check_eq(gmpAddon.URL, "http://127.0.0.1:8011/NOT-gmp-gmpopenh264-1.1.zip");
do_check_eq(gmpAddon.hashFunction, "sha512");
do_check_eq(gmpAddon.hashValue, "141592656f645eefc2b99af17bae396636ace1e33d079c88de715177584e2aee");
do_check_eq(gmpAddon.version, "9.1");
do_check_true(gmpAddon.isValid);
do_check_false(gmpAddon.isInstalled);
for (let i = 2; i < res.gmpAddons.length; i++) {
do_check_false(res.gmpAddons[i].isValid);
do_check_false(res.gmpAddons[i].isInstalled);
}
installManager.uninit();
});
/**
* Tests that checking for addons when there are also updates available
* works as expected.
*/
add_task(function* test_checkForAddons_updatesWithAddons() {
let responseXML =
"<?xml version=\"1.0\"?>" +
" <updates>" +
" <update type=\"minor\" displayVersion=\"33.0a1\" appVersion=\"33.0a1\" platformVersion=\"33.0a1\" buildID=\"20140628030201\">" +
" <patch type=\"complete\" URL=\"http://ftp.mozilla.org/pub/mozilla.org/firefox/nightly/2014/06/2014-06-28-03-02-01-mozilla-central/firefox-33.0a1.en-US.mac.complete.mar\" hashFunction=\"sha512\" hashValue=\"f3f90d71dff03ae81def80e64bba3e4569da99c9e15269f731c2b167c4fc30b3aed9f5fee81c19614120230ca333e73a5e7def1b8e45d03135b2069c26736219\" size=\"85249896\"/>" +
" </update>" +
" <addons>" +
" <addon id=\"gmp-gmpopenh264\"" +
" URL=\"http://127.0.0.1:8011/gmp-gmpopenh264-1.1.zip\"" +
" hashFunction=\"sha256\"" +
" hashValue=\"1118b90d6f645eefc2b99af17bae396636ace1e33d079c88de715177584e2aee\"" +
" version=\"1.1\"/>" +
" </addons>" +
"</updates>"
overrideXHR(200, responseXML);
let installManager = new GMPInstallManager();
let res = yield installManager.checkForAddons();
do_check_eq(res.gmpAddons.length, 1);
let gmpAddon = res.gmpAddons[0];
do_check_eq(gmpAddon.id, "gmp-gmpopenh264");
do_check_eq(gmpAddon.URL, "http://127.0.0.1:8011/gmp-gmpopenh264-1.1.zip");
do_check_eq(gmpAddon.hashFunction, "sha256");
do_check_eq(gmpAddon.hashValue, "1118b90d6f645eefc2b99af17bae396636ace1e33d079c88de715177584e2aee");
do_check_eq(gmpAddon.version, "1.1");
do_check_true(gmpAddon.isValid);
do_check_false(gmpAddon.isInstalled);
installManager.uninit();
});
/**
* Tests that installing found addons works as expected
*/
function* test_checkForAddons_installAddon(id, includeSize, wantInstallReject) {
do_print("Running installAddon for id: " + id +
", includeSize: " + includeSize +
" and wantInstallReject: " + wantInstallReject);
let httpServer = new HttpServer();
let dir = FileUtils.getDir("TmpD", [], true);
httpServer.registerDirectory("/", dir);
httpServer.start(-1);
let testserverPort = httpServer.identity.primaryPort;
let zipFileName = "test_" + id + "_GMP.zip";
let zipURL = URL_HOST + ":" + testserverPort + "/" + zipFileName;
do_print("zipURL: " + zipURL);
let data = "e~=0.5772156649";
let zipFile = createNewZipFile(zipFileName, data);
let hashFunc = "sha256";
let expectedDigest = yield computeHash(hashFunc, zipFile.path);
let fileSize = zipFile.fileSize;
if (wantInstallReject) {
fileSize = 1;
}
let responseXML =
"<?xml version=\"1.0\"?>" +
"<updates>" +
" <addons>" +
" <addon id=\"" + id + "-gmp-gmpopenh264\"" +
" URL=\"" + zipURL + "\"" +
" hashFunction=\"" + hashFunc + "\"" +
" hashValue=\"" + expectedDigest + "\"" +
(includeSize ? " size=\"" + fileSize + "\"" : "") +
" version=\"1.1\"/>" +
" </addons>" +
"</updates>"
overrideXHR(200, responseXML);
let installManager = new GMPInstallManager();
let res = yield installManager.checkForAddons();
do_check_eq(res.gmpAddons.length, 1);
let gmpAddon = res.gmpAddons[0];
do_check_false(gmpAddon.isInstalled);
try {
let extractedPaths = yield installManager.installAddon(gmpAddon);
if (wantInstallReject) {
do_check_true(false); // installAddon() should have thrown.
}
do_check_eq(extractedPaths.length, 1);
let extractedPath = extractedPaths[0];
do_print("Extracted path: " + extractedPath);
let extractedFile = Cc["@mozilla.org/file/local;1"].
createInstance(Ci.nsIFile);
extractedFile.initWithPath(extractedPath);
do_check_true(extractedFile.exists());
let readData = readStringFromFile(extractedFile);
do_check_eq(readData, data);
// Make sure the prefs are set correctly
do_check_true(!!GMPScope.GMPPrefs.get(
GMPScope.GMPPrefs.KEY_PLUGIN_LAST_UPDATE, "", gmpAddon.id));
do_check_eq(GMPScope.GMPPrefs.get(GMPScope.GMPPrefs.KEY_PLUGIN_VERSION, "",
gmpAddon.id),
"1.1");
do_check_eq(GMPScope.GMPPrefs.get(GMPScope.GMPPrefs.KEY_PLUGIN_ABI, "",
gmpAddon.id),
UpdateUtils.ABI);
// Make sure it reports as being installed
do_check_true(gmpAddon.isInstalled);
// Cleanup
extractedFile.parent.remove(true);
zipFile.remove(false);
httpServer.stop(function() {});
installManager.uninit();
} catch (ex) {
zipFile.remove(false);
if (!wantInstallReject) {
do_throw("install update should not reject " + ex.message);
}
}
}
add_task(test_checkForAddons_installAddon.bind(null, "1", true, false));
add_task(test_checkForAddons_installAddon.bind(null, "2", false, false));
add_task(test_checkForAddons_installAddon.bind(null, "3", true, true));
/**
* Tests simpleCheckAndInstall when autoupdate is disabled for a GMP
*/
add_task(function* test_simpleCheckAndInstall_autoUpdateDisabled() {
GMPScope.GMPPrefs.set(GMPScope.GMPPrefs.KEY_PLUGIN_AUTOUPDATE, false, GMPScope.OPEN_H264_ID);
let responseXML =
"<?xml version=\"1.0\"?>" +
"<updates>" +
" <addons>" +
// valid openh264
" <addon id=\"gmp-gmpopenh264\"" +
" URL=\"http://127.0.0.1:8011/gmp-gmpopenh264-1.1.zip\"" +
" hashFunction=\"sha256\"" +
" hashValue=\"1118b90d6f645eefc2b99af17bae396636ace1e33d079c88de715177584e2aee\"" +
" version=\"1.1\"/>" +
" </addons>" +
"</updates>"
overrideXHR(200, responseXML);
let installManager = new GMPInstallManager();
let result = yield installManager.simpleCheckAndInstall();
do_check_eq(result.status, "nothing-new-to-install");
Preferences.reset(GMPScope.GMPPrefs.KEY_UPDATE_LAST_CHECK);
GMPScope.GMPPrefs.set(GMPScope.GMPPrefs.KEY_PLUGIN_AUTOUPDATE, true, GMPScope.OPEN_H264_ID);
});
/**
* Tests simpleCheckAndInstall nothing to install
*/
add_task(function* test_simpleCheckAndInstall_nothingToInstall() {
let responseXML =
"<?xml version=\"1.0\"?>" +
"<updates>" +
"</updates>"
overrideXHR(200, responseXML);
let installManager = new GMPInstallManager();
let result = yield installManager.simpleCheckAndInstall();
do_check_eq(result.status, "nothing-new-to-install");
});
/**
* Tests simpleCheckAndInstall too frequent
*/
add_task(function* test_simpleCheckAndInstall_tooFrequent() {
let responseXML =
"<?xml version=\"1.0\"?>" +
"<updates>" +
"</updates>"
overrideXHR(200, responseXML);
let installManager = new GMPInstallManager();
let result = yield installManager.simpleCheckAndInstall();
do_check_eq(result.status, "too-frequent-no-check");
});
/**
* Tests that installing addons when there is no server works as expected
*/
add_test(function test_installAddon_noServer() {
let dir = FileUtils.getDir("TmpD", [], true);
let zipFileName = "test_GMP.zip";
let zipURL = URL_HOST + ":0/" + zipFileName;
let data = "e~=0.5772156649";
let zipFile = createNewZipFile(zipFileName, data);
let responseXML =
"<?xml version=\"1.0\"?>" +
"<updates>" +
" <addons>" +
" <addon id=\"gmp-gmpopenh264\"" +
" URL=\"" + zipURL + "\"" +
" hashFunction=\"sha256\"" +
" hashValue=\"11221cbda000347b054028b527a60e578f919cb10f322ef8077d3491c6fcb474\"" +
" version=\"1.1\"/>" +
" </addons>" +
"</updates>"
overrideXHR(200, responseXML);
let installManager = new GMPInstallManager();
let checkPromise = installManager.checkForAddons();
checkPromise.then(res => {
do_check_eq(res.gmpAddons.length, 1);
let gmpAddon = res.gmpAddons[0];
GMPInstallManager.overrideLeaveDownloadedZip = true;
let installPromise = installManager.installAddon(gmpAddon);
installPromise.then(extractedPaths => {
do_throw("No server for install should reject");
}, err => {
do_check_true(!!err);
installManager.uninit();
run_next_test();
});
}, () => {
do_throw("check should not reject for install no server");
});
});
/**
* Returns the read stream into a string
*/
function readStringFromInputStream(inputStream) {
let sis = Cc["@mozilla.org/scriptableinputstream;1"].
createInstance(Ci.nsIScriptableInputStream);
sis.init(inputStream);
let text = sis.read(sis.available());
sis.close();
return text;
}
/**
* Reads a string of text from a file.
* This function only works with ASCII text.
*/
function readStringFromFile(file) {
if (!file.exists()) {
do_print("readStringFromFile - file doesn't exist: " + file.path);
return null;
}
let fis = Cc["@mozilla.org/network/file-input-stream;1"].
createInstance(Ci.nsIFileInputStream);
fis.init(file, FileUtils.MODE_RDONLY, FileUtils.PERMS_FILE, 0);
return readStringFromInputStream(fis);
}
/**
* Bare bones XMLHttpRequest implementation for testing onprogress, onerror,
* and onload nsIDomEventListener handleEvent.
*/
function makeHandler(aVal) {
if (typeof aVal == "function")
return { handleEvent: aVal };
return aVal;
}
/**
* Constructs a mock xhr which is used for testing different aspects
* of responses.
*/
function xhr(inputStatus, inputResponse, options) {
this.inputStatus = inputStatus;
this.inputResponse = inputResponse;
this.status = 0;
this.responseXML = null;
this._aborted = false;
this._onabort = null;
this._onprogress = null;
this._onerror = null;
this._onload = null;
this._onloadend = null;
this._ontimeout = null;
this._url = null;
this._method = null;
this._timeout = 0;
this._notified = false;
this._options = options || {};
}
xhr.prototype = {
overrideMimeType: function(aMimetype) { },
setRequestHeader: function(aHeader, aValue) { },
status: null,
channel: { set notificationCallbacks(aVal) { } },
open: function(aMethod, aUrl) {
this.channel.originalURI = Services.io.newURI(aUrl, null, null);
this._method = aMethod; this._url = aUrl;
},
abort: function() {
this._dropRequest = true;
this._notify(["abort", "loadend"]);
},
responseXML: null,
responseText: null,
send: function(aBody) {
do_execute_soon(function() {
try {
if (this._options.dropRequest) {
if (this._timeout > 0 && this._options.timeout) {
this._notify(["timeout", "loadend"]);
}
return;
}
this.status = this.inputStatus;
this.responseText = this.inputResponse;
try {
let parser = Cc["@mozilla.org/xmlextras/domparser;1"].
createInstance(Ci.nsIDOMParser);
this.responseXML = parser.parseFromString(this.inputResponse,
"application/xml");
} catch (e) {
this.responseXML = null;
}
if (this.inputStatus === 200) {
this._notify(["load", "loadend"]);
} else {
this._notify(["error", "loadend"]);
}
} catch (ex) {
do_throw(ex);
}
}.bind(this));
},
set onabort(aValue) { this._onabort = makeHandler(aValue); },
get onabort() { return this._onabort; },
set onprogress(aValue) { this._onprogress = makeHandler(aValue); },
get onprogress() { return this._onprogress; },
set onerror(aValue) { this._onerror = makeHandler(aValue); },
get onerror() { return this._onerror; },
set onload(aValue) { this._onload = makeHandler(aValue); },
get onload() { return this._onload; },
set onloadend(aValue) { this._onloadend = makeHandler(aValue); },
get onloadend() { return this._onloadend; },
set ontimeout(aValue) { this._ontimeout = makeHandler(aValue); },
get ontimeout() { return this._ontimeout; },
set timeout(aValue) { this._timeout = aValue; },
_notify: function(events) {
if (this._notified) {
return;
}
this._notified = true;
for (let item of events) {
let k = "on" + item;
if (this[k]) {
do_print("Notifying " + item);
let e = {
target: this,
type: item,
};
this[k](e);
} else {
do_print("Notifying " + item + ", but there are no listeners");
}
}
},
addEventListener: function(aEvent, aValue, aCapturing) {
eval("this._on" + aEvent + " = aValue");
},
flags: Ci.nsIClassInfo.SINGLETON,
getScriptableHelper: () => null,
getInterfaces: function(aCount) {
let interfaces = [Ci.nsISupports];
aCount.value = interfaces.length;
return interfaces;
},
classDescription: "XMLHttpRequest",
contractID: "@mozilla.org/xmlextras/xmlhttprequest;1",
classID: Components.ID("{c9b37f43-4278-4304-a5e0-600991ab08cb}"),
createInstance: function(aOuter, aIID) {
if (aOuter == null)
return this.QueryInterface(aIID);
throw Cr.NS_ERROR_NO_AGGREGATION;
},
QueryInterface: function(aIID) {
if (aIID.equals(Ci.nsIClassInfo) ||
aIID.equals(Ci.nsISupports))
return this;
throw Cr.NS_ERROR_NO_INTERFACE;
},
get wrappedJSObject() { return this; }
};
/**
* Helper used to overrideXHR requests (no matter to what URL) with the
* specified status and response.
* @param status The status you want to get back when an XHR request is made
* @param response The response you want to get back when an XHR request is made
*/
function overrideXHR(status, response, options) {
overrideXHR.myxhr = new xhr(status, response, options);
ProductAddonCheckerScope.CreateXHR = function() {
return overrideXHR.myxhr;
};
return overrideXHR.myxhr;
}
/**
* Creates a new zip file containing a file with the specified data
* @param zipName The name of the zip file
* @param data The data to go inside the zip for the filename entry1.info
*/
function createNewZipFile(zipName, data) {
// Create a zip file which will be used for extracting
let stream = Cc["@mozilla.org/io/string-input-stream;1"].
createInstance(Ci.nsIStringInputStream);
stream.setData(data, data.length);
let zipWriter = Cc["@mozilla.org/zipwriter;1"].
createInstance(Components.interfaces.nsIZipWriter);
let zipFile = FileUtils.getFile("TmpD", [zipName]);
if (zipFile.exists()) {
zipFile.remove(false);
}
// From prio.h
const PR_RDWR = 0x04;
const PR_CREATE_FILE = 0x08;
const PR_TRUNCATE = 0x20;
zipWriter.open(zipFile, PR_RDWR | PR_CREATE_FILE | PR_TRUNCATE);
zipWriter.addEntryStream("entry1.info", Date.now(),
Ci.nsIZipWriter.COMPRESSION_BEST, stream, false);
zipWriter.close();
stream.close();
do_print("zip file created on disk at: " + zipFile.path);
return zipFile;
}

View file

@ -0,0 +1,257 @@
/* Any copyright is dedicated to the Public Domain.
http://creativecommons.org/publicdomain/zero/1.0/ */
Components.utils.import("resource://gre/modules/Http.jsm");
Components.utils.import("resource://testing-common/httpd.js");
const BinaryInputStream = Components.Constructor("@mozilla.org/binaryinputstream;1",
"nsIBinaryInputStream", "setInputStream");
var server;
const kDefaultServerPort = 9000;
const kSuccessPath = "/success";
const kBaseUrl = "http://localhost:" + kDefaultServerPort;
const kSuccessUrl = kBaseUrl + kSuccessPath;
const kPostPath = "/post";
const kPostUrl = kBaseUrl + kPostPath;
const kPostDataSent = [["foo", "bar"], ["complex", "!*()@"]];
const kPostDataReceived = "foo=bar&complex=%21%2A%28%29%40";
const kPostMimeTypeReceived = "application/x-www-form-urlencoded; charset=utf-8";
const kJsonPostPath = "/json_post";
const kJsonPostUrl = kBaseUrl + kJsonPostPath;
const kJsonPostData = JSON.stringify(kPostDataSent);
const kJsonPostMimeType = "application/json";
const kPutPath = "/put";
const kPutUrl = kBaseUrl + kPutPath;
const kPutDataSent = [["P", "NP"]];
const kPutDataReceived = "P=NP";
const kGetPath = "/get";
const kGetUrl = kBaseUrl + kGetPath;
function successResult(aRequest, aResponse) {
aResponse.setStatusLine(null, 200, "OK");
aResponse.setHeader("Content-Type", "application/json");
aResponse.write("Success!");
}
function getDataChecker(aExpectedMethod, aExpectedData, aExpectedMimeType = null) {
return function(aRequest, aResponse) {
let body = new BinaryInputStream(aRequest.bodyInputStream);
let bytes = [];
let avail;
while ((avail = body.available()) > 0)
Array.prototype.push.apply(bytes, body.readByteArray(avail));
do_check_eq(aRequest.method, aExpectedMethod);
// Checking if the Content-Type is as expected.
if (aExpectedMimeType) {
let contentType = aRequest.getHeader("Content-Type");
do_check_eq(contentType, aExpectedMimeType);
}
var data = String.fromCharCode.apply(null, bytes);
do_check_eq(data, aExpectedData);
aResponse.setStatusLine(null, 200, "OK");
aResponse.setHeader("Content-Type", "application/json");
aResponse.write("Success!");
}
}
add_test(function test_successCallback() {
do_test_pending();
let options = {
onLoad: function(aResponse) {
do_check_eq(aResponse, "Success!");
do_test_finished();
run_next_test();
},
onError: function(e) {
do_check_true(false);
do_test_finished();
run_next_test();
}
}
httpRequest(kSuccessUrl, options);
});
add_test(function test_errorCallback() {
do_test_pending();
let options = {
onSuccess: function(aResponse) {
do_check_true(false);
do_test_finished();
run_next_test();
},
onError: function(e, aResponse) {
do_check_eq(e, "404 - Not Found");
do_test_finished();
run_next_test();
}
}
httpRequest(kBaseUrl + "/failure", options);
});
add_test(function test_PostData() {
do_test_pending();
let options = {
onLoad: function(aResponse) {
do_check_eq(aResponse, "Success!");
do_test_finished();
run_next_test();
},
onError: function(e) {
do_check_true(false);
do_test_finished();
run_next_test();
},
postData: kPostDataSent
}
httpRequest(kPostUrl, options);
});
add_test(function test_PutData() {
do_test_pending();
let options = {
method: "PUT",
onLoad: function(aResponse) {
do_check_eq(aResponse, "Success!");
do_test_finished();
run_next_test();
},
onError: function(e) {
do_check_true(false);
do_test_finished();
run_next_test();
},
postData: kPutDataSent
}
httpRequest(kPutUrl, options);
});
add_test(function test_GetData() {
do_test_pending();
let options = {
onLoad: function(aResponse) {
do_check_eq(aResponse, "Success!");
do_test_finished();
run_next_test();
},
onError: function(e) {
do_check_true(false);
do_test_finished();
run_next_test();
},
postData: null
}
httpRequest(kGetUrl, options);
});
add_test(function test_OptionalParameters() {
let options = {
onLoad: null,
onError: null,
logger: null
};
// Just make sure that nothing throws when doing this (i.e. httpRequest
// doesn't try to access null options).
httpRequest(kGetUrl, options);
run_next_test();
});
/**
* Makes sure that httpRequest API allows setting a custom Content-Type header
* for POST requests when data is a string.
*/
add_test(function test_CustomContentTypeOnPost() {
do_test_pending();
// Preparing the request parameters.
let options = {
onLoad: function(aResponse) {
do_check_eq(aResponse, "Success!");
do_test_finished();
run_next_test();
},
onError: function(e) {
do_check_true(false);
do_test_finished();
run_next_test();
},
postData: kJsonPostData,
// Setting a custom Content-Type header.
headers: [['Content-Type', "application/json"]]
}
// Firing the request.
httpRequest(kJsonPostUrl, options);
});
/**
* Ensures that the httpRequest API provides a way to override the response
* MIME type.
*/
add_test(function test_OverrideMimeType() {
do_test_pending();
// Preparing the request parameters.
const kMimeType = 'text/xml; charset=UTF-8';
let options = {
onLoad: function(aResponse, xhr) {
do_check_eq(aResponse, "Success!");
// Set the expected MIME-type.
let reportedMimeType = xhr.getResponseHeader("Content-Type");
do_check_neq(reportedMimeType, kMimeType);
// responseXML should not be not null if overriding mime type succeeded.
do_check_true(xhr.responseXML != null);
do_test_finished();
run_next_test();
},
onError: function(e) {
do_check_true(false);
do_test_finished();
run_next_test();
}
};
// Firing the request.
let xhr = httpRequest(kGetUrl, options);
// Override the response MIME type.
xhr.overrideMimeType(kMimeType);
});
function run_test() {
// Set up a mock HTTP server to serve a success page.
server = new HttpServer();
server.registerPathHandler(kSuccessPath, successResult);
server.registerPathHandler(kPostPath,
getDataChecker("POST", kPostDataReceived,
kPostMimeTypeReceived));
server.registerPathHandler(kPutPath,
getDataChecker("PUT", kPutDataReceived));
server.registerPathHandler(kGetPath, getDataChecker("GET", ""));
server.registerPathHandler(kJsonPostPath,
getDataChecker("POST", kJsonPostData,
kJsonPostMimeType));
server.start(kDefaultServerPort);
run_next_test();
// Teardown.
do_register_cleanup(function() {
server.stop(function() { });
});
}

View file

@ -0,0 +1,238 @@
/* Any copyright is dedicated to the Public Domain.
* http://creativecommons.org/publicdomain/zero/1.0/ */
/*
* Tests the Integration.jsm module.
*/
"use strict";
const { classes: Cc, interfaces: Ci, utils: Cu, results: Cr } = Components;
Cu.import("resource://gre/modules/Integration.jsm", this);
Cu.import("resource://gre/modules/Services.jsm", this);
Cu.import("resource://gre/modules/Task.jsm", this);
const TestIntegration = {
value: "value",
get valueFromThis() {
return this.value;
},
get property() {
return this._property;
},
set property(value) {
this._property = value;
},
method(argument) {
this.methodArgument = argument;
return "method" + argument;
},
asyncMethod: Task.async(function* (argument) {
this.asyncMethodArgument = argument;
return "asyncMethod" + argument;
}),
};
let overrideFn = base => ({
value: "overridden-value",
get property() {
return "overridden-" + base.__lookupGetter__("property").call(this);
},
set property(value) {
base.__lookupSetter__("property").call(this, "overridden-" + value);
},
method() {
return "overridden-" + base.method.apply(this, arguments);
},
asyncMethod: Task.async(function* () {
return "overridden-" + (yield base.asyncMethod.apply(this, arguments));
}),
});
let superOverrideFn = base => ({
__proto__: base,
value: "overridden-value",
get property() {
return "overridden-" + super.property;
},
set property(value) {
super.property = "overridden-" + value;
},
method() {
return "overridden-" + super.method(...arguments);
},
asyncMethod: Task.async(function* () {
// We cannot use the "super" keyword in methods defined using "Task.async".
return "overridden-" + (yield base.asyncMethod.apply(this, arguments));
}),
});
/**
* Fails the test if the results of method invocations on the combined object
* don't match the expected results based on how many overrides are registered.
*
* @param combined
* The combined object based on the TestIntegration root.
* @param overridesCount
* Zero if the root object is not overridden, or a higher value to test
* the presence of one or more integration overrides.
*/
function* assertCombinedResults(combined, overridesCount) {
let expectedValue = overridesCount > 0 ? "overridden-value" : "value";
let prefix = "overridden-".repeat(overridesCount);
Assert.equal(combined.value, expectedValue);
Assert.equal(combined.valueFromThis, expectedValue);
combined.property = "property";
Assert.equal(combined.property, prefix.repeat(2) + "property");
combined.methodArgument = "";
Assert.equal(combined.method("-argument"), prefix + "method-argument");
Assert.equal(combined.methodArgument, "-argument");
combined.asyncMethodArgument = "";
Assert.equal(yield combined.asyncMethod("-argument"),
prefix + "asyncMethod-argument");
Assert.equal(combined.asyncMethodArgument, "-argument");
}
/**
* Fails the test if the results of method invocations on the combined object
* for the "testModule" integration point don't match the expected results based
* on how many overrides are registered.
*
* @param overridesCount
* Zero if the root object is not overridden, or a higher value to test
* the presence of one or more integration overrides.
*/
function* assertCurrentCombinedResults(overridesCount) {
let combined = Integration.testModule.getCombined(TestIntegration);
yield assertCombinedResults(combined, overridesCount);
}
/**
* Checks the initial state with no integration override functions registered.
*/
add_task(function* test_base() {
yield assertCurrentCombinedResults(0);
});
/**
* Registers and unregisters an integration override function.
*/
add_task(function* test_override() {
Integration.testModule.register(overrideFn);
yield assertCurrentCombinedResults(1);
// Registering the same function more than once has no effect.
Integration.testModule.register(overrideFn);
yield assertCurrentCombinedResults(1);
Integration.testModule.unregister(overrideFn);
yield assertCurrentCombinedResults(0);
});
/**
* Registers and unregisters more than one integration override function, of
* which one uses the prototype and the "super" keyword to access the base.
*/
add_task(function* test_override_super_multiple() {
Integration.testModule.register(overrideFn);
Integration.testModule.register(superOverrideFn);
yield assertCurrentCombinedResults(2);
Integration.testModule.unregister(overrideFn);
yield assertCurrentCombinedResults(1);
Integration.testModule.unregister(superOverrideFn);
yield assertCurrentCombinedResults(0);
});
/**
* Registers an integration override function that throws an exception, and
* ensures that this does not block other functions from being registered.
*/
add_task(function* test_override_error() {
let errorOverrideFn = base => { throw "Expected error." };
Integration.testModule.register(errorOverrideFn);
Integration.testModule.register(overrideFn);
yield assertCurrentCombinedResults(1);
Integration.testModule.unregister(errorOverrideFn);
Integration.testModule.unregister(overrideFn);
yield assertCurrentCombinedResults(0);
});
/**
* Checks that state saved using the "this" reference is preserved as a shallow
* copy when registering new integration override functions.
*/
add_task(function* test_state_preserved() {
let valueObject = { toString: () => "toString" };
let combined = Integration.testModule.getCombined(TestIntegration);
combined.property = valueObject;
Assert.ok(combined.property === valueObject);
Integration.testModule.register(overrideFn);
combined = Integration.testModule.getCombined(TestIntegration);
Assert.equal(combined.property, "overridden-toString");
Integration.testModule.unregister(overrideFn);
combined = Integration.testModule.getCombined(TestIntegration);
Assert.ok(combined.property === valueObject);
});
/**
* Checks that the combined integration objects cannot be used with XPCOM.
*
* This is limited by the fact that interfaces with the "[function]" annotation,
* for example nsIObserver, do not call the QueryInterface implementation.
*/
add_task(function* test_xpcom_throws() {
let combined = Integration.testModule.getCombined(TestIntegration);
// This calls QueryInterface because it looks for nsISupportsWeakReference.
Assert.throws(() => Services.obs.addObserver(combined, "test-topic", true),
"NS_NOINTERFACE");
});
/**
* Checks that getters defined by defineModuleGetter are able to retrieve the
* latest version of the combined integration object.
*/
add_task(function* test_defineModuleGetter() {
let objectForGetters = {};
// Test with and without the optional "symbol" parameter.
Integration.testModule.defineModuleGetter(objectForGetters,
"TestIntegration", "resource://testing-common/TestIntegration.jsm");
Integration.testModule.defineModuleGetter(objectForGetters,
"integration", "resource://testing-common/TestIntegration.jsm",
"TestIntegration");
Integration.testModule.register(overrideFn);
yield assertCombinedResults(objectForGetters.integration, 1);
yield assertCombinedResults(objectForGetters.TestIntegration, 1);
Integration.testModule.unregister(overrideFn);
yield assertCombinedResults(objectForGetters.integration, 0);
yield assertCombinedResults(objectForGetters.TestIntegration, 0);
});

View file

@ -0,0 +1,242 @@
/**
* Tests the JSONFile object.
*/
"use strict";
// Globals
const { classes: Cc, interfaces: Ci, utils: Cu, results: Cr } = Components;
Cu.import("resource://gre/modules/XPCOMUtils.jsm");
XPCOMUtils.defineLazyModuleGetter(this, "DownloadPaths",
"resource://gre/modules/DownloadPaths.jsm");
XPCOMUtils.defineLazyModuleGetter(this, "FileUtils",
"resource://gre/modules/FileUtils.jsm");
XPCOMUtils.defineLazyModuleGetter(this, "OS",
"resource://gre/modules/osfile.jsm");
XPCOMUtils.defineLazyModuleGetter(this, "JSONFile",
"resource://gre/modules/JSONFile.jsm");
let gFileCounter = Math.floor(Math.random() * 1000000);
/**
* Returns a reference to a temporary file, that is guaranteed not to exist, and
* to have never been created before.
*
* @param aLeafName
* Suggested leaf name for the file to be created.
*
* @return nsIFile pointing to a non-existent file in a temporary directory.
*
* @note It is not enough to delete the file if it exists, or to delete the file
* after calling nsIFile.createUnique, because on Windows the delete
* operation in the file system may still be pending, preventing a new
* file with the same name to be created.
*/
function getTempFile(aLeafName)
{
// Prepend a serial number to the extension in the suggested leaf name.
let [base, ext] = DownloadPaths.splitBaseNameAndExtension(aLeafName);
let leafName = base + "-" + gFileCounter + ext;
gFileCounter++;
// Get a file reference under the temporary directory for this test file.
let file = FileUtils.getFile("TmpD", [leafName]);
do_check_false(file.exists());
do_register_cleanup(function () {
if (file.exists()) {
file.remove(false);
}
});
return file;
}
const TEST_STORE_FILE_NAME = "test-store.json";
const TEST_DATA = {
number: 123,
string: "test",
object: {
prop1: 1,
prop2: 2,
},
};
// Tests
add_task(function* test_save_reload()
{
let storeForSave = new JSONFile({
path: getTempFile(TEST_STORE_FILE_NAME).path,
});
yield storeForSave.load();
do_check_true(storeForSave.dataReady);
do_check_matches(storeForSave.data, {});
Object.assign(storeForSave.data, TEST_DATA);
yield new Promise((resolve) => {
let save = storeForSave._save.bind(storeForSave);
storeForSave._save = () => {
save();
resolve();
};
storeForSave.saveSoon();
});
let storeForLoad = new JSONFile({
path: storeForSave.path,
});
yield storeForLoad.load();
Assert.deepEqual(storeForLoad.data, TEST_DATA);
});
add_task(function* test_load_sync()
{
let storeForSave = new JSONFile({
path: getTempFile(TEST_STORE_FILE_NAME).path
});
yield storeForSave.load();
Object.assign(storeForSave.data, TEST_DATA);
yield storeForSave._save();
let storeForLoad = new JSONFile({
path: storeForSave.path,
});
storeForLoad.ensureDataReady();
Assert.deepEqual(storeForLoad.data, TEST_DATA);
});
add_task(function* test_load_with_dataPostProcessor()
{
let storeForSave = new JSONFile({
path: getTempFile(TEST_STORE_FILE_NAME).path
});
yield storeForSave.load();
Object.assign(storeForSave.data, TEST_DATA);
yield storeForSave._save();
let random = Math.random();
let storeForLoad = new JSONFile({
path: storeForSave.path,
dataPostProcessor: (data) => {
Assert.deepEqual(data, TEST_DATA);
data.test = random;
return data;
},
});
yield storeForLoad.load();
do_check_eq(storeForLoad.data.test, random);
});
add_task(function* test_load_with_dataPostProcessor_fails()
{
let store = new JSONFile({
path: getTempFile(TEST_STORE_FILE_NAME).path,
dataPostProcessor: () => {
throw new Error("dataPostProcessor fails.");
},
});
yield Assert.rejects(store.load(), /dataPostProcessor fails\./);
do_check_false(store.dataReady);
});
add_task(function* test_load_sync_with_dataPostProcessor_fails()
{
let store = new JSONFile({
path: getTempFile(TEST_STORE_FILE_NAME).path,
dataPostProcessor: () => {
throw new Error("dataPostProcessor fails.");
},
});
Assert.throws(() => store.ensureDataReady(), /dataPostProcessor fails\./);
do_check_false(store.dataReady);
});
/**
* Loads data from a string in a predefined format. The purpose of this test is
* to verify that the JSON format used in previous versions can be loaded.
*/
add_task(function* test_load_string_predefined()
{
let store = new JSONFile({
path: getTempFile(TEST_STORE_FILE_NAME).path,
});
let string =
"{\"number\":123,\"string\":\"test\",\"object\":{\"prop1\":1,\"prop2\":2}}";
yield OS.File.writeAtomic(store.path, new TextEncoder().encode(string),
{ tmpPath: store.path + ".tmp" });
yield store.load();
Assert.deepEqual(store.data, TEST_DATA);
});
/**
* Loads data from a malformed JSON string.
*/
add_task(function* test_load_string_malformed()
{
let store = new JSONFile({
path: getTempFile(TEST_STORE_FILE_NAME).path,
});
let string = "{\"number\":123,\"string\":\"test\",\"object\":{\"prop1\":1,";
yield OS.File.writeAtomic(store.path, new TextEncoder().encode(string),
{ tmpPath: store.path + ".tmp" });
yield store.load();
// A backup file should have been created.
do_check_true(yield OS.File.exists(store.path + ".corrupt"));
yield OS.File.remove(store.path + ".corrupt");
// The store should be ready to accept new data.
do_check_true(store.dataReady);
do_check_matches(store.data, {});
});
/**
* Loads data from a malformed JSON string, using the synchronous initialization
* path.
*/
add_task(function* test_load_string_malformed_sync()
{
let store = new JSONFile({
path: getTempFile(TEST_STORE_FILE_NAME).path,
});
let string = "{\"number\":123,\"string\":\"test\",\"object\":{\"prop1\":1,";
yield OS.File.writeAtomic(store.path, new TextEncoder().encode(string),
{ tmpPath: store.path + ".tmp" });
store.ensureDataReady();
// A backup file should have been created.
do_check_true(yield OS.File.exists(store.path + ".corrupt"));
yield OS.File.remove(store.path + ".corrupt");
// The store should be ready to accept new data.
do_check_true(store.dataReady);
do_check_matches(store.data, {});
});

View file

@ -0,0 +1,592 @@
/* Any copyright is dedicated to the Public Domain.
http://creativecommons.org/publicdomain/zero/1.0/ */
/* eslint-disable block-spacing */
var {utils: Cu} = Components;
Cu.import("resource://gre/modules/NetUtil.jsm");
Cu.import("resource://gre/modules/osfile.jsm");
Cu.import("resource://gre/modules/Promise.jsm")
Cu.import("resource://gre/modules/Log.jsm");
var testFormatter = {
format: function format(message) {
return message.loggerName + "\t" +
message.levelDesc + "\t" +
message.message;
}
};
function MockAppender(formatter) {
Log.Appender.call(this, formatter);
this.messages = [];
}
MockAppender.prototype = {
__proto__: Log.Appender.prototype,
doAppend: function DApp_doAppend(message) {
this.messages.push(message);
}
};
function run_test() {
run_next_test();
}
add_task(function test_Logger() {
let log = Log.repository.getLogger("test.logger");
let appender = new MockAppender(new Log.BasicFormatter());
log.level = Log.Level.Debug;
appender.level = Log.Level.Info;
log.addAppender(appender);
log.info("info test");
log.debug("this should be logged but not appended.");
do_check_eq(appender.messages.length, 1);
let msgRe = /\d+\ttest.logger\t\INFO\tinfo test/;
do_check_true(msgRe.test(appender.messages[0]));
});
add_task(function test_Logger_parent() {
// Check whether parenting is correct
let grandparentLog = Log.repository.getLogger("grandparent");
let childLog = Log.repository.getLogger("grandparent.parent.child");
do_check_eq(childLog.parent.name, "grandparent");
let parentLog = Log.repository.getLogger("grandparent.parent");
do_check_eq(childLog.parent.name, "grandparent.parent");
// Check that appends are exactly in scope
let gpAppender = new MockAppender(new Log.BasicFormatter());
gpAppender.level = Log.Level.Info;
grandparentLog.addAppender(gpAppender);
childLog.info("child info test");
Log.repository.rootLogger.info("this shouldn't show up in gpAppender");
do_check_eq(gpAppender.messages.length, 1);
do_check_true(gpAppender.messages[0].indexOf("child info test") > 0);
});
add_test(function test_LoggerWithMessagePrefix() {
let log = Log.repository.getLogger("test.logger.prefix");
let appender = new MockAppender(new Log.MessageOnlyFormatter());
log.addAppender(appender);
let prefixed = Log.repository.getLoggerWithMessagePrefix(
"test.logger.prefix", "prefix: ");
log.warn("no prefix");
prefixed.warn("with prefix");
Assert.equal(appender.messages.length, 2, "2 messages were logged.");
Assert.deepEqual(appender.messages, [
"no prefix",
"prefix: with prefix",
], "Prefix logger works.");
run_next_test();
});
/*
* A utility method for checking object equivalence.
* Fields with a reqular expression value in expected will be tested
* against the corresponding value in actual. Otherwise objects
* are expected to have the same keys and equal values.
*/
function checkObjects(expected, actual) {
do_check_true(expected instanceof Object);
do_check_true(actual instanceof Object);
for (let key in expected) {
do_check_neq(actual[key], undefined);
if (expected[key] instanceof RegExp) {
do_check_true(expected[key].test(actual[key].toString()));
} else if (expected[key] instanceof Object) {
checkObjects(expected[key], actual[key]);
} else {
do_check_eq(expected[key], actual[key]);
}
}
for (let key in actual) {
do_check_neq(expected[key], undefined);
}
}
add_task(function test_StructuredLogCommands() {
let appender = new MockAppender(new Log.StructuredFormatter());
let logger = Log.repository.getLogger("test.StructuredOutput");
logger.addAppender(appender);
logger.level = Log.Level.Info;
logger.logStructured("test_message", {_message: "message string one"});
logger.logStructured("test_message", {_message: "message string two",
_level: "ERROR",
source_file: "test_Log.js"});
logger.logStructured("test_message");
logger.logStructured("test_message", {source_file: "test_Log.js",
message_position: 4});
let messageOne = {"_time": /\d+/,
"_namespace": "test.StructuredOutput",
"_level": "INFO",
"_message": "message string one",
"action": "test_message"};
let messageTwo = {"_time": /\d+/,
"_namespace": "test.StructuredOutput",
"_level": "ERROR",
"_message": "message string two",
"action": "test_message",
"source_file": "test_Log.js"};
let messageThree = {"_time": /\d+/,
"_namespace": "test.StructuredOutput",
"_level": "INFO",
"action": "test_message"};
let messageFour = {"_time": /\d+/,
"_namespace": "test.StructuredOutput",
"_level": "INFO",
"action": "test_message",
"source_file": "test_Log.js",
"message_position": 4};
checkObjects(messageOne, JSON.parse(appender.messages[0]));
checkObjects(messageTwo, JSON.parse(appender.messages[1]));
checkObjects(messageThree, JSON.parse(appender.messages[2]));
checkObjects(messageFour, JSON.parse(appender.messages[3]));
let errored = false;
try {
logger.logStructured("", {_message: "invalid message"});
} catch (e) {
errored = true;
do_check_eq(e, "An action is required when logging a structured message.");
} finally {
do_check_true(errored);
}
errored = false;
try {
logger.logStructured("message_action", "invalid params");
} catch (e) {
errored = true;
do_check_eq(e, "The params argument is required to be an object.");
} finally {
do_check_true(errored);
}
// Logging with unstructured interface should produce the same messages
// as the structured interface for these cases.
appender = new MockAppender(new Log.StructuredFormatter());
logger = Log.repository.getLogger("test.StructuredOutput1");
messageOne._namespace = "test.StructuredOutput1";
messageTwo._namespace = "test.StructuredOutput1";
logger.addAppender(appender);
logger.level = Log.Level.All;
logger.info("message string one", {action: "test_message"});
logger.error("message string two", {action: "test_message",
source_file: "test_Log.js"});
checkObjects(messageOne, JSON.parse(appender.messages[0]));
checkObjects(messageTwo, JSON.parse(appender.messages[1]));
});
add_task(function test_StorageStreamAppender() {
let appender = new Log.StorageStreamAppender(testFormatter);
do_check_eq(appender.getInputStream(), null);
// Log to the storage stream and verify the log was written and can be
// read back.
let logger = Log.repository.getLogger("test.StorageStreamAppender");
logger.addAppender(appender);
logger.info("OHAI");
let inputStream = appender.getInputStream();
let data = NetUtil.readInputStreamToString(inputStream,
inputStream.available());
do_check_eq(data, "test.StorageStreamAppender\tINFO\tOHAI\n");
// We can read it again even.
let sndInputStream = appender.getInputStream();
let sameData = NetUtil.readInputStreamToString(sndInputStream,
sndInputStream.available());
do_check_eq(data, sameData);
// Reset the appender and log some more.
appender.reset();
do_check_eq(appender.getInputStream(), null);
logger.debug("wut?!?");
inputStream = appender.getInputStream();
data = NetUtil.readInputStreamToString(inputStream,
inputStream.available());
do_check_eq(data, "test.StorageStreamAppender\tDEBUG\twut?!?\n");
});
function fileContents(path) {
let decoder = new TextDecoder();
return OS.File.read(path).then(array => {
return decoder.decode(array);
});
}
add_task(function* test_FileAppender() {
// This directory does not exist yet
let dir = OS.Path.join(do_get_profile().path, "test_Log");
do_check_false(yield OS.File.exists(dir));
let path = OS.Path.join(dir, "test_FileAppender");
let appender = new Log.FileAppender(path, testFormatter);
let logger = Log.repository.getLogger("test.FileAppender");
logger.addAppender(appender);
// Logging to a file that can't be created won't do harm.
do_check_false(yield OS.File.exists(path));
logger.info("OHAI!");
yield OS.File.makeDir(dir);
logger.info("OHAI");
yield appender._lastWritePromise;
do_check_eq((yield fileContents(path)),
"test.FileAppender\tINFO\tOHAI\n");
logger.info("OHAI");
yield appender._lastWritePromise;
do_check_eq((yield fileContents(path)),
"test.FileAppender\tINFO\tOHAI\n" +
"test.FileAppender\tINFO\tOHAI\n");
// Reset the appender and log some more.
yield appender.reset();
do_check_false(yield OS.File.exists(path));
logger.debug("O RLY?!?");
yield appender._lastWritePromise;
do_check_eq((yield fileContents(path)),
"test.FileAppender\tDEBUG\tO RLY?!?\n");
yield appender.reset();
logger.debug("1");
logger.info("2");
logger.info("3");
logger.info("4");
logger.info("5");
// Waiting on only the last promise should account for all of these.
yield appender._lastWritePromise;
// Messages ought to be logged in order.
do_check_eq((yield fileContents(path)),
"test.FileAppender\tDEBUG\t1\n" +
"test.FileAppender\tINFO\t2\n" +
"test.FileAppender\tINFO\t3\n" +
"test.FileAppender\tINFO\t4\n" +
"test.FileAppender\tINFO\t5\n");
});
add_task(function* test_BoundedFileAppender() {
let dir = OS.Path.join(do_get_profile().path, "test_Log");
if (!(yield OS.File.exists(dir))) {
yield OS.File.makeDir(dir);
}
let path = OS.Path.join(dir, "test_BoundedFileAppender");
// This appender will hold about two lines at a time.
let appender = new Log.BoundedFileAppender(path, testFormatter, 40);
let logger = Log.repository.getLogger("test.BoundedFileAppender");
logger.addAppender(appender);
logger.info("ONE");
logger.info("TWO");
yield appender._lastWritePromise;
do_check_eq((yield fileContents(path)),
"test.BoundedFileAppender\tINFO\tONE\n" +
"test.BoundedFileAppender\tINFO\tTWO\n");
logger.info("THREE");
logger.info("FOUR");
do_check_neq(appender._removeFilePromise, undefined);
yield appender._removeFilePromise;
yield appender._lastWritePromise;
do_check_eq((yield fileContents(path)),
"test.BoundedFileAppender\tINFO\tTHREE\n" +
"test.BoundedFileAppender\tINFO\tFOUR\n");
yield appender.reset();
logger.info("ONE");
logger.info("TWO");
logger.info("THREE");
logger.info("FOUR");
do_check_neq(appender._removeFilePromise, undefined);
yield appender._removeFilePromise;
yield appender._lastWritePromise;
do_check_eq((yield fileContents(path)),
"test.BoundedFileAppender\tINFO\tTHREE\n" +
"test.BoundedFileAppender\tINFO\tFOUR\n");
});
/*
* Test parameter formatting.
*/
add_task(function* log_message_with_params() {
let formatter = new Log.BasicFormatter();
function formatMessage(text, params) {
let full = formatter.format(new Log.LogMessage("test.logger", Log.Level.Warn, text, params));
return full.split("\t")[3];
}
// Strings are substituted directly.
do_check_eq(formatMessage("String is ${foo}", {foo: "bar"}),
"String is bar");
// Numbers are substituted.
do_check_eq(formatMessage("Number is ${number}", {number: 47}),
"Number is 47")
// The entire params object is JSON-formatted and substituted.
do_check_eq(formatMessage("Object is ${}", {foo: "bar"}),
'Object is {"foo":"bar"}');
// An object nested inside params is JSON-formatted and substituted.
do_check_eq(formatMessage("Sub object is ${sub}", {sub: {foo: "bar"}}),
'Sub object is {"foo":"bar"}');
// The substitution field is missing from params. Leave the placeholder behind
// to make the mistake obvious.
do_check_eq(formatMessage("Missing object is ${missing}", {}),
'Missing object is ${missing}');
// Make sure we don't treat the parameter name 'false' as a falsey value.
do_check_eq(formatMessage("False is ${false}", {false: true}),
'False is true');
// If an object has a .toJSON method, the formatter uses it.
let ob = function() {};
ob.toJSON = function() {return {sneaky: "value"}};
do_check_eq(formatMessage("JSON is ${sub}", {sub: ob}),
'JSON is {"sneaky":"value"}');
// Fall back to .toSource() if JSON.stringify() fails on an object.
ob = function() {};
ob.toJSON = function() {throw "oh noes JSON"};
do_check_eq(formatMessage("Fail is ${sub}", {sub: ob}),
'Fail is (function () {})');
// Fall back to .toString if both .toJSON and .toSource fail.
ob.toSource = function() {throw "oh noes SOURCE"};
do_check_eq(formatMessage("Fail is ${sub}", {sub: ob}),
'Fail is function () {}');
// Fall back to '[object]' if .toJSON, .toSource and .toString fail.
ob.toString = function() {throw "oh noes STRING"};
do_check_eq(formatMessage("Fail is ${sub}", {sub: ob}),
'Fail is [object]');
// If params are passed but there are no substitution in the text
// we JSON format and append the entire parameters object.
do_check_eq(formatMessage("Text with no subs", {a: "b", c: "d"}),
'Text with no subs: {"a":"b","c":"d"}');
// If we substitute one parameter but not the other,
// we ignore any params that aren't substituted.
do_check_eq(formatMessage("Text with partial sub ${a}", {a: "b", c: "d"}),
'Text with partial sub b');
// We don't format internal fields stored in params.
do_check_eq(formatMessage("Params with _ ${}", {a: "b", _c: "d", _level:20, _message:"froo",
_time:123456, _namespace:"here.there"}),
'Params with _ {"a":"b","_c":"d"}');
// Don't print an empty params holder if all params are internal.
do_check_eq(formatMessage("All params internal", {_level:20, _message:"froo",
_time:123456, _namespace:"here.there"}),
'All params internal');
// Format params with null and undefined values.
do_check_eq(formatMessage("Null ${n} undefined ${u}", {n: null, u: undefined}),
'Null null undefined undefined');
// Format params with number, bool, and Object/String type.
do_check_eq(formatMessage("number ${n} boolean ${b} boxed Boolean ${bx} String ${s}",
{n: 45, b: false, bx: new Boolean(true), s: new String("whatevs")}),
'number 45 boolean false boxed Boolean true String whatevs');
/*
* Check that errors get special formatting if they're formatted directly as
* a named param or they're the only param, but not if they're a field in a
* larger structure.
*/
let err = Components.Exception("test exception", Components.results.NS_ERROR_FAILURE);
let str = formatMessage("Exception is ${}", err);
do_check_true(str.includes('Exception is [Exception... "test exception"'));
do_check_true(str.includes("(NS_ERROR_FAILURE)"));
str = formatMessage("Exception is", err);
do_check_true(str.includes('Exception is: [Exception... "test exception"'));
str = formatMessage("Exception is ${error}", {error: err});
do_check_true(str.includes('Exception is [Exception... "test exception"'));
str = formatMessage("Exception is", {_error: err});
do_print(str);
// Exceptions buried inside objects are formatted badly.
do_check_true(str.includes('Exception is: {"_error":{}'));
// If the message text is null, the message contains only the formatted params object.
str = formatMessage(null, err);
do_check_true(str.startsWith('[Exception... "test exception"'));
// If the text is null and 'params' is a String object, the message is exactly that string.
str = formatMessage(null, new String("String in place of params"));
do_check_eq(str, "String in place of params");
// We use object.valueOf() internally; make sure a broken valueOf() method
// doesn't cause the logger to fail.
let vOf = {a: 1, valueOf: function() {throw "oh noes valueOf"}};
do_check_eq(formatMessage("Broken valueOf ${}", vOf),
'Broken valueOf ({a:1, valueOf:(function () {throw "oh noes valueOf"})})');
// Test edge cases of bad data to formatter:
// If 'params' is not an object, format it as a basic type.
do_check_eq(formatMessage("non-object no subst", 1),
'non-object no subst: 1');
do_check_eq(formatMessage("non-object all subst ${}", 2),
'non-object all subst 2');
do_check_eq(formatMessage("false no subst", false),
'false no subst: false');
do_check_eq(formatMessage("null no subst", null),
'null no subst: null');
// If 'params' is undefined and there are no substitutions expected,
// the message should still be output.
do_check_eq(formatMessage("undefined no subst", undefined),
'undefined no subst');
// If 'params' is not an object, no named substitutions can succeed;
// therefore we leave the placeholder and append the formatted params.
do_check_eq(formatMessage("non-object named subst ${junk} space", 3),
'non-object named subst ${junk} space: 3');
// If there are no params, we leave behind the placeholders in the text.
do_check_eq(formatMessage("no params ${missing}", undefined),
'no params ${missing}');
// If params doesn't contain any of the tags requested in the text,
// we leave them all behind and append the formatted params.
do_check_eq(formatMessage("object missing tag ${missing} space", {mising: "not here"}),
'object missing tag ${missing} space: {"mising":"not here"}');
// If we are given null text and no params, the resulting formatted message is empty.
do_check_eq(formatMessage(null), '');
});
/*
* If we call a log function with a non-string object in place of the text
* argument, and no parameters, treat that the same as logging empty text
* with the object argument as parameters. This makes the log useful when the
* caller does "catch(err) {logger.error(err)}"
*/
add_task(function* test_log_err_only() {
let log = Log.repository.getLogger("error.only");
let mockFormatter = { format: msg => msg };
let appender = new MockAppender(mockFormatter);
log.addAppender(appender);
/*
* Check that log.error(err) is treated the same as
* log.error(null, err) by the logMessage constructor; the formatMessage()
* tests above ensure that the combination of null text and an error object
* is formatted correctly.
*/
try {
eval("javascript syntax error");
}
catch (e) {
log.error(e);
msg = appender.messages.pop();
do_check_eq(msg.message, null);
do_check_eq(msg.params, e);
}
});
/*
* Test logStructured() messages through basic formatter.
*/
add_task(function* test_structured_basic() {
let log = Log.repository.getLogger("test.logger");
let appender = new MockAppender(new Log.BasicFormatter());
log.level = Log.Level.Info;
appender.level = Log.Level.Info;
log.addAppender(appender);
// A structured entry with no _message is treated the same as log./level/(null, params)
// except the 'action' field is added to the object.
log.logStructured("action", {data: "structure"});
do_check_eq(appender.messages.length, 1);
do_check_true(appender.messages[0].includes('{"data":"structure","action":"action"}'));
// A structured entry with _message and substitution is treated the same as
// log./level/(null, params).
log.logStructured("action", {_message: "Structured sub ${data}", data: "structure"});
do_check_eq(appender.messages.length, 2);
do_print(appender.messages[1]);
do_check_true(appender.messages[1].includes('Structured sub structure'));
});
/*
* Test that all the basic logger methods pass the message and params through to the appender.
*/
add_task(function* log_message_with_params() {
let log = Log.repository.getLogger("error.logger");
let mockFormatter = { format: msg => msg };
let appender = new MockAppender(mockFormatter);
log.addAppender(appender);
let testParams = {a:1, b:2};
log.fatal("Test fatal", testParams);
log.error("Test error", testParams);
log.warn("Test warn", testParams);
log.info("Test info", testParams);
log.config("Test config", testParams);
log.debug("Test debug", testParams);
log.trace("Test trace", testParams);
do_check_eq(appender.messages.length, 7);
for (let msg of appender.messages) {
do_check_true(msg.params === testParams);
do_check_true(msg.message.startsWith("Test "));
}
});
/*
* Check that we format JS Errors reasonably.
*/
add_task(function* format_errors() {
let pFormat = new Log.ParameterFormatter();
// Test that subclasses of Error are recognized as errors.
err = new ReferenceError("Ref Error", "ERROR_FILE", 28);
str = pFormat.format(err);
do_check_true(str.includes("ReferenceError"));
do_check_true(str.includes("ERROR_FILE:28"));
do_check_true(str.includes("Ref Error"));
// Test that JS-generated Errors are recognized and formatted.
try {
yield Promise.resolve(); // Scrambles the stack
eval("javascript syntax error");
}
catch (e) {
str = pFormat.format(e);
do_check_true(str.includes("SyntaxError: missing ;"));
// Make sure we identified it as an Error and formatted the error location as
// lineNumber:columnNumber.
do_check_true(str.includes(":1:11)"));
// Make sure that we use human-readable stack traces
// Check that the error doesn't contain any reference to "Promise.jsm" or "Task.jsm"
do_check_false(str.includes("Promise.jsm"));
do_check_false(str.includes("Task.jsm"));
do_check_true(str.includes("format_errors"));
}
});

View file

@ -0,0 +1,30 @@
print("Define some functions in well defined line positions for the test");
function foo(v) { return bar(v + 1); } // line 2
function bar(v) { return baz(v + 1); } // line 3
function baz(v) { throw new Error(v + 1); } // line 4
print("Make sure lazy constructor calling/assignment works");
Components.utils.import("resource://gre/modules/Log.jsm");
function run_test() {
print("Make sure functions, arguments, files are pretty printed in the trace");
let trace = "";
try {
foo(0);
}
catch (ex) {
trace = Log.stackTrace(ex);
}
print(`Got trace: ${trace}`);
do_check_neq(trace, "");
let bazPos = trace.indexOf("baz@test_Log_stackTrace.js:4");
let barPos = trace.indexOf("bar@test_Log_stackTrace.js:3");
let fooPos = trace.indexOf("foo@test_Log_stackTrace.js:2");
print(`String positions: ${bazPos} ${barPos} ${fooPos}`);
print("Make sure the desired messages show up");
do_check_true(bazPos >= 0);
do_check_true(barPos > bazPos);
do_check_true(fooPos > barPos);
}

View file

@ -0,0 +1,58 @@
/* Any copyright is dedicated to the Public Domain.
http://creativecommons.org/publicdomain/zero/1.0/ */
"use strict";
Components.utils.import("resource://gre/modules/MatchPattern.jsm");
Components.utils.import("resource://gre/modules/Services.jsm");
function test(url, pattern) {
let uri = Services.io.newURI(url, null, null);
let m = new MatchGlobs(pattern);
return m.matches(uri.spec);
}
function pass({url, pattern}) {
ok(test(url, pattern), `Expected match: ${JSON.stringify(pattern)}, ${url}`);
}
function fail({url, pattern}) {
ok(!test(url, pattern), `Expected no match: ${JSON.stringify(pattern)}, ${url}`);
}
function run_test() {
let moz = "http://mozilla.org";
pass({url: moz, pattern: ["*"]});
pass({url: moz, pattern: ["http://*"]}),
pass({url: moz, pattern: ["*mozilla*"]});
pass({url: moz, pattern: ["*example*", "*mozilla*"]});
pass({url: moz, pattern: ["*://*"]});
pass({url: "https://mozilla.org", pattern: ["*://*"]});
// Documentation example
pass({url: "http://www.example.com/foo/bar", pattern: ["http://???.example.com/foo/*"]});
pass({url: "http://the.example.com/foo/", pattern: ["http://???.example.com/foo/*"]});
fail({url: "http://my.example.com/foo/bar", pattern: ["http://???.example.com/foo/*"]});
fail({url: "http://example.com/foo/", pattern: ["http://???.example.com/foo/*"]});
fail({url: "http://www.example.com/foo", pattern: ["http://???.example.com/foo/*"]});
// Matches path
let path = moz + "/abc/def";
pass({url: path, pattern: ["*def"]});
pass({url: path, pattern: ["*c/d*"]});
pass({url: path, pattern: ["*org/abc*"]});
fail({url: path + "/", pattern: ["*def"]});
// Trailing slash
pass({url: moz, pattern: ["*.org/"]});
fail({url: moz, pattern: ["*.org"]});
// Wrong TLD
fail({url: moz, pattern: ["www*.m*.com/"]});
// Case sensitive
fail({url: moz, pattern: ["*.ORG/"]});
fail({url: moz, pattern: []});
}

View file

@ -0,0 +1,95 @@
/* Any copyright is dedicated to the Public Domain.
http://creativecommons.org/publicdomain/zero/1.0/ */
"use strict";
Components.utils.import("resource://gre/modules/MatchPattern.jsm");
Components.utils.import("resource://gre/modules/Services.jsm");
function test(url, pattern)
{
let uri = Services.io.newURI(url, null, null);
let m = new MatchPattern(pattern);
return m.matches(uri);
}
function pass({url, pattern})
{
do_check_true(test(url, pattern), `Expected match: ${JSON.stringify(pattern)}, ${url}`);
}
function fail({url, pattern})
{
do_check_false(test(url, pattern), `Expected no match: ${JSON.stringify(pattern)}, ${url}`);
}
function run_test()
{
// Invalid pattern.
fail({url: "http://mozilla.org", pattern: ""});
// Pattern must include trailing slash.
fail({url: "http://mozilla.org", pattern: "http://mozilla.org"});
// Protocol not allowed.
fail({url: "http://mozilla.org", pattern: "gopher://wuarchive.wustl.edu/"});
pass({url: "http://mozilla.org", pattern: "http://mozilla.org/"});
pass({url: "http://mozilla.org/", pattern: "http://mozilla.org/"});
pass({url: "http://mozilla.org/", pattern: "*://mozilla.org/"});
pass({url: "https://mozilla.org/", pattern: "*://mozilla.org/"});
fail({url: "file://mozilla.org/", pattern: "*://mozilla.org/"});
fail({url: "ftp://mozilla.org/", pattern: "*://mozilla.org/"});
fail({url: "http://mozilla.com", pattern: "http://*mozilla.com*/"});
fail({url: "http://mozilla.com", pattern: "http://mozilla.*/"});
fail({url: "http://mozilla.com", pattern: "http:/mozilla.com/"});
pass({url: "http://google.com", pattern: "http://*.google.com/"});
pass({url: "http://docs.google.com", pattern: "http://*.google.com/"});
pass({url: "http://mozilla.org:8080", pattern: "http://mozilla.org/"});
pass({url: "http://mozilla.org:8080", pattern: "*://mozilla.org/"});
fail({url: "http://mozilla.org:8080", pattern: "http://mozilla.org:8080/"});
// Now try with * in the path.
pass({url: "http://mozilla.org", pattern: "http://mozilla.org/*"});
pass({url: "http://mozilla.org/", pattern: "http://mozilla.org/*"});
pass({url: "http://mozilla.org/", pattern: "*://mozilla.org/*"});
pass({url: "https://mozilla.org/", pattern: "*://mozilla.org/*"});
fail({url: "file://mozilla.org/", pattern: "*://mozilla.org/*"});
fail({url: "http://mozilla.com", pattern: "http://mozilla.*/*"});
pass({url: "http://google.com", pattern: "http://*.google.com/*"});
pass({url: "http://docs.google.com", pattern: "http://*.google.com/*"});
// Check path stuff.
fail({url: "http://mozilla.com/abc/def", pattern: "http://mozilla.com/"});
pass({url: "http://mozilla.com/abc/def", pattern: "http://mozilla.com/*"});
pass({url: "http://mozilla.com/abc/def", pattern: "http://mozilla.com/a*f"});
pass({url: "http://mozilla.com/abc/def", pattern: "http://mozilla.com/a*"});
pass({url: "http://mozilla.com/abc/def", pattern: "http://mozilla.com/*f"});
fail({url: "http://mozilla.com/abc/def", pattern: "http://mozilla.com/*e"});
fail({url: "http://mozilla.com/abc/def", pattern: "http://mozilla.com/*c"});
fail({url: "http:///a.html", pattern: "http:///a.html"});
pass({url: "file:///foo", pattern: "file:///foo*"});
pass({url: "file:///foo/bar.html", pattern: "file:///foo*"});
pass({url: "http://mozilla.org/a", pattern: "<all_urls>"});
pass({url: "https://mozilla.org/a", pattern: "<all_urls>"});
pass({url: "ftp://mozilla.org/a", pattern: "<all_urls>"});
pass({url: "file:///a", pattern: "<all_urls>"});
fail({url: "gopher://wuarchive.wustl.edu/a", pattern: "<all_urls>"});
// Multiple patterns.
pass({url: "http://mozilla.org", pattern: ["http://mozilla.org/"]});
pass({url: "http://mozilla.org", pattern: ["http://mozilla.org/", "http://mozilla.com/"]});
pass({url: "http://mozilla.com", pattern: ["http://mozilla.org/", "http://mozilla.com/"]});
fail({url: "http://mozilla.biz", pattern: ["http://mozilla.org/", "http://mozilla.com/"]});
// Match url with fragments.
pass({url: "http://mozilla.org/base#some-fragment", pattern: "http://mozilla.org/base"});
}

View file

@ -0,0 +1,396 @@
/* Any copyright is dedicated to the Public Domain.
http://creativecommons.org/publicdomain/zero/1.0/ */
"use strict";
Components.utils.import("resource://gre/modules/MatchPattern.jsm");
Components.utils.import("resource://gre/modules/Services.jsm");
function createTestFilter({url, filters}) {
let m = new MatchURLFilters(filters);
return m.matches(url);
}
function expectPass({url, filters}) {
ok(createTestFilter({url, filters}),
`Expected match: ${JSON.stringify(filters)}, ${url}`);
}
function expectFail({url, filters}) {
ok(!createTestFilter({url, filters}),
`Expected no match: ${JSON.stringify(filters)}, ${url}`);
}
function expectThrow({url, filters, exceptionMessageContains}) {
let logData = {filters, url};
Assert.throws(
() => {
createTestFilter({url, filters});
},
exceptionMessageContains,
`Check received exception for expected message: ${JSON.stringify(logData)}`
);
}
add_task(function* test_match_url_filters() {
const shouldPass = true;
const shouldFail = true;
const shouldThrow = true;
var testCases = [
// Empty, undefined and null filters.
{shouldThrow, exceptionMessageContains: "filters array should not be empty",
filters: [], url: "http://mozilla.org", },
{shouldThrow, exceptionMessageContains: "filters should be an array",
filters: undefined, url: "http://mozilla.org"},
{shouldThrow, exceptionMessageContains: "filters should be an array",
filters: null, url: "http://mozilla.org"},
// Wrong formats (in a real webextension this will be blocked by the schema validation).
{shouldThrow, exceptionMessageContains: "filters should be an array", filters: {},
url: "http://mozilla.org"},
{shouldThrow, exceptionMessageContains: "filters should be an array",
filters: {nonExistentCriteria: true}, url: "http://mozilla.org", },
{shouldPass, filters: [{nonExistentCriteria: true}], url: "http://mozilla.org"},
// Schemes filter over various url schemes.
{shouldPass, filters: [{schemes: ["http"]}], url: "http://mozilla.org"},
{shouldPass, filters: [{schemes: ["https"]}], url: "https://mozilla.org"},
{shouldPass, filters: [{schemes: ["ftp"]}], url: "ftp://fake/ftp/url"},
{shouldPass, filters: [{schemes: ["about"]}], url: "about:blank"},
{shouldPass, filters: [{schemes: ["data"]}], url: "data:,testDataURL"},
{shouldFail, filters: [{schemes: ["http"]}], url: "ftp://fake/ftp/url"},
// Multiple schemes: pass when at least one scheme matches.
{shouldPass, filters: [{schemes: ["https", "about"]}], url: "https://mozilla.org"},
{shouldPass, filters: [{schemes: ["about", "https"]}], url: "https://mozilla.org"},
{shouldFail, filters: [{schemes: ["about", "http"]}], url: "https://mozilla.org"},
// Port filter: standard (implicit) ports.
{shouldPass, filters: [{ports: [443]}], url: "https://mozilla.org"},
{shouldPass, filters: [{ports: [80]}], url: "http://mozilla.org"},
{shouldPass, filters: [{ports: [21]}], url: "ftp://ftp.mozilla.org"},
// Port filter: schemes without a default port.
{shouldFail, filters: [{ports: [-1]}], url: "about:blank"},
{shouldFail, filters: [{ports: [-1]}], url: "data:,testDataURL"},
{shouldFail, filters: [{ports: [[1, 65535]]}], url: "about:blank"},
{shouldFail, filters: [{ports: [[1, 65535]]}], url: "data:,testDataURL"},
// Host filters (hostEquals, hostContains, hostPrefix, hostSuffix): schemes with an host.
{shouldFail, filters: [{hostEquals: ""}], url: "https://mozilla.org"},
{shouldPass, filters: [{hostEquals: null}], url: "https://mozilla.org"},
{shouldPass, filters: [{hostEquals: "mozilla.org"}], url: "https://mozilla.org"},
{shouldFail, filters: [{hostEquals: "mozilla.com"}], url: "https://mozilla.org"},
// NOTE: trying at least once another valid protocol.
{shouldPass, filters: [{hostEquals: "mozilla.org"}], url: "ftp://mozilla.org"},
{shouldFail, filters: [{hostEquals: "mozilla.com"}], url: "ftp://mozilla.org"},
{shouldPass, filters: [{hostEquals: "mozilla.org"}], url: "https://mozilla.org:8888"},
{shouldPass, filters: [{hostContains: "moz"}], url: "https://mozilla.org"},
// NOTE: an implicit '.' char is inserted into the host.
{shouldPass, filters: [{hostContains: ".moz"}], url: "https://mozilla.org"},
{shouldFail, filters: [{hostContains: "com"}], url: "https://mozilla.org"},
{shouldPass, filters: [{hostContains: ""}], url: "https://mozilla.org"},
{shouldPass, filters: [{hostContains: null}], url: "https://mozilla.org"},
{shouldPass, filters: [{hostPrefix: "moz"}], url: "https://mozilla.org"},
{shouldFail, filters: [{hostPrefix: "org"}], url: "https://mozilla.org"},
{shouldPass, filters: [{hostPrefix: ""}], url: "https://mozilla.org"},
{shouldPass, filters: [{hostPrefix: null}], url: "https://mozilla.org"},
{shouldPass, filters: [{hostSuffix: ".org"}], url: "https://mozilla.org"},
{shouldFail, filters: [{hostSuffix: "moz"}], url: "https://mozilla.org"},
{shouldPass, filters: [{hostSuffix: ""}], url: "https://mozilla.org"},
{shouldPass, filters: [{hostSuffix: null}], url: "https://mozilla.org"},
{shouldPass, filters: [{hostSuffix: "lla.org"}], url: "https://mozilla.org:8888"},
// hostEquals: urls without an host.
// TODO: should we explicitly cover hostContains, hostPrefix, hostSuffix for
// these sub-cases?
{shouldFail, filters: [{hostEquals: "blank"}], url: "about:blank"},
{shouldFail, filters: [{hostEquals: "blank"}], url: "about://blank"},
{shouldFail, filters: [{hostEquals: "testDataURL"}], url: "data:,testDataURL"},
{shouldPass, filters: [{hostEquals: ""}], url: "about:blank"},
{shouldPass, filters: [{hostEquals: ""}], url: "about://blank"},
{shouldPass, filters: [{hostEquals: ""}], url: "data:,testDataURL"},
// Path filters (pathEquals, pathContains, pathPrefix, pathSuffix).
{shouldFail, filters: [{pathEquals: ""}], url: "https://mozilla.org/test/path"},
{shouldPass, filters: [{pathEquals: null}], url: "https://mozilla.org/test/path"},
{shouldPass, filters: [{pathEquals: "/test/path"}], url: "https://mozilla.org/test/path"},
{shouldFail, filters: [{pathEquals: "/wrong/path"}], url: "https://mozilla.org/test/path"},
{shouldPass, filters: [{pathEquals: "/test/path"}], url: "https://mozilla.org:8888/test/path"},
// NOTE: trying at least once another valid protocol
{shouldPass, filters: [{pathEquals: "/test/path"}], url: "ftp://mozilla.org/test/path"},
{shouldFail, filters: [{pathEquals: "/wrong/path"}], url: "ftp://mozilla.org/test/path"},
{shouldPass, filters: [{pathContains: "st/"}], url: "https://mozilla.org/test/path"},
{shouldPass, filters: [{pathContains: "/test"}], url: "https://mozilla.org/test/path"},
{shouldFail, filters: [{pathContains: "org"}], url: "https://mozilla.org/test/path"},
{shouldPass, filters: [{pathContains: ""}], url: "https://mozilla.org/test/path"},
{shouldPass, filters: [{pathContains: null}], url: "https://mozilla.org/test/path"},
{shouldFail, filters: [{pathContains: "param"}], url: "https://mozilla.org:8888/test/path?param=1"},
{shouldFail, filters: [{pathContains: "ref"}], url: "https://mozilla.org:8888/test/path#ref"},
{shouldPass, filters: [{pathContains: "st/pa"}], url: "https://mozilla.org:8888/test/path"},
{shouldPass, filters: [{pathPrefix: "/te"}], url: "https://mozilla.org/test/path"},
{shouldFail, filters: [{pathPrefix: "org/"}], url: "https://mozilla.org/test/path"},
{shouldPass, filters: [{pathPrefix: ""}], url: "https://mozilla.org/test/path"},
{shouldPass, filters: [{pathPrefix: null}], url: "https://mozilla.org/test/path"},
{shouldPass, filters: [{pathSuffix: "/path"}], url: "https://mozilla.org/test/path"},
{shouldFail, filters: [{pathSuffix: "th/"}], url: "https://mozilla.org/test/path"},
{shouldPass, filters: [{pathSuffix: ""}], url: "https://mozilla.org/test/path"},
{shouldPass, filters: [{pathSuffix: null}], url: "https://mozilla.org/test/path"},
{shouldFail, filters: [{pathSuffix: "p=1"}], url: "https://mozilla.org:8888/test/path?p=1"},
{shouldFail, filters: [{pathSuffix: "ref"}], url: "https://mozilla.org:8888/test/path#ref"},
// Query filters (queryEquals, queryContains, queryPrefix, querySuffix).
{shouldFail, filters: [{queryEquals: ""}], url: "https://mozilla.org/?param=val"},
{shouldPass, filters: [{queryEquals: null}], url: "https://mozilla.org/?param=val"},
{shouldPass, filters: [{queryEquals: "param=val"}], url: "https://mozilla.org/?param=val"},
{shouldFail, filters: [{queryEquals: "?param=val"}], url: "https://mozilla.org/?param=val"},
{shouldFail, filters: [{queryEquals: "/path?param=val"}], url: "https://mozilla.org/path?param=val"},
// NOTE: about scheme urls cannot be matched by query.
{shouldFail, filters: [{queryEquals: "param=val"}], url: "about:blank?param=val"},
{shouldFail, filters: [{queryEquals: "param"}], url: "ftp://mozilla.org?param=val"},
{shouldPass, filters: [{queryContains: "ram"}], url: "https://mozilla.org/?param=val"},
{shouldPass, filters: [{queryContains: "=val"}], url: "https://mozilla.org/?param=val"},
{shouldFail, filters: [{queryContains: "?param"}], url: "https://mozilla.org/?param=val"},
{shouldFail, filters: [{queryContains: "path"}], url: "https://mozilla.org/path/?p=v#ref"},
{shouldPass, filters: [{queryContains: ""}], url: "https://mozilla.org/?param=val"},
{shouldPass, filters: [{queryContains: null}], url: "https://mozilla.org/?param=val"},
{shouldPass, filters: [{queryPrefix: "param"}], url: "https://mozilla.org/?param=val"},
{shouldFail, filters: [{queryPrefix: "p="}], url: "https://mozilla.org/?param=val"},
{shouldFail, filters: [{queryPrefix: "path"}], url: "https://mozilla.org/path?param=val"},
{shouldPass, filters: [{queryPrefix: ""}], url: "https://mozilla.org/?param=val"},
{shouldPass, filters: [{queryPrefix: null}], url: "https://mozilla.org/?param=val"},
{shouldPass, filters: [{querySuffix: "=val"}], url: "https://mozilla.org/?param=val"},
{shouldFail, filters: [{querySuffix: "=wrong"}], url: "https://mozilla.org/?param=val"},
{shouldPass, filters: [{querySuffix: ""}], url: "https://mozilla.org/?param=val"},
{shouldPass, filters: [{querySuffix: null}], url: "https://mozilla.org/?param=val"},
// URL filters (urlEquals, urlContains, urlPrefix, urlSuffix).
{shouldFail, filters: [{urlEquals: ""}], url: "https://mozilla.org/?p=v#ref"},
{shouldPass, filters: [{urlEquals: null}], url: "https://mozilla.org/?p=v#ref"},
{shouldPass, filters: [{urlEquals: "https://mozilla.org/?p=v#ref"}],
url: "https://mozilla.org/?p=v#ref"},
{shouldFail, filters: [{urlEquals: "https://mozilla.org/?p=v#ref2"}],
url: "https://mozilla.org/?p=v#ref"},
{shouldPass, filters: [{urlEquals: "about:blank?p=v#ref"}], url: "about:blank?p=v#ref"},
{shouldPass, filters: [{urlEquals: "ftp://mozilla.org?p=v#ref"}],
url: "ftp://mozilla.org?p=v#ref"},
{shouldPass, filters: [{urlContains: "org/?p"}], url: "https://mozilla.org/?p=v#ref"},
{shouldPass, filters: [{urlContains: "=v#ref"}], url: "https://mozilla.org/?p=v#ref"},
{shouldFail, filters: [{urlContains: "ftp"}], url: "https://mozilla.org/?p=v#ref"},
{shouldPass, filters: [{urlContains: ""}], url: "https://mozilla.org/?p=v#ref"},
{shouldPass, filters: [{urlContains: null}], url: "https://mozilla.org/?p=v#ref"},
{shouldPass, filters: [{urlPrefix: "http"}], url: "https://mozilla.org/?p=v#ref"},
{shouldFail, filters: [{urlPrefix: "moz"}], url: "https://mozilla.org/?p=v#ref"},
{shouldPass, filters: [{urlPrefix: ""}], url: "https://mozilla.org/?p=v#ref"},
{shouldPass, filters: [{urlPrefix: null}], url: "https://mozilla.org/?p=v#ref"},
{shouldPass, filters: [{urlSuffix: "#ref"}], url: "https://mozilla.org/?p=v#ref"},
{shouldFail, filters: [{urlSuffix: "=wrong"}], url: "https://mozilla.org/?p=v#ref"},
{shouldPass, filters: [{urlSuffix: ""}], url: "https://mozilla.org/?p=v#ref"},
{shouldPass, filters: [{urlSuffix: null}], url: "https://mozilla.org/?p=v#ref"},
// More url filters: urlMatches.
{shouldPass, filters: [{urlMatches: ".*://mozilla"}], url: "https://mozilla.org/?p=v#ref"},
{shouldPass, filters: [{urlMatches: ".*://mozilla"}], url: "ftp://mozilla.org/?p=v#ref"},
{shouldPass, filters: [{urlMatches: ".*://.*/\?p"}], url: "ftp://mozilla.org/?p=v#ref"},
// NOTE: urlMatches should not match the url without the ref.
{shouldFail, filters: [{urlMatches: "v#ref$"}], url: "https://mozilla.org/?p=v#ref"},
{shouldPass, filters: [{urlMatches: "^ftp"}], url: "ftp://mozilla.org/?p=v#ref"},
// More url filters: originAndPathMatches.
{shouldPass, filters: [{originAndPathMatches: ".*://mozilla"}],
url: "https://mozilla.org/?p=v#ref"},
{shouldPass, filters: [{originAndPathMatches: ".*://mozilla"}],
url: "ftp://mozilla.org/?p=v#ref"},
// NOTE: urlMatches should not match the url without the query and the ref.
{shouldFail, filters: [{originAndPathMatches: ".*://.*/\?p"}],
url: "ftp://mozilla.org/?p=v#ref"},
{shouldFail, filters: [{originAndPathMatches: "v#ref$"}],
url: "https://mozilla.org/?p=v#ref"},
{shouldPass, filters: [{originAndPathMatches: "^ftp"}],
url: "ftp://mozilla.org/?p=v#ref"},
// Filter with all criteria: all matches, none matches, some matches.
// All matches.
{shouldPass, filters: [
{
schemes: ["https", "http"],
ports: [443, 80],
hostEquals: "www.mozilla.org",
hostContains: ".moz",
hostPrefix: "www",
hostSuffix: "org",
pathEquals: "/sub/path",
pathContains: "b/p",
pathPrefix: "/sub",
pathSuffix: "/path",
queryEquals: "p=v",
queryContains: "1=",
queryPrefix: "p1",
querySuffix: "=v",
urlEquals: "https://www.mozilla.org/sub/path?p1=v#ref",
urlContains: "org/sub",
urlPrefix: "https://moz",
urlSuffix: "#ref",
urlMatches: "v#ref$",
originAndPathMatches: ".*://moz.*/"
},
], url: "https://www.mozilla.org/sub/path?p1=v#ref"},
// None matches.
{shouldFail, filters: [
{
schemes: ["http"],
ports: [80],
hostEquals: "mozilla.com",
hostContains: "www.moz",
hostPrefix: "www",
hostSuffix: "com",
pathEquals: "/wrong/path",
pathContains: "g/p",
pathPrefix: "/wrong",
pathSuffix: "/wrong",
queryEquals: "p2=v",
queryContains: "2=",
queryPrefix: "p2",
querySuffix: "=value",
urlEquals: "http://mozilla.com/sub/path?p1=v#ref",
urlContains: "com/sub",
urlPrefix: "http://moz",
urlSuffix: "#ref2",
urlMatches: "value#ref2$",
originAndPathMatches: ".*://moz.*com/"
},
], url: "https://mozilla.org/sub/path?p1=v#ref"},
// Some matches
{shouldFail, filters: [
{
schemes: ["https"],
ports: [80],
hostEquals: "mozilla.com",
hostContains: "www.moz",
hostPrefix: "www",
hostSuffix: "com",
pathEquals: "/wrong/path",
pathContains: "g/p",
pathPrefix: "/wrong",
pathSuffix: "/wrong",
queryEquals: "p2=v",
queryContains: "2=",
queryPrefix: "p2",
querySuffix: "=value",
urlEquals: "http://mozilla.com/sub/path?p1=v#ref",
urlContains: "com/sub",
urlPrefix: "http://moz",
urlSuffix: "#ref2",
urlMatches: "value#ref2$",
originAndPathMatches: ".*://moz.*com/"
},
], url: "https://mozilla.org/sub/path?p1=v#ref"},
// Filter with multiple filters: all matches, some matches, none matches.
// All matches.
{shouldPass, filters: [
{schemes: ["https", "http"]},
{ports: [443, 80]},
{hostEquals: "www.mozilla.org"},
{hostContains: ".moz"},
{hostPrefix: "www"},
{hostSuffix: "org"},
{pathEquals: "/sub/path"},
{pathContains: "b/p"},
{pathPrefix: "/sub"},
{pathSuffix: "/path"},
{queryEquals: "p=v"},
{queryContains: "1="},
{queryPrefix: "p1"},
{querySuffix: "=v"},
{urlEquals: "https://www.mozilla.org/sub/path?p1=v#ref"},
{urlContains: "org/sub"},
{urlPrefix: "https://moz"},
{urlSuffix: "#ref"},
{urlMatches: "v#ref$"},
{originAndPathMatches: ".*://moz.*/"},
], url: "https://www.mozilla.org/sub/path?p1=v#ref"},
// None matches.
{shouldFail, filters: [
{schemes: ["http"]},
{ports: [80]},
{hostEquals: "mozilla.com"},
{hostContains: "www.moz"},
{hostPrefix: "www"},
{hostSuffix: "com"},
{pathEquals: "/wrong/path"},
{pathContains: "g/p"},
{pathPrefix: "/wrong"},
{pathSuffix: "/wrong"},
{queryEquals: "p2=v"},
{queryContains: "2="},
{queryPrefix: "p2"},
{querySuffix: "=value"},
{urlEquals: "http://mozilla.com/sub/path?p1=v#ref"},
{urlContains: "com/sub"},
{urlPrefix: "http://moz"},
{urlSuffix: "#ref2"},
{urlMatches: "value#ref2$"},
{originAndPathMatches: ".*://moz.*com/"},
], url: "https://mozilla.org/sub/path?p1=v#ref"},
// Some matches.
{shouldPass, filters: [
{schemes: ["https"]},
{ports: [80]},
{hostEquals: "mozilla.com"},
{hostContains: "www.moz"},
{hostPrefix: "www"},
{hostSuffix: "com"},
{pathEquals: "/wrong/path"},
{pathContains: "g/p"},
{pathPrefix: "/wrong"},
{pathSuffix: "/wrong"},
{queryEquals: "p2=v"},
{queryContains: "2="},
{queryPrefix: "p2"},
{querySuffix: "=value"},
{urlEquals: "http://mozilla.com/sub/path?p1=v#ref"},
{urlContains: "com/sub"},
{urlPrefix: "http://moz"},
{urlSuffix: "#ref2"},
{urlMatches: "value#ref2$"},
{originAndPathMatches: ".*://moz.*com/"},
], url: "https://mozilla.org/sub/path?p1=v#ref"},
];
// Run all the the testCases defined above.
for (let currentTest of testCases) {
let {
exceptionMessageContains,
url, filters,
} = currentTest;
if (currentTest.shouldThrow) {
expectThrow({url, filters, exceptionMessageContains})
} else if (currentTest.shouldFail) {
expectFail({url, filters});
} else {
expectPass({url, filters});
}
}
});

View file

@ -0,0 +1,378 @@
/* Any copyright is dedicated to the Public Domain.
http://creativecommons.org/publicdomain/zero/1.0/ */
// See also browser/base/content/test/newtab/.
var { classes: Cc, interfaces: Ci, results: Cr, utils: Cu } = Components;
Cu.import("resource://gre/modules/NewTabUtils.jsm");
Cu.import("resource://gre/modules/Promise.jsm");
Cu.import("resource://gre/modules/Task.jsm");
Cu.import("resource://gre/modules/Services.jsm");
const PREF_NEWTAB_ENHANCED = "browser.newtabpage.enhanced";
function run_test() {
Services.prefs.setBoolPref(PREF_NEWTAB_ENHANCED, true);
run_next_test();
}
add_task(function* validCacheMidPopulation() {
let expectedLinks = makeLinks(0, 3, 1);
let provider = new TestProvider(done => done(expectedLinks));
provider.maxNumLinks = expectedLinks.length;
NewTabUtils.initWithoutProviders();
NewTabUtils.links.addProvider(provider);
let promise = new Promise(resolve => NewTabUtils.links.populateCache(resolve));
// isTopSiteGivenProvider() and getProviderLinks() should still return results
// even when cache is empty or being populated.
do_check_false(NewTabUtils.isTopSiteGivenProvider("example1.com", provider));
do_check_links(NewTabUtils.getProviderLinks(provider), []);
yield promise;
// Once the cache is populated, we get the expected results
do_check_true(NewTabUtils.isTopSiteGivenProvider("example1.com", provider));
do_check_links(NewTabUtils.getProviderLinks(provider), expectedLinks);
NewTabUtils.links.removeProvider(provider);
});
add_task(function* notifyLinkDelete() {
let expectedLinks = makeLinks(0, 3, 1);
let provider = new TestProvider(done => done(expectedLinks));
provider.maxNumLinks = expectedLinks.length;
NewTabUtils.initWithoutProviders();
NewTabUtils.links.addProvider(provider);
yield new Promise(resolve => NewTabUtils.links.populateCache(resolve));
do_check_links(NewTabUtils.links.getLinks(), expectedLinks);
// Remove a link.
let removedLink = expectedLinks[2];
provider.notifyLinkChanged(removedLink, 2, true);
let links = NewTabUtils.links._providers.get(provider);
// Check that sortedLinks is correctly updated.
do_check_links(NewTabUtils.links.getLinks(), expectedLinks.slice(0, 2));
// Check that linkMap is accurately updated.
do_check_eq(links.linkMap.size, 2);
do_check_true(links.linkMap.get(expectedLinks[0].url));
do_check_true(links.linkMap.get(expectedLinks[1].url));
do_check_false(links.linkMap.get(removedLink.url));
// Check that siteMap is correctly updated.
do_check_eq(links.siteMap.size, 2);
do_check_true(links.siteMap.has(NewTabUtils.extractSite(expectedLinks[0].url)));
do_check_true(links.siteMap.has(NewTabUtils.extractSite(expectedLinks[1].url)));
do_check_false(links.siteMap.has(NewTabUtils.extractSite(removedLink.url)));
NewTabUtils.links.removeProvider(provider);
});
add_task(function* populatePromise() {
let count = 0;
let expectedLinks = makeLinks(0, 10, 2);
let getLinksFcn = Task.async(function* (callback) {
// Should not be calling getLinksFcn twice
count++;
do_check_eq(count, 1);
yield Promise.resolve();
callback(expectedLinks);
});
let provider = new TestProvider(getLinksFcn);
NewTabUtils.initWithoutProviders();
NewTabUtils.links.addProvider(provider);
NewTabUtils.links.populateProviderCache(provider, () => {});
NewTabUtils.links.populateProviderCache(provider, () => {
do_check_links(NewTabUtils.links.getLinks(), expectedLinks);
NewTabUtils.links.removeProvider(provider);
});
});
add_task(function* isTopSiteGivenProvider() {
let expectedLinks = makeLinks(0, 10, 2);
// The lowest 2 frecencies have the same base domain.
expectedLinks[expectedLinks.length - 2].url = expectedLinks[expectedLinks.length - 1].url + "Test";
let provider = new TestProvider(done => done(expectedLinks));
provider.maxNumLinks = expectedLinks.length;
NewTabUtils.initWithoutProviders();
NewTabUtils.links.addProvider(provider);
yield new Promise(resolve => NewTabUtils.links.populateCache(resolve));
do_check_eq(NewTabUtils.isTopSiteGivenProvider("example2.com", provider), true);
do_check_eq(NewTabUtils.isTopSiteGivenProvider("example1.com", provider), false);
// Push out frecency 2 because the maxNumLinks is reached when adding frecency 3
let newLink = makeLink(3);
provider.notifyLinkChanged(newLink);
// There is still a frecent url with example2 domain, so it's still frecent.
do_check_eq(NewTabUtils.isTopSiteGivenProvider("example3.com", provider), true);
do_check_eq(NewTabUtils.isTopSiteGivenProvider("example2.com", provider), true);
// Push out frecency 3
newLink = makeLink(5);
provider.notifyLinkChanged(newLink);
// Push out frecency 4
newLink = makeLink(9);
provider.notifyLinkChanged(newLink);
// Our count reached 0 for the example2.com domain so it's no longer a frecent site.
do_check_eq(NewTabUtils.isTopSiteGivenProvider("example5.com", provider), true);
do_check_eq(NewTabUtils.isTopSiteGivenProvider("example2.com", provider), false);
NewTabUtils.links.removeProvider(provider);
});
add_task(function* multipleProviders() {
// Make each provider generate NewTabUtils.links.maxNumLinks links to check
// that no more than maxNumLinks are actually returned in the merged list.
let evenLinks = makeLinks(0, 2 * NewTabUtils.links.maxNumLinks, 2);
let evenProvider = new TestProvider(done => done(evenLinks));
let oddLinks = makeLinks(0, 2 * NewTabUtils.links.maxNumLinks - 1, 2);
let oddProvider = new TestProvider(done => done(oddLinks));
NewTabUtils.initWithoutProviders();
NewTabUtils.links.addProvider(evenProvider);
NewTabUtils.links.addProvider(oddProvider);
yield new Promise(resolve => NewTabUtils.links.populateCache(resolve));
let links = NewTabUtils.links.getLinks();
let expectedLinks = makeLinks(NewTabUtils.links.maxNumLinks,
2 * NewTabUtils.links.maxNumLinks,
1);
do_check_eq(links.length, NewTabUtils.links.maxNumLinks);
do_check_links(links, expectedLinks);
NewTabUtils.links.removeProvider(evenProvider);
NewTabUtils.links.removeProvider(oddProvider);
});
add_task(function* changeLinks() {
let expectedLinks = makeLinks(0, 20, 2);
let provider = new TestProvider(done => done(expectedLinks));
NewTabUtils.initWithoutProviders();
NewTabUtils.links.addProvider(provider);
yield new Promise(resolve => NewTabUtils.links.populateCache(resolve));
do_check_links(NewTabUtils.links.getLinks(), expectedLinks);
// Notify of a new link.
let newLink = makeLink(19);
expectedLinks.splice(1, 0, newLink);
provider.notifyLinkChanged(newLink);
do_check_links(NewTabUtils.links.getLinks(), expectedLinks);
// Notify of a link that's changed sort criteria.
newLink.frecency = 17;
expectedLinks.splice(1, 1);
expectedLinks.splice(2, 0, newLink);
provider.notifyLinkChanged({
url: newLink.url,
frecency: 17,
});
do_check_links(NewTabUtils.links.getLinks(), expectedLinks);
// Notify of a link that's changed title.
newLink.title = "My frecency is now 17";
provider.notifyLinkChanged({
url: newLink.url,
title: newLink.title,
});
do_check_links(NewTabUtils.links.getLinks(), expectedLinks);
// Notify of a new link again, but this time make it overflow maxNumLinks.
provider.maxNumLinks = expectedLinks.length;
newLink = makeLink(21);
expectedLinks.unshift(newLink);
expectedLinks.pop();
do_check_eq(expectedLinks.length, provider.maxNumLinks); // Sanity check.
provider.notifyLinkChanged(newLink);
do_check_links(NewTabUtils.links.getLinks(), expectedLinks);
// Notify of many links changed.
expectedLinks = makeLinks(0, 3, 1);
provider.notifyManyLinksChanged();
// Since _populateProviderCache() is async, we must wait until the provider's
// populate promise has been resolved.
yield NewTabUtils.links._providers.get(provider).populatePromise;
// NewTabUtils.links will now repopulate its cache
do_check_links(NewTabUtils.links.getLinks(), expectedLinks);
NewTabUtils.links.removeProvider(provider);
});
add_task(function* oneProviderAlreadyCached() {
let links1 = makeLinks(0, 10, 1);
let provider1 = new TestProvider(done => done(links1));
NewTabUtils.initWithoutProviders();
NewTabUtils.links.addProvider(provider1);
yield new Promise(resolve => NewTabUtils.links.populateCache(resolve));
do_check_links(NewTabUtils.links.getLinks(), links1);
let links2 = makeLinks(10, 20, 1);
let provider2 = new TestProvider(done => done(links2));
NewTabUtils.links.addProvider(provider2);
yield new Promise(resolve => NewTabUtils.links.populateCache(resolve));
do_check_links(NewTabUtils.links.getLinks(), links2.concat(links1));
NewTabUtils.links.removeProvider(provider1);
NewTabUtils.links.removeProvider(provider2);
});
add_task(function* newLowRankedLink() {
// Init a provider with 10 links and make its maximum number also 10.
let links = makeLinks(0, 10, 1);
let provider = new TestProvider(done => done(links));
provider.maxNumLinks = links.length;
NewTabUtils.initWithoutProviders();
NewTabUtils.links.addProvider(provider);
yield new Promise(resolve => NewTabUtils.links.populateCache(resolve));
do_check_links(NewTabUtils.links.getLinks(), links);
// Notify of a new link that's low-ranked enough not to make the list.
let newLink = makeLink(0);
provider.notifyLinkChanged(newLink);
do_check_links(NewTabUtils.links.getLinks(), links);
// Notify about the new link's title change.
provider.notifyLinkChanged({
url: newLink.url,
title: "a new title",
});
do_check_links(NewTabUtils.links.getLinks(), links);
NewTabUtils.links.removeProvider(provider);
});
add_task(function* extractSite() {
// All these should extract to the same site
[ "mozilla.org",
"m.mozilla.org",
"mobile.mozilla.org",
"www.mozilla.org",
"www3.mozilla.org",
].forEach(host => {
let url = "http://" + host;
do_check_eq(NewTabUtils.extractSite(url), "mozilla.org", "extracted same " + host);
});
// All these should extract to the same subdomain
[ "bugzilla.mozilla.org",
"www.bugzilla.mozilla.org",
].forEach(host => {
let url = "http://" + host;
do_check_eq(NewTabUtils.extractSite(url), "bugzilla.mozilla.org", "extracted eTLD+2 " + host);
});
// All these should not extract to the same site
[ "bugzilla.mozilla.org",
"bug123.bugzilla.mozilla.org",
"too.many.levels.bugzilla.mozilla.org",
"m2.mozilla.org",
"mobile30.mozilla.org",
"ww.mozilla.org",
"ww2.mozilla.org",
"wwwww.mozilla.org",
"wwwww50.mozilla.org",
"wwws.mozilla.org",
"secure.mozilla.org",
"secure10.mozilla.org",
"many.levels.deep.mozilla.org",
"just.check.in",
"192.168.0.1",
"localhost",
].forEach(host => {
let url = "http://" + host;
do_check_neq(NewTabUtils.extractSite(url), "mozilla.org", "extracted diff " + host);
});
// All these should not extract to the same site
[ "about:blank",
"file:///Users/user/file",
"chrome://browser/something",
"ftp://ftp.mozilla.org/",
].forEach(url => {
do_check_neq(NewTabUtils.extractSite(url), "mozilla.org", "extracted diff url " + url);
});
});
function TestProvider(getLinksFn) {
this.getLinks = getLinksFn;
this._observers = new Set();
}
TestProvider.prototype = {
addObserver: function (observer) {
this._observers.add(observer);
},
notifyLinkChanged: function (link, index=-1, deleted=false) {
this._notifyObservers("onLinkChanged", link, index, deleted);
},
notifyManyLinksChanged: function () {
this._notifyObservers("onManyLinksChanged");
},
_notifyObservers: function () {
let observerMethodName = arguments[0];
let args = Array.prototype.slice.call(arguments, 1);
args.unshift(this);
for (let obs of this._observers) {
if (obs[observerMethodName])
obs[observerMethodName].apply(NewTabUtils.links, args);
}
},
};
function do_check_links(actualLinks, expectedLinks) {
do_check_true(Array.isArray(actualLinks));
do_check_eq(actualLinks.length, expectedLinks.length);
for (let i = 0; i < expectedLinks.length; i++) {
let expected = expectedLinks[i];
let actual = actualLinks[i];
do_check_eq(actual.url, expected.url);
do_check_eq(actual.title, expected.title);
do_check_eq(actual.frecency, expected.frecency);
do_check_eq(actual.lastVisitDate, expected.lastVisitDate);
}
}
function makeLinks(frecRangeStart, frecRangeEnd, step) {
let links = [];
// Remember, links are ordered by frecency descending.
for (let i = frecRangeEnd; i > frecRangeStart; i -= step) {
links.push(makeLink(i));
}
return links;
}
function makeLink(frecency) {
return {
url: "http://example" + frecency + ".com/",
title: "My frecency is " + frecency,
frecency: frecency,
lastVisitDate: 0,
};
}

View file

@ -0,0 +1,96 @@
Components.utils.import("resource://gre/modules/ObjectUtils.jsm");
function run_test() {
run_next_test();
}
add_task(function* test_deepEqual() {
let deepEqual = ObjectUtils.deepEqual.bind(ObjectUtils);
// CommonJS 7.2
Assert.ok(deepEqual(new Date(2000, 3, 14), new Date(2000, 3, 14)), "deepEqual date");
Assert.ok(deepEqual(new Date(NaN), new Date(NaN)), "deepEqual invalid dates");
Assert.ok(!deepEqual(new Date(), new Date(2000, 3, 14)), "deepEqual date");
// 7.3
Assert.ok(deepEqual(/a/, /a/));
Assert.ok(deepEqual(/a/g, /a/g));
Assert.ok(deepEqual(/a/i, /a/i));
Assert.ok(deepEqual(/a/m, /a/m));
Assert.ok(deepEqual(/a/igm, /a/igm));
Assert.ok(!deepEqual(/ab/, /a/));
Assert.ok(!deepEqual(/a/g, /a/));
Assert.ok(!deepEqual(/a/i, /a/));
Assert.ok(!deepEqual(/a/m, /a/));
Assert.ok(!deepEqual(/a/igm, /a/im));
let re1 = /a/;
re1.lastIndex = 3;
Assert.ok(!deepEqual(re1, /a/));
// 7.4
Assert.ok(deepEqual(4, "4"), "deepEqual == check");
Assert.ok(deepEqual(true, 1), "deepEqual == check");
Assert.ok(!deepEqual(4, "5"), "deepEqual == check");
// 7.5
// having the same number of owned properties && the same set of keys
Assert.ok(deepEqual({a: 4}, {a: 4}));
Assert.ok(deepEqual({a: 4, b: "2"}, {a: 4, b: "2"}));
Assert.ok(deepEqual([4], ["4"]));
Assert.ok(!deepEqual({a: 4}, {a: 4, b: true}));
Assert.ok(deepEqual(["a"], {0: "a"}));
let a1 = [1, 2, 3];
let a2 = [1, 2, 3];
a1.a = "test";
a1.b = true;
a2.b = true;
a2.a = "test";
Assert.ok(!deepEqual(Object.keys(a1), Object.keys(a2)));
Assert.ok(deepEqual(a1, a2));
let nbRoot = {
toString: function() { return this.first + " " + this.last; }
};
function nameBuilder(first, last) {
this.first = first;
this.last = last;
return this;
}
nameBuilder.prototype = nbRoot;
function nameBuilder2(first, last) {
this.first = first;
this.last = last;
return this;
}
nameBuilder2.prototype = nbRoot;
let nb1 = new nameBuilder("Ryan", "Dahl");
let nb2 = new nameBuilder2("Ryan", "Dahl");
Assert.ok(deepEqual(nb1, nb2));
nameBuilder2.prototype = Object;
nb2 = new nameBuilder2("Ryan", "Dahl");
Assert.ok(!deepEqual(nb1, nb2));
// String literal + object
Assert.ok(!deepEqual("a", {}));
// Make sure deepEqual doesn't loop forever on circular refs
let b = {};
b.b = b;
let c = {};
c.b = c;
try {
Assert.ok(!deepEqual(b, c));
} catch (e) {
Assert.ok(true, "Didn't recurse infinitely.");
}
});

View file

@ -0,0 +1,29 @@
"use strict";
var {ObjectUtils} = Components.utils.import("resource://gre/modules/ObjectUtils.jsm", {});
var {PromiseTestUtils} = Components.utils.import("resource://testing-common/PromiseTestUtils.jsm", {});
add_task(function* test_strict() {
let loose = { a: 1 };
let strict = ObjectUtils.strict(loose);
loose.a; // Should not throw.
loose.b || undefined; // Should not throw.
strict.a; // Should not throw.
PromiseTestUtils.expectUncaughtRejection(/No such property: "b"/);
Assert.throws(() => strict.b, /No such property: "b"/);
"b" in strict; // Should not throw.
strict.b = 2;
strict.b; // Should not throw.
PromiseTestUtils.expectUncaughtRejection(/No such property: "c"/);
Assert.throws(() => strict.c, /No such property: "c"/);
"c" in strict; // Should not throw.
loose.c = 3;
strict.c; // Should not throw.
});
function run_test() {
run_next_test();
}

View file

@ -0,0 +1,85 @@
/* Any copyright is dedicated to the Public Domain.
* http://creativecommons.org/publicdomain/zero/1.0/
*/
// Tests that PerrmissionsUtils.jsm works as expected, including:
// * PermissionsUtils.importfromPrefs()
// <ROOT>.[whitelist|blacklist].add preferences are emptied when
// converted into permissions on startup.
const PREF_ROOT = "testpermissions.";
const TEST_PERM = "test-permission";
Components.utils.import("resource://gre/modules/Services.jsm");
Components.utils.import("resource://gre/modules/PermissionsUtils.jsm");
function run_test() {
test_importfromPrefs();
}
function test_importfromPrefs() {
// Create own preferences to test
Services.prefs.setCharPref(PREF_ROOT + "whitelist.add.EMPTY", "");
Services.prefs.setCharPref(PREF_ROOT + "whitelist.add.EMPTY2", ",");
Services.prefs.setCharPref(PREF_ROOT + "whitelist.add.TEST", "http://whitelist.example.com");
Services.prefs.setCharPref(PREF_ROOT + "whitelist.add.TEST2", "https://whitelist2-1.example.com,http://whitelist2-2.example.com:8080,about:home");
Services.prefs.setCharPref(PREF_ROOT + "whitelist.add.TEST3", "whitelist3-1.example.com,about:config"); // legacy style - host only
Services.prefs.setCharPref(PREF_ROOT + "blacklist.add.EMPTY", "");
Services.prefs.setCharPref(PREF_ROOT + "blacklist.add.TEST", "http://blacklist.example.com,");
Services.prefs.setCharPref(PREF_ROOT + "blacklist.add.TEST2", ",https://blacklist2-1.example.com,http://blacklist2-2.example.com:8080,about:mozilla");
Services.prefs.setCharPref(PREF_ROOT + "blacklist.add.TEST3", "blacklist3-1.example.com,about:preferences"); // legacy style - host only
// Check they are unknown in the permission manager prior to importing.
let whitelisted = ["http://whitelist.example.com",
"https://whitelist2-1.example.com",
"http://whitelist2-2.example.com:8080",
"http://whitelist3-1.example.com",
"https://whitelist3-1.example.com",
"about:config",
"about:home"];
let blacklisted = ["http://blacklist.example.com",
"https://blacklist2-1.example.com",
"http://blacklist2-2.example.com:8080",
"http://blacklist3-1.example.com",
"https://blacklist3-1.example.com",
"about:preferences",
"about:mozilla"];
let untouched = ["https://whitelist.example.com",
"https://blacklist.example.com",
"http://whitelist2-1.example.com",
"http://blacklist2-1.example.com",
"https://whitelist2-2.example.com:8080",
"https://blacklist2-2.example.com:8080"];
let unknown = whitelisted.concat(blacklisted).concat(untouched);
for (let url of unknown) {
let uri = Services.io.newURI(url, null, null);
do_check_eq(Services.perms.testPermission(uri, TEST_PERM), Services.perms.UNKNOWN_ACTION);
}
// Import them
PermissionsUtils.importFromPrefs(PREF_ROOT, TEST_PERM);
// Get list of preferences to check
let preferences = Services.prefs.getChildList(PREF_ROOT, {});
// Check preferences were emptied
for (let pref of preferences) {
do_check_eq(Services.prefs.getCharPref(pref), "");
}
// Check they were imported into the permissions manager
for (let url of whitelisted) {
let uri = Services.io.newURI(url, null, null);
do_check_eq(Services.perms.testPermission(uri, TEST_PERM), Services.perms.ALLOW_ACTION);
}
for (let url of blacklisted) {
let uri = Services.io.newURI(url, null, null);
do_check_eq(Services.perms.testPermission(uri, TEST_PERM), Services.perms.DENY_ACTION);
}
for (let url of untouched) {
let uri = Services.io.newURI(url, null, null);
do_check_eq(Services.perms.testPermission(uri, TEST_PERM), Services.perms.UNKNOWN_ACTION);
}
}

View file

@ -0,0 +1,378 @@
/* Any copyright is dedicated to the Public Domain.
* http://creativecommons.org/publicdomain/zero/1.0/ */
var {classes: Cc, interfaces: Ci, results: Cr, utils: Cu, manager: Cm} = Components;
Cu.import("resource://gre/modules/Preferences.jsm");
function run_test() {
run_next_test();
}
add_test(function test_set_get_pref() {
Preferences.set("test_set_get_pref.integer", 1);
do_check_eq(Preferences.get("test_set_get_pref.integer"), 1);
Preferences.set("test_set_get_pref.string", "foo");
do_check_eq(Preferences.get("test_set_get_pref.string"), "foo");
Preferences.set("test_set_get_pref.boolean", true);
do_check_eq(Preferences.get("test_set_get_pref.boolean"), true);
// Clean up.
Preferences.resetBranch("test_set_get_pref.");
run_next_test();
});
add_test(function test_set_get_branch_pref() {
let prefs = new Preferences("test_set_get_branch_pref.");
prefs.set("something", 1);
do_check_eq(prefs.get("something"), 1);
do_check_false(Preferences.has("something"));
// Clean up.
prefs.reset("something");
run_next_test();
});
add_test(function test_set_get_multiple_prefs() {
Preferences.set({ "test_set_get_multiple_prefs.integer": 1,
"test_set_get_multiple_prefs.string": "foo",
"test_set_get_multiple_prefs.boolean": true });
let [i, s, b] = Preferences.get(["test_set_get_multiple_prefs.integer",
"test_set_get_multiple_prefs.string",
"test_set_get_multiple_prefs.boolean"]);
do_check_eq(i, 1);
do_check_eq(s, "foo");
do_check_eq(b, true);
// Clean up.
Preferences.resetBranch("test_set_get_multiple_prefs.");
run_next_test();
});
add_test(function test_get_multiple_prefs_with_default_value() {
Preferences.set({ "test_get_multiple_prefs_with_default_value.a": 1,
"test_get_multiple_prefs_with_default_value.b": 2 });
let [a, b, c] = Preferences.get(["test_get_multiple_prefs_with_default_value.a",
"test_get_multiple_prefs_with_default_value.b",
"test_get_multiple_prefs_with_default_value.c"],
0);
do_check_eq(a, 1);
do_check_eq(b, 2);
do_check_eq(c, 0);
// Clean up.
Preferences.resetBranch("test_get_multiple_prefs_with_default_value.");
run_next_test();
});
add_test(function test_set_get_unicode_pref() {
Preferences.set("test_set_get_unicode_pref", String.fromCharCode(960));
do_check_eq(Preferences.get("test_set_get_unicode_pref"), String.fromCharCode(960));
// Clean up.
Preferences.reset("test_set_get_unicode_pref");
run_next_test();
});
add_test(function test_set_null_pref() {
try {
Preferences.set("test_set_null_pref", null);
// We expect this to throw, so the test is designed to fail if it doesn't.
do_check_true(false);
}
catch (ex) {}
run_next_test();
});
add_test(function test_set_undefined_pref() {
try {
Preferences.set("test_set_undefined_pref");
// We expect this to throw, so the test is designed to fail if it doesn't.
do_check_true(false);
}
catch (ex) {}
run_next_test();
});
add_test(function test_set_unsupported_pref() {
try {
Preferences.set("test_set_unsupported_pref", new Array());
// We expect this to throw, so the test is designed to fail if it doesn't.
do_check_true(false);
}
catch (ex) {}
run_next_test();
});
// Make sure that we can get a string pref that we didn't set ourselves
// (i.e. that the way we get a string pref using getComplexValue doesn't
// hork us getting a string pref that wasn't set using setComplexValue).
add_test(function test_get_string_pref() {
let svc = Cc["@mozilla.org/preferences-service;1"].
getService(Ci.nsIPrefService).
getBranch("");
svc.setCharPref("test_get_string_pref", "a normal string");
do_check_eq(Preferences.get("test_get_string_pref"), "a normal string");
// Clean up.
Preferences.reset("test_get_string_pref");
run_next_test();
});
add_test(function test_get_localized_string_pref() {
let svc = Cc["@mozilla.org/preferences-service;1"].
getService(Ci.nsIPrefService).
getBranch("");
let prefName = "test_get_localized_string_pref";
let localizedString = Cc["@mozilla.org/pref-localizedstring;1"]
.createInstance(Ci.nsIPrefLocalizedString);
localizedString.data = "a localized string";
svc.setComplexValue(prefName, Ci.nsIPrefLocalizedString, localizedString);
do_check_eq(Preferences.get(prefName, null, Ci.nsIPrefLocalizedString),
"a localized string");
// Clean up.
Preferences.reset(prefName);
run_next_test();
});
add_test(function test_set_get_number_pref() {
Preferences.set("test_set_get_number_pref", 5);
do_check_eq(Preferences.get("test_set_get_number_pref"), 5);
// Non-integer values get converted to integers.
Preferences.set("test_set_get_number_pref", 3.14159);
do_check_eq(Preferences.get("test_set_get_number_pref"), 3);
// Values outside the range -(2^31-1) to 2^31-1 overflow.
try {
Preferences.set("test_set_get_number_pref", Math.pow(2, 31));
// We expect this to throw, so the test is designed to fail if it doesn't.
do_check_true(false);
}
catch (ex) {}
// Clean up.
Preferences.reset("test_set_get_number_pref");
run_next_test();
});
add_test(function test_reset_pref() {
Preferences.set("test_reset_pref", 1);
Preferences.reset("test_reset_pref");
do_check_eq(Preferences.get("test_reset_pref"), undefined);
run_next_test();
});
add_test(function test_reset_pref_branch() {
Preferences.set("test_reset_pref_branch.foo", 1);
Preferences.set("test_reset_pref_branch.bar", 2);
Preferences.resetBranch("test_reset_pref_branch.");
do_check_eq(Preferences.get("test_reset_pref_branch.foo"), undefined);
do_check_eq(Preferences.get("test_reset_pref_branch.bar"), undefined);
run_next_test();
});
// Make sure the module doesn't throw an exception when asked to reset
// a nonexistent pref.
add_test(function test_reset_nonexistent_pref() {
Preferences.reset("test_reset_nonexistent_pref");
run_next_test();
});
// Make sure the module doesn't throw an exception when asked to reset
// a nonexistent pref branch.
add_test(function test_reset_nonexistent_pref_branch() {
Preferences.resetBranch("test_reset_nonexistent_pref_branch.");
run_next_test();
});
add_test(function test_observe_prefs_function() {
let observed = false;
let observer = function() { observed = !observed };
Preferences.observe("test_observe_prefs_function", observer);
Preferences.set("test_observe_prefs_function", "something");
do_check_true(observed);
Preferences.ignore("test_observe_prefs_function", observer);
Preferences.set("test_observe_prefs_function", "something else");
do_check_true(observed);
// Clean up.
Preferences.reset("test_observe_prefs_function");
run_next_test();
});
add_test(function test_observe_prefs_object() {
let observer = {
observed: false,
observe: function() {
this.observed = !this.observed;
}
};
Preferences.observe("test_observe_prefs_object", observer.observe, observer);
Preferences.set("test_observe_prefs_object", "something");
do_check_true(observer.observed);
Preferences.ignore("test_observe_prefs_object", observer.observe, observer);
Preferences.set("test_observe_prefs_object", "something else");
do_check_true(observer.observed);
// Clean up.
Preferences.reset("test_observe_prefs_object");
run_next_test();
});
add_test(function test_observe_prefs_nsIObserver() {
let observer = {
observed: false,
observe: function(subject, topic, data) {
this.observed = !this.observed;
do_check_true(subject instanceof Ci.nsIPrefBranch);
do_check_eq(topic, "nsPref:changed");
do_check_eq(data, "test_observe_prefs_nsIObserver");
}
};
Preferences.observe("test_observe_prefs_nsIObserver", observer);
Preferences.set("test_observe_prefs_nsIObserver", "something");
do_check_true(observer.observed);
Preferences.ignore("test_observe_prefs_nsIObserver", observer);
Preferences.set("test_observe_prefs_nsIObserver", "something else");
do_check_true(observer.observed);
// Clean up.
Preferences.reset("test_observe_prefs_nsIObserver");
run_next_test();
});
/*
add_test(function test_observe_exact_pref() {
let observed = false;
let observer = function() { observed = !observed };
Preferences.observe("test_observe_exact_pref", observer);
Preferences.set("test_observe_exact_pref.sub-pref", "something");
do_check_false(observed);
// Clean up.
Preferences.ignore("test_observe_exact_pref", observer);
Preferences.reset("test_observe_exact_pref.sub-pref");
run_next_test();
});
*/
add_test(function test_observe_value_of_set_pref() {
let observer = function(newVal) { do_check_eq(newVal, "something") };
Preferences.observe("test_observe_value_of_set_pref", observer);
Preferences.set("test_observe_value_of_set_pref", "something");
// Clean up.
Preferences.ignore("test_observe_value_of_set_pref", observer);
Preferences.reset("test_observe_value_of_set_pref");
run_next_test();
});
add_test(function test_observe_value_of_reset_pref() {
let observer = function(newVal) { do_check_true(typeof newVal == "undefined") };
Preferences.set("test_observe_value_of_reset_pref", "something");
Preferences.observe("test_observe_value_of_reset_pref", observer);
Preferences.reset("test_observe_value_of_reset_pref");
// Clean up.
Preferences.ignore("test_observe_value_of_reset_pref", observer);
run_next_test();
});
add_test(function test_has_pref() {
do_check_false(Preferences.has("test_has_pref"));
Preferences.set("test_has_pref", "foo");
do_check_true(Preferences.has("test_has_pref"));
Preferences.set("test_has_pref.foo", "foo");
Preferences.set("test_has_pref.bar", "bar");
let [hasFoo, hasBar, hasBaz] = Preferences.has(["test_has_pref.foo",
"test_has_pref.bar",
"test_has_pref.baz"]);
do_check_true(hasFoo);
do_check_true(hasBar);
do_check_false(hasBaz);
// Clean up.
Preferences.resetBranch("test_has_pref");
run_next_test();
});
add_test(function test_isSet_pref() {
// Use a pref that we know has a default value but no user-set value.
// This feels dangerous; perhaps we should create some other default prefs
// that we can use for testing.
do_check_false(Preferences.isSet("toolkit.defaultChromeURI"));
Preferences.set("toolkit.defaultChromeURI", "foo");
do_check_true(Preferences.isSet("toolkit.defaultChromeURI"));
// Clean up.
Preferences.reset("toolkit.defaultChromeURI");
run_next_test();
});
/*
add_test(function test_lock_prefs() {
// Use a pref that we know has a default value.
// This feels dangerous; perhaps we should create some other default prefs
// that we can use for testing.
do_check_false(Preferences.locked("toolkit.defaultChromeURI"));
Preferences.lock("toolkit.defaultChromeURI");
do_check_true(Preferences.locked("toolkit.defaultChromeURI"));
Preferences.unlock("toolkit.defaultChromeURI");
do_check_false(Preferences.locked("toolkit.defaultChromeURI"));
let val = Preferences.get("toolkit.defaultChromeURI");
Preferences.set("toolkit.defaultChromeURI", "test_lock_prefs");
do_check_eq(Preferences.get("toolkit.defaultChromeURI"), "test_lock_prefs");
Preferences.lock("toolkit.defaultChromeURI");
do_check_eq(Preferences.get("toolkit.defaultChromeURI"), val);
Preferences.unlock("toolkit.defaultChromeURI");
do_check_eq(Preferences.get("toolkit.defaultChromeURI"), "test_lock_prefs");
// Clean up.
Preferences.reset("toolkit.defaultChromeURI");
run_next_test();
});
*/

File diff suppressed because it is too large Load diff

View file

@ -0,0 +1,105 @@
/* Any copyright is dedicated to the Public Domain.
* http://creativecommons.org/publicdomain/zero/1.0/ */
"use strict";
Components.utils.import("resource://gre/modules/PromiseUtils.jsm");
Components.utils.import("resource://gre/modules/Timer.jsm");
Components.utils.import("resource://testing-common/PromiseTestUtils.jsm");
// Tests for PromiseUtils.jsm
function run_test() {
run_next_test();
}
// Tests for PromiseUtils.defer()
/* Tests for checking the resolve method of the Deferred object
* returned by PromiseUtils.defer() */
add_task(function* test_resolve_string() {
let def = PromiseUtils.defer();
let expected = "The promise is resolved " + Math.random();
def.resolve(expected);
let result = yield def.promise;
Assert.equal(result, expected, "def.resolve() resolves the promise");
});
/* Test for the case when undefined is passed to the resolve method
* of the Deferred object */
add_task(function* test_resolve_undefined() {
let def = PromiseUtils.defer();
def.resolve();
let result = yield def.promise;
Assert.equal(result, undefined, "resolve works with undefined as well");
});
/* Test when a pending promise is passed to the resolve method
* of the Deferred object */
add_task(function* test_resolve_pending_promise() {
let def = PromiseUtils.defer();
let expected = 100 + Math.random();
let p = new Promise((resolve, reject) => {
setTimeout(() => resolve(expected), 100);
});
def.resolve(p);
let result = yield def.promise;
Assert.equal(result, expected, "def.promise assumed the state of the passed promise");
});
/* Test when a resovled promise is passed
* to the resolve method of the Deferred object */
add_task(function* test_resolve_resolved_promise() {
let def = PromiseUtils.defer();
let expected = "Yeah resolved" + Math.random();
let p = new Promise((resolve, reject) => resolve(expected));
def.resolve(p);
let result = yield def.promise;
Assert.equal(result, expected, "Resolved promise is passed to the resolve method");
});
/* Test for the case when a rejected promise is
* passed to the resolve method */
add_task(function* test_resolve_rejected_promise() {
let def = PromiseUtils.defer();
let p = new Promise((resolve, reject) => reject(new Error("There its an rejection")));
def.resolve(p);
yield Assert.rejects(def.promise, /There its an rejection/, "Settled rejection promise passed to the resolve method");
});
/* Test for the checking the reject method of
* the Deferred object returned by PromiseUtils.defer() */
add_task(function* test_reject_Error() {
let def = PromiseUtils.defer();
def.reject(new Error("This one rejects"));
yield Assert.rejects(def.promise, /This one rejects/, "reject method with Error for rejection");
});
/* Test for the case when a pending Promise is passed to
* the reject method of Deferred object */
add_task(function* test_reject_pending_promise() {
let def = PromiseUtils.defer();
let p = new Promise((resolve, reject) => {
setTimeout(() => resolve(100), 100);
});
def.reject(p);
yield Assert.rejects(def.promise, Promise, "Rejection with a pending promise uses the passed promise itself as the reason of rejection");
});
/* Test for the case when a resolved Promise
* is passed to the reject method */
add_task(function* test_reject_resolved_promise() {
let def = PromiseUtils.defer();
let p = new Promise((resolve, reject) => resolve("This resolved"));
def.reject(p);
yield Assert.rejects(def.promise, Promise, "Rejection with a resolved promise uses the passed promise itself as the reason of rejection");
});
/* Test for the case when a rejected Promise is
* passed to the reject method */
add_task(function* test_reject_resolved_promise() {
PromiseTestUtils.expectUncaughtRejection(/This one rejects/);
let def = PromiseUtils.defer();
let p = new Promise((resolve, reject) => reject(new Error("This one rejects")));
def.reject(p);
yield Assert.rejects(def.promise, Promise, "Rejection with a rejected promise uses the passed promise itself as the reason of rejection");
});

View file

@ -0,0 +1,90 @@
/* Any copyright is dedicated to the Public Domain.
http://creativecommons.org/publicdomain/zero/1.0/ */
/**
* This file tests the Services.jsm module.
*/
// Globals
var Cc = Components.classes;
var Ci = Components.interfaces;
var Cu = Components.utils;
var Cr = Components.results;
Cu.import("resource://gre/modules/XPCOMUtils.jsm");
Cu.import("resource://gre/modules/Services.jsm");
function checkService(service, interface) {
do_print("Checking that Services." + service + " is an " + interface);
do_check_true(service in Services);
do_check_true(Services[service] instanceof interface);
}
// Tests
function run_test()
{
do_get_profile();
checkService("appShell", Ci.nsIAppShellService);
checkService("appinfo", Ci.nsIXULRuntime);
checkService("blocklist", Ci.nsIBlocklistService);
checkService("cache", Ci.nsICacheService);
checkService("cache2", Ci.nsICacheStorageService);
checkService("clipboard", Ci.nsIClipboard);
checkService("console", Ci.nsIConsoleService);
checkService("contentPrefs", Ci.nsIContentPrefService);
checkService("cookies", Ci.nsICookieManager2);
checkService("dirsvc", Ci.nsIDirectoryService);
checkService("dirsvc", Ci.nsIProperties);
checkService("DOMRequest", Ci.nsIDOMRequestService);
checkService("domStorageManager", Ci.nsIDOMStorageManager);
checkService("downloads", Ci.nsIDownloadManager);
checkService("droppedLinkHandler", Ci.nsIDroppedLinkHandler);
checkService("eTLD", Ci.nsIEffectiveTLDService);
checkService("focus", Ci.nsIFocusManager);
checkService("io", Ci.nsIIOService);
checkService("io", Ci.nsIIOService2);
checkService("locale", Ci.nsILocaleService);
checkService("logins", Ci.nsILoginManager);
checkService("obs", Ci.nsIObserverService);
checkService("perms", Ci.nsIPermissionManager);
checkService("prefs", Ci.nsIPrefBranch);
checkService("prefs", Ci.nsIPrefService);
checkService("prompt", Ci.nsIPromptService);
checkService("scriptSecurityManager", Ci.nsIScriptSecurityManager);
checkService("scriptloader", Ci.mozIJSSubScriptLoader);
checkService("startup", Ci.nsIAppStartup);
checkService("storage", Ci.mozIStorageService);
checkService("strings", Ci.nsIStringBundleService);
checkService("sysinfo", Ci.nsIPropertyBag2);
checkService("telemetry", Ci.nsITelemetry);
checkService("tm", Ci.nsIThreadManager);
checkService("uriFixup", Ci.nsIURIFixup);
checkService("urlFormatter", Ci.nsIURLFormatter);
checkService("vc", Ci.nsIVersionComparator);
checkService("wm", Ci.nsIWindowMediator);
checkService("ww", Ci.nsIWindowWatcher);
if ("nsIBrowserSearchService" in Ci) {
checkService("search", Ci.nsIBrowserSearchService);
}
if ("nsIAndroidBridge" in Ci) {
checkService("androidBridge", Ci.nsIAndroidBridge);
}
// In xpcshell tests, the "@mozilla.org/xre/app-info;1" component implements
// only the nsIXULRuntime interface, but not nsIXULAppInfo. To test the
// service getter for the latter interface, load mock app-info.
let tmp = {};
Cu.import("resource://testing-common/AppInfo.jsm", tmp);
tmp.updateAppInfo();
// We need to reload the module to update the lazy getter.
Cu.unload("resource://gre/modules/Services.jsm");
Cu.import("resource://gre/modules/Services.jsm");
checkService("appinfo", Ci.nsIXULAppInfo);
Cu.unload("resource://gre/modules/Services.jsm");
}

View file

@ -0,0 +1,38 @@
/* -*- indent-tabs-mode: nil; js-indent-level: 2 -*- */
/* This Source Code Form is subject to the terms of the Mozilla Public
* License, v. 2.0. If a copy of the MPL was not distributed with this
* file, You can obtain one at http://mozilla.org/MPL/2.0/. */
var { utils: Cu } = Components;
Cu.import("resource://gre/modules/Preferences.jsm");
Cu.import("resource://gre/modules/UpdateUtils.jsm");
const PREF_APP_UPDATE_CHANNEL = "app.update.channel";
const TEST_CHANNEL = "TestChannel";
const PREF_PARTNER_A = "app.partner.test_partner_a";
const TEST_PARTNER_A = "TestPartnerA";
const PREF_PARTNER_B = "app.partner.test_partner_b";
const TEST_PARTNER_B = "TestPartnerB";
add_task(function* test_updatechannel() {
let defaultPrefs = new Preferences({ defaultBranch: true });
let currentChannel = defaultPrefs.get(PREF_APP_UPDATE_CHANNEL);
do_check_eq(UpdateUtils.UpdateChannel, currentChannel);
do_check_eq(UpdateUtils.getUpdateChannel(true), currentChannel);
do_check_eq(UpdateUtils.getUpdateChannel(false), currentChannel);
defaultPrefs.set(PREF_APP_UPDATE_CHANNEL, TEST_CHANNEL);
do_check_eq(UpdateUtils.UpdateChannel, TEST_CHANNEL);
do_check_eq(UpdateUtils.getUpdateChannel(true), TEST_CHANNEL);
do_check_eq(UpdateUtils.getUpdateChannel(false), TEST_CHANNEL);
defaultPrefs.set(PREF_PARTNER_A, TEST_PARTNER_A);
defaultPrefs.set(PREF_PARTNER_B, TEST_PARTNER_B);
do_check_eq(UpdateUtils.UpdateChannel,
TEST_CHANNEL + "-cck-" + TEST_PARTNER_A + "-" + TEST_PARTNER_B);
do_check_eq(UpdateUtils.getUpdateChannel(true),
TEST_CHANNEL + "-cck-" + TEST_PARTNER_A + "-" + TEST_PARTNER_B);
do_check_eq(UpdateUtils.getUpdateChannel(false), TEST_CHANNEL);
});

View file

@ -0,0 +1,292 @@
/* -*- indent-tabs-mode: nil; js-indent-level: 2 -*- */
/* This Source Code Form is subject to the terms of the Mozilla Public
* License, v. 2.0. If a copy of the MPL was not distributed with this
* file, You can obtain one at http://mozilla.org/MPL/2.0/. */
var { classes: Cc, interfaces: Ci, utils: Cu } = Components;
Cu.import("resource://gre/modules/UpdateUtils.jsm");
Cu.import("resource://gre/modules/Services.jsm");
Cu.import("resource://gre/modules/AppConstants.jsm");
Cu.import("resource://testing-common/AppInfo.jsm");
Cu.import("resource://gre/modules/ctypes.jsm");
const PREF_APP_UPDATE_CHANNEL = "app.update.channel";
const PREF_APP_PARTNER_BRANCH = "app.partner.";
const PREF_DISTRIBUTION_ID = "distribution.id";
const PREF_DISTRIBUTION_VERSION = "distribution.version";
const URL_PREFIX = "http://localhost/";
const MSG_SHOULD_EQUAL = " should equal the expected value";
updateAppInfo();
const gAppInfo = getAppInfo();
const gDefaultPrefBranch = Services.prefs.getDefaultBranch(null);
function setUpdateChannel(aChannel) {
gDefaultPrefBranch.setCharPref(PREF_APP_UPDATE_CHANNEL, aChannel);
}
function getServicePack() {
// NOTE: This function is a helper function and not a test. Thus,
// it uses throw() instead of do_throw(). Any tests that use this function
// should catch exceptions thrown in this function and deal with them
// appropriately (usually by calling do_throw).
const BYTE = ctypes.uint8_t;
const WORD = ctypes.uint16_t;
const DWORD = ctypes.uint32_t;
const WCHAR = ctypes.char16_t;
const BOOL = ctypes.int;
// This structure is described at:
// http://msdn.microsoft.com/en-us/library/ms724833%28v=vs.85%29.aspx
const SZCSDVERSIONLENGTH = 128;
const OSVERSIONINFOEXW = new ctypes.StructType('OSVERSIONINFOEXW',
[
{dwOSVersionInfoSize: DWORD},
{dwMajorVersion: DWORD},
{dwMinorVersion: DWORD},
{dwBuildNumber: DWORD},
{dwPlatformId: DWORD},
{szCSDVersion: ctypes.ArrayType(WCHAR, SZCSDVERSIONLENGTH)},
{wServicePackMajor: WORD},
{wServicePackMinor: WORD},
{wSuiteMask: WORD},
{wProductType: BYTE},
{wReserved: BYTE}
]);
let kernel32 = ctypes.open("kernel32");
try {
let GetVersionEx = kernel32.declare("GetVersionExW",
ctypes.default_abi,
BOOL,
OSVERSIONINFOEXW.ptr);
let winVer = OSVERSIONINFOEXW();
winVer.dwOSVersionInfoSize = OSVERSIONINFOEXW.size;
if (0 === GetVersionEx(winVer.address())) {
// Using "throw" instead of "do_throw" (see NOTE above)
throw ("Failure in GetVersionEx (returned 0)");
}
return winVer.wServicePackMajor + "." + winVer.wServicePackMinor;
} finally {
kernel32.close();
}
}
function getProcArchitecture() {
// NOTE: This function is a helper function and not a test. Thus,
// it uses throw() instead of do_throw(). Any tests that use this function
// should catch exceptions thrown in this function and deal with them
// appropriately (usually by calling do_throw).
const WORD = ctypes.uint16_t;
const DWORD = ctypes.uint32_t;
// This structure is described at:
// http://msdn.microsoft.com/en-us/library/ms724958%28v=vs.85%29.aspx
const SYSTEM_INFO = new ctypes.StructType('SYSTEM_INFO',
[
{wProcessorArchitecture: WORD},
{wReserved: WORD},
{dwPageSize: DWORD},
{lpMinimumApplicationAddress: ctypes.voidptr_t},
{lpMaximumApplicationAddress: ctypes.voidptr_t},
{dwActiveProcessorMask: DWORD.ptr},
{dwNumberOfProcessors: DWORD},
{dwProcessorType: DWORD},
{dwAllocationGranularity: DWORD},
{wProcessorLevel: WORD},
{wProcessorRevision: WORD}
]);
let kernel32 = ctypes.open("kernel32");
try {
let GetNativeSystemInfo = kernel32.declare("GetNativeSystemInfo",
ctypes.default_abi,
ctypes.void_t,
SYSTEM_INFO.ptr);
let sysInfo = SYSTEM_INFO();
// Default to unknown
sysInfo.wProcessorArchitecture = 0xffff;
GetNativeSystemInfo(sysInfo.address());
switch (sysInfo.wProcessorArchitecture) {
case 9:
return "x64";
case 6:
return "IA64";
case 0:
return "x86";
default:
// Using "throw" instead of "do_throw" (see NOTE above)
throw ("Unknown architecture returned from GetNativeSystemInfo: " + sysInfo.wProcessorArchitecture);
}
} finally {
kernel32.close();
}
}
// Helper function for formatting a url and getting the result we're
// interested in
function getResult(url) {
url = UpdateUtils.formatUpdateURL(url);
return url.substr(URL_PREFIX.length).split("/")[0];
}
// url constructed with %PRODUCT%
add_task(function* test_product() {
let url = URL_PREFIX + "%PRODUCT%/";
Assert.equal(getResult(url), gAppInfo.name,
"the url param for %PRODUCT%" + MSG_SHOULD_EQUAL);
});
// url constructed with %VERSION%
add_task(function* test_version() {
let url = URL_PREFIX + "%VERSION%/";
Assert.equal(getResult(url), gAppInfo.version,
"the url param for %VERSION%" + MSG_SHOULD_EQUAL);
});
// url constructed with %BUILD_ID%
add_task(function* test_build_id() {
let url = URL_PREFIX + "%BUILD_ID%/";
Assert.equal(getResult(url), gAppInfo.appBuildID,
"the url param for %BUILD_ID%" + MSG_SHOULD_EQUAL);
});
// url constructed with %BUILD_TARGET%
// XXX TODO - it might be nice if we tested the actual ABI
add_task(function* test_build_target() {
let url = URL_PREFIX + "%BUILD_TARGET%/";
let abi;
try {
abi = gAppInfo.XPCOMABI;
} catch (e) {
do_throw("nsIXULAppInfo:XPCOMABI not defined\n");
}
if (AppConstants.platform == "macosx") {
// Mac universal build should report a different ABI than either macppc
// or mactel. This is necessary since nsUpdateService.js will set the ABI to
// Universal-gcc3 for Mac universal builds.
let macutils = Cc["@mozilla.org/xpcom/mac-utils;1"].
getService(Ci.nsIMacUtils);
if (macutils.isUniversalBinary) {
abi += "-u-" + macutils.architecturesInBinary;
}
} else if (AppConstants.platform == "win") {
// Windows build should report the CPU architecture that it's running on.
abi += "-" + getProcArchitecture();
}
Assert.equal(getResult(url), gAppInfo.OS + "_" + abi,
"the url param for %BUILD_TARGET%" + MSG_SHOULD_EQUAL);
});
// url constructed with %LOCALE%
// Bug 488936 added the update.locale file that stores the update locale
add_task(function* test_locale() {
// The code that gets the locale accesses the profile which is only available
// after calling do_get_profile in xpcshell tests. This prevents an error from
// being logged.
do_get_profile();
let url = URL_PREFIX + "%LOCALE%/";
Assert.equal(getResult(url), AppConstants.INSTALL_LOCALE,
"the url param for %LOCALE%" + MSG_SHOULD_EQUAL);
});
// url constructed with %CHANNEL%
add_task(function* test_channel() {
let url = URL_PREFIX + "%CHANNEL%/";
setUpdateChannel("test_channel");
Assert.equal(getResult(url), "test_channel",
"the url param for %CHANNEL%" + MSG_SHOULD_EQUAL);
});
// url constructed with %CHANNEL% with distribution partners
add_task(function* test_channel_distribution() {
let url = URL_PREFIX + "%CHANNEL%/";
gDefaultPrefBranch.setCharPref(PREF_APP_PARTNER_BRANCH + "test_partner1",
"test_partner1");
gDefaultPrefBranch.setCharPref(PREF_APP_PARTNER_BRANCH + "test_partner2",
"test_partner2");
Assert.equal(getResult(url),
"test_channel-cck-test_partner1-test_partner2",
"the url param for %CHANNEL%" + MSG_SHOULD_EQUAL);
});
// url constructed with %PLATFORM_VERSION%
add_task(function* test_platform_version() {
let url = URL_PREFIX + "%PLATFORM_VERSION%/";
Assert.equal(getResult(url), gAppInfo.platformVersion,
"the url param for %PLATFORM_VERSION%" + MSG_SHOULD_EQUAL);
});
// url constructed with %OS_VERSION%
add_task(function* test_os_version() {
let url = URL_PREFIX + "%OS_VERSION%/";
let osVersion;
let sysInfo = Cc["@mozilla.org/system-info;1"].getService(Ci.nsIPropertyBag2);
osVersion = sysInfo.getProperty("name") + " " + sysInfo.getProperty("version");
if (AppConstants.platform == "win") {
try {
let servicePack = getServicePack();
osVersion += "." + servicePack;
} catch (e) {
do_throw("Failure obtaining service pack: " + e);
}
if ("5.0" === sysInfo.getProperty("version")) { // Win2K
osVersion += " (unknown)";
} else {
try {
osVersion += " (" + getProcArchitecture() + ")";
} catch (e) {
do_throw("Failed to obtain processor architecture: " + e);
}
}
}
if (osVersion) {
try {
osVersion += " (" + sysInfo.getProperty("secondaryLibrary") + ")";
} catch (e) {
// Not all platforms have a secondary widget library, so an error is
// nothing to worry about.
}
osVersion = encodeURIComponent(osVersion);
}
Assert.equal(getResult(url), osVersion,
"the url param for %OS_VERSION%" + MSG_SHOULD_EQUAL);
});
// url constructed with %DISTRIBUTION%
add_task(function* test_distribution() {
let url = URL_PREFIX + "%DISTRIBUTION%/";
gDefaultPrefBranch.setCharPref(PREF_DISTRIBUTION_ID, "test_distro");
Assert.equal(getResult(url), "test_distro",
"the url param for %DISTRIBUTION%" + MSG_SHOULD_EQUAL);
});
// url constructed with %DISTRIBUTION_VERSION%
add_task(function* test_distribution_version() {
let url = URL_PREFIX + "%DISTRIBUTION_VERSION%/";
gDefaultPrefBranch.setCharPref(PREF_DISTRIBUTION_VERSION, "test_distro_version");
Assert.equal(getResult(url), "test_distro_version",
"the url param for %DISTRIBUTION_VERSION%" + MSG_SHOULD_EQUAL);
});
add_task(function* test_custom() {
Services.prefs.setCharPref("app.update.custom", "custom");
let url = URL_PREFIX + "%CUSTOM%/";
Assert.equal(getResult(url), "custom",
"the url query string for %CUSTOM%" + MSG_SHOULD_EQUAL);
});

View file

@ -0,0 +1,79 @@
/* Any copyright is dedicated to the Public Domain.
http://creativecommons.org/publicdomain/zero/1.0/ */
"use strict";
const ARCHIVE = "zips/zen.zip";
const SUBDIR = "zen";
const SYMLINK = "beyond_link";
const ENTRIES = ["beyond.txt", SYMLINK, "waterwood.txt"];
Components.utils.import("resource://gre/modules/ZipUtils.jsm");
Components.utils.import("resource://gre/modules/FileUtils.jsm");
Components.utils.import("resource://gre/modules/Services.jsm");
const archive = do_get_file(ARCHIVE, false);
const dir = do_get_profile().clone();
dir.append("test_ZipUtils");
function run_test() {
run_next_test();
}
function ensureExtracted(target) {
target.append(SUBDIR);
do_check_true(target.exists());
for (let i = 0; i < ENTRIES.length; i++) {
let entry = target.clone();
entry.append(ENTRIES[i]);
do_print("ENTRY " + entry.path);
do_check_true(entry.exists());
}
}
function ensureHasSymlink(target) {
// Just bail out if running on Windows, since symlinks do not exists there.
if (Services.appinfo.OS === "WINNT") {
return;
}
let entry = target.clone();
entry.append(SYMLINK);
do_print("ENTRY " + entry.path);
do_check_true(entry.exists());
do_check_true(entry.isSymlink());
}
add_task(function test_extractFiles() {
let target = dir.clone();
target.append("test_extractFiles");
try {
ZipUtils.extractFiles(archive, target);
} catch (e) {
do_throw("Failed to extract synchronously!");
}
ensureExtracted(target);
ensureHasSymlink(target);
});
add_task(function* test_extractFilesAsync() {
let target = dir.clone();
target.append("test_extractFilesAsync");
target.create(Components.interfaces.nsIFile.DIRECTORY_TYPE,
FileUtils.PERMS_DIRECTORY);
yield ZipUtils.extractFilesAsync(archive, target).then(
function success() {
do_print("SUCCESS");
ensureExtracted(target);
},
function failure() {
do_print("FAILURE");
do_throw("Failed to extract asynchronously!");
}
);
});

View file

@ -0,0 +1,95 @@
/* Any copyright is dedicated to the Public Domain.
* http://creativecommons.org/publicdomain/zero/1.0/ */
"use strict";
var {classes: Cc, interfaces: Ci, utils: Cu} = Components;
Cu.import("resource://gre/modules/ClientID.jsm");
Cu.import("resource://gre/modules/Task.jsm");
Cu.import("resource://services-common/utils.js");
Cu.import("resource://gre/modules/osfile.jsm");
Cu.import("resource://gre/modules/XPCOMUtils.jsm");
Cu.import("resource://gre/modules/Preferences.jsm");
function run_test() {
do_get_profile();
run_next_test();
}
add_task(function* () {
const drsPath = OS.Path.join(OS.Constants.Path.profileDir, "datareporting", "state.json");
const fhrDir = OS.Path.join(OS.Constants.Path.profileDir, "healthreport");
const fhrPath = OS.Path.join(fhrDir, "state.json");
const uuidRegex = /^[0-9a-f]{8}-[0-9a-f]{4}-[0-9a-f]{4}-[0-9a-f]{4}-[0-9a-f]{12}$/i;
const invalidIDs = [-1, 0.5, "INVALID-UUID", true, "", "3d1e1560-682a-4043-8cf2-aaaaaaaaaaaZ"];
const PREF_CACHED_CLIENTID = "toolkit.telemetry.cachedClientID";
yield OS.File.makeDir(fhrDir);
// Check that we are importing the FHR client ID.
let clientID = CommonUtils.generateUUID();
yield CommonUtils.writeJSON({clientID: clientID}, fhrPath);
Assert.equal(clientID, yield ClientID.getClientID());
// We should persist the ID in DRS now and not pick up a differing ID from FHR.
yield ClientID._reset();
yield CommonUtils.writeJSON({clientID: CommonUtils.generateUUID()}, fhrPath);
Assert.equal(clientID, yield ClientID.getClientID());
// We should be guarded against broken FHR data.
for (let invalidID of invalidIDs) {
yield ClientID._reset();
yield OS.File.remove(drsPath);
yield CommonUtils.writeJSON({clientID: invalidID}, fhrPath);
clientID = yield ClientID.getClientID();
Assert.equal(typeof(clientID), 'string');
Assert.ok(uuidRegex.test(clientID));
}
// We should be guarded against invalid FHR json.
yield ClientID._reset();
yield OS.File.remove(drsPath);
yield OS.File.writeAtomic(fhrPath, "abcd", {encoding: "utf-8", tmpPath: fhrPath + ".tmp"});
clientID = yield ClientID.getClientID();
Assert.equal(typeof(clientID), 'string');
Assert.ok(uuidRegex.test(clientID));
// We should be guarded against broken DRS data too and fall back to loading
// the FHR ID.
for (let invalidID of invalidIDs) {
yield ClientID._reset();
clientID = CommonUtils.generateUUID();
yield CommonUtils.writeJSON({clientID: clientID}, fhrPath);
yield CommonUtils.writeJSON({clientID: invalidID}, drsPath);
Assert.equal(clientID, yield ClientID.getClientID());
}
// We should be guarded against invalid DRS json too.
yield ClientID._reset();
yield OS.File.remove(fhrPath);
yield OS.File.writeAtomic(drsPath, "abcd", {encoding: "utf-8", tmpPath: drsPath + ".tmp"});
clientID = yield ClientID.getClientID();
Assert.equal(typeof(clientID), 'string');
Assert.ok(uuidRegex.test(clientID));
// If both the FHR and DSR data are broken, we should end up with a new client ID.
for (let invalidID of invalidIDs) {
yield ClientID._reset();
yield CommonUtils.writeJSON({clientID: invalidID}, fhrPath);
yield CommonUtils.writeJSON({clientID: invalidID}, drsPath);
clientID = yield ClientID.getClientID();
Assert.equal(typeof(clientID), 'string');
Assert.ok(uuidRegex.test(clientID));
}
// Assure that cached IDs are being checked for validity.
for (let invalidID of invalidIDs) {
yield ClientID._reset();
Preferences.set(PREF_CACHED_CLIENTID, invalidID);
let cachedID = ClientID.getCachedClientID();
Assert.strictEqual(cachedID, null, "ClientID should ignore invalid cached IDs");
let prefID = Preferences.get(PREF_CACHED_CLIENTID, null);
Assert.strictEqual(prefID, null, "ClientID should reset invalid cached IDs");
}
});

View file

@ -0,0 +1,9 @@
/* Any copyright is dedicated to the Public Domain.
* http://creativecommons.org/publicdomain/zero/1.0/ */
Components.utils.import("resource://gre/modules/third_party/jsesc/jsesc.js");
function run_test() {
do_check_eq(jsesc("teééést", {lowercaseHex: true}), "te\\xe9\\xe9\\xe9st");
do_check_eq(jsesc("teééést", {lowercaseHex: false}), "te\\xE9\\xE9\\xE9st");
}

View file

@ -0,0 +1,106 @@
/* This Source Code Form is subject to the terms of the Mozilla Public
* License, v. 2.0. If a copy of the MPL was not distributed with this file,
* You can obtain one at http://mozilla.org/MPL/2.0/. */
"use strict";
Components.utils.import("resource://gre/modules/PropertyListUtils.jsm");
function checkValue(aPropertyListObject, aType, aValue) {
do_check_eq(PropertyListUtils.getObjectType(aPropertyListObject), aType);
if (aValue !== undefined) {
// Perform strict equality checks until Bug 714467 is fixed.
let strictEqualityCheck = function(a, b) {
do_check_eq(typeof(a), typeof(b));
do_check_eq(a, b);
};
if (typeof(aPropertyListObject) == "object")
strictEqualityCheck(aPropertyListObject.valueOf(), aValue.valueOf());
else
strictEqualityCheck(aPropertyListObject, aValue);
}
}
function checkLazyGetterValue(aObject, aPropertyName, aType, aValue) {
let descriptor = Object.getOwnPropertyDescriptor(aObject, aPropertyName);
do_check_eq(typeof(descriptor.get), "function");
do_check_eq(typeof(descriptor.value), "undefined");
checkValue(aObject[aPropertyName], aType, aValue);
descriptor = Object.getOwnPropertyDescriptor(aObject, aPropertyName);
do_check_eq(typeof(descriptor.get), "undefined");
do_check_neq(typeof(descriptor.value), "undefined");
}
function checkMainPropertyList(aPropertyListRoot) {
const PRIMITIVE = PropertyListUtils.TYPE_PRIMITIVE;
checkValue(aPropertyListRoot, PropertyListUtils.TYPE_DICTIONARY);
// Check .has()
Assert.ok(aPropertyListRoot.has("Boolean"));
Assert.ok(!aPropertyListRoot.has("Nonexistent"));
checkValue(aPropertyListRoot.get("Boolean"), PRIMITIVE, false);
let array = aPropertyListRoot.get("Array");
checkValue(array, PropertyListUtils.TYPE_ARRAY);
do_check_eq(array.length, 8);
// Test both long and short values, since binary property lists store
// long values a little bit differently (see readDataLengthAndOffset).
// Short ASCII string
checkLazyGetterValue(array, 0, PRIMITIVE, "abc");
// Long ASCII string
checkLazyGetterValue(array, 1, PRIMITIVE, new Array(1001).join("a"));
// Short unicode string
checkLazyGetterValue(array, 2, PRIMITIVE, "\u05D0\u05D0\u05D0");
// Long unicode string
checkLazyGetterValue(array, 3, PRIMITIVE, new Array(1001).join("\u05D0"));
// Unicode surrogate pair
checkLazyGetterValue(array, 4, PRIMITIVE,
"\uD800\uDC00\uD800\uDC00\uD800\uDC00");
// Date
checkLazyGetterValue(array, 5, PropertyListUtils.TYPE_DATE,
new Date("2011-12-31T11:15:23Z"));
// Data
checkLazyGetterValue(array, 6, PropertyListUtils.TYPE_UINT8_ARRAY);
let dataAsString = Array.from(array[6]).map(b => String.fromCharCode(b)).join("");
do_check_eq(dataAsString, "2011-12-31T11:15:33Z");
// Dict
let dict = array[7];
checkValue(dict, PropertyListUtils.TYPE_DICTIONARY);
checkValue(dict.get("Negative Number"), PRIMITIVE, -400);
checkValue(dict.get("Real Number"), PRIMITIVE, 2.71828183);
checkValue(dict.get("Big Int"),
PropertyListUtils.TYPE_INT64,
"9007199254740993");
checkValue(dict.get("Negative Big Int"),
PropertyListUtils.TYPE_INT64,
"-9007199254740993");
}
function readPropertyList(aFile, aCallback) {
PropertyListUtils.read(aFile, function(aPropertyListRoot) {
// Null root indicates failure to read property list.
// Note: It is important not to run do_check_n/eq directly on Dict and array
// objects, because it cases their toString to get invoked, doing away with
// all the lazy getter we'd like to test later.
do_check_true(aPropertyListRoot !== null);
aCallback(aPropertyListRoot);
run_next_test();
});
}
function run_test() {
add_test(readPropertyList.bind(this,
do_get_file("propertyLists/bug710259_propertyListBinary.plist", false),
checkMainPropertyList));
add_test(readPropertyList.bind(this,
do_get_file("propertyLists/bug710259_propertyListXML.plist", false),
checkMainPropertyList));
run_next_test();
}

View file

@ -0,0 +1,97 @@
/* Any copyright is dedicated to the Public Domain.
http://creativecommons.org/publicdomain/zero/1.0/ */
Components.utils.import("resource://gre/modules/Services.jsm");
Components.utils.import("resource://gre/modules/CertUtils.jsm");
const PREF_PREFIX = "certutils.certs.";
function run_test() {
run_next_test();
}
function resetPrefs() {
var prefs = Services.prefs.getChildList(PREF_PREFIX);
prefs.forEach(Services.prefs.clearUserPref);
}
function attributes_match(aCert, aExpected) {
if (Object.keys(aCert).length != Object.keys(aExpected).length)
return false;
for (var attribute in aCert) {
if (!(attribute in aExpected))
return false;
if (aCert[attribute] != aExpected[attribute])
return false;
}
return true;
}
function test_results(aCerts, aExpected) {
do_check_eq(aCerts.length, aExpected.length);
for (var i = 0; i < aCerts.length; i++) {
if (!attributes_match(aCerts[i], aExpected[i])) {
dump("Attributes for certificate " + (i + 1) + " did not match expected attributes\n");
dump("Saw: " + aCerts[i].toSource() + "\n");
dump("Expected: " + aExpected[i].toSource() + "\n");
do_check_true(false);
}
}
}
add_test(function test_singleCert() {
Services.prefs.setCharPref(PREF_PREFIX + "1.attribute1", "foo");
Services.prefs.setCharPref(PREF_PREFIX + "1.attribute2", "bar");
var certs = readCertPrefs(PREF_PREFIX);
test_results(certs, [{
attribute1: "foo",
attribute2: "bar"
}]);
resetPrefs();
run_next_test();
});
add_test(function test_multipleCert() {
Services.prefs.setCharPref(PREF_PREFIX + "1.md5Fingerprint", "cf84a9a2a804e021f27cb5128fe151f4");
Services.prefs.setCharPref(PREF_PREFIX + "1.nickname", "1st cert");
Services.prefs.setCharPref(PREF_PREFIX + "2.md5Fingerprint", "9441051b7eb50e5ca2226095af710c1a");
Services.prefs.setCharPref(PREF_PREFIX + "2.nickname", "2nd cert");
var certs = readCertPrefs(PREF_PREFIX);
test_results(certs, [{
md5Fingerprint: "cf84a9a2a804e021f27cb5128fe151f4",
nickname: "1st cert"
}, {
md5Fingerprint: "9441051b7eb50e5ca2226095af710c1a",
nickname: "2nd cert"
}]);
resetPrefs();
run_next_test();
});
add_test(function test_skippedCert() {
Services.prefs.setCharPref(PREF_PREFIX + "1.issuerName", "Mozilla");
Services.prefs.setCharPref(PREF_PREFIX + "1.nickname", "1st cert");
Services.prefs.setCharPref(PREF_PREFIX + "2.issuerName", "Top CA");
Services.prefs.setCharPref(PREF_PREFIX + "2.nickname", "2nd cert");
Services.prefs.setCharPref(PREF_PREFIX + "4.issuerName", "Unknown CA");
Services.prefs.setCharPref(PREF_PREFIX + "4.nickname", "Ignored cert");
var certs = readCertPrefs(PREF_PREFIX);
test_results(certs, [{
issuerName: "Mozilla",
nickname: "1st cert"
}, {
issuerName: "Top CA",
nickname: "2nd cert"
}]);
resetPrefs();
run_next_test();
});

View file

@ -0,0 +1,25 @@
/* Any copyright is dedicated to the Public Domain.
* http://creativecommons.org/publicdomain/zero/1.0/ */
"use strict";
var {classes: Cc, interfaces: Ci, utils: Cu} = Components;
Cu.import("resource://gre/modules/ServiceRequest.jsm");
add_task(function* test_tls_conservative() {
const request = new ServiceRequest();
request.open("GET", "http://example.com", false);
const sr_channel = request.channel.QueryInterface(Ci.nsIHttpChannelInternal);
ok(("beConservative" in sr_channel), "TLS setting is present in SR channel");
ok(sr_channel.beConservative, "TLS setting in request channel is set to conservative for SR");
const xhr = new XMLHttpRequest();
xhr.open("GET", "http://example.com", false);
const xhr_channel = xhr.channel.QueryInterface(Ci.nsIHttpChannelInternal);
ok(("beConservative" in xhr_channel), "TLS setting is present in XHR channel");
ok(!xhr_channel.beConservative, "TLS setting in request channel is not set to conservative for XHR");
});

View file

@ -0,0 +1,306 @@
/* Any copyright is dedicated to the Public Domain.
* http://creativecommons.org/publicdomain/zero/1.0/ */
"use strict";
var {utils: Cu} = Components;
Cu.import("resource://gre/modules/Promise.jsm");
Cu.import("resource://gre/modules/SessionRecorder.jsm");
Cu.import("resource://gre/modules/Services.jsm");
Cu.import("resource://services-common/utils.js");
function run_test() {
run_next_test();
}
function monkeypatchStartupInfo(recorder, start=new Date(), offset=500) {
Object.defineProperty(recorder, "_getStartupInfo", {
value: function _getStartupInfo() {
return {
process: start,
main: new Date(start.getTime() + offset),
firstPaint: new Date(start.getTime() + 2 * offset),
sessionRestored: new Date(start.getTime() + 3 * offset),
};
}
});
}
function sleep(wait) {
let deferred = Promise.defer();
let timer = CommonUtils.namedTimer(function onTimer() {
deferred.resolve();
}, wait, deferred.promise, "_sleepTimer");
return deferred.promise;
}
function getRecorder(name, start, offset) {
let recorder = new SessionRecorder("testing." + name + ".");
monkeypatchStartupInfo(recorder, start, offset);
return recorder;
}
add_test(function test_basic() {
let recorder = getRecorder("basic");
recorder.onStartup();
recorder.onShutdown();
run_next_test();
});
add_task(function* test_current_properties() {
let now = new Date();
let recorder = getRecorder("current_properties", now);
yield sleep(25);
recorder.onStartup();
do_check_eq(recorder.startDate.getTime(), now.getTime());
do_check_eq(recorder.activeTicks, 0);
do_check_true(recorder.fineTotalTime > 0);
do_check_eq(recorder.main, 500);
do_check_eq(recorder.firstPaint, 1000);
do_check_eq(recorder.sessionRestored, 1500);
recorder.incrementActiveTicks();
do_check_eq(recorder.activeTicks, 1);
recorder._startDate = new Date(Date.now() - 1000);
recorder.updateTotalTime();
do_check_eq(recorder.totalTime, 1);
recorder.onShutdown();
});
// If startup info isn't present yet, we should install a timer and get
// it eventually.
add_task(function* test_current_availability() {
let recorder = new SessionRecorder("testing.current_availability.");
let now = new Date();
Object.defineProperty(recorder, "_getStartupInfo", {
value: function _getStartupInfo() {
return {
process: now,
main: new Date(now.getTime() + 500),
firstPaint: new Date(now.getTime() + 1000),
};
},
writable: true,
});
Object.defineProperty(recorder, "STARTUP_RETRY_INTERVAL_MS", {
value: 100,
});
let oldRecord = recorder.recordStartupFields;
let recordCount = 0;
Object.defineProperty(recorder, "recordStartupFields", {
value: function () {
recordCount++;
return oldRecord.call(recorder);
}
});
do_check_null(recorder._timer);
recorder.onStartup();
do_check_eq(recordCount, 1);
do_check_eq(recorder.sessionRestored, -1);
do_check_neq(recorder._timer, null);
yield sleep(125);
do_check_eq(recordCount, 2);
yield sleep(100);
do_check_eq(recordCount, 3);
do_check_eq(recorder.sessionRestored, -1);
monkeypatchStartupInfo(recorder, now);
yield sleep(100);
do_check_eq(recordCount, 4);
do_check_eq(recorder.sessionRestored, 1500);
// The timer should be removed and we should not fire again.
do_check_null(recorder._timer);
yield sleep(100);
do_check_eq(recordCount, 4);
recorder.onShutdown();
});
add_test(function test_timer_clear_on_shutdown() {
let recorder = new SessionRecorder("testing.timer_clear_on_shutdown.");
let now = new Date();
Object.defineProperty(recorder, "_getStartupInfo", {
value: function _getStartupInfo() {
return {
process: now,
main: new Date(now.getTime() + 500),
firstPaint: new Date(now.getTime() + 1000),
};
},
});
do_check_null(recorder._timer);
recorder.onStartup();
do_check_neq(recorder._timer, null);
recorder.onShutdown();
do_check_null(recorder._timer);
run_next_test();
});
add_task(function* test_previous_clean() {
let now = new Date();
let recorder = getRecorder("previous_clean", now);
yield sleep(25);
recorder.onStartup();
recorder.incrementActiveTicks();
recorder.incrementActiveTicks();
yield sleep(25);
recorder.onShutdown();
let total = recorder.totalTime;
yield sleep(25);
let now2 = new Date();
let recorder2 = getRecorder("previous_clean", now2, 100);
yield sleep(25);
recorder2.onStartup();
do_check_eq(recorder2.startDate.getTime(), now2.getTime());
do_check_eq(recorder2.main, 100);
do_check_eq(recorder2.firstPaint, 200);
do_check_eq(recorder2.sessionRestored, 300);
let sessions = recorder2.getPreviousSessions();
do_check_eq(Object.keys(sessions).length, 1);
do_check_true(0 in sessions);
let session = sessions[0];
do_check_true(session.clean);
do_check_eq(session.startDate.getTime(), now.getTime());
do_check_eq(session.main, 500);
do_check_eq(session.firstPaint, 1000);
do_check_eq(session.sessionRestored, 1500);
do_check_eq(session.totalTime, total);
do_check_eq(session.activeTicks, 2);
recorder2.onShutdown();
});
add_task(function* test_previous_abort() {
let now = new Date();
let recorder = getRecorder("previous_abort", now);
yield sleep(25);
recorder.onStartup();
recorder.incrementActiveTicks();
yield sleep(25);
let total = recorder.totalTime;
yield sleep(25);
let now2 = new Date();
let recorder2 = getRecorder("previous_abort", now2);
yield sleep(25);
recorder2.onStartup();
let sessions = recorder2.getPreviousSessions();
do_check_eq(Object.keys(sessions).length, 1);
do_check_true(0 in sessions);
let session = sessions[0];
do_check_false(session.clean);
do_check_eq(session.totalTime, total);
recorder.onShutdown();
recorder2.onShutdown();
});
add_task(function* test_multiple_sessions() {
for (let i = 0; i < 10; i++) {
let recorder = getRecorder("multiple_sessions");
yield sleep(25);
recorder.onStartup();
for (let j = 0; j < i; j++) {
recorder.incrementActiveTicks();
}
yield sleep(25);
recorder.onShutdown();
yield sleep(25);
}
let recorder = getRecorder("multiple_sessions");
recorder.onStartup();
let sessions = recorder.getPreviousSessions();
do_check_eq(Object.keys(sessions).length, 10);
for (let [i, session] of Object.entries(sessions)) {
do_check_eq(session.activeTicks, i);
if (i > 0) {
do_check_true(session.startDate.getTime() > sessions[i-1].startDate.getTime());
}
}
// #6 is preserved since >=.
let threshold = sessions[6].startDate;
recorder.pruneOldSessions(threshold);
sessions = recorder.getPreviousSessions();
do_check_eq(Object.keys(sessions).length, 4);
recorder.pruneOldSessions(threshold);
sessions = recorder.getPreviousSessions();
do_check_eq(Object.keys(sessions).length, 4);
do_check_eq(recorder._prunedIndex, 5);
recorder.onShutdown();
});
add_task(function* test_record_activity() {
let recorder = getRecorder("record_activity");
yield sleep(25);
recorder.onStartup();
let total = recorder.totalTime;
yield sleep(25);
for (let i = 0; i < 3; i++) {
Services.obs.notifyObservers(null, "user-interaction-active", null);
yield sleep(25);
do_check_true(recorder.fineTotalTime > total);
total = recorder.fineTotalTime;
}
do_check_eq(recorder.activeTicks, 3);
// Now send inactive. We should increment total time but not active.
Services.obs.notifyObservers(null, "user-interaction-inactive", null);
do_check_eq(recorder.activeTicks, 3);
do_check_true(recorder.fineTotalTime > total);
total = recorder.fineTotalTime;
yield sleep(25);
// If we send active again, this should be counted as inactive.
Services.obs.notifyObservers(null, "user-interaction-active", null);
do_check_eq(recorder.activeTicks, 3);
do_check_true(recorder.fineTotalTime > total);
total = recorder.fineTotalTime;
yield sleep(25);
// If we send active again, this should be counted as active.
Services.obs.notifyObservers(null, "user-interaction-active", null);
do_check_eq(recorder.activeTicks, 4);
Services.obs.notifyObservers(null, "user-interaction-active", null);
do_check_eq(recorder.activeTicks, 5);
recorder.onShutdown();
});

File diff suppressed because it is too large Load diff

View file

@ -0,0 +1,122 @@
/* Any copyright is dedicated to the Public Domain.
* http://creativecommons.org/publicdomain/zero/1.0/ */
"use strict";
var {classes: Cc, interfaces: Ci, utils: Cu} = Components;
do_get_profile();
Cu.import("resource://gre/modules/osfile.jsm");
// OS.File doesn't like to be first imported during shutdown
Cu.import("resource://gre/modules/Sqlite.jsm");
Cu.import("resource://gre/modules/Task.jsm");
Cu.import("resource://gre/modules/Services.jsm");
Cu.import("resource://gre/modules/AsyncShutdown.jsm");
Cu.import("resource://gre/modules/Promise.jsm");
function getConnection(dbName, extraOptions={}) {
let path = dbName + ".sqlite";
let options = {path: path};
for (let [k, v] of Object.entries(extraOptions)) {
options[k] = v;
}
return Sqlite.openConnection(options);
}
function* getDummyDatabase(name, extraOptions={}) {
const TABLES = {
dirs: "id INTEGER PRIMARY KEY AUTOINCREMENT, path TEXT",
files: "id INTEGER PRIMARY KEY AUTOINCREMENT, dir_id INTEGER, path TEXT",
};
let c = yield getConnection(name, extraOptions);
c._initialStatementCount = 0;
for (let [k, v] of Object.entries(TABLES)) {
yield c.execute("CREATE TABLE " + k + "(" + v + ")");
c._initialStatementCount++;
}
return c;
}
function sleep(ms) {
let deferred = Promise.defer();
let timer = Cc["@mozilla.org/timer;1"]
.createInstance(Ci.nsITimer);
timer.initWithCallback({
notify: function () {
deferred.resolve();
},
}, ms, timer.TYPE_ONE_SHOT);
return deferred.promise;
}
function run_test() {
run_next_test();
}
//
// ----------- Don't add a test after this one, as it shuts down Sqlite.jsm
//
add_task(function* test_shutdown_clients() {
do_print("Ensuring that Sqlite.jsm doesn't shutdown before its clients");
let assertions = [];
let sleepStarted = false;
let sleepComplete = false;
Sqlite.shutdown.addBlocker("test_sqlite.js shutdown blocker (sleep)",
Task.async(function*() {
sleepStarted = true;
yield sleep(100);
sleepComplete = true;
}));
assertions.push({name: "sleepStarted", value: () => sleepStarted});
assertions.push({name: "sleepComplete", value: () => sleepComplete});
Sqlite.shutdown.addBlocker("test_sqlite.js shutdown blocker (immediate)",
true);
let dbOpened = false;
let dbClosed = false;
Sqlite.shutdown.addBlocker("test_sqlite.js shutdown blocker (open a connection during shutdown)",
Task.async(function*() {
let db = yield getDummyDatabase("opened during shutdown");
dbOpened = true;
db.close().then(
() => dbClosed = true
); // Don't wait for this task to complete, Sqlite.jsm must wait automatically
}));
assertions.push({name: "dbOpened", value: () => dbOpened});
assertions.push({name: "dbClosed", value: () => dbClosed});
do_print("Now shutdown Sqlite.jsm synchronously");
Services.prefs.setBoolPref("toolkit.asyncshutdown.testing", true);
AsyncShutdown.profileBeforeChange._trigger();
Services.prefs.clearUserPref("toolkit.asyncshutdown.testing");
for (let {name, value} of assertions) {
do_print("Checking: " + name);
do_check_true(value());
}
do_print("Ensure that we cannot open databases anymore");
let exn;
try {
yield getDummyDatabase("opened after shutdown");
} catch (ex) {
exn = ex;
}
do_check_true(!!exn);
do_check_true(exn.message.indexOf("Sqlite.jsm has been shutdown") != -1);
});

View file

@ -0,0 +1,642 @@
/* Any copyright is dedicated to the Public Domain.
http://creativecommons.org/publicdomain/zero/1.0/ */
/**
* This file tests the Task.jsm module.
*/
////////////////////////////////////////////////////////////////////////////////
/// Globals
var Cc = Components.classes;
var Ci = Components.interfaces;
var Cu = Components.utils;
var Cr = Components.results;
Cu.import("resource://gre/modules/XPCOMUtils.jsm");
XPCOMUtils.defineLazyModuleGetter(this, "Promise",
"resource://gre/modules/Promise.jsm");
XPCOMUtils.defineLazyModuleGetter(this, "Services",
"resource://gre/modules/Services.jsm");
XPCOMUtils.defineLazyModuleGetter(this, "Task",
"resource://gre/modules/Task.jsm");
/**
* Returns a promise that will be resolved with the given value, when an event
* posted on the event loop of the main thread is processed.
*/
function promiseResolvedLater(aValue) {
let deferred = Promise.defer();
Services.tm.mainThread.dispatch(() => deferred.resolve(aValue),
Ci.nsIThread.DISPATCH_NORMAL);
return deferred.promise;
}
////////////////////////////////////////////////////////////////////////////////
/// Tests
function run_test()
{
run_next_test();
}
add_test(function test_normal()
{
Task.spawn(function () {
let result = yield Promise.resolve("Value");
for (let i = 0; i < 3; i++) {
result += yield promiseResolvedLater("!");
}
throw new Task.Result("Task result: " + result);
}).then(function (result) {
do_check_eq("Task result: Value!!!", result);
run_next_test();
}, function (ex) {
do_throw("Unexpected error: " + ex);
});
});
add_test(function test_exceptions()
{
Task.spawn(function () {
try {
yield Promise.reject("Rejection result by promise.");
do_throw("Exception expected because the promise was rejected.");
} catch (ex) {
// We catch this exception now, we will throw a different one later.
do_check_eq("Rejection result by promise.", ex);
}
throw new Error("Exception uncaught by task.");
}).then(function (result) {
do_throw("Unexpected success!");
}, function (ex) {
do_check_eq("Exception uncaught by task.", ex.message);
run_next_test();
});
});
add_test(function test_recursion()
{
function task_fibonacci(n) {
throw new Task.Result(n < 2 ? n : (yield task_fibonacci(n - 1)) +
(yield task_fibonacci(n - 2)));
};
Task.spawn(task_fibonacci(6)).then(function (result) {
do_check_eq(8, result);
run_next_test();
}, function (ex) {
do_throw("Unexpected error: " + ex);
});
});
add_test(function test_spawn_primitive()
{
function fibonacci(n) {
return n < 2 ? n : fibonacci(n - 1) + fibonacci(n - 2);
};
// Polymorphism between task and non-task functions (see "test_recursion").
Task.spawn(fibonacci(6)).then(function (result) {
do_check_eq(8, result);
run_next_test();
}, function (ex) {
do_throw("Unexpected error: " + ex);
});
});
add_test(function test_spawn_function()
{
Task.spawn(function () {
return "This is not a generator.";
}).then(function (result) {
do_check_eq("This is not a generator.", result);
run_next_test();
}, function (ex) {
do_throw("Unexpected error: " + ex);
});
});
add_test(function test_spawn_function_this()
{
Task.spawn(function () {
return this;
}).then(function (result) {
// Since the task function wasn't defined in strict mode, its "this" object
// should be the same as the "this" object in this function, i.e. the global
// object.
do_check_eq(result, this);
run_next_test();
}, function (ex) {
do_throw("Unexpected error: " + ex);
});
});
add_test(function test_spawn_function_this_strict()
{
"use strict";
Task.spawn(function () {
return this;
}).then(function (result) {
// Since the task function was defined in strict mode, its "this" object
// should be undefined.
do_check_eq(typeof(result), "undefined");
run_next_test();
}, function (ex) {
do_throw("Unexpected error: " + ex);
});
});
add_test(function test_spawn_function_returning_promise()
{
Task.spawn(function () {
return promiseResolvedLater("Resolution value.");
}).then(function (result) {
do_check_eq("Resolution value.", result);
run_next_test();
}, function (ex) {
do_throw("Unexpected error: " + ex);
});
});
add_test(function test_spawn_function_exceptions()
{
Task.spawn(function () {
throw new Error("Exception uncaught by task.");
}).then(function (result) {
do_throw("Unexpected success!");
}, function (ex) {
do_check_eq("Exception uncaught by task.", ex.message);
run_next_test();
});
});
add_test(function test_spawn_function_taskresult()
{
Task.spawn(function () {
throw new Task.Result("Task result");
}).then(function (result) {
do_check_eq("Task result", result);
run_next_test();
}, function (ex) {
do_throw("Unexpected error: " + ex);
});
});
add_test(function test_yielded_undefined()
{
Task.spawn(function () {
yield;
throw new Task.Result("We continued correctly.");
}).then(function (result) {
do_check_eq("We continued correctly.", result);
run_next_test();
}, function (ex) {
do_throw("Unexpected error: " + ex);
});
});
add_test(function test_yielded_primitive()
{
Task.spawn(function () {
throw new Task.Result("Primitive " + (yield "value."));
}).then(function (result) {
do_check_eq("Primitive value.", result);
run_next_test();
}, function (ex) {
do_throw("Unexpected error: " + ex);
});
});
add_test(function test_star_normal()
{
Task.spawn(function* () {
let result = yield Promise.resolve("Value");
for (let i = 0; i < 3; i++) {
result += yield promiseResolvedLater("!");
}
return "Task result: " + result;
}).then(function (result) {
do_check_eq("Task result: Value!!!", result);
run_next_test();
}, function (ex) {
do_throw("Unexpected error: " + ex);
});
});
add_test(function test_star_exceptions()
{
Task.spawn(function* () {
try {
yield Promise.reject("Rejection result by promise.");
do_throw("Exception expected because the promise was rejected.");
} catch (ex) {
// We catch this exception now, we will throw a different one later.
do_check_eq("Rejection result by promise.", ex);
}
throw new Error("Exception uncaught by task.");
}).then(function (result) {
do_throw("Unexpected success!");
}, function (ex) {
do_check_eq("Exception uncaught by task.", ex.message);
run_next_test();
});
});
add_test(function test_star_recursion()
{
function* task_fibonacci(n) {
return n < 2 ? n : (yield task_fibonacci(n - 1)) +
(yield task_fibonacci(n - 2));
};
Task.spawn(task_fibonacci(6)).then(function (result) {
do_check_eq(8, result);
run_next_test();
}, function (ex) {
do_throw("Unexpected error: " + ex);
});
});
add_test(function test_mixed_legacy_and_star()
{
Task.spawn(function* () {
return yield (function() {
throw new Task.Result(yield 5);
})();
}).then(function (result) {
do_check_eq(5, result);
run_next_test();
}, function (ex) {
do_throw("Unexpected error: " + ex);
});
});
add_test(function test_async_function_from_generator()
{
Task.spawn(function* () {
let object = {
asyncFunction: Task.async(function* (param) {
do_check_eq(this, object);
return param;
})
};
// Ensure the async function returns a promise that resolves as expected.
do_check_eq((yield object.asyncFunction(1)), 1);
// Ensure a second call to the async function also returns such a promise.
do_check_eq((yield object.asyncFunction(3)), 3);
}).then(function () {
run_next_test();
}, function (ex) {
do_throw("Unexpected error: " + ex);
});
});
add_test(function test_async_function_from_function()
{
Task.spawn(function* () {
return Task.spawn(function* () {
let object = {
asyncFunction: Task.async(function (param) {
do_check_eq(this, object);
return param;
})
};
// Ensure the async function returns a promise that resolves as expected.
do_check_eq((yield object.asyncFunction(5)), 5);
// Ensure a second call to the async function also returns such a promise.
do_check_eq((yield object.asyncFunction(7)), 7);
});
}).then(function () {
run_next_test();
}, function (ex) {
do_throw("Unexpected error: " + ex);
});
});
add_test(function test_async_function_that_throws_rejects_promise()
{
Task.spawn(function* () {
let object = {
asyncFunction: Task.async(function* () {
throw "Rejected!";
})
};
yield object.asyncFunction();
}).then(function () {
do_throw("unexpected success calling async function that throws error");
}, function (ex) {
do_check_eq(ex, "Rejected!");
run_next_test();
});
});
add_test(function test_async_return_function()
{
Task.spawn(function* () {
// Ensure an async function that returns a function resolves to the function
// itself instead of calling the function and resolving to its return value.
return Task.spawn(function* () {
let returnValue = function () {
return "These aren't the droids you're looking for.";
};
let asyncFunction = Task.async(function () {
return returnValue;
});
do_check_eq((yield asyncFunction()), returnValue);
});
}).then(function () {
run_next_test();
}, function (ex) {
do_throw("Unexpected error: " + ex);
});
});
add_test(function test_async_throw_argument_not_function()
{
Task.spawn(function* () {
// Ensure Task.async throws if its aTask argument is not a function.
Assert.throws(() => Task.async("not a function"),
/aTask argument must be a function/);
}).then(function () {
run_next_test();
}, function (ex) {
do_throw("Unexpected error: " + ex);
});
});
add_test(function test_async_throw_on_function_in_place_of_promise()
{
Task.spawn(function* () {
// Ensure Task.spawn throws if passed an async function.
Assert.throws(() => Task.spawn(Task.async(function* () {})),
/Cannot use an async function in place of a promise/);
}).then(function () {
run_next_test();
}, function (ex) {
do_throw("Unexpected error: " + ex);
});
});
////////////////// Test rewriting of stack traces
// Backup Task.Debuggin.maintainStack.
// Will be restored by `exit_stack_tests`.
var maintainStack;
add_test(function enter_stack_tests() {
maintainStack = Task.Debugging.maintainStack;
Task.Debugging.maintainStack = true;
run_next_test();
});
/**
* Ensure that a list of frames appear in a stack, in the right order
*/
function do_check_rewritten_stack(frames, ex) {
do_print("Checking that the expected frames appear in the right order");
do_print(frames.join(", "));
let stack = ex.stack;
do_print(stack);
let framesFound = 0;
let lineNumber = 0;
let reLine = /([^\r\n])+/g;
let match;
while (framesFound < frames.length && (match = reLine.exec(stack))) {
let line = match[0];
let frame = frames[framesFound];
do_print("Searching for " + frame + " in line " + line);
if (line.indexOf(frame) != -1) {
do_print("Found " + frame);
++framesFound;
} else {
do_print("Didn't find " + frame);
}
}
if (framesFound >= frames.length) {
return;
}
do_throw("Did not find: " + frames.slice(framesFound).join(", ") +
" in " + stack.substr(reLine.lastIndex));
do_print("Ensuring that we have removed Task.jsm, Promise.jsm");
do_check_true(stack.indexOf("Task.jsm") == -1);
do_check_true(stack.indexOf("Promise.jsm") == -1);
do_check_true(stack.indexOf("Promise-backend.js") == -1);
}
// Test that we get an acceptable rewritten stack when we launch
// an error in a Task.spawn.
add_test(function test_spawn_throw_stack() {
Task.spawn(function* task_spawn_throw_stack() {
for (let i = 0; i < 5; ++i) {
yield Promise.resolve(); // Without stack rewrite, this would lose valuable information
}
throw new Error("BOOM");
}).then(do_throw, function(ex) {
do_check_rewritten_stack(["task_spawn_throw_stack",
"test_spawn_throw_stack"],
ex);
run_next_test();
});
});
// Test that we get an acceptable rewritten stack when we yield
// a rejection in a Task.spawn.
add_test(function test_spawn_yield_reject_stack() {
Task.spawn(function* task_spawn_yield_reject_stack() {
for (let i = 0; i < 5; ++i) {
yield Promise.resolve(); // Without stack rewrite, this would lose valuable information
}
yield Promise.reject(new Error("BOOM"));
}).then(do_throw, function(ex) {
do_check_rewritten_stack(["task_spawn_yield_reject_stack",
"test_spawn_yield_reject_stack"],
ex);
run_next_test();
});
});
// Test that we get an acceptable rewritten stack when we launch
// an error in a Task.async function.
add_test(function test_async_function_throw_stack() {
let task_async_function_throw_stack = Task.async(function*() {
for (let i = 0; i < 5; ++i) {
yield Promise.resolve(); // Without stack rewrite, this would lose valuable information
}
throw new Error("BOOM");
})().then(do_throw, function(ex) {
do_check_rewritten_stack(["task_async_function_throw_stack",
"test_async_function_throw_stack"],
ex);
run_next_test();
});
});
// Test that we get an acceptable rewritten stack when we launch
// an error in a Task.async function.
add_test(function test_async_function_yield_reject_stack() {
let task_async_function_yield_reject_stack = Task.async(function*() {
for (let i = 0; i < 5; ++i) {
yield Promise.resolve(); // Without stack rewrite, this would lose valuable information
}
yield Promise.reject(new Error("BOOM"));
})().then(do_throw, function(ex) {
do_check_rewritten_stack(["task_async_function_yield_reject_stack",
"test_async_function_yield_reject_stack"],
ex);
run_next_test();
});
});
// Test that we get an acceptable rewritten stack when we launch
// an error in a Task.async function.
add_test(function test_async_method_throw_stack() {
let object = {
task_async_method_throw_stack: Task.async(function*() {
for (let i = 0; i < 5; ++i) {
yield Promise.resolve(); // Without stack rewrite, this would lose valuable information
}
throw new Error("BOOM");
})
};
object.task_async_method_throw_stack().then(do_throw, function(ex) {
do_check_rewritten_stack(["task_async_method_throw_stack",
"test_async_method_throw_stack"],
ex);
run_next_test();
});
});
// Test that we get an acceptable rewritten stack when we launch
// an error in a Task.async function.
add_test(function test_async_method_yield_reject_stack() {
let object = {
task_async_method_yield_reject_stack: Task.async(function*() {
for (let i = 0; i < 5; ++i) {
yield Promise.resolve(); // Without stack rewrite, this would lose valuable information
}
yield Promise.reject(new Error("BOOM"));
})
};
object.task_async_method_yield_reject_stack().then(do_throw, function(ex) {
do_check_rewritten_stack(["task_async_method_yield_reject_stack",
"test_async_method_yield_reject_stack"],
ex);
run_next_test();
});
});
// Test that two tasks whose execution takes place interleaved do not capture each other's stack.
add_test(function test_throw_stack_do_not_capture_the_wrong_task() {
for (let iter_a of [3, 4, 5]) { // Vary the interleaving
for (let iter_b of [3, 4, 5]) {
Task.spawn(function* task_a() {
for (let i = 0; i < iter_a; ++i) {
yield Promise.resolve();
}
throw new Error("BOOM");
}).then(do_throw, function(ex) {
do_check_rewritten_stack(["task_a",
"test_throw_stack_do_not_capture_the_wrong_task"],
ex);
do_check_true(!ex.stack.includes("task_b"));
run_next_test();
});
Task.spawn(function* task_b() {
for (let i = 0; i < iter_b; ++i) {
yield Promise.resolve();
}
});
}
}
});
// Put things together
add_test(function test_throw_complex_stack()
{
// Setup the following stack:
// inner_method()
// task_3()
// task_2()
// task_1()
// function_3()
// function_2()
// function_1()
// test_throw_complex_stack()
(function function_1() {
return (function function_2() {
return (function function_3() {
return Task.spawn(function* task_1() {
yield Promise.resolve();
try {
yield Task.spawn(function* task_2() {
yield Promise.resolve();
yield Task.spawn(function* task_3() {
yield Promise.resolve();
let inner_object = {
inner_method: Task.async(function*() {
throw new Error("BOOM");
})
};
yield Promise.resolve();
yield inner_object.inner_method();
});
});
} catch (ex) {
yield Promise.resolve();
throw ex;
}
});
})();
})();
})().then(
() => do_throw("Shouldn't have succeeded"),
(ex) => {
let expect = ["inner_method",
"task_3",
"task_2",
"task_1",
"function_3",
"function_2",
"function_1",
"test_throw_complex_stack"];
do_check_rewritten_stack(expect, ex);
run_next_test();
});
});
add_test(function test_without_maintainStack() {
do_print("Calling generateReadableStack without a Task");
Task.Debugging.generateReadableStack(new Error("Not a real error"));
Task.Debugging.maintainStack = false;
do_print("Calling generateReadableStack with neither a Task nor maintainStack");
Task.Debugging.generateReadableStack(new Error("Not a real error"));
do_print("Calling generateReadableStack without maintainStack");
Task.spawn(function*() {
Task.Debugging.generateReadableStack(new Error("Not a real error"));
run_next_test();
});
});
add_test(function exit_stack_tests() {
Task.Debugging.maintainStack = maintainStack;
run_next_test();
});

View file

@ -0,0 +1,57 @@
/* Any copyright is dedicated to the Public Domain.
* http://creativecommons.org/publicdomain/zero/1.0/ */
"use strict";
// Tests exports from Timer.jsm
var imported = {};
Components.utils.import("resource://gre/modules/Timer.jsm", imported);
function run_test() {
run_next_test();
}
add_task(function* test_setTimeout() {
let timeout1 = imported.setTimeout(() => do_throw("Should not be called"), 100);
do_check_eq(typeof timeout1, "number", "setTimeout returns a number");
do_check_true(timeout1 > 0, "setTimeout returns a positive number");
imported.clearTimeout(timeout1);
yield new Promise((resolve) => {
let timeout2 = imported.setTimeout((param1, param2) => {
do_check_true(true, "Should be called");
do_check_eq(param1, 5, "first parameter is correct");
do_check_eq(param2, "test", "second parameter is correct");
resolve();
}, 100, 5, "test");
do_check_eq(typeof timeout2, "number", "setTimeout returns a number");
do_check_true(timeout2 > 0, "setTimeout returns a positive number");
do_check_neq(timeout1, timeout2, "Calling setTimeout again returns a different value");
});
});
add_task(function* test_setInterval() {
let interval1 = imported.setInterval(() => do_throw("Should not be called!"), 100);
do_check_eq(typeof interval1, "number", "setInterval returns a number");
do_check_true(interval1 > 0, "setTimeout returns a positive number");
imported.clearInterval(interval1);
const EXPECTED_CALLS = 5;
let calls = 0;
yield new Promise((resolve) => {
let interval2 = imported.setInterval((param1, param2) => {
do_check_true(true, "Should be called");
do_check_eq(param1, 15, "first parameter is correct");
do_check_eq(param2, "hola", "second parameter is correct");
if (calls >= EXPECTED_CALLS) {
resolve();
}
calls++;
}, 100, 15, "hola");
});
});

View file

@ -0,0 +1,149 @@
/* Any copyright is dedicated to the Public Domain.
* http://creativecommons.org/publicdomain/zero/1.0/ */
"use strict";
var Cu = Components.utils;
Cu.import("resource://gre/modules/Services.jsm");
Cu.import("resource://gre/modules/WebChannel.jsm");
const ERROR_ID_ORIGIN_REQUIRED = "WebChannel id and originOrPermission are required.";
const VALID_WEB_CHANNEL_ID = "id";
const URL_STRING = "http://example.com";
const VALID_WEB_CHANNEL_ORIGIN = Services.io.newURI(URL_STRING, null, null);
const TEST_PERMISSION_NAME = "test-webchannel-permissions";
var MockWebChannelBroker = {
_channelMap: new Map(),
registerChannel: function(channel) {
if (!this._channelMap.has(channel)) {
this._channelMap.set(channel);
}
},
unregisterChannel: function (channelToRemove) {
this._channelMap.delete(channelToRemove)
}
};
function run_test() {
run_next_test();
}
/**
* Web channel tests
*/
/**
* Test channel listening with originOrPermission being an nsIURI.
*/
add_task(function test_web_channel_listen() {
return new Promise((resolve, reject) => {
let channel = new WebChannel(VALID_WEB_CHANNEL_ID, VALID_WEB_CHANNEL_ORIGIN, {
broker: MockWebChannelBroker
});
let delivered = 0;
do_check_eq(channel.id, VALID_WEB_CHANNEL_ID);
do_check_eq(channel._originOrPermission.spec, VALID_WEB_CHANNEL_ORIGIN.spec);
do_check_eq(channel._deliverCallback, null);
channel.listen(function(id, message, target) {
do_check_eq(id, VALID_WEB_CHANNEL_ID);
do_check_true(message);
do_check_true(message.command);
do_check_true(target.sender);
delivered++;
// 2 messages should be delivered
if (delivered === 2) {
channel.stopListening();
do_check_eq(channel._deliverCallback, null);
resolve();
}
});
// send two messages
channel.deliver({
id: VALID_WEB_CHANNEL_ID,
message: {
command: "one"
}
}, { sender: true });
channel.deliver({
id: VALID_WEB_CHANNEL_ID,
message: {
command: "two"
}
}, { sender: true });
});
});
/**
* Test channel listening with originOrPermission being a permission string.
*/
add_task(function test_web_channel_listen_permission() {
return new Promise((resolve, reject) => {
// add a new permission
Services.perms.add(VALID_WEB_CHANNEL_ORIGIN, TEST_PERMISSION_NAME, Services.perms.ALLOW_ACTION);
do_register_cleanup(() => Services.perms.remove(VALID_WEB_CHANNEL_ORIGIN, TEST_PERMISSION_NAME));
let channel = new WebChannel(VALID_WEB_CHANNEL_ID, TEST_PERMISSION_NAME, {
broker: MockWebChannelBroker
});
let delivered = 0;
do_check_eq(channel.id, VALID_WEB_CHANNEL_ID);
do_check_eq(channel._originOrPermission, TEST_PERMISSION_NAME);
do_check_eq(channel._deliverCallback, null);
channel.listen(function(id, message, target) {
do_check_eq(id, VALID_WEB_CHANNEL_ID);
do_check_true(message);
do_check_true(message.command);
do_check_true(target.sender);
delivered++;
// 2 messages should be delivered
if (delivered === 2) {
channel.stopListening();
do_check_eq(channel._deliverCallback, null);
resolve();
}
});
// send two messages
channel.deliver({
id: VALID_WEB_CHANNEL_ID,
message: {
command: "one"
}
}, { sender: true });
channel.deliver({
id: VALID_WEB_CHANNEL_ID,
message: {
command: "two"
}
}, { sender: true });
});
});
/**
* Test constructor
*/
add_test(function test_web_channel_constructor() {
do_check_eq(constructorTester(), ERROR_ID_ORIGIN_REQUIRED);
do_check_eq(constructorTester(undefined), ERROR_ID_ORIGIN_REQUIRED);
do_check_eq(constructorTester(undefined, VALID_WEB_CHANNEL_ORIGIN), ERROR_ID_ORIGIN_REQUIRED);
do_check_eq(constructorTester(VALID_WEB_CHANNEL_ID, undefined), ERROR_ID_ORIGIN_REQUIRED);
do_check_false(constructorTester(VALID_WEB_CHANNEL_ID, VALID_WEB_CHANNEL_ORIGIN));
run_next_test();
});
function constructorTester(id, origin) {
try {
new WebChannel(id, origin);
} catch (e) {
return e.message;
}
return false;
}

View file

@ -0,0 +1,88 @@
/* Any copyright is dedicated to the Public Domain.
* http://creativecommons.org/publicdomain/zero/1.0/ */
"use strict";
var Cu = Components.utils;
Cu.import("resource://gre/modules/Services.jsm");
Cu.import("resource://gre/modules/WebChannel.jsm");
const VALID_WEB_CHANNEL_ID = "id";
const URL_STRING = "http://example.com";
const VALID_WEB_CHANNEL_ORIGIN = Services.io.newURI(URL_STRING, null, null);
function run_test() {
run_next_test();
}
/**
* Test WebChannelBroker channel map
*/
add_test(function test_web_channel_broker_channel_map() {
let channel = {};
let channel2 = {};
do_check_eq(WebChannelBroker._channelMap.size, 0);
do_check_false(WebChannelBroker._messageListenerAttached);
// make sure _channelMap works correctly
WebChannelBroker.registerChannel(channel);
do_check_eq(WebChannelBroker._channelMap.size, 1);
do_check_true(WebChannelBroker._messageListenerAttached);
WebChannelBroker.registerChannel(channel2);
do_check_eq(WebChannelBroker._channelMap.size, 2);
WebChannelBroker.unregisterChannel(channel);
do_check_eq(WebChannelBroker._channelMap.size, 1);
// make sure the correct channel is unregistered
do_check_false(WebChannelBroker._channelMap.has(channel));
do_check_true(WebChannelBroker._channelMap.has(channel2));
WebChannelBroker.unregisterChannel(channel2);
do_check_eq(WebChannelBroker._channelMap.size, 0);
run_next_test();
});
/**
* Test WebChannelBroker _listener test
*/
add_task(function test_web_channel_broker_listener() {
return new Promise((resolve, reject) => {
var channel = {
id: VALID_WEB_CHANNEL_ID,
_originCheckCallback: requestPrincipal => {
return VALID_WEB_CHANNEL_ORIGIN.prePath === requestPrincipal.origin;
},
deliver: function(data, sender) {
do_check_eq(data.id, VALID_WEB_CHANNEL_ID);
do_check_eq(data.message.command, "hello");
do_check_neq(sender, undefined);
WebChannelBroker.unregisterChannel(channel);
resolve();
}
};
WebChannelBroker.registerChannel(channel);
var mockEvent = {
data: {
id: VALID_WEB_CHANNEL_ID,
message: {
command: "hello"
}
},
principal: {
origin: URL_STRING
},
objects: {
},
};
WebChannelBroker._listener(mockEvent);
});
});

View file

@ -0,0 +1,75 @@
[DEFAULT]
head =
tail =
support-files =
propertyLists/bug710259_propertyListBinary.plist
propertyLists/bug710259_propertyListXML.plist
chromeappsstore.sqlite
zips/zen.zip
[test_BinarySearch.js]
skip-if = toolkit == 'android'
[test_CanonicalJSON.js]
[test_client_id.js]
skip-if = toolkit == 'android'
[test_Color.js]
[test_DeferredTask.js]
skip-if = toolkit == 'android'
[test_FileUtils.js]
skip-if = toolkit == 'android'
[test_FinderIterator.js]
[test_GMPInstallManager.js]
skip-if = toolkit == 'android'
[test_Http.js]
skip-if = toolkit == 'android'
[test_Integration.js]
[test_jsesc.js]
skip-if = toolkit == 'android'
[test_JSONFile.js]
skip-if = toolkit == 'android'
[test_Log.js]
skip-if = toolkit == 'android'
[test_MatchPattern.js]
skip-if = toolkit == 'android'
[test_MatchGlobs.js]
skip-if = toolkit == 'android'
[test_MatchURLFilters.js]
skip-if = toolkit == 'android'
[test_NewTabUtils.js]
skip-if = toolkit == 'android'
[test_ObjectUtils.js]
skip-if = toolkit == 'android'
[test_ObjectUtils_strict.js]
skip-if = toolkit == 'android'
[test_PermissionsUtils.js]
skip-if = toolkit == 'android'
[test_Preferences.js]
skip-if = toolkit == 'android'
[test_Promise.js]
skip-if = toolkit == 'android'
[test_PromiseUtils.js]
skip-if = toolkit == 'android'
[test_propertyListsUtils.js]
skip-if = toolkit == 'android'
[test_readCertPrefs.js]
skip-if = toolkit == 'android'
[test_Services.js]
skip-if = toolkit == 'android'
[test_session_recorder.js]
skip-if = toolkit == 'android'
[test_sqlite.js]
skip-if = toolkit == 'android'
[test_sqlite_shutdown.js]
skip-if = toolkit == 'android'
[test_task.js]
skip-if = toolkit == 'android'
[test_timer.js]
skip-if = toolkit == 'android'
[test_UpdateUtils_url.js]
[test_UpdateUtils_updatechannel.js]
[test_web_channel.js]
[test_web_channel_broker.js]
[test_ZipUtils.js]
skip-if = toolkit == 'android'
[test_Log_stackTrace.js]
[test_servicerequest_xhr.js]

Binary file not shown.