set_functionality.js 34 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661662663664665666667668669670671672673674675676677678679680681682683684685686687688689690691692693694695696697698699700701702703704705706707708709710711712713714715716717718719720721722723724725726727728729730731732733734735736737738739740741742743744745746747748749750751752753754755756757758759760761762763764765766767768769770771772773774775776777778
  1. //-------------------------------------------------------------------------------------------------------
  2. // Copyright (C) Microsoft. All rights reserved.
  3. // Licensed under the MIT license. See LICENSE.txt file in the project root for full license information.
  4. //-------------------------------------------------------------------------------------------------------
  5. // Functional Set tests -- verifies the APIs work correctly
  6. WScript.LoadScriptFile("..\\UnitTestFramework\\UnitTestFramework.js");
  7. function getNewSetWith12345() {
  8. var set = new Set();
  9. set.add(1);
  10. set.add(2);
  11. set.add(3);
  12. set.add(4);
  13. set.add(5);
  14. return set;
  15. }
  16. var globalObject = this;
  17. var tests = [
  18. {
  19. name: "Set constructor called on undefined or Set.prototype returns new Set object (and throws on null)",
  20. body: function () {
  21. // Set is no longer allowed to be called as a function unless the object it is given
  22. // for its this argument already has the [[SetData]] property on it.
  23. // TODO: When we implement @@create support, update this test to reflect it.
  24. //
  25. // For IE11 we simply throw if Set() is called as a function instead of in a new expression
  26. assert.throws(function () { Set.call(undefined); }, TypeError, "Set.call() throws TypeError given undefined");
  27. assert.throws(function () { Set.call(null); }, TypeError, "Set.call() throws TypeError given null");
  28. assert.throws(function () { Set.call(Set.prototype); }, TypeError, "Set.call() throws TypeError given Set.prototype");
  29. /*
  30. var set1 = Set.call(undefined);
  31. assert.isTrue(set1 !== null && set1 !== undefined && set1 !== Set.prototype, "Set constructor creates new Set object when this is undefined");
  32. var set2 = Set.call(Set.prototype);
  33. assert.isTrue(set2 !== null && set2 !== undefined && set2 !== Set.prototype, "Set constructor creates new Set object when this is equal to Set.prototype");
  34. var o = { };
  35. Object.preventExtensions(o);
  36. assert.throws(function () { Set.call(null); }, TypeError, "Set constructor throws on null");
  37. assert.throws(function () { Set.call(o); }, TypeError, "Set constructor throws on non-extensible object");
  38. */
  39. }
  40. },
  41. {
  42. name: "Set constructor throws when called on already initialized Set object",
  43. body: function () {
  44. var set = new Set();
  45. assert.throws(function () { Set.call(set); }, TypeError);
  46. // Set is no longer allowed to be called as a function unless the object it is given
  47. // for its this argument already has the [[SetData]] property on it.
  48. // TODO: When we implement @@create support, update this test to reflect it.
  49. /*
  50. var obj = {};
  51. Set.call(obj);
  52. assert.throws(function () { Set.call(obj); }, TypeError);
  53. function MySet() {
  54. Set.call(this);
  55. }
  56. MySet.prototype = new Set();
  57. MySet.prototype.constructor = MySet;
  58. var myset = new MySet();
  59. assert.throws(function () { Set.call(myset); }, TypeError);
  60. assert.throws(function () { MySet.call(myset); }, TypeError);
  61. */
  62. }
  63. },
  64. {
  65. name: "Set constructor populates the set with values from given optional iterable argument",
  66. body: function () {
  67. var s = new Set([ 'a', 'b', 'c' ]);
  68. assert.areEqual(3, s.size, "s is initialized with three entries");
  69. assert.isTrue(s.has('a'), "s has value 'a'");
  70. assert.isTrue(s.has('b'), "s has value 'b'");
  71. assert.isTrue(s.has('c'), "s has value 'c'");
  72. var customIterable = {
  73. [Symbol.iterator]: function () {
  74. var i = 1;
  75. return {
  76. next: function () {
  77. return {
  78. done: i > 4,
  79. value: i++ * 2
  80. };
  81. }
  82. };
  83. }
  84. };
  85. s = new Set(customIterable);
  86. assert.areEqual(4, s.size, "s is initialized with four entries");
  87. assert.isTrue(s.has(2), "s has value 2");
  88. assert.isTrue(s.has(4), "s has value 4");
  89. assert.isTrue(s.has(6), "s has value 6");
  90. assert.isTrue(s.has(8), "s has value 8");
  91. }
  92. },
  93. {
  94. name : "Set constructor caches next method from iterator",
  95. body: function () {
  96. let iterCount = 0;
  97. const iter = {
  98. [Symbol.iterator]() { return this; },
  99. next() {
  100. this.next = function (){ throw new Error ("Next should have been cached so this should not be called") };
  101. return {
  102. value : iterCount,
  103. done : iterCount++ > 2
  104. }
  105. }
  106. }
  107. s = new Set(iter);
  108. assert.areEqual(3, s.size, "s is initialized with three entries");
  109. assert.isTrue(s.has(0), "s has value '0'");
  110. assert.isTrue(s.has(1), "s has value '1'");
  111. assert.isTrue(s.has(2), "s has value '2'");
  112. }
  113. },
  114. {
  115. name: "Set constructor throws exceptions for non- and malformed iterable arguments",
  116. body: function () {
  117. var iterableNoIteratorMethod = { [Symbol.iterator]: 123 };
  118. var iterableBadIteratorMethod = { [Symbol.iterator]: function () { } };
  119. var iterableNoIteratorNextMethod = { [Symbol.iterator]: function () { return { }; } };
  120. var iterableBadIteratorNextMethod = { [Symbol.iterator]: function () { return { next: 123 }; } };
  121. var iterableNoIteratorResultObject = { [Symbol.iterator]: function () { return { next: function () { } }; } };
  122. assert.throws(function () { new Set(123); }, TypeError, "new Set() throws on non-object", "Function expected");
  123. assert.throws(function () { new Set({ }); }, TypeError, "new Set() throws on non-iterable object", "Function expected");
  124. assert.throws(function () { new Set(iterableNoIteratorMethod); }, TypeError, "new Set() throws on non-iterable object where @@iterator property is not a function", "Function expected");
  125. assert.throws(function () { new Set(iterableBadIteratorMethod); }, TypeError, "new Set() throws on non-iterable object where @@iterator function doesn't return an iterator", "Object expected");
  126. assert.throws(function () { new Set(iterableNoIteratorNextMethod); }, TypeError, "new Set() throws on iterable object where iterator object does not have next property", "Function expected");
  127. assert.throws(function () { new Set(iterableBadIteratorNextMethod); }, TypeError, "new Set() throws on iterable object where iterator object's next property is not a function", "Function expected");
  128. assert.throws(function () { new Set(iterableNoIteratorResultObject); }, TypeError, "new Set() throws on iterable object where iterator object's next method doesn't return an iterator result", "Object expected");
  129. }
  130. },
  131. {
  132. name: "APIs throw TypeError where specified",
  133. body: function () {
  134. function MySetImposter() { }
  135. MySetImposter.prototype = new Set();
  136. MySetImposter.prototype.constructor = MySetImposter;
  137. var o = new MySetImposter();
  138. assert.throws(function () { o.add(1); }, TypeError, "add should throw if this doesn't have SetData property");
  139. assert.throws(function () { o.clear(); }, TypeError, "clear should throw if this doesn't have SetData property");
  140. assert.throws(function () { o.delete(1); }, TypeError, "delete should throw if this doesn't have SetData property");
  141. assert.throws(function () { o.forEach(function (k, v, s) { }); }, TypeError, "forEach should throw if this doesn't have SetData property");
  142. assert.throws(function () { o.has(1); }, TypeError, "has should throw if this doesn't have SetData property");
  143. assert.throws(function () { WScript.Echo(o.size); }, TypeError, "size should throw if this doesn't have SetData property");
  144. assert.throws(function () { Set.prototype.add.call(); }, TypeError, "add should throw if called with no arguments");
  145. assert.throws(function () { Set.prototype.clear.call(); }, TypeError, "clear should throw if called with no arguments");
  146. assert.throws(function () { Set.prototype.delete.call(); }, TypeError, "delete should throw if called with no arguments");
  147. assert.throws(function () { Set.prototype.forEach.call(); }, TypeError, "forEach should throw if called with no arguments");
  148. assert.throws(function () { Set.prototype.has.call(); }, TypeError, "has should throw if called with no arguments");
  149. assert.throws(function () { Object.getOwnPropertyDescriptor(Set.prototype, "size").get.call(); }, TypeError, "size should throw if called with no arguments");
  150. assert.throws(function () { Set.prototype.add.call(null, 1); }, TypeError, "add should throw if this is null");
  151. assert.throws(function () { Set.prototype.clear.call(null); }, TypeError, "clear should throw if this is null");
  152. assert.throws(function () { Set.prototype.delete.call(null, 1); }, TypeError, "delete should throw if this is null");
  153. assert.throws(function () { Set.prototype.forEach.call(null, function (k, v, s) { }); }, TypeError, "forEach should throw if this is null");
  154. assert.throws(function () { Set.prototype.has.call(null, 1); }, TypeError, "has should throw if this is null");
  155. assert.throws(function () { Object.getOwnPropertyDescriptor(Set.prototype, "size").get.call(null); }, TypeError, "size should throw if this is null");
  156. assert.throws(function () { Set.prototype.add.call(undefined, 1); }, TypeError, "add should throw if this is undefined");
  157. assert.throws(function () { Set.prototype.clear.call(undefined); }, TypeError, "clear should throw if this is undefined");
  158. assert.throws(function () { Set.prototype.delete.call(undefined, 1); }, TypeError, "delete should throw if this is undefined");
  159. assert.throws(function () { Set.prototype.forEach.call(undefined, function (k, v, s) { }); }, TypeError, "forEach should throw if this is undefined");
  160. assert.throws(function () { Set.prototype.has.call(undefined, 1); }, TypeError, "has should throw if this is undefined");
  161. assert.throws(function () { Object.getOwnPropertyDescriptor(Set.prototype, "size").get.call(undefined); }, TypeError, "size should throw if this is undefined");
  162. var set = new Set();
  163. assert.throws(function () { set.forEach(null); }, TypeError, "forEach should throw if its first argument is not callable, e.g. null");
  164. assert.throws(function () { set.forEach(undefined); }, TypeError, "forEach should throw if its first argument is not callable, e.g. undefined");
  165. assert.throws(function () { set.forEach(true); }, TypeError, "forEach should throw if its first argument is not callable, e.g. a boolean");
  166. assert.throws(function () { set.forEach(10); }, TypeError, "forEach should throw if its first argument is not callable, e.g. a number");
  167. assert.throws(function () { set.forEach("hello"); }, TypeError, "forEach should throw if its first argument is not callable, e.g. a string");
  168. }
  169. },
  170. {
  171. name: "Basic usage, add, clear, delete, has, size",
  172. body: function () {
  173. var set = new Set();
  174. assert.isTrue(set.size === 0, "Initially empty");
  175. set.add(1);
  176. set.add(2);
  177. set.add("Hello");
  178. var o = {};
  179. set.add(o);
  180. assert.isTrue(set.has(1), "Should contain 1");
  181. assert.isTrue(set.has(2), "Should contain 2");
  182. assert.isTrue(set.has("Hello"), "Should contain \"Hello\"");
  183. assert.isTrue(set.has(o), "Should contain o");
  184. assert.isTrue(set.size === 4, "Should contain four values");
  185. assert.isFalse(set.has(0), "Shouldn't contain other values");
  186. assert.isFalse(set.has("goodbye"), "Shouldn't contain other values");
  187. assert.isFalse(set.has(set), "Shouldn't contain other values");
  188. set.clear();
  189. assert.isTrue(set.size === 0, "Should be empty again");
  190. assert.isFalse(set.has(1), "Should no longer contain 1");
  191. assert.isFalse(set.has(2), "Should no longer contain 2");
  192. assert.isFalse(set.has("Hello"), "Should no longer contain \"Hello\"");
  193. assert.isFalse(set.has(o), "Should no longer contain o");
  194. set.add(1);
  195. set.add(2);
  196. set.add("Hello");
  197. set.add(o);
  198. assert.isTrue(set.has(1), "Should contain 1 again");
  199. assert.isTrue(set.has(2), "Should contain 2 again");
  200. assert.isTrue(set.has("Hello"), "Should contain \"Hello\" again");
  201. assert.isTrue(set.has(o), "Should contain o again");
  202. assert.isTrue(set.size === 4, "Should contain four values again");
  203. set.delete(2);
  204. assert.isTrue(set.has(1), "Should still contain 1");
  205. assert.isFalse(set.has(2), "Should no longer contain 2");
  206. assert.isTrue(set.has("Hello"), "Should still contain \"Hello\"");
  207. assert.isTrue(set.has(o), "Should still contain o");
  208. assert.isTrue(set.size === 3, "Should contain three values now");
  209. set.delete(o);
  210. set.delete("Hello");
  211. assert.isTrue(set.has(1), "Should still contain 1");
  212. assert.isFalse(set.has(2), "Should no longer contain 2");
  213. assert.isFalse(set.has("Hello"), "Should no longer contain \"Hello\"");
  214. assert.isFalse(set.has(o), "Should no longer contain o");
  215. assert.isTrue(set.size === 1, "Should contain one value now");
  216. set.delete(1);
  217. assert.isFalse(set.has(1), "Should no longer contain 1");
  218. assert.isTrue(set.size === 0, "Should be empty again");
  219. }
  220. },
  221. {
  222. name: "Not specifying arguments should default them to undefined",
  223. body: function () {
  224. var set = new Set();
  225. assert.isFalse(set.has(), "Should not have undefined");
  226. assert.isFalse(set.delete(), "undefined is not in the set, delete should return false");
  227. set.add();
  228. assert.isTrue(set.has(), "Should have undefined");
  229. assert.isTrue(set.delete(), "undefined is in the set, delete should return true");
  230. }
  231. },
  232. {
  233. name: "Extra arguments should be ignored",
  234. body: function () {
  235. var set = new Set();
  236. assert.isFalse(set.has(1, 2, 3), "Looks for 1, ignores 2 and 3, set is empty so should return false");
  237. assert.isFalse(set.delete(1, 2, 3), "Tries to delete 1, ignores 2 and 3, set is empty so should return false");
  238. // 2 and 3 should be ignored and not added to the set
  239. set.add(1, 2, 3);
  240. assert.isTrue(set.has(1), "Should contain 1");
  241. assert.isFalse(set.has(2), "Should not contain 2");
  242. assert.isFalse(set.has(3), "Should not contain 3");
  243. assert.isTrue(set.has(1, 2, 3), "Should contain 1, has should ignore 2 and 3");
  244. assert.isFalse(set.has(2, 1, 3), "Should not contain 2, has should ignore 1 and 3");
  245. assert.isFalse(set.delete(2, 1, 3), "2 is not found so should return false, ignores 1 and 3");
  246. assert.isFalse(set.delete(3, 1), "3 is not found so should return false, ignores 1");
  247. assert.isTrue(set.delete(1, 2, 3), "1 is found and deleted, so should return true, ignores 2 and 3");
  248. }
  249. },
  250. {
  251. name: "Delete should return true if item was in set, false if not",
  252. body: function () {
  253. var set = new Set();
  254. set.add(1);
  255. assert.isFalse(set.delete(2), "2 is not in the set, delete should return false");
  256. assert.isTrue(set.delete(1), "1 is in the set, delete should return true");
  257. assert.isFalse(set.delete(1), "1 is no longer in the set, delete should now return false");
  258. }
  259. },
  260. {
  261. name: "Adding the same value twice is valid",
  262. body: function () {
  263. var set = new Set();
  264. set.add(1);
  265. set.add(1);
  266. set.add(2);
  267. set.delete(1);
  268. set.add(2);
  269. set.add(1);
  270. set.add(1);
  271. }
  272. },
  273. {
  274. name: "clear returns undefined, add returns the set instance itself",
  275. body: function () {
  276. var set = new Set();
  277. assert.areEqual(set, set.add(1), "Adding new element should return Set instance");
  278. assert.areEqual(set, set.add(1), "Adding existing element should return Set instance");
  279. assert.areEqual(undefined, set.clear(), "Clearing set should return undefined");
  280. }
  281. },
  282. {
  283. name: "Value comparison is implemented according to SameValueZero algorithm defined in spec (i.e. not by object reference identity)",
  284. body: function () {
  285. var set = new Set();
  286. set.add(3.14159);
  287. set.add("hello");
  288. set.add(8589934592);
  289. assert.isTrue(set.has(3.14159), "Set contains floating point number");
  290. assert.isTrue(set.has(3.0 + 0.14159), "Set contains floating point number even if calculated differently");
  291. assert.isTrue(set.has("hello"), "Set contains string");
  292. assert.isTrue(set.has("hel" + "lo"), "Set contains string even if different reference identity");
  293. assert.isTrue(set.has(8589934592), "Set contains 64 bit integer value");
  294. assert.isTrue(set.has(65536 + 8589869056), "Set contains 64 bit integer value even if calculated differently");
  295. set.add(-0);
  296. assert.isTrue(set.has(-0), "Set contains -0");
  297. assert.isTrue(set.has(+0), "Set contains +0");
  298. set.add(0);
  299. assert.isTrue(set.has(-0), "Set still contains -0");
  300. assert.isTrue(set.has(+0), "Set still contains +0");
  301. set.delete(-0);
  302. assert.isFalse(set.has(-0), "Set does not contain -0");
  303. assert.isFalse(set.has(+0), "Set does not contain +0");
  304. set.add(+0);
  305. assert.isTrue(set.has(-0), "Set contains -0");
  306. assert.isTrue(set.has(+0), "Set contains +0");
  307. set.add(-0);
  308. assert.isTrue(set.has(-0), "Set still contains -0");
  309. assert.isTrue(set.has(+0), "Set still contains +0");
  310. set.delete(0);
  311. assert.isFalse(set.has(-0), "Set does not contain -0");
  312. assert.isFalse(set.has(+0), "Set does not contain +0");
  313. set.add(Number.NEGATIVE_INFINITY);
  314. assert.isTrue(set.has(Number.NEGATIVE_INFINITY), "Set contains negative infinity");
  315. assert.isFalse(set.has(Number.POSITIVE_INFINITY), "Set does not contain positive infinity");
  316. set.add(Infinity);
  317. assert.isTrue(set.has(Number.NEGATIVE_INFINITY), "Set contains negative infinity");
  318. assert.isTrue(set.has(Number.POSITIVE_INFINITY), "Set contains positive infinity");
  319. set.delete(Number.NEGATIVE_INFINITY);
  320. assert.isFalse(set.has(Number.NEGATIVE_INFINITY), "Set does not contain negative infinity");
  321. assert.isTrue(set.has(Number.POSITIVE_INFINITY), "Set contains positive infinity");
  322. assert.isFalse(set.has(NaN), "Set does not contain NaN");
  323. set.add(NaN);
  324. assert.isTrue(set.has(NaN), "Set contains NaN");
  325. assert.isTrue(set.has(parseInt("blah")), "Set contains NaN resulting from parseInt(\"Blah\")");
  326. assert.isTrue(set.has(Math.sqrt(-1)), "Set contains NaN resulting from Math.sqrt(-1)");
  327. assert.isTrue(set.has(0 * Infinity), "Set contains NaN resulting from 0 * Infinity");
  328. }
  329. },
  330. {
  331. name: "forEach should set the this value of the callback correctly",
  332. body: function () {
  333. var set = new Set();
  334. set.add(1);
  335. set.forEach(function (key, val, set) {
  336. assert.isTrue(this === globalObject, "set.forEach should use undefined as value of this keyword if second argument is not specified which is converted to the global object");
  337. });
  338. var o = { };
  339. set.forEach(function (key, val, set) {
  340. assert.isTrue(this === o, "set.forEach should use second argument if specified as value of this keyword");
  341. }, o);
  342. set.forEach(function (key, val, set) {
  343. assert.isTrue(this.valueOf() === 10, "set.forEach should use second argument if specified as value of this keyword even if it is a non-object (which will be converted to an object)");
  344. }, 10);
  345. }
  346. },
  347. {
  348. name: "forEach should enumerate set items in insertion order and should not call the callback for empty sets",
  349. body: function () {
  350. var i = 0;
  351. var set = getNewSetWith12345();
  352. var didExecute = false;
  353. set.forEach(function (key, val, set) {
  354. i += 1;
  355. assert.isTrue(val == i, "set.forEach should enumerate 1, 2, 3, 4, 5 in that order");
  356. didExecute = true;
  357. });
  358. assert.isTrue(didExecute, "set.forEach should have enumerated items");
  359. // a second forEach should start at the beginning again
  360. i = 0;
  361. didExecute = false;
  362. set.forEach(function (key, val, set) {
  363. i += 1;
  364. assert.isTrue(val == i, "Repeated set.forEach should enumerate 1, 2, 3, 4, 5 in that order again");
  365. didExecute = true;
  366. });
  367. assert.isTrue(didExecute, "set.forEach should have enumerated items");
  368. set.clear();
  369. set.forEach(function (key, val, set) {
  370. assert.fail("Shouldn't execute; set should be empty");
  371. });
  372. set = new Set();
  373. set.forEach(function (key, val, set) {
  374. assert.fail("Shouldn't execute; set should be empty");
  375. });
  376. }
  377. },
  378. {
  379. name: "forEach should enumerate all set items if any deletes occur on items that have already been enumerated",
  380. body: function () {
  381. var i = 0;
  382. var set = getNewSetWith12345();
  383. var didExecute = false;
  384. set.forEach(function (key, val, set) {
  385. set.delete(val);
  386. i += 1;
  387. assert.isTrue(val == i, "set.forEach should enumerate 1, 2, 3, 4, 5 in that order");
  388. didExecute = true;
  389. });
  390. assert.isTrue(didExecute, "set.forEach should have enumerated items");
  391. set.forEach(function (key, val, set) {
  392. assert.fail("Shouldn't execute; set should be empty");
  393. });
  394. i = 0;
  395. set = getNewSetWith12345();
  396. didExecute = false;
  397. set.forEach(function (key, val, set) {
  398. if (val >= 3) {
  399. set.delete(val - 2);
  400. }
  401. i += 1;
  402. assert.isTrue(val == i, "set.forEach should enumerate 1, 2, 3, 4, 5 in that order");
  403. didExecute = true;
  404. });
  405. assert.isTrue(didExecute, "set.forEach should have enumerated items");
  406. i = 3;
  407. didExecute = false;
  408. set.forEach(function (key, val, set) {
  409. i += 1;
  410. assert.isTrue(val == i, "set.forEach should enumerate 4, 5 in that order");
  411. didExecute = true;
  412. });
  413. assert.isTrue(didExecute, "set.forEach should have enumerated items");
  414. }
  415. },
  416. {
  417. name: "forEach should not enumerate set items that are deleted during enumeration before being visited",
  418. body: function () {
  419. var i = 1;
  420. var set = getNewSetWith12345();
  421. var didExecute = false;
  422. set.forEach(function (key, val, set) {
  423. assert.isTrue(val == i, "set.forEach should enumerate 1, 3, 5 in that order");
  424. set.delete(val + 1);
  425. i += 2;
  426. didExecute = true;
  427. });
  428. assert.isTrue(didExecute, "set.forEach should have enumerated items");
  429. didExecute = false;
  430. set.forEach(function (key, val, set) {
  431. assert.isTrue(val == 1, "set.forEach should enumerate 1 only");
  432. set.delete(3);
  433. set.delete(5);
  434. didExecute = true;
  435. });
  436. assert.isTrue(didExecute, "set.forEach should have enumerated items");
  437. didExecute = false;
  438. set.forEach(function (key, val, set) {
  439. assert.isTrue(val == 1, "set.forEach should enumerate 1 only again");
  440. set.delete(1);
  441. didExecute = true;
  442. });
  443. assert.isTrue(didExecute, "set.forEach should have enumerated items");
  444. set.forEach(function (key, val, set) {
  445. assert.fail("Shouldn't execute, set should be empty");
  446. });
  447. set = getNewSetWith12345();
  448. i = 0;
  449. didExecute = false;
  450. set.forEach(function (key, val, set) {
  451. set.delete(6 - val);
  452. i += 1;
  453. assert.isTrue(val == i && val <= 3, "set.forEach should enumerate 1, 2, 3 in that order");
  454. didExecute = true;
  455. });
  456. assert.isTrue(didExecute, "set.forEach should have enumerated items");
  457. i = 0;
  458. didExecute = false;
  459. set.forEach(function (key, val, set) {
  460. i += 1;
  461. assert.isTrue(val == i && val <= 2, "set.forEach should enumerate 1, 2 in that order");
  462. didExecute = true;
  463. });
  464. assert.isTrue(didExecute, "set.forEach should have enumerated items");
  465. }
  466. },
  467. {
  468. name: "forEach should continue to enumerate items as long as they are added but only if they were not already in the set",
  469. body: function () {
  470. var i = 0;
  471. var set = new Set();
  472. set.add(1);
  473. set.forEach(function (key, val, set) {
  474. i += 1;
  475. assert.isTrue(val == i, "set.forEach should enumerate 1 through 20 in order");
  476. if (val < 20)
  477. {
  478. set.add(val + 1);
  479. }
  480. });
  481. assert.isTrue(i == 20, "set.forEach should have enumerated up to 20");
  482. i = 0;
  483. set.forEach(function (key, val, set) {
  484. i += 1;
  485. assert.isTrue(val == i, "set.forEach should only enumerate 1 through 20 in order once each, no duplicates");
  486. if (val < 20)
  487. {
  488. set.add(val + 1);
  489. }
  490. });
  491. assert.isTrue(i == 20, "set.forEach should have enumerated up to 20 again");
  492. }
  493. },
  494. {
  495. name: "forEach should stop enumerating items if the set is cleared during enumeration",
  496. body: function () {
  497. var i = 0;
  498. var set = getNewSetWith12345();
  499. set.forEach(function (key, val, set) {
  500. i += 1;
  501. assert.isTrue(val == i, "set.forEach should enumerate 1 and stop");
  502. if (val == 1)
  503. {
  504. set.clear();
  505. }
  506. });
  507. assert.isTrue(i == 1, "set.forEach should have stopped after 1");
  508. i = 0;
  509. set = getNewSetWith12345();
  510. set.forEach(function (key, val, set) {
  511. i += 1;
  512. assert.isTrue(val == i, "set.forEach should enumerate 1, 2 and stop");
  513. if (val == 2)
  514. {
  515. set.clear();
  516. }
  517. });
  518. assert.isTrue(i == 2, "set.forEach should have stopped after 1, 2");
  519. i = 0;
  520. set = getNewSetWith12345();
  521. set.forEach(function (key, val, set) {
  522. i += 1;
  523. assert.isTrue(val == i, "set.forEach should enumerate 1, 2, 3 and stop");
  524. if (val == 3)
  525. {
  526. set.clear();
  527. }
  528. });
  529. assert.isTrue(i == 3, "set.forEach should have stopped after 1, 2, 3");
  530. i = 0;
  531. set = getNewSetWith12345();
  532. set.forEach(function (key, val, set) {
  533. i += 1;
  534. assert.isTrue(val == i, "set.forEach should enumerate 1, 2, 3, 4 and stop");
  535. if (val == 4)
  536. {
  537. set.clear();
  538. }
  539. });
  540. assert.isTrue(i == 4, "set.forEach should have stopped after 1, 2, 3, 4");
  541. i = 0;
  542. set = getNewSetWith12345();
  543. set.forEach(function (key, val, set) {
  544. i += 1;
  545. assert.isTrue(val == i, "set.forEach should enumerate 1, 2, 3, 4, 5 and stop");
  546. if (val == 5)
  547. {
  548. set.clear();
  549. }
  550. });
  551. assert.isTrue(i == 5, "set.forEach should have enumerated all 1, 2, 3, 4, 5");
  552. assert.isTrue(set.size == 0, "set should be empty");
  553. }
  554. },
  555. {
  556. name: "forEach should revisit items if they are removed after being visited but re-added before enumeration stops",
  557. body: function () {
  558. var i = 0;
  559. var didExecute = false;
  560. var set = getNewSetWith12345();
  561. set.forEach(function (key, val, set) {
  562. if (val == 3) {
  563. set.delete(2);
  564. set.delete(1);
  565. set.add(1);
  566. set.add(2);
  567. }
  568. i += 1;
  569. assert.isTrue(val == i, "set.forEach should enumerate 1, 2, 3, 4, 5, 1, 2 in that order");
  570. if (val == 5) {
  571. i = 0;
  572. }
  573. didExecute = true;
  574. });
  575. assert.isTrue(didExecute, "set.forEach should have enumerated items");
  576. i = 2;
  577. didExecute = false;
  578. set.forEach(function (key, val, set) {
  579. i += 1;
  580. assert.isTrue(val == i, "set.forEach should enumerate 3, 4, 5, 1, 2 in that order");
  581. if (val == 5) {
  582. i = 0;
  583. }
  584. didExecute = true;
  585. });
  586. assert.isTrue(didExecute, "set.forEach should have enumerated items");
  587. }
  588. },
  589. {
  590. name: "forEach should continue enumeration indefinitely if items are repeatedly removed and re-added without end",
  591. body: function () {
  592. var set = new Set();
  593. set.add(1);
  594. set.add(2);
  595. var vals = [ 1, 2, 1, 2, 1, 2, 1, 2, 1, 2 ];
  596. var i = 0;
  597. set.forEach(function (key, val, set) {
  598. if (i < 9) {
  599. if (val == 1) {
  600. set.delete(1);
  601. set.add(2);
  602. } else if (val == 2) {
  603. set.delete(2);
  604. set.add(1);
  605. }
  606. }
  607. assert.isTrue(val == vals[i], "set.forEach should enumerate 1, 2, 1, 2, 1, 2, 1, 2, 1, 2");
  608. i += 1;
  609. });
  610. assert.isTrue(i == 10, "set.forEach should have called the callback 10 times");
  611. }
  612. },
  613. {
  614. name: "Set.prototype.add should normalize -0 keys to +0 which is observable via Set.prototype.forEach",
  615. body: function() {
  616. var set = new Set();
  617. set.add(-0);
  618. set.forEach(function (val, key, set) {
  619. // do not use assert.areEqual(-0, ...) because it compares -0 and +0 as equal
  620. assert.isTrue(+Infinity === 1 / key && key === 0, "-0 keys are normalized to +0");
  621. });
  622. }
  623. },
  624. {
  625. name: "Exprgen bug 3097715: When throwing a TypeError a valid scriptContext should be used",
  626. body: function () {
  627. var func3 = function () { };
  628. assert.throws(function () { Array()(func3(...new Set([func3, func3]))) }, TypeError, "Should throw TypeError");
  629. }
  630. },
  631. {
  632. name: "Values that are int versus double should compare and hash equal (github #390)",
  633. body: function() {
  634. var set = new Set();
  635. set.add(1);
  636. assert.isTrue(set.has(1), "sanity check, set has value 1");
  637. var value = 1.1;
  638. value -= 0.1; // value is now 1.0, a double, rather than an int
  639. assert.isTrue(set.has(value), "1.0 should be equal to the value 1 and set has it");
  640. }
  641. },
  642. {
  643. name: "-NaN and +NaN in set should not assert (github #6063)",
  644. body: function() {
  645. let set = new Set([+NaN, -NaN, "asdf"]);
  646. assert.isTrue(set.has(-NaN));
  647. assert.isTrue(set.has(NaN));
  648. assert.isTrue(set.has("asdf"));
  649. }
  650. },
  651. ];
  652. testRunner.runTests(tests, { verbose: WScript.Arguments[0] != "summary" });