Bug 767588 - GCLI number type should allow restriction to integers; r=jwalker

This commit is contained in:
Adam Goforth 2013-02-22 16:37:48 +00:00
parent d7eaa96994
commit 4ee6e79b69
6 changed files with 120 additions and 4 deletions

View File

@ -223,13 +223,23 @@ exports.StringType = StringType;
/**
* We don't currently plan to distinguish between integers and floats
* We distinguish between integers and floats with the _allowFloat flag.
*/
function NumberType(typeSpec) {
// Default to integer values
this._allowFloat = !!typeSpec.allowFloat;
if (typeSpec) {
this._min = typeSpec.min;
this._max = typeSpec.max;
this._step = typeSpec.step || 1;
if (!this._allowFloat &&
(this._isFloat(this._min) ||
this._isFloat(this._max) ||
this._isFloat(this._step))) {
throw new Error('allowFloat is false, but non-integer values given in type spec');
}
}
else {
this._step = 1;
@ -274,7 +284,19 @@ NumberType.prototype.parse = function(arg) {
return new Conversion(undefined, arg, Status.INCOMPLETE, '');
}
var value = parseInt(arg.text, 10);
if (!this._allowFloat && (arg.text.indexOf('.') !== -1)) {
return new Conversion(undefined, arg, Status.ERROR,
l10n.lookupFormat('typesNumberNotInt', [ arg.text ]));
}
var value;
if (this._allowFloat) {
value = parseFloat(arg.text);
}
else {
value = parseInt(arg.text, 10);
}
if (isNaN(value)) {
return new Conversion(undefined, arg, Status.ERROR,
l10n.lookupFormat('typesNumberNan', [ arg.text ]));
@ -336,6 +358,14 @@ NumberType.prototype._boundsCheck = function(value) {
return value;
};
/**
* Return true if the given value is a finite number and not an integer, else
* return false.
*/
NumberType.prototype._isFloat = function(value) {
return ((typeof value === 'number') && isFinite(value) && (value % 1 !== 0));
};
NumberType.prototype.name = 'number';
exports.NumberType = NumberType;

View File

@ -324,6 +324,52 @@ exports.testSingleNumber = function() {
assert.is('tsu', requ.commandAssignment.value.name);
assert.is('x', assign1.arg.text);
assert.is(undefined, assign1.value);
update({ typed: 'tsu 1.5', cursor: { start: 7, end: 7 } });
assert.is( 'VVVVEEE', statuses);
assert.is(Status.ERROR, status);
assert.is('tsu', requ.commandAssignment.value.name);
assert.is('1.5', assign1.arg.text);
assert.is(undefined, assign1.value);
};
exports.testSingleFloat = function() {
update({ typed: 'tsf', cursor: { start: 3, end: 3 } });
assert.is( 'VVV', statuses);
assert.is(Status.ERROR, status);
assert.is('tsf', requ.commandAssignment.value.name);
assert.is('', assign1.arg.text);
assert.is(undefined, assign1.value);
update({ typed: 'tsf ', cursor: { start: 4, end: 4 } });
assert.is( 'VVVV', statuses);
assert.is(Status.ERROR, status);
assert.is('tsf', requ.commandAssignment.value.name);
assert.is('', assign1.arg.text);
assert.is(undefined, assign1.value);
update({ typed: 'tsf 1', cursor: { start: 5, end: 5 } });
assert.is( 'VVVVV', statuses);
assert.is(Status.VALID, status);
assert.is('tsf', requ.commandAssignment.value.name);
assert.is('1', assign1.arg.text);
assert.is(1, assign1.value);
assert.is('number', typeof assign1.value);
update({ typed: 'tsf x', cursor: { start: 5, end: 5 } });
assert.is( 'VVVVE', statuses);
assert.is(Status.ERROR, status);
assert.is('tsf', requ.commandAssignment.value.name);
assert.is('x', assign1.arg.text);
assert.is(undefined, assign1.value);
update({ typed: 'tsf 1.5', cursor: { start: 7, end: 7 } });
assert.is( 'VVVVVVV', statuses);
assert.is(Status.VALID, status);
assert.is('tsf', requ.commandAssignment.value.name);
assert.is('1.5', assign1.arg.text);
assert.is(1.5, assign1.value);
assert.is('number', typeof assign1.value);
};
exports.testElement = function(options) {

View File

@ -203,6 +203,34 @@ exports.testIncrDecr = function() {
check('tsu 10', KEY_DOWNS_TO, 'tsu 9');
check('tsu 100', KEY_DOWNS_TO, 'tsu 10');
check('tsf -70', KEY_UPS_TO, 'tsf -6.5');
check('tsf -6.5', KEY_UPS_TO, 'tsf -6');
check('tsf -6', KEY_UPS_TO, 'tsf -4.5');
check('tsf -4.5', KEY_UPS_TO, 'tsf -3');
check('tsf -4', KEY_UPS_TO, 'tsf -3');
check('tsf -3', KEY_UPS_TO, 'tsf -1.5');
check('tsf -1.5', KEY_UPS_TO, 'tsf 0');
check('tsf 0', KEY_UPS_TO, 'tsf 1.5');
check('tsf 1.5', KEY_UPS_TO, 'tsf 3');
check('tsf 2', KEY_UPS_TO, 'tsf 3');
check('tsf 3', KEY_UPS_TO, 'tsf 4.5');
check('tsf 5', KEY_UPS_TO, 'tsf 6');
check('tsf 100', KEY_UPS_TO, 'tsf -6.5');
check('tsf -70', KEY_DOWNS_TO, 'tsf 11.5');
check('tsf -6.5', KEY_DOWNS_TO, 'tsf -6.5');
check('tsf -6', KEY_DOWNS_TO, 'tsf -6.5');
check('tsf -4.5', KEY_DOWNS_TO, 'tsf -6');
check('tsf -4', KEY_DOWNS_TO, 'tsf -4.5');
check('tsf -3', KEY_DOWNS_TO, 'tsf -4.5');
check('tsf -1.5', KEY_DOWNS_TO, 'tsf -3');
check('tsf 0', KEY_DOWNS_TO, 'tsf -1.5');
check('tsf 1.5', KEY_DOWNS_TO, 'tsf 0');
check('tsf 2', KEY_DOWNS_TO, 'tsf 1.5');
check('tsf 3', KEY_DOWNS_TO, 'tsf 1.5');
check('tsf 5', KEY_DOWNS_TO, 'tsf 4.5');
check('tsf 100', KEY_DOWNS_TO, 'tsf 11.5');
// Bug 707007 - GCLI increment and decrement operations cycle through
// selection options in the wrong order
check('tselarr 1', KEY_DOWNS_TO, 'tselarr 2');

View File

@ -114,10 +114,10 @@ exports.testActivate = function(options) {
options: [ 'true' ]
}, options);
type('asdf', {
type('wxqy', {
important: false,
options: [ ],
error: 'Can\'t use \'asdf\'.'
error: 'Can\'t use \'wxqy\'.'
}, options);
type('', { }, options);

View File

@ -72,6 +72,7 @@ mockCommands.setup = function() {
canon.addCommand(mockCommands.tsb);
canon.addCommand(mockCommands.tss);
canon.addCommand(mockCommands.tsu);
canon.addCommand(mockCommands.tsf);
canon.addCommand(mockCommands.tsn);
canon.addCommand(mockCommands.tsnDif);
canon.addCommand(mockCommands.tsnExt);
@ -99,6 +100,7 @@ mockCommands.shutdown = function() {
canon.removeCommand(mockCommands.tsb);
canon.removeCommand(mockCommands.tss);
canon.removeCommand(mockCommands.tsu);
canon.removeCommand(mockCommands.tsf);
canon.removeCommand(mockCommands.tsn);
canon.removeCommand(mockCommands.tsnDif);
canon.removeCommand(mockCommands.tsnExt);
@ -231,6 +233,12 @@ mockCommands.tsu = {
exec: createExec('tsu')
};
mockCommands.tsf = {
name: 'tsf',
params: [ { name: 'num', type: { name: 'number', allowFloat: true, max: 11.5, min: -6.5, step: 1.5 } } ],
exec: createExec('tsf')
};
mockCommands.tsn = {
name: 'tsn'
};

View File

@ -92,6 +92,10 @@ typesNumberMax=%1$S is greater than maximum allowed: %2$S.
# message is displayed.
typesNumberMin=%1$S is smaller than minimum allowed: %2$S.
# LOCALIZATION NOTE (typesNumberNotInt): When the command line is passed a
# number, but the number has a decimal part and floats are not allowed.
typesNumberNotInt='%S' must be an integer.
# LOCALIZATION NOTE (typesSelectionNomatch): When the command line is passed
# an option with a limited number of correct values, but the passed value is
# not one of them, this error message is displayed.