3559 lines
104 KiB
Plaintext
3559 lines
104 KiB
Plaintext
// Comprehensive test suite for cell runtime stability
|
|
// Self-running: no test harness required
|
|
// Uses disrupt/disruption instead of throw/try/catch
|
|
|
|
var passed = 0
|
|
var failed = 0
|
|
var error_names = []
|
|
var error_reasons = []
|
|
var fail_msg = ""
|
|
|
|
// pre-allocate 500 slots to avoid array growth during disruption handlers
|
|
for (var _i = 0; _i < 5; _i++) {
|
|
error_names[] = null
|
|
error_reasons[] = null
|
|
}
|
|
|
|
var fail = function(msg) {
|
|
fail_msg = msg
|
|
disrupt
|
|
}
|
|
|
|
var assert_eq = function(actual, expected, msg) {
|
|
if (actual != expected) fail(msg + " (got=" + text(actual) + ")")
|
|
}
|
|
|
|
var run = function(name, fn) {
|
|
fail_msg = ""
|
|
fn()
|
|
passed = passed + 1
|
|
} disruption {
|
|
error_names[failed] = name
|
|
error_reasons[failed] = fail_msg == "" ? "disruption" : fail_msg
|
|
failed = failed + 1
|
|
}
|
|
|
|
var should_disrupt = function(fn) {
|
|
var caught = false
|
|
var wrapper = function() {
|
|
fn()
|
|
} disruption {
|
|
caught = true
|
|
}
|
|
wrapper()
|
|
return caught
|
|
}
|
|
|
|
// ============================================================================
|
|
// ARITHMETIC OPERATORS - Numbers
|
|
// ============================================================================
|
|
|
|
run("number addition", function() {
|
|
if (1 + 2 != 3) fail("basic addition failed")
|
|
if (0 + 0 != 0) fail("zero addition failed")
|
|
if (-5 + 3 != -2) fail("negative addition failed")
|
|
if (0.1 + 0.2 - 0.3 > 0.0001) fail("float addition precision issue")
|
|
})
|
|
|
|
run("number subtraction", function() {
|
|
if (5 - 3 != 2) fail("basic subtraction failed")
|
|
if (0 - 5 != -5) fail("zero subtraction failed")
|
|
if (-5 - -3 != -2) fail("negative subtraction failed")
|
|
})
|
|
|
|
run("number multiplication", function() {
|
|
if (3 * 4 != 12) fail("basic multiplication failed")
|
|
if (0 * 100 != 0) fail("zero multiplication failed")
|
|
if (-3 * 4 != -12) fail("negative multiplication failed")
|
|
if (-3 * -4 != 12) fail("double negative multiplication failed")
|
|
})
|
|
|
|
run("number division", function() {
|
|
if (12 / 4 != 3) fail("basic division failed")
|
|
if (1 / 2 != 0.5) fail("fractional division failed")
|
|
if (-12 / 4 != -3) fail("negative division failed")
|
|
if (12 / -4 != -3) fail("division by negative failed")
|
|
})
|
|
|
|
run("number modulo", function() {
|
|
if (10 % 3 != 1) fail("basic modulo failed")
|
|
if (10 % 5 != 0) fail("even modulo failed")
|
|
if (-10 % 3 != -1) fail("negative modulo failed")
|
|
})
|
|
|
|
run("number exponentiation", function() {
|
|
if (2 ** 3 != 8) fail("basic exponentiation failed")
|
|
if (5 ** 0 != 1) fail("zero exponent failed")
|
|
if (2 ** -1 != 0.5) fail("negative exponent failed")
|
|
})
|
|
|
|
// ============================================================================
|
|
// STRING OPERATORS
|
|
// ============================================================================
|
|
|
|
run("string plus string", function() {
|
|
var x = "hello" + " world"
|
|
if (x != "hello world") fail("string + string should work")
|
|
})
|
|
|
|
run("string concatenation empty", function() {
|
|
if ("" + "" != "") fail("empty string concatenation failed")
|
|
if ("hello" + "" != "hello") fail("concatenation with empty string failed")
|
|
if ("" + "world" != "world") fail("empty + string failed")
|
|
})
|
|
|
|
// ============================================================================
|
|
// TYPE MIXING SHOULD DISRUPT
|
|
// ============================================================================
|
|
|
|
run("number plus string disrupts", function() {
|
|
if (!should_disrupt(function() { var x = 1 + "hello" })) fail("number + string should disrupt")
|
|
})
|
|
|
|
run("string plus number disrupts", function() {
|
|
if (!should_disrupt(function() { var x = "hello" + 1 })) fail("string + number should disrupt")
|
|
})
|
|
|
|
run("object plus string disrupts", function() {
|
|
if (!should_disrupt(function() { var x = {} + "hello" })) fail("object + string should disrupt")
|
|
})
|
|
|
|
run("string plus object disrupts", function() {
|
|
if (!should_disrupt(function() { var x = "hello" + {} })) fail("string + object should disrupt")
|
|
})
|
|
|
|
run("array plus string disrupts", function() {
|
|
if (!should_disrupt(function() { var x = [] + "hello" })) fail("array + string should disrupt")
|
|
})
|
|
|
|
run("string plus array disrupts", function() {
|
|
if (!should_disrupt(function() { var x = "hello" + [] })) fail("string + array should disrupt")
|
|
})
|
|
|
|
run("boolean plus string disrupts", function() {
|
|
if (!should_disrupt(function() { var x = true + "hello" })) fail("boolean + string should disrupt")
|
|
})
|
|
|
|
run("string plus boolean disrupts", function() {
|
|
if (!should_disrupt(function() { var x = "hello" + false })) fail("string + boolean should disrupt")
|
|
})
|
|
|
|
run("null plus string disrupts", function() {
|
|
if (!should_disrupt(function() { var x = null + "hello" })) fail("null + string should disrupt")
|
|
})
|
|
|
|
run("string plus null disrupts", function() {
|
|
if (!should_disrupt(function() { var x = "hello" + null })) fail("string + null should disrupt")
|
|
})
|
|
|
|
// ============================================================================
|
|
// COMPARISON OPERATORS
|
|
// ============================================================================
|
|
|
|
run("equality numbers", function() {
|
|
if (!(5 == 5)) fail("number equality failed")
|
|
if (5 == 6) fail("number inequality detection failed")
|
|
if (!(0 == 0)) fail("zero equality failed")
|
|
if (!(-5 == -5)) fail("negative equality failed")
|
|
})
|
|
|
|
run("inequality numbers", function() {
|
|
if (5 != 5) fail("number inequality failed")
|
|
if (!(5 != 6)) fail("number difference detection failed")
|
|
})
|
|
|
|
run("less than", function() {
|
|
if (!(3 < 5)) fail("less than failed")
|
|
if (5 < 3) fail("not less than failed")
|
|
if (5 < 5) fail("equal not less than failed")
|
|
})
|
|
|
|
run("less than or equal", function() {
|
|
if (!(3 <= 5)) fail("less than or equal failed")
|
|
if (!(5 <= 5)) fail("equal in less than or equal failed")
|
|
if (6 <= 5) fail("not less than or equal failed")
|
|
})
|
|
|
|
run("greater than", function() {
|
|
if (!(5 > 3)) fail("greater than failed")
|
|
if (3 > 5) fail("not greater than failed")
|
|
if (5 > 5) fail("equal not greater than failed")
|
|
})
|
|
|
|
run("greater than or equal", function() {
|
|
if (!(5 >= 3)) fail("greater than or equal failed")
|
|
if (!(5 >= 5)) fail("equal in greater than or equal failed")
|
|
if (3 >= 5) fail("not greater than or equal failed")
|
|
})
|
|
|
|
run("string equality", function() {
|
|
if (!("hello" == "hello")) fail("string equality failed")
|
|
if ("hello" == "world") fail("string inequality detection failed")
|
|
if (!("" == "")) fail("empty string equality failed")
|
|
})
|
|
|
|
run("null equality", function() {
|
|
if (!(null == null)) fail("null equality failed")
|
|
if (null == 0) fail("null should not equal 0")
|
|
if (null == false) fail("null should not equal false")
|
|
if (null == "") fail("null should not equal empty string")
|
|
})
|
|
|
|
run("boolean equality", function() {
|
|
if (!(true == true)) fail("true equality failed")
|
|
if (!(false == false)) fail("false equality failed")
|
|
if (true == false) fail("boolean inequality detection failed")
|
|
})
|
|
|
|
// ============================================================================
|
|
// LOGICAL OPERATORS
|
|
// ============================================================================
|
|
|
|
run("logical and", function() {
|
|
if (!(true && true)) fail("true && true failed")
|
|
if (true && false) fail("true && false failed")
|
|
if (false && true) fail("false && true failed")
|
|
if (false && false) fail("false && false failed")
|
|
})
|
|
|
|
run("logical or", function() {
|
|
if (!(true || true)) fail("true || true failed")
|
|
if (!(true || false)) fail("true || false failed")
|
|
if (!(false || true)) fail("false || true failed")
|
|
if (false || false) fail("false || false failed")
|
|
})
|
|
|
|
run("logical not", function() {
|
|
if (!(!false)) fail("!false failed")
|
|
if (!true) fail("!true failed")
|
|
})
|
|
|
|
run("short circuit and", function() {
|
|
var called = false
|
|
var fn = function() { called = true; return true }
|
|
var result = false && fn()
|
|
if (called) fail("AND should short circuit")
|
|
})
|
|
|
|
run("short circuit or", function() {
|
|
var called = false
|
|
var fn = function() { called = true; return false }
|
|
var result = true || fn()
|
|
if (called) fail("OR should short circuit")
|
|
})
|
|
|
|
// ============================================================================
|
|
// BITWISE OPERATORS
|
|
// ============================================================================
|
|
|
|
run("bitwise and", function() {
|
|
if ((5 & 3) != 1) fail("bitwise AND failed")
|
|
if ((12 & 10) != 8) fail("bitwise AND failed")
|
|
})
|
|
|
|
run("bitwise or", function() {
|
|
if ((5 | 3) != 7) fail("bitwise OR failed")
|
|
if ((12 | 10) != 14) fail("bitwise OR failed")
|
|
})
|
|
|
|
run("bitwise xor", function() {
|
|
if ((5 ^ 3) != 6) fail("bitwise XOR failed")
|
|
if ((12 ^ 10) != 6) fail("bitwise XOR failed")
|
|
})
|
|
|
|
run("bitwise not", function() {
|
|
if (~5 != -6) fail("bitwise NOT failed")
|
|
if (~0 != -1) fail("bitwise NOT of zero failed")
|
|
})
|
|
|
|
run("left shift", function() {
|
|
if ((5 << 2) != 20) fail("left shift failed")
|
|
if ((1 << 3) != 8) fail("left shift failed")
|
|
})
|
|
|
|
run("right shift", function() {
|
|
if ((20 >> 2) != 5) fail("right shift failed")
|
|
if ((8 >> 3) != 1) fail("right shift failed")
|
|
})
|
|
|
|
run("unsigned right shift", function() {
|
|
if ((-1 >>> 1) != 2147483647) fail("unsigned right shift failed")
|
|
})
|
|
|
|
// ============================================================================
|
|
// VARIABLE DECLARATIONS AND SCOPING
|
|
// ============================================================================
|
|
|
|
run("var declaration", function() {
|
|
var x = 5
|
|
if (x != 5) fail("var declaration failed")
|
|
})
|
|
|
|
run("var reassignment", function() {
|
|
var x = 5
|
|
x = 10
|
|
if (x != 10) fail("var reassignment failed")
|
|
})
|
|
|
|
run("var block scope basic", function() {
|
|
var x = 1
|
|
{
|
|
var x = 2
|
|
if (x != 2) fail("var should be block scoped - inner scope failed")
|
|
}
|
|
if (x != 1) fail("var should be block scoped - outer scope affected")
|
|
})
|
|
|
|
run("var block scope if", function() {
|
|
var x = 1
|
|
if (true) {
|
|
var x = 2
|
|
if (x != 2) fail("var in if block should be scoped")
|
|
}
|
|
if (x != 1) fail("var in if block should not affect outer scope")
|
|
})
|
|
|
|
run("var block scope for", function() {
|
|
var x = 1
|
|
for (var i = 0; i < 1; i = i + 1) {
|
|
var x = 2
|
|
if (x != 2) fail("var in for block should be scoped")
|
|
}
|
|
if (x != 1) fail("var in for block should not affect outer scope")
|
|
})
|
|
|
|
run("var for loop iterator scope", function() {
|
|
var sum = 0
|
|
for (var i = 0; i < 3; i = i + 1) {
|
|
sum = sum + i
|
|
}
|
|
if (sum != 3) fail("for loop should work with block scoped var")
|
|
if (!should_disrupt(function() { var y = i })) fail("for loop iterator should not leak to outer scope")
|
|
})
|
|
|
|
run("var nested blocks", function() {
|
|
var x = 1
|
|
{
|
|
var x = 2
|
|
{
|
|
var x = 3
|
|
if (x != 3) fail("var in nested block level 2 failed")
|
|
}
|
|
if (x != 2) fail("var in nested block level 1 failed")
|
|
}
|
|
if (x != 1) fail("var in nested blocks outer scope failed")
|
|
})
|
|
|
|
run("var redeclaration different scope", function() {
|
|
var x = 1
|
|
{
|
|
var x = 2
|
|
}
|
|
if (x != 1) fail("var in different scope should not affect outer")
|
|
})
|
|
|
|
run("var while scope", function() {
|
|
var x = 1
|
|
var count = 0
|
|
while (count < 1) {
|
|
var x = 2
|
|
if (x != 2) fail("var in while should be block scoped")
|
|
count = count + 1
|
|
}
|
|
if (x != 1) fail("var in while should not affect outer scope")
|
|
})
|
|
|
|
run("var no initialization", function() {
|
|
{
|
|
var x
|
|
if (x != null) fail("uninitialized var should be null")
|
|
}
|
|
})
|
|
|
|
run("multiple var declaration", function() {
|
|
var a = 1, b = 2, c = 3
|
|
if (a != 1 || b != 2 || c != 3) fail("multiple var declaration failed")
|
|
})
|
|
|
|
run("function scope", function() {
|
|
var outer = "outer"
|
|
var fn = function() {
|
|
var inner = "inner"
|
|
return inner
|
|
}
|
|
if (fn() != "inner") fail("function scope failed")
|
|
})
|
|
|
|
// ============================================================================
|
|
// FUNCTION CALLS
|
|
// ============================================================================
|
|
|
|
run("function call no args", function() {
|
|
var fn = function() { return 42 }
|
|
if (fn() != 42) fail("function call with no args failed")
|
|
})
|
|
|
|
run("function call one arg", function() {
|
|
var fn = function(x) { return x * 2 }
|
|
if (fn(5) != 10) fail("function call with one arg failed")
|
|
})
|
|
|
|
run("function call multiple args", function() {
|
|
var fn = function(a, b, c) { return a + b + c }
|
|
if (fn(1, 2, 3) != 6) fail("function call with multiple args failed")
|
|
})
|
|
|
|
run("function call extra args", function() {
|
|
var fn = function(a, b) { return a + b }
|
|
if (fn(1, 2, 3, 4) != 3) fail("function call with extra args failed")
|
|
})
|
|
|
|
run("function call missing args", function() {
|
|
var fn = function(a, b, c) { return (a || 0) + (b || 0) + (c || 0) }
|
|
if (fn(1) != 1) fail("function call with missing args failed")
|
|
})
|
|
|
|
run("function return", function() {
|
|
var fn = function() { return 5 }
|
|
if (fn() != 5) fail("function return failed")
|
|
})
|
|
|
|
run("function return early", function() {
|
|
var fn = function() {
|
|
return 5
|
|
return 10
|
|
}
|
|
if (fn() != 5) fail("early return failed")
|
|
})
|
|
|
|
run("function no return", function() {
|
|
var fn = function() { var x = 5 }
|
|
if (fn() != null) fail("function with no return should return null")
|
|
})
|
|
|
|
run("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) fail("nested function calls failed")
|
|
})
|
|
|
|
run("function as value", function() {
|
|
var fn = function() { return 42 }
|
|
var fn2 = fn
|
|
if (fn2() != 42) fail("function as value failed")
|
|
})
|
|
|
|
run("function closure", function() {
|
|
var outer = function(x) {
|
|
return function(y) {
|
|
return x + y
|
|
}
|
|
}
|
|
var add5 = outer(5)
|
|
if (add5(3) != 8) fail("closure failed")
|
|
})
|
|
|
|
run("function closure mutation", function() {
|
|
var counter = function() {
|
|
var count = 0
|
|
return function() {
|
|
count = count + 1
|
|
return count
|
|
}
|
|
}
|
|
var c = counter()
|
|
if (c() != 1) fail("closure mutation failed (1)")
|
|
if (c() != 2) fail("closure mutation failed (2)")
|
|
if (c() != 3) fail("closure mutation failed (3)")
|
|
})
|
|
|
|
// ============================================================================
|
|
// RECURSION
|
|
// ============================================================================
|
|
|
|
run("simple recursion", function() {
|
|
var factorial = function(n) {
|
|
if (n <= 1) return 1
|
|
return n * factorial(n - 1)
|
|
}
|
|
if (factorial(5) != 120) fail("factorial recursion failed")
|
|
})
|
|
|
|
run("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)) fail("mutual recursion even failed")
|
|
if (isOdd(4)) fail("mutual recursion odd failed")
|
|
})
|
|
|
|
run("deep recursion", function() {
|
|
var sum = function(n) {
|
|
if (n == 0) return 0
|
|
return n + sum(n - 1)
|
|
}
|
|
if (sum(100) != 5050) fail("deep recursion failed")
|
|
})
|
|
|
|
// ============================================================================
|
|
// ARRAYS
|
|
// ============================================================================
|
|
|
|
run("array literal", function() {
|
|
var arr = [1, 2, 3]
|
|
if (arr[0] != 1 || arr[1] != 2 || arr[2] != 3) fail("array literal failed")
|
|
})
|
|
|
|
run("array length", function() {
|
|
var arr = [1, 2, 3, 4, 5]
|
|
if (length(arr) != 5) fail("array length failed")
|
|
})
|
|
|
|
run("array empty", function() {
|
|
var arr = []
|
|
if (length(arr) != 0) fail("empty array length failed")
|
|
})
|
|
|
|
run("array push", function() {
|
|
var arr = [1, 2]
|
|
arr[] = 3
|
|
if (length(arr) != 3) fail("array push length failed")
|
|
if (arr[2] != 3) fail("array push value failed")
|
|
})
|
|
|
|
run("array pop", function() {
|
|
var arr = [1, 2, 3]
|
|
var val = arr[]
|
|
if (val != 3) fail("array pop value failed")
|
|
if (length(arr) != 2) fail("array pop length failed")
|
|
})
|
|
|
|
run("array index access", function() {
|
|
var arr = [10, 20, 30]
|
|
if (arr[0] != 10) fail("array index 0 failed")
|
|
if (arr[1] != 20) fail("array index 1 failed")
|
|
if (arr[2] != 30) fail("array index 2 failed")
|
|
})
|
|
|
|
run("array index assignment", function() {
|
|
var arr = [1, 2, 3]
|
|
arr[1] = 99
|
|
if (arr[1] != 99) fail("array index assignment failed")
|
|
})
|
|
|
|
run("array mixed types", function() {
|
|
var arr = [1, "hello", true, null, {}]
|
|
if (arr[0] != 1) fail("mixed array number failed")
|
|
if (arr[1] != "hello") fail("mixed array string failed")
|
|
if (arr[2] != true) fail("mixed array boolean failed")
|
|
if (arr[3] != null) fail("mixed array null failed")
|
|
})
|
|
|
|
run("array nested", function() {
|
|
var arr = [[1, 2], [3, 4]]
|
|
if (arr[0][0] != 1) fail("nested array access failed")
|
|
if (arr[1][1] != 4) fail("nested array access failed")
|
|
})
|
|
|
|
// ============================================================================
|
|
// OBJECTS
|
|
// ============================================================================
|
|
|
|
run("object literal", function() {
|
|
var obj = {a: 1, b: 2}
|
|
if (obj.a != 1 || obj.b != 2) fail("object literal failed")
|
|
})
|
|
|
|
run("object property access", function() {
|
|
var obj = {name: "Alice", age: 30}
|
|
if (obj.name != "Alice") fail("object property access failed")
|
|
if (obj.age != 30) fail("object property access failed")
|
|
})
|
|
|
|
run("object bracket access", function() {
|
|
var obj = {x: 10, y: 20}
|
|
if (obj["x"] != 10) fail("object bracket access failed")
|
|
if (obj["y"] != 20) fail("object bracket access failed")
|
|
})
|
|
|
|
run("object property assignment", function() {
|
|
var obj = {a: 1}
|
|
obj.a = 99
|
|
if (obj.a != 99) fail("object property assignment failed")
|
|
})
|
|
|
|
run("object add property", function() {
|
|
var obj = {}
|
|
obj.newProp = 42
|
|
if (obj.newProp != 42) fail("object add property failed")
|
|
})
|
|
|
|
run("object computed property", function() {
|
|
var key = "dynamicKey"
|
|
var obj = {}
|
|
obj[key] = 123
|
|
if (obj.dynamicKey != 123) fail("object computed property failed")
|
|
})
|
|
|
|
run("object nested", function() {
|
|
var obj = {outer: {inner: 42}}
|
|
if (obj.outer.inner != 42) fail("nested object access failed")
|
|
})
|
|
|
|
run("object method", function() {
|
|
var obj = {
|
|
value: 10,
|
|
getValue: function() { return this.value }
|
|
}
|
|
if (obj.getValue() != 10) fail("object method failed")
|
|
})
|
|
|
|
run("object this binding", function() {
|
|
var obj = {
|
|
x: 5,
|
|
getX: function() { return this.x }
|
|
}
|
|
if (obj.getX() != 5) fail("this binding failed")
|
|
})
|
|
|
|
// ============================================================================
|
|
// CONTROL FLOW - IF/ELSE
|
|
// ============================================================================
|
|
|
|
run("if true", function() {
|
|
var x = 0
|
|
if (true) x = 1
|
|
if (x != 1) fail("if true failed")
|
|
})
|
|
|
|
run("if false", function() {
|
|
var x = 0
|
|
if (false) x = 1
|
|
if (x != 0) fail("if false failed")
|
|
})
|
|
|
|
run("if else true", function() {
|
|
var x = 0
|
|
if (true) x = 1
|
|
else x = 2
|
|
if (x != 1) fail("if else true failed")
|
|
})
|
|
|
|
run("if else false", function() {
|
|
var x = 0
|
|
if (false) x = 1
|
|
else x = 2
|
|
if (x != 2) fail("if else false failed")
|
|
})
|
|
|
|
run("if else if", function() {
|
|
var x = 0
|
|
if (false) x = 1
|
|
else if (true) x = 2
|
|
else x = 3
|
|
if (x != 2) fail("if else if failed")
|
|
})
|
|
|
|
run("nested if", function() {
|
|
var x = 0
|
|
if (true) {
|
|
if (true) {
|
|
x = 1
|
|
}
|
|
}
|
|
if (x != 1) fail("nested if failed")
|
|
})
|
|
|
|
// ============================================================================
|
|
// CONTROL FLOW - WHILE LOOPS
|
|
// ============================================================================
|
|
|
|
run("while loop", function() {
|
|
var i = 0
|
|
var sum = 0
|
|
while (i < 5) {
|
|
sum = sum + i
|
|
i = i + 1
|
|
}
|
|
if (sum != 10) fail("while loop failed")
|
|
})
|
|
|
|
run("while never executes", function() {
|
|
var x = 0
|
|
while (false) {
|
|
x = 1
|
|
}
|
|
if (x != 0) fail("while never executes failed")
|
|
})
|
|
|
|
run("while break", function() {
|
|
var i = 0
|
|
while (true) {
|
|
if (i >= 5) break
|
|
i = i + 1
|
|
}
|
|
if (i != 5) fail("while break failed")
|
|
})
|
|
|
|
run("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) fail("while continue failed")
|
|
})
|
|
|
|
// ============================================================================
|
|
// CONTROL FLOW - FOR LOOPS
|
|
// ============================================================================
|
|
|
|
run("for loop", function() {
|
|
var sum = 0
|
|
for (var i = 0; i < 5; i = i + 1) {
|
|
sum = sum + i
|
|
}
|
|
if (sum != 10) fail("for loop failed")
|
|
})
|
|
|
|
run("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) fail("for loop break failed")
|
|
})
|
|
|
|
run("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) fail("for loop continue failed")
|
|
})
|
|
|
|
run("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) fail("nested for loops failed")
|
|
})
|
|
|
|
// ============================================================================
|
|
// DISRUPTION HANDLING
|
|
// ============================================================================
|
|
|
|
run("disruption caught", function() {
|
|
if (!should_disrupt(function() { disrupt })) fail("disruption not caught")
|
|
})
|
|
|
|
run("no disruption path", function() {
|
|
var x = 0
|
|
var attempt = function() {
|
|
x = 1
|
|
} disruption {
|
|
x = 2
|
|
}
|
|
attempt()
|
|
if (x != 1) fail("non-disrupting code should not trigger disruption clause")
|
|
})
|
|
|
|
run("nested disruption", function() {
|
|
var x = 0
|
|
var outer = function() {
|
|
var inner = function() {
|
|
disrupt
|
|
} disruption {
|
|
x = 1
|
|
}
|
|
inner()
|
|
x = 2
|
|
} disruption {
|
|
x = 3
|
|
}
|
|
outer()
|
|
if (x != 2) fail("nested disruption failed")
|
|
})
|
|
|
|
run("disruption re-raise", function() {
|
|
var outer_caught = false
|
|
var outer = function() {
|
|
var inner = function() {
|
|
disrupt
|
|
} disruption {
|
|
disrupt
|
|
}
|
|
inner()
|
|
} disruption {
|
|
outer_caught = true
|
|
}
|
|
outer()
|
|
if (!outer_caught) fail("disruption re-raise failed")
|
|
})
|
|
|
|
// ============================================================================
|
|
// TYPE CHECKING WITH is_* FUNCTIONS
|
|
// ============================================================================
|
|
|
|
run("is_number", function() {
|
|
if (!is_number(42)) fail("is_number 42 failed")
|
|
if (!is_number(3.14)) fail("is_number float failed")
|
|
if (!is_number(-5)) fail("is_number negative failed")
|
|
if (is_number("42")) fail("is_number string should be false")
|
|
if (is_number(true)) fail("is_number boolean should be false")
|
|
if (is_number(null)) fail("is_number null should be false")
|
|
if (is_number({})) fail("is_number object should be false")
|
|
if (is_number([])) fail("is_number array should be false")
|
|
})
|
|
|
|
run("is_text", function() {
|
|
if (!is_text("hello")) fail("is_text string failed")
|
|
if (!is_text("")) fail("is_text empty string failed")
|
|
if (is_text(42)) fail("is_text number should be false")
|
|
if (is_text(true)) fail("is_text boolean should be false")
|
|
if (is_text(null)) fail("is_text null should be false")
|
|
if (is_text({})) fail("is_text object should be false")
|
|
if (is_text([])) fail("is_text array should be false")
|
|
})
|
|
|
|
run("is_logical", function() {
|
|
if (!is_logical(true)) fail("is_logical true failed")
|
|
if (!is_logical(false)) fail("is_logical false failed")
|
|
if (is_logical(1)) fail("is_logical number should be false")
|
|
if (is_logical("true")) fail("is_logical string should be false")
|
|
if (is_logical(null)) fail("is_logical null should be false")
|
|
if (is_logical({})) fail("is_logical object should be false")
|
|
if (is_logical([])) fail("is_logical array should be false")
|
|
})
|
|
|
|
run("is_object", function() {
|
|
if (!is_object({})) fail("is_object empty object failed")
|
|
if (!is_object({a: 1})) fail("is_object object failed")
|
|
if (is_object([])) fail("is_object array should be false")
|
|
if (is_object(null)) fail("is_object null should be false")
|
|
if (is_object(42)) fail("is_object number should be false")
|
|
if (is_object("hello")) fail("is_object string should be false")
|
|
if (is_object(true)) fail("is_object boolean should be false")
|
|
})
|
|
|
|
run("is_array", function() {
|
|
if (!is_array([])) fail("is_array empty array failed")
|
|
if (!is_array([1, 2, 3])) fail("is_array array failed")
|
|
if (is_array({})) fail("is_array object should be false")
|
|
if (is_array(null)) fail("is_array null should be false")
|
|
if (is_array(42)) fail("is_array number should be false")
|
|
if (is_array("hello")) fail("is_array string should be false")
|
|
if (is_array(true)) fail("is_array boolean should be false")
|
|
})
|
|
|
|
run("is_function", function() {
|
|
if (!is_function(function(){})) fail("is_function function failed")
|
|
var fn = function(x) { return x * 2 }
|
|
if (!is_function(fn)) fail("is_function named function failed")
|
|
if (is_function({})) fail("is_function object should be false")
|
|
if (is_function([])) fail("is_function array should be false")
|
|
if (is_function(null)) fail("is_function null should be false")
|
|
if (is_function(42)) fail("is_function number should be false")
|
|
if (is_function("hello")) fail("is_function string should be false")
|
|
if (is_function(true)) fail("is_function boolean should be false")
|
|
})
|
|
|
|
run("is_null", function() {
|
|
if (!is_null(null)) fail("is_null null failed")
|
|
if (is_null(0)) fail("is_null zero should be false")
|
|
if (is_null(false)) fail("is_null false should be false")
|
|
if (is_null("")) fail("is_null empty string should be false")
|
|
if (is_null({})) fail("is_null object should be false")
|
|
if (is_null([])) fail("is_null array should be false")
|
|
var x
|
|
if (!is_null(x)) fail("is_null undefined variable should be true")
|
|
})
|
|
|
|
run("is_blob", function() {
|
|
if (is_blob(null)) fail("is_blob null should be false")
|
|
if (is_blob(42)) fail("is_blob number should be false")
|
|
if (is_blob("hello")) fail("is_blob string should be false")
|
|
if (is_blob(true)) fail("is_blob boolean should be false")
|
|
if (is_blob({})) fail("is_blob object should be false")
|
|
if (is_blob([])) fail("is_blob array should be false")
|
|
if (is_blob(function(){})) fail("is_blob function should be false")
|
|
})
|
|
|
|
run("is_proto", function() {
|
|
var a = {}
|
|
var b = meme(a)
|
|
if (!is_proto(b, a)) fail("is_proto failed on meme")
|
|
})
|
|
|
|
// ============================================================================
|
|
// GLOBAL FUNCTIONS - LENGTH
|
|
// ============================================================================
|
|
|
|
run("length string", function() {
|
|
if (length("hello") != 5) fail("length string failed")
|
|
if (length("") != 0) fail("length empty string failed")
|
|
})
|
|
|
|
run("length array", function() {
|
|
if (length([1,2,3]) != 3) fail("length array failed")
|
|
if (length([]) != 0) fail("length empty array failed")
|
|
})
|
|
|
|
run("length null", function() {
|
|
if (length(null) != null) fail("length null failed")
|
|
})
|
|
|
|
run("length number", function() {
|
|
if (length(123) != null) fail("length number should return null")
|
|
})
|
|
|
|
// ============================================================================
|
|
// GLOBAL FUNCTIONS - REVERSE
|
|
// ============================================================================
|
|
|
|
run("reverse array", function() {
|
|
var arr = [1, 2, 3, 4, 5]
|
|
var rev = reverse(arr)
|
|
if (rev[0] != 5) fail("reverse array first failed")
|
|
if (rev[4] != 1) fail("reverse array last failed")
|
|
if (length(rev) != 5) fail("reverse array length failed")
|
|
})
|
|
|
|
run("reverse empty array", function() {
|
|
var rev = reverse([])
|
|
if (length(rev) != 0) fail("reverse empty array failed")
|
|
})
|
|
|
|
run("reverse preserves original", function() {
|
|
var arr = [1, 2, 3]
|
|
var rev = reverse(arr)
|
|
if (arr[0] != 1) fail("reverse should not mutate original")
|
|
})
|
|
|
|
// ============================================================================
|
|
// GLOBAL FUNCTIONS - MEME (PROTOTYPAL INHERITANCE)
|
|
// ============================================================================
|
|
|
|
run("meme basic", function() {
|
|
var parent = {x: 10}
|
|
var child = meme(parent)
|
|
if (child.x != 10) fail("meme basic inheritance failed")
|
|
})
|
|
|
|
run("meme with mixins", function() {
|
|
var parent = {x: 10}
|
|
var mixin = {y: 20}
|
|
var child = meme(parent, mixin)
|
|
if (child.x != 10) fail("meme with mixin parent prop failed")
|
|
if (child.y != 20) fail("meme with mixin own prop failed")
|
|
})
|
|
|
|
run("meme override", function() {
|
|
var parent = {x: 10}
|
|
var child = meme(parent)
|
|
child.x = 20
|
|
if (child.x != 20) fail("meme override failed")
|
|
if (parent.x != 10) fail("meme should not mutate parent")
|
|
})
|
|
|
|
run("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) fail("meme multiple mixins failed")
|
|
})
|
|
|
|
// ============================================================================
|
|
// GLOBAL FUNCTIONS - PROTO
|
|
// ============================================================================
|
|
|
|
run("proto basic", function() {
|
|
var parent = {x: 10}
|
|
var child = meme(parent)
|
|
var p = proto(child)
|
|
if (p != parent) fail("proto basic failed")
|
|
})
|
|
|
|
run("proto object literal", function() {
|
|
var obj = {x: 10}
|
|
var p = proto(obj)
|
|
if (p != null) fail("proto of object literal should be null")
|
|
})
|
|
|
|
run("proto non object", function() {
|
|
if (proto(42) != null) fail("proto of number should return null")
|
|
if (proto("hello") != null) fail("proto of string should return null")
|
|
})
|
|
|
|
// ============================================================================
|
|
// GLOBAL FUNCTIONS - STONE (FREEZE)
|
|
// ============================================================================
|
|
|
|
run("stone object", function() {
|
|
var obj = {x: 10}
|
|
stone(obj)
|
|
if (!should_disrupt(function() { obj.x = 20 })) fail("stone object should prevent modification")
|
|
})
|
|
|
|
run("is_stone frozen", function() {
|
|
var obj = {x: 10}
|
|
if (is_stone(obj)) fail("stone.p should return false before freezing")
|
|
stone(obj)
|
|
if (!is_stone(obj)) fail("stone.p should return true after freezing")
|
|
})
|
|
|
|
run("stone array", function() {
|
|
var arr = [1, 2, 3]
|
|
stone(arr)
|
|
if (!should_disrupt(function() { arr[0] = 99 })) fail("stone array should prevent modification")
|
|
})
|
|
|
|
// ============================================================================
|
|
// TERNARY OPERATOR
|
|
// ============================================================================
|
|
|
|
run("ternary true", function() {
|
|
var x = true ? 1 : 2
|
|
if (x != 1) fail("ternary true failed")
|
|
})
|
|
|
|
run("ternary false", function() {
|
|
var x = false ? 1 : 2
|
|
if (x != 2) fail("ternary false failed")
|
|
})
|
|
|
|
run("ternary nested", function() {
|
|
var x = true ? (false ? 1 : 2) : 3
|
|
if (x != 2) fail("ternary nested failed")
|
|
})
|
|
|
|
run("ternary with expressions", function() {
|
|
var a = 5
|
|
var b = 10
|
|
var max = (a > b) ? a : b
|
|
if (max != 10) fail("ternary with expressions failed")
|
|
})
|
|
|
|
// ============================================================================
|
|
// UNARY OPERATORS
|
|
// ============================================================================
|
|
|
|
run("unary plus", function() {
|
|
if (+5 != 5) fail("unary plus positive failed")
|
|
if (+-5 != -5) fail("unary plus negative failed")
|
|
})
|
|
|
|
run("unary minus", function() {
|
|
if (-5 != -5) fail("unary minus failed")
|
|
if (-(-5) != 5) fail("double unary minus failed")
|
|
})
|
|
|
|
run("increment postfix", function() {
|
|
var x = 5
|
|
var y = x++
|
|
if (y != 5) fail("postfix increment return value failed")
|
|
if (x != 6) fail("postfix increment side effect failed")
|
|
})
|
|
|
|
run("increment prefix", function() {
|
|
var x = 5
|
|
var y = ++x
|
|
if (y != 6) fail("prefix increment return value failed")
|
|
if (x != 6) fail("prefix increment side effect failed")
|
|
})
|
|
|
|
run("decrement postfix", function() {
|
|
var x = 5
|
|
var y = x--
|
|
if (y != 5) fail("postfix decrement return value failed")
|
|
if (x != 4) fail("postfix decrement side effect failed")
|
|
})
|
|
|
|
run("decrement prefix", function() {
|
|
var x = 5
|
|
var y = --x
|
|
if (y != 4) fail("prefix decrement return value failed")
|
|
if (x != 4) fail("prefix decrement side effect failed")
|
|
})
|
|
|
|
// ============================================================================
|
|
// COMPOUND ASSIGNMENT OPERATORS
|
|
// ============================================================================
|
|
|
|
run("plus equals", function() {
|
|
var x = 5
|
|
x += 3
|
|
if (x != 8) fail("plus equals failed")
|
|
})
|
|
|
|
run("minus equals", function() {
|
|
var x = 10
|
|
x -= 3
|
|
if (x != 7) fail("minus equals failed")
|
|
})
|
|
|
|
run("times equals", function() {
|
|
var x = 4
|
|
x *= 3
|
|
if (x != 12) fail("times equals failed")
|
|
})
|
|
|
|
run("divide equals", function() {
|
|
var x = 12
|
|
x /= 3
|
|
if (x != 4) fail("divide equals failed")
|
|
})
|
|
|
|
run("modulo equals", function() {
|
|
var x = 10
|
|
x %= 3
|
|
if (x != 1) fail("modulo equals failed")
|
|
})
|
|
|
|
// ============================================================================
|
|
// EDGE CASES AND SPECIAL VALUES
|
|
// ============================================================================
|
|
|
|
run("division by zero is null", function() {
|
|
var inf = 1 / 0
|
|
if (inf != null) fail("division by zero should be null")
|
|
var ninf = -1 / 0
|
|
if (ninf != null) fail("negative division by zero should be null")
|
|
})
|
|
|
|
run("zero div zero is null", function() {
|
|
var nan = 0 / 0
|
|
if (nan != null) fail("0/0 should be null")
|
|
})
|
|
|
|
run("max safe integer", function() {
|
|
var max = 9007199254740991
|
|
if (max + 1 - 1 != max) fail("max safe integer precision lost")
|
|
})
|
|
|
|
run("min safe integer", function() {
|
|
var min = -9007199254740991
|
|
if (min - 1 + 1 != min) fail("min safe integer precision lost")
|
|
})
|
|
|
|
run("empty string falsy", function() {
|
|
if ("") fail("empty string should be falsy")
|
|
})
|
|
|
|
run("zero falsy", function() {
|
|
if (0) fail("zero should be falsy")
|
|
})
|
|
|
|
run("null falsy", function() {
|
|
if (null) fail("null should be falsy")
|
|
})
|
|
|
|
run("false falsy", function() {
|
|
if (false) fail("false should be falsy")
|
|
})
|
|
|
|
run("nonempty string truthy", function() {
|
|
if (!"hello") fail("non-empty string should be truthy")
|
|
})
|
|
|
|
run("nonzero number truthy", function() {
|
|
if (!42) fail("non-zero number should be truthy")
|
|
})
|
|
|
|
run("object truthy", function() {
|
|
if (!{}) fail("empty object should be truthy")
|
|
})
|
|
|
|
run("array truthy", function() {
|
|
if (![]) fail("empty array should be truthy")
|
|
})
|
|
|
|
// ============================================================================
|
|
// OPERATOR PRECEDENCE
|
|
// ============================================================================
|
|
|
|
run("precedence multiply add", function() {
|
|
if (2 + 3 * 4 != 14) fail("multiply before add precedence failed")
|
|
})
|
|
|
|
run("precedence parentheses", function() {
|
|
if ((2 + 3) * 4 != 20) fail("parentheses precedence failed")
|
|
})
|
|
|
|
run("precedence comparison logical", function() {
|
|
if (!(1 < 2 && 3 < 4)) fail("comparison before logical precedence failed")
|
|
})
|
|
|
|
run("precedence equality logical", function() {
|
|
if (!(1 == 1 || 2 == 3)) fail("equality before logical precedence failed")
|
|
})
|
|
|
|
run("precedence unary multiplication", function() {
|
|
if (-2 * 3 != -6) fail("unary before multiplication precedence failed")
|
|
})
|
|
|
|
// ============================================================================
|
|
// COMMA OPERATOR
|
|
// ============================================================================
|
|
|
|
run("comma operator", function() {
|
|
var x = (1, 2, 3)
|
|
if (x != 3) fail("comma operator failed")
|
|
})
|
|
|
|
run("comma operator with side effects", function() {
|
|
var a = 0
|
|
var x = (a = 1, a = 2, a + 1)
|
|
if (x != 3) fail("comma operator with side effects failed")
|
|
if (a != 2) fail("comma operator side effects failed")
|
|
})
|
|
|
|
// ============================================================================
|
|
// VARIABLE SHADOWING
|
|
// ============================================================================
|
|
|
|
run("variable shadowing function", function() {
|
|
var x = 10
|
|
var fn = function() {
|
|
var x = 20
|
|
return x
|
|
}
|
|
if (fn() != 20) fail("function shadowing failed")
|
|
if (x != 10) fail("outer variable after shadowing failed")
|
|
})
|
|
|
|
run("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) fail("nested shadowing failed")
|
|
})
|
|
|
|
// ============================================================================
|
|
// FUNCTION ARITY
|
|
// ============================================================================
|
|
|
|
run("function length property", function() {
|
|
var fn0 = function() {}
|
|
var fn1 = function(a) {}
|
|
var fn2 = function(a, b) {}
|
|
if (length(fn0) != 0) fail("function length 0 failed")
|
|
if (length(fn1) != 1) fail("function length 1 failed")
|
|
if (length(fn2) != 2) fail("function length 2 failed")
|
|
})
|
|
|
|
// ============================================================================
|
|
// NULL AND UNDEFINED BEHAVIOR
|
|
// ============================================================================
|
|
|
|
run("undefined variable is null", function() {
|
|
var x
|
|
if (x != null) fail("undefined variable should be null")
|
|
})
|
|
|
|
// ============================================================================
|
|
// NUMBERS - SPECIAL OPERATIONS
|
|
// ============================================================================
|
|
|
|
run("number plus empty string disrupts", function() {
|
|
if (!should_disrupt(function() { var n = 42; var result = n + "" })) fail("number + string should disrupt")
|
|
})
|
|
|
|
run("number division by zero", function() {
|
|
var result = 1 / 0
|
|
if (result != null) fail("division by zero should give null")
|
|
})
|
|
|
|
run("number negative division by zero", function() {
|
|
var result = -1 / 0
|
|
if (result != null) fail("negative division by zero should give null")
|
|
})
|
|
|
|
run("zero division by zero", function() {
|
|
var result = 0 / 0
|
|
if (result != null) fail("0/0 should give null")
|
|
})
|
|
|
|
run("negative zero normalized", function() {
|
|
var nz = -0
|
|
if (nz != 0) fail("-0 should equal 0")
|
|
var mul_nz = 0 * -1
|
|
if (mul_nz != 0) fail("0 * -1 should be 0")
|
|
var neg_zero = -(0)
|
|
if (neg_zero != 0) fail("-(0) should be 0")
|
|
})
|
|
|
|
run("overflow is null", function() {
|
|
var result = 1e38 * 1e38
|
|
if (result != null) fail("overflow should give null")
|
|
})
|
|
|
|
run("modulo by zero is null", function() {
|
|
var result = 5 % 0
|
|
if (result != null) fail("modulo by zero should give null")
|
|
})
|
|
|
|
// ============================================================================
|
|
// OBJECT PROPERTY EXISTENCE
|
|
// ============================================================================
|
|
|
|
run("in operator", function() {
|
|
var obj = {a: 1, b: 2}
|
|
if (!("a" in obj)) fail("in operator for existing property failed")
|
|
if ("c" in obj) fail("in operator for non-existing property failed")
|
|
})
|
|
|
|
run("in operator prototype", function() {
|
|
var parent = {x: 10}
|
|
var child = meme(parent)
|
|
if (!("x" in child)) fail("in operator should find inherited property")
|
|
})
|
|
|
|
// ============================================================================
|
|
// GLOBAL FUNCTIONS - LOGICAL
|
|
// ============================================================================
|
|
|
|
run("logical function numbers", function() {
|
|
if (logical(0) != false) fail("logical(0) should be false")
|
|
if (logical(1) != true) fail("logical(1) should be true")
|
|
})
|
|
|
|
run("logical function strings", function() {
|
|
if (logical("false") != false) fail("logical('false') should be false")
|
|
if (logical("true") != true) fail("logical('true') should be true")
|
|
})
|
|
|
|
run("logical function booleans", function() {
|
|
if (logical(false) != false) fail("logical(false) should be false")
|
|
if (logical(true) != true) fail("logical(true) should be true")
|
|
})
|
|
|
|
run("logical function null", function() {
|
|
if (logical(null) != false) fail("logical(null) should be false")
|
|
})
|
|
|
|
run("logical function invalid", function() {
|
|
if (logical("invalid") != null) fail("logical(invalid) should return null")
|
|
if (logical(42) != null) fail("logical(42) should return null")
|
|
})
|
|
|
|
// ============================================================================
|
|
// ARRAY METHODS
|
|
// ============================================================================
|
|
|
|
run("array concat", function() {
|
|
var arr1 = [1, 2]
|
|
var arr2 = [3, 4]
|
|
var combined = array(arr1, arr2)
|
|
if (length(combined) != 4) fail("array concat length failed")
|
|
if (combined[2] != 3) fail("array concat values failed")
|
|
})
|
|
|
|
run("array join", function() {
|
|
var arr = ["a", "b", "c"]
|
|
var str = text(arr, ",")
|
|
if (str != "a,b,c") fail("array join with text() failed")
|
|
})
|
|
|
|
run("text array join numbers disrupt", function() {
|
|
if (!should_disrupt(function() { text([1, 2, 3], ",") })) fail("text([numbers], sep) should disrupt")
|
|
})
|
|
|
|
run("text array join numbers explicit", function() {
|
|
var arr = array([1, 2, 3], x => text(x))
|
|
if (text(arr, ",") != "1,2,3") fail("explicit numeric join failed")
|
|
})
|
|
|
|
// ============================================================================
|
|
// STRING METHODS
|
|
// ============================================================================
|
|
|
|
run("string substring", function() {
|
|
var str = "hello"
|
|
if (text(str, 1, 4) != "ell") fail("string substring failed")
|
|
})
|
|
|
|
run("string substring first", function() {
|
|
var str = "hello"
|
|
if (text(str, 1) != "ello") fail("string substring first failed")
|
|
})
|
|
|
|
run("string substring to neg", function() {
|
|
var str = "hello"
|
|
if (text(str, 1, -2) != "el") fail("string substring to negative failed")
|
|
})
|
|
|
|
run("string slice", function() {
|
|
var str = "hello"
|
|
if (text(str, 1, 4) != "ell") fail("string slice failed")
|
|
if (text(str, -2) != "lo") fail("string slice negative failed")
|
|
})
|
|
|
|
run("string indexOf", function() {
|
|
var str = "hello world"
|
|
if (search(str, "world") != 6) fail("string search failed")
|
|
if (search(str, "xyz") != null) fail("string search not found failed")
|
|
})
|
|
|
|
run("string toLowerCase", function() {
|
|
var str = "HELLO"
|
|
if (lower(str) != "hello") fail("string toLowerCase failed")
|
|
})
|
|
|
|
run("string toUpperCase", function() {
|
|
var str = "hello"
|
|
if (upper(str) != "HELLO") fail("string toUpperCase failed")
|
|
})
|
|
|
|
run("string split", function() {
|
|
var str = "a,b,c"
|
|
var parts = array(str, ",")
|
|
if (length(parts) != 3) fail("string split length failed")
|
|
if (parts[1] != "b") fail("string split values failed")
|
|
})
|
|
|
|
run("null access", function() {
|
|
var val = {}
|
|
var nn = val.a
|
|
if (nn != null) fail("val.a should return null")
|
|
})
|
|
|
|
// ============================================================================
|
|
// OBJECT-AS-KEY (Private Property Access)
|
|
// ============================================================================
|
|
|
|
run("object key basic", function() {
|
|
var k1 = {}
|
|
var k2 = {}
|
|
var o = {}
|
|
o[k1] = 123
|
|
o[k2] = 456
|
|
if (o[k1] != 123) fail("object key k1 failed")
|
|
if (o[k2] != 456) fail("object key k2 failed")
|
|
})
|
|
|
|
run("object key new object different key", function() {
|
|
var k1 = {}
|
|
var o = {}
|
|
o[k1] = 123
|
|
if (o[{}] != null) fail("new object should be different key")
|
|
})
|
|
|
|
run("object key in operator", function() {
|
|
var k1 = {}
|
|
var o = {}
|
|
o[k1] = 123
|
|
if (!(k1 in o)) fail("in operator should find object key")
|
|
})
|
|
|
|
run("object key delete", function() {
|
|
var k1 = {}
|
|
var o = {}
|
|
o[k1] = 123
|
|
delete o[k1]
|
|
if ((k1 in o)) fail("delete should remove object key")
|
|
})
|
|
|
|
run("object key no string collision", function() {
|
|
var a = {}
|
|
var b = {}
|
|
var o = {}
|
|
o[a] = 1
|
|
o[b] = 2
|
|
if (o[a] != 1) fail("object key a should be 1")
|
|
if (o[b] != 2) fail("object key b should be 2")
|
|
})
|
|
|
|
run("object key same object same key", function() {
|
|
var k = {}
|
|
var o = {}
|
|
o[k] = 100
|
|
o[k] = 200
|
|
if (o[k] != 200) fail("same object should be same key")
|
|
})
|
|
|
|
run("object key computed property", function() {
|
|
var k = {}
|
|
var o = {}
|
|
o[k] = function() { return 42 }
|
|
if (o[k]() != 42) fail("object key with function value failed")
|
|
})
|
|
|
|
run("object key multiple objects multiple keys", function() {
|
|
var k1 = {}
|
|
var k2 = {}
|
|
var k3 = {}
|
|
var o = {}
|
|
o[k1] = "one"
|
|
o[k2] = "two"
|
|
o[k3] = "three"
|
|
if (o[k1] != "one") fail("multiple keys k1 failed")
|
|
if (o[k2] != "two") fail("multiple keys k2 failed")
|
|
if (o[k3] != "three") fail("multiple keys k3 failed")
|
|
})
|
|
|
|
run("object key with string keys", function() {
|
|
var k = {}
|
|
var o = {name: "test"}
|
|
o[k] = "private"
|
|
if (o.name != "test") fail("string key should still work")
|
|
if (o[k] != "private") fail("object key should work with string keys")
|
|
})
|
|
|
|
run("object key overwrite", function() {
|
|
var k = {}
|
|
var o = {}
|
|
o[k] = 1
|
|
o[k] = 2
|
|
o[k] = 3
|
|
if (o[k] != 3) fail("object key overwrite failed")
|
|
})
|
|
|
|
run("object key nested objects", function() {
|
|
var k1 = {}
|
|
var k2 = {}
|
|
var inner = {}
|
|
inner[k2] = "nested"
|
|
var outer = {}
|
|
outer[k1] = inner
|
|
if (outer[k1][k2] != "nested") fail("nested object keys failed")
|
|
})
|
|
|
|
run("array for", function() {
|
|
var a = [1,2,3]
|
|
arrfor(a, (x,i) => {
|
|
if (x-1 != i) fail("array for failed")
|
|
})
|
|
})
|
|
|
|
// ============================================================================
|
|
// INVALID KEY TYPES DISRUPT ON SET
|
|
// ============================================================================
|
|
|
|
run("array string key disrupts", function() {
|
|
if (!should_disrupt(function() { var a = []; a["a"] = 1 })) fail("array should not use string as key")
|
|
})
|
|
|
|
run("array object key disrupts", function() {
|
|
if (!should_disrupt(function() { var a = []; var b = {}; a[b] = 1 })) fail("array should not use object as key")
|
|
})
|
|
|
|
run("array boolean key disrupts", function() {
|
|
if (!should_disrupt(function() { var a = []; a[true] = 1 })) fail("array should not use boolean as key")
|
|
})
|
|
|
|
run("array null key disrupts", function() {
|
|
if (!should_disrupt(function() { var a = []; a[null] = 1 })) fail("array should not use null as key")
|
|
})
|
|
|
|
run("array array key disrupts", function() {
|
|
if (!should_disrupt(function() { var a = []; var c = []; a[c] = 1 })) fail("array should not use array as key")
|
|
})
|
|
|
|
run("obj number key disrupts", function() {
|
|
if (!should_disrupt(function() { var a = {}; a[1] = 1 })) fail("object should not use number as key")
|
|
})
|
|
|
|
run("obj array key disrupts", function() {
|
|
if (!should_disrupt(function() { var a = {}; var c = []; a[c] = 1 })) fail("object should not use array as key")
|
|
})
|
|
|
|
run("obj boolean key disrupts", function() {
|
|
if (!should_disrupt(function() { var a = {}; a[true] = 1 })) fail("object should not use boolean as key")
|
|
})
|
|
|
|
run("obj null key disrupts", function() {
|
|
if (!should_disrupt(function() { var a = {}; a[null] = 1 })) fail("object should not use null as key")
|
|
})
|
|
|
|
// ============================================================================
|
|
// RETRIEVAL WITH INVALID KEY RETURNS NULL (not disrupt)
|
|
// ============================================================================
|
|
|
|
run("array get string key returns null", function() {
|
|
var a = [1, 2, 3]
|
|
if (a["x"] != null) fail("array get with string key should return null")
|
|
})
|
|
|
|
run("array get negative index returns null", function() {
|
|
var a = [1, 2, 3]
|
|
if (a[-1] != null) fail("array get with negative index should return null")
|
|
})
|
|
|
|
run("array get object key returns null", function() {
|
|
var a = [1, 2, 3]
|
|
var k = {}
|
|
if (a[k] != null) fail("array get with object key should return null")
|
|
})
|
|
|
|
run("array get array key returns null", function() {
|
|
var a = [1, 2, 3]
|
|
if (a[[1, 2]] != null) fail("array get with array key should return null")
|
|
})
|
|
|
|
run("array get boolean key returns null", function() {
|
|
var a = [1, 2, 3]
|
|
if (a[true] != null) fail("array get with boolean key should return null")
|
|
})
|
|
|
|
run("array get null key returns null", function() {
|
|
var a = [1, 2, 3]
|
|
if (a[null] != null) fail("array get with null key should return null")
|
|
})
|
|
|
|
run("obj get number key returns null", function() {
|
|
var o = {a: 1}
|
|
if (o[5] != null) fail("object get with number key should return null")
|
|
})
|
|
|
|
run("obj get array key returns null", function() {
|
|
var o = {a: 1}
|
|
if (o[[1, 2]] != null) fail("object get with array key should return null")
|
|
})
|
|
|
|
run("obj get boolean key returns null", function() {
|
|
var o = {a: 1}
|
|
if (o[true] != null) fail("object get with boolean key should return null")
|
|
})
|
|
|
|
run("obj get null key returns null", function() {
|
|
var o = {a: 1}
|
|
if (o[null] != null) fail("object get with null key should return null")
|
|
})
|
|
|
|
// ============================================================================
|
|
// FUNCTION AS VALUE - functions should not have properties
|
|
// ============================================================================
|
|
|
|
run("function property get arity", function() {
|
|
var fn = function(a, b) { return a + b }
|
|
var arity = length(fn)
|
|
if (arity != 2) fail("length of function should return its arity")
|
|
})
|
|
|
|
run("function property set disrupts", function() {
|
|
if (!should_disrupt(function() { var fn = function() {}; fn.foo = 123 })) fail("setting property on function should disrupt")
|
|
})
|
|
|
|
run("function bracket access disrupts", function() {
|
|
if (!should_disrupt(function() { var fn = function() {}; var x = fn["length"]() })) fail("bracket access on function should disrupt")
|
|
})
|
|
|
|
run("length returns function arity", function() {
|
|
var fn0 = function() { return 1 }
|
|
var fn1 = function(a) { return a }
|
|
var fn2 = function(a, b) { return a + b }
|
|
var fn3 = function(a, b, c) { return a + b + c }
|
|
if (length(fn0) != 0) fail("length(fn0) should be 0")
|
|
if (length(fn1) != 1) fail("length(fn1) should be 1")
|
|
if (length(fn2) != 2) fail("length(fn2) should be 2")
|
|
if (length(fn3) != 3) fail("length(fn3) should be 3")
|
|
})
|
|
|
|
run("call invokes function", function() {
|
|
var fn = function(a, b) { return a + b }
|
|
var result = call(fn, null, [3, 4])
|
|
if (result != 7) fail("call(fn, null, [3, 4]) should return 7")
|
|
})
|
|
|
|
run("call with this binding", function() {
|
|
var obj = { value: 10 }
|
|
var fn = function(x) { return this.value + x }
|
|
var result = call(fn, obj, [5])
|
|
if (result != 15) fail("call(fn, obj, [5]) should return 15")
|
|
})
|
|
|
|
run("call no args", function() {
|
|
var fn = function() { return 42 }
|
|
var result = call(fn, null)
|
|
if (result != 42) fail("call(fn, null) should return 42")
|
|
})
|
|
|
|
run("builtin function properties still work", function() {
|
|
var min_result = min(5, 3)
|
|
if (min_result != 3) fail("min should work")
|
|
})
|
|
|
|
// ============================================================================
|
|
// FUNCTION PROXY - Method call sugar for bytecode functions
|
|
// ============================================================================
|
|
|
|
run("function proxy basic", function() {
|
|
var proxy = function(name, args) {
|
|
return `called:${name}:${length(args)}`
|
|
}
|
|
var result = proxy.foo()
|
|
if (result != "called:foo:0") fail("basic proxy call failed")
|
|
})
|
|
|
|
run("function proxy with one arg", function() {
|
|
var proxy = function(name, args) {
|
|
return `${name}-${args[0]}`
|
|
}
|
|
var result = proxy.test("value")
|
|
if (result != "test-value") fail("proxy with one arg failed")
|
|
})
|
|
|
|
run("function proxy with multiple args", function() {
|
|
var proxy = function(name, args) {
|
|
var sum = 0
|
|
for (var i = 0; i < length(args); i++) {
|
|
sum = sum + args[i]
|
|
}
|
|
return `${name}:${sum}`
|
|
}
|
|
var result = proxy.add(1, 2, 3, 4)
|
|
if (result != "add:10") fail("proxy with multiple args failed")
|
|
})
|
|
|
|
run("function proxy bracket notation", function() {
|
|
var proxy = function(name, args) {
|
|
return `bracket:${name}`
|
|
}
|
|
var result = proxy["myMethod"]()
|
|
if (result != "bracket:myMethod") fail("proxy bracket notation failed")
|
|
})
|
|
|
|
run("function proxy dynamic method name", function() {
|
|
var proxy = function(name, args) {
|
|
return name
|
|
}
|
|
var methodName = "dynamic"
|
|
var result = proxy[methodName]()
|
|
if (result != "dynamic") fail("proxy dynamic method name failed")
|
|
})
|
|
|
|
run("function proxy dispatch to record", function() {
|
|
var my_record = {
|
|
greet: function(name) {
|
|
return `Hello, ${name}`
|
|
},
|
|
add: function(a, b) {
|
|
return a + b
|
|
}
|
|
}
|
|
var proxy = function(name, args) {
|
|
if (is_function(my_record[name])) {
|
|
return apply(my_record[name], args)
|
|
}
|
|
disrupt
|
|
}
|
|
if (proxy.greet("World") != "Hello, World") fail("proxy dispatch greet failed")
|
|
if (proxy.add(3, 4) != 7) fail("proxy dispatch add failed")
|
|
})
|
|
|
|
run("function proxy unknown method disrupts", function() {
|
|
var proxy = function(name, args) {
|
|
disrupt
|
|
}
|
|
if (!should_disrupt(function() { proxy.nonexistent() })) fail("proxy should disrupt for unknown method")
|
|
})
|
|
|
|
run("function proxy is function", function() {
|
|
var proxy = function(name, args) {
|
|
return name
|
|
}
|
|
if (!is_function(proxy)) fail("proxy should be a function")
|
|
})
|
|
|
|
run("function proxy length is 2", function() {
|
|
var proxy = function(name, args) {
|
|
return name
|
|
}
|
|
if (length(proxy) != 2) fail("proxy function should have length 2")
|
|
})
|
|
|
|
run("function proxy property read disrupts", function() {
|
|
if (!should_disrupt(function() { var fn = function() { return 1 }; var x = fn.someProp })) fail("reading property from non-proxy function should disrupt")
|
|
})
|
|
|
|
run("function proxy nested calls", function() {
|
|
var outer = function(name, args) {
|
|
if (name == "inner") {
|
|
return args[0].double(5)
|
|
}
|
|
return "outer:" + name
|
|
}
|
|
var inner = function(name, args) {
|
|
if (name == "double") {
|
|
return args[0] * 2
|
|
}
|
|
return "inner:" + name
|
|
}
|
|
var result = outer.inner(inner)
|
|
if (result != 10) fail("nested proxy calls failed")
|
|
})
|
|
|
|
run("function proxy returns null", function() {
|
|
var proxy = function(name, args) {
|
|
return null
|
|
}
|
|
var result = proxy.anything()
|
|
if (result != null) fail("proxy returning null failed")
|
|
})
|
|
|
|
run("function proxy returns object", function() {
|
|
var proxy = function(name, args) {
|
|
return {method: name, argCount: length(args)}
|
|
}
|
|
var result = proxy.test(1, 2, 3)
|
|
if (result.method != "test") fail("proxy returning object method failed")
|
|
if (result.argCount != 3) fail("proxy returning object argCount failed")
|
|
})
|
|
|
|
run("function proxy returns function", function() {
|
|
var proxy = function(name, args) {
|
|
return function() { return name }
|
|
}
|
|
var result = proxy.getFn()
|
|
if (result() != "getFn") fail("proxy returning function failed")
|
|
})
|
|
|
|
run("function proxy args array is real array", function() {
|
|
var proxy = function(name, args) {
|
|
if (!is_array(args)) disrupt
|
|
args[] = 4
|
|
return length(args)
|
|
}
|
|
var result = proxy.test(1, 2, 3)
|
|
if (result != 4) fail("proxy args should be modifiable array")
|
|
})
|
|
|
|
run("function proxy no this binding", function() {
|
|
var proxy = function(name, args) {
|
|
return this
|
|
}
|
|
var result = proxy.test()
|
|
if (result != null) fail("proxy should have null this")
|
|
})
|
|
|
|
run("function proxy integer bracket key disrupts", function() {
|
|
var proxy = function(name, args) {
|
|
return `key:${name}`
|
|
}
|
|
if (!should_disrupt(function() { var result = proxy[42]() })) fail("proxy with integer bracket key should disrupt")
|
|
})
|
|
|
|
// ============================================================================
|
|
// REDUCE FUNCTION
|
|
// ============================================================================
|
|
|
|
run("reduce sum", function() {
|
|
var arr = [1, 2, 3, 4, 5]
|
|
var result = reduce(arr, (a, b) => a + b)
|
|
if (result != 15) fail("reduce sum failed")
|
|
})
|
|
|
|
run("reduce product", function() {
|
|
var arr = [1, 2, 3, 4, 5]
|
|
var result = reduce(arr, (a, b) => a * b)
|
|
if (result != 120) fail("reduce product failed")
|
|
})
|
|
|
|
run("reduce with initial", function() {
|
|
var arr = [1, 2, 3]
|
|
var result = reduce(arr, (a, b) => a + b, 10)
|
|
if (result != 16) fail("reduce with initial failed")
|
|
})
|
|
|
|
run("reduce with initial zero", function() {
|
|
var arr = [1, 2, 3]
|
|
var result = reduce(arr, (a, b) => a + b, 0)
|
|
if (result != 6) fail("reduce with initial zero failed")
|
|
})
|
|
|
|
run("reduce empty array no initial", function() {
|
|
var arr = []
|
|
var result = reduce(arr, (a, b) => a + b)
|
|
if (result != null) fail("reduce empty array without initial should return null")
|
|
})
|
|
|
|
run("reduce empty array with initial", function() {
|
|
var arr = []
|
|
var result = reduce(arr, (a, b) => a + b, 42)
|
|
if (result != 42) fail("reduce empty array with initial should return initial")
|
|
})
|
|
|
|
run("reduce single element no initial", function() {
|
|
var arr = [42]
|
|
var result = reduce(arr, (a, b) => a + b)
|
|
if (result != 42) fail("reduce single element without initial failed")
|
|
})
|
|
|
|
run("reduce single element with initial", function() {
|
|
var arr = [5]
|
|
var result = reduce(arr, (a, b) => a + b, 10)
|
|
if (result != 15) fail("reduce single element with initial failed")
|
|
})
|
|
|
|
run("reduce reverse", function() {
|
|
var arr = [1, 2, 3, 4]
|
|
var result = reduce(arr, (a, b) => a - b, 0, true)
|
|
if (result != -10) fail("reduce reverse failed")
|
|
})
|
|
|
|
run("reduce string concat", function() {
|
|
var arr = ["a", "b", "c"]
|
|
var result = reduce(arr, (a, b) => a + b)
|
|
if (result != "abc") fail("reduce string concat failed")
|
|
})
|
|
|
|
// ============================================================================
|
|
// SORT FUNCTION
|
|
// ============================================================================
|
|
|
|
run("sort numbers", function() {
|
|
var arr = [3, 1, 4, 1, 5, 9, 2, 6]
|
|
var sorted = sort(arr)
|
|
if (sorted[0] != 1 || sorted[1] != 1 || sorted[2] != 2) fail("sort numbers failed")
|
|
if (sorted[7] != 9) fail("sort numbers last element failed")
|
|
})
|
|
|
|
run("sort strings", function() {
|
|
var arr = ["banana", "apple", "cherry"]
|
|
var sorted = sort(arr)
|
|
if (sorted[0] != "apple") fail("sort strings failed")
|
|
if (sorted[2] != "cherry") fail("sort strings last failed")
|
|
})
|
|
|
|
run("sort preserves original", function() {
|
|
var arr = [3, 1, 2]
|
|
var sorted = sort(arr)
|
|
if (arr[0] != 3) fail("sort should not mutate original")
|
|
})
|
|
|
|
run("sort empty array", function() {
|
|
var arr = []
|
|
var sorted = sort(arr)
|
|
if (length(sorted) != 0) fail("sort empty array failed")
|
|
})
|
|
|
|
run("sort single element", function() {
|
|
var arr = [42]
|
|
var sorted = sort(arr)
|
|
if (sorted[0] != 42) fail("sort single element failed")
|
|
})
|
|
|
|
run("sort by field", function() {
|
|
var arr = [
|
|
{name: "Charlie", age: 30},
|
|
{name: "Alice", age: 25},
|
|
{name: "Bob", age: 35}
|
|
]
|
|
var sorted = sort(arr, "name")
|
|
if (sorted[0].name != "Alice") fail("sort by field failed")
|
|
if (sorted[2].name != "Charlie") fail("sort by field last failed")
|
|
})
|
|
|
|
run("sort by index", function() {
|
|
var arr = [[3, "c"], [1, "a"], [2, "b"]]
|
|
var sorted = sort(arr, 0)
|
|
if (sorted[0][1] != "a") fail("sort by index failed")
|
|
})
|
|
|
|
run("sort stable", function() {
|
|
var arr = [
|
|
{name: "A", order: 1},
|
|
{name: "B", order: 1},
|
|
{name: "C", order: 1}
|
|
]
|
|
var sorted = sort(arr, "order")
|
|
if (sorted[0].name != "A" || sorted[1].name != "B" || sorted[2].name != "C") {
|
|
fail("sort should be stable")
|
|
}
|
|
})
|
|
|
|
run("sort negative numbers", function() {
|
|
var arr = [-5, 3, -1, 0, 2]
|
|
var sorted = sort(arr)
|
|
if (sorted[0] != -5 || sorted[4] != 3) fail("sort negative numbers failed")
|
|
})
|
|
|
|
// ============================================================================
|
|
// FILTER FUNCTION
|
|
// ============================================================================
|
|
|
|
run("filter basic", function() {
|
|
var arr = [1, 2, 3, 4, 5, 6]
|
|
var evens = filter(arr, x => x % 2 == 0)
|
|
if (length(evens) != 3) fail("filter basic length failed")
|
|
if (evens[0] != 2 || evens[1] != 4 || evens[2] != 6) fail("filter basic values failed")
|
|
})
|
|
|
|
run("filter all pass", function() {
|
|
var arr = [2, 4, 6]
|
|
var result = filter(arr, x => x % 2 == 0)
|
|
if (length(result) != 3) fail("filter all pass failed")
|
|
})
|
|
|
|
run("filter none pass", function() {
|
|
var arr = [1, 3, 5]
|
|
var result = filter(arr, x => x % 2 == 0)
|
|
if (length(result) != 0) fail("filter none pass failed")
|
|
})
|
|
|
|
run("filter empty array", function() {
|
|
var arr = []
|
|
var result = filter(arr, x => true)
|
|
if (length(result) != 0) fail("filter empty array failed")
|
|
})
|
|
|
|
run("filter with index", function() {
|
|
var arr = ["a", "b", "c", "d"]
|
|
var result = filter(arr, (x, i) => i % 2 == 0)
|
|
if (length(result) != 2) fail("filter with index length failed")
|
|
if (result[0] != "a" || result[1] != "c") fail("filter with index values failed")
|
|
})
|
|
|
|
run("filter preserves original", function() {
|
|
var arr = [1, 2, 3]
|
|
var result = filter(arr, x => x > 1)
|
|
if (length(arr) != 3) fail("filter should not mutate original")
|
|
})
|
|
|
|
run("filter objects", function() {
|
|
var arr = [{active: true}, {active: false}, {active: true}]
|
|
var result = filter(arr, x => x.active)
|
|
if (length(result) != 2) fail("filter objects failed")
|
|
})
|
|
|
|
// ============================================================================
|
|
// FIND FUNCTION
|
|
// ============================================================================
|
|
|
|
run("find basic", function() {
|
|
var arr = [1, 2, 3, 4, 5]
|
|
var idx = find(arr, x => x > 3)
|
|
if (idx != 3) fail("find basic failed")
|
|
})
|
|
|
|
run("find first element", function() {
|
|
var arr = [10, 2, 3]
|
|
var idx = find(arr, x => x > 5)
|
|
if (idx != 0) fail("find first element failed")
|
|
})
|
|
|
|
run("find last element", function() {
|
|
var arr = [1, 2, 10]
|
|
var idx = find(arr, x => x > 5)
|
|
if (idx != 2) fail("find last element failed")
|
|
})
|
|
|
|
run("find not found", function() {
|
|
var arr = [1, 2, 3]
|
|
var idx = find(arr, x => x > 10)
|
|
if (idx != null) fail("find not found should return null")
|
|
})
|
|
|
|
run("find empty array", function() {
|
|
var arr = []
|
|
var idx = find(arr, x => true)
|
|
if (idx != null) fail("find in empty array should return null")
|
|
})
|
|
|
|
run("find by value", function() {
|
|
var arr = [10, 20, 30, 20]
|
|
var idx = find(arr, 20)
|
|
if (idx != 1) fail("find by value failed")
|
|
})
|
|
|
|
run("find reverse", function() {
|
|
var arr = [10, 20, 30, 20]
|
|
var idx = find(arr, 20, true)
|
|
if (idx != 3) fail("find reverse failed")
|
|
})
|
|
|
|
run("find with from", function() {
|
|
var arr = [10, 20, 30, 20]
|
|
var idx = find(arr, 20, false, 2)
|
|
if (idx != 3) fail("find with from failed")
|
|
})
|
|
|
|
run("find with index callback", function() {
|
|
var arr = ["a", "b", "c"]
|
|
var idx = find(arr, (x, i) => i == 1)
|
|
if (idx != 1) fail("find with index callback failed")
|
|
})
|
|
|
|
// ============================================================================
|
|
// ABS FUNCTION
|
|
// ============================================================================
|
|
|
|
run("abs positive", function() {
|
|
if (abs(5) != 5) fail("abs positive failed")
|
|
})
|
|
|
|
run("abs negative", function() {
|
|
if (abs(-5) != 5) fail("abs negative failed")
|
|
})
|
|
|
|
run("abs zero", function() {
|
|
if (abs(0) != 0) fail("abs zero failed")
|
|
})
|
|
|
|
run("abs float", function() {
|
|
if (abs(-3.14) != 3.14) fail("abs float failed")
|
|
})
|
|
|
|
run("abs non number", function() {
|
|
if (abs("5") != null) fail("abs non-number should return null")
|
|
if (abs(null) != null) fail("abs null should return null")
|
|
})
|
|
|
|
// ============================================================================
|
|
// FLOOR FUNCTION
|
|
// ============================================================================
|
|
|
|
run("floor positive", function() {
|
|
if (floor(3.7) != 3) fail("floor positive failed")
|
|
})
|
|
|
|
run("floor negative", function() {
|
|
if (floor(-3.7) != -4) fail("floor negative failed")
|
|
})
|
|
|
|
run("floor integer", function() {
|
|
if (floor(5) != 5) fail("floor integer failed")
|
|
})
|
|
|
|
run("floor zero", function() {
|
|
if (floor(0) != 0) fail("floor zero failed")
|
|
})
|
|
|
|
run("floor with place", function() {
|
|
if (floor(12.3775, -2) != 12.37) fail("floor with place failed")
|
|
})
|
|
|
|
run("floor negative with place", function() {
|
|
if (floor(-12.3775, -2) != -12.38) fail("floor negative with place failed")
|
|
})
|
|
|
|
// ============================================================================
|
|
// CEILING FUNCTION
|
|
// ============================================================================
|
|
|
|
run("ceiling positive", function() {
|
|
if (ceiling(3.2) != 4) fail("ceiling positive failed")
|
|
})
|
|
|
|
run("ceiling negative", function() {
|
|
if (ceiling(-3.7) != -3) fail("ceiling negative failed")
|
|
})
|
|
|
|
run("ceiling integer", function() {
|
|
if (ceiling(5) != 5) fail("ceiling integer failed")
|
|
})
|
|
|
|
run("ceiling zero", function() {
|
|
if (ceiling(0) != 0) fail("ceiling zero failed")
|
|
})
|
|
|
|
run("ceiling with place", function() {
|
|
if (ceiling(12.3775, -2) != 12.38) fail("ceiling with place failed")
|
|
})
|
|
|
|
run("ceiling negative with place", function() {
|
|
if (ceiling(-12.3775, -2) != -12.37) fail("ceiling negative with place failed")
|
|
})
|
|
|
|
// ============================================================================
|
|
// ROUND FUNCTION
|
|
// ============================================================================
|
|
|
|
run("round down", function() {
|
|
if (round(3.4) != 3) fail("round down failed")
|
|
})
|
|
|
|
run("round up", function() {
|
|
if (round(3.6) != 4) fail("round up failed")
|
|
})
|
|
|
|
run("round half", function() {
|
|
if (round(3.5) != 4) fail("round half failed")
|
|
})
|
|
|
|
run("round negative", function() {
|
|
if (round(-3.5) != -3 && round(-3.5) != -4) fail("round negative failed")
|
|
})
|
|
|
|
run("round integer", function() {
|
|
if (round(5) != 5) fail("round integer failed")
|
|
})
|
|
|
|
run("round with places", function() {
|
|
if (round(12.3775, -2) != 12.38) fail("round with places failed")
|
|
})
|
|
|
|
run("round to tens", function() {
|
|
if (round(12.3775, 1) != 10) fail("round to tens failed")
|
|
})
|
|
|
|
// ============================================================================
|
|
// TRUNC FUNCTION
|
|
// ============================================================================
|
|
|
|
run("trunc positive", function() {
|
|
if (trunc(3.7) != 3) fail("trunc positive failed")
|
|
})
|
|
|
|
run("trunc negative", function() {
|
|
if (trunc(-3.7) != -3) fail("trunc negative failed")
|
|
})
|
|
|
|
run("trunc integer", function() {
|
|
if (trunc(5) != 5) fail("trunc integer failed")
|
|
})
|
|
|
|
run("trunc zero", function() {
|
|
if (trunc(0) != 0) fail("trunc zero failed")
|
|
})
|
|
|
|
run("trunc with places", function() {
|
|
if (trunc(12.3775, -2) != 12.37) fail("trunc with places failed")
|
|
})
|
|
|
|
run("trunc negative with places", function() {
|
|
if (trunc(-12.3775, -2) != -12.37) fail("trunc negative with places failed")
|
|
})
|
|
|
|
// ============================================================================
|
|
// SIGN FUNCTION
|
|
// ============================================================================
|
|
|
|
run("sign positive", function() {
|
|
if (sign(5) != 1) fail("sign positive failed")
|
|
})
|
|
|
|
run("sign negative", function() {
|
|
if (sign(-5) != -1) fail("sign negative failed")
|
|
})
|
|
|
|
run("sign zero", function() {
|
|
if (sign(0) != 0) fail("sign zero failed")
|
|
})
|
|
|
|
run("sign float", function() {
|
|
if (sign(0.001) != 1) fail("sign positive float failed")
|
|
if (sign(-0.001) != -1) fail("sign negative float failed")
|
|
})
|
|
|
|
run("sign non number", function() {
|
|
if (sign("5") != null) fail("sign non-number should return null")
|
|
})
|
|
|
|
// ============================================================================
|
|
// WHOLE AND FRACTION FUNCTIONS
|
|
// ============================================================================
|
|
|
|
run("whole positive", function() {
|
|
if (whole(3.7) != 3) fail("whole positive failed")
|
|
})
|
|
|
|
run("whole negative", function() {
|
|
if (whole(-3.7) != -3) fail("whole negative failed")
|
|
})
|
|
|
|
run("whole integer", function() {
|
|
if (whole(5) != 5) fail("whole integer failed")
|
|
})
|
|
|
|
run("whole non number", function() {
|
|
if (whole("5") != null) fail("whole non-number should return null")
|
|
})
|
|
|
|
run("fraction positive", function() {
|
|
var f = fraction(3.75)
|
|
if (f < 0.74 || f > 0.76) fail("fraction positive failed")
|
|
})
|
|
|
|
run("fraction negative", function() {
|
|
var f = fraction(-3.75)
|
|
if (f > -0.74 || f < -0.76) fail("fraction negative failed")
|
|
})
|
|
|
|
run("fraction integer", function() {
|
|
if (fraction(5) != 0) fail("fraction integer failed")
|
|
})
|
|
|
|
run("fraction non number", function() {
|
|
if (fraction("5") != null) fail("fraction non-number should return null")
|
|
})
|
|
|
|
// ============================================================================
|
|
// NEG FUNCTION
|
|
// ============================================================================
|
|
|
|
run("neg positive", function() {
|
|
if (neg(5) != -5) fail("neg positive failed")
|
|
})
|
|
|
|
run("neg negative", function() {
|
|
if (neg(-5) != 5) fail("neg negative failed")
|
|
})
|
|
|
|
run("neg zero", function() {
|
|
if (neg(0) != 0) fail("neg zero failed")
|
|
})
|
|
|
|
run("neg float", function() {
|
|
if (neg(3.14) != -3.14) fail("neg float failed")
|
|
})
|
|
|
|
run("neg non number", function() {
|
|
if (neg("5") != null) fail("neg non-number should return null")
|
|
})
|
|
|
|
// ============================================================================
|
|
// MODULO FUNCTION
|
|
// ============================================================================
|
|
|
|
run("modulo positive", function() {
|
|
if (modulo(10, 3) != 1) fail("modulo positive failed")
|
|
})
|
|
|
|
run("modulo negative dividend", function() {
|
|
var result = modulo(-10, 3)
|
|
if (result != 2) fail("modulo negative dividend failed")
|
|
})
|
|
|
|
run("modulo negative divisor", function() {
|
|
var result = modulo(10, -3)
|
|
if (result != -2) fail("modulo negative divisor failed")
|
|
})
|
|
|
|
run("modulo both negative", function() {
|
|
var result = modulo(-10, -3)
|
|
if (result != -1) fail("modulo both negative failed")
|
|
})
|
|
|
|
run("modulo zero dividend", function() {
|
|
if (modulo(0, 5) != 0) fail("modulo zero dividend failed")
|
|
})
|
|
|
|
run("modulo zero divisor", function() {
|
|
if (modulo(10, 0) != null) fail("modulo zero divisor should return null")
|
|
})
|
|
|
|
run("modulo floats", function() {
|
|
var result = modulo(5.5, 2)
|
|
if (result < 1.4 || result > 1.6) fail("modulo floats failed")
|
|
})
|
|
|
|
// ============================================================================
|
|
// MIN AND MAX FUNCTIONS
|
|
// ============================================================================
|
|
|
|
run("min basic", function() {
|
|
if (min(3, 5) != 3) fail("min basic failed")
|
|
})
|
|
|
|
run("min equal", function() {
|
|
if (min(5, 5) != 5) fail("min equal failed")
|
|
})
|
|
|
|
run("min negative", function() {
|
|
if (min(-3, -5) != -5) fail("min negative failed")
|
|
})
|
|
|
|
run("min mixed", function() {
|
|
if (min(-3, 5) != -3) fail("min mixed failed")
|
|
})
|
|
|
|
run("min float", function() {
|
|
if (min(3.14, 2.71) != 2.71) fail("min float failed")
|
|
})
|
|
|
|
run("min non number", function() {
|
|
if (min(3, "5") != null) fail("min non-number should return null")
|
|
if (min("3", 5) != null) fail("min first non-number should return null")
|
|
})
|
|
|
|
run("max basic", function() {
|
|
if (max(3, 5) != 5) fail("max basic failed")
|
|
})
|
|
|
|
run("max equal", function() {
|
|
if (max(5, 5) != 5) fail("max equal failed")
|
|
})
|
|
|
|
run("max negative", function() {
|
|
if (max(-3, -5) != -3) fail("max negative failed")
|
|
})
|
|
|
|
run("max mixed", function() {
|
|
if (max(-3, 5) != 5) fail("max mixed failed")
|
|
})
|
|
|
|
run("max float", function() {
|
|
if (max(3.14, 2.71) != 3.14) fail("max float failed")
|
|
})
|
|
|
|
run("max non number", function() {
|
|
if (max(3, "5") != null) fail("max non-number should return null")
|
|
})
|
|
|
|
run("min max constrain", function() {
|
|
var val = 8
|
|
var constrained = min(max(val, 0), 10)
|
|
if (constrained != 8) fail("min max constrain in range failed")
|
|
constrained = min(max(-5, 0), 10)
|
|
if (constrained != 0) fail("min max constrain below failed")
|
|
constrained = min(max(15, 0), 10)
|
|
if (constrained != 10) fail("min max constrain above failed")
|
|
})
|
|
|
|
// ============================================================================
|
|
// CODEPOINT FUNCTION
|
|
// ============================================================================
|
|
|
|
run("codepoint letter", function() {
|
|
if (codepoint("A") != 65) fail("codepoint A failed")
|
|
if (codepoint("a") != 97) fail("codepoint a failed")
|
|
})
|
|
|
|
run("codepoint digit", function() {
|
|
if (codepoint("0") != 48) fail("codepoint 0 failed")
|
|
})
|
|
|
|
run("codepoint unicode", function() {
|
|
if (codepoint("\u00E9") != 233) fail("codepoint unicode failed")
|
|
})
|
|
|
|
run("codepoint first char", function() {
|
|
if (codepoint("ABC") != 65) fail("codepoint should return first char")
|
|
})
|
|
|
|
run("codepoint empty", function() {
|
|
if (codepoint("") != null) fail("codepoint empty should return null")
|
|
})
|
|
|
|
run("codepoint non text", function() {
|
|
if (codepoint(65) != null) fail("codepoint non-text should return null")
|
|
})
|
|
|
|
// ============================================================================
|
|
// CHARACTER FUNCTION
|
|
// ============================================================================
|
|
|
|
run("character letter", function() {
|
|
if (character(65) != "A") fail("character 65 failed")
|
|
if (character(97) != "a") fail("character 97 failed")
|
|
})
|
|
|
|
run("character digit", function() {
|
|
if (character(48) != "0") fail("character 48 failed")
|
|
})
|
|
|
|
run("character unicode", function() {
|
|
if (character(233) != "\u00E9") fail("character unicode failed")
|
|
})
|
|
|
|
run("character from text", function() {
|
|
if (character("hello") != "h") fail("character from text failed")
|
|
})
|
|
|
|
run("character invalid", function() {
|
|
if (character(-1) != "") fail("character negative should return empty")
|
|
})
|
|
|
|
// ============================================================================
|
|
// SEARCH FUNCTION
|
|
// ============================================================================
|
|
|
|
run("search found", function() {
|
|
if (search("hello world", "world") != 6) fail("search found failed")
|
|
})
|
|
|
|
run("search not found", function() {
|
|
if (search("hello world", "xyz") != null) fail("search not found should return null")
|
|
})
|
|
|
|
run("search beginning", function() {
|
|
if (search("hello world", "hello") != 0) fail("search beginning failed")
|
|
})
|
|
|
|
run("search single char", function() {
|
|
if (search("hello", "l") != 2) fail("search single char failed")
|
|
})
|
|
|
|
run("search with from", function() {
|
|
if (search("hello hello", "hello", 1) != 6) fail("search with from failed")
|
|
})
|
|
|
|
run("search empty pattern", function() {
|
|
if (search("hello", "") != 0) fail("search empty pattern failed")
|
|
})
|
|
|
|
run("search negative from", function() {
|
|
var result = search("hello world", "world", -5)
|
|
if (result != 6) fail("search negative from failed")
|
|
})
|
|
|
|
// ============================================================================
|
|
// REPLACE FUNCTION
|
|
// ============================================================================
|
|
|
|
run("replace basic", function() {
|
|
var result = replace("hello world", "world", "universe")
|
|
if (result != "hello universe") fail("replace basic failed")
|
|
})
|
|
|
|
run("replace not found", function() {
|
|
var result = replace("hello world", "xyz", "abc")
|
|
if (result != "hello world") fail("replace not found should return original")
|
|
})
|
|
|
|
run("replace multiple", function() {
|
|
var result = replace("banana", "a", "o")
|
|
if (result != "bonono") fail("replace multiple failed")
|
|
})
|
|
|
|
run("replace with limit", function() {
|
|
var result = replace("banana", "a", "o", 1)
|
|
if (result != "bonana") fail("replace with limit failed")
|
|
})
|
|
|
|
run("replace empty target", function() {
|
|
var result = replace("abc", "", "-")
|
|
if (result != "-a-b-c-") fail("replace empty target failed")
|
|
})
|
|
|
|
run("replace to empty", function() {
|
|
var result = replace("hello", "l", "")
|
|
if (result != "heo") fail("replace to empty failed")
|
|
})
|
|
|
|
run("replace with function", function() {
|
|
var result = replace("hello", "l", (match, pos) => `[${pos}]`)
|
|
if (result != "he[2][3]o") fail("replace with function failed")
|
|
})
|
|
|
|
run("replace with function limit", function() {
|
|
var result = replace("banana", "a", (match, pos) => `[${pos}]`, 2)
|
|
if (result != "b[1]n[3]na") fail("replace with function limit failed")
|
|
})
|
|
|
|
run("replace with regex", function() {
|
|
var result = replace("banana", /a/, "o")
|
|
if (result != "bonono") fail("replace with regex failed")
|
|
})
|
|
|
|
run("replace with regex limit", function() {
|
|
var result = replace("banana", /a/, "o", 2)
|
|
if (result != "bonona") fail("replace with regex limit failed")
|
|
})
|
|
|
|
run("replace with regex function", function() {
|
|
var result = replace("hello", /l/, (match, pos) => `[${pos}]`)
|
|
if (result != "he[2][3]o") fail("replace with regex function failed")
|
|
})
|
|
|
|
// ============================================================================
|
|
// TEXT FUNCTION (Conversion and Slicing)
|
|
// ============================================================================
|
|
|
|
run("text number basic", function() {
|
|
if (text(123) != "123") fail("text number basic failed")
|
|
})
|
|
|
|
run("text number negative", function() {
|
|
if (text(-456) != "-456") fail("text number negative failed")
|
|
})
|
|
|
|
run("text number float", function() {
|
|
var result = text(3.14)
|
|
if (search(result, "3.14") != 0) fail("text number float failed")
|
|
})
|
|
|
|
run("text array join empty sep", function() {
|
|
var result = text(["a", "b", "c"], "")
|
|
if (result != "abc") fail("text array join empty sep failed")
|
|
})
|
|
|
|
run("text slice basic", function() {
|
|
if (text("hello", 1, 4) != "ell") fail("text slice basic failed")
|
|
})
|
|
|
|
run("text slice from only", function() {
|
|
if (text("hello", 2) != "llo") fail("text slice from only failed")
|
|
})
|
|
|
|
run("text slice negative from", function() {
|
|
if (text("hello", -2) != "lo") fail("text slice negative from failed")
|
|
})
|
|
|
|
run("text slice negative to", function() {
|
|
if (text("hello", 0, -2) != "hel") fail("text slice negative to failed")
|
|
})
|
|
|
|
run("text boolean", function() {
|
|
if (text(true) != "true") fail("text true failed")
|
|
if (text(false) != "false") fail("text false failed")
|
|
})
|
|
|
|
run("text null", function() {
|
|
if (text(null) != "null") fail("text null failed")
|
|
})
|
|
|
|
// ============================================================================
|
|
// NUMBER FUNCTION (Conversion)
|
|
// ============================================================================
|
|
|
|
run("number from string", function() {
|
|
if (number("123") != 123) fail("number from string failed")
|
|
})
|
|
|
|
run("number from negative string", function() {
|
|
if (number("-456") != -456) fail("number from negative string failed")
|
|
})
|
|
|
|
run("number from float string", function() {
|
|
if (number("3.14") != 3.14) fail("number from float string failed")
|
|
})
|
|
|
|
run("number invalid string", function() {
|
|
if (number("abc") != null) fail("number invalid string should return null")
|
|
})
|
|
|
|
run("number from boolean", function() {
|
|
if (number(true) != 1) fail("number from true failed")
|
|
if (number(false) != 0) fail("number from false failed")
|
|
})
|
|
|
|
run("number from number", function() {
|
|
if (number(42) != 42) fail("number from number failed")
|
|
})
|
|
|
|
run("number with radix", function() {
|
|
if (number("FF", 16) != 255) fail("number hex failed")
|
|
if (number("1010", 2) != 10) fail("number binary failed")
|
|
})
|
|
|
|
run("number leading zeros", function() {
|
|
if (number("007") != 7) fail("number leading zeros failed")
|
|
})
|
|
|
|
// ============================================================================
|
|
// ARRAY FUNCTION (Creator and Slicing)
|
|
// ============================================================================
|
|
|
|
run("array create with length", function() {
|
|
var arr = array(5)
|
|
if (length(arr) != 5) fail("array create length failed")
|
|
if (arr[0] != null) fail("array create should init to null")
|
|
})
|
|
|
|
run("array create with initial", function() {
|
|
var arr = array(3, 42)
|
|
if (arr[0] != 42 || arr[1] != 42 || arr[2] != 42) fail("array create with initial failed")
|
|
})
|
|
|
|
run("array create with function", function() {
|
|
var arr = array(3, i => i * 2)
|
|
if (arr[0] != 0 || arr[1] != 2 || arr[2] != 4) fail("array create with function failed")
|
|
})
|
|
|
|
run("array copy", function() {
|
|
var orig = [1, 2, 3]
|
|
var copy = array(orig)
|
|
copy[0] = 99
|
|
if (orig[0] != 1) fail("array copy should not affect original")
|
|
})
|
|
|
|
run("array slice basic", function() {
|
|
var arr = [1, 2, 3, 4, 5]
|
|
var sliced = array(arr, 1, 3)
|
|
if (length(sliced) != 2) fail("array slice length failed")
|
|
if (sliced[0] != 2 || sliced[1] != 3) fail("array slice values failed")
|
|
})
|
|
|
|
run("array slice negative", function() {
|
|
var arr = [1, 2, 3, 4, 5]
|
|
var sliced = array(arr, -3)
|
|
if (length(sliced) != 3) fail("array slice negative failed")
|
|
if (sliced[0] != 3) fail("array slice negative value failed")
|
|
})
|
|
|
|
run("array from object keys", function() {
|
|
var obj = {a: 1, b: 2, c: 3}
|
|
var keys = array(obj)
|
|
if (length(keys) != 3) fail("array from object keys length failed")
|
|
})
|
|
|
|
run("array from text", function() {
|
|
var arr = array("abc")
|
|
if (length(arr) != 3) fail("array from text length failed")
|
|
if (arr[0] != "a" || arr[1] != "b" || arr[2] != "c") fail("array from text values failed")
|
|
})
|
|
|
|
run("array split text", function() {
|
|
var arr = array("a,b,c", ",")
|
|
if (length(arr) != 3) fail("array split text length failed")
|
|
if (arr[1] != "b") fail("array split text value failed")
|
|
})
|
|
|
|
// ============================================================================
|
|
// TRIM FUNCTION
|
|
// ============================================================================
|
|
|
|
run("trim spaces", function() {
|
|
if (trim(" hello ") != "hello") fail("trim spaces failed")
|
|
})
|
|
|
|
run("trim tabs", function() {
|
|
if (trim("\thello\t") != "hello") fail("trim tabs failed")
|
|
})
|
|
|
|
run("trim mixed", function() {
|
|
if (trim(" \t hello \n ") != "hello") fail("trim mixed failed")
|
|
})
|
|
|
|
run("trim no whitespace", function() {
|
|
if (trim("hello") != "hello") fail("trim no whitespace failed")
|
|
})
|
|
|
|
run("trim empty", function() {
|
|
if (trim("") != "") fail("trim empty failed")
|
|
})
|
|
|
|
run("trim all whitespace", function() {
|
|
if (trim(" ") != "") fail("trim all whitespace failed")
|
|
})
|
|
|
|
// ============================================================================
|
|
// LOWER AND UPPER FUNCTIONS
|
|
// ============================================================================
|
|
|
|
run("lower basic", function() {
|
|
if (lower("HELLO") != "hello") fail("lower basic failed")
|
|
})
|
|
|
|
run("lower mixed", function() {
|
|
if (lower("HeLLo WoRLD") != "hello world") fail("lower mixed failed")
|
|
})
|
|
|
|
run("lower already lower", function() {
|
|
if (lower("hello") != "hello") fail("lower already lower failed")
|
|
})
|
|
|
|
run("lower with numbers", function() {
|
|
if (lower("ABC123") != "abc123") fail("lower with numbers failed")
|
|
})
|
|
|
|
run("upper basic", function() {
|
|
if (upper("hello") != "HELLO") fail("upper basic failed")
|
|
})
|
|
|
|
run("upper mixed", function() {
|
|
if (upper("HeLLo WoRLD") != "HELLO WORLD") fail("upper mixed failed")
|
|
})
|
|
|
|
run("upper already upper", function() {
|
|
if (upper("HELLO") != "HELLO") fail("upper already upper failed")
|
|
})
|
|
|
|
run("upper with numbers", function() {
|
|
if (upper("abc123") != "ABC123") fail("upper with numbers failed")
|
|
})
|
|
|
|
// ============================================================================
|
|
// APPLY FUNCTION
|
|
// ============================================================================
|
|
|
|
run("apply basic", function() {
|
|
var fn = function(a, b) { return a + b }
|
|
var result = apply(fn, [3, 4])
|
|
if (result != 7) fail("apply basic failed")
|
|
})
|
|
|
|
run("apply no args", function() {
|
|
var fn = function() { return 42 }
|
|
var result = apply(fn, [])
|
|
if (result != 42) fail("apply no args failed")
|
|
})
|
|
|
|
run("apply single arg", function() {
|
|
var fn = function(x) { return x * 2 }
|
|
var result = apply(fn, [5])
|
|
if (result != 10) fail("apply single arg failed")
|
|
})
|
|
|
|
run("apply many args", function() {
|
|
var fn = function(a, b, c, d) { return a + b + c + d }
|
|
var result = apply(fn, [1, 2, 3, 4])
|
|
if (result != 10) fail("apply many args failed")
|
|
})
|
|
|
|
run("apply non function", function() {
|
|
var result = apply(42, [1, 2])
|
|
if (result != 42) fail("apply non-function should return first arg")
|
|
})
|
|
|
|
// ============================================================================
|
|
// CALL FUNCTION (Additional Tests)
|
|
// ============================================================================
|
|
|
|
run("call many args", function() {
|
|
var fn = function(a, b, c, d) { return a * b + c * d }
|
|
var result = call(fn, null, [2, 3, 4, 5])
|
|
if (result != 26) fail("call many args failed")
|
|
})
|
|
|
|
run("call method style", function() {
|
|
var obj = {
|
|
value: 10,
|
|
multiply: function(x) { return this.value * x }
|
|
}
|
|
var result = call(obj.multiply, obj, [5])
|
|
if (result != 50) fail("call method style failed")
|
|
})
|
|
|
|
run("call change this", function() {
|
|
var obj1 = { value: 10 }
|
|
var obj2 = { value: 20 }
|
|
var fn = function() { return this.value }
|
|
if (call(fn, obj1) != 10) fail("call this obj1 failed")
|
|
if (call(fn, obj2) != 20) fail("call this obj2 failed")
|
|
})
|
|
|
|
// ============================================================================
|
|
// ARRFOR FUNCTION (Array For-Each)
|
|
// ============================================================================
|
|
|
|
run("arrfor basic", function() {
|
|
var arr = [1, 2, 3]
|
|
var sum = 0
|
|
arrfor(arr, x => { sum = sum + x })
|
|
if (sum != 6) fail("arrfor basic failed")
|
|
})
|
|
|
|
run("arrfor with index", function() {
|
|
var arr = ["a", "b", "c"]
|
|
var indices = []
|
|
arrfor(arr, (x, i) => { indices[] = i })
|
|
if (indices[0] != 0 || indices[2] != 2) fail("arrfor with index failed")
|
|
})
|
|
|
|
run("arrfor empty", function() {
|
|
var called = false
|
|
arrfor([], x => { called = true })
|
|
if (called) fail("arrfor empty should not call function")
|
|
})
|
|
|
|
run("arrfor mutation", function() {
|
|
var arr = [1, 2, 3]
|
|
var results = []
|
|
arrfor(arr, x => { results[] = x * 2 })
|
|
if (results[0] != 2 || results[1] != 4 || results[2] != 6) fail("arrfor mutation failed")
|
|
})
|
|
|
|
// ============================================================================
|
|
// STONE FUNCTION (Additional Tests)
|
|
// ============================================================================
|
|
|
|
run("stone returns value", function() {
|
|
var obj = {x: 1}
|
|
var result = stone(obj)
|
|
if (result != obj) fail("stone should return the value")
|
|
})
|
|
|
|
run("stone idempotent", function() {
|
|
var obj = {x: 1}
|
|
stone(obj)
|
|
stone(obj)
|
|
if (!is_stone(obj)) fail("stone should be idempotent")
|
|
})
|
|
|
|
// ============================================================================
|
|
// PROTO FUNCTION (Additional Tests)
|
|
// ============================================================================
|
|
|
|
run("proto chain", function() {
|
|
var grandparent = {a: 1}
|
|
var parent = meme(grandparent)
|
|
var child = meme(parent)
|
|
if (proto(child) != parent) fail("proto chain child->parent failed")
|
|
if (proto(parent) != grandparent) fail("proto chain parent->grandparent failed")
|
|
})
|
|
|
|
run("proto array disrupts", function() {
|
|
if (!should_disrupt(function() { proto([1, 2, 3]) })) fail("proto of array should disrupt")
|
|
})
|
|
|
|
// ============================================================================
|
|
// MEME FUNCTION (Additional Tests)
|
|
// ============================================================================
|
|
|
|
run("meme method inheritance", function() {
|
|
var parent = {
|
|
greet: function() { return "hello" }
|
|
}
|
|
var child = meme(parent)
|
|
if (child.greet() != "hello") fail("meme method inheritance failed")
|
|
})
|
|
|
|
run("meme this in inherited method", function() {
|
|
var parent = {
|
|
getValue: function() { return this.value }
|
|
}
|
|
var child = meme(parent)
|
|
child.value = 42
|
|
if (child.getValue() != 42) fail("meme this in inherited method failed")
|
|
})
|
|
|
|
run("meme deep chain", function() {
|
|
var a = {x: 1}
|
|
var b = meme(a)
|
|
var c = meme(b)
|
|
var d = meme(c)
|
|
if (d.x != 1) fail("meme deep chain failed")
|
|
})
|
|
|
|
// ============================================================================
|
|
// DELETE OPERATOR
|
|
// ============================================================================
|
|
|
|
run("delete property", function() {
|
|
var obj = {a: 1, b: 2}
|
|
delete obj.a
|
|
if ("a" in obj) fail("delete property failed")
|
|
if (obj.b != 2) fail("delete should not affect other properties")
|
|
})
|
|
|
|
run("delete array element disrupts", function() {
|
|
if (!should_disrupt(function() { var arr = [1, 2, 3]; delete arr[1] })) fail("delete on array element should disrupt")
|
|
})
|
|
|
|
run("delete nonexistent", function() {
|
|
var obj = {a: 1}
|
|
delete obj.b
|
|
if (obj.a != 1) fail("delete nonexistent should not affect object")
|
|
})
|
|
|
|
// ============================================================================
|
|
// TYPEOF-LIKE BEHAVIOR
|
|
// ============================================================================
|
|
|
|
run("is integer", function() {
|
|
if (!is_number(5) || 5 % 1 != 0) fail("is_integer positive failed")
|
|
if (!is_number(-5) || -5 % 1 != 0) fail("is_integer negative failed")
|
|
if (is_number(5.5) && 5.5 % 1 == 0) fail("is_integer float should not be integer")
|
|
})
|
|
|
|
// ============================================================================
|
|
// ARRAY MAP-LIKE WITH ARRAY FUNCTION
|
|
// ============================================================================
|
|
|
|
run("array map basic", function() {
|
|
var arr = [1, 2, 3]
|
|
var doubled = array(arr, x => x * 2)
|
|
if (doubled[0] != 2 || doubled[1] != 4 || doubled[2] != 6) fail("array map basic failed")
|
|
})
|
|
|
|
run("array map with index", function() {
|
|
var arr = ["a", "b", "c"]
|
|
var result = array(arr, (x, i) => `${x}${i}`)
|
|
if (result[0] != "a0" || result[1] != "b1") fail("array map with index failed")
|
|
})
|
|
|
|
run("array map reverse", function() {
|
|
var arr = [1, 2, 3]
|
|
var result = array(arr, x => x * 2, true)
|
|
if (result[0] != 6 || result[2] != 2) fail("array map reverse failed")
|
|
})
|
|
|
|
run("array map with exit", function() {
|
|
var arr = [1, 2, 3, 4, 5]
|
|
var result = array(arr, x => {
|
|
if (x > 3) return null
|
|
return x * 2
|
|
}, false, null)
|
|
if (length(result) != 5) fail("array map with exit length unexpected")
|
|
})
|
|
|
|
// ============================================================================
|
|
// ERROR OBJECTS
|
|
// ============================================================================
|
|
|
|
run("error creation", function() {
|
|
var e = Error("test message")
|
|
if (e.message != "test message") fail("Error creation failed")
|
|
})
|
|
|
|
// ============================================================================
|
|
// STRING METHOD EDGE CASES
|
|
// ============================================================================
|
|
|
|
run("string startsWith", function() {
|
|
if (!starts_with("hello", "hel")) fail("startsWith match failed")
|
|
if (starts_with("hello", "ell")) fail("startsWith no match failed")
|
|
if (!starts_with("hello", "")) fail("startsWith empty should match")
|
|
})
|
|
|
|
run("string endsWith", function() {
|
|
if (!ends_with("hello", "llo")) fail("endsWith match failed")
|
|
if (ends_with("hello", "ell")) fail("endsWith no match failed")
|
|
if (!ends_with("hello", "")) fail("endsWith empty should match")
|
|
})
|
|
|
|
run("string includes", function() {
|
|
if (search("hello world", "world") == null) fail("includes match failed")
|
|
if (search("hello", "xyz") != null) fail("includes no match failed")
|
|
if (search("hello", "") == null) fail("includes empty should match")
|
|
})
|
|
|
|
// ============================================================================
|
|
// ARRAY METHOD EDGE CASES
|
|
// ============================================================================
|
|
|
|
run("array includes", function() {
|
|
var arr = [1, 2, 3]
|
|
if (find(arr, 2) == null) fail("array includes match failed")
|
|
if (find(arr, 5) != null) fail("array includes no match failed")
|
|
})
|
|
|
|
run("array every", function() {
|
|
var arr = [2, 4, 6]
|
|
if (!every(arr, x => x % 2 == 0)) fail("array every all pass failed")
|
|
arr = [2, 3, 6]
|
|
if (every(arr, x => x % 2 == 0)) fail("array every not all pass failed")
|
|
})
|
|
|
|
run("array some", function() {
|
|
var arr = [1, 2, 3]
|
|
if (!some(arr, x => x > 2)) fail("array some match failed")
|
|
if (some(arr, x => x > 5)) fail("array some no match failed")
|
|
})
|
|
|
|
// ============================================================================
|
|
// ADDITIONAL EDGE CASES
|
|
// ============================================================================
|
|
|
|
run("nested array access", function() {
|
|
var arr = [[[1, 2], [3, 4]], [[5, 6], [7, 8]]]
|
|
if (arr[0][1][0] != 3) fail("nested array access failed")
|
|
if (arr[1][1][1] != 8) fail("nested array access deep failed")
|
|
})
|
|
|
|
run("nested object access", function() {
|
|
var obj = {a: {b: {c: {d: 42}}}}
|
|
if (obj.a.b.c.d != 42) fail("nested object access failed")
|
|
})
|
|
|
|
run("mixed nested access", function() {
|
|
var data = {users: [{name: "Alice"}, {name: "Bob"}]}
|
|
if (data.users[1].name != "Bob") fail("mixed nested access failed")
|
|
})
|
|
|
|
run("object with null value", function() {
|
|
var obj = {a: null, b: 2}
|
|
if (obj.a != null) fail("object null value failed")
|
|
if (!("a" in obj)) fail("object with null should have key")
|
|
})
|
|
|
|
run("array with null values", function() {
|
|
var arr = [1, null, 3]
|
|
if (arr[1] != null) fail("array null value failed")
|
|
if (length(arr) != 3) fail("array with null length failed")
|
|
})
|
|
|
|
run("function returning function", function() {
|
|
var outer = function(x) {
|
|
return function(y) {
|
|
return function(z) {
|
|
return x + y + z
|
|
}
|
|
}
|
|
}
|
|
if (outer(1)(2)(3) != 6) fail("function returning function failed")
|
|
})
|
|
|
|
run("immediately invoked function", function() {
|
|
var result = (function(x) { return x * 2 })(21)
|
|
if (result != 42) fail("immediately invoked function failed")
|
|
})
|
|
|
|
run("text split text", function() {
|
|
var t = "hello world"
|
|
var result = array(t, " ")
|
|
if (length(result) != 2) fail("text split failed")
|
|
if (result[0] != "hello") fail("text split first failed")
|
|
if (result[1] != "world") fail("text split second failed")
|
|
})
|
|
|
|
run("text split regex", function() {
|
|
var t = "hello world"
|
|
var result = array(t, /\s+/)
|
|
if (length(result) != 2) fail("text split regex failed")
|
|
if (result[0] != "hello") fail("text split regex first failed")
|
|
if (result[1] != "world") fail("text split regex second failed")
|
|
})
|
|
|
|
run("text search text", function() {
|
|
var t = "hello world"
|
|
var result = search(t, "world")
|
|
if (result != 6) fail("text search failed")
|
|
})
|
|
|
|
run("text search regex", function() {
|
|
var t = "hello world"
|
|
var result = search(t, /world/)
|
|
if (result != 6) fail("text search regex failed")
|
|
})
|
|
|
|
run("extract basic text", function() {
|
|
var t = "hello world"
|
|
var result = extract(t, "world")
|
|
if (result[0] != "world") fail("extract basic text failed")
|
|
})
|
|
|
|
run("extract text not found", function() {
|
|
var t = "hello world"
|
|
var result = extract(t, "xyz")
|
|
if (result != null) fail("extract not found should return null")
|
|
})
|
|
|
|
run("extract regex basic", function() {
|
|
var t = "hello world"
|
|
var result = extract(t, /world/)
|
|
if (result[0] != "world") fail("extract regex basic failed")
|
|
})
|
|
|
|
run("extract regex with capture group", function() {
|
|
var t = "hello world"
|
|
var result = extract(t, /(\w+) (\w+)/)
|
|
if (result[0] != "hello world") fail("extract regex full match failed")
|
|
if (result[1] != "hello") fail("extract regex capture group 1 failed")
|
|
if (result[2] != "world") fail("extract regex capture group 2 failed")
|
|
})
|
|
|
|
run("extract regex digits", function() {
|
|
var t = "abc123def456"
|
|
var result = extract(t, /(\d+)/)
|
|
if (result[0] != "123") fail("extract regex digits failed")
|
|
if (result[1] != "123") fail("extract regex digits capture failed")
|
|
})
|
|
|
|
run("extract with from", function() {
|
|
var t = "hello hello world"
|
|
var result = extract(t, "hello", 1)
|
|
if (result[0] != "hello") fail("extract with from failed")
|
|
})
|
|
|
|
run("extract with from to", function() {
|
|
var t = "hello world hello"
|
|
var result = extract(t, "hello", 0, 10)
|
|
if (result[0] != "hello") fail("extract with from to failed")
|
|
})
|
|
|
|
run("extract regex case insensitive", function() {
|
|
var t = "Hello World"
|
|
var result = extract(t, /hello/i)
|
|
if (result[0] != "Hello") fail("extract regex case insensitive failed")
|
|
})
|
|
|
|
// ============================================================================
|
|
// GC PATHOLOGICAL CASES
|
|
// ============================================================================
|
|
|
|
run("gc cycle object self", function() {
|
|
var obj = {name: "root"}
|
|
obj.self = obj
|
|
if (obj.self != obj) fail("self cycle failed")
|
|
})
|
|
|
|
run("gc cycle array self", function() {
|
|
var arr = []
|
|
for (var i = 0; i < 10; i++) {
|
|
arr[] = arr
|
|
}
|
|
if (arr[0] != arr) fail("array self cycle failed")
|
|
})
|
|
|
|
run("gc cycle object array pair", function() {
|
|
var obj = {kind: "node"}
|
|
var arr = [obj]
|
|
obj.arr = arr
|
|
if (obj.arr[0] != obj) fail("object/array cycle failed")
|
|
})
|
|
|
|
run("gc shared references", function() {
|
|
var shared = {value: 42}
|
|
var a = {ref: shared}
|
|
var b = {ref: shared}
|
|
if (a.ref != shared || b.ref != shared) fail("shared reference failed")
|
|
})
|
|
|
|
run("gc object key cycle", function() {
|
|
var k = {}
|
|
var v = {label: "value"}
|
|
var o = {}
|
|
o[k] = v
|
|
v.back = o
|
|
if (o[k].back != o) fail("object key cycle failed")
|
|
})
|
|
|
|
run("gc object text key mix", function() {
|
|
var obj = {}
|
|
var key = "alpha"
|
|
var inner = {token: "x"}
|
|
obj[key] = inner
|
|
obj["beta"] = [inner, obj]
|
|
if (obj.alpha.token != "x") fail("text key value failed")
|
|
if (obj.beta[1] != obj) fail("text key cycle failed")
|
|
})
|
|
|
|
// ============================================================================
|
|
// OBJECT INTRINSIC TESTS
|
|
// ============================================================================
|
|
|
|
run("object shallow copy", function() {
|
|
var orig = {a: 1, b: 2, c: 3}
|
|
var copy = object(orig)
|
|
if (copy.a != 1) fail("object copy a failed")
|
|
if (copy.b != 2) fail("object copy b failed")
|
|
if (copy.c != 3) fail("object copy c failed")
|
|
copy.a = 99
|
|
if (orig.a != 1) fail("object copy should not mutate original")
|
|
})
|
|
|
|
run("object combine", function() {
|
|
var obj1 = {a: 1, b: 2}
|
|
var obj2 = {c: 3, d: 4}
|
|
var combined = object(obj1, obj2)
|
|
if (combined.a != 1) fail("object combine a failed")
|
|
if (combined.b != 2) fail("object combine b failed")
|
|
if (combined.c != 3) fail("object combine c failed")
|
|
if (combined.d != 4) fail("object combine d failed")
|
|
})
|
|
|
|
run("object combine override", function() {
|
|
var obj1 = {a: 1, b: 2}
|
|
var obj2 = {b: 99, c: 3}
|
|
var combined = object(obj1, obj2)
|
|
if (combined.a != 1) fail("object combine override a failed")
|
|
if (combined.b != 99) fail("object combine should override with second arg")
|
|
if (combined.c != 3) fail("object combine override c failed")
|
|
})
|
|
|
|
run("object select keys", function() {
|
|
var orig = {a: 1, b: 2, c: 3, d: 4}
|
|
var selected = object(orig, ["a", "c"])
|
|
if (selected.a != 1) fail("object select a failed")
|
|
if (selected.c != 3) fail("object select c failed")
|
|
if (selected.b != null) fail("object select should not include b")
|
|
if (selected.d != null) fail("object select should not include d")
|
|
})
|
|
|
|
run("object from keys true", function() {
|
|
var keys = ["x", "y", "z"]
|
|
var obj = object(keys)
|
|
if (obj.x != true) fail("object from keys x failed")
|
|
if (obj.y != true) fail("object from keys y failed")
|
|
if (obj.z != true) fail("object from keys z failed")
|
|
})
|
|
|
|
run("object from keys function", function() {
|
|
var keys = ["a", "b", "c"]
|
|
var obj = object(keys, function(k) { return k + "_val" })
|
|
if (obj.a != "a_val") fail("object from keys func a failed")
|
|
if (obj.b != "b_val") fail("object from keys func b failed")
|
|
if (obj.c != "c_val") fail("object from keys func c failed")
|
|
})
|
|
|
|
// ============================================================================
|
|
// SPLAT INTRINSIC TESTS
|
|
// ============================================================================
|
|
|
|
run("splat prototype flattening", function() {
|
|
var proto = {x: 10, y: 20}
|
|
var obj = meme(proto, {z: 30})
|
|
var flat = splat(obj)
|
|
if (flat.x != 10) fail("splat x failed")
|
|
if (flat.y != 20) fail("splat y failed")
|
|
if (flat.z != 30) fail("splat z failed")
|
|
})
|
|
|
|
// ============================================================================
|
|
// REVERSE INTRINSIC TESTS (detailed)
|
|
// ============================================================================
|
|
|
|
run("reverse array detailed", function() {
|
|
var arr = [1, 2, 3, 4, 5]
|
|
var rev = reverse(arr)
|
|
if (rev[0] != 5) fail("reverse[0] failed")
|
|
if (rev[1] != 4) fail("reverse[1] failed")
|
|
if (rev[2] != 3) fail("reverse[2] failed")
|
|
if (rev[3] != 2) fail("reverse[3] failed")
|
|
if (rev[4] != 1) fail("reverse[4] failed")
|
|
if (arr[0] != 1) fail("reverse should not mutate original")
|
|
})
|
|
|
|
// ============================================================================
|
|
// APPLY INTRINSIC TESTS
|
|
// ============================================================================
|
|
|
|
run("fn.apply with array args", function() {
|
|
def sum = function(a, b, c) { return a + b + c }
|
|
var result = fn.apply(sum, [1, 2, 3])
|
|
if (result != 6) fail("apply with array args failed")
|
|
})
|
|
|
|
run("fn.apply with no args", function() {
|
|
def ret42 = function() { return 42 }
|
|
var result = fn.apply(ret42)
|
|
if (result != 42) fail("apply with no args failed")
|
|
})
|
|
|
|
run("fn.apply with single value", function() {
|
|
def double = function(x) { return x * 2 }
|
|
var result = fn.apply(double, 10)
|
|
if (result != 20) fail("apply with single value failed")
|
|
})
|
|
|
|
// ============================================================================
|
|
// GC STRESS TESTS FOR FIXED INTRINSICS
|
|
// ============================================================================
|
|
|
|
run("gc reverse under pressure", function() {
|
|
var arrays = []
|
|
for (var i = 0; i < 100; i = i + 1) {
|
|
arrays[i] = [i, i+1, i+2, i+3, i+4]
|
|
}
|
|
for (var i = 0; i < 100; i = i + 1) {
|
|
var rev = reverse(arrays[i])
|
|
if (rev[0] != i+4) fail("gc reverse stress failed")
|
|
}
|
|
})
|
|
|
|
run("gc object select under pressure", function() {
|
|
var objs = []
|
|
for (var i = 0; i < 100; i = i + 1) {
|
|
objs[i] = {a: i, b: i+1, c: i+2, d: i+3}
|
|
}
|
|
for (var i = 0; i < 100; i = i + 1) {
|
|
var selected = object(objs[i], ["a", "c"])
|
|
if (selected.a != i) fail("gc object select stress failed")
|
|
if (selected.c != i+2) fail("gc object select stress c failed")
|
|
}
|
|
})
|
|
|
|
run("gc object from keys function under pressure", function() {
|
|
var keysets = []
|
|
for (var i = 0; i < 50; i = i + 1) {
|
|
keysets[i] = [`k${i}`, `j${i}`, `m${i}`]
|
|
}
|
|
for (var i = 0; i < 50; i = i + 1) {
|
|
var obj = object(keysets[i], function(k) { return k + "_value" })
|
|
var expected = `k${i}_value`
|
|
if (obj[`k${i}`] != expected) fail("gc object from keys func stress failed")
|
|
}
|
|
})
|
|
|
|
// ============================================================================
|
|
// DEFAULT PARAMETER TESTS
|
|
// ============================================================================
|
|
|
|
run("default param constant", function() {
|
|
var f = function(a, b=10) { return a + b }
|
|
assert_eq(f(1), 11, "default param constant")
|
|
})
|
|
|
|
run("default param overridden", function() {
|
|
var f = function(a, b=10) { return a + b }
|
|
assert_eq(f(1, 2), 3, "default param overridden")
|
|
})
|
|
|
|
run("default param uses earlier param", function() {
|
|
var f = function(a, b=a+1) { return b }
|
|
assert_eq(f(5), 6, "default param uses earlier param")
|
|
})
|
|
|
|
run("default param uses earlier param overridden", function() {
|
|
var f = function(a, b=a+1) { return b }
|
|
assert_eq(f(5, 20), 20, "default param uses earlier param overridden")
|
|
})
|
|
|
|
run("multiple default params", function() {
|
|
var f = function(a, b=10, c=a+1) { return a + b + c }
|
|
assert_eq(f(1), 13, "multiple defaults f(1)")
|
|
assert_eq(f(1, 2), 5, "multiple defaults f(1,2)")
|
|
assert_eq(f(1, 2, 3), 6, "multiple defaults f(1,2,3)")
|
|
})
|
|
|
|
run("arrow function default param", function() {
|
|
var g = (x, y=100) => x + y
|
|
assert_eq(g(5), 105, "arrow default param")
|
|
assert_eq(g(5, 20), 25, "arrow default param overridden")
|
|
})
|
|
|
|
run("default param null passed explicitly", function() {
|
|
var f = function(a, b=10) { return b }
|
|
assert_eq(f(1, null), 10, "explicit null triggers default")
|
|
})
|
|
|
|
run("default param with string", function() {
|
|
var f = function(a, b="hello") { return b }
|
|
assert_eq(f(1), "hello", "default string param")
|
|
assert_eq(f(1, "world"), "world", "default string overridden")
|
|
})
|
|
|
|
run("default param first param has no default", function() {
|
|
var f = function(a, b=42) { return a }
|
|
assert_eq(f(7), 7, "first param no default")
|
|
})
|
|
|
|
// ============================================================================
|
|
// FUNCTINO TESTS
|
|
// ============================================================================
|
|
|
|
run("functino +! addition", function() {
|
|
assert_eq(+!(3, 4), 7, "+! addition")
|
|
})
|
|
|
|
run("functino -! subtraction", function() {
|
|
assert_eq(-!(10, 3), 7, "-! subtraction")
|
|
})
|
|
|
|
run("functino *! multiplication", function() {
|
|
assert_eq(*!(5, 6), 30, "*! multiplication")
|
|
})
|
|
|
|
run("functino /! division", function() {
|
|
assert_eq(/!(10, 2), 5, "/! division")
|
|
})
|
|
|
|
run("functino %! modulo", function() {
|
|
assert_eq(%!(10, 3), 1, "%! modulo")
|
|
})
|
|
|
|
run("functino **! power", function() {
|
|
assert_eq(**!(2, 10), 1024, "**! power")
|
|
})
|
|
|
|
run("functino <! less than", function() {
|
|
assert_eq(<!(3, 5), true, "<! true")
|
|
assert_eq(<!(5, 3), false, "<! false")
|
|
assert_eq(<!(3, 3), false, "<! equal")
|
|
})
|
|
|
|
run("functino >! greater than", function() {
|
|
assert_eq(>!(5, 3), true, ">! true")
|
|
assert_eq(>!(3, 5), false, ">! false")
|
|
assert_eq(>!(3, 3), false, ">! equal")
|
|
})
|
|
|
|
run("functino <=! less or equal", function() {
|
|
assert_eq(<=!(3, 5), true, "<=! less")
|
|
assert_eq(<=!(3, 3), true, "<=! equal")
|
|
assert_eq(<=!(5, 3), false, "<=! greater")
|
|
})
|
|
|
|
run("functino >=! greater or equal", function() {
|
|
assert_eq(>=!(5, 3), true, ">=! greater")
|
|
assert_eq(>=!(3, 3), true, ">=! equal")
|
|
assert_eq(>=!(3, 5), false, ">=! less")
|
|
})
|
|
|
|
run("functino =! equality", function() {
|
|
assert_eq(=!(5, 5), true, "=! true")
|
|
assert_eq(=!(5, 3), false, "=! false")
|
|
})
|
|
|
|
run("functino !=! inequality", function() {
|
|
assert_eq(!=!(5, 3), true, "!=! true")
|
|
assert_eq(!=!(5, 5), false, "!=! false")
|
|
})
|
|
|
|
run("functino =! with tolerance", function() {
|
|
assert_eq(=!(1.0, 1.0001, 0.001), true, "=! within tolerance")
|
|
assert_eq(=!(1.0, 1.01, 0.001), false, "=! outside tolerance")
|
|
})
|
|
|
|
run("functino !=! with tolerance", function() {
|
|
assert_eq(!=!(1.0, 1.01, 0.001), true, "!=! outside tolerance")
|
|
assert_eq(!=!(1.0, 1.0001, 0.001), false, "!=! within tolerance")
|
|
})
|
|
|
|
run("functino &! bitwise and", function() {
|
|
assert_eq(&!(0xff, 0x0f), 0x0f, "&! bitwise and")
|
|
})
|
|
|
|
run("functino |! bitwise or", function() {
|
|
assert_eq(|!(0xf0, 0x0f), 0xff, "|! bitwise or")
|
|
})
|
|
|
|
run("functino ^! bitwise xor", function() {
|
|
assert_eq(^!(0xff, 0x0f), 0xf0, "^! bitwise xor")
|
|
})
|
|
|
|
run("functino <<! shift left", function() {
|
|
assert_eq(<<!(1, 4), 16, "<<! shift left")
|
|
})
|
|
|
|
run("functino >>! shift right", function() {
|
|
assert_eq(>>!(16, 4), 1, ">>! shift right")
|
|
})
|
|
|
|
run("functino ~! bitwise not", function() {
|
|
assert_eq(~!(0), -1, "~! bitwise not 0")
|
|
})
|
|
|
|
run("functino []! array index", function() {
|
|
var arr = [10, 20, 30]
|
|
assert_eq([]!(arr, 0), 10, "[]! index 0")
|
|
assert_eq([]!(arr, 1), 20, "[]! index 1")
|
|
assert_eq([]!(arr, 2), 30, "[]! index 2")
|
|
})
|
|
|
|
run("functino &&! logical and", function() {
|
|
assert_eq(&&!(true, true), true, "&&! true true")
|
|
assert_eq(&&!(true, false), false, "&&! true false")
|
|
assert_eq(&&!(false, true), false, "&&! false true")
|
|
assert_eq(&&!(1, 2), 2, "&&! truthy returns right")
|
|
assert_eq(&&!(0, 2), 0, "&&! falsy returns left")
|
|
})
|
|
|
|
run("functino ||! logical or", function() {
|
|
assert_eq(||!(false, true), true, "||! false true")
|
|
assert_eq(||!(true, false), true, "||! true false")
|
|
assert_eq(||!(false, false), false, "||! false false")
|
|
assert_eq(||!(0, 5), 5, "||! falsy returns right")
|
|
assert_eq(||!(3, 5), 3, "||! truthy returns left")
|
|
})
|
|
|
|
run("functino >>>! unsigned shift right", function() {
|
|
assert_eq(>>>!(-1, 28), 15, ">>>! unsigned shift right")
|
|
})
|
|
|
|
// ============================================================================
|
|
// SUMMARY
|
|
// ============================================================================
|
|
|
|
print(text(passed) + " passed, " + text(failed) + " failed out of " + text(passed + failed))
|
|
if (failed > 0) {
|
|
print("")
|
|
for (var _j = 0; _j < failed; _j++) {
|
|
print(" FAIL " + error_names[_j] + ": " + error_reasons[_j])
|
|
}
|
|
}
|