Bob/tests/test_try_catch_extensive.bob
2025-08-11 18:26:41 -04:00

96 lines
2.9 KiB
Plaintext

print("\n--- Test: try/catch/finally (extensive) ---");
// 1) Basic catch and finally order
var steps = [];
try { steps.push("A"); throw {"message":"err"}; } catch (e) { steps.push("B:" + e.message); } finally { steps.push("C"); }
assert(steps.len() == 3, "basic order length");
assert(steps[0] == "A", "order A");
assert(steps[1] == "B:err", "order B");
assert(steps[2] == "C", "order C");
// 2) No catch; finally runs; thrown propagates
var caughtOuter = false; var fin = [];
try {
try { fin.push("try"); throw {"message":"up"}; } finally { fin.push("finally"); }
} catch (e) { caughtOuter = (e.message == "up"); }
assert(caughtOuter == true, "outer caught propagated throw");
assert(fin.len() == 2 && fin[1] == "finally", "finally executed without catch");
// 3) Rethrow from catch
caughtOuter = false;
try {
try { throw {"message":"boom"}; }
catch (e) { throw e; }
} catch (e) { caughtOuter = (e.message == "boom"); }
assert(caughtOuter == true, "rethrow works");
// 4) finally overriding return
func f() {
try { return 1; } finally { return 2; }
}
assert(f() == 2, "finally overrides return");
// 5) finally overriding throw
func g() {
try { throw {"message":"x"}; } finally { return 3; }
}
assert(g() == 3, "finally overrides throw with return");
// 6) Loop with continue/break inside finally
var i = 0; var seq = [];
while (i < 3) {
try { i = i + 1; seq.push(i); }
finally {
if (i < 3) continue;
}
}
assert(i == 3 && seq.len() == 3, "finally continue in loop");
i = 0; seq = [];
while (true) {
try { i = i + 1; seq.push(i); if (i == 2) throw {"message":"stop"}; }
catch (e) { seq.push("caught:" + e.message); }
finally {
if (i >= 2) break;
}
}
assert(seq.len() == 3 && seq[2] == "caught:stop", "finally break in loop");
// 7) Throw from method and catch in caller
class T { func f(x) { if (x < 0) throw {"message":"neg"}; return x; } }
var t = T();
var ok = 0;
try { ok = t.f(5); } catch (e) { ok = -1; }
var got = "";
try { t.f(-1); } catch (e) { got = e.message; }
assert(ok == 5 && got == "neg", "throw from method");
// 8) Throw non-dict value
var t = "";
try { throw "oops"; } catch (e) { t = type(e) + ":" + e; }
assert(t == "string:oops", "throw non-dict");
// 9) Nested try with inner catch consuming error
var mark = [];
try {
try { throw {"message":"inner"}; }
catch (e) { mark.push(e.message); }
} catch (e) { mark.push("outer:" + e.message); }
assert(mark.len() == 1 && mark[0] == "inner", "inner catch consumes");
// 10) Method/extension throws and is caught
class P { func bad() { throw {"message":"m"}; } }
var p = P();
var mmsg = "";
try { p.bad(); } catch (e) { mmsg = e.message; }
assert(mmsg == "m", "method throw caught");
extension array { func thrower() { throw {"message":"arr"}; } }
var a = [1]; var amsg = "";
try { a.thrower(); } catch (e) { amsg = e.message; }
assert(amsg == "arr", "extension throw caught");
print("try/catch/finally extensive: PASS");