git » fjorth.git » master » tree

[master] / fjorth.html

<!doctype html>
<html>
<head>
  <meta charset="utf-8" />
  <title>Fjorth — QUnit Tests</title>
  <!-- Prefer local QUnit assets to avoid network prompts; if unavailable, swap to CDN manually -->
  <link rel="stylesheet" href="qunit.css">
</head>
<body>
  <div id="qunit"></div>
  <div id="qunit-fixture"></div>

  <!-- Interpreter (callback errors) -->
  <script src="fjorth.js"></script>

  <!-- QUnit (local preferred) -->
  <script id="qunit-js" src="qunit.js"></script>

  <!-- Tests (ported to QUnit) -->
  <script src="test.js"> </script>

  <!-- Prefer local QUnit script; provide CDN fallback comment if needed -->
  <!-- If local qunit.js isn't available in your environment, replace the line above with:
  <script id="qunit-js" src="https://app.unpkg.com/qunit@2.24.1/files/qunit/qunit.js"></script>
  -->
  <!-- Additional tests: Structured Error Handling -->
  <script>
  (function(){
    function runCodeExpectErr(code, vmOpts={}) {
      return new Promise((resolve) => {
        const seen=[];
        const vm = new Fjorth(Object.assign({ onError: e => seen.push(e), stepBudget: 2000, sliceMs: 0 }, vmOpts));
        try {
          vm.reset();
          vm.load(code);
          vm.run((err) => {
            resolve({ vm, err, seen });
          });
        } catch (e) {
          resolve({ vm, err: e, seen: [...seen, e] });
        }
      });
    }

    function startErrTests(){
      QUnit.module('Structured Error Handling (callback API)');

      QUnit.test('undefined word: onError + idle get same Error with rich fields', async assert => {
        const { vm, err, seen } = await runCodeExpectErr('NOPE');
        assert.ok(err instanceof Error, 'idle callback receives Error');
        assert.strictEqual(seen.length, 1, 'onError called once');
        assert.strictEqual(seen[0], err, 'onError error === idle error');
        assert.strictEqual(err.code, 'undefined_word');
        assert.strictEqual(err.phase, 'interpret');
        assert.strictEqual(err.token, 'NOPE');
        assert.strictEqual(vm.lastError, err, 'vm.lastError points to same error');
        assert.strictEqual(vm.running, false, 'vm halted');
      });

      QUnit.test('stack underflow: structured exec error and halt, no further tokens executed', async assert => {
        const { vm, err } = await runCodeExpectErr('DUP 1 2 +');
        assert.strictEqual(err.code, 'stack_underflow');
        assert.strictEqual(err.phase, 'exec');
        assert.ok(typeof err.stackDepth === 'number', 'err.stackDepth is number');
        assert.ok(typeof err.rstackDepth === 'number', 'err.rstackDepth is number');
        assert.ok(typeof err.ip === 'number' || typeof err.ip === 'undefined', 'err.ip present or undefined');
        assert.strictEqual(vm.D.length, 0, 'no subsequent execution after error');
      });

      QUnit.test('rstack underflow: structured exec error', async assert => {
        const { err } = await runCodeExpectErr('R>');
        assert.strictEqual(err.code, 'rstack_underflow');
        assert.strictEqual(err.phase, 'exec');
      });

      QUnit.test('compile-time misuse: ELSE outside compile', async assert => {
        const { err } = await runCodeExpectErr('ELSE');
        assert.strictEqual(err.code, 'else_outside_compile');
        assert.strictEqual(err.phase, 'compile');
      });

      QUnit.test('compile-time: ":" with no name', async assert => {
        const { err } = await runCodeExpectErr(':');
        assert.strictEqual(err.code, 'colon_no_name');
        assert.strictEqual(err.phase, 'compile');
      });

      QUnit.test('errors do not throw synchronously from run()', assert => {
        assert.expect(1);
        const vm = new Fjorth({});
        vm.reset();
        vm.load('NOPE');
        let threw = false;
        try { vm.run(() => {}); } catch (e) { threw = true; }
        assert.strictEqual(threw, false, 'no synchronous throw');
      });
    }

    if (window.QUnit) {
      startErrTests();
    } else {
      window.addEventListener('load', () => { if (window.QUnit) startErrTests(); });
    }
  })();
  </script>
</body>
</html>