1// Copyright 2009 the V8 project authors. All rights reserved. 2// Redistribution and use in source and binary forms, with or without 3// modification, are permitted provided that the following conditions are 4// met: 5// 6// * Redistributions of source code must retain the above copyright 7// notice, this list of conditions and the following disclaimer. 8// * Redistributions in binary form must reproduce the above 9// copyright notice, this list of conditions and the following 10// disclaimer in the documentation and/or other materials provided 11// with the distribution. 12// * Neither the name of Google Inc. nor the names of its 13// contributors may be used to endorse or promote products derived 14// from this software without specific prior written permission. 15// 16// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS 17// "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT 18// LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR 19// A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT 20// OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, 21// SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT 22// LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, 23// DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY 24// THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT 25// (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE 26// OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. 27 28/** 29 * @fileoverview Test reduce and reduceRight 30 */ 31 32function clone(v) { 33 // Shallow-copies arrays, returns everything else verbatim. 34 if (v instanceof Array) { 35 // Shallow-copy an array. 36 var newArray = new Array(v.length); 37 for (var i in v) { 38 newArray[i] = v[i]; 39 } 40 return newArray; 41 } 42 return v; 43} 44 45 46// Creates a callback function for reduce/reduceRight that tests the number 47// of arguments and otherwise behaves as "func", but which also 48// records all calls in an array on the function (as arrays of arguments 49// followed by result). 50function makeRecorder(func, testName) { 51 var record = []; 52 var f = function recorder(a, b, i, s) { 53 assertEquals(4, arguments.length, 54 testName + "(number of arguments: " + arguments.length + ")"); 55 assertEquals("number", typeof(i), testName + "(index must be number)"); 56 assertEquals(s[i], b, testName + "(current argument is at index)"); 57 if (record.length > 0) { 58 var prevRecord = record[record.length - 1]; 59 var prevResult = prevRecord[prevRecord.length - 1]; 60 assertEquals(prevResult, a, 61 testName + "(prev result -> current input)"); 62 } 63 var args = [clone(a), clone(b), i, clone(s)]; 64 var result = func.apply(this, arguments); 65 args.push(clone(result)); 66 record.push(args); 67 return result; 68 }; 69 f.record = record; 70 return f; 71} 72 73 74function testReduce(type, 75 testName, 76 expectedResult, 77 expectedCalls, 78 array, 79 combine, 80 init) { 81 var rec = makeRecorder(combine); 82 var result; 83 var performsCall; 84 if (arguments.length > 6) { 85 result = array[type](rec, init); 86 } else { 87 result = array[type](rec); 88 } 89 var calls = rec.record; 90 assertEquals(expectedCalls.length, calls.length, 91 testName + " (number of calls)"); 92 for (var i = 0; i < expectedCalls.length; i++) { 93 assertEquals(expectedCalls[i], calls[i], 94 testName + " (call " + (i + 1) + ")"); 95 } 96 assertEquals(expectedResult, result, testName + " (result)"); 97} 98 99 100function sum(a, b) { return a + b; } 101function prod(a, b) { return a * b; } 102function dec(a, b, i, arr) { return a + b * Math.pow(10, arr.length - i - 1); } 103function accumulate(acc, elem, i) { acc[i] = elem; return acc; } 104 105// ---- Test Reduce[Left] 106 107var simpleArray = [2,4,6] 108 109testReduce("reduce", "SimpleReduceSum", 12, 110 [[0, 2, 0, simpleArray, 2], 111 [2, 4, 1, simpleArray, 6], 112 [6, 6, 2, simpleArray, 12]], 113 simpleArray, sum, 0); 114 115testReduce("reduce", "SimpleReduceProd", 48, 116 [[1, 2, 0, simpleArray, 2], 117 [2, 4, 1, simpleArray, 8], 118 [8, 6, 2, simpleArray, 48]], 119 simpleArray, prod, 1); 120 121testReduce("reduce", "SimpleReduceDec", 246, 122 [[0, 2, 0, simpleArray, 200], 123 [200, 4, 1, simpleArray, 240], 124 [240, 6, 2, simpleArray, 246]], 125 simpleArray, dec, 0); 126 127testReduce("reduce", "SimpleReduceAccumulate", simpleArray, 128 [[[], 2, 0, simpleArray, [2]], 129 [[2], 4, 1, simpleArray, [2, 4]], 130 [[2,4], 6, 2, simpleArray, simpleArray]], 131 simpleArray, accumulate, []); 132 133 134testReduce("reduce", "EmptyReduceSum", 0, [], [], sum, 0); 135testReduce("reduce", "EmptyReduceProd", 1, [], [], prod, 1); 136testReduce("reduce", "EmptyReduceDec", 0, [], [], dec, 0); 137testReduce("reduce", "EmptyReduceAccumulate", [], [], [], accumulate, []); 138 139testReduce("reduce", "EmptyReduceSumNoInit", 0, [], [0], sum); 140testReduce("reduce", "EmptyReduceProdNoInit", 1, [], [1], prod); 141testReduce("reduce", "EmptyReduceDecNoInit", 0, [], [0], dec); 142testReduce("reduce", "EmptyReduceAccumulateNoInit", [], [], [[]], accumulate); 143 144 145var simpleSparseArray = [,,,2,,4,,6,,]; 146testReduce("reduce", "SimpleSparseReduceSum", 12, 147 [[0, 2, 3, simpleSparseArray, 2], 148 [2, 4, 5, simpleSparseArray, 6], 149 [6, 6, 7, simpleSparseArray, 12]], 150 simpleSparseArray, sum, 0); 151 152testReduce("reduce", "SimpleSparseReduceProd", 48, 153 [[1, 2, 3, simpleSparseArray, 2], 154 [2, 4, 5, simpleSparseArray, 8], 155 [8, 6, 7, simpleSparseArray, 48]], 156 simpleSparseArray, prod, 1); 157 158testReduce("reduce", "SimpleSparseReduceDec", 204060, 159 [[0, 2, 3, simpleSparseArray, 200000], 160 [200000, 4, 5, simpleSparseArray, 204000], 161 [204000, 6, 7, simpleSparseArray, 204060]], 162 simpleSparseArray, dec, 0); 163 164testReduce("reduce", "SimpleSparseReduceAccumulate", [,,,2,,4,,6], 165 [[[], 2, 3, simpleSparseArray, [,,,2]], 166 [[,,,2], 4, 5, simpleSparseArray, [,,,2,,4]], 167 [[,,,2,,4], 6, 7, simpleSparseArray, [,,,2,,4,,6]]], 168 simpleSparseArray, accumulate, []); 169 170 171testReduce("reduce", "EmptySparseReduceSumNoInit", 0, [], [,,0,,], sum); 172testReduce("reduce", "EmptySparseReduceProdNoInit", 1, [], [,,1,,], prod); 173testReduce("reduce", "EmptySparseReduceDecNoInit", 0, [], [,,0,,], dec); 174testReduce("reduce", "EmptySparseReduceAccumulateNoInit", 175 [], [], [,,[],,], accumulate); 176 177 178var verySparseArray = []; 179verySparseArray.length = 10000; 180verySparseArray[2000] = 2; 181verySparseArray[5000] = 4; 182verySparseArray[9000] = 6; 183var verySparseSlice2 = verySparseArray.slice(0, 2001); 184var verySparseSlice4 = verySparseArray.slice(0, 5001); 185var verySparseSlice6 = verySparseArray.slice(0, 9001); 186 187testReduce("reduce", "VerySparseReduceSum", 12, 188 [[0, 2, 2000, verySparseArray, 2], 189 [2, 4, 5000, verySparseArray, 6], 190 [6, 6, 9000, verySparseArray, 12]], 191 verySparseArray, sum, 0); 192 193testReduce("reduce", "VerySparseReduceProd", 48, 194 [[1, 2, 2000, verySparseArray, 2], 195 [2, 4, 5000, verySparseArray, 8], 196 [8, 6, 9000, verySparseArray, 48]], 197 verySparseArray, prod, 1); 198 199testReduce("reduce", "VerySparseReduceDec", Infinity, 200 [[0, 2, 2000, verySparseArray, Infinity], 201 [Infinity, 4, 5000, verySparseArray, Infinity], 202 [Infinity, 6, 9000, verySparseArray, Infinity]], 203 verySparseArray, dec, 0); 204 205testReduce("reduce", "VerySparseReduceAccumulate", 206 verySparseSlice6, 207 [[[], 2, 2000, verySparseArray, verySparseSlice2], 208 [verySparseSlice2, 4, 5000, verySparseArray, verySparseSlice4], 209 [verySparseSlice4, 6, 9000, verySparseArray, verySparseSlice6]], 210 verySparseArray, accumulate, []); 211 212 213testReduce("reduce", "VerySparseReduceSumNoInit", 12, 214 [[2, 4, 5000, verySparseArray, 6], 215 [6, 6, 9000, verySparseArray, 12]], 216 verySparseArray, sum); 217 218testReduce("reduce", "VerySparseReduceProdNoInit", 48, 219 [[2, 4, 5000, verySparseArray, 8], 220 [8, 6, 9000, verySparseArray, 48]], 221 verySparseArray, prod); 222 223testReduce("reduce", "VerySparseReduceDecNoInit", Infinity, 224 [[2, 4, 5000, verySparseArray, Infinity], 225 [Infinity, 6, 9000, verySparseArray, Infinity]], 226 verySparseArray, dec); 227 228testReduce("reduce", "SimpleSparseReduceAccumulateNoInit", 229 2, 230 [[2, 4, 5000, verySparseArray, 2], 231 [2, 6, 9000, verySparseArray, 2]], 232 verySparseArray, accumulate); 233 234 235// ---- Test ReduceRight 236 237testReduce("reduceRight", "SimpleReduceRightSum", 12, 238 [[0, 6, 2, simpleArray, 6], 239 [6, 4, 1, simpleArray, 10], 240 [10, 2, 0, simpleArray, 12]], 241 simpleArray, sum, 0); 242 243testReduce("reduceRight", "SimpleReduceRightProd", 48, 244 [[1, 6, 2, simpleArray, 6], 245 [6, 4, 1, simpleArray, 24], 246 [24, 2, 0, simpleArray, 48]], 247 simpleArray, prod, 1); 248 249testReduce("reduceRight", "SimpleReduceRightDec", 246, 250 [[0, 6, 2, simpleArray, 6], 251 [6, 4, 1, simpleArray, 46], 252 [46, 2, 0, simpleArray, 246]], 253 simpleArray, dec, 0); 254 255testReduce("reduceRight", "SimpleReduceRightAccumulate", simpleArray, 256 [[[], 6, 2, simpleArray, [,,6]], 257 [[,,6], 4, 1, simpleArray, [,4,6]], 258 [[,4,6], 2, 0, simpleArray, simpleArray]], 259 simpleArray, accumulate, []); 260 261 262testReduce("reduceRight", "EmptyReduceRightSum", 0, [], [], sum, 0); 263testReduce("reduceRight", "EmptyReduceRightProd", 1, [], [], prod, 1); 264testReduce("reduceRight", "EmptyReduceRightDec", 0, [], [], dec, 0); 265testReduce("reduceRight", "EmptyReduceRightAccumulate", [], 266 [], [], accumulate, []); 267 268testReduce("reduceRight", "EmptyReduceRightSumNoInit", 0, [], [0], sum); 269testReduce("reduceRight", "EmptyReduceRightProdNoInit", 1, [], [1], prod); 270testReduce("reduceRight", "EmptyReduceRightDecNoInit", 0, [], [0], dec); 271testReduce("reduceRight", "EmptyReduceRightAccumulateNoInit", 272 [], [], [[]], accumulate); 273 274 275testReduce("reduceRight", "SimpleSparseReduceRightSum", 12, 276 [[0, 6, 7, simpleSparseArray, 6], 277 [6, 4, 5, simpleSparseArray, 10], 278 [10, 2, 3, simpleSparseArray, 12]], 279 simpleSparseArray, sum, 0); 280 281testReduce("reduceRight", "SimpleSparseReduceRightProd", 48, 282 [[1, 6, 7, simpleSparseArray, 6], 283 [6, 4, 5, simpleSparseArray, 24], 284 [24, 2, 3, simpleSparseArray, 48]], 285 simpleSparseArray, prod, 1); 286 287testReduce("reduceRight", "SimpleSparseReduceRightDec", 204060, 288 [[0, 6, 7, simpleSparseArray, 60], 289 [60, 4, 5, simpleSparseArray, 4060], 290 [4060, 2, 3, simpleSparseArray, 204060]], 291 simpleSparseArray, dec, 0); 292 293testReduce("reduceRight", "SimpleSparseReduceRightAccumulate", [,,,2,,4,,6], 294 [[[], 6, 7, simpleSparseArray, [,,,,,,,6]], 295 [[,,,,,,,6], 4, 5, simpleSparseArray, [,,,,,4,,6]], 296 [[,,,,,4,,6], 2, 3, simpleSparseArray, [,,,2,,4,,6]]], 297 simpleSparseArray, accumulate, []); 298 299 300testReduce("reduceRight", "EmptySparseReduceRightSumNoInit", 301 0, [], [,,0,,], sum); 302testReduce("reduceRight", "EmptySparseReduceRightProdNoInit", 303 1, [], [,,1,,], prod); 304testReduce("reduceRight", "EmptySparseReduceRightDecNoInit", 305 0, [], [,,0,,], dec); 306testReduce("reduceRight", "EmptySparseReduceRightAccumulateNoInit", 307 [], [], [,,[],,], accumulate); 308 309 310var verySparseSuffix6 = []; 311verySparseSuffix6[9000] = 6; 312var verySparseSuffix4 = []; 313verySparseSuffix4[5000] = 4; 314verySparseSuffix4[9000] = 6; 315var verySparseSuffix2 = verySparseSlice6; 316 317 318testReduce("reduceRight", "VerySparseReduceRightSum", 12, 319 [[0, 6, 9000, verySparseArray, 6], 320 [6, 4, 5000, verySparseArray, 10], 321 [10, 2, 2000, verySparseArray, 12]], 322 verySparseArray, sum, 0); 323 324testReduce("reduceRight", "VerySparseReduceRightProd", 48, 325 [[1, 6, 9000, verySparseArray, 6], 326 [6, 4, 5000, verySparseArray, 24], 327 [24, 2, 2000, verySparseArray, 48]], 328 verySparseArray, prod, 1); 329 330testReduce("reduceRight", "VerySparseReduceRightDec", Infinity, 331 [[0, 6, 9000, verySparseArray, Infinity], 332 [Infinity, 4, 5000, verySparseArray, Infinity], 333 [Infinity, 2, 2000, verySparseArray, Infinity]], 334 verySparseArray, dec, 0); 335 336testReduce("reduceRight", "VerySparseReduceRightAccumulate", 337 verySparseSuffix2, 338 [[[], 6, 9000, verySparseArray, verySparseSuffix6], 339 [verySparseSuffix6, 4, 5000, verySparseArray, verySparseSuffix4], 340 [verySparseSuffix4, 2, 2000, verySparseArray, verySparseSuffix2]], 341 verySparseArray, accumulate, []); 342 343 344testReduce("reduceRight", "VerySparseReduceRightSumNoInit", 12, 345 [[6, 4, 5000, verySparseArray, 10], 346 [10, 2, 2000, verySparseArray, 12]], 347 verySparseArray, sum); 348 349testReduce("reduceRight", "VerySparseReduceRightProdNoInit", 48, 350 [[6, 4, 5000, verySparseArray, 24], 351 [24, 2, 2000, verySparseArray, 48]], 352 verySparseArray, prod); 353 354testReduce("reduceRight", "VerySparseReduceRightDecNoInit", Infinity, 355 [[6, 4, 5000, verySparseArray, Infinity], 356 [Infinity, 2, 2000, verySparseArray, Infinity]], 357 verySparseArray, dec); 358 359testReduce("reduceRight", "SimpleSparseReduceRightAccumulateNoInit", 360 6, 361 [[6, 4, 5000, verySparseArray, 6], 362 [6, 2, 2000, verySparseArray, 6]], 363 verySparseArray, accumulate); 364 365 366// undefined is an element 367var undefArray = [,,undefined,,undefined,,]; 368 369testReduce("reduce", "SparseUndefinedReduceAdd", NaN, 370 [[0, undefined, 2, undefArray, NaN], 371 [NaN, undefined, 4, undefArray, NaN], 372 ], 373 undefArray, sum, 0); 374 375testReduce("reduceRight", "SparseUndefinedReduceRightAdd", NaN, 376 [[0, undefined, 4, undefArray, NaN], 377 [NaN, undefined, 2, undefArray, NaN], 378 ], undefArray, sum, 0); 379 380testReduce("reduce", "SparseUndefinedReduceAddNoInit", NaN, 381 [[undefined, undefined, 4, undefArray, NaN], 382 ], undefArray, sum); 383 384testReduce("reduceRight", "SparseUndefinedReduceRightAddNoInit", NaN, 385 [[undefined, undefined, 2, undefArray, NaN], 386 ], undefArray, sum); 387 388 389// Ignore non-array properties: 390 391var arrayPlus = [1,2,,3]; 392arrayPlus[-1] = NaN; 393arrayPlus[Math.pow(2,32)] = NaN; 394arrayPlus[NaN] = NaN; 395arrayPlus["00"] = NaN; 396arrayPlus["02"] = NaN; 397arrayPlus["-0"] = NaN; 398 399testReduce("reduce", "ArrayWithNonElementPropertiesReduce", 6, 400 [[0, 1, 0, arrayPlus, 1], 401 [1, 2, 1, arrayPlus, 3], 402 [3, 3, 3, arrayPlus, 6], 403 ], arrayPlus, sum, 0); 404 405testReduce("reduceRight", "ArrayWithNonElementPropertiesReduceRight", 6, 406 [[0, 3, 3, arrayPlus, 3], 407 [3, 2, 1, arrayPlus, 5], 408 [5, 1, 0, arrayPlus, 6], 409 ], arrayPlus, sum, 0); 410 411 412// Test error conditions: 413 414var exception = false; 415try { 416 [1].reduce("not a function"); 417} catch (e) { 418 exception = true; 419 assertTrue(e instanceof TypeError, 420 "reduce callback not a function not throwing TypeError"); 421 assertTrue(e.message.indexOf(" is not a function") >= 0, 422 "reduce non function TypeError type"); 423} 424assertTrue(exception); 425 426exception = false; 427try { 428 [1].reduceRight("not a function"); 429} catch (e) { 430 exception = true; 431 assertTrue(e instanceof TypeError, 432 "reduceRight callback not a function not throwing TypeError"); 433 assertTrue(e.message.indexOf(" is not a function") >= 0, 434 "reduceRight non function TypeError type"); 435} 436assertTrue(exception); 437 438exception = false; 439try { 440 [].reduce(sum); 441} catch (e) { 442 exception = true; 443 assertTrue(e instanceof TypeError, 444 "reduce no initial value not throwing TypeError"); 445 assertEquals("Reduce of empty array with no initial value", e.message, 446 "reduce no initial TypeError type"); 447} 448assertTrue(exception); 449 450exception = false; 451try { 452 [].reduceRight(sum); 453} catch (e) { 454 exception = true; 455 assertTrue(e instanceof TypeError, 456 "reduceRight no initial value not throwing TypeError"); 457 assertEquals("Reduce of empty array with no initial value", e.message, 458 "reduceRight no initial TypeError type"); 459} 460assertTrue(exception); 461 462exception = false; 463try { 464 [,,,].reduce(sum); 465} catch (e) { 466 exception = true; 467 assertTrue(e instanceof TypeError, 468 "reduce sparse no initial value not throwing TypeError"); 469 assertEquals("Reduce of empty array with no initial value", e.message, 470 "reduce no initial TypeError type"); 471} 472assertTrue(exception); 473 474exception = false; 475try { 476 [,,,].reduceRight(sum); 477} catch (e) { 478 exception = true; 479 assertTrue(e instanceof TypeError, 480 "reduceRight sparse no initial value not throwing TypeError"); 481 assertEquals("Reduce of empty array with no initial value", e.message, 482 "reduceRight no initial TypeError type"); 483} 484assertTrue(exception); 485 486 487// Array changing length 488 489function manipulator(a, b, i, s) { 490 if (s.length % 2) { 491 s[s.length * 3] = i; 492 } else { 493 s.length = s.length >> 1; 494 } 495 return a + b; 496} 497 498var arr = [1, 2, 3, 4]; 499testReduce("reduce", "ArrayManipulationShort", 3, 500 [[0, 1, 0, [1, 2, 3, 4], 1], 501 [1, 2, 1, [1, 2], 3], 502 ], arr, manipulator, 0); 503 504var arr = [1, 2, 3, 4, 5]; 505testReduce("reduce", "ArrayManipulationLonger", 10, 506 [[0, 1, 0, [1, 2, 3, 4, 5], 1], 507 [1, 2, 1, [1, 2, 3, 4, 5,,,,,,,,,,, 0], 3], 508 [3, 3, 2, [1, 2, 3, 4, 5,,,,], 6], 509 [6, 4, 3, [1, 2, 3, 4], 10], 510 ], arr, manipulator, 0); 511 512function extender(a, b, i, s) { 513 s[s.length] = s.length; 514 return a + b; 515} 516 517var arr = [1, 2, 3, 4]; 518testReduce("reduce", "ArrayManipulationExtender", 10, 519 [[0, 1, 0, [1, 2, 3, 4], 1], 520 [1, 2, 1, [1, 2, 3, 4, 4], 3], 521 [3, 3, 2, [1, 2, 3, 4, 4, 5], 6], 522 [6, 4, 3, [1, 2, 3, 4, 4, 5, 6], 10], 523 ], arr, extender, 0); 524 525var arr = []; 526Object.defineProperty(arr, "0", { get: function() { delete this[0] }, 527 configurable: true }); 528assertEquals(undefined, arr.reduce(function(val) { return val })); 529 530var arr = []; 531Object.defineProperty(arr, "0", { get: function() { delete this[0] }, 532 configurable: true}); 533assertEquals(undefined, arr.reduceRight(function(val) { return val })); 534