blob: ad5a6888f2ef03d89b8ad5efd15a5e753b96fcc9 [file] [log] [blame]
//-------------------------------------------------------------------------------------------------------
// Copyright (C) Microsoft. All rights reserved.
// Licensed under the MIT license. See LICENSE.txt file in the project root for full license information.
//-------------------------------------------------------------------------------------------------------
WScript.LoadScriptFile("..\\UnitTestFramework\\UnitTestFramework.js");
var nextCount = 0;
var returnCount = 0;
var value1 = 1;
var done1 = false;
var iterable = {};
var shouldNextThrow = false;
var shouldReturnThrow = false;
iterable[Symbol.iterator] = function () {
return {
next: function() {
nextCount++;
if (shouldNextThrow) { throw new Error('Exception from next function'); }
return {value : value1, done:done1};
},
return: function (value) {
returnCount++;
if (shouldReturnThrow) { throw new Error('Exception from return function'); }
return {done:true};
}
};
};
var obj = {
set prop(val) {
throw new Error('From setter');
}
};
var tests = [
{
name : "Destructuring - no .return function defined is a valid scenario",
body : function () {
var iterable = {};
iterable[Symbol.iterator] = function () {
return {
next : function () {
return {
value : 10,
done : false
};
}
};
};
var [a] = iterable;
assert.areEqual(a, 10, "Destructuring declaration calls next on iterator and should get 10");
var b;
[b] = iterable;
assert.areEqual(b, 10, "Destructuring expression calls next on iterator and should get 10");
}
},
{
name : "Destructuring - validating that the iterable has .return field but not as a function",
body : function () {
var returnFnc = undefined;
var iterable = {};
iterable[Symbol.iterator] = function() {
return {
next: function() {
return {value:10, done:false};
},
return: returnFnc
};
};
var a;
assert.doesNotThrow(function() {
[a] = iterable;
}, "iterator has return field which returns 'undefined' and that should not throw");
assert.doesNotThrow(function() {
returnFnc = null;
[a] = iterable;
}, "iterator has return field which returns 'null' and that should not throw");
assert.throws(function () { returnFnc = {}; [a] = iterable; }, TypeError,
"return field is not a function and returns other than 'undefined'/'null' should throw Type Error");
}
},
{
name : "Destructuring - basic validation with .return function when the pattern is empty or has one element",
body : function () {
nextCount = 0;
returnCount = 0;
[] = iterable;
assert.areEqual(nextCount, 0, "Empty LHS pattern should not call the next function");
assert.areEqual(returnCount, 1, "Evaluation of empty LHS pattern will call return function");
nextCount = 0;
returnCount = 0;
var [] = iterable;
assert.areEqual(nextCount, 0, "Empty LHS declaration pattern should not call the next function");
assert.areEqual(returnCount, 1, "Evaluation of empty LHS declaration pattern call return function");
nextCount = 0;
returnCount = 0;
[,] = iterable;
assert.areEqual(nextCount, 1, "Comma operator causes the next to be called");
assert.areEqual(returnCount, 1, "Exhausting the LHS pattern (after comma) will call the call return function");
var a;
nextCount = 0;
returnCount = 0;
[a] = iterable;
assert.areEqual(nextCount, 1, "Evaluating destructuring element will call next function");
assert.areEqual(returnCount, 1, "Exhausting the LHS pattern (after comma) will call the call return function");
}
},
{
name : "Destructuring - validation with .return function with nesting pattern",
body : function () {
nextCount = 0;
returnCount = 0;
[[a]] = [iterable];
assert.areEqual(nextCount, 1, "Nested array pattern will call next function when 'a' is evauluated");
assert.areEqual(returnCount, 1, "When nested array pattern exhausts the return function will be called");
nextCount = 0;
returnCount = 0;
value1 = iterable;
[a, [a]] = iterable;
assert.areEqual(nextCount, 3, "Recursive iterators on RHS - next function will be called 3 times in order to exhaust elements");
assert.areEqual(returnCount, 2, "Recursive iterators on RHS - return function for two nested iterators will be called");
value1 = 1; // Reset it back
nextCount = 0;
returnCount = 0;
value1 = iterable;
var [a, [a]] = iterable;
assert.areEqual(nextCount, 3, "Array declaration - Recursive iterators on RHS - next function will be called 3 times in order to exhaust elements");
assert.areEqual(returnCount, 2, "Array declaration - Recursive iterators on RHS - return function for two nested iterators will be called");
value1 = 1; // Reset it back
}
},
{
name : "Destructuring - change .done to true will not call .return function",
body : function () {
nextCount = 0;
returnCount = 0;
done1 = true;
var [a] = iterable;
assert.areEqual(nextCount, 1, "next function is called to evaluate the element on the LHS");
assert.areEqual(returnCount, 0, "'done' is set true on next function call - which will ensure that return function is not called");
done1 = false;
}
},
{
name : "Destructuring - validating that exception will call the .return function",
body : function () {
nextCount = 0;
returnCount = 0;
assert.throws(function () { [obj.prop] = iterable; }, Error, "Pattern throws error while assigning .value", "From setter");
assert.areEqual(returnCount, 1, "Abrupt completion due to exception will call the return function");
nextCount = 0;
returnCount = 0;
value1 = iterable;
assert.throws(function () { [k, [obj.prop]] = iterable; }, Error, "Nested pattern throws error while assigning .value", "From setter");
assert.areEqual(returnCount, 2, "Nested recursive iterators - abrupt completion due to exception will call the return function");
value1 = 1;
nextCount = 0;
returnCount = 0;
value1 = iterable;
assert.throws(function () { [[[[obj.prop]]]] = iterable; }, Error, "Nested pattern throws error while assigning .value", "From setter");
assert.areEqual(nextCount, 4, "Nested recursing iterators - 4 times next function called due to 4 level depth of array pattern");
assert.areEqual(returnCount, 4, "Nested recursing iterators - 4 times return function called due to abrupt comletion");
value1 = 1;
done1 = true;
nextCount = 0;
returnCount = 0;
assert.throws(function () { [obj.prop] = iterable; }, Error, "Pattern throws error while assigning .value", "From setter");
assert.areEqual(returnCount, 0, "Ensuring that return function is not called when 'done' is set to true during abrupt completion");
done1 = false; // Reset it back.
}
},
{
name : "Destructuring - chained iterators will have their .return function called correctly",
body : function () {
var nextCount1 = 0;
var nextCount2 = 0;
var returnCount1 = 0;
var returnCount2 = 0;
var iterable = {};
iterable[Symbol.iterator] = function() {
return {
next: function() {
nextCount1++;
return { value: iterable2, done: false };
},
return: function() {
returnCount1++;
return {done:true};
},
};
};
var iterable2 = {};
iterable2[Symbol.iterator] = function() {
return {
next: function() {
nextCount2++;
return { value: [0], done: false };
},
return: function() {
returnCount2++;
return {done:true};
}
};
};
nextCount1 = 0;
nextCount2 = 0;
returnCount1 = 0;
returnCount2 = 0;
assert.throws(function () {[[obj.prop]] = iterable; }, Error, "Pattern throws error while assigning .value", "From setter");
assert.areEqual(nextCount1, 1, "next function for the first iterator is called");
assert.areEqual(nextCount2, 1, "next function for the second iterator is called");
assert.areEqual(returnCount1, 1, "return function for the second iterator is called" );
assert.areEqual(returnCount2, 1, "return function for the first iterator is called");
}
},
{
name : "Destructuring - .return function should not be called when .next function throws",
body : function () {
shouldNextThrow = true;
returnCount = 0;
assert.throws(function () { var [a] = iterable; }, Error, "Array declaration - Calling .next throws", "Exception from next function");
assert.areEqual(returnCount, 0, "Ensuring that return function is not called in array declaration pattern");
assert.throws(function () { [a] = iterable; }, Error, "Array expression - Calling .next throws", "Exception from next function");
assert.areEqual(returnCount, 0, "Ensuring that return function is not called in array expression pattern");
assert.throws(function () { var [...a] = iterable; }, Error, "Array declaration with rest - Calling .next throws", "Exception from next function");
assert.areEqual(returnCount, 0, "Ensuring that return function is not called when evalauting rest in array declaration pattern");
assert.throws(function () { [...a] = iterable; }, Error, "Array expression with rest - Calling .next throws", "Exception from next function");
assert.areEqual(returnCount, 0, "Ensuring that return function is not called when evaluating rest in array expression pattern");
shouldNextThrow = false;
}
},
{
name : "Destructuring - .return function should not be called when fetching .value throws",
body : function () {
var iterable2 = {};
iterable2[Symbol.iterator] = function() {
return {
next: function() {
return {get value () { throw new Error('Exception while getting value'); }, done:false};
},
return: function() {
assert.fail('return should not be called');
return {};
},
};
};
assert.throws(function () { [a] = iterable2; }, Error, "Fetch .value throws", "Exception while getting value");
}
},
{
name : "Destructuring - .return function can also throw",
body : function () {
shouldReturnThrow = true;
assert.throws(function () { [a] = iterable; }, Error, "Calling .return throws", "Exception from return function");
shouldReturnThrow = false;
}
},
{
name : "Destructuring - caller and .return function both throw and caller's exception wins",
body : function () {
shouldReturnThrow = true;
returnCount = 0;
assert.throws(function () { [obj.prop] = iterable; }, Error, "Setting value will throw", "From setter");
assert.areEqual(returnCount, 1, "Ensuring that return function is called");
shouldReturnThrow = false;
}
},
{
name : "Destructuring - with generator",
body : function () {
var finallyCount = 0;
function *gf() {
try {
yield 1;
assert.fail('should not reach after yield');
}
finally {
finallyCount++;
}
assert.fail('should not reach after finally');;
}
[a] = gf();
assert.areEqual(finallyCount, 1, "Exhausting pattern will call return function on generator, which will execute finally block");
finallyCount = 0;
assert.throws(function () { [obj.prop] = gf(); }, Error, "Assigning value to destructuring element can throw", "From setter");
assert.areEqual(finallyCount, 1, "Exception causes to call return function on generator, which will execute finally block");
function* gf2() {
yield 1;
assert.fail('should not reach after yield');
}
var returnCount = 0;
gf2.prototype.return = function() {
returnCount++;
return {};
};
[a] = gf2();
assert.areEqual(returnCount, 1, "Exhausting pattern will call return function on generator");
gf2.prototype.return = function() {
returnCount++;
throw new Error('Exception from return function');
};
assert.throws(function () { [a] = gf2(); }, Error, "Return function throws", "Exception from return function");
returnCount = 0;
assert.throws(function () { [obj.prop] = gf2(); }, Error, "Exception at destructuring element wins", "From setter");
assert.areEqual(returnCount, 1, "Exception causes to call return function on generator");
}
},
{
name : "Destructuring - at function parameter",
body : function () {
nextCount = 0;
returnCount = 0;
(function([a, b]) {})(iterable);
assert.areEqual(nextCount, 2, "next function will be called 2 times to evaluate 2 elements in the pattern");
assert.areEqual(returnCount, 1, "return function will be called once the pattern exhausts");
shouldReturnThrow = true;
assert.throws(function () { (function([a, b]) {})(iterable) }, Error, "Calling return function while at param throws", "Exception from return function");
shouldReturnThrow = false;
}
},
{
name : "Destructuring - assigning to rest parameter can throw but should not call .return function",
body : function () {
function* gf() {
yield 1;
yield 2;
}
gf.prototype.return = function() {
assert.fail('return function should not be called');
};
assert.throws(function () { [...obj.prop] = gf(); }, Error, "Assigning to rest head can throw", "From setter");
}
},
{
name : "Destructuring - .next throws during rest assignment but it should not call the .return function",
body : function () {
returnCount = 0;
shouldNextThrow = true;
assert.throws(function () { var [...a] = iterable; }, Error, "next function throws in the array declaration evaluation", "Exception from next function");
assert.throws(function () { [...a] = iterable; }, Error, "next function throws in the array expression evaluation", "Exception from next function");
shouldNextThrow = false;
assert.areEqual(returnCount, 0, "return function should be called even when the next function throws");
}
},
{
name : "Destructuring - has yield as initializer",
body : function () {
var returnCalled = 0;
function *innerGen () { yield undefined; yield 21; }
innerGen.prototype.return = function () {
returnCalled++;
return {};
};
var x;
var iter = (function * () {
([x = yield] = innerGen());
}());
iter.next();
var iterationResult = iter.next(10);
assert.areEqual(x, 10, "calling next with value 10 will assign 'x' to 10");
assert.isTrue(iterationResult.done, "iterator is completed as there is nothing more to yield");
assert.areEqual(returnCalled, 1, "Destructuring elements exhaust and that should call return function");
}
},
{
name : "Destructuring - yield in the pattern and inner return throws",
body : function () {
function *innerGen () { yield undefined; }
innerGen.prototype.return = function () {
throw new Error('Exception from return function');
};
var x;
var iter = (function * () {
([x = yield] = innerGen());
assert.fail('Unreachable code');
}());
iter.next();
assert.throws(function () {
iter.return();
}, Error, "calling return on the outer generator will call return on inner generator", 'Exception from return function');
}
},
{
name : "Destructuring - yield in the pattern and both caller and inner return throws",
body : function () {
function *innerGen () { yield undefined;}
var returnCalled = 0;
innerGen.prototype.return = function () {
returnCalled++;
throw new Error('Exception from return function');
};
var x;
var iter = (function * () {
([x = yield] = innerGen());
assert.fail('Unreachable code');
}());
iter.next();
assert.throws(function () {
iter.throw(new Error('Exception from outer throw'));
}, Error, "calling throw on the outer generator will call return on inner generator but outer exxception wins", 'Exception from outer throw');
assert.areEqual(returnCalled, 1, "ensuring that return function is called");
}
},
{
name : "Destructuring - .return will be called even before .next function is called if yield as return",
body : function () {
function *innerGen () { assert.fail('innerGen body is not executed');}
var returnCalled = 0;
innerGen.prototype.return = function () {
returnCalled++;
return {};
};
var x;
var iter = (function * () {
([{}[yield]] = innerGen());
assert.fail('Unreachable code');
}());
iter.next();
iter.return();
assert.areEqual(returnCalled, 1, "ensuring that return function is called");
}
},
{
name : "Destructuring - with rest - .return will be called even before .next function is called if yield as return",
body : function () {
function *innerGen () { assert.fail('innerGen body is not executed');}
var returnCalled = 0;
innerGen.prototype.return = function () {
returnCalled++;
return {};
};
var x;
var iter = (function * () {
([...{}[yield]] = innerGen());
assert.fail('Unreachable code');
}());
iter.next();
iter.return();
assert.areEqual(returnCalled, 1, "ensuring that return function is called");
}
},
{
name : "For..of - validation of calling .return function on abrupt loop break",
body : function () {
returnCount = 0;
for (i of iterable) {
break;
}
assert.areEqual(returnCount, 1, "return function is called as the loop is abruptly completed due to 'break'");
returnCount = 0;
(function () {
for (i of iterable) {
return;
}
})();
assert.areEqual(returnCount, 1, "return function is called as the loop is abruptly completed due to 'return'");
returnCount = 0;
(function () {
var loop = true;
outer2 : while (loop) {
loop = false;
for (i of iterable) {
continue outer2;
}
}
})();
assert.areEqual(returnCount, 1, "return function is called as the loop is abruptly completed due to 'continue'");
returnCount = 0;
(function () {
var loop = true;
outer3 : while (loop) {
loop = false;
for (i of iterable) {
break outer3;
}
}
})();
assert.areEqual(returnCount, 1, "return function is called as the loop is abruptly completed due to 'break label'");
nextCount = 0;
returnCount = 0;
assert.throws(function () {
for (i of iterable) {
(function () {
throw new Error('break loop by causing an exception');
})();
}
}, Error);
assert.areEqual(nextCount, 1, "next function is called once as the loop iterates only once");
assert.areEqual(returnCount, 1, "return function is called as the loop is abruptly completed due to an exception");
}
},
{
name : "For..of - validation of .return function with nesting pattern",
body : function () {
nextCount = 0;
returnCount = 0;
// Creating recursing iterator. the value is another iterator.
value1 = iterable;
(function() {
for (var iter of iterable) {
for (var iter2 of iter) {
return;
}
}
})();
assert.areEqual(nextCount, 2, "Nested iterators - next function is called 2 times, once for each loop");
assert.areEqual(returnCount, 2, "Nested iterators - return function is called 2 times as two loops are abruptly completed due to 'return'");
nextCount = 0;
returnCount = 0;
assert.throws(function() {
for (var iter of iterable) {
for (var iter2 of iter) {
throw new Error('error');
}
}
}, Error);
assert.areEqual(nextCount, 2, "Nested iterators - next function is called 2 times, once for each loop");
assert.areEqual(returnCount, 2, "Nested iterators - return function is called 2 times as two loops are abruptly completed due to exception");
}
},
{
name : "For..of - change .done to true will not call .return function",
body : function () {
returnCount = 0;
done1 = true;
for (var i of iterable) {
assert.fail('This will not reach as the .done is marked to true');
}
assert.areEqual(returnCount, 0, "Loop is completed as .done is set to true, this ensures that return function should not be called");
done1 = false;
}
},
{
name : "For..of - validating that causing an exception on assigning value to for..of head will call .return function",
body : function () {
returnCount = 0;
assert.throws(function () {for (obj.prop of iterable) {
assert.fail('Should not reach here as assigning to obj.prop throws');
}}, Error, "Assigning to loop head can throw", "From setter");
assert.areEqual(returnCount, 1, "Abrupt loop break due to exception in assigning value to head should call return function");
returnCount = 0;
value1 = iterable;
assert.throws(function () {
for (var iter1 of iterable) {
for (obj.prop of iter1) {
assert.fail('Should not reach here as assigning to obj.prop throws');
}
}
}, Error, "Assigning to loop head can throw", "From setter");
assert.areEqual(returnCount, 2, "Exception caused in inner loop when assigning value to head should call the return function");
value1 = 1;
}
},
{
name : "For..of - .return function should not be called when .next function throws",
body : function () {
returnCount = 0;
shouldNextThrow = true;
assert.throws(function () { for (var i of iterable) { } }, Error, "Calling .next throws", "Exception from next function");
shouldNextThrow = false;
assert.areEqual(returnCount, 0, "Ensuring that exception in next function should not call the return function");
}
},
{
name : "For..of - .return function should not be called when fetching .value throws",
body : function () {
var iterable2 = {};
iterable2[Symbol.iterator] = function() {
return {
next: function() {
return {get value () { throw new Error('Exception while getting value'); }, done:false};
},
return: function() {
assert.fail('return should not be called');
},
};
};
assert.throws(function () { for (var i of iterable2) { } }, Error,
".value causes an exception but that should not call the return function",
"Exception while getting value");
}
},
{
name : "For..of - .return function can also throw",
body : function () {
shouldReturnThrow = true;
assert.throws(function () { for (var i of iterable) { break; } }, Error, ".return function throws", "Exception from return function");
shouldReturnThrow = false;
}
},
{
name : "For..of - caller and .return function both throw and caller's exception wins",
body : function () {
shouldReturnThrow = true;
returnCount = 0;
assert.throws(function () { for (obj.prop of iterable) { break; } }, Error, "Setting value will throw", "From setter");
assert.areEqual(returnCount, 1, "Ensuring that abrupt loop completion due to exception will call return function");
shouldReturnThrow = false;
}
},
{
name : "For..of - with generator",
body : function () {
var finallyCount = 0;
function *gf() {
try {
yield 1;
assert.fail('Should not reach here after yield');
}
finally {
finallyCount++;
}
assert.fail('Should not reach here after finally');;
}
for (var i of gf()) {
break;
}
assert.areEqual(finallyCount, 1, "'break' causes the return function called which should execute the finally block");
finallyCount = 0;
assert.throws(function () { for (obj.prop of gf()) { } }, Error, "Setting value will throw", "From setter");
assert.areEqual(finallyCount, 1, "Exception causes the return function called which should execute the finally block");
function* gf2() {
yield 1;
assert.fail('Should not reach here after yield');
}
var returnCount = 0;
gf2.prototype.return = function() {
returnCount++;
return {};
};
for (var i of gf2()) {
break;
}
assert.areEqual(returnCount, 1, "Loop break due to 'break' should call the return function on the generator");
gf2.prototype.return = function() {
returnCount++;
throw new Error('Exception from return function');
};
assert.throws(function () { for (i of gf2()) { break; } }, Error, "return function throws", "Exception from return function");
returnCount = 0;
assert.throws(function () { for (obj.prop of gf2()) { } }, Error, "Exception at destructuring element wins", "From setter");
assert.areEqual(returnCount, 1, "Exception in loop should call the return function");
}
},
{
name : "Iterator close with yield *",
body : function () {
var returnCalled = 0;
let innerGen = function*() { yield 1; yield 2 };
innerGen.prototype.return = function () {
returnCalled++;
return {};
};
function* gf() { yield* innerGen() }
for (var i of gf()) {
break;
}
assert.areEqual(returnCalled, 1, "Loop break due to 'break' should call the return function, yield * should propagate that to inner generator");
returnCalled = 0;
(function() {
for (var i of gf()) {
return;
}
})();
assert.areEqual(returnCalled, 1, "Loop break due to 'return' should call the return function, yield * should propagate that to inner generator");
returnCalled = 0;
assert.throws(function () { for (var i of gf()) { throw new Error(''); } }, Error);
assert.areEqual(returnCalled, 1, "Loop break due to 'throw' should call the return function, yield * should propagate that to inner generator");
returnCalled = 0;
var [x2] = gf();
assert.areEqual(returnCalled, 1, "Exhausting destructuring element will call the return function");
}
},
{
name : "Array.from - Iterator closing when mapping function throws",
body : function () {
var mapFn = function() {
throw new Error('');
};
returnCount = 0;
assert.throws(function () {
Array.from(iterable, mapFn);
}, Error);
assert.areEqual(returnCount, 1, "Exception in mapping function in Array.from should call the return function");
}
},
{
name : "Array.from - .return function should not be called when .next function throws",
body : function () {
shouldNextThrow = true;
returnCount = 0;
assert.throws(function () {
Array.from(iterable);
}, Error);
assert.areEqual(returnCount, 0, "next function causes exception which should not call the return function");
shouldNextThrow = false;
}
},
{
name : "Array.from - both mapping function and .return throw but the outer exception wins",
body : function () {
var mapFn = function() {
throw new Error('Exception from map function');
};
returnCount = 0;
shouldReturnThrow = true;
assert.throws(function () {
Array.from(iterable, mapFn);
}, Error, 'Validate that the exception from return function is skipped', 'Exception from map function');
shouldReturnThrow = false;
assert.areEqual(returnCount, 1, "return function is called when mapping function throws");
}
},
{
name : "Map - calling .return function in the event of exception",
body : function () {
returnCount = 0;
assert.throws(function () {
new Map(iterable);
}, TypeError);
assert.areEqual(returnCount, 1, "return function is called when iterator does not return object");
Map.prototype.set = function() {
throw new Error('');
};
value1 = [];
returnCount = 0;
assert.throws(function () {
new Map(iterable);
}, Error);
assert.areEqual(returnCount, 1, "return function is called when .set function throws");
value1 = 1;
}
},
{
name : "Map - .return function should not be called when .next function throws",
body : function () {
returnCount = 0;
shouldNextThrow = true;
assert.throws(function () {
new Map(iterable);
}, Error);
assert.areEqual(returnCount, 0, "next function causes exception which should not call the return function");
shouldNextThrow = false;
}
},
{
name : "Map - both .set function and .return throw but the outer exception wins",
body : function () {
Map.prototype.set = function() {
throw new Error('Exception from set function');
};
value1 = [];
returnCount = 0;
shouldReturnThrow = true;
assert.throws(function () {
new Map(iterable);
}, Error, 'Validate that the exception from return function is skipped', 'Exception from set function');
shouldReturnThrow = true;
value1 = 1;
assert.areEqual(returnCount, 1, "return function is called as the set function throws");
}
},
{
name : "WeakMap - calling .return function in the event of exception",
body : function () {
returnCount = 0;
assert.throws(function () {
new WeakMap(iterable);
}, TypeError);
assert.areEqual(returnCount, 1, "return function is called when iterator does not return object");
WeakMap.prototype.set = function() {
throw new Error('');
};
value1 = [];
returnCount = 0;
assert.throws(function () {
new WeakMap(iterable);
}, Error);
assert.areEqual(returnCount, 1, "return function is called as the set function throws");
value1 = 1;
}
},
{
name : "WeakMap - .return function should not be called when .next function throws",
body : function () {
returnCount = 0;
shouldNextThrow = true;
assert.throws(function () {
new WeakMap(iterable);
}, Error);
assert.areEqual(returnCount, 0, "next function causes exception which should not call the return function");
shouldNextThrow = false;
}
},
{
name : "WeakMap - both .set function and .return throw but the outer exception wins",
body : function () {
WeakMap.prototype.set = function() {
throw new Error('Exception from set function');
};
value1 = [];
returnCount = 0;
shouldReturnThrow = true;
assert.throws(function () {
new WeakMap(iterable);
}, Error, 'Validate that the exception from return function is skipped', 'Exception from set function');
shouldReturnThrow = true;
value1 = 1;
assert.areEqual(returnCount, 1, "return function is called as the set function throws");
}
},
{
name : "Set - calling .return function when .add function throws",
body : function () {
Set.prototype.add = function() {
throw new Error('');
};
returnCount = 0;
assert.throws(function () {
new Set(iterable);
}, Error);
assert.areEqual(returnCount, 1, "return function is called as the add function throws");
}
},
{
name : "Set - .return function should not be called when .next function throws",
body : function () {
returnCount = 0;
shouldNextThrow = true;
assert.throws(function () {
new Set(iterable);
}, Error);
assert.areEqual(returnCount, 0, "next function causes exception which should not call the return function");
shouldNextThrow = false;
}
},
{
name : "Set - both .add function and .return throw but the outer exception wins",
body : function () {
Set.prototype.add = function() {
throw new Error('Exception from add function');
};
returnCount = 0;
shouldReturnThrow = true;
assert.throws(function () {
new Set(iterable);
}, Error, 'Validate that the exception from return function is skipped', 'Exception from add function');
shouldReturnThrow = false;
assert.areEqual(returnCount, 1, "return function is called as the add function throws");
}
},
{
name : "WeakSet - calling .return function when .add function throws",
body : function () {
WeakSet.prototype.add = function() {
throw new Error('');
};
returnCount = 0;
assert.throws(function () {
new WeakSet(iterable);
}, Error);
assert.areEqual(returnCount, 1, "return function is called as the add function throws");
}
},
{
name : "WeakSet - .return function should not be called when .next function throws",
body : function () {
returnCount = 0;
shouldNextThrow = true;
assert.throws(function () {
new WeakSet(iterable);
}, Error);
assert.areEqual(returnCount, 0, "next function causes exception which should not call the return function");
shouldNextThrow = false;
}
},
{
name : "WeakSet - both .add function and .return throw but the outer exception wins",
body : function () {
WeakSet.prototype.add = function() {
throw new Error('Exception from add function');
};
returnCount = 0;
shouldReturnThrow = true;
assert.throws(function () {
new WeakSet(iterable);
}, Error, 'Validate that the exception from return function is skipped', 'Exception from add function');
shouldReturnThrow = false;
assert.areEqual(returnCount, 1, "return function is called as the add function throws");
}
},
{
name : "Promise.all - call .return function when .resolve function throws",
body : function () {
Promise.resolve = function () {
throw new Error('');
}
returnCount = 0;
Promise.all(iterable);
assert.areEqual(returnCount, 1, "return function is called as the resolve function throws");
}
},
{
name : "Promise.all - .return function should not be called when .next function throws",
body : function () {
returnCount = 0;
shouldNextThrow = true;
Promise.all(iterable);
shouldNextThrow = false;
assert.areEqual(returnCount, 0, "next function causes exception which should not call the return function");
}
},
{
name : "Promise.all - both .resolve and .return function thrown and outer exception wins",
body : function () {
Promise.resolve = function () {
throw new Error('Exception from resolve function');
}
returnCount = 0;
shouldReturnThrow = true;
var p = Promise.all(iterable);
shouldReturnThrow = false;
assert.areEqual(returnCount, 1, "return function is called as the resolve function throws");
p.catch( function (err) {
assert.areEqual(err.message, 'Exception from resolve function');
});
}
},
{
name : "Promise.race - call .return function when .resolve function throws",
body : function () {
Promise.resolve = function () {
throw new Error('');
}
returnCount = 0;
Promise.race(iterable);
assert.areEqual(returnCount, 1, "return function is called as the resolve function throws");
}
},
{
name : "Promise.race - .return function should not be called when .next function throws",
body : function () {
returnCount = 0;
shouldNextThrow = true;
Promise.race(iterable);
shouldNextThrow = false;
assert.areEqual(returnCount, 0, "next function causes exception which should not call the return function");
}
},
{
name : "Promise.race - both .resolve and .return function thrown and outer exception wins",
body : function () {
Promise.resolve = function () {
throw new Error('Exception from resolve function');
}
returnCount = 0;
shouldReturnThrow = true;
var p = Promise.race(iterable);
shouldReturnThrow = false;
assert.areEqual(returnCount, 1, "return function is called as the resolve function throws");
p.catch( function (err) {
assert.areEqual(err.message, 'Exception from resolve function');
});
}
},
];
testRunner.runTests(tests, {
verbose : WScript.Arguments[0] != "summary"
});