Bug 874821 - Enable better remote gcli testing; r=mratcliffe

This commit is contained in:
Joe Walker 2013-05-25 09:51:31 +01:00
parent 70d15618e2
commit 57536173c7
10 changed files with 833 additions and 237 deletions

View File

@ -57,6 +57,7 @@ MOCHITEST_BROWSER_FILES = \
browser_gcli_keyboard3.js \
browser_gcli_menu.js \
browser_gcli_node.js \
browser_gcli_remote.js \
browser_gcli_resource.js \
browser_gcli_scratchpad.js \
browser_gcli_spell.js \

View File

@ -0,0 +1,462 @@
/*
* Copyright 2012, Mozilla Foundation and contributors
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
// define(function(require, exports, module) {
// <INJECTED SOURCE:START>
// THIS FILE IS GENERATED FROM SOURCE IN THE GCLI PROJECT
// DO NOT EDIT IT DIRECTLY
var exports = {};
const TEST_URI = "data:text/html;charset=utf-8,<p id='gcli-input'>gcli-testRemote.js</p>";
function test() {
helpers.addTabWithToolbar(TEST_URI, function(options) {
return helpers.runTests(options, exports);
}).then(finish);
}
// <INJECTED SOURCE:END>
'use strict';
// var assert = require('test/assert');
// var helpers = require('gclitest/helpers');
// var mockCommands = require('gclitest/mockCommands');
exports.setup = function(options) {
mockCommands.setup();
};
exports.shutdown = function(options) {
mockCommands.shutdown();
};
exports.testRemote = function(options) {
return helpers.audit(options, [
{
skipRemainingIf: !options.isHttp,
setup: 'remote ',
check: {
input: 'remote ',
hints: '',
markup: 'EEEEEEV',
cursor: 7,
current: '__command',
status: 'ERROR',
options: [ ],
message: 'Can\'t use \'remote\'.',
predictions: [ ],
unassigned: [ ],
}
},
{
setup: 'connect remote',
check: {
input: 'connect remote',
hints: ' [options]',
markup: 'VVVVVVVVVVVVVV',
cursor: 14,
current: 'prefix',
status: 'VALID',
options: [ ],
message: '',
predictions: [ ],
unassigned: [ ],
args: {
command: { name: 'connect' },
prefix: { value: 'remote', arg: ' remote', status: 'VALID', message: '' },
host: { value: undefined, arg: '', status: 'VALID', message: '' },
port: { value: undefined, arg: '', status: 'VALID', message: '' },
}
},
exec: {
output: /^Added [0-9]* commands.$/,
completed: false,
type: 'string',
error: false
}
},
{
setup: 'remote ',
check: {
input: 'remote ',
hints: '',
markup: 'IIIIIIV',
cursor: 7,
current: '__command',
status: 'ERROR',
optionsIncludes: [
'remote', 'remote cd', 'remote context', 'remote echo',
'remote exec', 'remote exit', 'remote firefox', 'remote help',
'remote intro', 'remote make'
],
message: '',
predictions: [ 'remote' ],
unassigned: [ ],
}
},
{
setup: 'remote echo hello world',
check: {
input: 'remote echo hello world',
hints: '',
markup: 'VVVVVVVVVVVVVVVVVVVVVVV',
cursor: 23,
current: 'message',
status: 'VALID',
options: [ ],
message: '',
predictions: [ ],
unassigned: [ ],
args: {
command: { name: 'remote echo' },
message: {
value: 'hello world',
arg: ' hello world',
status: 'VALID',
message: ''
}
}
},
exec: {
output: 'hello world',
completed: false,
type: 'string',
error: false
}
},
{
setup: 'remote exec ls',
check: {
input: 'remote exec ls',
hints: '',
markup: 'VVVVVVVVVVVVVV',
cursor: 14,
current: 'command',
status: 'VALID',
options: [ ],
message: '',
predictions: [ ],
unassigned: [ ],
args: {
command: {
value: 'ls',
arg: ' ls',
status: 'VALID',
message: ''
}
}
},
exec: {
// output: '', We can't rely on the contents of the FS
completed: false,
type: 'string',
error: false
}
},
{
setup: 'remote sleep mistake',
check: {
input: 'remote sleep mistake',
hints: '',
markup: 'VVVVVVVVVVVVVEEEEEEE',
cursor: 20,
current: 'length',
status: 'ERROR',
options: [ ],
message: 'Can\'t convert "mistake" to a number.',
predictions: [ ],
unassigned: [ ],
args: {
command: { name: 'remote sleep' },
length: {
value: undefined,
arg: ' mistake',
status: 'ERROR',
message: 'Can\'t convert "mistake" to a number.'
}
}
}
},
{
setup: 'remote sleep 1',
check: {
input: 'remote sleep 1',
hints: '',
markup: 'VVVVVVVVVVVVVV',
cursor: 14,
current: 'length',
status: 'VALID',
options: [ ],
message: '',
predictions: [ ],
unassigned: [ ],
args: {
command: { name: 'remote sleep' },
length: { value: 1, arg: ' 1', status: 'VALID', message: '' }
}
},
exec: {
output: 'Done',
completed: false,
type: 'string',
error: false
}
},
{
setup: 'remote help ',
skipIf: true, // The help command is not remotable
check: {
input: 'remote help ',
hints: '[search]',
markup: 'VVVVVVVVVVVV',
cursor: 12,
current: 'search',
status: 'VALID',
options: [ ],
message: '',
predictions: [ ],
unassigned: [ ],
args: {
command: { name: 'remote help' },
search: {
value: undefined,
arg: '',
status: 'VALID',
message: ''
}
}
},
exec: {
output: '',
completed: false,
type: 'string',
error: false
}
},
{
setup: 'remote intro',
check: {
input: 'remote intro',
hints: '',
markup: 'VVVVVVVVVVVV',
cursor: 12,
current: '__command',
status: 'VALID',
options: [ ],
message: '',
predictions: [ ],
unassigned: [ ],
args: {
command: { name: 'remote intro' }
}
},
exec: {
output: [
/^This command line/,
/F1\/Escape/
],
completed: false,
type: 'intro',
error: false
}
},
{
setup: 'context remote',
check: {
input: 'context remote',
hints: '',
markup: 'VVVVVVVVVVVVVV',
cursor: 14,
current: 'prefix',
status: 'VALID',
optionsContains: [ 'remote', 'remote cd', 'remote echo', 'remote exec', 'remote exit', 'remote firefox', 'remote help', 'remote intro', 'remote make' ],
message: '',
predictionsContains: [ 'remote', 'remote cd', 'remote echo', 'remote exec', 'remote exit', 'remote firefox', 'remote help', 'remote intro', 'remote make', 'remote pref' ],
unassigned: [ ],
args: {
command: { name: 'context' },
prefix: {
/*value:[object Object],*/
arg: ' remote',
status: 'VALID',
message: ''
}
}
},
exec: {
output: 'Using remote as a command prefix',
completed: true,
type: 'string',
error: false
}
},
{
setup: 'exec ls',
check: {
input: 'exec ls',
hints: '',
markup: 'VVVVVVV',
cursor: 7,
current: 'command',
status: 'VALID',
options: [ ],
message: '',
predictions: [ ],
unassigned: [ ],
args: {
command: { value: 'ls', arg: ' ls', status: 'VALID', message: '' },
}
},
exec: {
// output: '', We can't rely on the contents of the filesystem
completed: false,
type: 'string',
error: false
}
},
{
setup: 'echo hello world',
check: {
input: 'echo hello world',
hints: '',
markup: 'VVVVVVVVVVVVVVVV',
cursor: 16,
current: 'message',
status: 'VALID',
options: [ ],
message: '',
predictions: [ ],
unassigned: [ ],
args: {
command: { name: 'remote echo' },
message: {
value: 'hello world',
arg: ' hello world',
status: 'VALID',
message: ''
}
}
},
exec: {
output: /^hello world$/,
type: 'string',
error: false
}
},
{
setup: 'context',
check: {
input: 'context',
hints: ' [prefix]',
markup: 'VVVVVVV',
cursor: 7,
current: '__command',
status: 'VALID',
optionsContains: [ 'remote', 'remote cd', 'remote echo', 'remote exec', 'remote exit', 'remote firefox', 'remote help', 'remote intro', 'remote make' ],
message: '',
predictions: [ ],
unassigned: [ ],
args: {
command: { name: 'context' },
prefix: { value: undefined, arg: '', status: 'VALID', message: '' }
}
},
exec: {
output: 'Command prefix is unset',
completed: true,
type: 'string',
error: false
}
},
{
setup: 'disconnect ',
check: {
input: 'disconnect ',
hints: 'remote',
markup: 'VVVVVVVVVVV',
cursor: 11,
current: 'prefix',
status: 'ERROR',
options: [ 'remote' ],
message: '',
predictions: [ 'remote' ],
unassigned: [ ],
args: {
command: { name: 'disconnect' },
prefix: {
value: undefined,
arg: '',
status: 'INCOMPLETE',
message: ''
}
}
}
},
{
setup: 'disconnect remote --force',
check: {
input: 'disconnect remote --force',
hints: '',
markup: 'VVVVVVVVVVVVVVVVVVVVVVVVV',
cursor: 25,
current: 'force',
status: 'VALID',
message: '',
unassigned: [ ],
args: {
command: { name: 'disconnect' },
prefix: {
value: function(connection) {
assert.is(connection.prefix, 'remote', 'disconnecting remote');
},
arg: ' remote',
status: 'VALID',
message: ''
}
}
},
exec: {
output: /^Removed [0-9]* commands.$/,
completed: true,
type: 'string',
error: false
}
},
{
setup: 'remote ',
check: {
input: 'remote ',
hints: '',
markup: 'EEEEEEV',
cursor: 7,
current: '__command',
status: 'ERROR',
options: [ ],
message: 'Can\'t use \'remote\'.',
predictions: [ ],
unassigned: [ ],
}
}
]);
};
// });

