1613 lines
50 KiB
Plaintext
1613 lines
50 KiB
Plaintext
// Comprehensive test suite for cell runtime stability
|
|
// Tests all core features before implementing performance optimizations
|
|
// (bytecode passes, ICs, quickening, tail call optimization)
|
|
//
|
|
return {
|
|
// ============================================================================
|
|
// ARITHMETIC OPERATORS - Numbers
|
|
// ============================================================================
|
|
|
|
test_number_addition: function() {
|
|
if (1 + 2 != 3) throw "basic addition failed"
|
|
if (0 + 0 != 0) throw "zero addition failed"
|
|
if (-5 + 3 != -2) throw "negative addition failed"
|
|
if (0.1 + 0.2 - 0.3 > 0.0001) throw "float addition precision issue"
|
|
},
|
|
|
|
test_number_subtraction: function() {
|
|
if (5 - 3 != 2) throw "basic subtraction failed"
|
|
if (0 - 5 != -5) throw "zero subtraction failed"
|
|
if (-5 - -3 != -2) throw "negative subtraction failed"
|
|
},
|
|
|
|
test_number_multiplication: function() {
|
|
if (3 * 4 != 12) throw "basic multiplication failed"
|
|
if (0 * 100 != 0) throw "zero multiplication failed"
|
|
if (-3 * 4 != -12) throw "negative multiplication failed"
|
|
if (-3 * -4 != 12) throw "double negative multiplication failed"
|
|
},
|
|
|
|
test_number_division: function() {
|
|
if (12 / 4 != 3) throw "basic division failed"
|
|
if (1 / 2 != 0.5) throw "fractional division failed"
|
|
if (-12 / 4 != -3) throw "negative division failed"
|
|
if (12 / -4 != -3) throw "division by negative failed"
|
|
},
|
|
|
|
test_number_modulo: function() {
|
|
if (10 % 3 != 1) throw "basic modulo failed"
|
|
if (10 % 5 != 0) throw "even modulo failed"
|
|
if (-10 % 3 != -1) throw "negative modulo failed"
|
|
},
|
|
|
|
test_number_exponentiation: function() {
|
|
if (2 ** 3 != 8) throw "basic exponentiation failed"
|
|
if (5 ** 0 != 1) throw "zero exponent failed"
|
|
if (2 ** -1 != 0.5) throw "negative exponent failed"
|
|
},
|
|
|
|
// ============================================================================
|
|
// STRING OPERATORS
|
|
// ============================================================================
|
|
|
|
test_string_plus_string_works: function() {
|
|
var x = "hello" + " world"
|
|
if (x != "hello world") throw "string + string should work"
|
|
},
|
|
|
|
test_string_concatenation_empty: function() {
|
|
if ("" + "" != "") throw "empty string concatenation failed"
|
|
if ("hello" + "" != "hello") throw "concatenation with empty string failed"
|
|
if ("" + "world" != "world") throw "empty + string failed"
|
|
},
|
|
|
|
// ============================================================================
|
|
// TYPE MIXING SHOULD THROW
|
|
// ============================================================================
|
|
|
|
test_number_plus_string_throws: function() {
|
|
var caught = false
|
|
try {
|
|
var x = 1 + "hello"
|
|
} catch (e) {
|
|
caught = true
|
|
}
|
|
if (!caught) throw "number + string should throw"
|
|
},
|
|
|
|
test_string_plus_number_throws: function() {
|
|
var caught = false
|
|
try {
|
|
var x = "hello" + 1
|
|
} catch (e) {
|
|
caught = true
|
|
}
|
|
if (!caught) throw "string + number should throw"
|
|
},
|
|
|
|
test_object_plus_string_throws: function() {
|
|
var caught = false
|
|
try {
|
|
var x = {} + "hello"
|
|
} catch (e) {
|
|
caught = true
|
|
}
|
|
if (!caught) throw "object + string should throw"
|
|
},
|
|
|
|
test_string_plus_object_throws: function() {
|
|
var caught = false
|
|
try {
|
|
var x = "hello" + {}
|
|
} catch (e) {
|
|
caught = true
|
|
}
|
|
if (!caught) throw "string + object should throw"
|
|
},
|
|
|
|
test_array_plus_string_throws: function() {
|
|
var caught = false
|
|
try {
|
|
var x = [] + "hello"
|
|
} catch (e) {
|
|
caught = true
|
|
}
|
|
if (!caught) throw "array + string should throw"
|
|
},
|
|
|
|
test_string_plus_array_throws: function() {
|
|
var caught = false
|
|
try {
|
|
var x = "hello" + []
|
|
} catch (e) {
|
|
caught = true
|
|
}
|
|
if (!caught) throw "string + array should throw"
|
|
},
|
|
|
|
test_boolean_plus_string_throws: function() {
|
|
var caught = false
|
|
try {
|
|
var x = true + "hello"
|
|
} catch (e) {
|
|
caught = true
|
|
}
|
|
if (!caught) throw "boolean + string should throw"
|
|
},
|
|
|
|
test_string_plus_boolean_throws: function() {
|
|
var caught = false
|
|
try {
|
|
var x = "hello" + false
|
|
} catch (e) {
|
|
caught = true
|
|
}
|
|
if (!caught) throw "string + boolean should throw"
|
|
},
|
|
|
|
test_null_plus_string_throws: function() {
|
|
var caught = false
|
|
try {
|
|
var x = null + "hello"
|
|
} catch (e) {
|
|
caught = true
|
|
}
|
|
if (!caught) throw "null + string should throw"
|
|
},
|
|
|
|
test_string_plus_null_throws: function() {
|
|
var caught = false
|
|
try {
|
|
var x = "hello" + null
|
|
} catch (e) {
|
|
caught = true
|
|
}
|
|
if (!caught) throw "string + null should throw"
|
|
},
|
|
|
|
// ============================================================================
|
|
// COMPARISON OPERATORS
|
|
// ============================================================================
|
|
|
|
test_equality_numbers: function() {
|
|
if (!(5 == 5)) throw "number equality failed"
|
|
if (5 == 6) throw "number inequality detection failed"
|
|
if (!(0 == 0)) throw "zero equality failed"
|
|
if (!(-5 == -5)) throw "negative equality failed"
|
|
},
|
|
|
|
test_inequality_numbers: function() {
|
|
if (5 != 5) throw "number inequality failed"
|
|
if (!(5 != 6)) throw "number difference detection failed"
|
|
},
|
|
|
|
test_less_than: function() {
|
|
if (!(3 < 5)) throw "less than failed"
|
|
if (5 < 3) throw "not less than failed"
|
|
if (5 < 5) throw "equal not less than failed"
|
|
},
|
|
|
|
test_less_than_or_equal: function() {
|
|
if (!(3 <= 5)) throw "less than or equal failed"
|
|
if (!(5 <= 5)) throw "equal in less than or equal failed"
|
|
if (6 <= 5) throw "not less than or equal failed"
|
|
},
|
|
|
|
test_greater_than: function() {
|
|
if (!(5 > 3)) throw "greater than failed"
|
|
if (3 > 5) throw "not greater than failed"
|
|
if (5 > 5) throw "equal not greater than failed"
|
|
},
|
|
|
|
test_greater_than_or_equal: function() {
|
|
if (!(5 >= 3)) throw "greater than or equal failed"
|
|
if (!(5 >= 5)) throw "equal in greater than or equal failed"
|
|
if (3 >= 5) throw "not greater than or equal failed"
|
|
},
|
|
|
|
test_string_equality: function() {
|
|
if (!("hello" == "hello")) throw "string equality failed"
|
|
if ("hello" == "world") throw "string inequality detection failed"
|
|
if (!("" == "")) throw "empty string equality failed"
|
|
},
|
|
|
|
test_null_equality: function() {
|
|
if (!(null == null)) throw "null equality failed"
|
|
if (null == 0) throw "null should not equal 0"
|
|
if (null == false) throw "null should not equal false"
|
|
if (null == "") throw "null should not equal empty string"
|
|
},
|
|
|
|
test_boolean_equality: function() {
|
|
if (!(true == true)) throw "true equality failed"
|
|
if (!(false == false)) throw "false equality failed"
|
|
if (true == false) throw "boolean inequality detection failed"
|
|
},
|
|
|
|
// ============================================================================
|
|
// LOGICAL OPERATORS
|
|
// ============================================================================
|
|
|
|
test_logical_and: function() {
|
|
if (!(true && true)) throw "true && true failed"
|
|
if (true && false) throw "true && false failed"
|
|
if (false && true) throw "false && true failed"
|
|
if (false && false) throw "false && false failed"
|
|
},
|
|
|
|
test_logical_or: function() {
|
|
if (!(true || true)) throw "true || true failed"
|
|
if (!(true || false)) throw "true || false failed"
|
|
if (!(false || true)) throw "false || true failed"
|
|
if (false || false) throw "false || false failed"
|
|
},
|
|
|
|
test_logical_not: function() {
|
|
if (!(!false)) throw "!false failed"
|
|
if (!true) throw "!true failed"
|
|
},
|
|
|
|
test_short_circuit_and: function() {
|
|
var called = false
|
|
var fn = function() { called = true; return true }
|
|
var result = false && fn()
|
|
if (called) throw "AND should short circuit"
|
|
},
|
|
|
|
test_short_circuit_or: function() {
|
|
var called = false
|
|
var fn = function() { called = true; return false }
|
|
var result = true || fn()
|
|
if (called) throw "OR should short circuit"
|
|
},
|
|
|
|
// ============================================================================
|
|
// BITWISE OPERATORS
|
|
// ============================================================================
|
|
|
|
test_bitwise_and: function() {
|
|
if ((5 & 3) != 1) throw "bitwise AND failed"
|
|
if ((12 & 10) != 8) throw "bitwise AND failed"
|
|
},
|
|
|
|
test_bitwise_or: function() {
|
|
if ((5 | 3) != 7) throw "bitwise OR failed"
|
|
if ((12 | 10) != 14) throw "bitwise OR failed"
|
|
},
|
|
|
|
test_bitwise_xor: function() {
|
|
if ((5 ^ 3) != 6) throw "bitwise XOR failed"
|
|
if ((12 ^ 10) != 6) throw "bitwise XOR failed"
|
|
},
|
|
|
|
test_bitwise_not: function() {
|
|
if (~5 != -6) throw "bitwise NOT failed"
|
|
if (~0 != -1) throw "bitwise NOT of zero failed"
|
|
},
|
|
|
|
test_left_shift: function() {
|
|
if ((5 << 2) != 20) throw "left shift failed"
|
|
if ((1 << 3) != 8) throw "left shift failed"
|
|
},
|
|
|
|
test_right_shift: function() {
|
|
if ((20 >> 2) != 5) throw "right shift failed"
|
|
if ((8 >> 3) != 1) throw "right shift failed"
|
|
},
|
|
|
|
test_unsigned_right_shift: function() {
|
|
if ((-1 >>> 1) != 2147483647) throw "unsigned right shift failed"
|
|
},
|
|
|
|
// ============================================================================
|
|
// VARIABLE DECLARATIONS AND SCOPING
|
|
// ============================================================================
|
|
|
|
test_var_declaration: function() {
|
|
var x = 5
|
|
if (x != 5) throw "var declaration failed"
|
|
},
|
|
|
|
test_var_reassignment: function() {
|
|
var x = 5
|
|
x = 10
|
|
if (x != 10) throw "var reassignment failed"
|
|
},
|
|
|
|
// ============================================================================
|
|
// VAR BLOCK SCOPING (var now behaves like let)
|
|
// ============================================================================
|
|
|
|
test_var_block_scope_basic: function() {
|
|
var x = 1
|
|
{
|
|
var x = 2
|
|
if (x != 2) throw "var should be block scoped - inner scope failed"
|
|
}
|
|
if (x != 1) throw "var should be block scoped - outer scope affected"
|
|
},
|
|
|
|
test_var_block_scope_if: function() {
|
|
var x = 1
|
|
if (true) {
|
|
var x = 2
|
|
if (x != 2) throw "var in if block should be scoped"
|
|
}
|
|
if (x != 1) throw "var in if block should not affect outer scope"
|
|
},
|
|
|
|
test_var_block_scope_for: function() {
|
|
var x = 1
|
|
for (var i = 0; i < 1; i = i + 1) {
|
|
var x = 2
|
|
if (x != 2) throw "var in for block should be scoped"
|
|
}
|
|
if (x != 1) throw "var in for block should not affect outer scope"
|
|
},
|
|
|
|
test_var_for_loop_iterator_scope: function() {
|
|
var sum = 0
|
|
for (var i = 0; i < 3; i = i + 1) {
|
|
sum = sum + i
|
|
}
|
|
if (sum != 3) throw "for loop should work with block scoped var"
|
|
var caught = false
|
|
try {
|
|
var y = i
|
|
} catch (e) {
|
|
caught = true
|
|
}
|
|
if (!caught) throw "for loop iterator should not leak to outer scope"
|
|
},
|
|
|
|
test_var_nested_blocks: function() {
|
|
var x = 1
|
|
{
|
|
var x = 2
|
|
{
|
|
var x = 3
|
|
if (x != 3) throw "var in nested block level 2 failed"
|
|
}
|
|
if (x != 2) throw "var in nested block level 1 failed"
|
|
}
|
|
if (x != 1) throw "var in nested blocks outer scope failed"
|
|
},
|
|
|
|
test_var_redeclaration_different_scope: function() {
|
|
var x = 1
|
|
{
|
|
var x = 2
|
|
}
|
|
if (x != 1) throw "var in different scope should not affect outer"
|
|
},
|
|
|
|
test_var_switch_scope: function() {
|
|
var x = 1
|
|
switch (1) {
|
|
case 1:
|
|
var x = 2
|
|
if (x != 2) throw "var in switch should be block scoped"
|
|
break
|
|
}
|
|
if (x != 1) throw "var in switch should not affect outer scope"
|
|
},
|
|
|
|
test_var_while_scope: function() {
|
|
var x = 1
|
|
var count = 0
|
|
while (count < 1) {
|
|
var x = 2
|
|
if (x != 2) throw "var in while should be block scoped"
|
|
count = count + 1
|
|
}
|
|
if (x != 1) throw "var in while should not affect outer scope"
|
|
},
|
|
|
|
test_var_no_initialization: function() {
|
|
{
|
|
var x
|
|
if (x != null) throw "uninitialized var should be null"
|
|
}
|
|
},
|
|
|
|
test_multiple_var_declaration: function() {
|
|
var a = 1, b = 2, c = 3
|
|
if (a != 1 || b != 2 || c != 3) throw "multiple var declaration failed"
|
|
},
|
|
|
|
test_function_scope: function() {
|
|
var outer = "outer"
|
|
var fn = function() {
|
|
var inner = "inner"
|
|
return inner
|
|
}
|
|
if (fn() != "inner") throw "function scope failed"
|
|
},
|
|
|
|
// ============================================================================
|
|
// FUNCTION CALLS
|
|
// ============================================================================
|
|
|
|
test_function_call_no_args: function() {
|
|
var fn = function() { return 42 }
|
|
if (fn() != 42) throw "function call with no args failed"
|
|
},
|
|
|
|
test_function_call_one_arg: function() {
|
|
var fn = function(x) { return x * 2 }
|
|
if (fn(5) != 10) throw "function call with one arg failed"
|
|
},
|
|
|
|
test_function_call_multiple_args: function() {
|
|
var fn = function(a, b, c) { return a + b + c }
|
|
if (fn(1, 2, 3) != 6) throw "function call with multiple args failed"
|
|
},
|
|
|
|
test_function_call_extra_args: function() {
|
|
var fn = function(a, b) { return a + b }
|
|
if (fn(1, 2, 3, 4) != 3) throw "function call with extra args failed"
|
|
},
|
|
|
|
test_function_call_missing_args: function() {
|
|
var fn = function(a, b, c) { return (a || 0) + (b || 0) + (c || 0) }
|
|
if (fn(1) != 1) throw "function call with missing args failed"
|
|
},
|
|
|
|
test_function_return: function() {
|
|
var fn = function() { return 5 }
|
|
if (fn() != 5) throw "function return failed"
|
|
},
|
|
|
|
test_function_return_early: function() {
|
|
var fn = function() {
|
|
return 5
|
|
return 10
|
|
}
|
|
if (fn() != 5) throw "early return failed"
|
|
},
|
|
|
|
test_function_no_return: function() {
|
|
var fn = function() { var x = 5 }
|
|
if (fn() != null) throw "function with no return should return null"
|
|
},
|
|
|
|
test_nested_function_calls: function() {
|
|
var add = function(a, b) { return a + b }
|
|
var mul = function(a, b) { return a * b }
|
|
if (add(mul(2, 3), mul(4, 5)) != 26) throw "nested function calls failed"
|
|
},
|
|
|
|
test_function_as_value: function() {
|
|
var fn = function() { return 42 }
|
|
var fn2 = fn
|
|
if (fn2() != 42) throw "function as value failed"
|
|
},
|
|
|
|
test_function_closure: function() {
|
|
var outer = function(x) {
|
|
return function(y) {
|
|
return x + y
|
|
}
|
|
}
|
|
var add5 = outer(5)
|
|
if (add5(3) != 8) throw "closure failed"
|
|
},
|
|
|
|
test_function_closure_mutation: function() {
|
|
var counter = function() {
|
|
var count = 0
|
|
return function() {
|
|
count = count + 1
|
|
return count
|
|
}
|
|
}
|
|
var c = counter()
|
|
if (c() != 1) throw "closure mutation failed (1)"
|
|
if (c() != 2) throw "closure mutation failed (2)"
|
|
if (c() != 3) throw "closure mutation failed (3)"
|
|
},
|
|
|
|
// ============================================================================
|
|
// RECURSION
|
|
// ============================================================================
|
|
|
|
test_simple_recursion: function() {
|
|
var factorial = function(n) {
|
|
if (n <= 1) return 1
|
|
return n * factorial(n - 1)
|
|
}
|
|
if (factorial(5) != 120) throw "factorial recursion failed"
|
|
},
|
|
|
|
test_mutual_recursion: function() {
|
|
var isEven = function(n) {
|
|
if (n == 0) return true
|
|
return isOdd(n - 1)
|
|
}
|
|
var isOdd = function(n) {
|
|
if (n == 0) return false
|
|
return isEven(n - 1)
|
|
}
|
|
if (!isEven(4)) throw "mutual recursion even failed"
|
|
if (isOdd(4)) throw "mutual recursion odd failed"
|
|
},
|
|
|
|
test_deep_recursion: function() {
|
|
var sum = function(n) {
|
|
if (n == 0) return 0
|
|
return n + sum(n - 1)
|
|
}
|
|
if (sum(100) != 5050) throw "deep recursion failed"
|
|
},
|
|
|
|
// ============================================================================
|
|
// ARRAYS
|
|
// ============================================================================
|
|
|
|
test_array_literal: function() {
|
|
var arr = [1, 2, 3]
|
|
if (arr[0] != 1 || arr[1] != 2 || arr[2] != 3) throw "array literal failed"
|
|
},
|
|
|
|
test_array_length: function() {
|
|
var arr = [1, 2, 3, 4, 5]
|
|
if (length(arr) != 5) throw "array length failed"
|
|
},
|
|
|
|
test_array_empty: function() {
|
|
var arr = []
|
|
if (length(arr) != 0) throw "empty array length failed"
|
|
},
|
|
|
|
test_array_push: function() {
|
|
var arr = [1, 2]
|
|
arr.push(3)
|
|
if (length(arr) != 3) throw "array push length failed"
|
|
if (arr[2] != 3) throw "array push value failed"
|
|
},
|
|
|
|
test_array_pop: function() {
|
|
var arr = [1, 2, 3]
|
|
var val = arr.pop()
|
|
if (val != 3) throw "array pop value failed"
|
|
if (length(arr) != 2) throw "array pop length failed"
|
|
},
|
|
|
|
test_array_index_access: function() {
|
|
var arr = [10, 20, 30]
|
|
if (arr[0] != 10) throw "array index 0 failed"
|
|
if (arr[1] != 20) throw "array index 1 failed"
|
|
if (arr[2] != 30) throw "array index 2 failed"
|
|
},
|
|
|
|
test_array_index_assignment: function() {
|
|
var arr = [1, 2, 3]
|
|
arr[1] = 99
|
|
if (arr[1] != 99) throw "array index assignment failed"
|
|
},
|
|
|
|
test_array_mixed_types: function() {
|
|
var arr = [1, "hello", true, null, {}]
|
|
if (arr[0] != 1) throw "mixed array number failed"
|
|
if (arr[1] != "hello") throw "mixed array string failed"
|
|
if (arr[2] != true) throw "mixed array boolean failed"
|
|
if (arr[3] != null) throw "mixed array null failed"
|
|
},
|
|
|
|
test_array_nested: function() {
|
|
var arr = [[1, 2], [3, 4]]
|
|
if (arr[0][0] != 1) throw "nested array access failed"
|
|
if (arr[1][1] != 4) throw "nested array access failed"
|
|
},
|
|
|
|
// ============================================================================
|
|
// OBJECTS
|
|
// ============================================================================
|
|
|
|
test_object_literal: function() {
|
|
var obj = {a: 1, b: 2}
|
|
if (obj.a != 1 || obj.b != 2) throw "object literal failed"
|
|
},
|
|
|
|
test_object_property_access: function() {
|
|
var obj = {name: "Alice", age: 30}
|
|
if (obj.name != "Alice") throw "object property access failed"
|
|
if (obj.age != 30) throw "object property access failed"
|
|
},
|
|
|
|
test_object_bracket_access: function() {
|
|
var obj = {x: 10, y: 20}
|
|
if (obj["x"] != 10) throw "object bracket access failed"
|
|
if (obj["y"] != 20) throw "object bracket access failed"
|
|
},
|
|
|
|
test_object_property_assignment: function() {
|
|
var obj = {a: 1}
|
|
obj.a = 99
|
|
if (obj.a != 99) throw "object property assignment failed"
|
|
},
|
|
|
|
test_object_add_property: function() {
|
|
var obj = {}
|
|
obj.newProp = 42
|
|
if (obj.newProp != 42) throw "object add property failed"
|
|
},
|
|
|
|
test_object_computed_property: function() {
|
|
var key = "dynamicKey"
|
|
var obj = {}
|
|
obj[key] = 123
|
|
if (obj.dynamicKey != 123) throw "object computed property failed"
|
|
},
|
|
|
|
test_object_nested: function() {
|
|
var obj = {outer: {inner: 42}}
|
|
if (obj.outer.inner != 42) throw "nested object access failed"
|
|
},
|
|
|
|
test_object_method: function() {
|
|
var obj = {
|
|
value: 10,
|
|
getValue: function() { return this.value }
|
|
}
|
|
if (obj.getValue() != 10) throw "object method failed"
|
|
},
|
|
|
|
test_object_this_binding: function() {
|
|
var obj = {
|
|
x: 5,
|
|
getX: function() { return this.x }
|
|
}
|
|
if (obj.getX() != 5) throw "this binding failed"
|
|
},
|
|
|
|
// ============================================================================
|
|
// CONTROL FLOW - IF/ELSE
|
|
// ============================================================================
|
|
|
|
test_if_true: function() {
|
|
var x = 0
|
|
if (true) x = 1
|
|
if (x != 1) throw "if true failed"
|
|
},
|
|
|
|
test_if_false: function() {
|
|
var x = 0
|
|
if (false) x = 1
|
|
if (x != 0) throw "if false failed"
|
|
},
|
|
|
|
test_if_else_true: function() {
|
|
var x = 0
|
|
if (true) x = 1
|
|
else x = 2
|
|
if (x != 1) throw "if else true failed"
|
|
},
|
|
|
|
test_if_else_false: function() {
|
|
var x = 0
|
|
if (false) x = 1
|
|
else x = 2
|
|
if (x != 2) throw "if else false failed"
|
|
},
|
|
|
|
test_if_else_if: function() {
|
|
var x = 0
|
|
if (false) x = 1
|
|
else if (true) x = 2
|
|
else x = 3
|
|
if (x != 2) throw "if else if failed"
|
|
},
|
|
|
|
test_nested_if: function() {
|
|
var x = 0
|
|
if (true) {
|
|
if (true) {
|
|
x = 1
|
|
}
|
|
}
|
|
if (x != 1) throw "nested if failed"
|
|
},
|
|
|
|
// ============================================================================
|
|
// CONTROL FLOW - WHILE LOOPS
|
|
// ============================================================================
|
|
|
|
test_while_loop: function() {
|
|
var i = 0
|
|
var sum = 0
|
|
while (i < 5) {
|
|
sum = sum + i
|
|
i = i + 1
|
|
}
|
|
if (sum != 10) throw "while loop failed"
|
|
},
|
|
|
|
test_while_never_executes: function() {
|
|
var x = 0
|
|
while (false) {
|
|
x = 1
|
|
}
|
|
if (x != 0) throw "while never executes failed"
|
|
},
|
|
|
|
test_while_break: function() {
|
|
var i = 0
|
|
while (true) {
|
|
if (i >= 5) break
|
|
i = i + 1
|
|
}
|
|
if (i != 5) throw "while break failed"
|
|
},
|
|
|
|
test_while_continue: function() {
|
|
var i = 0
|
|
var sum = 0
|
|
while (i < 10) {
|
|
i = i + 1
|
|
if (i % 2 == 0) continue
|
|
sum = sum + i
|
|
}
|
|
if (sum != 25) throw "while continue failed"
|
|
},
|
|
|
|
// ============================================================================
|
|
// CONTROL FLOW - FOR LOOPS
|
|
// ============================================================================
|
|
|
|
test_for_loop: function() {
|
|
var sum = 0
|
|
for (var i = 0; i < 5; i = i + 1) {
|
|
sum = sum + i
|
|
}
|
|
if (sum != 10) throw "for loop failed"
|
|
},
|
|
|
|
test_for_loop_break: function() {
|
|
var sum = 0
|
|
for (var i = 0; i < 10; i = i + 1) {
|
|
if (i == 5) break
|
|
sum = sum + i
|
|
}
|
|
if (sum != 10) throw "for loop break failed"
|
|
},
|
|
|
|
test_for_loop_continue: function() {
|
|
var sum = 0
|
|
for (var i = 0; i < 10; i = i + 1) {
|
|
if (i % 2 == 0) continue
|
|
sum = sum + i
|
|
}
|
|
if (sum != 25) throw "for loop continue failed"
|
|
},
|
|
|
|
test_for_in_array: function() {
|
|
var arr = [10, 20, 30]
|
|
var sum = 0
|
|
for (var i in arr) {
|
|
sum = sum + arr[i]
|
|
}
|
|
if (sum != 60) throw "for in array failed"
|
|
},
|
|
|
|
test_for_in_object: function() {
|
|
var obj = {a: 1, b: 2, c: 3}
|
|
var sum = 0
|
|
for (var key in obj) {
|
|
sum = sum + obj[key]
|
|
}
|
|
if (sum != 6) throw "for in object failed"
|
|
},
|
|
|
|
test_nested_for_loops: function() {
|
|
var sum = 0
|
|
for (var i = 0; i < 3; i = i + 1) {
|
|
for (var j = 0; j < 3; j = j + 1) {
|
|
sum = sum + 1
|
|
}
|
|
}
|
|
if (sum != 9) throw "nested for loops failed"
|
|
},
|
|
|
|
// ============================================================================
|
|
// CONTROL FLOW - SWITCH
|
|
// ============================================================================
|
|
|
|
test_switch_case: function() {
|
|
var x = 2
|
|
var result = 0
|
|
switch (x) {
|
|
case 1:
|
|
result = 10
|
|
break
|
|
case 2:
|
|
result = 20
|
|
break
|
|
case 3:
|
|
result = 30
|
|
break
|
|
}
|
|
if (result != 20) throw "switch case failed"
|
|
},
|
|
|
|
test_switch_default: function() {
|
|
var x = 99
|
|
var result = 0
|
|
switch (x) {
|
|
case 1:
|
|
result = 10
|
|
break
|
|
default:
|
|
result = -1
|
|
break
|
|
}
|
|
if (result != -1) throw "switch default failed"
|
|
},
|
|
|
|
test_switch_fallthrough: function() {
|
|
var x = 1
|
|
var result = 0
|
|
switch (x) {
|
|
case 1:
|
|
result = result + 1
|
|
case 2:
|
|
result = result + 2
|
|
break
|
|
case 3:
|
|
result = result + 3
|
|
break
|
|
}
|
|
if (result != 3) throw "switch fallthrough failed"
|
|
},
|
|
|
|
// ============================================================================
|
|
// ERROR HANDLING - TRY/CATCH
|
|
// ============================================================================
|
|
|
|
test_try_catch: function() {
|
|
var caught = false
|
|
try {
|
|
throw "error"
|
|
} catch (e) {
|
|
caught = true
|
|
}
|
|
if (!caught) throw "try catch failed"
|
|
},
|
|
|
|
test_try_catch_error_value: function() {
|
|
var errorMsg = null
|
|
try {
|
|
throw "my error"
|
|
} catch (e) {
|
|
errorMsg = e
|
|
}
|
|
if (errorMsg != "my error") throw "try catch error value failed"
|
|
},
|
|
|
|
test_try_no_error: function() {
|
|
var x = 0
|
|
try {
|
|
x = 1
|
|
} catch (e) {
|
|
x = 2
|
|
}
|
|
if (x != 1) throw "try no error failed"
|
|
},
|
|
|
|
test_nested_try_catch: function() {
|
|
var x = 0
|
|
try {
|
|
try {
|
|
throw "inner"
|
|
} catch (e) {
|
|
x = 1
|
|
}
|
|
x = 2
|
|
} catch (e) {
|
|
x = 3
|
|
}
|
|
if (x != 2) throw "nested try catch failed"
|
|
},
|
|
|
|
test_try_catch_rethrow: function() {
|
|
var outerCaught = false
|
|
try {
|
|
try {
|
|
throw "error"
|
|
} catch (e) {
|
|
throw e
|
|
}
|
|
} catch (e) {
|
|
outerCaught = true
|
|
}
|
|
if (!outerCaught) throw "try catch rethrow failed"
|
|
},
|
|
|
|
// ============================================================================
|
|
// TYPEOF AND TYPE CHECKING
|
|
// ============================================================================
|
|
|
|
test_typeof_number: function() {
|
|
if (typeof 42 != "number") throw "typeof number failed"
|
|
if (typeof 3.14 != "number") throw "typeof float failed"
|
|
if (typeof -5 != "number") throw "typeof negative failed"
|
|
},
|
|
|
|
test_typeof_string: function() {
|
|
if (typeof "hello" != "string") throw "typeof string failed"
|
|
if (typeof "" != "string") throw "typeof empty string failed"
|
|
},
|
|
|
|
test_typeof_boolean: function() {
|
|
if (typeof true != "boolean") throw "typeof true failed"
|
|
if (typeof false != "boolean") throw "typeof false failed"
|
|
},
|
|
|
|
test_typeof_object: function() {
|
|
if (typeof {} != "object") throw "typeof object failed"
|
|
if (typeof [] != "object") throw "typeof array failed"
|
|
},
|
|
|
|
test_typeof_function: function() {
|
|
if (typeof function(){} != "function") throw "typeof function failed"
|
|
},
|
|
|
|
// ============================================================================
|
|
// ISA TYPE CHECKING
|
|
// ============================================================================
|
|
|
|
test_isa_number: function() {
|
|
if (!isa(42, number)) throw "isa number failed"
|
|
if (isa("42", number)) throw "isa string not number failed"
|
|
},
|
|
|
|
test_isa_text: function() {
|
|
if (!isa("hello", text)) throw "isa text failed"
|
|
if (isa(123, text)) throw "isa number not text failed"
|
|
},
|
|
|
|
test_isa_logical: function() {
|
|
if (!isa(true, logical)) throw "isa true failed"
|
|
if (!isa(false, logical)) throw "isa false failed"
|
|
if (isa(1, logical)) throw "isa number not logical failed"
|
|
},
|
|
|
|
test_isa_array: function() {
|
|
if (!isa([], array)) throw "isa empty array failed"
|
|
if (!isa([1,2,3], array)) throw "isa array failed"
|
|
if (isa({}, array)) throw "isa object not array failed"
|
|
},
|
|
|
|
test_isa_object: function() {
|
|
if (!isa({}, object)) throw "isa empty object failed"
|
|
if (!isa({a:1}, object)) throw "isa object failed"
|
|
if (isa([], object)) throw "isa array not object failed"
|
|
if (isa(null, object)) throw "isa null not object failed"
|
|
},
|
|
|
|
test_isa_null: function() {
|
|
if (isa(null, number)) throw "null not number"
|
|
if (isa(null, text)) throw "null not text"
|
|
if (isa(null, object)) throw "null not object"
|
|
},
|
|
|
|
// ============================================================================
|
|
// GLOBAL FUNCTIONS - LENGTH
|
|
// ============================================================================
|
|
|
|
test_length_string: function() {
|
|
if (length("hello") != 5) throw "length string failed"
|
|
if (length("") != 0) throw "length empty string failed"
|
|
},
|
|
|
|
test_length_array: function() {
|
|
if (length([1,2,3]) != 3) throw "length array failed"
|
|
if (length([]) != 0) throw "length empty array failed"
|
|
},
|
|
|
|
test_length_null: function() {
|
|
if (length(null) != null) throw "length null failed"
|
|
},
|
|
|
|
test_length_number: function() {
|
|
if (length(123) != null) throw "length number should return null"
|
|
},
|
|
|
|
// ============================================================================
|
|
// GLOBAL FUNCTIONS - REVERSE
|
|
// ============================================================================
|
|
|
|
test_reverse_array: function() {
|
|
var arr = [1, 2, 3, 4, 5]
|
|
var rev = reverse(arr)
|
|
if (rev[0] != 5) throw "reverse array first failed"
|
|
if (rev[4] != 1) throw "reverse array last failed"
|
|
if (length(rev) != 5) throw "reverse array length failed"
|
|
},
|
|
|
|
test_reverse_empty_array: function() {
|
|
var rev = reverse([])
|
|
if (length(rev) != 0) throw "reverse empty array failed"
|
|
},
|
|
|
|
test_reverse_preserves_original: function() {
|
|
var arr = [1, 2, 3]
|
|
var rev = reverse(arr)
|
|
if (arr[0] != 1) throw "reverse should not mutate original"
|
|
},
|
|
|
|
// ============================================================================
|
|
// GLOBAL FUNCTIONS - MEME (PROTOTYPAL INHERITANCE)
|
|
// ============================================================================
|
|
|
|
test_meme_basic: function() {
|
|
var parent = {x: 10}
|
|
var child = meme(parent)
|
|
if (child.x != 10) throw "meme basic inheritance failed"
|
|
},
|
|
|
|
test_meme_with_mixins: function() {
|
|
var parent = {x: 10}
|
|
var mixin = {y: 20}
|
|
var child = meme(parent, mixin)
|
|
if (child.x != 10) throw "meme with mixin parent prop failed"
|
|
if (child.y != 20) throw "meme with mixin own prop failed"
|
|
},
|
|
|
|
test_meme_override: function() {
|
|
var parent = {x: 10}
|
|
var child = meme(parent)
|
|
child.x = 20
|
|
if (child.x != 20) throw "meme override failed"
|
|
if (parent.x != 10) throw "meme should not mutate parent"
|
|
},
|
|
|
|
test_meme_multiple_mixins: function() {
|
|
var parent = {a: 1}
|
|
var mixin1 = {b: 2}
|
|
var mixin2 = {c: 3}
|
|
var child = meme(parent, mixin1, mixin2)
|
|
if (child.a != 1 || child.b != 2 || child.c != 3) throw "meme multiple mixins failed"
|
|
},
|
|
|
|
// ============================================================================
|
|
// GLOBAL FUNCTIONS - PROTO
|
|
// ============================================================================
|
|
|
|
test_proto_basic: function() {
|
|
var parent = {x: 10}
|
|
var child = meme(parent)
|
|
var p = proto(child)
|
|
if (p != parent) throw "proto basic failed"
|
|
},
|
|
|
|
test_proto_object_literal: function() {
|
|
var obj = {x: 10}
|
|
var p = proto(obj)
|
|
if (p != null) throw "proto of object literal should be null"
|
|
},
|
|
|
|
test_proto_non_object: function() {
|
|
if (proto(42) != null) throw "proto of number should return null"
|
|
if (proto("hello") != null) throw "proto of string should return null"
|
|
},
|
|
|
|
// ============================================================================
|
|
// GLOBAL FUNCTIONS - STONE (FREEZE)
|
|
// ============================================================================
|
|
|
|
test_stone_object: function() {
|
|
var obj = {x: 10}
|
|
stone(obj)
|
|
var caught = false
|
|
try {
|
|
obj.x = 20
|
|
} catch (e) {
|
|
caught = true
|
|
}
|
|
if (!caught) throw "stone object should prevent modification"
|
|
},
|
|
|
|
test_stone_p_frozen: function() {
|
|
var obj = {x: 10}
|
|
if (stone.p(obj)) throw "stone.p should return false before freezing"
|
|
stone(obj)
|
|
if (!stone.p(obj)) throw "stone.p should return true after freezing"
|
|
},
|
|
|
|
test_stone_array: function() {
|
|
var arr = [1, 2, 3]
|
|
stone(arr)
|
|
var caught = false
|
|
try {
|
|
arr[0] = 99
|
|
} catch (e) {
|
|
caught = true
|
|
}
|
|
if (!caught) throw "stone array should prevent modification"
|
|
},
|
|
|
|
// ============================================================================
|
|
// TERNARY OPERATOR
|
|
// ============================================================================
|
|
|
|
test_ternary_true: function() {
|
|
var x = true ? 1 : 2
|
|
if (x != 1) throw "ternary true failed"
|
|
},
|
|
|
|
test_ternary_false: function() {
|
|
var x = false ? 1 : 2
|
|
if (x != 2) throw "ternary false failed"
|
|
},
|
|
|
|
test_ternary_nested: function() {
|
|
var x = true ? (false ? 1 : 2) : 3
|
|
if (x != 2) throw "ternary nested failed"
|
|
},
|
|
|
|
test_ternary_with_expressions: function() {
|
|
var a = 5
|
|
var b = 10
|
|
var max = (a > b) ? a : b
|
|
if (max != 10) throw "ternary with expressions failed"
|
|
},
|
|
|
|
// ============================================================================
|
|
// UNARY OPERATORS
|
|
// ============================================================================
|
|
|
|
test_unary_plus: function() {
|
|
if (+5 != 5) throw "unary plus positive failed"
|
|
if (+-5 != -5) throw "unary plus negative failed"
|
|
},
|
|
|
|
test_unary_minus: function() {
|
|
if (-5 != -5) throw "unary minus failed"
|
|
if (-(-5) != 5) throw "double unary minus failed"
|
|
},
|
|
|
|
test_increment_postfix: function() {
|
|
var x = 5
|
|
var y = x++
|
|
if (y != 5) throw "postfix increment return value failed"
|
|
if (x != 6) throw "postfix increment side effect failed"
|
|
},
|
|
|
|
test_increment_prefix: function() {
|
|
var x = 5
|
|
var y = ++x
|
|
if (y != 6) throw "prefix increment return value failed"
|
|
if (x != 6) throw "prefix increment side effect failed"
|
|
},
|
|
|
|
test_decrement_postfix: function() {
|
|
var x = 5
|
|
var y = x--
|
|
if (y != 5) throw "postfix decrement return value failed"
|
|
if (x != 4) throw "postfix decrement side effect failed"
|
|
},
|
|
|
|
test_decrement_prefix: function() {
|
|
var x = 5
|
|
var y = --x
|
|
if (y != 4) throw "prefix decrement return value failed"
|
|
if (x != 4) throw "prefix decrement side effect failed"
|
|
},
|
|
|
|
// ============================================================================
|
|
// COMPOUND ASSIGNMENT OPERATORS
|
|
// ============================================================================
|
|
|
|
test_plus_equals: function() {
|
|
var x = 5
|
|
x += 3
|
|
if (x != 8) throw "plus equals failed"
|
|
},
|
|
|
|
test_minus_equals: function() {
|
|
var x = 10
|
|
x -= 3
|
|
if (x != 7) throw "minus equals failed"
|
|
},
|
|
|
|
test_times_equals: function() {
|
|
var x = 4
|
|
x *= 3
|
|
if (x != 12) throw "times equals failed"
|
|
},
|
|
|
|
test_divide_equals: function() {
|
|
var x = 12
|
|
x /= 3
|
|
if (x != 4) throw "divide equals failed"
|
|
},
|
|
|
|
test_modulo_equals: function() {
|
|
var x = 10
|
|
x %= 3
|
|
if (x != 1) throw "modulo equals failed"
|
|
},
|
|
|
|
// ============================================================================
|
|
// EDGE CASES AND SPECIAL VALUES
|
|
// ============================================================================
|
|
|
|
test_infinity: function() {
|
|
var inf = 1 / 0
|
|
if (!(inf > 1000000)) throw "infinity failed"
|
|
if (!(-inf < -1000000)) throw "negative infinity failed"
|
|
},
|
|
|
|
test_nan: function() {
|
|
var nan = 0 / 0
|
|
if (nan == nan) throw "NaN should not equal itself"
|
|
},
|
|
|
|
test_max_safe_integer: function() {
|
|
var max = 9007199254740991
|
|
if (max + 1 - 1 != max) throw "max safe integer precision lost"
|
|
},
|
|
|
|
test_min_safe_integer: function() {
|
|
var min = -9007199254740991
|
|
if (min - 1 + 1 != min) throw "min safe integer precision lost"
|
|
},
|
|
|
|
test_empty_string_falsy: function() {
|
|
if ("") throw "empty string should be falsy"
|
|
},
|
|
|
|
test_zero_falsy: function() {
|
|
if (0) throw "zero should be falsy"
|
|
},
|
|
|
|
test_null_falsy: function() {
|
|
if (null) throw "null should be falsy"
|
|
},
|
|
|
|
test_false_falsy: function() {
|
|
if (false) throw "false should be falsy"
|
|
},
|
|
|
|
test_nonempty_string_truthy: function() {
|
|
if (!"hello") throw "non-empty string should be truthy"
|
|
},
|
|
|
|
test_nonzero_number_truthy: function() {
|
|
if (!42) throw "non-zero number should be truthy"
|
|
},
|
|
|
|
test_object_truthy: function() {
|
|
if (!{}) throw "empty object should be truthy"
|
|
},
|
|
|
|
test_array_truthy: function() {
|
|
if (![]) throw "empty array should be truthy"
|
|
},
|
|
|
|
// ============================================================================
|
|
// OPERATOR PRECEDENCE
|
|
// ============================================================================
|
|
|
|
test_precedence_multiply_add: function() {
|
|
if (2 + 3 * 4 != 14) throw "multiply before add precedence failed"
|
|
},
|
|
|
|
test_precedence_parentheses: function() {
|
|
if ((2 + 3) * 4 != 20) throw "parentheses precedence failed"
|
|
},
|
|
|
|
test_precedence_comparison_logical: function() {
|
|
if (!(1 < 2 && 3 < 4)) throw "comparison before logical precedence failed"
|
|
},
|
|
|
|
test_precedence_equality_logical: function() {
|
|
if (!(1 == 1 || 2 == 3)) throw "equality before logical precedence failed"
|
|
},
|
|
|
|
test_precedence_unary_multiplication: function() {
|
|
if (-2 * 3 != -6) throw "unary before multiplication precedence failed"
|
|
},
|
|
|
|
// ============================================================================
|
|
// COMMA OPERATOR
|
|
// ============================================================================
|
|
|
|
test_comma_operator: function() {
|
|
var x = (1, 2, 3)
|
|
if (x != 3) throw "comma operator failed"
|
|
},
|
|
|
|
test_comma_operator_with_side_effects: function() {
|
|
var a = 0
|
|
var x = (a = 1, a = 2, a + 1)
|
|
if (x != 3) throw "comma operator with side effects failed"
|
|
if (a != 2) throw "comma operator side effects failed"
|
|
},
|
|
|
|
// ============================================================================
|
|
// VARIABLE SHADOWING
|
|
// ============================================================================
|
|
|
|
test_variable_shadowing_function: function() {
|
|
var x = 10
|
|
var fn = function() {
|
|
var x = 20
|
|
return x
|
|
}
|
|
if (fn() != 20) throw "function shadowing failed"
|
|
if (x != 10) throw "outer variable after shadowing failed"
|
|
},
|
|
|
|
test_variable_shadowing_nested: function() {
|
|
var x = 10
|
|
var fn1 = function() {
|
|
var x = 20
|
|
var fn2 = function() {
|
|
var x = 30
|
|
return x
|
|
}
|
|
return fn2() + x
|
|
}
|
|
if (fn1() != 50) throw "nested shadowing failed"
|
|
},
|
|
|
|
// ============================================================================
|
|
// FUNCTION ARITY
|
|
// ============================================================================
|
|
|
|
test_function_length_property: function() {
|
|
var fn0 = function() {}
|
|
var fn1 = function(a) {}
|
|
var fn2 = function(a, b) {}
|
|
if (length(fn0) != 0) throw "function length 0 failed"
|
|
if (length(fn1) != 1) throw "function length 1 failed"
|
|
if (length(fn2) != 2) throw "function length 2 failed"
|
|
},
|
|
|
|
// ============================================================================
|
|
// NULL AND UNDEFINED BEHAVIOR
|
|
// ============================================================================
|
|
|
|
test_undefined_variable_is_null: function() {
|
|
var x
|
|
if (x != null) throw "undefined variable should be null"
|
|
},
|
|
|
|
// ============================================================================
|
|
// NUMBERS - SPECIAL OPERATIONS
|
|
// ============================================================================
|
|
|
|
test_number_toString_implicit: function() {
|
|
var n = 42
|
|
var caught = false
|
|
try {
|
|
var result = n + ""
|
|
} catch (e) {
|
|
caught = true
|
|
}
|
|
if (!caught) throw "number + string should throw"
|
|
},
|
|
|
|
test_number_division_by_zero: function() {
|
|
var result = 1 / 0
|
|
if (!(result > 1000000)) throw "division by zero should give infinity"
|
|
},
|
|
|
|
test_number_negative_division_by_zero: function() {
|
|
var result = -1 / 0
|
|
if (!(result < -1000000)) throw "negative division by zero should give -infinity"
|
|
},
|
|
|
|
test_zero_division_by_zero: function() {
|
|
var result = 0 / 0
|
|
if (result == result) throw "0/0 should give NaN"
|
|
},
|
|
|
|
// ============================================================================
|
|
// OBJECT PROPERTY EXISTENCE
|
|
// ============================================================================
|
|
|
|
test_in_operator: function() {
|
|
var obj = {a: 1, b: 2}
|
|
if (!("a" in obj)) throw "in operator for existing property failed"
|
|
if ("c" in obj) throw "in operator for non-existing property failed"
|
|
},
|
|
|
|
test_in_operator_array: function() {
|
|
var arr = [10, 20, 30]
|
|
if (!(0 in arr)) throw "in operator for array index 0 failed"
|
|
if (!(2 in arr)) throw "in operator for array index 2 failed"
|
|
if (3 in arr) throw "in operator for out of bounds index failed"
|
|
},
|
|
|
|
test_in_operator_prototype: function() {
|
|
var parent = {x: 10}
|
|
var child = meme(parent)
|
|
if (!("x" in child)) throw "in operator should find inherited property"
|
|
},
|
|
|
|
// ============================================================================
|
|
// GLOBAL FUNCTIONS - LOGICAL
|
|
// ============================================================================
|
|
|
|
test_logical_function_numbers: function() {
|
|
if (logical(0) != false) throw "logical(0) should be false"
|
|
if (logical(1) != true) throw "logical(1) should be true"
|
|
},
|
|
|
|
test_logical_function_strings: function() {
|
|
if (logical("false") != false) throw "logical('false') should be false"
|
|
if (logical("true") != true) throw "logical('true') should be true"
|
|
},
|
|
|
|
test_logical_function_booleans: function() {
|
|
if (logical(false) != false) throw "logical(false) should be false"
|
|
if (logical(true) != true) throw "logical(true) should be true"
|
|
},
|
|
|
|
test_logical_function_null: function() {
|
|
if (logical(null) != false) throw "logical(null) should be false"
|
|
},
|
|
|
|
test_logical_function_invalid: function() {
|
|
if (logical("invalid") != null) throw "logical(invalid) should return null"
|
|
if (logical(42) != null) throw "logical(42) should return null"
|
|
},
|
|
|
|
// ============================================================================
|
|
// ARRAY METHODS
|
|
// ============================================================================
|
|
|
|
test_array_shift: function() {
|
|
var arr = [1, 2, 3]
|
|
var first = arr.shift()
|
|
if (first != 1) throw "array shift value failed"
|
|
if (length(arr) != 2) throw "array shift length failed"
|
|
if (arr[0] != 2) throw "array shift remaining failed"
|
|
},
|
|
|
|
test_array_unshift: function() {
|
|
var arr = [2, 3]
|
|
arr.unshift(1)
|
|
if (length(arr) != 3) throw "array unshift length failed"
|
|
if (arr[0] != 1) throw "array unshift value failed"
|
|
},
|
|
|
|
test_array_splice: function() {
|
|
var arr = [1, 2, 3, 4, 5]
|
|
var removed = arr.splice(1, 2)
|
|
if (length(removed) != 2) throw "array splice removed length failed"
|
|
if (removed[0] != 2) throw "array splice removed values failed"
|
|
if (length(arr) != 3) throw "array splice remaining length failed"
|
|
if (arr[1] != 4) throw "array splice remaining values failed"
|
|
},
|
|
|
|
test_array_slice: function() {
|
|
var arr = [1, 2, 3, 4, 5]
|
|
var sliced = arr.slice(1, 3)
|
|
if (length(sliced) != 2) throw "array slice length failed"
|
|
if (sliced[0] != 2) throw "array slice first failed"
|
|
if (sliced[1] != 3) throw "array slice second failed"
|
|
if (length(arr) != 5) throw "array slice should not mutate original"
|
|
},
|
|
|
|
test_array_concat: function() {
|
|
var arr1 = [1, 2]
|
|
var arr2 = [3, 4]
|
|
var combined = arr1.concat(arr2)
|
|
if (length(combined) != 4) throw "array concat length failed"
|
|
if (combined[2] != 3) throw "array concat values failed"
|
|
},
|
|
|
|
test_array_join: function() {
|
|
var arr = [1, 2, 3]
|
|
var caught = false
|
|
try {
|
|
var str = arr.join(",")
|
|
} catch (e) {
|
|
caught = true
|
|
}
|
|
},
|
|
|
|
test_array_indexOf: function() {
|
|
var arr = [10, 20, 30, 20]
|
|
if (arr.indexOf(20) != 1) throw "array indexOf failed"
|
|
if (arr.indexOf(99) != -1) throw "array indexOf not found failed"
|
|
},
|
|
|
|
test_array_lastIndexOf: function() {
|
|
var arr = [10, 20, 30, 20]
|
|
if (arr.lastIndexOf(20) != 3) throw "array lastIndexOf failed"
|
|
if (arr.lastIndexOf(99) != -1) throw "array lastIndexOf not found failed"
|
|
},
|
|
|
|
// ============================================================================
|
|
// STRING METHODS
|
|
// ============================================================================
|
|
|
|
test_string_charAt: function() {
|
|
var str = "hello"
|
|
if (str.charAt(0) != "h") throw "string charAt first failed"
|
|
if (str.charAt(4) != "o") throw "string charAt last failed"
|
|
},
|
|
|
|
test_string_charCodeAt: function() {
|
|
var str = "A"
|
|
if (str.charCodeAt(0) != 65) throw "string charCodeAt failed"
|
|
},
|
|
|
|
test_string_substring: function() {
|
|
var str = "hello"
|
|
if (str.substring(1, 4) != "ell") throw "string substring failed"
|
|
},
|
|
|
|
test_string_substr: function() {
|
|
var str = "hello"
|
|
if (str.substr(1, 3) != "ell") throw "string substr failed"
|
|
},
|
|
|
|
test_string_slice: function() {
|
|
var str = "hello"
|
|
if (str.slice(1, 4) != "ell") throw "string slice failed"
|
|
if (str.slice(-2) != "lo") throw "string slice negative failed"
|
|
},
|
|
|
|
test_string_indexOf: function() {
|
|
var str = "hello world"
|
|
if (str.indexOf("world") != 6) throw "string indexOf failed"
|
|
if (str.indexOf("xyz") != -1) throw "string indexOf not found failed"
|
|
},
|
|
|
|
test_string_lastIndexOf: function() {
|
|
var str = "hello hello"
|
|
if (str.lastIndexOf("hello") != 6) throw "string lastIndexOf failed"
|
|
},
|
|
|
|
test_string_toLowerCase: function() {
|
|
var str = "HELLO"
|
|
if (str.toLowerCase() != "hello") throw "string toLowerCase failed"
|
|
},
|
|
|
|
test_string_toUpperCase: function() {
|
|
var str = "hello"
|
|
if (str.toUpperCase() != "HELLO") throw "string toUpperCase failed"
|
|
},
|
|
|
|
test_string_trim: function() {
|
|
var str = " hello "
|
|
if (str.trim() != "hello") throw "string trim failed"
|
|
},
|
|
|
|
test_string_split: function() {
|
|
var str = "a,b,c"
|
|
var parts = str.split(",")
|
|
if (length(parts) != 3) throw "string split length failed"
|
|
if (parts[1] != "b") throw "string split values failed"
|
|
},
|
|
|
|
test_string_replace: function() {
|
|
var str = "hello world"
|
|
var replaced = str.replace("world", "universe")
|
|
if (replaced != "hello universe") throw "string replace failed"
|
|
},
|
|
|
|
test_string_match: function() {
|
|
var str = "hello123"
|
|
var hasNumbers = /\d/.test(str)
|
|
if (!hasNumbers) throw "string match with regex failed"
|
|
},
|
|
|
|
test_string_plus_string_works: function() {
|
|
var x = "hello" + " world"
|
|
if (x != "hello world") throw "string + string should work"
|
|
},
|
|
|
|
null_access: function() {
|
|
var val = {}
|
|
var nn = val.a
|
|
if (nn != null) throw "val.a should return null"
|
|
},
|
|
}
|