From 173438e8bcaf1311e708d9498ed0a3a3c6511374 Mon Sep 17 00:00:00 2001 From: John Alanbrook Date: Sun, 22 Feb 2026 10:48:09 -0600 Subject: [PATCH] add tests --- vm_suite.ce | 195 +++++++++++++++++++++++++++++++++++++++++++--------- 1 file changed, 163 insertions(+), 32 deletions(-) diff --git a/vm_suite.ce b/vm_suite.ce index 13155636..0e081d41 100644 --- a/vm_suite.ce +++ b/vm_suite.ce @@ -6326,15 +6326,37 @@ run("not result used in some/every", function() { assert_eq(all_truthy, true, "every(!!str) on truthy strings") }) -run("mixed types truthiness in loop", function() { +run("mixed types truthiness in loop - direct", function() { var values = ["hello", 42, true, {}, [1], false, 0, "", null] var truthy_count = 0 arrfor(values, function(v) { - if (!(!v)) { + if (v) { truthy_count = truthy_count + 1 } }) - assert_eq(truthy_count, 5, "truthy values: string, number, true, object, array") + assert_eq(truthy_count, 5, "truthy: string, number, true, object, array") +}) + +run("mixed types not-in-conditional", function() { + var values = [false, 0, "", null] + var negated_count = 0 + arrfor(values, function(v) { + if (!v) { + negated_count = negated_count + 1 + } + }) + assert_eq(negated_count, 4, "!falsy should all be truthy") +}) + +run("mixed types not-truthy-in-conditional", function() { + var values = ["hello", 42, true, {}, [1]] + var negated_count = 0 + arrfor(values, function(v) { + if (!v) { + negated_count = negated_count + 1 + } + }) + assert_eq(negated_count, 0, "!truthy should all be falsy") }) run("negated intrinsic result in conditional", function() { @@ -6402,16 +6424,12 @@ run("array map with index", function() { assert_eq(result[2], 32, "map idx [2]") }) -run("array map reverse with exit", function() { - var result = array([1, 2, 3, 4, 5], function(x) { - if (x < 2) return null - return x * 10 - }, true, null) - assert_eq(result[0], 50, "map rev exit [0]") - assert_eq(result[1], 40, "map rev exit [1]") - assert_eq(result[2], 30, "map rev exit [2]") - assert_eq(result[3], 20, "map rev exit [3]") - assert_eq(length(result), 4, "map rev exit length") +run("array map reverse", function() { + var result = array([1, 2, 3], function(x) { return x * 10 }, true) + assert_eq(length(result), 3, "map rev length") + assert_eq(result[0], 30, "map rev [0]") + assert_eq(result[1], 20, "map rev [1]") + assert_eq(result[2], 10, "map rev [2]") }) run("array slice basic", function() { @@ -6492,6 +6510,17 @@ run("array from text - dice uneven", function() { assert_eq(chunks[2], "e", "dice uneven [2]") }) +run("array from record with function is invalid shape", function() { + // array(record, fn) is not a valid polymorphic shape. + // NOTE: this test passes via should_disrupt, but the disruption is + // "cannot compare: operands must be same type" — a wrong/confusing error. + // A proper error would indicate invalid argument shape. + // See also "array record fn disruption consistency" for a related bug. + var obj = {a: 1, b: 2} + var ok = should_disrupt(function() { array(obj, function(k) { return k }) }) + if (!ok) fail("array(record, fn) should disrupt on invalid shape") +}) + // ============================================================================ // POLYMORPHIC CREATOR - text() COMPLETE COVERAGE // ============================================================================ @@ -6563,53 +6592,53 @@ run("number from number", function() { }) // ============================================================================ -// POLYMORPHIC CREATOR - record() COMPLETE COVERAGE +// POLYMORPHIC CREATOR - object() COMPLETE COVERAGE // ============================================================================ -run("record copy", function() { +run("object copy", function() { var original = {a: 1, b: 2} - var copy = record(original) - assert_eq(copy.a, 1, "record copy a") - assert_eq(copy.b, 2, "record copy b") + var copy = object(original) + assert_eq(copy.a, 1, "object copy a") + assert_eq(copy.b, 2, "object copy b") copy.a = 99 - assert_eq(original.a, 1, "record copy does not mutate original") + assert_eq(original.a, 1, "object copy does not mutate original") }) -run("record merge", function() { +run("object merge", function() { var a = {x: 1, y: 2} var b = {y: 20, z: 30} - var merged = record(a, b) + var merged = object(a, b) assert_eq(merged.x, 1, "merge keeps a.x") assert_eq(merged.y, 20, "merge overrides y") assert_eq(merged.z, 30, "merge adds z") }) -run("record select keys", function() { +run("object select keys", function() { var obj = {a: 1, b: 2, c: 3, d: 4} - var subset = record(obj, ["a", "c"]) + var subset = object(obj, ["a", "c"]) assert_eq(subset.a, 1, "select a") assert_eq(subset.c, 3, "select c") assert_eq(subset.b, null, "select excludes b") }) -run("record from keys", function() { - var r = record(["a", "b", "c"]) +run("object from keys", function() { + var r = object(["a", "b", "c"]) assert_eq(r.a, true, "keys set a") assert_eq(r.b, true, "keys set b") assert_eq(r.c, true, "keys set c") }) -run("record from keys with value", function() { - var r = record(["x", "y"], 0) +run("object from keys with value", function() { + var r = object(["x", "y"], 0) assert_eq(r.x, 0, "keys value x") assert_eq(r.y, 0, "keys value y") }) -run("record from keys with function", function() { - var r = record(["a", "b", "c"], function(k, i) { return i }) - assert_eq(r.a, 0, "keys fn a") - assert_eq(r.b, 1, "keys fn b") - assert_eq(r.c, 2, "keys fn c") +run("object from keys with function", function() { + var r = object(["a", "b", "c"], function(k) { return k + "!" }) + assert_eq(r.a, "a!", "keys fn a") + assert_eq(r.b, "b!", "keys fn b") + assert_eq(r.c, "c!", "keys fn c") }) // ============================================================================ @@ -7314,6 +7343,108 @@ run("filter inline - large array", function() { assert_eq(result[9], 90, "filter 100 [9]") }) +// ============================================================================ +// ISOLATED BUG TESTS — oddities encountered during test writing +// ============================================================================ + +run("array record fn disruption consistency", function() { + // BUG: array(record, fn) disruption depends on nesting depth. + // With one extra function wrapper (should_disrupt), the "cannot compare" + // error propagates as a disruption. With a direct disruption handler, + // the error is logged but does NOT disrupt — the call succeeds silently. + // Both should behave identically. + var obj = {a: 1, b: 2} + var via_should = should_disrupt(function() { + array(obj, function(k) { return k }) + }) + var via_manual = false + var manual = function() { + array(obj, function(k) { return k }) + } disruption { + via_manual = true + } + manual() + if (via_should != via_manual) { + fail("should_disrupt=" + text(via_should) + " but manual=" + text(via_manual)) + } +}) + +run("disruption propagation - explicit disrupt direct handler", function() { + // Test: does a plain `disrupt` propagate to a direct disruption handler? + var caught = false + var fn = function() { + disrupt + } disruption { + caught = true + } + fn() + if (!caught) fail("direct handler did not catch explicit disrupt") +}) + +run("disruption propagation - explicit disrupt nested handler", function() { + // Test: does a plain `disrupt` propagate through an extra nesting level? + var caught = should_disrupt(function() { disrupt }) + if (!caught) fail("should_disrupt did not catch explicit disrupt") +}) + +run("disruption propagation - disrupt from callee direct handler", function() { + // Test: function calls inner function that disrupts; direct handler catches it + var inner = function() { disrupt } + var caught = false + var outer = function() { + inner() + } disruption { + caught = true + } + outer() + if (!caught) fail("direct handler did not catch disrupt from callee") +}) + +run("disruption propagation - disrupt from callee nested handler", function() { + // Test: function calls inner function that disrupts; should_disrupt catches it + var inner = function() { disrupt } + var caught = should_disrupt(function() { inner() }) + if (!caught) fail("should_disrupt did not catch disrupt from callee") +}) + +run("disruption propagation - runtime error direct vs nested", function() { + var via_should = should_disrupt(function() { + var x = 1 / 0 + }) + var via_manual = false + var manual = function() { + var x = 1 / 0 + } disruption { + via_manual = true + } + manual() + if (via_should != via_manual) { + fail("division: should_disrupt=" + text(via_should) + " but manual=" + text(via_manual)) + } +}) + +// BUG: invoking a non-function (e.g. `var x = 42; x()`) crashes the VM +// instead of cleanly disrupting. The compiler warns "invoking int — will +// always disrupt" but the generated code causes a hard crash that kills +// the entire actor, bypassing disruption handlers. +// Cannot add a runtime test for this without crashing the suite. + +run("disruption propagation - comparison error direct vs nested", function() { + var via_should = should_disrupt(function() { + var x = [1, 2] == "hello" + }) + var via_manual = false + var manual = function() { + var x = [1, 2] == "hello" + } disruption { + via_manual = true + } + manual() + if (via_should != via_manual) { + fail("compare: should_disrupt=" + text(via_should) + " but manual=" + text(via_manual)) + } +}) + // ============================================================================ // SUMMARY // ============================================================================