View File

@ -226,7 +226,7 @@ helpers._actual = {
.replace(/ $/, '');
};
var promisedJoin = util.promised(join);
var promisedJoin = Promise.promised(join);
return promisedJoin(templateData.directTabText,
templateData.emptyParameters,
templateData.arrowTabText);
@ -314,12 +314,12 @@ helpers._createDebugCheck = function(options) {
var hintsPromise = helpers._actual.hints(options);
var predictionsPromise = helpers._actual.predictions(options);
return util.all(hintsPromise, predictionsPromise).then(function(values) {
return Promise.all(hintsPromise, predictionsPromise).then(function(values) {
var hints = values[0];
var predictions = values[1];
var output = '';
output += 'helpers.audit(options, [\n';
output += 'return helpers.audit(options, [\n';
output += ' {\n';
if (cursor === input.length) {
@ -627,13 +627,14 @@ helpers._check = function(options, name, checks) {
Object.keys(checks.args).forEach(function(paramName) {
var check = checks.args[paramName];
var assignment;
if (paramName === 'command') {
// We allow an 'argument' called 'command' to be the command itself, but
// what if the command has a parameter called 'command' (for example, an
// 'exec' command)? We default to using the parameter because checking
// the command value is less useful
var assignment = requisition.getAssignment(paramName);
if (assignment == null && paramName === 'command') {
assignment = requisition.commandAssignment;
}
else {
assignment = requisition.getAssignment(paramName);
}
if (assignment == null) {
assert.ok(false, 'Unknown arg: ' + paramName + suffix);
@ -641,9 +642,19 @@ helpers._check = function(options, name, checks) {
}
if ('value' in check) {
assert.is(assignment.value,
check.value,
'arg.' + paramName + '.value' + suffix);
if (typeof check.value === 'function') {
try {
check.value(assignment.value);
}
catch (ex) {
assert.ok(false, '' + ex);
}
}
else {
assert.is(assignment.value,
check.value,
'arg.' + paramName + '.value' + suffix);
}
}
if ('name' in check) {
@ -689,7 +700,7 @@ helpers._check = function(options, name, checks) {
});
}
return util.all(outstanding).then(function() {
return Promise.all(outstanding).then(function() {
// Ensure the promise resolves to nothing
return undefined;
});
@ -707,7 +718,16 @@ helpers._exec = function(options, name, expected) {
return Promise.resolve({});
}
var output = options.display.requisition.exec({ hidden: true });
var output;
try {
output = options.display.requisition.exec({ hidden: true });
}
catch (ex) {
assert.ok(false, 'Failure executing \'' + name + '\': ' + ex);
util.errorHandler(ex);
return Promise.resolve({});
}
if ('completed' in expected) {
assert.is(output.completed,
@ -725,9 +745,6 @@ helpers._exec = function(options, name, expected) {
}
var checkOutput = function() {
var div = options.window.document.createElement('div');
var conversionContext = options.display.requisition.conversionContext;
if ('type' in expected) {
assert.is(output.type,
expected.type,
@ -740,20 +757,20 @@ helpers._exec = function(options, name, expected) {
'output.error for: ' + name);
}
var conversionContext = options.display.requisition.conversionContext;
var convertPromise = converters.convert(output.data, output.type, 'dom',
conversionContext);
return convertPromise.then(function(node) {
div.appendChild(node);
var actualOutput = div.textContent.trim();
var actualOutput = node.textContent.trim();
var doTest = function(match, against) {
if (match.test(against)) {
assert.ok(true, 'html output for ' + name + ' should match ' +
match.source);
assert.ok(true, 'html output for ' + name + ' should match /' +
match.source + '/');
} else {
assert.ok(false, 'html output for ' + name + ' should match ' +
assert.ok(false, 'html output for ' + name + ' should match /' +
match.source +
'. Actual textContent: "' + against + '"');
'/. Actual textContent: "' + against + '"');
}
};
@ -810,11 +827,13 @@ var totalResponseTime = 0;
var averageOver = 0;
var maxResponseTime = 0;
var maxResponseCulprit = undefined;
var start = undefined;
/**
* Restart the stats collection process
*/
helpers.resetResponseTimes = function() {
start = new Date().getTime();
totalResponseTime = 0;
averageOver = 0;
maxResponseTime = 0;
@ -849,6 +868,20 @@ Object.defineProperty(helpers, 'maxResponseCulprit', {
enumerable: true
});
/**
* Quick summary of the times
*/
Object.defineProperty(helpers, 'timingSummary', {
get: function() {
var elapsed = (new Date().getTime() - start) / 1000;
return 'Total ' + elapsed + 's, ' +
'ave response ' + helpers.averageResponseTime + 'ms, ' +
'max response ' + helpers.maxResponseTime + 'ms ' +
'from \'' + helpers.maxResponseCulprit + '\'';
},
enumerable: true
});
/**
* A way of turning a set of tests into something more declarative, this helps
* to allow tests to be asynchronous.

View File

@ -226,7 +226,7 @@ helpers._actual = {
.replace(/ $/, '');
};
var promisedJoin = util.promised(join);
var promisedJoin = Promise.promised(join);
return promisedJoin(templateData.directTabText,
templateData.emptyParameters,
templateData.arrowTabText);
@ -314,12 +314,12 @@ helpers._createDebugCheck = function(options) {
var hintsPromise = helpers._actual.hints(options);
var predictionsPromise = helpers._actual.predictions(options);
return util.all(hintsPromise, predictionsPromise).then(function(values) {
return Promise.all(hintsPromise, predictionsPromise).then(function(values) {
var hints = values[0];
var predictions = values[1];
var output = '';
output += 'helpers.audit(options, [\n';
output += 'return helpers.audit(options, [\n';
output += ' {\n';
if (cursor === input.length) {
@ -627,13 +627,14 @@ helpers._check = function(options, name, checks) {
Object.keys(checks.args).forEach(function(paramName) {
var check = checks.args[paramName];
var assignment;
if (paramName === 'command') {
// We allow an 'argument' called 'command' to be the command itself, but
// what if the command has a parameter called 'command' (for example, an
// 'exec' command)? We default to using the parameter because checking
// the command value is less useful
var assignment = requisition.getAssignment(paramName);
if (assignment == null && paramName === 'command') {
assignment = requisition.commandAssignment;
}
else {
assignment = requisition.getAssignment(paramName);
}
if (assignment == null) {
assert.ok(false, 'Unknown arg: ' + paramName + suffix);
@ -641,9 +642,19 @@ helpers._check = function(options, name, checks) {
}
if ('value' in check) {
assert.is(assignment.value,
check.value,
'arg.' + paramName + '.value' + suffix);
if (typeof check.value === 'function') {
try {
check.value(assignment.value);
}
catch (ex) {
assert.ok(false, '' + ex);
}
}
else {
assert.is(assignment.value,
check.value,
'arg.' + paramName + '.value' + suffix);
}
}
if ('name' in check) {
@ -689,7 +700,7 @@ helpers._check = function(options, name, checks) {
});
}
return util.all(outstanding).then(function() {
return Promise.all(outstanding).then(function() {
// Ensure the promise resolves to nothing
return undefined;
});
@ -707,7 +718,16 @@ helpers._exec = function(options, name, expected) {
return Promise.resolve({});
}
var output = options.display.requisition.exec({ hidden: true });
var output;
try {
output = options.display.requisition.exec({ hidden: true });
}
catch (ex) {
assert.ok(false, 'Failure executing \'' + name + '\': ' + ex);
util.errorHandler(ex);
return Promise.resolve({});
}
if ('completed' in expected) {
assert.is(output.completed,
@ -725,9 +745,6 @@ helpers._exec = function(options, name, expected) {
}
var checkOutput = function() {
var div = options.window.document.createElement('div');
var conversionContext = options.display.requisition.conversionContext;
if ('type' in expected) {
assert.is(output.type,
expected.type,
@ -740,20 +757,20 @@ helpers._exec = function(options, name, expected) {
'output.error for: ' + name);
}
var conversionContext = options.display.requisition.conversionContext;
var convertPromise = converters.convert(output.data, output.type, 'dom',
conversionContext);
return convertPromise.then(function(node) {
div.appendChild(node);
var actualOutput = div.textContent.trim();
var actualOutput = node.textContent.trim();
var doTest = function(match, against) {
if (match.test(against)) {
assert.ok(true, 'html output for ' + name + ' should match ' +
match.source);
assert.ok(true, 'html output for ' + name + ' should match /' +
match.source + '/');
} else {
assert.ok(false, 'html output for ' + name + ' should match ' +
assert.ok(false, 'html output for ' + name + ' should match /' +
match.source +
'. Actual textContent: "' + against + '"');
'/. Actual textContent: "' + against + '"');
}
};
@ -810,11 +827,13 @@ var totalResponseTime = 0;
var averageOver = 0;
var maxResponseTime = 0;
var maxResponseCulprit = undefined;
var start = undefined;
/**
* Restart the stats collection process
*/
helpers.resetResponseTimes = function() {
start = new Date().getTime();
totalResponseTime = 0;
averageOver = 0;
maxResponseTime = 0;
@ -849,6 +868,20 @@ Object.defineProperty(helpers, 'maxResponseCulprit', {
enumerable: true
});
/**
* Quick summary of the times
*/
Object.defineProperty(helpers, 'timingSummary', {
get: function() {
var elapsed = (new Date().getTime() - start) / 1000;
return 'Total ' + elapsed + 's, ' +
'ave response ' + helpers.averageResponseTime + 'ms, ' +
'max response ' + helpers.maxResponseTime + 'ms ' +
'from \'' + helpers.maxResponseCulprit + '\'';
},
enumerable: true
});
/**
* A way of turning a set of tests into something more declarative, this helps
* to allow tests to be asynchronous.

View File

@ -226,7 +226,7 @@ helpers._actual = {
.replace(/ $/, '');
};
var promisedJoin = util.promised(join);
var promisedJoin = Promise.promised(join);
return promisedJoin(templateData.directTabText,
templateData.emptyParameters,
templateData.arrowTabText);
@ -314,12 +314,12 @@ helpers._createDebugCheck = function(options) {
var hintsPromise = helpers._actual.hints(options);
var predictionsPromise = helpers._actual.predictions(options);
return util.all(hintsPromise, predictionsPromise).then(function(values) {
return Promise.all(hintsPromise, predictionsPromise).then(function(values) {
var hints = values[0];
var predictions = values[1];
var output = '';
output += 'helpers.audit(options, [\n';
output += 'return helpers.audit(options, [\n';
output += ' {\n';
if (cursor === input.length) {
@ -627,13 +627,14 @@ helpers._check = function(options, name, checks) {
Object.keys(checks.args).forEach(function(paramName) {
var check = checks.args[paramName];
var assignment;
if (paramName === 'command') {
// We allow an 'argument' called 'command' to be the command itself, but
// what if the command has a parameter called 'command' (for example, an
// 'exec' command)? We default to using the parameter because checking
// the command value is less useful
var assignment = requisition.getAssignment(paramName);
if (assignment == null && paramName === 'command') {
assignment = requisition.commandAssignment;
}
else {
assignment = requisition.getAssignment(paramName);
}
if (assignment == null) {
assert.ok(false, 'Unknown arg: ' + paramName + suffix);
@ -641,9 +642,19 @@ helpers._check = function(options, name, checks) {
}
if ('value' in check) {
assert.is(assignment.value,
check.value,
'arg.' + paramName + '.value' + suffix);
if (typeof check.value === 'function') {
try {
check.value(assignment.value);
}
catch (ex) {
assert.ok(false, '' + ex);
}
}
else {
assert.is(assignment.value,
check.value,
'arg.' + paramName + '.value' + suffix);
}
}
if ('name' in check) {
@ -689,7 +700,7 @@ helpers._check = function(options, name, checks) {
});
}
return util.all(outstanding).then(function() {
return Promise.all(outstanding).then(function() {
// Ensure the promise resolves to nothing
return undefined;
});
@ -707,7 +718,16 @@ helpers._exec = function(options, name, expected) {
return Promise.resolve({});
}
var output = options.display.requisition.exec({ hidden: true });
var output;
try {
output = options.display.requisition.exec({ hidden: true });
}
catch (ex) {
assert.ok(false, 'Failure executing \'' + name + '\': ' + ex);
util.errorHandler(ex);
return Promise.resolve({});
}
if ('completed' in expected) {
assert.is(output.completed,
@ -725,9 +745,6 @@ helpers._exec = function(options, name, expected) {
}
var checkOutput = function() {
var div = options.window.document.createElement('div');
var conversionContext = options.display.requisition.conversionContext;
if ('type' in expected) {
assert.is(output.type,
expected.type,
@ -740,20 +757,20 @@ helpers._exec = function(options, name, expected) {
'output.error for: ' + name);
}
var conversionContext = options.display.requisition.conversionContext;
var convertPromise = converters.convert(output.data, output.type, 'dom',
conversionContext);
return convertPromise.then(function(node) {
div.appendChild(node);
var actualOutput = div.textContent.trim();
var actualOutput = node.textContent.trim();
var doTest = function(match, against) {
if (match.test(against)) {
assert.ok(true, 'html output for ' + name + ' should match ' +
match.source);
assert.ok(true, 'html output for ' + name + ' should match /' +
match.source + '/');
} else {
assert.ok(false, 'html output for ' + name + ' should match ' +
assert.ok(false, 'html output for ' + name + ' should match /' +
match.source +
'. Actual textContent: "' + against + '"');
'/. Actual textContent: "' + against + '"');
}
};
@ -810,11 +827,13 @@ var totalResponseTime = 0;
var averageOver = 0;
var maxResponseTime = 0;
var maxResponseCulprit = undefined;
var start = undefined;
/**
* Restart the stats collection process
*/
helpers.resetResponseTimes = function() {
start = new Date().getTime();
totalResponseTime = 0;
averageOver = 0;
maxResponseTime = 0;
@ -849,6 +868,20 @@ Object.defineProperty(helpers, 'maxResponseCulprit', {
enumerable: true
});
/**
* Quick summary of the times
*/
Object.defineProperty(helpers, 'timingSummary', {
get: function() {
var elapsed = (new Date().getTime() - start) / 1000;
return 'Total ' + elapsed + 's, ' +
'ave response ' + helpers.averageResponseTime + 'ms, ' +
'max response ' + helpers.maxResponseTime + 'ms ' +
'from \'' + helpers.maxResponseCulprit + '\'';
},
enumerable: true
});
/**
* A way of turning a set of tests into something more declarative, this helps
* to allow tests to be asynchronous.

View File

@ -24,7 +24,7 @@ let require = (Cu.import("resource://gre/modules/devtools/Require.jsm", {})).req
Components.utils.import("resource://gre/modules/devtools/gcli.jsm", {});
let console = (Cu.import("resource://gre/modules/devtools/Console.jsm", {})).console;
let TargetFactory = (Cu.import("resource:///modules/devtools/gDevTools.jsm", {})).devtools.TargetFactory;
let TargetFactory = (Cu.import("resource://gre/modules/devtools/Loader.jsm", {})).devtools.TargetFactory;
let Promise = (Cu.import("resource://gre/modules/commonjs/sdk/core/promise.js", {})).Promise;
let assert = { ok: ok, is: is, log: info };
@ -226,7 +226,7 @@ helpers._actual = {
.replace(/ $/, '');
};
var promisedJoin = util.promised(join);
var promisedJoin = Promise.promised(join);
return promisedJoin(templateData.directTabText,
templateData.emptyParameters,
templateData.arrowTabText);
@ -314,12 +314,12 @@ helpers._createDebugCheck = function(options) {
var hintsPromise = helpers._actual.hints(options);
var predictionsPromise = helpers._actual.predictions(options);
return util.all(hintsPromise, predictionsPromise).then(function(values) {
return Promise.all(hintsPromise, predictionsPromise).then(function(values) {
var hints = values[0];
var predictions = values[1];
var output = '';
output += 'helpers.audit(options, [\n';
output += 'return helpers.audit(options, [\n';
output += ' {\n';
if (cursor === input.length) {
@ -627,13 +627,14 @@ helpers._check = function(options, name, checks) {
Object.keys(checks.args).forEach(function(paramName) {
var check = checks.args[paramName];
var assignment;
if (paramName === 'command') {
// We allow an 'argument' called 'command' to be the command itself, but
// what if the command has a parameter called 'command' (for example, an
// 'exec' command)? We default to using the parameter because checking
// the command value is less useful
var assignment = requisition.getAssignment(paramName);
if (assignment == null && paramName === 'command') {
assignment = requisition.commandAssignment;
}
else {
assignment = requisition.getAssignment(paramName);
}
if (assignment == null) {
assert.ok(false, 'Unknown arg: ' + paramName + suffix);
@ -641,9 +642,19 @@ helpers._check = function(options, name, checks) {
}
if ('value' in check) {
assert.is(assignment.value,
check.value,
'arg.' + paramName + '.value' + suffix);
if (typeof check.value === 'function') {
try {
check.value(assignment.value);
}
catch (ex) {
assert.ok(false, '' + ex);
}
}
else {
assert.is(assignment.value,
check.value,
'arg.' + paramName + '.value' + suffix);
}
}
if ('name' in check) {
@ -689,7 +700,7 @@ helpers._check = function(options, name, checks) {
});
}
return util.all(outstanding).then(function() {
return Promise.all(outstanding).then(function() {
// Ensure the promise resolves to nothing
return undefined;
});
@ -707,7 +718,16 @@ helpers._exec = function(options, name, expected) {
return Promise.resolve({});
}
var output = options.display.requisition.exec({ hidden: true });
var output;
try {
output = options.display.requisition.exec({ hidden: true });
}
catch (ex) {
assert.ok(false, 'Failure executing \'' + name + '\': ' + ex);
util.errorHandler(ex);
return Promise.resolve({});
}
if ('completed' in expected) {
assert.is(output.completed,
@ -725,9 +745,6 @@ helpers._exec = function(options, name, expected) {
}
var checkOutput = function() {
var div = options.window.document.createElement('div');
var conversionContext = options.display.requisition.conversionContext;
if ('type' in expected) {
assert.is(output.type,
expected.type,
@ -740,20 +757,20 @@ helpers._exec = function(options, name, expected) {
'output.error for: ' + name);
}
var conversionContext = options.display.requisition.conversionContext;
var convertPromise = converters.convert(output.data, output.type, 'dom',
conversionContext);
return convertPromise.then(function(node) {
div.appendChild(node);
var actualOutput = div.textContent.trim();
var actualOutput = node.textContent.trim();
var doTest = function(match, against) {
if (match.test(against)) {
assert.ok(true, 'html output for ' + name + ' should match ' +
match.source);
assert.ok(true, 'html output for ' + name + ' should match /' +
match.source + '/');
} else {
assert.ok(false, 'html output for ' + name + ' should match ' +
assert.ok(false, 'html output for ' + name + ' should match /' +
match.source +
'. Actual textContent: "' + against + '"');
'/. Actual textContent: "' + against + '"');
}
};
@ -810,11 +827,13 @@ var totalResponseTime = 0;
var averageOver = 0;
var maxResponseTime = 0;
var maxResponseCulprit = undefined;
var start = undefined;
/**
* Restart the stats collection process
*/
helpers.resetResponseTimes = function() {
start = new Date().getTime();
totalResponseTime = 0;
averageOver = 0;
maxResponseTime = 0;
@ -849,6 +868,20 @@ Object.defineProperty(helpers, 'maxResponseCulprit', {
enumerable: true
});
/**
* Quick summary of the times
*/
Object.defineProperty(helpers, 'timingSummary', {
get: function() {
var elapsed = (new Date().getTime() - start) / 1000;
return 'Total ' + elapsed + 's, ' +
'ave response ' + helpers.averageResponseTime + 'ms, ' +
'max response ' + helpers.maxResponseTime + 'ms ' +
'from \'' + helpers.maxResponseCulprit + '\'';
},
enumerable: true
});
/**
* A way of turning a set of tests into something more declarative, this helps
* to allow tests to be asynchronous.

View File

@ -226,7 +226,7 @@ helpers._actual = {
.replace(/ $/, '');
};
var promisedJoin = util.promised(join);
var promisedJoin = Promise.promised(join);
return promisedJoin(templateData.directTabText,
templateData.emptyParameters,
templateData.arrowTabText);
@ -314,12 +314,12 @@ helpers._createDebugCheck = function(options) {
var hintsPromise = helpers._actual.hints(options);
var predictionsPromise = helpers._actual.predictions(options);
return util.all(hintsPromise, predictionsPromise).then(function(values) {
return Promise.all(hintsPromise, predictionsPromise).then(function(values) {
var hints = values[0];
var predictions = values[1];
var output = '';
output += 'helpers.audit(options, [\n';
output += 'return helpers.audit(options, [\n';
output += ' {\n';
if (cursor === input.length) {
@ -627,13 +627,14 @@ helpers._check = function(options, name, checks) {
Object.keys(checks.args).forEach(function(paramName) {
var check = checks.args[paramName];
var assignment;
if (paramName === 'command') {
// We allow an 'argument' called 'command' to be the command itself, but
// what if the command has a parameter called 'command' (for example, an
// 'exec' command)? We default to using the parameter because checking
// the command value is less useful
var assignment = requisition.getAssignment(paramName);
if (assignment == null && paramName === 'command') {
assignment = requisition.commandAssignment;
}
else {
assignment = requisition.getAssignment(paramName);
}
if (assignment == null) {
assert.ok(false, 'Unknown arg: ' + paramName + suffix);
@ -641,9 +642,19 @@ helpers._check = function(options, name, checks) {
}
if ('value' in check) {
assert.is(assignment.value,
check.value,
'arg.' + paramName + '.value' + suffix);
if (typeof check.value === 'function') {
try {
check.value(assignment.value);
}
catch (ex) {
assert.ok(false, '' + ex);
}
}
else {
assert.is(assignment.value,
check.value,
'arg.' + paramName + '.value' + suffix);
}
}
if ('name' in check) {
@ -689,7 +700,7 @@ helpers._check = function(options, name, checks) {
});
}
return util.all(outstanding).then(function() {
return Promise.all(outstanding).then(function() {
// Ensure the promise resolves to nothing
return undefined;
});
@ -707,7 +718,16 @@ helpers._exec = function(options, name, expected) {
return Promise.resolve({});
}
var output = options.display.requisition.exec({ hidden: true });
var output;
try {
output = options.display.requisition.exec({ hidden: true });
}
catch (ex) {
assert.ok(false, 'Failure executing \'' + name + '\': ' + ex);
util.errorHandler(ex);
return Promise.resolve({});
}
if ('completed' in expected) {
assert.is(output.completed,
@ -725,9 +745,6 @@ helpers._exec = function(options, name, expected) {
}
var checkOutput = function() {
var div = options.window.document.createElement('div');
var conversionContext = options.display.requisition.conversionContext;
if ('type' in expected) {
assert.is(output.type,
expected.type,
@ -740,20 +757,20 @@ helpers._exec = function(options, name, expected) {
'output.error for: ' + name);
}
var conversionContext = options.display.requisition.conversionContext;
var convertPromise = converters.convert(output.data, output.type, 'dom',
conversionContext);
return convertPromise.then(function(node) {
div.appendChild(node);
var actualOutput = div.textContent.trim();
var actualOutput = node.textContent.trim();
var doTest = function(match, against) {
if (match.test(against)) {
assert.ok(true, 'html output for ' + name + ' should match ' +
match.source);
assert.ok(true, 'html output for ' + name + ' should match /' +
match.source + '/');
} else {
assert.ok(false, 'html output for ' + name + ' should match ' +
assert.ok(false, 'html output for ' + name + ' should match /' +
match.source +
'. Actual textContent: "' + against + '"');
'/. Actual textContent: "' + against + '"');
}
};
@ -810,11 +827,13 @@ var totalResponseTime = 0;
var averageOver = 0;
var maxResponseTime = 0;
var maxResponseCulprit = undefined;
var start = undefined;
/**
* Restart the stats collection process
*/
helpers.resetResponseTimes = function() {
start = new Date().getTime();
totalResponseTime = 0;
averageOver = 0;
maxResponseTime = 0;
@ -849,6 +868,20 @@ Object.defineProperty(helpers, 'maxResponseCulprit', {
enumerable: true
});
/**
* Quick summary of the times
*/
Object.defineProperty(helpers, 'timingSummary', {
get: function() {
var elapsed = (new Date().getTime() - start) / 1000;
return 'Total ' + elapsed + 's, ' +
'ave response ' + helpers.averageResponseTime + 'ms, ' +
'max response ' + helpers.maxResponseTime + 'ms ' +
'from \'' + helpers.maxResponseCulprit + '\'';
},
enumerable: true
});
/**
* A way of turning a set of tests into something more declarative, this helps
* to allow tests to be asynchronous.

View File

@ -306,10 +306,21 @@ disconnectManual=Connect to the server, creating local versions of the commands
# short as possible.
disconnectPrefixDesc=Parent prefix for imported commands
# LOCALIZATION NOTE (disconnectForceDesc): A short description of the 'force'
# parameter to the 'disconnect' command. This string is designed to be shown
# in a dialog with restricted space, which is why it should be as short as
# possible.
disconnectForceDesc=Ignore outstanding requests
# LOCALIZATION NOTE (disconnectReply): The output of the 'disconnect' command,
# telling the user what it's done.
disconnectReply=Removed %S commands.
# LOCALIZATION NOTE (disconnectOutstanding): An error message displayed when
# the user attempts to disconnect before all requests have completed. %1$S is
# a list of commands which are incomplete
disconnectOutstanding=Outstanding requests (%1$S)
# LOCALIZATION NOTE (prefDesc): A very short description of the 'pref'
# command. This string is designed to be shown in a menu alongside the command
# name, which is why it should be as short as possible. See prefManual for a

View File

@ -104,7 +104,7 @@ var mozl10n = {};
})(mozl10n);
define('gcli/index', ['require', 'exports', 'module' , 'gcli/types/basic', 'gcli/types/selection', 'gcli/types/command', 'gcli/types/date', 'gcli/types/javascript', 'gcli/types/node', 'gcli/types/resource', 'gcli/types/setting', 'gcli/settings', 'gcli/ui/intro', 'gcli/ui/focus', 'gcli/ui/fields/basic', 'gcli/ui/fields/javascript', 'gcli/ui/fields/selection', 'gcli/commands/connect', 'gcli/commands/context', 'gcli/commands/help', 'gcli/commands/pref', 'gcli/canon', 'gcli/converters', 'gcli/types', 'gcli/ui/ffdisplay'], function(require, exports, module) {
define('gcli/index', ['require', 'exports', 'module' , 'gcli/types/basic', 'gcli/types/selection', 'gcli/types/command', 'gcli/types/date', 'gcli/types/javascript', 'gcli/types/node', 'gcli/types/resource', 'gcli/types/setting', 'gcli/settings', 'gcli/ui/intro', 'gcli/ui/focus', 'gcli/ui/fields/basic', 'gcli/ui/fields/javascript', 'gcli/ui/fields/selection', 'gcli/commands/connect', 'gcli/commands/context', 'gcli/commands/help', 'gcli/commands/pref', 'gcli/canon', 'gcli/converters', 'gcli/ui/ffdisplay'], function(require, exports, module) {
'use strict';
@ -143,9 +143,6 @@ define('gcli/index', ['require', 'exports', 'module' , 'gcli/types/basic', 'gcli
exports.removeCommand = require('gcli/canon').removeCommand;
exports.addConverter = require('gcli/converters').addConverter;
exports.removeConverter = require('gcli/converters').removeConverter;
exports.addType = require('gcli/types').addType;
exports.removeType = require('gcli/types').removeType;
exports.lookup = mozl10n.lookup;
exports.lookupFormat = mozl10n.lookupFormat;
@ -624,7 +621,7 @@ ArrayType.prototype.parse = function(arg, context) {
}.bind(this);
var conversionPromises = arg.getArguments().map(subArgParse);
return util.all(conversionPromises).then(function(conversions) {
return Promise.all(conversionPromises).then(function(conversions) {
return new ArrayConversion(conversions, arg);
});
};
@ -657,15 +654,17 @@ exports.ArrayType = ArrayType;
define('util/promise', ['require', 'exports', 'module' ], function(require, exports, module) {
'use strict';
'use strict';
var imported = {};
Components.utils.import("resource://gre/modules/commonjs/sdk/core/promise.js",
imported);
var imported = {};
Components.utils.import("resource://gre/modules/commonjs/sdk/core/promise.js",
imported);
exports.defer = imported.Promise.defer;
exports.resolve = imported.Promise.resolve;
exports.reject = imported.Promise.reject;
exports.defer = imported.Promise.defer;
exports.resolve = imported.Promise.resolve;
exports.reject = imported.Promise.reject;
exports.promised = imported.Promise.promised;
exports.all = imported.Promise.all;
});
/*
@ -906,65 +905,6 @@ exports.createEvent = function(name) {
var Promise = require('util/promise');
/**
* Implementation of 'promised', while we wait for bug 790195 to be fixed.
* @see Consuming promises in https://addons.mozilla.org/en-US/developers/docs/sdk/latest/modules/sdk/core/promise.html
* @see https://bugzilla.mozilla.org/show_bug.cgi?id=790195
* @see https://github.com/mozilla/addon-sdk/blob/master/packages/api-utils/lib/promise.js#L179
*/
exports.promised = (function() {
// Note: Define shortcuts and utility functions here in order to avoid
// slower property accesses and unnecessary closure creations on each
// call of this popular function.
var call = Function.call;
var concat = Array.prototype.concat;
// Utility function that does following:
// execute([ f, self, args...]) => f.apply(self, args)
function execute(args) { return call.apply(call, args); }
// Utility function that takes promise of `a` array and maybe promise `b`
// as arguments and returns promise for `a.concat(b)`.
function promisedConcat(promises, unknown) {
return promises.then(function(values) {
return Promise.resolve(unknown).then(function(value) {
return values.concat([ value ]);
});
});
}
return function promised(f, prototype) {
/**
Returns a wrapped `f`, which when called returns a promise that resolves to
`f(...)` passing all the given arguments to it, which by the way may be
promises. Optionally second `prototype` argument may be provided to be used
a prototype for a returned promise.
## Example
var promise = promised(Array)(1, promise(2), promise(3))
promise.then(console.log) // => [ 1, 2, 3 ]
**/
return function promised() {
// create array of [ f, this, args... ]
return concat.apply([ f, this ], arguments).
// reduce it via `promisedConcat` to get promised array of fulfillments
reduce(promisedConcat, Promise.resolve([], prototype)).
// finally map that to promise of `f.apply(this, args...)`
then(execute);
};
};
})();
/**
* Convert an array of promises to a single promise, which is resolved (with an
* array containing resolved values) only when all the component promises are
* resolved.
*/
exports.all = exports.promised(Array);
/**
* Utility to convert a resolved promise to a concrete value.
* Warning: This is something of an experiment. The alternative of mixing
@ -995,8 +935,10 @@ exports.synchronize = function(promise) {
};
/**
* promiseMap is roughly like Array.map except that the action is taken to be
* something that completes asynchronously, returning a promise.
* promiseEach is roughly like Array.forEach except that the action is taken to
* be something that completes asynchronously, returning a promise, so we wait
* for the action to complete for each array element before moving onto the
* next.
* @param array An array of objects to enumerate
* @param action A function to call for each member of the array
* @param scope Optional object to use as 'this' for the function calls
@ -1010,23 +952,26 @@ exports.promiseEach = function(array, action, scope) {
}
var deferred = Promise.defer();
var replies = [];
var callNext = function(index) {
var replies = [];
var promiseReply = action.call(scope, array[index]);
Promise.resolve(promiseReply).then(function(reply) {
var onSuccess = function(reply) {
replies[index] = reply;
var nextIndex = index + 1;
if (nextIndex >= array.length) {
if (index + 1 >= array.length) {
deferred.resolve(replies);
}
else {
callNext(nextIndex);
callNext(index + 1);
}
}).then(null, function(ex) {
};
var onFailure = function(ex) {
deferred.reject(ex);
});
};
var reply = action.call(scope, array[index], index, array);
Promise.resolve(reply).then(onSuccess).then(null, onFailure);
};
callNext(0);
@ -3867,6 +3812,10 @@ Canon.prototype.addProxyCommands = function(prefix, commandSpecs, remoter, to) {
names.forEach(function(name) {
var commandSpec = commandSpecs[name];
if (commandSpec.noRemote) {
return;
}
if (!commandSpec.isParent) {
commandSpec.exec = function(args, context) {
context.commandName = name;
@ -6777,7 +6726,7 @@ Requisition.prototype.complete = function(cursor, predictionChoice) {
outstanding.push(promise);
}
return util.all(outstanding).then(function() {
return Promise.all(outstanding).then(function() {
this.onTextChange();
this.onTextChange.resumeFire();
}.bind(this));
@ -7594,19 +7543,19 @@ Requisition.prototype._assign = function(args) {
if (!this.commandAssignment.value) {
this._addUnassignedArgs(args);
return util.all(outstanding);
return Promise.all(outstanding);
}
if (args.length === 0) {
this.setBlankArguments();
return util.all(outstanding);
return Promise.all(outstanding);
}
// Create an error if the command does not take parameters, but we have
// been given them ...
if (this.assignmentCount === 0) {
this._addUnassignedArgs(args);
return util.all(outstanding);
return Promise.all(outstanding);
}
// Special case: if there is only 1 parameter, and that's of type
@ -7616,7 +7565,7 @@ Requisition.prototype._assign = function(args) {
if (assignment.param.type.name === 'string') {
var arg = (args.length === 1) ? args[0] : new MergedArgument(args);
outstanding.push(this.setAssignment(assignment, arg, noArgUp));
return util.all(outstanding);
return Promise.all(outstanding);
}
}
@ -7723,7 +7672,7 @@ Requisition.prototype._assign = function(args) {
// What's left is can't be assigned, but we need to extract
this._addUnassignedArgs(args);
return util.all(outstanding);
return Promise.all(outstanding);
};
exports.Requisition = Requisition;
@ -9555,10 +9504,12 @@ var connect = {
createRemoter: function(prefix, connection) {
return function(cmdArgs, context) {
var typed = context.typed;
if (typed.indexOf(prefix) !== 0) {
throw new Error("Missing prefix");
// If we've been called using a 'context' then there will be no prefix
// otherwise we need to remove it
if (typed.indexOf(prefix) === 0) {
typed = typed.substring(prefix.length).replace(/^ */, "");
}
typed = typed.substring(prefix.length).replace(/^ */, "");
return connection.execute(typed, cmdArgs).then(function(reply) {
var typedData = context.typedData(reply.type, reply.data);
@ -9598,12 +9549,19 @@ var disconnect = {
name: 'prefix',
type: 'connection',
description: l10n.lookup('disconnectPrefixDesc'),
},
{
name: 'force',
type: 'boolean',
description: l10n.lookup('disconnectForceDesc'),
hidden: connector.disconnectSupportsForce,
option: true
}
],
returnType: 'string',
exec: function(args, context) {
return args.prefix.disconnect().then(function() {
return args.prefix.disconnect(args.force).then(function() {
var removed = canon.removeProxyCommands(args.prefix.prefix);
delete connections[args.prefix.prefix];
return l10n.lookupFormat('disconnectReply', [ removed.length ]);
@ -9647,13 +9605,15 @@ exports.shutdown = function() {
* limitations under the License.
*/
define('util/connect/connector', ['require', 'exports', 'module' ], function(require, exports, module) {
define('util/connect/connector', ['require', 'exports', 'module' , 'util/promise'], function(require, exports, module) {
'use strict';
var debuggerSocketConnect = Components.utils.import('resource://gre/modules/devtools/dbg-client.jsm', {}).debuggerSocketConnect;
var DebuggerClient = Components.utils.import('resource://gre/modules/devtools/dbg-client.jsm', {}).DebuggerClient;
var Promise = require('util/promise');
/**
* What port should we use by default?
*/
@ -9740,33 +9700,13 @@ Connection.prototype.getCommandSpecs = function() {
/**
* Send an execute request. Replies are handled by the setup in connect()
*/
Connection.prototype.execute = function(typed, cmdArgs) {
var deferred = Promise.defer();
var request = {
to: this.actor,
type: 'execute',
typed: typed,
args: cmdArgs
};
this.client.request(request, function(response) {
deferred.resolve(response.reply);
});
return deferred.promise;
};
/**
* Send an execute request.
*/
Connection.prototype.execute = function(typed, cmdArgs) {
var request = new Request(this.actor, typed, cmdArgs);
this.requests[request.json.id] = request;
this.requests[request.json.requestId] = request;
this.client.request(request.json, function(response) {
var request = this.requests[response.id];
delete this.requests[response.id];
var request = this.requests[response.requestId];
delete this.requests[response.requestId];
request.complete(response.error, response.type, response.data);
}.bind(this));
@ -9774,10 +9714,12 @@ Connection.prototype.execute = function(typed, cmdArgs) {
return request.promise;
};
exports.disconnectSupportsForce = false;
/**
* Kill this connection
*/
Connection.prototype.disconnect = function() {
Connection.prototype.disconnect = function(force) {
var deferred = Promise.defer();
this.client.close(function() {
@ -9797,7 +9739,7 @@ function Request(actor, typed, args) {
type: 'execute',
typed: typed,
args: args,
id: Request._nextRequestId++,
requestId: 'id-' + Request._nextRequestId++,
};
this._deferred = Promise.defer();
@ -9861,6 +9803,7 @@ var contextCmdSpec = {
}
],
returnType: 'string',
noRemote: true,
exec: function echo(args, context) {
// Do not copy this code
var requisition = context.__dlhjshfw;
@ -10214,6 +10157,17 @@ var terminalDomConverter = {
}
};
/**
* Convert a terminal object to a string
*/
var terminalStringConverter = {
from: 'terminal',
to: 'string',
exec: function(data, context) {
return Array.isArray(data) ? data.join('') : '' + data;
}
};
/**
* Several converters are just data.toString inside a 'p' element
*/
@ -10393,6 +10347,7 @@ exports.convert = function(data, from, to, conversionContext) {
exports.addConverter(viewDomConverter);
exports.addConverter(viewStringConverter);
exports.addConverter(terminalDomConverter);
exports.addConverter(terminalStringConverter);
exports.addConverter(stringDomConverter);
exports.addConverter(numberDomConverter);
exports.addConverter(booleanDomConverter);

View File

@ -55,15 +55,17 @@ GcliActor.prototype.execute = function(request) {
contentWindow.document);
let requisition = new Requisition(environment);
let outputPromise = requisition.updateExec(request.typed);
let output = util.synchronize(outputPromise);
return {
id: request.id,
data: output.data,
type: output.type,
error: output.error
};
requisition.updateExec(request.typed).then(output => {
return output.promise.then(() => {
this.connection.send({
from: this.actorID,
requestId: request.requestId,
data: output.data,
type: output.type,
error: output.error
});
});
}).then(null, console.error);
};
GcliActor.prototype.requestTypes = {