// Run test.sx directly against sx-browser.js. // // sx-browser.js parses and evaluates test.sx — SX tests itself. // This script provides only platform functions (error catching, reporting). // // Usage: node shared/sx/tests/run.js Object.defineProperty(globalThis, "document", { value: undefined, writable: true }); var path = require("path"); var fs = require("fs"); var Sx = require(path.resolve(__dirname, "../../static/scripts/sx-browser.js")); // --- Test state --- var suiteStack = []; var passed = 0, failed = 0, testNum = 0; // --- Helpers --- function isNil(x) { return x === Sx.NIL || x === null || x === undefined; } function deepEqual(a, b) { if (a === b) return true; if (isNil(a) && isNil(b)) return true; if (typeof a !== typeof b) return false; if (Array.isArray(a) && Array.isArray(b)) { if (a.length !== b.length) return false; for (var i = 0; i < a.length; i++) if (!deepEqual(a[i], b[i])) return false; return true; } if (a && typeof a === "object" && b && typeof b === "object") { var ka = Object.keys(a), kb = Object.keys(b); if (ka.length !== kb.length) return false; for (var j = 0; j < ka.length; j++) if (!deepEqual(a[ka[j]], b[ka[j]])) return false; return true; } return false; } // --- Platform functions injected into the SX env --- var env = { // Error catching — calls an SX thunk, returns result dict "try-call": function(thunk) { try { Sx.eval([thunk], env); return { ok: true }; } catch(e) { return { ok: false, error: e.message || String(e) }; } }, // Test reporting "report-pass": function(name) { testNum++; passed++; var fullName = suiteStack.concat([name]).join(" > "); console.log("ok " + testNum + " - " + fullName); }, "report-fail": function(name, error) { testNum++; failed++; var fullName = suiteStack.concat([name]).join(" > "); console.log("not ok " + testNum + " - " + fullName); console.log(" # " + error); }, // Suite context "push-suite": function(name) { suiteStack.push(name); }, "pop-suite": function() { suiteStack.pop(); }, // Primitives that sx-browser.js has internally but doesn't expose through env "equal?": function(a, b) { return deepEqual(a, b); }, "eq?": function(a, b) { return a === b; }, "boolean?": function(x) { return typeof x === "boolean"; }, "string-length": function(s) { return String(s).length; }, "substring": function(s, start, end) { return String(s).slice(start, end); }, "string-contains?": function(s, needle) { return String(s).indexOf(needle) !== -1; }, "upcase": function(s) { return String(s).toUpperCase(); }, "downcase": function(s) { return String(s).toLowerCase(); }, "reverse": function(c) { return c ? c.slice().reverse() : []; }, "flatten": function(c) { var r = []; for (var i = 0; i < (c||[]).length; i++) { if (Array.isArray(c[i])) for (var j = 0; j < c[i].length; j++) r.push(c[i][j]); else r.push(c[i]); } return r; }, "has-key?": function(d, k) { return d && typeof d === "object" && k in d; }, "append": function(c, x) { return Array.isArray(x) ? (c||[]).concat(x) : (c||[]).concat([x]); }, }; // --- Read and evaluate test.sx --- var src = fs.readFileSync(path.resolve(__dirname, "../ref/test.sx"), "utf8"); var exprs = Sx.parseAll(src); console.log("TAP version 13"); for (var i = 0; i < exprs.length; i++) { Sx.eval(exprs[i], env); } // --- Summary --- console.log(""); console.log("1.." + testNum); console.log("# tests " + (passed + failed)); console.log("# pass " + passed); if (failed > 0) { console.log("# fail " + failed); process.exit(1); }