mirror of https://github.com/svaarala/duktape.git
Sami Vaarala
12 years ago
1 changed files with 376 additions and 0 deletions
@ -0,0 +1,376 @@ |
|||||
|
/* |
||||
|
* Array.prototype.sort() tests. |
||||
|
* |
||||
|
* The current implementation is qsort() with a random pivot, which makes |
||||
|
* this test non-deterministic unless the internal randomizer state is |
||||
|
* initialized with a fixed seed. |
||||
|
*/ |
||||
|
|
||||
|
function dumpValue(v) { |
||||
|
var i, n; |
||||
|
var tmp; |
||||
|
var t; |
||||
|
|
||||
|
n = v.length; |
||||
|
tmp = []; |
||||
|
for (i = 0; i < n; i++) { |
||||
|
if (v.hasOwnProperty(i)) { |
||||
|
t = v[i]; |
||||
|
if (typeof t === 'function') { |
||||
|
tmp.push('function'); |
||||
|
} else if (typeof t === 'object') { |
||||
|
tmp.push('object'); |
||||
|
} else { |
||||
|
tmp.push(String(t)); |
||||
|
} |
||||
|
} else { |
||||
|
tmp.push('nonexistent'); |
||||
|
} |
||||
|
} |
||||
|
|
||||
|
return tmp.join(','); |
||||
|
} |
||||
|
|
||||
|
function test(this_value, args) { |
||||
|
var t; |
||||
|
|
||||
|
try { |
||||
|
t = Array.prototype.sort.apply(this_value, args); |
||||
|
print(dumpValue(t)); |
||||
|
} catch (e) { |
||||
|
print(e.name); |
||||
|
} |
||||
|
} |
||||
|
|
||||
|
function printDesc(obj, key) { |
||||
|
var pd = Object.getOwnPropertyDescriptor(obj, key); |
||||
|
if (!pd) { |
||||
|
print(key, 'nonexistent'); |
||||
|
return; |
||||
|
} |
||||
|
|
||||
|
print(key, |
||||
|
'value=' + pd.value, |
||||
|
'writable=' + pd.writable, |
||||
|
'enumerable=' + pd.enumerable, |
||||
|
'configurable=' + pd.configurable); |
||||
|
} |
||||
|
|
||||
|
/*=== |
||||
|
===*/ |
||||
|
|
||||
|
print('basic'); |
||||
|
|
||||
|
function basicTest() { |
||||
|
/* Note that all cases without an explicit compare function will sort |
||||
|
* elements as strings after ToString() coercion, e.g. 1 and 2 are |
||||
|
* compared with string comparison "1" vs "2". Thus, "1" < "10" < "2". |
||||
|
*/ |
||||
|
|
||||
|
// empty case
|
||||
|
|
||||
|
test([], []); |
||||
|
test({ length: 0 }, []); |
||||
|
test({}, []); |
||||
|
|
||||
|
// one element case
|
||||
|
|
||||
|
test([1], []); |
||||
|
|
||||
|
// a few different lengths, in various orders
|
||||
|
|
||||
|
test([1,2], []); |
||||
|
test([2,1], []); |
||||
|
test([1,2,3], []); |
||||
|
test([1,3,2], []); |
||||
|
test([2,1,3], []); |
||||
|
test([2,3,1], []); |
||||
|
test([3,1,2], []); |
||||
|
test([3,2,1], []); |
||||
|
|
||||
|
// a few different lengths with some equal elements
|
||||
|
|
||||
|
test([1,1], []); |
||||
|
test([1,1,2], []); |
||||
|
test([1,2,1], []); |
||||
|
test([2,1,1], []); |
||||
|
|
||||
|
// undefined elements will be pushed to the end, and 'nonexistent' elements
|
||||
|
// even farther
|
||||
|
|
||||
|
obj = [1,2,undefined,3,4,undefined,5,6,undefined,7,8,undefined,9,10]; |
||||
|
obj.length = 20; |
||||
|
test(obj, []); |
||||
|
|
||||
|
// sparse array
|
||||
|
|
||||
|
obj = [1,5,10]; |
||||
|
obj[100] = 2; |
||||
|
obj[101] = 7; |
||||
|
obj[102] = 13; |
||||
|
obj[50] = 12; |
||||
|
test(obj, []); |
||||
|
|
||||
|
// elements are compared as strings by default
|
||||
|
|
||||
|
test([1,2,3,4,5,6,7,8,9,10], []); |
||||
|
test([2,4,6,8,10,1,3,5,7,9], []); |
||||
|
|
||||
|
// explicit sort function for numbers, ascending and descending
|
||||
|
|
||||
|
test([1,2,3,4,5,6,7,8,9,10], [ function(a,b) { return a-b; } ]); |
||||
|
test([2,4,6,8,10,1,3,5,7,9], [ function(a,b) { return b-a; } ]); |
||||
|
} |
||||
|
|
||||
|
try { |
||||
|
basicTest(); |
||||
|
} catch (e) { |
||||
|
print(e); |
||||
|
} |
||||
|
|
||||
|
/*=== |
||||
|
===*/ |
||||
|
|
||||
|
print('exhaustive'); |
||||
|
|
||||
|
function cloneArray(x) { |
||||
|
return x.map(function(x) { return x; }); |
||||
|
} |
||||
|
|
||||
|
function makeNonArray(x) { |
||||
|
var res = {}; |
||||
|
var i; |
||||
|
|
||||
|
for (i = 0; i < x.length; i++) { |
||||
|
if (x.hasOwnProperty(i)) { |
||||
|
res[i] = x[i]; |
||||
|
} |
||||
|
} |
||||
|
res.length = x.length; |
||||
|
|
||||
|
return res; |
||||
|
} |
||||
|
|
||||
|
function exhaustiveSortTest(input, make_sparse, make_nonarray) { |
||||
|
function f(arr, rem) { |
||||
|
var i, n; |
||||
|
var new_arr, new_rem; |
||||
|
var tmp; |
||||
|
|
||||
|
n = rem.length; |
||||
|
if (n == 0) { |
||||
|
if (make_sparse) { |
||||
|
n = arr.length; |
||||
|
arr[10000] = 'foo'; |
||||
|
delete arr[10000]; |
||||
|
arr.length = n; |
||||
|
} else if (make_nonarray) { |
||||
|
arr = makeNonArray(arr); |
||||
|
} |
||||
|
|
||||
|
tmp = dumpValue(arr); |
||||
|
Array.prototype.sort.call(arr); |
||||
|
print(tmp, '--', dumpValue(arr)); |
||||
|
} else { |
||||
|
for (i = 0; i < n; i++) { |
||||
|
new_arr = cloneArray(arr); |
||||
|
new_rem = cloneArray(rem); |
||||
|
new_arr.push(new_rem.splice(i, 1)[0]); |
||||
|
f(new_arr, new_rem); |
||||
|
} |
||||
|
} |
||||
|
} |
||||
|
|
||||
|
f([], input); |
||||
|
} |
||||
|
|
||||
|
try { |
||||
|
exhaustiveSortTest([1,2,3], false, false); |
||||
|
exhaustiveSortTest([1,2,3], true, false); |
||||
|
exhaustiveSortTest([1,2,3], false, true); |
||||
|
exhaustiveSortTest([1,2,3,3,4,5], false, false); |
||||
|
exhaustiveSortTest([1,2,3,3,4,5], true, false); |
||||
|
exhaustiveSortTest([1,2,3,3,4,5], false, true); |
||||
|
} catch (e) { |
||||
|
print(e); |
||||
|
} |
||||
|
|
||||
|
/*=== |
||||
|
===*/ |
||||
|
|
||||
|
// http://en.wikipedia.org/wiki/Linear_congruential_generator#Parameters_in_common_use
|
||||
|
|
||||
|
print('random strings'); |
||||
|
|
||||
|
var rnd_x = 123; |
||||
|
function prng(max) { |
||||
|
rnd_x = (rnd_x * 16807) % 0x7fffffff; |
||||
|
return rnd_x % max; |
||||
|
} |
||||
|
|
||||
|
function rndString(maxLen) { |
||||
|
var len = prng(maxLen); |
||||
|
var i; |
||||
|
var res = ''; |
||||
|
|
||||
|
for (i = 0; i < len; i++) { |
||||
|
res += String.fromCharCode(0x41 + prng(26)); |
||||
|
} |
||||
|
|
||||
|
return res; |
||||
|
} |
||||
|
|
||||
|
function randomStringsTest() { |
||||
|
var arr = []; |
||||
|
var i; |
||||
|
var str1, str2; |
||||
|
|
||||
|
for (i = 0; i < 10000; i++) { |
||||
|
arr.push(rndString(8)); |
||||
|
} |
||||
|
|
||||
|
print(arr); |
||||
|
|
||||
|
arr.sort(); |
||||
|
str1 = arr.toString(); |
||||
|
print(str1); |
||||
|
|
||||
|
arr.sort(); |
||||
|
str2 = arr.toString(); |
||||
|
print(str1 === str2); |
||||
|
} |
||||
|
|
||||
|
randomStringsTest(); |
||||
|
|
||||
|
/*=== |
||||
|
===*/ |
||||
|
|
||||
|
print('attributes'); |
||||
|
|
||||
|
function attributeTest() { |
||||
|
var obj; |
||||
|
|
||||
|
// attributes are kept when both swapped elements exist: two [[Get]]+[[Put]]
|
||||
|
// pairs are used
|
||||
|
|
||||
|
obj = []; |
||||
|
Object.defineProperties(obj, { |
||||
|
'0': { value: 'foo', writable: true, enumerable: false, configurable: true }, |
||||
|
'1': { value: 'bar', writable: true, enumerable: true, configurable: true }, |
||||
|
}); |
||||
|
printDesc(obj, '0'); printDesc(obj, '1'); printDesc(obj, 'length'); |
||||
|
obj.sort(); |
||||
|
printDesc(obj, '0'); printDesc(obj, '1'); printDesc(obj, 'length'); |
||||
|
|
||||
|
// same test but make attributes non-configurable; this must not prevent sorting
|
||||
|
// because writability is sufficient when swapped pairs exist
|
||||
|
|
||||
|
obj = []; |
||||
|
Object.defineProperties(obj, { |
||||
|
'0': { value: 'foo', writable: true, enumerable: false, configurable: false }, |
||||
|
'1': { value: 'bar', writable: true, enumerable: true, configurable: false }, |
||||
|
}); |
||||
|
printDesc(obj, '0'); printDesc(obj, '1'); printDesc(obj, 'length'); |
||||
|
obj.sort(); |
||||
|
printDesc(obj, '0'); printDesc(obj, '1'); printDesc(obj, 'length'); |
||||
|
|
||||
|
// if only one end of the swapped pair exists, the other is [[Delete]]'d
|
||||
|
// and [[Put]] will create a property with default [[Put]] attributes, i.e.
|
||||
|
// writable, configurable, and enumerable
|
||||
|
|
||||
|
obj = []; |
||||
|
Object.defineProperties(obj, { |
||||
|
'1': { value: 'bar', writable: true, enumerable: false, configurable: true }, |
||||
|
}); |
||||
|
printDesc(obj, '0'); printDesc(obj, '1'); printDesc(obj, 'length'); |
||||
|
obj.sort(); |
||||
|
printDesc(obj, '0'); printDesc(obj, '1'); printDesc(obj, 'length'); |
||||
|
|
||||
|
// same test but '1' is non-configurable -> TypeError
|
||||
|
// the final state of 'obj' is implementation defined: the specification doesn't
|
||||
|
// mandate whether or not the [[Put]] for '0' should precede the [[Delete]] for '1'.
|
||||
|
|
||||
|
obj = []; |
||||
|
Object.defineProperties(obj, { |
||||
|
'1': { value: 'bar', writable: true, enumerable: false, configurable: false }, |
||||
|
}); |
||||
|
printDesc(obj, '0'); printDesc(obj, '1'); printDesc(obj, 'length'); |
||||
|
obj.sort(); |
||||
|
printDesc(obj, '0'); printDesc(obj, '1'); printDesc(obj, 'length'); |
||||
|
} |
||||
|
|
||||
|
try { |
||||
|
attributeTest(); |
||||
|
} catch (e) { |
||||
|
print(e); |
||||
|
} |
||||
|
|
||||
|
/*=== |
||||
|
===*/ |
||||
|
|
||||
|
print('comparefunction'); |
||||
|
|
||||
|
function compareFunctionTest() { |
||||
|
// the default comparison function compares strings with ToString(); verify
|
||||
|
// its behavior first
|
||||
|
|
||||
|
// basic ascending numeric sort
|
||||
|
|
||||
|
test([ '1', 5, '3e0', '9.8', { valueOf: function() { return '7' } }], |
||||
|
[ function(a,b) { return Number(a) - Number(b); } ]); |
||||
|
|
||||
|
// basic descending numeric sort
|
||||
|
|
||||
|
test([ '1', 5, '3e0', '9.8', { valueOf: function() { return '7' } }], |
||||
|
[ function(a,b) { return Number(b) - Number(a); } ]); |
||||
|
|
||||
|
// compareFn TypeError for a non-callable compareFn only occurs when
|
||||
|
// it is about to be called for the first time
|
||||
|
|
||||
|
test([1], [ 'non-callable' ]); // *no* TypeError
|
||||
|
test([1,2], [ 'non-callable' ]); // -> TypeError
|
||||
|
test([1,2], [ {} ]); // -> TypeError
|
||||
|
test([1,2], [ { toLocaleString: 'non-callable' } ]); // -> TypeError
|
||||
|
|
||||
|
// compareFn only gets called if both SortCompare() arguments exist
|
||||
|
// and neither is 'undefined'. In this test only one element exists
|
||||
|
// and is not undefined, so no compareFn calls can be made
|
||||
|
|
||||
|
obj = []; |
||||
|
obj.length = 10; |
||||
|
obj[0] = undefined; |
||||
|
obj[2] = undefined; |
||||
|
obj[3] = 'foo'; |
||||
|
obj[6] = undefined; |
||||
|
obj[7] = undefined; |
||||
|
obj[9] = undefined; |
||||
|
test(obj, [ function(a,b) { print('should never be called'); } ]); |
||||
|
} |
||||
|
|
||||
|
try { |
||||
|
compareFunctionTest(); |
||||
|
} catch (e) { |
||||
|
print(e); |
||||
|
} |
||||
|
|
||||
|
/* |
||||
|
automatic comparefn |
||||
|
manual comparefn |
||||
|
|
||||
|
already ascending |
||||
|
already descending |
||||
|
random |
||||
|
|
||||
|
sparse array with an inherited index showing through |
||||
|
-> impl dependent, what does e.g. node do? |
||||
|
|
||||
|
non-extensible object -> impl dep |
||||
|
non-configurable elem (below length) -> impl dep |
||||
|
non-writable elem (below length) -> impl dep |
||||
|
accessor elem (below length) |
||||
|
|
||||
|
non-configrable elem above length -> NOT impl dep |
||||
|
non-writable elem (above length) -> NOT impl dep |
||||
|
accessor elem (above length) |
||||
|
*/ |
||||
|
|
Loading…
Reference in new issue