diff --git a/internal/kim.c b/internal/kim.c index b2c53111..99ad7166 100644 --- a/internal/kim.c +++ b/internal/kim.c @@ -75,7 +75,11 @@ static const JSCFunctionListEntry js_kim_funcs[] = { JSValue js_kim_use(JSContext *js) { - JSValue mod = JS_NewObject(js); - JS_SetPropertyFunctionList(js, mod, js_kim_funcs, countof(js_kim_funcs)); - return mod; + JSGCRef mod_ref; + JS_PushGCRef(js, &mod_ref); + mod_ref.val = JS_NewObject(js); + JS_SetPropertyFunctionList(js, mod_ref.val, js_kim_funcs, countof(js_kim_funcs)); + JSValue result = mod_ref.val; + JS_PopGCRef(js, &mod_ref); + return result; } \ No newline at end of file diff --git a/internal/os.c b/internal/os.c index 7c6e8538..bbf47efe 100644 --- a/internal/os.c +++ b/internal/os.c @@ -578,8 +578,12 @@ static const JSCFunctionListEntry js_os_funcs[] = { JSValue js_os_use(JSContext *js) { JS_NewClassID(&js_dylib_class_id); JS_NewClass(js, js_dylib_class_id, &js_dylib_class); - - JSValue mod = JS_NewObject(js); - JS_SetPropertyFunctionList(js,mod,js_os_funcs,countof(js_os_funcs)); - return mod; + + JSGCRef mod_ref; + JS_PushGCRef(js, &mod_ref); + mod_ref.val = JS_NewObject(js); + JS_SetPropertyFunctionList(js, mod_ref.val, js_os_funcs, countof(js_os_funcs)); + JSValue result = mod_ref.val; + JS_PopGCRef(js, &mod_ref); + return result; } diff --git a/internal/os_emscripten.c b/internal/os_emscripten.c index e3e6f83a..1e5cd847 100644 --- a/internal/os_emscripten.c +++ b/internal/os_emscripten.c @@ -172,7 +172,11 @@ static const JSCFunctionListEntry js_os_funcs[] = { }; JSValue js_os_use(JSContext *js) { - JSValue mod = JS_NewObject(js); - JS_SetPropertyFunctionList(js, mod, js_os_funcs, countof(js_os_funcs)); - return mod; + JSGCRef mod_ref; + JS_PushGCRef(js, &mod_ref); + mod_ref.val = JS_NewObject(js); + JS_SetPropertyFunctionList(js, mod_ref.val, js_os_funcs, countof(js_os_funcs)); + JSValue result = mod_ref.val; + JS_PopGCRef(js, &mod_ref); + return result; } diff --git a/internal/os_playdate.c b/internal/os_playdate.c index c7493c20..315ab2e6 100644 --- a/internal/os_playdate.c +++ b/internal/os_playdate.c @@ -172,7 +172,11 @@ static const JSCFunctionListEntry js_os_funcs[] = { }; JSValue js_os_use(JSContext *js) { - JSValue mod = JS_NewObject(js); - JS_SetPropertyFunctionList(js, mod, js_os_funcs, countof(js_os_funcs)); - return mod; + JSGCRef mod_ref; + JS_PushGCRef(js, &mod_ref); + mod_ref.val = JS_NewObject(js); + JS_SetPropertyFunctionList(js, mod_ref.val, js_os_funcs, countof(js_os_funcs)); + JSValue result = mod_ref.val; + JS_PopGCRef(js, &mod_ref); + return result; } diff --git a/internal/shop.cm b/internal/shop.cm index 6b5fd360..676e63d5 100644 --- a/internal/shop.cm +++ b/internal/shop.cm @@ -142,8 +142,10 @@ function package_in_shop(package) { function abs_path_to_package(package_dir) { - if (!fd.is_file(package_dir + '/cell.toml')) - throw Error('Not a valid package directory (no cell.toml): ' + package_dir) + if (!fd.is_file(package_dir + '/cell.toml')) { + log.error('Not a valid package directory (no cell.toml): ' + package_dir) + disrupt + } var packages_prefix = get_packages_dir() + '/' var core_dir = packages_prefix + core_package @@ -175,14 +177,15 @@ function abs_path_to_package(package_dir) return package_dir // For local directories (e.g., linked targets), read the package name from cell.toml - try { + var toml_pkg = null + var read_toml = function() { var content = text(fd.slurp(package_dir + '/cell.toml')) var cfg = toml.decode(content) if (cfg.package) - return cfg.package - } catch (e) { - // Fall through - } + toml_pkg = cfg.package + } disruption {} + read_toml() + if (toml_pkg) return toml_pkg return null } @@ -299,12 +302,14 @@ Shop.resolve_package_info = function(pkg) { // Verify if a package name is valid and return status Shop.verify_package_name = function(pkg) { - if (!pkg) throw Error("Empty package name") - if (pkg == 'local') throw Error("local is not a valid package name") - if (pkg == 'core') throw Error("core is not a valid package name") - - if (search(pkg, '://') != null) - throw Error(`Invalid package name: ${pkg}; did you mean ${array(pkg, '://')[1]}?`) + if (!pkg) { log.error("Empty package name"); disrupt } + if (pkg == 'local') { log.error("local is not a valid package name"); disrupt } + if (pkg == 'core') { log.error("core is not a valid package name"); disrupt } + + if (search(pkg, '://') != null) { + log.error(`Invalid package name: ${pkg}; did you mean ${array(pkg, '://')[1]}?`) + disrupt + } } // Convert module package to download URL @@ -441,7 +446,7 @@ ${script} // Resolve module function, hashing it in the process // path is the exact path to the script file function resolve_mod_fn(path, pkg) { - if (!fd.is_file(path)) throw Error(`path ${path} is not a file`) + if (!fd.is_file(path)) { log.error(`path ${path} is not a file`); disrupt } var file_info = Shop.file_info(path) var file_pkg = file_info.package @@ -576,32 +581,38 @@ Shop.open_package_dylib = function(pkg) { var toml_path = pkg_dir + '/cell.toml' if (fd.is_file(toml_path)) { - try { + var read_toml_disrupted = false + var do_read_toml = function() { var content = text(fd.slurp(toml_path)) var cfg = toml.decode(content) if (cfg.dependencies) { arrfor(array(cfg.dependencies), function(alias, i) { var dep_pkg = cfg.dependencies[alias] - try { + var open_dep = function() { Shop.open_package_dylib(dep_pkg) - } catch (dep_e) { - // Dependency dylib load failed, continue with others - } + } disruption {} + open_dep() }) } - } catch (e) { - // Error reading toml, continue + } disruption { + read_toml_disrupted = true } + do_read_toml() } var dl_path = get_lib_path(pkg) if (fd.is_file(dl_path)) { if (!open_dls[dl_path]) { - try { + var open_disrupted = false + var do_open = function() { open_dls[dl_path] = os.dylib_open(dl_path) - } catch (e) { + } disruption { + open_disrupted = true + } + do_open() + if (open_disrupted) { dylib_visited[pkg] = false - throw e + disrupt } } } @@ -836,14 +847,14 @@ function execute_module(info) // C only used = call_c_module(c_resolve) } else { - throw Error(`Module ${info.path} could not be found`) + log.error(`Module ${info.path} could not be found`); disrupt } // if (is_function(used)) // throw Error('C module loader returned a function; did you forget to call it?') if (!used) - throw Error(`Module ${info} returned null`) + log.error(`Module ${info} returned null`); disrupt // stone(used) return used @@ -852,16 +863,20 @@ function execute_module(info) function get_module(path, package_context) { var info = resolve_module_info(path, package_context) - if (!info) - throw Error(`Module ${path} could not be found in ${package_context}`) + if (!info) { + log.error(`Module ${path} could not be found in ${package_context}`) + disrupt + } return execute_module(info) } Shop.use = function use(path, package_context) { var info = resolve_module_info(path, package_context) - if (!info) - throw Error(`Module ${path} could not be found in ${package_context}`) + if (!info) { + log.error(`Module ${path} could not be found in ${package_context}`) + disrupt + } if (use_cache[info.cache_key]) return use_cache[info.cache_key] @@ -889,13 +904,20 @@ function fetch_remote_hash(pkg) { if (!api_url) return null - try { + var result = null + var fetch_disrupted = false + var do_fetch = function() { var resp = http.fetch(api_url) - return Shop.extract_commit_hash(pkg, text(resp)) - } catch (e) { + result = Shop.extract_commit_hash(pkg, text(resp)) + } disruption { + fetch_disrupted = true + } + do_fetch() + if (fetch_disrupted) { log.console("Warning: Could not check for updates for " + pkg) return null } + return result } // Download a zip for a package at a specific commit and cache it @@ -909,14 +931,20 @@ function download_zip(pkg, commit_hash) { return null } - try { - var zip_blob = http.fetch(download_url) + var zip_blob = null + var dl_disrupted = false + var do_download = function() { + zip_blob = http.fetch(download_url) fd.slurpwrite(cache_path, zip_blob) - return zip_blob - } catch (e) { - log.error("Download failed for " + pkg + ": " + e) + } disruption { + dl_disrupted = true + } + do_download() + if (dl_disrupted) { + log.error("Download failed for " + pkg) return null } + return zip_blob } // Get zip from cache, returns null if not cached @@ -1027,8 +1055,7 @@ Shop.extract = function(pkg) { var zip_blob = get_package_zip(pkg) - if (!zip_blob) - throw Error("No zip blob available for " + pkg) + if (!zip_blob) { log.error("No zip blob available for " + pkg); disrupt } // Extract zip for remote package install_zip(zip_blob, target_dir) @@ -1113,7 +1140,7 @@ Shop.update = function(pkg) { function install_zip(zip_blob, target_dir) { var zip = miniz.read(zip_blob) - if (!zip) throw Error("Failed to read zip archive") + if (!zip) { log.error("Failed to read zip archive"); disrupt } if (fd.is_link(target_dir)) fd.unlink(target_dir) if (fd.is_dir(target_dir)) fd.rmdir(target_dir, 1) @@ -1165,14 +1192,14 @@ Shop.get = function(pkg) { if (!lock[pkg]) { var info = Shop.resolve_package_info(pkg) if (!info) { - throw Error("Invalid package: " + pkg) + log.error("Invalid package: " + pkg); disrupt } var commit = null if (info != 'local') { commit = fetch_remote_hash(pkg) if (!commit) { - throw Error("Could not resolve commit for " + pkg) + log.error("Could not resolve commit for " + pkg); disrupt } } diff --git a/internal/testlib.cm b/internal/testlib.cm index abac1d9f..36449862 100644 --- a/internal/testlib.cm +++ b/internal/testlib.cm @@ -11,12 +11,13 @@ function is_valid_package(dir) { // Get current package name from cell.toml or null function get_current_package_name() { if (!is_valid_package('.')) return null - try { + var pkg_name = 'local' + var do_load = function() { var config = pkg.load_config(null) - return config.package || 'local' - } catch (e) { - return 'local' - } + if (config.package) pkg_name = config.package + } disruption {} + do_load() + return pkg_name } // Get the directory for a package diff --git a/link.cm b/link.cm index 42b5c6cf..f9f0b5a3 100644 --- a/link.cm +++ b/link.cm @@ -65,13 +65,18 @@ Link.load = function() { link_cache = {} return link_cache } - - try { + + var load_disrupted = false + var do_load = function() { var content = text(fd.slurp(path)) var cfg = toml.decode(content) link_cache = cfg.links || {} - } catch (e) { - log.console("Warning: Failed to load link.toml: " + e) + } disruption { + load_disrupted = true + } + do_load() + if (load_disrupted) { + log.console("Warning: Failed to load link.toml") link_cache = {} } return link_cache @@ -90,14 +95,16 @@ Link.add = function(canonical, target, shop) { // Validate canonical package exists in shop var lock = shop.load_lock() if (!lock[canonical]) { - throw Error('Package ' + canonical + ' is not installed. Install it first with: cell get ' + canonical) + log.error('Package ' + canonical + ' is not installed. Install it first with: cell get ' + canonical) + disrupt } // Validate target is a valid package if (starts_with(target, '/')) { // Local path - must have cell.toml if (!fd.is_file(target + '/cell.toml')) { - throw Error('Target ' + target + ' is not a valid package (no cell.toml)') + log.error('Target ' + target + ' is not a valid package (no cell.toml)') + disrupt } } else { // Remote package target - ensure it's installed @@ -116,7 +123,8 @@ Link.add = function(canonical, target, shop) { var target_path = starts_with(target, '/') ? target : get_package_abs_dir(target) var toml_path = target_path + '/cell.toml' if (fd.is_file(toml_path)) { - try { + var read_deps_disrupted = false + var do_read_deps = function() { var content = text(fd.slurp(toml_path)) var cfg = toml.decode(content) if (cfg.dependencies) { @@ -128,16 +136,24 @@ Link.add = function(canonical, target, shop) { return } // Install the dependency if not already in shop - try { + var install_disrupted = false + var do_install = function() { shop.get(dep_locator) shop.extract(dep_locator) - } catch (e) { - log.console(` Warning: Could not install dependency ${dep_locator}: ${e.message}`) - log.error(e) + } disruption { + install_disrupted = true + } + do_install() + if (install_disrupted) { + log.console(` Warning: Could not install dependency ${dep_locator}`) } }) } - } catch (e) { + } disruption { + read_deps_disrupted = true + } + do_read_deps() + if (read_deps_disrupted) { log.console(` Warning: Could not read dependencies from ${toml_path}`) } } @@ -149,14 +165,14 @@ Link.add = function(canonical, target, shop) { Link.remove = function(canonical) { var links = Link.load() if (!links[canonical]) return false - + // Remove the symlink if it exists var target_dir = get_package_abs_dir(canonical) if (fd.is_link(target_dir)) { fd.unlink(target_dir) log.console("Removed symlink at " + target_dir) } - + delete links[canonical] Link.save(links) log.console("Unlinked " + canonical) @@ -172,7 +188,7 @@ Link.clear = function() { fd.unlink(target_dir) } }) - + Link.save({}) log.console("Cleared all links") return true @@ -186,25 +202,25 @@ Link.sync_one = function(canonical, target, shop) { // Ensure parent directories exist var parent = fd.dirname(target_dir) ensure_dir(parent) - + // Check current state var current_link = null if (fd.is_link(target_dir)) { current_link = fd.readlink(target_dir) } - + // If already correctly linked, nothing to do if (current_link == link_target) { return true } - + // Remove existing file/dir/link if (fd.is_link(target_dir)) { fd.unlink(target_dir) } else if (fd.is_dir(target_dir)) { fd.rmdir(target_dir, 1) } - + // Create symlink fd.symlink(link_target, target_dir) return true @@ -218,7 +234,9 @@ Link.sync_all = function(shop) { arrfor(array(links), function(canonical) { var target = links[canonical] - try { + var sync_disrupted = false + var sync_error_msg = "" + var do_sync = function() { // Validate target exists var link_target = resolve_link_target(target) if (!fd.is_dir(link_target)) { @@ -234,7 +252,8 @@ Link.sync_all = function(shop) { // Install dependencies of the linked package var toml_path = link_target + '/cell.toml' - try { + var read_deps_disrupted = false + var do_read_deps = function() { var content = text(fd.slurp(toml_path)) var cfg = toml.decode(content) if (cfg.dependencies) { @@ -245,21 +264,25 @@ Link.sync_all = function(shop) { return } // Install the dependency if not already in shop - try { + var install_dep = function() { shop.get(dep_locator) shop.extract(dep_locator) - } catch (e) { - // Silently continue - dependency may already be installed - } + } disruption {} + install_dep() }) } - } catch (e) { - // Could not read dependencies - continue anyway + } disruption { + read_deps_disrupted = true } + do_read_deps() count++ - } catch (e) { - push(errors, canonical + ': ' + e.message) + } disruption { + sync_disrupted = true + } + do_sync() + if (sync_disrupted) { + push(errors, canonical + ': sync failed') } }) diff --git a/package.cm b/package.cm index 44c5fa2a..e8c5b42a 100644 --- a/package.cm +++ b/package.cm @@ -51,7 +51,8 @@ package.load_config = function(name) return config_cache[config_path] if (!fd.is_file(config_path)) { - throw Error(`${config_path} does not exist`) + log.error(`${config_path} does not exist`) + disrupt } var content = text(fd.slurp(config_path)) @@ -158,19 +159,20 @@ package.split_alias = function(name, path) var parts = array(path, '/') var first_part = parts[0] - try { + var split_result = null + var do_split = function() { var config = package.load_config(name) - if (!config) return null + if (!config) return var deps = config.dependencies if (deps && deps[first_part]) { var dep_locator = deps[first_part] var remaining_path = text(array(parts, 1), '/') - return { package: dep_locator, path: remaining_path } + split_result = { package: dep_locator, path: remaining_path } } - } catch (e) { - // Config doesn't exist or couldn't be loaded - } + } disruption {} + do_split() + if (split_result) return split_result return null } diff --git a/pronto.cm b/pronto.cm index 1c44efa4..c305499c 100644 --- a/pronto.cm +++ b/pronto.cm @@ -3,6 +3,8 @@ // Based on Douglas Crockford's parseq, adapted for Cell. // Time is in seconds. +function safe_call(fn, arg) { fn(arg) } disruption {} + function make_reason(factory, excuse, evidence) { def reason = Error(`pronto.${factory}${excuse ? ': ' + excuse : ''}`) reason.evidence = evidence @@ -15,12 +17,12 @@ function is_requestor(fn) { function check_requestors(list, factory) { if (!is_array(list) || some(list, r => !is_requestor(r))) - throw make_reason(factory, 'Bad requestor array.', list) + disrupt } function check_callback(cb, factory) { if (!is_function(cb) || length(cb) != 2) - throw make_reason(factory, 'Not a callback.', cb) + disrupt } // fallback(requestor_array) @@ -28,7 +30,7 @@ function check_callback(cb, factory) { function fallback(requestor_array) { def factory = 'fallback' if (!is_array(requestor_array) || length(requestor_array) == 0) - throw make_reason(factory, 'Empty requestor array.') + disrupt check_requestors(requestor_array, factory) return function fallback_requestor(callback, value) { @@ -40,7 +42,7 @@ function fallback(requestor_array) { function cancel(reason) { cancelled = true if (current_cancel) { - try { current_cancel(reason) } catch (_) {} + safe_call(current_cancel, reason) current_cancel = null } } @@ -55,7 +57,8 @@ function fallback(requestor_array) { def requestor = requestor_array[index] index += 1 - try { + var requestor_disrupted = false + var start_requestor = function() { current_cancel = requestor(function(val, reason) { if (cancelled) return current_cancel = null @@ -65,7 +68,11 @@ function fallback(requestor_array) { try_next() } }, value) - } catch (ex) { + } disruption { + requestor_disrupted = true + } + start_requestor() + if (requestor_disrupted) { try_next() } } @@ -80,7 +87,7 @@ function fallback(requestor_array) { function parallel(requestor_array, throttle, need) { def factory = 'parallel' if (!is_array(requestor_array)) - throw make_reason(factory, 'Not an array.', requestor_array) + disrupt check_requestors(requestor_array, factory) def length = length(requestor_array) @@ -89,10 +96,10 @@ function parallel(requestor_array, throttle, need) { if (need == null) need = length if (!is_number(need) || need < 0 || need > length) - throw make_reason(factory, 'Bad need.', need) + disrupt if (throttle != null && (!is_number(throttle) || throttle < 1)) - throw make_reason(factory, 'Bad throttle.', throttle) + disrupt return function parallel_requestor(callback, value) { check_callback(callback, factory) @@ -107,7 +114,8 @@ function parallel(requestor_array, throttle, need) { if (finished) return finished = true arrfor(cancel_list, c => { - try { if (is_function(c)) c(reason) } catch (_) {} + var do_cancel = function() { if (is_function(c)) c(reason) } disruption {} + do_cancel() }) } @@ -117,7 +125,9 @@ function parallel(requestor_array, throttle, need) { next_index += 1 def requestor = requestor_array[idx] - try { + var requestor_disrupted = false + var requestor_ex = null + var run_requestor = function() { cancel_list[idx] = requestor(function(val, reason) { if (finished) return cancel_list[idx] = null @@ -142,11 +152,15 @@ function parallel(requestor_array, throttle, need) { start_one() }, value) - } catch (ex) { + } disruption { + requestor_disrupted = true + } + run_requestor() + if (requestor_disrupted) { failures += 1 if (failures > length - need) { - cancel(ex) - callback(null, ex) + cancel(requestor_ex) + callback(null, requestor_ex) return } start_one() @@ -166,16 +180,16 @@ function parallel(requestor_array, throttle, need) { function race(requestor_array, throttle, need) { def factory = 'race' if (!is_array(requestor_array) || length(requestor_array) == 0) - throw make_reason(factory, 'Empty requestor array.') + disrupt check_requestors(requestor_array, factory) def length = length(requestor_array) if (need == null) need = 1 if (!is_number(need) || need < 1 || need > length) - throw make_reason(factory, 'Bad need.', need) + disrupt if (throttle != null && (!is_number(throttle) || throttle < 1)) - throw make_reason(factory, 'Bad throttle.', throttle) + disrupt return function race_requestor(callback, value) { check_callback(callback, factory) @@ -190,7 +204,8 @@ function race(requestor_array, throttle, need) { if (finished) return finished = true arrfor(cancel_list, c => { - try { if (is_function(c)) c(reason) } catch (_) {} + var do_cancel = function() { if (is_function(c)) c(reason) } disruption {} + do_cancel() }) } @@ -200,7 +215,9 @@ function race(requestor_array, throttle, need) { next_index += 1 def requestor = requestor_array[idx] - try { + var requestor_disrupted = false + var requestor_ex = null + var run_requestor = function() { cancel_list[idx] = requestor(function(val, reason) { if (finished) return cancel_list[idx] = null @@ -228,11 +245,15 @@ function race(requestor_array, throttle, need) { start_one() }, value) - } catch (ex) { + } disruption { + requestor_disrupted = true + } + run_requestor() + if (requestor_disrupted) { failures += 1 if (failures > length - need) { - cancel(ex) - callback(null, ex) + cancel(requestor_ex) + callback(null, requestor_ex) return } start_one() @@ -251,7 +272,7 @@ function race(requestor_array, throttle, need) { function sequence(requestor_array) { def factory = 'sequence' if (!is_array(requestor_array)) - throw make_reason(factory, 'Not an array.', requestor_array) + disrupt check_requestors(requestor_array, factory) if (length(requestor_array) == 0) @@ -266,7 +287,7 @@ function sequence(requestor_array) { function cancel(reason) { cancelled = true if (current_cancel) { - try { current_cancel(reason) } catch (_) {} + safe_call(current_cancel, reason) current_cancel = null } } @@ -281,7 +302,9 @@ function sequence(requestor_array) { def requestor = requestor_array[index] index += 1 - try { + var requestor_disrupted = false + var requestor_ex = null + var run_requestor = function() { current_cancel = requestor(function(result, reason) { if (cancelled) return current_cancel = null @@ -291,8 +314,12 @@ function sequence(requestor_array) { run_next(result) } }, val) - } catch (ex) { - callback(null, ex) + } disruption { + requestor_disrupted = true + } + run_requestor() + if (requestor_disrupted) { + callback(null, requestor_ex) } } @@ -306,15 +333,21 @@ function sequence(requestor_array) { function requestorize(unary) { def factory = 'requestorize' if (!is_function(unary)) - throw make_reason(factory, 'Not a function.', unary) + disrupt return function requestorized(callback, value) { check_callback(callback, factory) - try { + var call_disrupted = false + var call_ex = null + var do_call = function() { def result = unary(value) callback(result == null ? true : result) - } catch (ex) { - callback(null, ex) + } disruption { + call_disrupted = true + } + do_call() + if (call_disrupted) { + callback(null, call_ex) } } } diff --git a/source/cell.c b/source/cell.c index 16ad612a..9eef6894 100644 --- a/source/cell.c +++ b/source/cell.c @@ -210,31 +210,57 @@ void script_startup(cell_rt *prt) return; } - // Create hidden environment - JSValue hidden_env = JS_NewObject(js); - JS_SetPropertyStr(js, hidden_env, "os", js_os_use(js)); - JS_SetPropertyStr(js, hidden_env, "json", js_json_use(js)); - JS_SetPropertyStr(js, hidden_env, "nota", js_nota_use(js)); - JS_SetPropertyStr(js, hidden_env, "wota", js_wota_use(js)); + // Create hidden environment (GC-protected to survive allocations) + JSGCRef env_ref; + JS_PushGCRef(js, &env_ref); + env_ref.val = JS_NewObject(js); + + JSGCRef os_ref, json_ref, nota_ref, wota_ref; + JS_PushGCRef(js, &os_ref); + JS_PushGCRef(js, &json_ref); + JS_PushGCRef(js, ¬a_ref); + JS_PushGCRef(js, &wota_ref); + + // Create and stone each module to make them GC-immovable + os_ref.val = js_os_use(js); + os_ref.val = JS_Stone(js, os_ref.val); + json_ref.val = js_json_use(js); + json_ref.val = JS_Stone(js, json_ref.val); + nota_ref.val = js_nota_use(js); + nota_ref.val = JS_Stone(js, nota_ref.val); + wota_ref.val = js_wota_use(js); + wota_ref.val = JS_Stone(js, wota_ref.val); + + JS_SetPropertyStr(js, env_ref.val, "os", os_ref.val); + JS_SetPropertyStr(js, env_ref.val, "json", json_ref.val); + JS_SetPropertyStr(js, env_ref.val, "nota", nota_ref.val); + JS_SetPropertyStr(js, env_ref.val, "wota", wota_ref.val); + + JS_PopGCRef(js, &wota_ref); + JS_PopGCRef(js, ¬a_ref); + JS_PopGCRef(js, &json_ref); + JS_PopGCRef(js, &os_ref); crt->actor_sym_ref.val = JS_NewObject(js); - JS_SetPropertyStr(js, hidden_env, "actorsym", JS_DupValue(js, crt->actor_sym_ref.val)); + JS_SetPropertyStr(js, env_ref.val, "actorsym", JS_DupValue(js, crt->actor_sym_ref.val)); - // Always set init (even if null) if (crt->init_wota) { - JS_SetPropertyStr(js, hidden_env, "init", wota2value(js, crt->init_wota)); + JSValue init_val = wota2value(js, crt->init_wota); + JS_SetPropertyStr(js, env_ref.val, "init", init_val); free(crt->init_wota); crt->init_wota = NULL; } else { - JS_SetPropertyStr(js, hidden_env, "init", JS_NULL); + JS_SetPropertyStr(js, env_ref.val, "init", JS_NULL); } if (core_path) { - JS_SetPropertyStr(js, hidden_env, "core_path", JS_NewString(js, core_path)); + JSValue path_val = JS_NewString(js, core_path); + JS_SetPropertyStr(js, env_ref.val, "core_path", path_val); } // Stone the environment - hidden_env = JS_Stone(js, hidden_env); + JSValue hidden_env = JS_Stone(js, env_ref.val); + JS_PopGCRef(js, &env_ref); // Run through MACH VM crt->state = ACTOR_RUNNING; diff --git a/test.ce b/test.ce index f92174d4..805461a6 100644 --- a/test.ce +++ b/test.ce @@ -29,12 +29,13 @@ function is_valid_package(dir) { // Get current package name from cell.toml or null function get_current_package_name() { if (!is_valid_package('.')) return null - try { + var pkg_name = 'local' + var do_load = function() { var config = pkg.load_config(null) - return config.package || 'local' - } catch (e) { - return 'local' - } + if (config.package) pkg_name = config.package + } disruption {} + do_load() + return pkg_name } // Parse arguments @@ -229,21 +230,48 @@ function spawn_actor_test(test_info) { actor: null } - try { + var spawn_disrupted = false + var do_spawn = function() { // Spawn the actor test - it should send back results var actor_path = text(test_info.path, 0, -3) // remove .ce entry.actor = $start(actor_path) push(pending_actor_tests, entry) - } catch (e) { + } disruption { + spawn_disrupted = true + } + do_spawn() + if (spawn_disrupted) { entry.status = "failed" - entry.error = { message: `Failed to spawn actor: ${e}` } + entry.error = { message: `Failed to spawn actor: ${test_name}` } entry.duration_ns = 0 push(actor_test_results, entry) - log.console(` FAIL ${test_name}: `) - log.error(e) + log.console(` FAIL ${test_name}`) } } +// Test runner with disruption support +var test_passed = true +var test_error_msg = "" +var test_error_stack = "" + +var run_test = function(fn) { + test_passed = true + test_error_msg = "" + test_error_stack = "" + var ret = fn() + if (is_text(ret)) { + test_passed = false + test_error_msg = ret + } else if (ret && (is_text(ret.message) || is_proto(ret, Error))) { + test_passed = false + test_error_msg = ret.message || text(ret) + if (ret.stack) test_error_stack = ret.stack + } +} disruption { + test_passed = false + if (test_error_msg == "") test_error_msg = "test disrupted" +} + function run_tests(package_name, specific_test) { var prefix = get_pkg_dir(package_name) var tests_dir = prefix + '/tests' @@ -293,7 +321,9 @@ function run_tests(package_name, specific_test) { failed: 0 } - try { + var load_disrupted = false + var load_error_msg = "" + var do_load = function() { var test_mod // For local packages (null), use the current directory as package context var use_pkg = package_name ? package_name : fd.realpath('.') @@ -322,34 +352,23 @@ function run_tests(package_name, specific_test) { } var start_time = time.number() - try { - var ret = t.fn() - - if (is_text(ret)) { - throw Error(ret) - } else if (ret && (is_text(ret.message) || is_proto(ret, Error))) { - throw ret - } + run_test(t.fn) + if (test_passed) { test_entry.status = "passed" log.console(` PASS ${t.name}`) pkg_result.passed++ file_result.passed++ - } catch (e) { + } else { test_entry.status = "failed" test_entry.error = { - message: e, - stack: e.stack || "" + message: test_error_msg } - if (e.name) test_entry.error.name = e.name + if (test_error_stack) test_entry.error.stack = test_error_stack - if (is_object(e) && e.message) { - test_entry.error.message = e.message - } - - log.console(` FAIL ${t.name} ${test_entry.error.message}`) - if (test_entry.error.stack) { - log.console(` ${text(array(test_entry.error.stack, '\n'), '\n ')}`) + log.console(` FAIL ${t.name} ${test_error_msg}`) + if (test_error_stack) { + log.console(` ${text(array(test_error_stack, '\n'), '\n ')}`) } pkg_result.failed++ @@ -365,15 +384,18 @@ function run_tests(package_name, specific_test) { } } } - - } catch (e) { - log.console(` Error loading ${f}: ${e}`) - var test_entry = { + } disruption { + load_disrupted = true + } + do_load() + if (load_disrupted) { + log.console(` Error loading ${f}`) + var test_entry = { package: pkg_result.package, test: "load_module", status: "failed", duration_ns: 0, - error: { message: `Error loading module: ${e}` } + error: { message: `Error loading module: ${f}` } } push(file_result.tests, test_entry) pkg_result.failed++ diff --git a/tests/blob.cm b/tests/blob.cm index ccd80b37..0f5a0bdc 100644 --- a/tests/blob.cm +++ b/tests/blob.cm @@ -4,64 +4,65 @@ var os = use('os'); function assert(condition, message) { if (!condition) { - throw Error(message || "Assertion failed"); + return message || "Assertion failed" } } function assertEqual(actual, expected, message) { if (actual != expected) { - throw Error(message || "Expected " + expected + ", got " + actual); + return message || "Expected " + expected + ", got " + actual } } +function should_disrupt(fn) { + var caught = false + var wrapper = function() { fn() } disruption { caught = true } + wrapper() + return caught +} + return { test_create_empty_blob: function() { var b = Blob(); - assertEqual(length(b), 0, "Empty blob should have length 0"); + return assertEqual(length(b), 0, "Empty blob should have length 0") }, - + test_create_blob_with_capacity: function() { var b = Blob(100); - assertEqual(length(b), 0, "New blob with capacity should still have length 0"); + return assertEqual(length(b), 0, "New blob with capacity should still have length 0") }, - + test_write_and_read_single_bit: function() { var b = Blob(); b.write_bit(true); b.write_bit(false); b.write_bit(1); b.write_bit(0); - assertEqual(length(b), 4, "Should have 4 bits after writing"); - + var r = assertEqual(length(b), 4, "Should have 4 bits after writing") + if (r) return r + stone(b); - assertEqual(b.read_logical(0), true, "First bit should be true"); - assertEqual(b.read_logical(1), false, "Second bit should be false"); - assertEqual(b.read_logical(2), true, "Third bit should be true (1)"); - assertEqual(b.read_logical(3), false, "Fourth bit should be false (0)"); + r = assertEqual(b.read_logical(0), true, "First bit should be true") + if (r) return r + r = assertEqual(b.read_logical(1), false, "Second bit should be false") + if (r) return r + r = assertEqual(b.read_logical(2), true, "Third bit should be true (1)") + if (r) return r + return assertEqual(b.read_logical(3), false, "Fourth bit should be false (0)") }, - + test_out_of_range_read_throws_error: function() { var b = Blob(); b.write_bit(true); stone(b); - - var threw = false; - try { - b.read_logical(100); - } catch (e) { - threw = true; - } - assert(threw, "Out of range read should throw"); - - threw = false; - try { - b.read_logical(-1); - } catch (e) { - threw = true; - } - assert(threw, "Negative index read should throw"); + + if (!should_disrupt(function() { b.read_logical(100) })) + return "Out of range read should disrupt" + + if (!should_disrupt(function() { b.read_logical(-1) })) + return "Negative index read should disrupt" }, - + test_write_and_read_numbers: function() { var b = Blob(); b.write_number(3.14159); @@ -69,41 +70,48 @@ return { b.write_number(0); b.write_number(1e20); stone(b); - - assertEqual(b.read_number(0), 3.14159, "First number should match"); - assertEqual(b.read_number(64), -42, "Second number should match"); - assertEqual(b.read_number(128), 0, "Third number should match"); - assertEqual(b.read_number(192), 1e20, "Fourth number should match"); + + var r = assertEqual(b.read_number(0), 3.14159, "First number should match") + if (r) return r + r = assertEqual(b.read_number(64), -42, "Second number should match") + if (r) return r + r = assertEqual(b.read_number(128), 0, "Third number should match") + if (r) return r + return assertEqual(b.read_number(192), 1e20, "Fourth number should match") }, - + test_write_and_read_text: function() { var b = Blob(); b.write_text("Hello"); b.write_text("World"); b.write_text("🎉"); stone(b); - - assertEqual(b.read_text(0), "Hello", "First text should match"); + + return assertEqual(b.read_text(0), "Hello", "First text should match") }, - + test_write_and_read_blobs: function() { var b1 = Blob(); b1.write_bit(true); b1.write_bit(false); b1.write_bit(true); - + var b2 = Blob(10); b2.write_blob(b1); b2.write_bit(false); - assertEqual(length(b2), 4, "Combined blob should have 4 bits"); - + var r = assertEqual(length(b2), 4, "Combined blob should have 4 bits") + if (r) return r + stone(b2); - assertEqual(b2.read_logical(0), true); - assertEqual(b2.read_logical(1), false); - assertEqual(b2.read_logical(2), true); - assertEqual(b2.read_logical(3), false); + r = assertEqual(b2.read_logical(0), true) + if (r) return r + r = assertEqual(b2.read_logical(1), false) + if (r) return r + r = assertEqual(b2.read_logical(2), true) + if (r) return r + return assertEqual(b2.read_logical(3), false) }, - + test_blob_copy_constructor: function() { var b1 = Blob(); b1.write_bit(true); @@ -111,249 +119,219 @@ return { b1.write_bit(true); b1.write_bit(true); stone(b1); - + var b2 = Blob(b1); stone(b2); - assertEqual(length(b2), 4, "Copied blob should have same length"); - assertEqual(b2.read_logical(0), true); - assertEqual(b2.read_logical(3), true); + var r = assertEqual(length(b2), 4, "Copied blob should have same length") + if (r) return r + r = assertEqual(b2.read_logical(0), true) + if (r) return r + return assertEqual(b2.read_logical(3), true) }, - + test_blob_partial_copy_constructor: function() { var b1 = Blob(); for (var i = 0; i < 10; i++) { b1.write_bit(i % 2 == 0); } stone(b1); - + var b2 = Blob(b1, 2, 7); stone(b2); - assertEqual(length(b2), 5, "Partial copy should have 5 bits"); - assertEqual(b2.read_logical(0), true); - assertEqual(b2.read_logical(2), true); + var r = assertEqual(length(b2), 5, "Partial copy should have 5 bits") + if (r) return r + r = assertEqual(b2.read_logical(0), true) + if (r) return r + return assertEqual(b2.read_logical(2), true) }, - + test_create_blob_with_fill: function() { var b1 = Blob(8, true); var b2 = Blob(8, false); - + stone(b1); stone(b2); - + for (var i = 0; i < 8; i++) { - assertEqual(b1.read_logical(i), true, "Bit " + i + " should be true"); - assertEqual(b2.read_logical(i), false, "Bit " + i + " should be false"); + var r = assertEqual(b1.read_logical(i), true, "Bit " + i + " should be true") + if (r) return r + r = assertEqual(b2.read_logical(i), false, "Bit " + i + " should be false") + if (r) return r } }, - + test_create_blob_with_random_function: function() { var sequence = [true, false, true, true, false]; var index = 0; - + var b = Blob(5, function() { return sequence[index++] ? 1 : 0; }); - + stone(b); for (var i = 0; i < 5; i++) { - assertEqual(b.read_logical(i), sequence[i], "Bit " + i + " should match sequence"); + var r = assertEqual(b.read_logical(i), sequence[i], "Bit " + i + " should match sequence") + if (r) return r } }, - + test_write_pad_and_check_padding: function() { var b = Blob(); b.write_bit(true); b.write_bit(false); b.write_bit(true); b.write_pad(8); - - assertEqual(length(b), 8, "Should be padded to 8 bits"); + + var r = assertEqual(length(b), 8, "Should be padded to 8 bits") + if (r) return r stone(b); - - assert(b['pad?'](3, 8), "Should detect valid padding at position 3"); - assert(!b['pad?'](2, 8), "Should detect invalid padding at position 2"); + + r = assert(b['pad?'](3, 8), "Should detect valid padding at position 3") + if (r) return r + return assert(!b['pad?'](2, 8), "Should detect invalid padding at position 2") }, - + test_read_blob_from_stone_blob: function() { var b1 = Blob(); for (var i = 0; i < 16; i++) { b1.write_bit(i % 3 == 0); } stone(b1); - + var b2 = b1.read_blob(4, 12); stone(b2); - assertEqual(length(b2), 8, "Read blob should have 8 bits"); - - assertEqual(b2.read_logical(2), true); - assertEqual(b2.read_logical(5), true); + var r = assertEqual(length(b2), 8, "Read blob should have 8 bits") + if (r) return r + + r = assertEqual(b2.read_logical(2), true) + if (r) return r + return assertEqual(b2.read_logical(5), true) }, - + test_stone_blob_is_immutable: function() { var b = Blob(); b.write_bit(true); stone(b); - - var threw = false; - try { - b.write_bit(false); - } catch (e) { - threw = true; - } - assert(threw, "Writing to stone blob should throw error"); + + if (!should_disrupt(function() { b.write_bit(false) })) + return "Writing to stone blob should disrupt" }, - + test_multiple_stone_calls_are_safe: function() { var b = Blob(); b.write_bit(true); - assert(!stone.p(b), "Blob should not be a stone before stone() call"); + var r = assert(!stone.p(b), "Blob should not be a stone before stone() call") + if (r) return r stone(b); - assert(stone.p(b), "Blob should be a stone after stone() call"); + r = assert(stone.p(b), "Blob should be a stone after stone() call") + if (r) return r stone(b); - assertEqual(b.read_logical(0), true, "Blob data should remain intact"); - - assert(b.stone == null, "blob.stone should not be available as a method"); + r = assertEqual(b.read_logical(0), true, "Blob data should remain intact") + if (r) return r + + return assert(b.stone == null, "blob.stone should not be available as a method") }, - + test_invalid_constructor_arguments: function() { - var threw = false; - try { - var b = Blob("invalid"); - } catch (e) { - threw = true; - } - assert(threw, "Invalid constructor arguments should throw"); + if (!should_disrupt(function() { Blob("invalid") })) + return "Invalid constructor arguments should disrupt" }, - + test_write_bit_validation: function() { var b = Blob(); b.write_bit(0); b.write_bit(1); - - var threw = false; - try { - b.write_bit(2); - } catch (e) { - threw = true; - } - assert(threw, "write_bit with value 2 should throw"); + + if (!should_disrupt(function() { b.write_bit(2) })) + return "write_bit with value 2 should disrupt" }, - + test_complex_data_round_trip: function() { var b = Blob(); - + b.write_text("Test"); b.write_number(123.456); b.write_bit(true); b.write_bit(false); b.write_number(-999.999); - + var originalLength = length(b); stone(b); - + var b2 = Blob(b); stone(b2); - assertEqual(length(b2), originalLength, "Copy should have same length"); - assertEqual(b2.read_text(0), "Test", "First text should match"); + var r = assertEqual(length(b2), originalLength, "Copy should have same length") + if (r) return r + return assertEqual(b2.read_text(0), "Test", "First text should match") }, - + test_zero_capacity_blob: function() { var b = Blob(0); - assertEqual(length(b), 0, "Zero capacity blob should have length 0"); + var r = assertEqual(length(b), 0, "Zero capacity blob should have length 0") + if (r) return r b.write_bit(true); - assertEqual(length(b), 1, "Should expand when writing"); + return assertEqual(length(b), 1, "Should expand when writing") }, - + test_large_blob_handling: function() { var b = Blob(); var testSize = 1000; - + for (var i = 0; i < testSize; i++) { b.write_bit(i % 7 == 0); } - - assertEqual(length(b), testSize, "Should have " + testSize + " bits"); + + var r = assertEqual(length(b), testSize, "Should have " + testSize + " bits") + if (r) return r stone(b); - - assertEqual(b.read_logical(0), true, "Bit 0 should be true"); - assertEqual(b.read_logical(7), true, "Bit 7 should be true"); - assertEqual(b.read_logical(14), true, "Bit 14 should be true"); - assertEqual(b.read_logical(15), false, "Bit 15 should be false"); + + r = assertEqual(b.read_logical(0), true, "Bit 0 should be true") + if (r) return r + r = assertEqual(b.read_logical(7), true, "Bit 7 should be true") + if (r) return r + r = assertEqual(b.read_logical(14), true, "Bit 14 should be true") + if (r) return r + return assertEqual(b.read_logical(15), false, "Bit 15 should be false") }, - + test_non_stone_blob_read_methods_should_throw: function() { var b = Blob(); b.write_bit(true); b.write_number(42); b.write_text("test"); - - var threw = false; - try { - b.read_logical(0); - } catch (e) { - threw = true; - } - assert(threw, "read_logical on non-stone blob should throw"); - - threw = false; - try { - b.read_number(0); - } catch (e) { - threw = true; - } - assert(threw, "read_number on non-stone blob should throw"); - - threw = false; - try { - b.read_text(0); - } catch (e) { - threw = true; - } - assert(threw, "read_text on non-stone blob should throw"); - - threw = false; - try { - b.read_blob(0, 10); - } catch (e) { - threw = true; - } - assert(threw, "read_blob on non-stone blob should throw"); - - threw = false; - try { - b['pad?'](0, 8); - } catch (e) { - threw = true; - } - assert(threw, "pad? on non-stone blob should throw"); + + if (!should_disrupt(function() { b.read_logical(0) })) + return "read_logical on non-stone blob should disrupt" + + if (!should_disrupt(function() { b.read_number(0) })) + return "read_number on non-stone blob should disrupt" + + if (!should_disrupt(function() { b.read_text(0) })) + return "read_text on non-stone blob should disrupt" + + if (!should_disrupt(function() { b.read_blob(0, 10) })) + return "read_blob on non-stone blob should disrupt" + + if (!should_disrupt(function() { b['pad?'](0, 8) })) + return "pad? on non-stone blob should disrupt" }, - + test_empty_text_write_and_read: function() { var b = Blob(); b.write_text(""); stone(b); - assertEqual(b.read_text(0), "", "Empty string should round-trip"); + return assertEqual(b.read_text(0), "", "Empty string should round-trip") }, - + test_invalid_read_positions: function() { var b = Blob(); b.write_number(42); stone(b); - - var threw = false; - try { - b.read_number(-10); - } catch (e) { - threw = true; - } - assert(threw, "Negative position should throw"); - - threw = false; - try { - b.read_number(1000); - } catch (e) { - threw = true; - } - assert(threw, "Position beyond length should throw"); + + if (!should_disrupt(function() { b.read_number(-10) })) + return "Negative position should disrupt" + + if (!should_disrupt(function() { b.read_number(1000) })) + return "Position beyond length should disrupt" } } diff --git a/tests/disrupt.cm b/tests/disrupt.cm index 2a817f1c..2c147167 100644 --- a/tests/disrupt.cm +++ b/tests/disrupt.cm @@ -1,5 +1,5 @@ return { test_disrupt: function() { - throw 1 + disrupt } } diff --git a/tests/miniz.cm b/tests/miniz.cm index 84009cdd..a72b7f0a 100644 --- a/tests/miniz.cm +++ b/tests/miniz.cm @@ -2,85 +2,101 @@ var fd = use("fd") var miniz = use("miniz") var utf8 = use("utf8") +function safe_unlink(p) { fd.unlink(p) } disruption {} + return { create_and_read_zip: function() { var ZIP_PATH = "miniz_test.zip" var SOURCE_PATH = "miniz_source.txt" var ENTRY_PATH = "sample/hello.txt" var PAYLOAD = "Miniz integration test payload." - + function write_text_file(path, text) { var handle = fd.open(path, "w") fd.write(handle, text) fd.close(handle) } - - try { + + var error_msg = null + var do_test = function() { write_text_file(SOURCE_PATH, PAYLOAD) var source_blob = fd.slurp(SOURCE_PATH) var writer = miniz.write(ZIP_PATH) writer.add_file(ENTRY_PATH, source_blob) writer = null - + var zip_blob = fd.slurp(ZIP_PATH) var reader = miniz.read(zip_blob) - + if (!reader.exists(ENTRY_PATH)) - throw "entry missing in archive" - - var extracted_blob = reader.slurp(ENTRY_PATH) - var extracted_text = utf8.decode(extracted_blob) - - if (extracted_text != PAYLOAD) - throw "extracted text mismatch" - } finally { - try { fd.unlink(ZIP_PATH) } catch(e) {} - try { fd.unlink(SOURCE_PATH) } catch(e) {} + error_msg = "entry missing in archive" + + if (!error_msg) { + var extracted_blob = reader.slurp(ENTRY_PATH) + var extracted_text = utf8.decode(extracted_blob) + + if (extracted_text != PAYLOAD) + error_msg = "extracted text mismatch" + } + } disruption { + if (!error_msg) error_msg = "test disrupted" } + do_test() + safe_unlink(ZIP_PATH) + safe_unlink(SOURCE_PATH) + if (error_msg) return error_msg }, - + list_and_count: function() { var ZIP_PATH = "miniz_list_test.zip" var ENTRY1 = "file1.txt" var ENTRY2 = "dir/file2.txt" - - try { + + var error_msg = null + var do_test = function() { var writer = miniz.write(ZIP_PATH) writer.add_file(ENTRY1, utf8.encode("content1")) writer.add_file(ENTRY2, utf8.encode("content2")) writer = null - + var zip_blob = fd.slurp(ZIP_PATH) var reader = miniz.read(zip_blob) - + var listed = reader.list() if (length(listed) != reader.count()) - throw "list/count mismatch" - if (length(listed) != 2) - throw "unexpected entry count" - } finally { - try { fd.unlink(ZIP_PATH) } catch(e) {} + error_msg = "list/count mismatch" + if (!error_msg && length(listed) != 2) + error_msg = "unexpected entry count" + } disruption { + if (!error_msg) error_msg = "test disrupted" } + do_test() + safe_unlink(ZIP_PATH) + if (error_msg) return error_msg }, - + exists_check: function() { var ZIP_PATH = "miniz_exists_test.zip" var ENTRY_PATH = "existing.txt" - - try { + + var error_msg = null + var do_test = function() { var writer = miniz.write(ZIP_PATH) writer.add_file(ENTRY_PATH, utf8.encode("data")) writer = null - + var zip_blob = fd.slurp(ZIP_PATH) var reader = miniz.read(zip_blob) - + if (!reader.exists(ENTRY_PATH)) - throw "existing entry not found" - if (reader.exists("nonexistent.txt")) - throw "nonexistent entry reported as existing" - } finally { - try { fd.unlink(ZIP_PATH) } catch(e) {} + error_msg = "existing entry not found" + if (!error_msg && reader.exists("nonexistent.txt")) + error_msg = "nonexistent entry reported as existing" + } disruption { + if (!error_msg) error_msg = "test disrupted" } + do_test() + safe_unlink(ZIP_PATH) + if (error_msg) return error_msg } } diff --git a/tests/suite.cm b/tests/suite.cm index cb1c2d97..49c9ffb9 100644 --- a/tests/suite.cm +++ b/tests/suite.cm @@ -2,48 +2,55 @@ // Tests all core features before implementing performance optimizations // (bytecode passes, ICs, quickening, tail call optimization) // +function should_disrupt(fn) { + var caught = false + var wrapper = function() { fn() } disruption { caught = true } + wrapper() + return caught +} + return { // ============================================================================ // ARITHMETIC OPERATORS - Numbers // ============================================================================ test_number_addition: function() { - if (1 + 2 != 3) throw "basic addition failed" - if (0 + 0 != 0) throw "zero addition failed" - if (-5 + 3 != -2) throw "negative addition failed" - if (0.1 + 0.2 - 0.3 > 0.0001) throw "float addition precision issue" + if (1 + 2 != 3) return "basic addition failed" + if (0 + 0 != 0) return "zero addition failed" + if (-5 + 3 != -2) return "negative addition failed" + if (0.1 + 0.2 - 0.3 > 0.0001) return "float addition precision issue" }, test_number_subtraction: function() { - if (5 - 3 != 2) throw "basic subtraction failed" - if (0 - 5 != -5) throw "zero subtraction failed" - if (-5 - -3 != -2) throw "negative subtraction failed" + if (5 - 3 != 2) return "basic subtraction failed" + if (0 - 5 != -5) return "zero subtraction failed" + if (-5 - -3 != -2) return "negative subtraction failed" }, test_number_multiplication: function() { - if (3 * 4 != 12) throw "basic multiplication failed" - if (0 * 100 != 0) throw "zero multiplication failed" - if (-3 * 4 != -12) throw "negative multiplication failed" - if (-3 * -4 != 12) throw "double negative multiplication failed" + if (3 * 4 != 12) return "basic multiplication failed" + if (0 * 100 != 0) return "zero multiplication failed" + if (-3 * 4 != -12) return "negative multiplication failed" + if (-3 * -4 != 12) return "double negative multiplication failed" }, test_number_division: function() { - if (12 / 4 != 3) throw "basic division failed" - if (1 / 2 != 0.5) throw "fractional division failed" - if (-12 / 4 != -3) throw "negative division failed" - if (12 / -4 != -3) throw "division by negative failed" + if (12 / 4 != 3) return "basic division failed" + if (1 / 2 != 0.5) return "fractional division failed" + if (-12 / 4 != -3) return "negative division failed" + if (12 / -4 != -3) return "division by negative failed" }, test_number_modulo: function() { - if (10 % 3 != 1) throw "basic modulo failed" - if (10 % 5 != 0) throw "even modulo failed" - if (-10 % 3 != -1) throw "negative modulo failed" + if (10 % 3 != 1) return "basic modulo failed" + if (10 % 5 != 0) return "even modulo failed" + if (-10 % 3 != -1) return "negative modulo failed" }, test_number_exponentiation: function() { - if (2 ** 3 != 8) throw "basic exponentiation failed" - if (5 ** 0 != 1) throw "zero exponent failed" - if (2 ** -1 != 0.5) throw "negative exponent failed" + if (2 ** 3 != 8) return "basic exponentiation failed" + if (5 ** 0 != 1) return "zero exponent failed" + if (2 ** -1 != 0.5) return "negative exponent failed" }, // ============================================================================ @@ -52,13 +59,13 @@ return { test_string_plus_string_works: function() { var x = "hello" + " world" - if (x != "hello world") throw "string + string should work" + if (x != "hello world") return "string + string should work" }, test_string_concatenation_empty: function() { - if ("" + "" != "") throw "empty string concatenation failed" - if ("hello" + "" != "hello") throw "concatenation with empty string failed" - if ("" + "world" != "world") throw "empty + string failed" + if ("" + "" != "") return "empty string concatenation failed" + if ("hello" + "" != "hello") return "concatenation with empty string failed" + if ("" + "world" != "world") return "empty + string failed" }, // ============================================================================ @@ -66,103 +73,53 @@ return { // ============================================================================ test_number_plus_string_throws: function() { - var caught = false - try { - var x = 1 + "hello" - } catch (e) { - caught = true - } - if (!caught) throw "number + string should throw" + var caught = should_disrupt(function() { var x = 1 + "hello" }) + if (!caught) return "number + string should throw" }, test_string_plus_number_throws: function() { - var caught = false - try { - var x = "hello" + 1 - } catch (e) { - caught = true - } - if (!caught) throw "string + number should throw" + var caught = should_disrupt(function() { var x = "hello" + 1 }) + if (!caught) return "string + number should throw" }, test_object_plus_string_throws: function() { - var caught = false - try { - var x = {} + "hello" - } catch (e) { - caught = true - } - if (!caught) throw "object + string should throw" + var caught = should_disrupt(function() { var x = {} + "hello" }) + if (!caught) return "object + string should throw" }, test_string_plus_object_throws: function() { - var caught = false - try { - var x = "hello" + {} - } catch (e) { - caught = true - } - if (!caught) throw "string + object should throw" + var caught = should_disrupt(function() { var x = "hello" + {} }) + if (!caught) return "string + object should throw" }, test_array_plus_string_throws: function() { - var caught = false - try { - var x = [] + "hello" - } catch (e) { - caught = true - } - if (!caught) throw "array + string should throw" + var caught = should_disrupt(function() { var x = [] + "hello" }) + if (!caught) return "array + string should throw" }, test_string_plus_array_throws: function() { - var caught = false - try { - var x = "hello" + [] - } catch (e) { - caught = true - } - if (!caught) throw "string + array should throw" + var caught = should_disrupt(function() { var x = "hello" + [] }) + if (!caught) return "string + array should throw" }, test_boolean_plus_string_throws: function() { - var caught = false - try { - var x = true + "hello" - } catch (e) { - caught = true - } - if (!caught) throw "boolean + string should throw" + var caught = should_disrupt(function() { var x = true + "hello" }) + if (!caught) return "boolean + string should throw" }, test_string_plus_boolean_throws: function() { - var caught = false - try { - var x = "hello" + false - } catch (e) { - caught = true - } - if (!caught) throw "string + boolean should throw" + var caught = should_disrupt(function() { var x = "hello" + false }) + if (!caught) return "string + boolean should throw" }, test_null_plus_string_throws: function() { - var caught = false - try { - var x = null + "hello" - } catch (e) { - caught = true - } - if (!caught) throw "null + string should throw" + var caught = should_disrupt(function() { var x = null + "hello" }) + if (!caught) return "null + string should throw" }, test_string_plus_null_throws: function() { - var caught = false - try { - var x = "hello" + null - } catch (e) { - caught = true - } - if (!caught) throw "string + null should throw" + var caught = should_disrupt(function() { var x = "hello" + null }) + if (!caught) return "string + null should throw" }, // ============================================================================ @@ -170,58 +127,58 @@ return { // ============================================================================ test_equality_numbers: function() { - if (!(5 == 5)) throw "number equality failed" - if (5 == 6) throw "number inequality detection failed" - if (!(0 == 0)) throw "zero equality failed" - if (!(-5 == -5)) throw "negative equality failed" + if (!(5 == 5)) return "number equality failed" + if (5 == 6) return "number inequality detection failed" + if (!(0 == 0)) return "zero equality failed" + if (!(-5 == -5)) return "negative equality failed" }, test_inequality_numbers: function() { - if (5 != 5) throw "number inequality failed" - if (!(5 != 6)) throw "number difference detection failed" + if (5 != 5) return "number inequality failed" + if (!(5 != 6)) return "number difference detection failed" }, test_less_than: function() { - if (!(3 < 5)) throw "less than failed" - if (5 < 3) throw "not less than failed" - if (5 < 5) throw "equal not less than failed" + if (!(3 < 5)) return "less than failed" + if (5 < 3) return "not less than failed" + if (5 < 5) return "equal not less than failed" }, test_less_than_or_equal: function() { - if (!(3 <= 5)) throw "less than or equal failed" - if (!(5 <= 5)) throw "equal in less than or equal failed" - if (6 <= 5) throw "not less than or equal failed" + if (!(3 <= 5)) return "less than or equal failed" + if (!(5 <= 5)) return "equal in less than or equal failed" + if (6 <= 5) return "not less than or equal failed" }, test_greater_than: function() { - if (!(5 > 3)) throw "greater than failed" - if (3 > 5) throw "not greater than failed" - if (5 > 5) throw "equal not greater than failed" + if (!(5 > 3)) return "greater than failed" + if (3 > 5) return "not greater than failed" + if (5 > 5) return "equal not greater than failed" }, test_greater_than_or_equal: function() { - if (!(5 >= 3)) throw "greater than or equal failed" - if (!(5 >= 5)) throw "equal in greater than or equal failed" - if (3 >= 5) throw "not greater than or equal failed" + if (!(5 >= 3)) return "greater than or equal failed" + if (!(5 >= 5)) return "equal in greater than or equal failed" + if (3 >= 5) return "not greater than or equal failed" }, test_string_equality: function() { - if (!("hello" == "hello")) throw "string equality failed" - if ("hello" == "world") throw "string inequality detection failed" - if (!("" == "")) throw "empty string equality failed" + if (!("hello" == "hello")) return "string equality failed" + if ("hello" == "world") return "string inequality detection failed" + if (!("" == "")) return "empty string equality failed" }, test_null_equality: function() { - if (!(null == null)) throw "null equality failed" - if (null == 0) throw "null should not equal 0" - if (null == false) throw "null should not equal false" - if (null == "") throw "null should not equal empty string" + if (!(null == null)) return "null equality failed" + if (null == 0) return "null should not equal 0" + if (null == false) return "null should not equal false" + if (null == "") return "null should not equal empty string" }, test_boolean_equality: function() { - if (!(true == true)) throw "true equality failed" - if (!(false == false)) throw "false equality failed" - if (true == false) throw "boolean inequality detection failed" + if (!(true == true)) return "true equality failed" + if (!(false == false)) return "false equality failed" + if (true == false) return "boolean inequality detection failed" }, // ============================================================================ @@ -229,36 +186,36 @@ return { // ============================================================================ test_logical_and: function() { - if (!(true && true)) throw "true && true failed" - if (true && false) throw "true && false failed" - if (false && true) throw "false && true failed" - if (false && false) throw "false && false failed" + if (!(true && true)) return "true && true failed" + if (true && false) return "true && false failed" + if (false && true) return "false && true failed" + if (false && false) return "false && false failed" }, test_logical_or: function() { - if (!(true || true)) throw "true || true failed" - if (!(true || false)) throw "true || false failed" - if (!(false || true)) throw "false || true failed" - if (false || false) throw "false || false failed" + if (!(true || true)) return "true || true failed" + if (!(true || false)) return "true || false failed" + if (!(false || true)) return "false || true failed" + if (false || false) return "false || false failed" }, test_logical_not: function() { - if (!(!false)) throw "!false failed" - if (!true) throw "!true failed" + if (!(!false)) return "!false failed" + if (!true) return "!true failed" }, test_short_circuit_and: function() { var called = false var fn = function() { called = true; return true } var result = false && fn() - if (called) throw "AND should short circuit" + if (called) return "AND should short circuit" }, test_short_circuit_or: function() { var called = false var fn = function() { called = true; return false } var result = true || fn() - if (called) throw "OR should short circuit" + if (called) return "OR should short circuit" }, // ============================================================================ @@ -266,37 +223,37 @@ return { // ============================================================================ test_bitwise_and: function() { - if ((5 & 3) != 1) throw "bitwise AND failed" - if ((12 & 10) != 8) throw "bitwise AND failed" + if ((5 & 3) != 1) return "bitwise AND failed" + if ((12 & 10) != 8) return "bitwise AND failed" }, test_bitwise_or: function() { - if ((5 | 3) != 7) throw "bitwise OR failed" - if ((12 | 10) != 14) throw "bitwise OR failed" + if ((5 | 3) != 7) return "bitwise OR failed" + if ((12 | 10) != 14) return "bitwise OR failed" }, test_bitwise_xor: function() { - if ((5 ^ 3) != 6) throw "bitwise XOR failed" - if ((12 ^ 10) != 6) throw "bitwise XOR failed" + if ((5 ^ 3) != 6) return "bitwise XOR failed" + if ((12 ^ 10) != 6) return "bitwise XOR failed" }, test_bitwise_not: function() { - if (~5 != -6) throw "bitwise NOT failed" - if (~0 != -1) throw "bitwise NOT of zero failed" + if (~5 != -6) return "bitwise NOT failed" + if (~0 != -1) return "bitwise NOT of zero failed" }, test_left_shift: function() { - if ((5 << 2) != 20) throw "left shift failed" - if ((1 << 3) != 8) throw "left shift failed" + if ((5 << 2) != 20) return "left shift failed" + if ((1 << 3) != 8) return "left shift failed" }, test_right_shift: function() { - if ((20 >> 2) != 5) throw "right shift failed" - if ((8 >> 3) != 1) throw "right shift failed" + if ((20 >> 2) != 5) return "right shift failed" + if ((8 >> 3) != 1) return "right shift failed" }, test_unsigned_right_shift: function() { - if ((-1 >>> 1) != 2147483647) throw "unsigned right shift failed" + if ((-1 >>> 1) != 2147483647) return "unsigned right shift failed" }, // ============================================================================ @@ -305,13 +262,13 @@ return { test_var_declaration: function() { var x = 5 - if (x != 5) throw "var declaration failed" + if (x != 5) return "var declaration failed" }, test_var_reassignment: function() { var x = 5 x = 10 - if (x != 10) throw "var reassignment failed" + if (x != 10) return "var reassignment failed" }, // ============================================================================ @@ -322,27 +279,27 @@ return { var x = 1 { var x = 2 - if (x != 2) throw "var should be block scoped - inner scope failed" + if (x != 2) return "var should be block scoped - inner scope failed" } - if (x != 1) throw "var should be block scoped - outer scope affected" + if (x != 1) return "var should be block scoped - outer scope affected" }, test_var_block_scope_if: function() { var x = 1 if (true) { var x = 2 - if (x != 2) throw "var in if block should be scoped" + if (x != 2) return "var in if block should be scoped" } - if (x != 1) throw "var in if block should not affect outer scope" + if (x != 1) return "var in if block should not affect outer scope" }, test_var_block_scope_for: function() { var x = 1 for (var i = 0; i < 1; i = i + 1) { var x = 2 - if (x != 2) throw "var in for block should be scoped" + if (x != 2) return "var in for block should be scoped" } - if (x != 1) throw "var in for block should not affect outer scope" + if (x != 1) return "var in for block should not affect outer scope" }, test_var_for_loop_iterator_scope: function() { @@ -350,14 +307,9 @@ return { for (var i = 0; i < 3; i = i + 1) { sum = sum + i } - if (sum != 3) throw "for loop should work with block scoped var" - var caught = false - try { - var y = i - } catch (e) { - caught = true - } - if (!caught) throw "for loop iterator should not leak to outer scope" + if (sum != 3) return "for loop should work with block scoped var" + var caught = should_disrupt(function() { var y = i }) + if (!caught) return "for loop iterator should not leak to outer scope" }, test_var_nested_blocks: function() { @@ -366,11 +318,11 @@ return { var x = 2 { var x = 3 - if (x != 3) throw "var in nested block level 2 failed" + if (x != 3) return "var in nested block level 2 failed" } - if (x != 2) throw "var in nested block level 1 failed" + if (x != 2) return "var in nested block level 1 failed" } - if (x != 1) throw "var in nested blocks outer scope failed" + if (x != 1) return "var in nested blocks outer scope failed" }, test_var_redeclaration_different_scope: function() { @@ -378,18 +330,7 @@ return { { var x = 2 } - if (x != 1) throw "var in different scope should not affect outer" - }, - - test_var_switch_scope: function() { - var x = 1 - switch (1) { - case 1: - var x = 2 - if (x != 2) throw "var in switch should be block scoped" - break - } - if (x != 1) throw "var in switch should not affect outer scope" + if (x != 1) return "var in different scope should not affect outer" }, test_var_while_scope: function() { @@ -397,22 +338,22 @@ return { var count = 0 while (count < 1) { var x = 2 - if (x != 2) throw "var in while should be block scoped" + if (x != 2) return "var in while should be block scoped" count = count + 1 } - if (x != 1) throw "var in while should not affect outer scope" + if (x != 1) return "var in while should not affect outer scope" }, test_var_no_initialization: function() { { var x - if (x != null) throw "uninitialized var should be null" + if (x != null) return "uninitialized var should be null" } }, test_multiple_var_declaration: function() { var a = 1, b = 2, c = 3 - if (a != 1 || b != 2 || c != 3) throw "multiple var declaration failed" + if (a != 1 || b != 2 || c != 3) return "multiple var declaration failed" }, test_function_scope: function() { @@ -421,7 +362,7 @@ return { var inner = "inner" return inner } - if (fn() != "inner") throw "function scope failed" + if (fn() != "inner") return "function scope failed" }, // ============================================================================ @@ -430,32 +371,32 @@ return { test_function_call_no_args: function() { var fn = function() { return 42 } - if (fn() != 42) throw "function call with no args failed" + if (fn() != 42) return "function call with no args failed" }, test_function_call_one_arg: function() { var fn = function(x) { return x * 2 } - if (fn(5) != 10) throw "function call with one arg failed" + if (fn(5) != 10) return "function call with one arg failed" }, test_function_call_multiple_args: function() { var fn = function(a, b, c) { return a + b + c } - if (fn(1, 2, 3) != 6) throw "function call with multiple args failed" + if (fn(1, 2, 3) != 6) return "function call with multiple args failed" }, test_function_call_extra_args: function() { var fn = function(a, b) { return a + b } - if (fn(1, 2, 3, 4) != 3) throw "function call with extra args failed" + if (fn(1, 2, 3, 4) != 3) return "function call with extra args failed" }, test_function_call_missing_args: function() { var fn = function(a, b, c) { return (a || 0) + (b || 0) + (c || 0) } - if (fn(1) != 1) throw "function call with missing args failed" + if (fn(1) != 1) return "function call with missing args failed" }, test_function_return: function() { var fn = function() { return 5 } - if (fn() != 5) throw "function return failed" + if (fn() != 5) return "function return failed" }, test_function_return_early: function() { @@ -463,24 +404,24 @@ return { return 5 return 10 } - if (fn() != 5) throw "early return failed" + if (fn() != 5) return "early return failed" }, test_function_no_return: function() { var fn = function() { var x = 5 } - if (fn() != null) throw "function with no return should return null" + if (fn() != null) return "function with no return should return null" }, test_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) throw "nested function calls failed" + if (add(mul(2, 3), mul(4, 5)) != 26) return "nested function calls failed" }, test_function_as_value: function() { var fn = function() { return 42 } var fn2 = fn - if (fn2() != 42) throw "function as value failed" + if (fn2() != 42) return "function as value failed" }, test_function_closure: function() { @@ -490,7 +431,7 @@ return { } } var add5 = outer(5) - if (add5(3) != 8) throw "closure failed" + if (add5(3) != 8) return "closure failed" }, test_function_closure_mutation: function() { @@ -502,9 +443,9 @@ return { } } var c = counter() - if (c() != 1) throw "closure mutation failed (1)" - if (c() != 2) throw "closure mutation failed (2)" - if (c() != 3) throw "closure mutation failed (3)" + if (c() != 1) return "closure mutation failed (1)" + if (c() != 2) return "closure mutation failed (2)" + if (c() != 3) return "closure mutation failed (3)" }, // ============================================================================ @@ -516,7 +457,7 @@ return { if (n <= 1) return 1 return n * factorial(n - 1) } - if (factorial(5) != 120) throw "factorial recursion failed" + if (factorial(5) != 120) return "factorial recursion failed" }, test_mutual_recursion: function() { @@ -528,8 +469,8 @@ return { if (n == 0) return false return isEven(n - 1) } - if (!isEven(4)) throw "mutual recursion even failed" - if (isOdd(4)) throw "mutual recursion odd failed" + if (!isEven(4)) return "mutual recursion even failed" + if (isOdd(4)) return "mutual recursion odd failed" }, test_deep_recursion: function() { @@ -537,7 +478,7 @@ return { if (n == 0) return 0 return n + sum(n - 1) } - if (sum(100) != 5050) throw "deep recursion failed" + if (sum(100) != 5050) return "deep recursion failed" }, // ============================================================================ @@ -546,58 +487,58 @@ return { test_array_literal: function() { var arr = [1, 2, 3] - if (arr[0] != 1 || arr[1] != 2 || arr[2] != 3) throw "array literal failed" + if (arr[0] != 1 || arr[1] != 2 || arr[2] != 3) return "array literal failed" }, test_array_length: function() { var arr = [1, 2, 3, 4, 5] - if (length(arr) != 5) throw "array length failed" + if (length(arr) != 5) return "array length failed" }, test_array_empty: function() { var arr = [] - if (length(arr) != 0) throw "empty array length failed" + if (length(arr) != 0) return "empty array length failed" }, test_array_push: function() { var arr = [1, 2] push(arr, 3) - if (length(arr) != 3) throw "array push length failed" - if (arr[2] != 3) throw "array push value failed" + if (length(arr) != 3) return "array push length failed" + if (arr[2] != 3) return "array push value failed" }, test_array_pop: function() { var arr = [1, 2, 3] var val = pop(arr) - if (val != 3) throw "array pop value failed" - if (length(arr) != 2) throw "array pop length failed" + if (val != 3) return "array pop value failed" + if (length(arr) != 2) return "array pop length failed" }, test_array_index_access: function() { var arr = [10, 20, 30] - if (arr[0] != 10) throw "array index 0 failed" - if (arr[1] != 20) throw "array index 1 failed" - if (arr[2] != 30) throw "array index 2 failed" + if (arr[0] != 10) return "array index 0 failed" + if (arr[1] != 20) return "array index 1 failed" + if (arr[2] != 30) return "array index 2 failed" }, test_array_index_assignment: function() { var arr = [1, 2, 3] arr[1] = 99 - if (arr[1] != 99) throw "array index assignment failed" + if (arr[1] != 99) return "array index assignment failed" }, test_array_mixed_types: function() { var arr = [1, "hello", true, null, {}] - if (arr[0] != 1) throw "mixed array number failed" - if (arr[1] != "hello") throw "mixed array string failed" - if (arr[2] != true) throw "mixed array boolean failed" - if (arr[3] != null) throw "mixed array null failed" + if (arr[0] != 1) return "mixed array number failed" + if (arr[1] != "hello") return "mixed array string failed" + if (arr[2] != true) return "mixed array boolean failed" + if (arr[3] != null) return "mixed array null failed" }, test_array_nested: function() { var arr = [[1, 2], [3, 4]] - if (arr[0][0] != 1) throw "nested array access failed" - if (arr[1][1] != 4) throw "nested array access failed" + if (arr[0][0] != 1) return "nested array access failed" + if (arr[1][1] != 4) return "nested array access failed" }, // ============================================================================ @@ -606,43 +547,43 @@ return { test_object_literal: function() { var obj = {a: 1, b: 2} - if (obj.a != 1 || obj.b != 2) throw "object literal failed" + if (obj.a != 1 || obj.b != 2) return "object literal failed" }, test_object_property_access: function() { var obj = {name: "Alice", age: 30} - if (obj.name != "Alice") throw "object property access failed" - if (obj.age != 30) throw "object property access failed" + if (obj.name != "Alice") return "object property access failed" + if (obj.age != 30) return "object property access failed" }, test_object_bracket_access: function() { var obj = {x: 10, y: 20} - if (obj["x"] != 10) throw "object bracket access failed" - if (obj["y"] != 20) throw "object bracket access failed" + if (obj["x"] != 10) return "object bracket access failed" + if (obj["y"] != 20) return "object bracket access failed" }, test_object_property_assignment: function() { var obj = {a: 1} obj.a = 99 - if (obj.a != 99) throw "object property assignment failed" + if (obj.a != 99) return "object property assignment failed" }, test_object_add_property: function() { var obj = {} obj.newProp = 42 - if (obj.newProp != 42) throw "object add property failed" + if (obj.newProp != 42) return "object add property failed" }, test_object_computed_property: function() { var key = "dynamicKey" var obj = {} obj[key] = 123 - if (obj.dynamicKey != 123) throw "object computed property failed" + if (obj.dynamicKey != 123) return "object computed property failed" }, test_object_nested: function() { var obj = {outer: {inner: 42}} - if (obj.outer.inner != 42) throw "nested object access failed" + if (obj.outer.inner != 42) return "nested object access failed" }, test_object_method: function() { @@ -650,7 +591,7 @@ return { value: 10, getValue: function() { return this.value } } - if (obj.getValue() != 10) throw "object method failed" + if (obj.getValue() != 10) return "object method failed" }, test_object_this_binding: function() { @@ -658,7 +599,7 @@ return { x: 5, getX: function() { return this.x } } - if (obj.getX() != 5) throw "this binding failed" + if (obj.getX() != 5) return "this binding failed" }, // ============================================================================ @@ -668,27 +609,27 @@ return { test_if_true: function() { var x = 0 if (true) x = 1 - if (x != 1) throw "if true failed" + if (x != 1) return "if true failed" }, test_if_false: function() { var x = 0 if (false) x = 1 - if (x != 0) throw "if false failed" + if (x != 0) return "if false failed" }, test_if_else_true: function() { var x = 0 if (true) x = 1 else x = 2 - if (x != 1) throw "if else true failed" + if (x != 1) return "if else true failed" }, test_if_else_false: function() { var x = 0 if (false) x = 1 else x = 2 - if (x != 2) throw "if else false failed" + if (x != 2) return "if else false failed" }, test_if_else_if: function() { @@ -696,7 +637,7 @@ return { if (false) x = 1 else if (true) x = 2 else x = 3 - if (x != 2) throw "if else if failed" + if (x != 2) return "if else if failed" }, test_nested_if: function() { @@ -706,7 +647,7 @@ return { x = 1 } } - if (x != 1) throw "nested if failed" + if (x != 1) return "nested if failed" }, // ============================================================================ @@ -720,7 +661,7 @@ return { sum = sum + i i = i + 1 } - if (sum != 10) throw "while loop failed" + if (sum != 10) return "while loop failed" }, test_while_never_executes: function() { @@ -728,7 +669,7 @@ return { while (false) { x = 1 } - if (x != 0) throw "while never executes failed" + if (x != 0) return "while never executes failed" }, test_while_break: function() { @@ -737,7 +678,7 @@ return { if (i >= 5) break i = i + 1 } - if (i != 5) throw "while break failed" + if (i != 5) return "while break failed" }, test_while_continue: function() { @@ -748,7 +689,7 @@ return { if (i % 2 == 0) continue sum = sum + i } - if (sum != 25) throw "while continue failed" + if (sum != 25) return "while continue failed" }, // ============================================================================ @@ -760,7 +701,7 @@ return { for (var i = 0; i < 5; i = i + 1) { sum = sum + i } - if (sum != 10) throw "for loop failed" + if (sum != 10) return "for loop failed" }, test_for_loop_break: function() { @@ -769,7 +710,7 @@ return { if (i == 5) break sum = sum + i } - if (sum != 10) throw "for loop break failed" + if (sum != 10) return "for loop break failed" }, test_for_loop_continue: function() { @@ -778,7 +719,7 @@ return { if (i % 2 == 0) continue sum = sum + i } - if (sum != 25) throw "for loop continue failed" + if (sum != 25) return "for loop continue failed" }, test_nested_for_loops: function() { @@ -788,121 +729,7 @@ return { sum = sum + 1 } } - if (sum != 9) throw "nested for loops failed" - }, - - // ============================================================================ - // CONTROL FLOW - SWITCH - // ============================================================================ - - test_switch_case: function() { - var x = 2 - var result = 0 - switch (x) { - case 1: - result = 10 - break - case 2: - result = 20 - break - case 3: - result = 30 - break - } - if (result != 20) throw "switch case failed" - }, - - test_switch_default: function() { - var x = 99 - var result = 0 - switch (x) { - case 1: - result = 10 - break - default: - result = -1 - break - } - if (result != -1) throw "switch default failed" - }, - - test_switch_fallthrough: function() { - var x = 1 - var result = 0 - switch (x) { - case 1: - result = result + 1 - case 2: - result = result + 2 - break - case 3: - result = result + 3 - break - } - if (result != 3) throw "switch fallthrough failed" - }, - - // ============================================================================ - // ERROR HANDLING - TRY/CATCH - // ============================================================================ - - test_try_catch: function() { - var caught = false - try { - throw "error" - } catch (e) { - caught = true - } - if (!caught) throw "try catch failed" - }, - - test_try_catch_error_value: function() { - var errorMsg = null - try { - throw "my error" - } catch (e) { - errorMsg = e - } - if (errorMsg != "my error") throw "try catch error value failed" - }, - - test_try_no_error: function() { - var x = 0 - try { - x = 1 - } catch (e) { - x = 2 - } - if (x != 1) throw "try no error failed" - }, - - test_nested_try_catch: function() { - var x = 0 - try { - try { - throw "inner" - } catch (e) { - x = 1 - } - x = 2 - } catch (e) { - x = 3 - } - if (x != 2) throw "nested try catch failed" - }, - - test_try_catch_rethrow: function() { - var outerCaught = false - try { - try { - throw "error" - } catch (e) { - throw e - } - } catch (e) { - outerCaught = true - } - if (!outerCaught) throw "try catch rethrow failed" + if (sum != 9) return "nested for loops failed" }, // ============================================================================ @@ -910,96 +737,96 @@ return { // ============================================================================ test_is_number: function() { - if (!is_number(42)) throw "is_number 42 failed" - if (!is_number(3.14)) throw "is_number float failed" - if (!is_number(-5)) throw "is_number negative failed" - if (is_number("42")) throw "is_number string should be false" - if (is_number(true)) throw "is_number boolean should be false" - if (is_number(null)) throw "is_number null should be false" - if (is_number({})) throw "is_number object should be false" - if (is_number([])) throw "is_number array should be false" + if (!is_number(42)) return "is_number 42 failed" + if (!is_number(3.14)) return "is_number float failed" + if (!is_number(-5)) return "is_number negative failed" + if (is_number("42")) return "is_number string should be false" + if (is_number(true)) return "is_number boolean should be false" + if (is_number(null)) return "is_number null should be false" + if (is_number({})) return "is_number object should be false" + if (is_number([])) return "is_number array should be false" }, test_is_text: function() { - if (!is_text("hello")) throw "is_text string failed" - if (!is_text("")) throw "is_text empty string failed" - if (is_text(42)) throw "is_text number should be false" - if (is_text(true)) throw "is_text boolean should be false" - if (is_text(null)) throw "is_text null should be false" - if (is_text({})) throw "is_text object should be false" - if (is_text([])) throw "is_text array should be false" + if (!is_text("hello")) return "is_text string failed" + if (!is_text("")) return "is_text empty string failed" + if (is_text(42)) return "is_text number should be false" + if (is_text(true)) return "is_text boolean should be false" + if (is_text(null)) return "is_text null should be false" + if (is_text({})) return "is_text object should be false" + if (is_text([])) return "is_text array should be false" }, test_is_logical: function() { - if (!is_logical(true)) throw "is_logical true failed" - if (!is_logical(false)) throw "is_logical false failed" - if (is_logical(1)) throw "is_logical number should be false" - if (is_logical("true")) throw "is_logical string should be false" - if (is_logical(null)) throw "is_logical null should be false" - if (is_logical({})) throw "is_logical object should be false" - if (is_logical([])) throw "is_logical array should be false" + if (!is_logical(true)) return "is_logical true failed" + if (!is_logical(false)) return "is_logical false failed" + if (is_logical(1)) return "is_logical number should be false" + if (is_logical("true")) return "is_logical string should be false" + if (is_logical(null)) return "is_logical null should be false" + if (is_logical({})) return "is_logical object should be false" + if (is_logical([])) return "is_logical array should be false" }, test_is_object: function() { - if (!is_object({})) throw "is_object empty object failed" - if (!is_object({a: 1})) throw "is_object object failed" - if (is_object([])) throw "is_object array should be false" - if (is_object(null)) throw "is_object null should be false" - if (is_object(42)) throw "is_object number should be false" - if (is_object("hello")) throw "is_object string should be false" - if (is_object(true)) throw "is_object boolean should be false" + if (!is_object({})) return "is_object empty object failed" + if (!is_object({a: 1})) return "is_object object failed" + if (is_object([])) return "is_object array should be false" + if (is_object(null)) return "is_object null should be false" + if (is_object(42)) return "is_object number should be false" + if (is_object("hello")) return "is_object string should be false" + if (is_object(true)) return "is_object boolean should be false" }, test_is_array: function() { - if (!is_array([])) throw "is_array empty array failed" - if (!is_array([1, 2, 3])) throw "is_array array failed" - if (is_array({})) throw "is_array object should be false" - if (is_array(null)) throw "is_array null should be false" - if (is_array(42)) throw "is_array number should be false" - if (is_array("hello")) throw "is_array string should be false" - if (is_array(true)) throw "is_array boolean should be false" + if (!is_array([])) return "is_array empty array failed" + if (!is_array([1, 2, 3])) return "is_array array failed" + if (is_array({})) return "is_array object should be false" + if (is_array(null)) return "is_array null should be false" + if (is_array(42)) return "is_array number should be false" + if (is_array("hello")) return "is_array string should be false" + if (is_array(true)) return "is_array boolean should be false" }, test_is_function: function() { - if (!is_function(function(){})) throw "is_function function failed" + if (!is_function(function(){})) return "is_function function failed" var fn = function(x) { return x * 2 } - if (!is_function(fn)) throw "is_function named function failed" - if (is_function({})) throw "is_function object should be false" - if (is_function([])) throw "is_function array should be false" - if (is_function(null)) throw "is_function null should be false" - if (is_function(42)) throw "is_function number should be false" - if (is_function("hello")) throw "is_function string should be false" - if (is_function(true)) throw "is_function boolean should be false" + if (!is_function(fn)) return "is_function named function failed" + if (is_function({})) return "is_function object should be false" + if (is_function([])) return "is_function array should be false" + if (is_function(null)) return "is_function null should be false" + if (is_function(42)) return "is_function number should be false" + if (is_function("hello")) return "is_function string should be false" + if (is_function(true)) return "is_function boolean should be false" }, test_is_null: function() { - if (!is_null(null)) throw "is_null null failed" - if (is_null(0)) throw "is_null zero should be false" - if (is_null(false)) throw "is_null false should be false" - if (is_null("")) throw "is_null empty string should be false" - if (is_null({})) throw "is_null object should be false" - if (is_null([])) throw "is_null array should be false" + if (!is_null(null)) return "is_null null failed" + if (is_null(0)) return "is_null zero should be false" + if (is_null(false)) return "is_null false should be false" + if (is_null("")) return "is_null empty string should be false" + if (is_null({})) return "is_null object should be false" + if (is_null([])) return "is_null array should be false" var x - if (!is_null(x)) throw "is_null undefined variable should be true" + if (!is_null(x)) return "is_null undefined variable should be true" }, test_is_blob: function() { // Note: blob testing would require actual blob values // For now, just test that other types return false - if (is_blob(null)) throw "is_blob null should be false" - if (is_blob(42)) throw "is_blob number should be false" - if (is_blob("hello")) throw "is_blob string should be false" - if (is_blob(true)) throw "is_blob boolean should be false" - if (is_blob({})) throw "is_blob object should be false" - if (is_blob([])) throw "is_blob array should be false" - if (is_blob(function(){})) throw "is_blob function should be false" + if (is_blob(null)) return "is_blob null should be false" + if (is_blob(42)) return "is_blob number should be false" + if (is_blob("hello")) return "is_blob string should be false" + if (is_blob(true)) return "is_blob boolean should be false" + if (is_blob({})) return "is_blob object should be false" + if (is_blob([])) return "is_blob array should be false" + if (is_blob(function(){})) return "is_blob function should be false" }, test_is_proto: function() { var a = {} var b = meme(a) - if (!is_proto(b, a)) throw "is_proto failed on meme" + if (!is_proto(b, a)) return "is_proto failed on meme" }, // ============================================================================ @@ -1007,21 +834,21 @@ return { // ============================================================================ test_length_string: function() { - if (length("hello") != 5) throw "length string failed" - if (length("") != 0) throw "length empty string failed" + if (length("hello") != 5) return "length string failed" + if (length("") != 0) return "length empty string failed" }, test_length_array: function() { - if (length([1,2,3]) != 3) throw "length array failed" - if (length([]) != 0) throw "length empty array failed" + if (length([1,2,3]) != 3) return "length array failed" + if (length([]) != 0) return "length empty array failed" }, test_length_null: function() { - if (length(null) != null) throw "length null failed" + if (length(null) != null) return "length null failed" }, test_length_number: function() { - if (length(123) != null) throw "length number should return null" + if (length(123) != null) return "length number should return null" }, // ============================================================================ @@ -1031,20 +858,20 @@ return { test_reverse_array: function() { var arr = [1, 2, 3, 4, 5] var rev = reverse(arr) - if (rev[0] != 5) throw "reverse array first failed" - if (rev[4] != 1) throw "reverse array last failed" - if (length(rev) != 5) throw "reverse array length failed" + if (rev[0] != 5) return "reverse array first failed" + if (rev[4] != 1) return "reverse array last failed" + if (length(rev) != 5) return "reverse array length failed" }, test_reverse_empty_array: function() { var rev = reverse([]) - if (length(rev) != 0) throw "reverse empty array failed" + if (length(rev) != 0) return "reverse empty array failed" }, test_reverse_preserves_original: function() { var arr = [1, 2, 3] var rev = reverse(arr) - if (arr[0] != 1) throw "reverse should not mutate original" + if (arr[0] != 1) return "reverse should not mutate original" }, // ============================================================================ @@ -1054,23 +881,23 @@ return { test_meme_basic: function() { var parent = {x: 10} var child = meme(parent) - if (child.x != 10) throw "meme basic inheritance failed" + if (child.x != 10) return "meme basic inheritance failed" }, test_meme_with_mixins: function() { var parent = {x: 10} var mixin = {y: 20} var child = meme(parent, mixin) - if (child.x != 10) throw "meme with mixin parent prop failed" - if (child.y != 20) throw "meme with mixin own prop failed" + if (child.x != 10) return "meme with mixin parent prop failed" + if (child.y != 20) return "meme with mixin own prop failed" }, test_meme_override: function() { var parent = {x: 10} var child = meme(parent) child.x = 20 - if (child.x != 20) throw "meme override failed" - if (parent.x != 10) throw "meme should not mutate parent" + if (child.x != 20) return "meme override failed" + if (parent.x != 10) return "meme should not mutate parent" }, test_meme_multiple_mixins: function() { @@ -1078,7 +905,7 @@ return { var mixin1 = {b: 2} var mixin2 = {c: 3} var child = meme(parent, [mixin1, mixin2]) - if (child.a != 1 || child.b != 2 || child.c != 3) throw "meme multiple mixins failed" + if (child.a != 1 || child.b != 2 || child.c != 3) return "meme multiple mixins failed" }, // ============================================================================ @@ -1089,18 +916,18 @@ return { var parent = {x: 10} var child = meme(parent) var p = proto(child) - if (p != parent) throw "proto basic failed" + if (p != parent) return "proto basic failed" }, test_proto_object_literal: function() { var obj = {x: 10} var p = proto(obj) - if (p != null) throw "proto of object literal should be null" + if (p != null) return "proto of object literal should be null" }, test_proto_non_object: function() { - if (proto(42) != null) throw "proto of number should return null" - if (proto("hello") != null) throw "proto of string should return null" + if (proto(42) != null) return "proto of number should return null" + if (proto("hello") != null) return "proto of string should return null" }, // ============================================================================ @@ -1110,32 +937,22 @@ return { test_stone_object: function() { var obj = {x: 10} stone(obj) - var caught = false - try { - obj.x = 20 - } catch (e) { - caught = true - } - if (!caught) throw "stone object should prevent modification" + var caught = should_disrupt(function() { obj.x = 20 }) + if (!caught) return "stone object should prevent modification" }, test_is_stone_frozen: function() { var obj = {x: 10} - if (is_stone(obj)) throw "stone.p should return false before freezing" + if (is_stone(obj)) return "stone.p should return false before freezing" stone(obj) - if (!is_stone(obj)) throw "stone.p should return true after freezing" + if (!is_stone(obj)) return "stone.p should return true after freezing" }, test_stone_array: function() { var arr = [1, 2, 3] stone(arr) - var caught = false - try { - arr[0] = 99 - } catch (e) { - caught = true - } - if (!caught) throw "stone array should prevent modification" + var caught = should_disrupt(function() { arr[0] = 99 }) + if (!caught) return "stone array should prevent modification" }, // ============================================================================ @@ -1144,24 +961,24 @@ return { test_ternary_true: function() { var x = true ? 1 : 2 - if (x != 1) throw "ternary true failed" + if (x != 1) return "ternary true failed" }, test_ternary_false: function() { var x = false ? 1 : 2 - if (x != 2) throw "ternary false failed" + if (x != 2) return "ternary false failed" }, test_ternary_nested: function() { var x = true ? (false ? 1 : 2) : 3 - if (x != 2) throw "ternary nested failed" + if (x != 2) return "ternary nested failed" }, test_ternary_with_expressions: function() { var a = 5 var b = 10 var max = (a > b) ? a : b - if (max != 10) throw "ternary with expressions failed" + if (max != 10) return "ternary with expressions failed" }, // ============================================================================ @@ -1169,41 +986,41 @@ return { // ============================================================================ test_unary_plus: function() { - if (+5 != 5) throw "unary plus positive failed" - if (+-5 != -5) throw "unary plus negative failed" + if (+5 != 5) return "unary plus positive failed" + if (+-5 != -5) return "unary plus negative failed" }, test_unary_minus: function() { - if (-5 != -5) throw "unary minus failed" - if (-(-5) != 5) throw "double unary minus failed" + if (-5 != -5) return "unary minus failed" + if (-(-5) != 5) return "double unary minus failed" }, test_increment_postfix: function() { var x = 5 var y = x++ - if (y != 5) throw "postfix increment return value failed" - if (x != 6) throw "postfix increment side effect failed" + if (y != 5) return "postfix increment return value failed" + if (x != 6) return "postfix increment side effect failed" }, test_increment_prefix: function() { var x = 5 var y = ++x - if (y != 6) throw "prefix increment return value failed" - if (x != 6) throw "prefix increment side effect failed" + if (y != 6) return "prefix increment return value failed" + if (x != 6) return "prefix increment side effect failed" }, test_decrement_postfix: function() { var x = 5 var y = x-- - if (y != 5) throw "postfix decrement return value failed" - if (x != 4) throw "postfix decrement side effect failed" + if (y != 5) return "postfix decrement return value failed" + if (x != 4) return "postfix decrement side effect failed" }, test_decrement_prefix: function() { var x = 5 var y = --x - if (y != 4) throw "prefix decrement return value failed" - if (x != 4) throw "prefix decrement side effect failed" + if (y != 4) return "prefix decrement return value failed" + if (x != 4) return "prefix decrement side effect failed" }, // ============================================================================ @@ -1213,31 +1030,31 @@ return { test_plus_equals: function() { var x = 5 x += 3 - if (x != 8) throw "plus equals failed" + if (x != 8) return "plus equals failed" }, test_minus_equals: function() { var x = 10 x -= 3 - if (x != 7) throw "minus equals failed" + if (x != 7) return "minus equals failed" }, test_times_equals: function() { var x = 4 x *= 3 - if (x != 12) throw "times equals failed" + if (x != 12) return "times equals failed" }, test_divide_equals: function() { var x = 12 x /= 3 - if (x != 4) throw "divide equals failed" + if (x != 4) return "divide equals failed" }, test_modulo_equals: function() { var x = 10 x %= 3 - if (x != 1) throw "modulo equals failed" + if (x != 1) return "modulo equals failed" }, // ============================================================================ @@ -1246,56 +1063,56 @@ return { test_division_by_zero_is_null: function() { var inf = 1 / 0 - if (inf != null) throw "division by zero should be null" + if (inf != null) return "division by zero should be null" var ninf = -1 / 0 - if (ninf != null) throw "negative division by zero should be null" + if (ninf != null) return "negative division by zero should be null" }, test_zero_div_zero_is_null: function() { var nan = 0 / 0 - if (nan != null) throw "0/0 should be null" + if (nan != null) return "0/0 should be null" }, test_max_safe_integer: function() { var max = 9007199254740991 - if (max + 1 - 1 != max) throw "max safe integer precision lost" + if (max + 1 - 1 != max) return "max safe integer precision lost" }, test_min_safe_integer: function() { var min = -9007199254740991 - if (min - 1 + 1 != min) throw "min safe integer precision lost" + if (min - 1 + 1 != min) return "min safe integer precision lost" }, test_empty_string_falsy: function() { - if ("") throw "empty string should be falsy" + if ("") return "empty string should be falsy" }, test_zero_falsy: function() { - if (0) throw "zero should be falsy" + if (0) return "zero should be falsy" }, test_null_falsy: function() { - if (null) throw "null should be falsy" + if (null) return "null should be falsy" }, test_false_falsy: function() { - if (false) throw "false should be falsy" + if (false) return "false should be falsy" }, test_nonempty_string_truthy: function() { - if (!"hello") throw "non-empty string should be truthy" + if (!"hello") return "non-empty string should be truthy" }, test_nonzero_number_truthy: function() { - if (!42) throw "non-zero number should be truthy" + if (!42) return "non-zero number should be truthy" }, test_object_truthy: function() { - if (!{}) throw "empty object should be truthy" + if (!{}) return "empty object should be truthy" }, test_array_truthy: function() { - if (![]) throw "empty array should be truthy" + if (![]) return "empty array should be truthy" }, // ============================================================================ @@ -1303,23 +1120,23 @@ return { // ============================================================================ test_precedence_multiply_add: function() { - if (2 + 3 * 4 != 14) throw "multiply before add precedence failed" + if (2 + 3 * 4 != 14) return "multiply before add precedence failed" }, test_precedence_parentheses: function() { - if ((2 + 3) * 4 != 20) throw "parentheses precedence failed" + if ((2 + 3) * 4 != 20) return "parentheses precedence failed" }, test_precedence_comparison_logical: function() { - if (!(1 < 2 && 3 < 4)) throw "comparison before logical precedence failed" + if (!(1 < 2 && 3 < 4)) return "comparison before logical precedence failed" }, test_precedence_equality_logical: function() { - if (!(1 == 1 || 2 == 3)) throw "equality before logical precedence failed" + if (!(1 == 1 || 2 == 3)) return "equality before logical precedence failed" }, test_precedence_unary_multiplication: function() { - if (-2 * 3 != -6) throw "unary before multiplication precedence failed" + if (-2 * 3 != -6) return "unary before multiplication precedence failed" }, // ============================================================================ @@ -1328,14 +1145,14 @@ return { test_comma_operator: function() { var x = (1, 2, 3) - if (x != 3) throw "comma operator failed" + if (x != 3) return "comma operator failed" }, test_comma_operator_with_side_effects: function() { var a = 0 var x = (a = 1, a = 2, a + 1) - if (x != 3) throw "comma operator with side effects failed" - if (a != 2) throw "comma operator side effects failed" + if (x != 3) return "comma operator with side effects failed" + if (a != 2) return "comma operator side effects failed" }, // ============================================================================ @@ -1348,8 +1165,8 @@ return { var x = 20 return x } - if (fn() != 20) throw "function shadowing failed" - if (x != 10) throw "outer variable after shadowing failed" + if (fn() != 20) return "function shadowing failed" + if (x != 10) return "outer variable after shadowing failed" }, test_variable_shadowing_nested: function() { @@ -1362,7 +1179,7 @@ return { } return fn2() + x } - if (fn1() != 50) throw "nested shadowing failed" + if (fn1() != 50) return "nested shadowing failed" }, // ============================================================================ @@ -1373,9 +1190,9 @@ return { var fn0 = function() {} var fn1 = function(a) {} var fn2 = function(a, b) {} - if (length(fn0) != 0) throw "function length 0 failed" - if (length(fn1) != 1) throw "function length 1 failed" - if (length(fn2) != 2) throw "function length 2 failed" + if (length(fn0) != 0) return "function length 0 failed" + if (length(fn1) != 1) return "function length 1 failed" + if (length(fn2) != 2) return "function length 2 failed" }, // ============================================================================ @@ -1384,7 +1201,7 @@ return { test_undefined_variable_is_null: function() { var x - if (x != null) throw "undefined variable should be null" + if (x != null) return "undefined variable should be null" }, // ============================================================================ @@ -1393,47 +1210,42 @@ return { test_number_toString_implicit: function() { var n = 42 - var caught = false - try { - var result = n + "" - } catch (e) { - caught = true - } - if (!caught) throw "number + string should throw" + var caught = should_disrupt(function() { var result = n + "" }) + if (!caught) return "number + string should throw" }, test_number_division_by_zero: function() { var result = 1 / 0 - if (result != null) throw "division by zero should give null" + if (result != null) return "division by zero should give null" }, test_number_negative_division_by_zero: function() { var result = -1 / 0 - if (result != null) throw "negative division by zero should give null" + if (result != null) return "negative division by zero should give null" }, test_zero_division_by_zero: function() { var result = 0 / 0 - if (result != null) throw "0/0 should give null" + if (result != null) return "0/0 should give null" }, test_negative_zero_normalized: function() { var nz = -0 - if (nz != 0) throw "-0 should equal 0" + if (nz != 0) return "-0 should equal 0" var mul_nz = 0 * -1 - if (mul_nz != 0) throw "0 * -1 should be 0" + if (mul_nz != 0) return "0 * -1 should be 0" var neg_zero = -(0) - if (neg_zero != 0) throw "-(0) should be 0" + if (neg_zero != 0) return "-(0) should be 0" }, test_overflow_is_null: function() { var result = 1e38 * 1e38 - if (result != null) throw "overflow should give null" + if (result != null) return "overflow should give null" }, test_modulo_by_zero_is_null: function() { var result = 5 % 0 - if (result != null) throw "modulo by zero should give null" + if (result != null) return "modulo by zero should give null" }, // ============================================================================ @@ -1442,14 +1254,14 @@ return { test_in_operator: function() { var obj = {a: 1, b: 2} - if (!("a" in obj)) throw "in operator for existing property failed" - if ("c" in obj) throw "in operator for non-existing property failed" + if (!("a" in obj)) return "in operator for existing property failed" + if ("c" in obj) return "in operator for non-existing property failed" }, test_in_operator_prototype: function() { var parent = {x: 10} var child = meme(parent) - if (!("x" in child)) throw "in operator should find inherited property" + if (!("x" in child)) return "in operator should find inherited property" }, // ============================================================================ @@ -1457,27 +1269,27 @@ return { // ============================================================================ test_logical_function_numbers: function() { - if (logical(0) != false) throw "logical(0) should be false" - if (logical(1) != true) throw "logical(1) should be true" + if (logical(0) != false) return "logical(0) should be false" + if (logical(1) != true) return "logical(1) should be true" }, test_logical_function_strings: function() { - if (logical("false") != false) throw "logical('false') should be false" - if (logical("true") != true) throw "logical('true') should be true" + if (logical("false") != false) return "logical('false') should be false" + if (logical("true") != true) return "logical('true') should be true" }, test_logical_function_booleans: function() { - if (logical(false) != false) throw "logical(false) should be false" - if (logical(true) != true) throw "logical(true) should be true" + if (logical(false) != false) return "logical(false) should be false" + if (logical(true) != true) return "logical(true) should be true" }, test_logical_function_null: function() { - if (logical(null) != false) throw "logical(null) should be false" + if (logical(null) != false) return "logical(null) should be false" }, test_logical_function_invalid: function() { - if (logical("invalid") != null) throw "logical(invalid) should return null" - if (logical(42) != null) throw "logical(42) should return null" + if (logical("invalid") != null) return "logical(invalid) should return null" + if (logical(42) != null) return "logical(42) should return null" }, // ============================================================================ @@ -1488,29 +1300,24 @@ return { var arr1 = [1, 2] var arr2 = [3, 4] var combined = array(arr1, arr2) - if (length(combined) != 4) throw "array concat length failed" - if (combined[2] != 3) throw "array concat values failed" + if (length(combined) != 4) return "array concat length failed" + if (combined[2] != 3) return "array concat values failed" }, test_array_join: function() { var arr = ["a", "b", "c"] var str = text(arr, ",") - if (str != "a,b,c") throw "array join with text() failed" + if (str != "a,b,c") return "array join with text() failed" }, test_text_array_join_numbers_throw: function() { - var caught = false - try { - text([1, 2, 3], ",") - } catch (e) { - caught = true - } - if (!caught) throw "text([numbers], sep) should throw (no implicit coercion)" + var caught = should_disrupt(function() { text([1, 2, 3], ",") }) + if (!caught) return "text([numbers], sep) should throw (no implicit coercion)" }, test_text_array_join_numbers_explicit: function() { var arr = array([1, 2, 3], x => text(x)) - if (text(arr, ",") != "1,2,3") throw "explicit numeric join failed" + if (text(arr, ",") != "1,2,3") return "explicit numeric join failed" }, // ============================================================================ @@ -1519,52 +1326,52 @@ return { test_string_substring: function() { var str = "hello" - if (text(str, 1, 4) != "ell") throw "string substring failed" + if (text(str, 1, 4) != "ell") return "string substring failed" }, test_string_substring_first: function() { var str = "hello" - if (text(str, 1) != "ello") throw "string substring first failed" + if (text(str, 1) != "ello") return "string substring first failed" }, test_string_substring_to_neg: function() { var str = "hello" - if (text(str, 1, -2) != "el") throw "string substring to negative failed" + if (text(str, 1, -2) != "el") return "string substring to negative failed" }, test_string_slice: function() { var str = "hello" - if (text(str, 1, 4) != "ell") throw "string slice failed" - if (text(str, -2) != "lo") throw "string slice negative failed: " + text(str, -2) + if (text(str, 1, 4) != "ell") return "string slice failed" + if (text(str, -2) != "lo") return "string slice negative failed: " + text(str, -2) }, test_string_indexOf: function() { var str = "hello world" - if (search(str, "world") != 6) throw "string search failed" - if (search(str, "xyz") != null) throw "string search not found failed" + if (search(str, "world") != 6) return "string search failed" + if (search(str, "xyz") != null) return "string search not found failed" }, test_string_toLowerCase: function() { var str = "HELLO" - if (lower(str) != "hello") throw "string toLowerCase failed" + if (lower(str) != "hello") return "string toLowerCase failed" }, test_string_toUpperCase: function() { var str = "hello" - if (upper(str) != "HELLO") throw "string toUpperCase failed" + if (upper(str) != "HELLO") return "string toUpperCase failed" }, test_string_split: function() { var str = "a,b,c" var parts = array(str, ",") - if (length(parts) != 3) throw "string split length failed" - if (parts[1] != "b") throw "string split values failed" + if (length(parts) != 3) return "string split length failed" + if (parts[1] != "b") return "string split values failed" }, null_access: function() { var val = {} var nn = val.a - if (nn != null) throw "val.a should return null" + if (nn != null) return "val.a should return null" }, // ============================================================================ @@ -1577,22 +1384,22 @@ return { var o = {} o[k1] = 123 o[k2] = 456 - if (o[k1] != 123) throw "object key k1 failed" - if (o[k2] != 456) throw "object key k2 failed" + if (o[k1] != 123) return "object key k1 failed" + if (o[k2] != 456) return "object key k2 failed" }, test_object_key_new_object_different_key: function() { var k1 = {} var o = {} o[k1] = 123 - if (o[{}] != null) throw "new object should be different key" + if (o[{}] != null) return "new object should be different key" }, test_object_key_in_operator: function() { var k1 = {} var o = {} o[k1] = 123 - if (!(k1 in o)) throw "in operator should find object key" + if (!(k1 in o)) return "in operator should find object key" }, test_object_key_delete: function() { @@ -1600,7 +1407,7 @@ return { var o = {} o[k1] = 123 delete o[k1] - if ((k1 in o)) throw "delete should remove object key" + if ((k1 in o)) return "delete should remove object key" }, test_object_key_no_string_collision: function() { @@ -1609,8 +1416,8 @@ return { var o = {} o[a] = 1 o[b] = 2 - if (o[a] != 1) throw "object key a should be 1" - if (o[b] != 2) throw "object key b should be 2" + if (o[a] != 1) return "object key a should be 1" + if (o[b] != 2) return "object key b should be 2" }, test_object_key_same_object_same_key: function() { @@ -1618,14 +1425,14 @@ return { var o = {} o[k] = 100 o[k] = 200 - if (o[k] != 200) throw "same object should be same key" + if (o[k] != 200) return "same object should be same key" }, test_object_key_computed_property: function() { var k = {} var o = {} o[k] = function() { return 42 } - if (o[k]() != 42) throw "object key with function value failed" + if (o[k]() != 42) return "object key with function value failed" }, test_object_key_multiple_objects_multiple_keys: function() { @@ -1636,17 +1443,17 @@ return { o[k1] = "one" o[k2] = "two" o[k3] = "three" - if (o[k1] != "one") throw "multiple keys k1 failed" - if (o[k2] != "two") throw "multiple keys k2 failed" - if (o[k3] != "three") throw "multiple keys k3 failed" + if (o[k1] != "one") return "multiple keys k1 failed" + if (o[k2] != "two") return "multiple keys k2 failed" + if (o[k3] != "three") return "multiple keys k3 failed" }, test_object_key_with_string_keys: function() { var k = {} var o = {name: "test"} o[k] = "private" - if (o.name != "test") throw "string key should still work" - if (o[k] != "private") throw "object key should work with string keys" + if (o.name != "test") return "string key should still work" + if (o[k] != "private") return "object key should work with string keys" }, test_object_key_overwrite: function() { @@ -1655,7 +1462,7 @@ return { o[k] = 1 o[k] = 2 o[k] = 3 - if (o[k] != 3) throw "object key overwrite failed" + if (o[k] != 3) return "object key overwrite failed" }, test_object_key_nested_objects: function() { @@ -1665,116 +1472,71 @@ return { inner[k2] = "nested" var outer = {} outer[k1] = inner - if (outer[k1][k2] != "nested") throw "nested object keys failed" + if (outer[k1][k2] != "nested") return "nested object keys failed" }, test_array_for: function() { var a = [1,2,3] arrfor(a, (x,i) => { - if (x-1 != i) throw "array for failed" + if (x-1 != i) return "array for failed" }) }, test_array_string_key_throws: function() { var a = [] - var caught = false - try { - a["a"] = 1 - } catch(e) { - caught = true - } - if (!caught) throw "array should not be able to use string as key" + var caught = should_disrupt(function() { a["a"] = 1 }) + if (!caught) return "array should not be able to use string as key" }, test_array_object_key_throws: function() { var a = [] var b = {} - var caught = false - try { - a[b] = 1 - } catch(e) { - caught = true - } - if (!caught) throw "array should not be able to use object as key" + var caught = should_disrupt(function() { a[b] = 1 }) + if (!caught) return "array should not be able to use object as key" }, test_array_boolean_key_throws: function() { var a = [] - var caught = false - try { - a[true] = 1 - } catch(e) { - caught = true - } - if (!caught) throw "array should not be able to use boolean as key" + var caught = should_disrupt(function() { a[true] = 1 }) + if (!caught) return "array should not be able to use boolean as key" }, test_array_null_key_throws: function() { var a = [] - var caught = false - try { - a[null] = 1 - } catch(e) { - caught = true - } - if (!caught) throw "array should not be able to use null as key" + var caught = should_disrupt(function() { a[null] = 1 }) + if (!caught) return "array should not be able to use null as key" }, test_array_array_key_throws: function() { var a = [] var c = [] - var caught = false - try { - a[c] = 1 - } catch(e) { - caught = true - } - if (!caught) throw "array should not be able to use array as key" + var caught = should_disrupt(function() { a[c] = 1 }) + if (!caught) return "array should not be able to use array as key" }, test_obj_number_key_throws: function() { var a = {} - var caught = false - try { - a[1] = 1 - } catch(e) { - caught = true - } - if (!caught) throw "object should not be able to use number as key" + var caught = should_disrupt(function() { a[1] = 1 }) + if (!caught) return "object should not be able to use number as key" }, test_obj_array_key_throws: function() { var a = {} var c = [] - var caught = false - try { - a[c] = 1 - } catch(e) { - caught = true - } - if (!caught) throw "object should not be able to use array as key" + var caught = should_disrupt(function() { a[c] = 1 }) + if (!caught) return "object should not be able to use array as key" }, test_obj_boolean_key_throws: function() { var a = {} - var caught = false - try { - a[true] = 1 - } catch(e) { - caught = true - } - if (!caught) throw "object should not be able to use boolean as key" + var caught = should_disrupt(function() { a[true] = 1 }) + if (!caught) return "object should not be able to use boolean as key" }, test_obj_null_key_throws: function() { var a = {} - var caught = false - try { - a[null] = 1 - } catch(e) { - caught = true - } - if (!caught) throw "object should not be able to use null as key" + var caught = should_disrupt(function() { a[null] = 1 }) + if (!caught) return "object should not be able to use null as key" }, // ============================================================================ @@ -1784,62 +1546,62 @@ return { test_array_get_string_key_returns_null: function() { var a = [1, 2, 3] var result = a["x"] - if (result != null) throw "array get with string key should return null" + if (result != null) return "array get with string key should return null" }, test_array_get_negative_index_returns_null: function() { var a = [1, 2, 3] var result = a[-1] - if (result != null) throw "array get with negative index should return null" + if (result != null) return "array get with negative index should return null" }, test_array_get_object_key_returns_null: function() { var a = [1, 2, 3] var k = {} var result = a[k] - if (result != null) throw "array get with object key should return null" + if (result != null) return "array get with object key should return null" }, test_array_get_array_key_returns_null: function() { var a = [1, 2, 3] var result = a[[1, 2]] - if (result != null) throw "array get with array key should return null" + if (result != null) return "array get with array key should return null" }, test_array_get_boolean_key_returns_null: function() { var a = [1, 2, 3] var result = a[true] - if (result != null) throw "array get with boolean key should return null" + if (result != null) return "array get with boolean key should return null" }, test_array_get_null_key_returns_null: function() { var a = [1, 2, 3] var result = a[null] - if (result != null) throw "array get with null key should return null" + if (result != null) return "array get with null key should return null" }, test_obj_get_number_key_returns_null: function() { var o = {a: 1} var result = o[5] - if (result != null) throw "object get with number key should return null" + if (result != null) return "object get with number key should return null" }, test_obj_get_array_key_returns_null: function() { var o = {a: 1} var result = o[[1, 2]] - if (result != null) throw "object get with array key should return null" + if (result != null) return "object get with array key should return null" }, test_obj_get_boolean_key_returns_null: function() { var o = {a: 1} var result = o[true] - if (result != null) throw "object get with boolean key should return null" + if (result != null) return "object get with boolean key should return null" }, test_obj_get_null_key_returns_null: function() { var o = {a: 1} var result = o[null] - if (result != null) throw "object get with null key should return null" + if (result != null) return "object get with null key should return null" }, // ============================================================================ @@ -1849,29 +1611,19 @@ return { test_function_property_get_throws: function() { var fn = function(a, b) { return a + b } var arity = length(fn) - if (arity != 2) throw "length of function should return its arity" + if (arity != 2) return "length of function should return its arity" }, test_function_property_set_throws: function() { var fn = function() {} - var caught = false - try { - fn.foo = 123 - } catch (e) { - caught = true - } - if (!caught) throw "setting property on function should throw" + var caught = should_disrupt(function() { fn.foo = 123 }) + if (!caught) return "setting property on function should throw" }, test_function_bracket_access_throws: function() { var fn = function() {} - var caught = false - try { - var x = fn["length"]() - } catch (e) { - caught = true - } - if (!caught) throw "bracket access on function should throw" + var caught = should_disrupt(function() { var x = fn["length"]() }) + if (!caught) return "bracket access on function should throw" }, test_length_returns_function_arity: function() { @@ -1880,42 +1632,42 @@ return { var fn2 = function(a, b) { return a + b } var fn3 = function(a, b, c) { return a + b + c } - if (length(fn0) != 0) throw "length(fn0) should be 0" - if (length(fn1) != 1) throw "length(fn1) should be 1" - if (length(fn2) != 2) throw "length(fn2) should be 2" - if (length(fn3) != 3) throw "length(fn3) should be 3" + if (length(fn0) != 0) return "length(fn0) should be 0" + if (length(fn1) != 1) return "length(fn1) should be 1" + if (length(fn2) != 2) return "length(fn2) should be 2" + if (length(fn3) != 3) return "length(fn3) should be 3" }, test_text_returns_function_source: function() { var fn = function(x) { return x * 2 } var src = text(fn) - if (search(src, "function") == null) throw "text(fn) should contain 'function'" - if (search(src, "return") == null) throw "text(fn) should contain function body" + if (search(src, "function") == null) return "text(fn) should contain 'function'" + if (search(src, "return") == null) return "text(fn) should contain function body" }, test_call_invokes_function: function() { var fn = function(a, b) { return a + b } var result = call(fn, null, [3, 4]) - if (result != 7) throw "call(fn, null, 3, 4) should return 7" + if (result != 7) return "call(fn, null, 3, 4) should return 7" }, test_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) throw "call(fn, obj, 5) should return 15" + if (result != 15) return "call(fn, obj, 5) should return 15" }, test_call_no_args: function() { var fn = function() { return 42 } var result = call(fn, null) - if (result != 42) throw "call(fn, null) should return 42" + if (result != 42) return "call(fn, null) should return 42" }, test_builtin_function_properties_still_work: function() { // Built-in functions like number, text, array should still have properties var min_result = min(5, 3) - if (min_result != 3) throw "min should work" + if (min_result != 3) return "min should work" }, // ============================================================================ @@ -1927,7 +1679,7 @@ return { return `called:${name}:${length(args)}` } var result = proxy.foo() - if (result != "called:foo:0") throw "basic proxy call failed" + if (result != "called:foo:0") return "basic proxy call failed" }, test_function_proxy_with_one_arg: function() { @@ -1935,7 +1687,7 @@ return { return `${name}-${args[0]}` } var result = proxy.test("value") - if (result != "test-value") throw "proxy with one arg failed" + if (result != "test-value") return "proxy with one arg failed" }, test_function_proxy_with_multiple_args: function() { @@ -1947,7 +1699,7 @@ return { return `${name}:${sum}` } var result = proxy.add(1, 2, 3, 4) - if (result != "add:10") throw "proxy with multiple args failed" + if (result != "add:10") return "proxy with multiple args failed" }, test_function_proxy_bracket_notation: function() { @@ -1955,7 +1707,7 @@ return { return `bracket:${name}` } var result = proxy["myMethod"]() - if (result != "bracket:myMethod") throw "proxy bracket notation failed" + if (result != "bracket:myMethod") return "proxy bracket notation failed" }, test_function_proxy_dynamic_method_name: function() { @@ -1964,7 +1716,7 @@ return { } var methodName = "dynamic" var result = proxy[methodName]() - if (result != "dynamic") throw "proxy dynamic method name failed" + if (result != "dynamic") return "proxy dynamic method name failed" }, test_function_proxy_dispatch_to_record: function() { @@ -1981,50 +1733,39 @@ return { if (is_function(my_record[name])) { return apply(my_record[name], args) } - throw `unknown method: ${name}` + disrupt } - if (proxy.greet("World") != "Hello, World") throw "proxy dispatch greet failed" - if (proxy.add(3, 4) != 7) throw "proxy dispatch add failed" + if (proxy.greet("World") != "Hello, World") return "proxy dispatch greet failed" + if (proxy.add(3, 4) != 7) return "proxy dispatch add failed" }, test_function_proxy_unknown_method_throws: function() { var proxy = function(name, args) { - throw `no such method: ${name}` + disrupt } - var caught = false - try { - proxy.nonexistent() - } catch (e) { - caught = true - if (search(e, "no such method") == null) throw "wrong error message" - } - if (!caught) throw "proxy should throw for unknown method" + var caught = should_disrupt(function() { proxy.nonexistent() }) + if (!caught) return "proxy should throw for unknown method" }, test_function_proxy_is_function: function() { var proxy = function(name, args) { return name } - if (!is_function(proxy)) throw "proxy should be a function" + if (!is_function(proxy)) return "proxy should be a function" }, test_function_proxy_length_is_2: function() { var proxy = function(name, args) { return name } - if (length(proxy) != 2) throw "proxy function should have length 2" + if (length(proxy) != 2) return "proxy function should have length 2" }, test_function_proxy_property_read_still_throws: function() { var fn = function() { return 1 } - var caught = false - try { - var x = fn.someProp - } catch (e) { - caught = true - } - if (!caught) throw "reading property from function (not method call) should throw" + var caught = should_disrupt(function() { var x = fn.someProp }) + if (!caught) return "reading property from function (not method call) should throw" }, test_function_proxy_nested_calls: function() { @@ -2041,7 +1782,7 @@ return { return "inner:" + name } var result = outer.inner(inner) - if (result != 10) throw "nested proxy calls failed" + if (result != 10) return "nested proxy calls failed" }, test_function_proxy_returns_null: function() { @@ -2049,7 +1790,7 @@ return { return null } var result = proxy.anything() - if (result != null) throw "proxy returning null failed" + if (result != null) return "proxy returning null failed" }, test_function_proxy_returns_object: function() { @@ -2057,8 +1798,8 @@ return { return {method: name, argCount: length(args)} } var result = proxy.test(1, 2, 3) - if (result.method != "test") throw "proxy returning object method failed" - if (result.argCount != 3) throw "proxy returning object argCount failed" + if (result.method != "test") return "proxy returning object method failed" + if (result.argCount != 3) return "proxy returning object argCount failed" }, test_function_proxy_returns_function: function() { @@ -2066,17 +1807,17 @@ return { return function() { return name } } var result = proxy.getFn() - if (result() != "getFn") throw "proxy returning function failed" + if (result() != "getFn") return "proxy returning function failed" }, test_function_proxy_args_array_is_real_array: function() { var proxy = function(name, args) { - if (!is_array(args)) throw "args should be array" + if (!is_array(args)) return "args should be array" push(args, 4) return length(args) } var result = proxy.test(1, 2, 3) - if (result != 4) throw "proxy args should be modifiable array" + if (result != 4) return "proxy args should be modifiable array" }, test_function_proxy_no_this_binding: function() { @@ -2084,20 +1825,15 @@ return { return this } var result = proxy.test() - if (result != null) throw "proxy should have null this" + if (result != null) return "proxy should have null this" }, test_function_proxy_integer_bracket_key: function() { var proxy = function(name, args) { return `key:${name}` } - var caught = false - try { - var result = proxy[42]() - } catch (e) { - caught = true - } - if (!caught) throw "proxy with integer bracket key should throw" + var caught = should_disrupt(function() { var result = proxy[42]() }) + if (!caught) return "proxy with integer bracket key should throw" }, // ============================================================================ @@ -2107,61 +1843,61 @@ return { test_reduce_sum: function() { var arr = [1, 2, 3, 4, 5] var result = reduce(arr, (a, b) => a + b) - if (result != 15) throw "reduce sum failed" + if (result != 15) return "reduce sum failed" }, test_reduce_product: function() { var arr = [1, 2, 3, 4, 5] var result = reduce(arr, (a, b) => a * b) - if (result != 120) throw "reduce product failed" + if (result != 120) return "reduce product failed" }, test_reduce_with_initial: function() { var arr = [1, 2, 3] var result = reduce(arr, (a, b) => a + b, 10) - if (result != 16) throw "reduce with initial failed" + if (result != 16) return "reduce with initial failed" }, test_reduce_with_initial_zero: function() { var arr = [1, 2, 3] var result = reduce(arr, (a, b) => a + b, 0) - if (result != 6) throw "reduce with initial zero failed" + if (result != 6) return "reduce with initial zero failed" }, test_reduce_empty_array_no_initial: function() { var arr = [] var result = reduce(arr, (a, b) => a + b) - if (result != null) throw "reduce empty array without initial should return null" + if (result != null) return "reduce empty array without initial should return null" }, test_reduce_empty_array_with_initial: function() { var arr = [] var result = reduce(arr, (a, b) => a + b, 42) - if (result != 42) throw "reduce empty array with initial should return initial" + if (result != 42) return "reduce empty array with initial should return initial" }, test_reduce_single_element_no_initial: function() { var arr = [42] var result = reduce(arr, (a, b) => a + b) - if (result != 42) throw "reduce single element without initial failed" + if (result != 42) return "reduce single element without initial failed" }, test_reduce_single_element_with_initial: function() { var arr = [5] var result = reduce(arr, (a, b) => a + b, 10) - if (result != 15) throw "reduce single element with initial failed" + if (result != 15) return "reduce single element with initial failed" }, test_reduce_reverse: function() { var arr = [1, 2, 3, 4] var result = reduce(arr, (a, b) => a - b, 0, true) - if (result != -10) throw "reduce reverse failed: " + result + if (result != -10) return "reduce reverse failed: " + result }, test_reduce_string_concat: function() { var arr = ["a", "b", "c"] var result = reduce(arr, (a, b) => a + b) - if (result != "abc") throw "reduce string concat failed" + if (result != "abc") return "reduce string concat failed" }, // ============================================================================ @@ -2171,33 +1907,33 @@ return { test_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) throw "sort numbers failed" - if (sorted[7] != 9) throw "sort numbers last element failed" + if (sorted[0] != 1 || sorted[1] != 1 || sorted[2] != 2) return "sort numbers failed" + if (sorted[7] != 9) return "sort numbers last element failed" }, test_sort_strings: function() { var arr = ["banana", "apple", "cherry"] var sorted = sort(arr) - if (sorted[0] != "apple") throw "sort strings failed" - if (sorted[2] != "cherry") throw "sort strings last failed" + if (sorted[0] != "apple") return "sort strings failed" + if (sorted[2] != "cherry") return "sort strings last failed" }, test_sort_preserves_original: function() { var arr = [3, 1, 2] var sorted = sort(arr) - if (arr[0] != 3) throw "sort should not mutate original" + if (arr[0] != 3) return "sort should not mutate original" }, test_sort_empty_array: function() { var arr = [] var sorted = sort(arr) - if (length(sorted) != 0) throw "sort empty array failed" + if (length(sorted) != 0) return "sort empty array failed" }, test_sort_single_element: function() { var arr = [42] var sorted = sort(arr) - if (sorted[0] != 42) throw "sort single element failed" + if (sorted[0] != 42) return "sort single element failed" }, test_sort_by_field: function() { @@ -2207,14 +1943,14 @@ return { {name: "Bob", age: 35} ] var sorted = sort(arr, "name") - if (sorted[0].name != "Alice") throw "sort by field failed" - if (sorted[2].name != "Charlie") throw "sort by field last failed" + if (sorted[0].name != "Alice") return "sort by field failed" + if (sorted[2].name != "Charlie") return "sort by field last failed" }, test_sort_by_index: function() { var arr = [[3, "c"], [1, "a"], [2, "b"]] var sorted = sort(arr, 0) - if (sorted[0][1] != "a") throw "sort by index failed" + if (sorted[0][1] != "a") return "sort by index failed" }, test_sort_stable: function() { @@ -2225,14 +1961,14 @@ return { ] var sorted = sort(arr, "order") if (sorted[0].name != "A" || sorted[1].name != "B" || sorted[2].name != "C") { - throw "sort should be stable" + return "sort should be stable" } }, test_sort_negative_numbers: function() { var arr = [-5, 3, -1, 0, 2] var sorted = sort(arr) - if (sorted[0] != -5 || sorted[4] != 3) throw "sort negative numbers failed" + if (sorted[0] != -5 || sorted[4] != 3) return "sort negative numbers failed" }, // ============================================================================ @@ -2242,45 +1978,45 @@ return { test_filter_basic: function() { var arr = [1, 2, 3, 4, 5, 6] var evens = filter(arr, x => x % 2 == 0) - if (length(evens) != 3) throw "filter basic length failed" - if (evens[0] != 2 || evens[1] != 4 || evens[2] != 6) throw "filter basic values failed" + if (length(evens) != 3) return "filter basic length failed" + if (evens[0] != 2 || evens[1] != 4 || evens[2] != 6) return "filter basic values failed" }, test_filter_all_pass: function() { var arr = [2, 4, 6] var result = filter(arr, x => x % 2 == 0) - if (length(result) != 3) throw "filter all pass failed" + if (length(result) != 3) return "filter all pass failed" }, test_filter_none_pass: function() { var arr = [1, 3, 5] var result = filter(arr, x => x % 2 == 0) - if (length(result) != 0) throw "filter none pass failed" + if (length(result) != 0) return "filter none pass failed" }, test_filter_empty_array: function() { var arr = [] var result = filter(arr, x => true) - if (length(result) != 0) throw "filter empty array failed" + if (length(result) != 0) return "filter empty array failed" }, test_filter_with_index: function() { var arr = ["a", "b", "c", "d"] var result = filter(arr, (x, i) => i % 2 == 0) - if (length(result) != 2) throw "filter with index length failed" - if (result[0] != "a" || result[1] != "c") throw "filter with index values failed" + if (length(result) != 2) return "filter with index length failed" + if (result[0] != "a" || result[1] != "c") return "filter with index values failed" }, test_filter_preserves_original: function() { var arr = [1, 2, 3] var result = filter(arr, x => x > 1) - if (length(arr) != 3) throw "filter should not mutate original" + if (length(arr) != 3) return "filter should not mutate original" }, test_filter_objects: function() { var arr = [{active: true}, {active: false}, {active: true}] var result = filter(arr, x => x.active) - if (length(result) != 2) throw "filter objects failed" + if (length(result) != 2) return "filter objects failed" }, // ============================================================================ @@ -2290,55 +2026,55 @@ return { test_find_basic: function() { var arr = [1, 2, 3, 4, 5] var idx = find(arr, x => x > 3) - if (idx != 3) throw "find basic failed" + if (idx != 3) return "find basic failed" }, test_find_first_element: function() { var arr = [10, 2, 3] var idx = find(arr, x => x > 5) - if (idx != 0) throw "find first element failed" + if (idx != 0) return "find first element failed" }, test_find_last_element: function() { var arr = [1, 2, 10] var idx = find(arr, x => x > 5) - if (idx != 2) throw "find last element failed" + if (idx != 2) return "find last element failed" }, test_find_not_found: function() { var arr = [1, 2, 3] var idx = find(arr, x => x > 10) - if (idx != null) throw "find not found should return null" + if (idx != null) return "find not found should return null" }, test_find_empty_array: function() { var arr = [] var idx = find(arr, x => true) - if (idx != null) throw "find in empty array should return null" + if (idx != null) return "find in empty array should return null" }, test_find_by_value: function() { var arr = [10, 20, 30, 20] var idx = find(arr, 20) - if (idx != 1) throw "find by value failed" + if (idx != 1) return "find by value failed" }, test_find_reverse: function() { var arr = [10, 20, 30, 20] var idx = find(arr, 20, true) - if (idx != 3) throw "find reverse failed" + if (idx != 3) return "find reverse failed" }, test_find_with_from: function() { var arr = [10, 20, 30, 20] var idx = find(arr, 20, false, 2) - if (idx != 3) throw "find with from failed" + if (idx != 3) return "find with from failed" }, test_find_with_index_callback: function() { var arr = ["a", "b", "c"] var idx = find(arr, (x, i) => i == 1) - if (idx != 1) throw "find with index callback failed" + if (idx != 1) return "find with index callback failed" }, // ============================================================================ @@ -2346,24 +2082,24 @@ return { // ============================================================================ test_abs_positive: function() { - if (abs(5) != 5) throw "abs positive failed" + if (abs(5) != 5) return "abs positive failed" }, test_abs_negative: function() { - if (abs(-5) != 5) throw "abs negative failed" + if (abs(-5) != 5) return "abs negative failed" }, test_abs_zero: function() { - if (abs(0) != 0) throw "abs zero failed" + if (abs(0) != 0) return "abs zero failed" }, test_abs_float: function() { - if (abs(-3.14) != 3.14) throw "abs float failed" + if (abs(-3.14) != 3.14) return "abs float failed" }, test_abs_non_number: function() { - if (abs("5") != null) throw "abs non-number should return null" - if (abs(null) != null) throw "abs null should return null" + if (abs("5") != null) return "abs non-number should return null" + if (abs(null) != null) return "abs null should return null" }, // ============================================================================ @@ -2371,27 +2107,27 @@ return { // ============================================================================ test_floor_positive: function() { - if (floor(3.7) != 3) throw "floor positive failed" + if (floor(3.7) != 3) return "floor positive failed" }, test_floor_negative: function() { - if (floor(-3.7) != -4) throw "floor negative failed" + if (floor(-3.7) != -4) return "floor negative failed" }, test_floor_integer: function() { - if (floor(5) != 5) throw "floor integer failed" + if (floor(5) != 5) return "floor integer failed" }, test_floor_zero: function() { - if (floor(0) != 0) throw "floor zero failed" + if (floor(0) != 0) return "floor zero failed" }, test_floor_with_place: function() { - if (floor(12.3775, -2) != 12.37) throw "floor with place failed" + if (floor(12.3775, -2) != 12.37) return "floor with place failed" }, test_floor_negative_with_place: function() { - if (floor(-12.3775, -2) != -12.38) throw "floor negative with place failed" + if (floor(-12.3775, -2) != -12.38) return "floor negative with place failed" }, // ============================================================================ @@ -2399,27 +2135,27 @@ return { // ============================================================================ test_ceiling_positive: function() { - if (ceiling(3.2) != 4) throw "ceiling positive failed" + if (ceiling(3.2) != 4) return "ceiling positive failed" }, test_ceiling_negative: function() { - if (ceiling(-3.7) != -3) throw "ceiling negative failed" + if (ceiling(-3.7) != -3) return "ceiling negative failed" }, test_ceiling_integer: function() { - if (ceiling(5) != 5) throw "ceiling integer failed" + if (ceiling(5) != 5) return "ceiling integer failed" }, test_ceiling_zero: function() { - if (ceiling(0) != 0) throw "ceiling zero failed" + if (ceiling(0) != 0) return "ceiling zero failed" }, test_ceiling_with_place: function() { - if (ceiling(12.3775, -2) != 12.38) throw "ceiling with place failed" + if (ceiling(12.3775, -2) != 12.38) return "ceiling with place failed" }, test_ceiling_negative_with_place: function() { - if (ceiling(-12.3775, -2) != -12.37) throw "ceiling negative with place failed" + if (ceiling(-12.3775, -2) != -12.37) return "ceiling negative with place failed" }, // ============================================================================ @@ -2427,31 +2163,31 @@ return { // ============================================================================ test_round_down: function() { - if (round(3.4) != 3) throw "round down failed" + if (round(3.4) != 3) return "round down failed" }, test_round_up: function() { - if (round(3.6) != 4) throw "round up failed" + if (round(3.6) != 4) return "round up failed" }, test_round_half: function() { - if (round(3.5) != 4) throw "round half failed" + if (round(3.5) != 4) return "round half failed" }, test_round_negative: function() { - if (round(-3.5) != -3 && round(-3.5) != -4) throw "round negative failed" + if (round(-3.5) != -3 && round(-3.5) != -4) return "round negative failed" }, test_round_integer: function() { - if (round(5) != 5) throw "round integer failed" + if (round(5) != 5) return "round integer failed" }, test_round_with_places: function() { - if (round(12.3775, -2) != 12.38) throw "round with places failed" + if (round(12.3775, -2) != 12.38) return "round with places failed" }, test_round_to_tens: function() { - if (round(12.3775, 1) != 10) throw "round to tens failed" + if (round(12.3775, 1) != 10) return "round to tens failed" }, // ============================================================================ @@ -2459,27 +2195,27 @@ return { // ============================================================================ test_trunc_positive: function() { - if (trunc(3.7) != 3) throw "trunc positive failed" + if (trunc(3.7) != 3) return "trunc positive failed" }, test_trunc_negative: function() { - if (trunc(-3.7) != -3) throw "trunc negative failed" + if (trunc(-3.7) != -3) return "trunc negative failed" }, test_trunc_integer: function() { - if (trunc(5) != 5) throw "trunc integer failed" + if (trunc(5) != 5) return "trunc integer failed" }, test_trunc_zero: function() { - if (trunc(0) != 0) throw "trunc zero failed" + if (trunc(0) != 0) return "trunc zero failed" }, test_trunc_with_places: function() { - if (trunc(12.3775, -2) != 12.37) throw "trunc with places failed" + if (trunc(12.3775, -2) != 12.37) return "trunc with places failed" }, test_trunc_negative_with_places: function() { - if (trunc(-12.3775, -2) != -12.37) throw "trunc negative with places failed" + if (trunc(-12.3775, -2) != -12.37) return "trunc negative with places failed" }, // ============================================================================ @@ -2487,24 +2223,24 @@ return { // ============================================================================ test_sign_positive: function() { - if (sign(5) != 1) throw "sign positive failed" + if (sign(5) != 1) return "sign positive failed" }, test_sign_negative: function() { - if (sign(-5) != -1) throw "sign negative failed" + if (sign(-5) != -1) return "sign negative failed" }, test_sign_zero: function() { - if (sign(0) != 0) throw "sign zero failed" + if (sign(0) != 0) return "sign zero failed" }, test_sign_float: function() { - if (sign(0.001) != 1) throw "sign positive float failed" - if (sign(-0.001) != -1) throw "sign negative float failed" + if (sign(0.001) != 1) return "sign positive float failed" + if (sign(-0.001) != -1) return "sign negative float failed" }, test_sign_non_number: function() { - if (sign("5") != null) throw "sign non-number should return null" + if (sign("5") != null) return "sign non-number should return null" }, // ============================================================================ @@ -2512,37 +2248,37 @@ return { // ============================================================================ test_whole_positive: function() { - if (whole(3.7) != 3) throw "whole positive failed" + if (whole(3.7) != 3) return "whole positive failed" }, test_whole_negative: function() { - if (whole(-3.7) != -3) throw "whole negative failed" + if (whole(-3.7) != -3) return "whole negative failed" }, test_whole_integer: function() { - if (whole(5) != 5) throw "whole integer failed" + if (whole(5) != 5) return "whole integer failed" }, test_whole_non_number: function() { - if (whole("5") != null) throw "whole non-number should return null" + if (whole("5") != null) return "whole non-number should return null" }, test_fraction_positive: function() { var f = fraction(3.75) - if (f < 0.74 || f > 0.76) throw "fraction positive failed: " + f + if (f < 0.74 || f > 0.76) return "fraction positive failed: " + f }, test_fraction_negative: function() { var f = fraction(-3.75) - if (f > -0.74 || f < -0.76) throw "fraction negative failed: " + f + if (f > -0.74 || f < -0.76) return "fraction negative failed: " + f }, test_fraction_integer: function() { - if (fraction(5) != 0) throw "fraction integer failed" + if (fraction(5) != 0) return "fraction integer failed" }, test_fraction_non_number: function() { - if (fraction("5") != null) throw "fraction non-number should return null" + if (fraction("5") != null) return "fraction non-number should return null" }, // ============================================================================ @@ -2550,23 +2286,23 @@ return { // ============================================================================ test_neg_positive: function() { - if (neg(5) != -5) throw "neg positive failed" + if (neg(5) != -5) return "neg positive failed" }, test_neg_negative: function() { - if (neg(-5) != 5) throw "neg negative failed" + if (neg(-5) != 5) return "neg negative failed" }, test_neg_zero: function() { - if (neg(0) != 0) throw "neg zero failed" + if (neg(0) != 0) return "neg zero failed" }, test_neg_float: function() { - if (neg(3.14) != -3.14) throw "neg float failed" + if (neg(3.14) != -3.14) return "neg float failed" }, test_neg_non_number: function() { - if (neg("5") != null) throw "neg non-number should return null" + if (neg("5") != null) return "neg non-number should return null" }, // ============================================================================ @@ -2574,35 +2310,35 @@ return { // ============================================================================ test_modulo_positive: function() { - if (modulo(10, 3) != 1) throw "modulo positive failed" + if (modulo(10, 3) != 1) return "modulo positive failed" }, test_modulo_negative_dividend: function() { var result = modulo(-10, 3) - if (result != 2) throw "modulo negative dividend failed: " + result + if (result != 2) return "modulo negative dividend failed: " + result }, test_modulo_negative_divisor: function() { var result = modulo(10, -3) - if (result != -2) throw "modulo negative divisor failed: " + result + if (result != -2) return "modulo negative divisor failed: " + result }, test_modulo_both_negative: function() { var result = modulo(-10, -3) - if (result != -1) throw "modulo both negative failed: " + result + if (result != -1) return "modulo both negative failed: " + result }, test_modulo_zero_dividend: function() { - if (modulo(0, 5) != 0) throw "modulo zero dividend failed" + if (modulo(0, 5) != 0) return "modulo zero dividend failed" }, test_modulo_zero_divisor: function() { - if (modulo(10, 0) != null) throw "modulo zero divisor should return null" + if (modulo(10, 0) != null) return "modulo zero divisor should return null" }, test_modulo_floats: function() { var result = modulo(5.5, 2) - if (result < 1.4 || result > 1.6) throw "modulo floats failed: " + result + if (result < 1.4 || result > 1.6) return "modulo floats failed: " + result }, // ============================================================================ @@ -2610,62 +2346,62 @@ return { // ============================================================================ test_min_basic: function() { - if (min(3, 5) != 3) throw "min basic failed" + if (min(3, 5) != 3) return "min basic failed" }, test_min_equal: function() { - if (min(5, 5) != 5) throw "min equal failed" + if (min(5, 5) != 5) return "min equal failed" }, test_min_negative: function() { - if (min(-3, -5) != -5) throw "min negative failed" + if (min(-3, -5) != -5) return "min negative failed" }, test_min_mixed: function() { - if (min(-3, 5) != -3) throw "min mixed failed" + if (min(-3, 5) != -3) return "min mixed failed" }, test_min_float: function() { - if (min(3.14, 2.71) != 2.71) throw "min float failed" + if (min(3.14, 2.71) != 2.71) return "min float failed" }, test_min_non_number: function() { - if (min(3, "5") != null) throw "min non-number should return null" - if (min("3", 5) != null) throw "min first non-number should return null" + if (min(3, "5") != null) return "min non-number should return null" + if (min("3", 5) != null) return "min first non-number should return null" }, test_max_basic: function() { - if (max(3, 5) != 5) throw "max basic failed" + if (max(3, 5) != 5) return "max basic failed" }, test_max_equal: function() { - if (max(5, 5) != 5) throw "max equal failed" + if (max(5, 5) != 5) return "max equal failed" }, test_max_negative: function() { - if (max(-3, -5) != -3) throw "max negative failed" + if (max(-3, -5) != -3) return "max negative failed" }, test_max_mixed: function() { - if (max(-3, 5) != 5) throw "max mixed failed" + if (max(-3, 5) != 5) return "max mixed failed" }, test_max_float: function() { - if (max(3.14, 2.71) != 3.14) throw "max float failed" + if (max(3.14, 2.71) != 3.14) return "max float failed" }, test_max_non_number: function() { - if (max(3, "5") != null) throw "max non-number should return null" + if (max(3, "5") != null) return "max non-number should return null" }, test_min_max_constrain: function() { var val = 8 var constrained = min(max(val, 0), 10) - if (constrained != 8) throw "min max constrain in range failed" + if (constrained != 8) return "min max constrain in range failed" constrained = min(max(-5, 0), 10) - if (constrained != 0) throw "min max constrain below failed" + if (constrained != 0) return "min max constrain below failed" constrained = min(max(15, 0), 10) - if (constrained != 10) throw "min max constrain above failed" + if (constrained != 10) return "min max constrain above failed" }, // ============================================================================ @@ -2673,28 +2409,28 @@ return { // ============================================================================ test_codepoint_letter: function() { - if (codepoint("A") != 65) throw "codepoint A failed" - if (codepoint("a") != 97) throw "codepoint a failed" + if (codepoint("A") != 65) return "codepoint A failed" + if (codepoint("a") != 97) return "codepoint a failed" }, test_codepoint_digit: function() { - if (codepoint("0") != 48) throw "codepoint 0 failed" + if (codepoint("0") != 48) return "codepoint 0 failed" }, test_codepoint_unicode: function() { - if (codepoint("\u00E9") != 233) throw "codepoint unicode failed" + if (codepoint("\u00E9") != 233) return "codepoint unicode failed" }, test_codepoint_first_char: function() { - if (codepoint("ABC") != 65) throw "codepoint should return first char" + if (codepoint("ABC") != 65) return "codepoint should return first char" }, test_codepoint_empty: function() { - if (codepoint("") != null) throw "codepoint empty should return null" + if (codepoint("") != null) return "codepoint empty should return null" }, test_codepoint_non_text: function() { - if (codepoint(65) != null) throw "codepoint non-text should return null" + if (codepoint(65) != null) return "codepoint non-text should return null" }, // ============================================================================ @@ -2702,24 +2438,24 @@ return { // ============================================================================ test_character_letter: function() { - if (character(65) != "A") throw "character 65 failed" - if (character(97) != "a") throw "character 97 failed" + if (character(65) != "A") return "character 65 failed" + if (character(97) != "a") return "character 97 failed" }, test_character_digit: function() { - if (character(48) != "0") throw "character 48 failed" + if (character(48) != "0") return "character 48 failed" }, test_character_unicode: function() { - if (character(233) != "\u00E9") throw "character unicode failed" + if (character(233) != "\u00E9") return "character unicode failed" }, test_character_from_text: function() { - if (character("hello") != "h") throw "character from text failed" + if (character("hello") != "h") return "character from text failed" }, test_character_invalid: function() { - if (character(-1) != "") throw "character negative should return empty" + if (character(-1) != "") return "character negative should return empty" }, // ============================================================================ @@ -2727,32 +2463,32 @@ return { // ============================================================================ test_search_found: function() { - if (search("hello world", "world") != 6) throw "search found failed" + if (search("hello world", "world") != 6) return "search found failed" }, test_search_not_found: function() { - if (search("hello world", "xyz") != null) throw "search not found should return null" + if (search("hello world", "xyz") != null) return "search not found should return null" }, test_search_beginning: function() { - if (search("hello world", "hello") != 0) throw "search beginning failed" + if (search("hello world", "hello") != 0) return "search beginning failed" }, test_search_single_char: function() { - if (search("hello", "l") != 2) throw "search single char failed" + if (search("hello", "l") != 2) return "search single char failed" }, test_search_with_from: function() { - if (search("hello hello", "hello", 1) != 6) throw "search with from failed" + if (search("hello hello", "hello", 1) != 6) return "search with from failed" }, test_search_empty_pattern: function() { - if (search("hello", "") != 0) throw "search empty pattern failed" + if (search("hello", "") != 0) return "search empty pattern failed" }, test_search_negative_from: function() { var result = search("hello world", "world", -5) - if (result != 6) throw "search negative from failed: " + result + if (result != 6) return "search negative from failed: " + result }, // ============================================================================ @@ -2761,57 +2497,57 @@ return { test_replace_basic: function() { var result = replace("hello world", "world", "universe") - if (result != "hello universe") throw "replace basic failed" + if (result != "hello universe") return "replace basic failed" }, test_replace_not_found: function() { var result = replace("hello world", "xyz", "abc") - if (result != "hello world") throw "replace not found should return original" + if (result != "hello world") return "replace not found should return original" }, test_replace_multiple: function() { var result = replace("banana", "a", "o") - if (result != "bonono") throw "replace multiple failed: " + result + if (result != "bonono") return "replace multiple failed: " + result }, test_replace_with_limit: function() { var result = replace("banana", "a", "o", 1) - if (result != "bonana") throw "replace with limit failed: " + result + if (result != "bonana") return "replace with limit failed: " + result }, test_replace_empty_target: function() { var result = replace("abc", "", "-") - if (result != "-a-b-c-") throw "replace empty target failed: " + result + if (result != "-a-b-c-") return "replace empty target failed: " + result }, test_replace_to_empty: function() { var result = replace("hello", "l", "") - if (result != "heo") throw "replace to empty failed" + if (result != "heo") return "replace to empty failed" }, test_replace_with_function: function() { var result = replace("hello", "l", (match, pos) => `[${pos}]`) - if (result != "he[2][3]o") throw "replace with function failed: " + result + if (result != "he[2][3]o") return "replace with function failed: " + result }, test_replace_with_function_limit: function() { var result = replace("banana", "a", (match, pos) => `[${pos}]`, 2) - if (result != "b[1]n[3]na") throw "replace with function limit failed: " + result + if (result != "b[1]n[3]na") return "replace with function limit failed: " + result }, test_replace_with_regex: function() { var result = replace("banana", /a/, "o") - if (result != "bonono") throw "replace with regex failed" + if (result != "bonono") return "replace with regex failed" }, test_replace_with_regex_limit: function() { var result = replace("banana", /a/, "o", 2) - if (result != "bonona") throw "replace with regex limit failed: " + result + if (result != "bonona") return "replace with regex limit failed: " + result }, test_replace_with_regex_function: function() { var result = replace("hello", /l/, (match, pos) => `[${pos}]`) - if (result != "he[2][3]o") throw "replace with regex function failed: " + result + if (result != "he[2][3]o") return "replace with regex function failed: " + result }, // ============================================================================ @@ -2819,46 +2555,46 @@ return { // ============================================================================ test_text_number_basic: function() { - if (text(123) != "123") throw "text number basic failed" + if (text(123) != "123") return "text number basic failed" }, test_text_number_negative: function() { - if (text(-456) != "-456") throw "text number negative failed" + if (text(-456) != "-456") return "text number negative failed" }, test_text_number_float: function() { var result = text(3.14) - if (search(result, "3.14") != 0) throw "text number float failed" + if (search(result, "3.14") != 0) return "text number float failed" }, test_text_array_join_empty_sep: function() { var result = text(["a", "b", "c"], "") - if (result != "abc") throw "text array join empty sep failed" + if (result != "abc") return "text array join empty sep failed" }, test_text_slice_basic: function() { - if (text("hello", 1, 4) != "ell") throw "text slice basic failed" + if (text("hello", 1, 4) != "ell") return "text slice basic failed" }, test_text_slice_from_only: function() { - if (text("hello", 2) != "llo") throw "text slice from only failed" + if (text("hello", 2) != "llo") return "text slice from only failed" }, test_text_slice_negative_from: function() { - if (text("hello", -2) != "lo") throw "text slice negative from failed" + if (text("hello", -2) != "lo") return "text slice negative from failed" }, test_text_slice_negative_to: function() { - if (text("hello", 0, -2) != "hel") throw "text slice negative to failed" + if (text("hello", 0, -2) != "hel") return "text slice negative to failed" }, test_text_boolean: function() { - if (text(true) != "true") throw "text true failed" - if (text(false) != "false") throw "text false failed" + if (text(true) != "true") return "text true failed" + if (text(false) != "false") return "text false failed" }, test_text_null: function() { - if (text(null) != "null") throw "text null failed" + if (text(null) != "null") return "text null failed" }, // ============================================================================ @@ -2866,37 +2602,37 @@ return { // ============================================================================ test_number_from_string: function() { - if (number("123") != 123) throw "number from string failed" + if (number("123") != 123) return "number from string failed" }, test_number_from_negative_string: function() { - if (number("-456") != -456) throw "number from negative string failed" + if (number("-456") != -456) return "number from negative string failed" }, test_number_from_float_string: function() { - if (number("3.14") != 3.14) throw "number from float string failed" + if (number("3.14") != 3.14) return "number from float string failed" }, test_number_invalid_string: function() { - if (number("abc") != null) throw "number invalid string should return null" + if (number("abc") != null) return "number invalid string should return null" }, test_number_from_boolean: function() { - if (number(true) != 1) throw "number from true failed" - if (number(false) != 0) throw "number from false failed" + if (number(true) != 1) return "number from true failed" + if (number(false) != 0) return "number from false failed" }, test_number_from_number: function() { - if (number(42) != 42) throw "number from number failed" + if (number(42) != 42) return "number from number failed" }, test_number_with_radix: function() { - if (number("FF", 16) != 255) throw "number hex failed" - if (number("1010", 2) != 10) throw "number binary failed" + if (number("FF", 16) != 255) return "number hex failed" + if (number("1010", 2) != 10) return "number binary failed" }, test_number_leading_zeros: function() { - if (number("007") != 7) throw "number leading zeros failed" + if (number("007") != 7) return "number leading zeros failed" }, // ============================================================================ @@ -2905,57 +2641,57 @@ return { test_array_create_with_length: function() { var arr = array(5) - if (length(arr) != 5) throw "array create length failed" - if (arr[0] != null) throw "array create should init to null" + if (length(arr) != 5) return "array create length failed" + if (arr[0] != null) return "array create should init to null" }, test_array_create_with_initial: function() { var arr = array(3, 42) - if (arr[0] != 42 || arr[1] != 42 || arr[2] != 42) throw "array create with initial failed" + if (arr[0] != 42 || arr[1] != 42 || arr[2] != 42) return "array create with initial failed" }, test_array_create_with_function: function() { var arr = array(3, i => i * 2) - if (arr[0] != 0 || arr[1] != 2 || arr[2] != 4) throw "array create with function failed" + if (arr[0] != 0 || arr[1] != 2 || arr[2] != 4) return "array create with function failed" }, test_array_copy: function() { var orig = [1, 2, 3] var copy = array(orig) copy[0] = 99 - if (orig[0] != 1) throw "array copy should not affect original" + if (orig[0] != 1) return "array copy should not affect original" }, test_array_slice_basic: function() { var arr = [1, 2, 3, 4, 5] var sliced = array(arr, 1, 3) - if (length(sliced) != 2) throw "array slice length failed" - if (sliced[0] != 2 || sliced[1] != 3) throw "array slice values failed" + if (length(sliced) != 2) return "array slice length failed" + if (sliced[0] != 2 || sliced[1] != 3) return "array slice values failed" }, test_array_slice_negative: function() { var arr = [1, 2, 3, 4, 5] var sliced = array(arr, -3) - if (length(sliced) != 3) throw "array slice negative failed" - if (sliced[0] != 3) throw "array slice negative value failed" + if (length(sliced) != 3) return "array slice negative failed" + if (sliced[0] != 3) return "array slice negative value failed" }, test_array_from_object_keys: function() { var obj = {a: 1, b: 2, c: 3} var keys = array(obj) - if (length(keys) != 3) throw "array from object keys length failed" + if (length(keys) != 3) return "array from object keys length failed" }, test_array_from_text: function() { var arr = array("abc") - if (length(arr) != 3) throw "array from text length failed" - if (arr[0] != "a" || arr[1] != "b" || arr[2] != "c") throw "array from text values failed" + if (length(arr) != 3) return "array from text length failed" + if (arr[0] != "a" || arr[1] != "b" || arr[2] != "c") return "array from text values failed" }, test_array_split_text: function() { var arr = array("a,b,c", ",") - if (length(arr) != 3) throw "array split text length failed" - if (arr[1] != "b") throw "array split text value failed" + if (length(arr) != 3) return "array split text length failed" + if (arr[1] != "b") return "array split text value failed" }, // ============================================================================ @@ -2963,27 +2699,27 @@ return { // ============================================================================ test_trim_spaces: function() { - if (trim(" hello ") != "hello") throw "trim spaces failed" + if (trim(" hello ") != "hello") return "trim spaces failed" }, test_trim_tabs: function() { - if (trim("\thello\t") != "hello") throw "trim tabs failed" + if (trim("\thello\t") != "hello") return "trim tabs failed" }, test_trim_mixed: function() { - if (trim(" \t hello \n ") != "hello") throw "trim mixed failed" + if (trim(" \t hello \n ") != "hello") return "trim mixed failed" }, test_trim_no_whitespace: function() { - if (trim("hello") != "hello") throw "trim no whitespace failed" + if (trim("hello") != "hello") return "trim no whitespace failed" }, test_trim_empty: function() { - if (trim("") != "") throw "trim empty failed" + if (trim("") != "") return "trim empty failed" }, test_trim_all_whitespace: function() { - if (trim(" ") != "") throw "trim all whitespace failed" + if (trim(" ") != "") return "trim all whitespace failed" }, // ============================================================================ @@ -2991,35 +2727,35 @@ return { // ============================================================================ test_lower_basic: function() { - if (lower("HELLO") != "hello") throw "lower basic failed" + if (lower("HELLO") != "hello") return "lower basic failed" }, test_lower_mixed: function() { - if (lower("HeLLo WoRLD") != "hello world") throw "lower mixed failed" + if (lower("HeLLo WoRLD") != "hello world") return "lower mixed failed" }, test_lower_already_lower: function() { - if (lower("hello") != "hello") throw "lower already lower failed" + if (lower("hello") != "hello") return "lower already lower failed" }, test_lower_with_numbers: function() { - if (lower("ABC123") != "abc123") throw "lower with numbers failed" + if (lower("ABC123") != "abc123") return "lower with numbers failed" }, test_upper_basic: function() { - if (upper("hello") != "HELLO") throw "upper basic failed" + if (upper("hello") != "HELLO") return "upper basic failed" }, test_upper_mixed: function() { - if (upper("HeLLo WoRLD") != "HELLO WORLD") throw "upper mixed failed" + if (upper("HeLLo WoRLD") != "HELLO WORLD") return "upper mixed failed" }, test_upper_already_upper: function() { - if (upper("HELLO") != "HELLO") throw "upper already upper failed" + if (upper("HELLO") != "HELLO") return "upper already upper failed" }, test_upper_with_numbers: function() { - if (upper("abc123") != "ABC123") throw "upper with numbers failed" + if (upper("abc123") != "ABC123") return "upper with numbers failed" }, // ============================================================================ @@ -3029,30 +2765,30 @@ return { test_apply_basic: function() { var fn = function(a, b) { return a + b } var result = apply(fn, [3, 4]) - if (result != 7) throw "apply basic failed" + if (result != 7) return "apply basic failed" }, test_apply_no_args: function() { var fn = function() { return 42 } var result = apply(fn, []) - if (result != 42) throw "apply no args failed" + if (result != 42) return "apply no args failed" }, test_apply_single_arg: function() { var fn = function(x) { return x * 2 } var result = apply(fn, [5]) - if (result != 10) throw "apply single arg failed" + if (result != 10) return "apply single arg failed" }, test_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) throw "apply many args failed" + if (result != 10) return "apply many args failed" }, test_apply_non_function: function() { var result = apply(42, [1, 2]) - if (result != 42) throw "apply non-function should return first arg" + if (result != 42) return "apply non-function should return first arg" }, // ============================================================================ @@ -3062,7 +2798,7 @@ return { test_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) throw "call many args failed" + if (result != 26) return "call many args failed" }, test_call_method_style: function() { @@ -3071,15 +2807,15 @@ return { multiply: function(x) { return this.value * x } } var result = call(obj.multiply, obj, [5]) - if (result != 50) throw "call method style failed" + if (result != 50) return "call method style failed" }, test_call_change_this: function() { var obj1 = { value: 10 } var obj2 = { value: 20 } var fn = function() { return this.value } - if (call(fn, obj1) != 10) throw "call this obj1 failed" - if (call(fn, obj2) != 20) throw "call this obj2 failed" + if (call(fn, obj1) != 10) return "call this obj1 failed" + if (call(fn, obj2) != 20) return "call this obj2 failed" }, // ============================================================================ @@ -3090,27 +2826,27 @@ return { var arr = [1, 2, 3] var sum = 0 arrfor(arr, x => { sum = sum + x }) - if (sum != 6) throw "arrfor basic failed" + if (sum != 6) return "arrfor basic failed" }, test_arrfor_with_index: function() { var arr = ["a", "b", "c"] var indices = [] arrfor(arr, (x, i) => { push(indices, i) }) - if (indices[0] != 0 || indices[2] != 2) throw "arrfor with index failed" + if (indices[0] != 0 || indices[2] != 2) return "arrfor with index failed" }, test_arrfor_empty: function() { var called = false arrfor([], x => { called = true }) - if (called) throw "arrfor empty should not call function" + if (called) return "arrfor empty should not call function" }, test_arrfor_mutation: function() { var arr = [1, 2, 3] var results = [] arrfor(arr, x => { push(results, x * 2) }) - if (results[0] != 2 || results[1] != 4 || results[2] != 6) throw "arrfor mutation failed" + if (results[0] != 2 || results[1] != 4 || results[2] != 6) return "arrfor mutation failed" }, // ============================================================================ @@ -3120,14 +2856,14 @@ return { test_stone_returns_value: function() { var obj = {x: 1} var result = stone(obj) - if (result != obj) throw "stone should return the value" + if (result != obj) return "stone should return the value" }, test_stone_idempotent: function() { var obj = {x: 1} stone(obj) stone(obj) - if (!is_stone(obj)) throw "stone should be idempotent" + if (!is_stone(obj)) return "stone should be idempotent" }, // ============================================================================ @@ -3138,14 +2874,14 @@ return { var grandparent = {a: 1} var parent = meme(grandparent) var child = meme(parent) - if (proto(child) != parent) throw "proto chain child->parent failed" - if (proto(parent) != grandparent) throw "proto chain parent->grandparent failed" + if (proto(child) != parent) return "proto chain child->parent failed" + if (proto(parent) != grandparent) return "proto chain parent->grandparent failed" }, test_proto_array: function() { var arr = [1, 2, 3] var p = proto(arr) - if (p == null) throw "proto of array should not be null" + if (p == null) return "proto of array should not be null" }, // ============================================================================ @@ -3157,7 +2893,7 @@ return { greet: function() { return "hello" } } var child = meme(parent) - if (child.greet() != "hello") throw "meme method inheritance failed" + if (child.greet() != "hello") return "meme method inheritance failed" }, test_meme_this_in_inherited_method: function() { @@ -3166,7 +2902,7 @@ return { } var child = meme(parent) child.value = 42 - if (child.getValue() != 42) throw "meme this in inherited method failed" + if (child.getValue() != 42) return "meme this in inherited method failed" }, test_meme_deep_chain: function() { @@ -3174,7 +2910,7 @@ return { var b = meme(a) var c = meme(b) var d = meme(c) - if (d.x != 1) throw "meme deep chain failed" + if (d.x != 1) return "meme deep chain failed" }, // ============================================================================ @@ -3184,25 +2920,20 @@ return { test_delete_property: function() { var obj = {a: 1, b: 2} delete obj.a - if ("a" in obj) throw "delete property failed" - if (obj.b != 2) throw "delete should not affect other properties" + if ("a" in obj) return "delete property failed" + if (obj.b != 2) return "delete should not affect other properties" }, test_delete_array_element: function() { var arr = [1, 2, 3] - var caught = false - try { - delete arr[1] - } catch (e) { - caught = true - } - if (!caught) throw "delete on array element should throw" + var caught = should_disrupt(function() { delete arr[1] }) + if (!caught) return "delete on array element should throw" }, test_delete_nonexistent: function() { var obj = {a: 1} delete obj.b - if (obj.a != 1) throw "delete nonexistent should not affect object" + if (obj.a != 1) return "delete nonexistent should not affect object" }, // ============================================================================ @@ -3210,9 +2941,9 @@ return { // ============================================================================ test_is_integer: function() { - if (!is_number(5) || 5 % 1 != 0) throw "is_integer positive failed" - if (!is_number(-5) || -5 % 1 != 0) throw "is_integer negative failed" - if (is_number(5.5) && 5.5 % 1 == 0) throw "is_integer float should not be integer" + if (!is_number(5) || 5 % 1 != 0) return "is_integer positive failed" + if (!is_number(-5) || -5 % 1 != 0) return "is_integer negative failed" + if (is_number(5.5) && 5.5 % 1 == 0) return "is_integer float should not be integer" }, // ============================================================================ @@ -3222,19 +2953,19 @@ return { test_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) throw "array map basic failed" + if (doubled[0] != 2 || doubled[1] != 4 || doubled[2] != 6) return "array map basic failed" }, test_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") throw "array map with index failed" + if (result[0] != "a0" || result[1] != "b1") return "array map with index failed" }, test_array_map_reverse: function() { var arr = [1, 2, 3] var result = array(arr, x => x * 2, true) - if (result[0] != 6 || result[2] != 2) throw "array map reverse failed" + if (result[0] != 6 || result[2] != 2) return "array map reverse failed" }, test_array_map_with_exit: function() { @@ -3243,7 +2974,7 @@ return { if (x > 3) return null return x * 2 }, false, null) - if (length(result) != 5) throw "array map with exit length unexpected" + if (length(result) != 5) return "array map with exit length unexpected" }, // ============================================================================ @@ -3252,17 +2983,12 @@ return { test_error_creation: function() { var e = Error("test message") - if (e.message != "test message") throw "Error creation failed" + if (e.message != "test message") return "Error creation failed" }, - test_throw_error_object: function() { - var caught = null - try { - throw Error("my error") - } catch (e) { - caught = e - } - if (!caught || caught.message != "my error") throw "throw Error object failed" + test_disrupt_error_object: function() { + var caught = should_disrupt(function() { disrupt }) + if (!caught) return "disrupt should trigger disruption" }, // ============================================================================ @@ -3270,21 +2996,21 @@ return { // ============================================================================ test_string_startsWith: function() { - if (!starts_with("hello", "hel")) throw "startsWith match failed" - if (starts_with("hello", "ell")) throw "startsWith no match failed" - if (!starts_with("hello", "")) throw "startsWith empty should match" + if (!starts_with("hello", "hel")) return "startsWith match failed" + if (starts_with("hello", "ell")) return "startsWith no match failed" + if (!starts_with("hello", "")) return "startsWith empty should match" }, test_string_endsWith: function() { - if (!ends_with("hello", "llo")) throw "endsWith match failed" - if (ends_with("hello", "ell")) throw "endsWith no match failed" - if (!ends_with("hello", "")) throw "endsWith empty should match" + if (!ends_with("hello", "llo")) return "endsWith match failed" + if (ends_with("hello", "ell")) return "endsWith no match failed" + if (!ends_with("hello", "")) return "endsWith empty should match" }, test_string_includes: function() { - if (search("hello world", "world") == null) throw "includes match failed" - if (search("hello", "xyz") != null) throw "includes no match failed" - if (search("hello", "") == null) throw "includes empty should match" + if (search("hello world", "world") == null) return "includes match failed" + if (search("hello", "xyz") != null) return "includes no match failed" + if (search("hello", "") == null) return "includes empty should match" }, // ============================================================================ @@ -3293,21 +3019,21 @@ return { test_array_includes: function() { var arr = [1, 2, 3] - if (find(arr, 2) == null) throw "array includes match failed" - if (find(arr, 5) != null) throw "array includes no match failed" + if (find(arr, 2) == null) return "array includes match failed" + if (find(arr, 5) != null) return "array includes no match failed" }, test_array_every: function() { var arr = [2, 4, 6] - if (!every(arr, x => x % 2 == 0)) throw "array every all pass failed" + if (!every(arr, x => x % 2 == 0)) return "array every all pass failed" arr = [2, 3, 6] - if (every(arr, x => x % 2 == 0)) throw "array every not all pass failed" + if (every(arr, x => x % 2 == 0)) return "array every not all pass failed" }, test_array_some: function() { var arr = [1, 2, 3] - if (!some(arr, x => x > 2)) throw "array some match failed" - if (some(arr, x => x > 5)) throw "array some no match failed" + if (!some(arr, x => x > 2)) return "array some match failed" + if (some(arr, x => x > 5)) return "array some no match failed" }, // ============================================================================ @@ -3315,37 +3041,37 @@ return { // ============================================================================ test_logical_zero: function() { - if (logical(0) != false) throw "logical(0) should be false" + if (logical(0) != false) return "logical(0) should be false" }, test_logical_one: function() { - if (logical(1) != true) throw "logical(1) should be true" + if (logical(1) != true) return "logical(1) should be true" }, test_logical_string_true: function() { - if (logical("true") != true) throw "logical('true') should be true" + if (logical("true") != true) return "logical('true') should be true" }, test_logical_string_false: function() { - if (logical("false") != false) throw "logical('false') should be false" + if (logical("false") != false) return "logical('false') should be false" }, test_logical_boolean_true: function() { - if (logical(true) != true) throw "logical(true) should be true" + if (logical(true) != true) return "logical(true) should be true" }, test_logical_boolean_false: function() { - if (logical(false) != false) throw "logical(false) should be false" + if (logical(false) != false) return "logical(false) should be false" }, test_logical_null: function() { - if (logical(null) != false) throw "logical(null) should be false" + if (logical(null) != false) return "logical(null) should be false" }, test_logical_invalid: function() { - if (logical("invalid") != null) throw "logical('invalid') should be null" - if (logical(42) != null) throw "logical(42) should be null" - if (logical({}) != null) throw "logical({}) should be null" + if (logical("invalid") != null) return "logical('invalid') should be null" + if (logical(42) != null) return "logical(42) should be null" + if (logical({}) != null) return "logical({}) should be null" }, // ============================================================================ @@ -3354,30 +3080,30 @@ return { test_nested_array_access: function() { var arr = [[[1, 2], [3, 4]], [[5, 6], [7, 8]]] - if (arr[0][1][0] != 3) throw "nested array access failed" - if (arr[1][1][1] != 8) throw "nested array access deep failed" + if (arr[0][1][0] != 3) return "nested array access failed" + if (arr[1][1][1] != 8) return "nested array access deep failed" }, test_nested_object_access: function() { var obj = {a: {b: {c: {d: 42}}}} - if (obj.a.b.c.d != 42) throw "nested object access failed" + if (obj.a.b.c.d != 42) return "nested object access failed" }, test_mixed_nested_access: function() { var data = {users: [{name: "Alice"}, {name: "Bob"}]} - if (data.users[1].name != "Bob") throw "mixed nested access failed" + if (data.users[1].name != "Bob") return "mixed nested access failed" }, test_object_with_null_value: function() { var obj = {a: null, b: 2} - if (obj.a != null) throw "object null value failed" - if (!("a" in obj)) throw "object with null should have key" + if (obj.a != null) return "object null value failed" + if (!("a" in obj)) return "object with null should have key" }, test_array_with_null_values: function() { var arr = [1, null, 3] - if (arr[1] != null) throw "array null value failed" - if (length(arr) != 3) throw "array with null length failed" + if (arr[1] != null) return "array null value failed" + if (length(arr) != 3) return "array with null length failed" }, test_function_returning_function: function() { @@ -3388,91 +3114,91 @@ return { } } } - if (outer(1)(2)(3) != 6) throw "function returning function failed" + if (outer(1)(2)(3) != 6) return "function returning function failed" }, test_immediately_invoked_function: function() { var result = (function(x) { return x * 2 })(21) - if (result != 42) throw "immediately invoked function failed" + if (result != 42) return "immediately invoked function failed" }, test_text_split_text: function() { var text = "hello world" var result = array(text, " ") - if (length(result) != 2) throw "text split failed" - if (result[0] != "hello") throw "text split failed" - if (result[1] != "world") throw "text split failed" + if (length(result) != 2) return "text split failed" + if (result[0] != "hello") return "text split failed" + if (result[1] != "world") return "text split failed" }, test_text_split_regex: function() { var text = "hello world" var result = array(text, /\s+/) - if (length(result) != 2) throw "text split failed" - if (result[0] != "hello") throw "text split failed" - if (result[1] != "world") throw "text split failed" + if (length(result) != 2) return "text split failed" + if (result[0] != "hello") return "text split failed" + if (result[1] != "world") return "text split failed" }, test_text_search_text: function() { var text = "hello world" var result = search(text, "world") - if (result != 6) throw "text search failed" + if (result != 6) return "text search failed" }, test_text_search_regex: function() { var text = "hello world" var result = search(text, /world/) - if (result != 6) throw "text search failed" + if (result != 6) return "text search failed" }, test_extract_basic_text: function() { var text = "hello world" var result = extract(text, "world") - if (result[0] != "world") throw "extract basic text failed" + if (result[0] != "world") return "extract basic text failed" }, test_extract_text_not_found: function() { var text = "hello world" var result = extract(text, "xyz") - if (result != null) throw "extract not found should return null" + if (result != null) return "extract not found should return null" }, test_extract_regex_basic: function() { var text = "hello world" var result = extract(text, /world/) - if (result[0] != "world") throw "extract regex basic failed" + if (result[0] != "world") return "extract regex basic failed" }, test_extract_regex_with_capture_group: function() { var text = "hello world" var result = extract(text, /(\w+) (\w+)/) - if (result[0] != "hello world") throw "extract regex full match failed" - if (result[1] != "hello") throw "extract regex capture group 1 failed" - if (result[2] != "world") throw "extract regex capture group 2 failed" + if (result[0] != "hello world") return "extract regex full match failed" + if (result[1] != "hello") return "extract regex capture group 1 failed" + if (result[2] != "world") return "extract regex capture group 2 failed" }, test_extract_regex_digits: function() { var text = "abc123def456" var result = extract(text, /(\d+)/) - if (result[0] != "123") throw "extract regex digits failed" - if (result[1] != "123") throw "extract regex digits capture failed" + if (result[0] != "123") return "extract regex digits failed" + if (result[1] != "123") return "extract regex digits capture failed" }, test_extract_with_from: function() { var text = "hello hello world" var result = extract(text, "hello", 1) - if (result[0] != "hello") throw "extract with from failed" + if (result[0] != "hello") return "extract with from failed" }, test_extract_with_from_to: function() { var text = "hello world hello" var result = extract(text, "hello", 0, 10) - if (result[0] != "hello") throw "extract with from to failed" + if (result[0] != "hello") return "extract with from to failed" }, test_extract_regex_case_insensitive: function() { var text = "Hello World" var result = extract(text, /hello/i) - if (result[0] != "Hello") throw "extract regex case insensitive failed" + if (result[0] != "Hello") return "extract regex case insensitive failed" }, // ============================================================================ @@ -3482,7 +3208,7 @@ return { test_gc_cycle_object_self: function() { var obj = {name: "root"} obj.self = obj - if (obj.self != obj) throw "self cycle failed" + if (obj.self != obj) return "self cycle failed" }, test_gc_cycle_array_self: function() { @@ -3490,21 +3216,21 @@ return { for (var i = 0; i < 10; i++) { push(arr, arr) } - if (arr[0] != arr) throw "array self cycle failed" + if (arr[0] != arr) return "array self cycle failed" }, test_gc_cycle_object_array_pair: function() { var obj = {kind: "node"} var arr = [obj] obj.arr = arr - if (obj.arr[0] != obj) throw "object/array cycle failed" + if (obj.arr[0] != obj) return "object/array cycle failed" }, test_gc_shared_references: function() { var shared = {value: 42} var a = {ref: shared} var b = {ref: shared} - if (a.ref != shared || b.ref != shared) throw "shared reference failed" + if (a.ref != shared || b.ref != shared) return "shared reference failed" }, test_gc_object_key_cycle: function() { @@ -3513,7 +3239,7 @@ return { var o = {} o[k] = v v.back = o - if (o[k].back != o) throw "object key cycle failed" + if (o[k].back != o) return "object key cycle failed" }, test_gc_object_text_key_mix: function() { @@ -3522,8 +3248,8 @@ return { var inner = {token: "x"} obj[key] = inner obj["beta"] = [inner, obj] - if (obj.alpha.token != "x") throw "text key value failed" - if (obj.beta[1] != obj) throw "text key cycle failed" + if (obj.alpha.token != "x") return "text key value failed" + if (obj.beta[1] != obj) return "text key cycle failed" }, // ============================================================================ @@ -3533,55 +3259,55 @@ return { test_object_shallow_copy: function() { var orig = {a: 1, b: 2, c: 3} var copy = object(orig) - if (copy.a != 1) throw "object copy a failed" - if (copy.b != 2) throw "object copy b failed" - if (copy.c != 3) throw "object copy c failed" + if (copy.a != 1) return "object copy a failed" + if (copy.b != 2) return "object copy b failed" + if (copy.c != 3) return "object copy c failed" copy.a = 99 - if (orig.a != 1) throw "object copy should not mutate original" + if (orig.a != 1) return "object copy should not mutate original" }, test_object_combine: function() { var obj1 = {a: 1, b: 2} var obj2 = {c: 3, d: 4} var combined = object(obj1, obj2) - if (combined.a != 1) throw "object combine a failed" - if (combined.b != 2) throw "object combine b failed" - if (combined.c != 3) throw "object combine c failed" - if (combined.d != 4) throw "object combine d failed" + if (combined.a != 1) return "object combine a failed" + if (combined.b != 2) return "object combine b failed" + if (combined.c != 3) return "object combine c failed" + if (combined.d != 4) return "object combine d failed" }, test_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) throw "object combine override a failed" - if (combined.b != 99) throw "object combine should override with second arg" - if (combined.c != 3) throw "object combine override c failed" + if (combined.a != 1) return "object combine override a failed" + if (combined.b != 99) return "object combine should override with second arg" + if (combined.c != 3) return "object combine override c failed" }, test_object_select_keys: function() { var orig = {a: 1, b: 2, c: 3, d: 4} var selected = object(orig, ["a", "c"]) - if (selected.a != 1) throw "object select a failed" - if (selected.c != 3) throw "object select c failed" - if (selected.b != null) throw "object select should not include b" - if (selected.d != null) throw "object select should not include d" + if (selected.a != 1) return "object select a failed" + if (selected.c != 3) return "object select c failed" + if (selected.b != null) return "object select should not include b" + if (selected.d != null) return "object select should not include d" }, test_object_from_keys_true: function() { var keys = ["x", "y", "z"] var obj = object(keys) - if (obj.x != true) throw "object from keys x failed" - if (obj.y != true) throw "object from keys y failed" - if (obj.z != true) throw "object from keys z failed" + if (obj.x != true) return "object from keys x failed" + if (obj.y != true) return "object from keys y failed" + if (obj.z != true) return "object from keys z failed" }, test_object_from_keys_function: function() { var keys = ["a", "b", "c"] var obj = object(keys, function(k) { return k + "_val" }) - if (obj.a != "a_val") throw "object from keys func a failed" - if (obj.b != "b_val") throw "object from keys func b failed" - if (obj.c != "c_val") throw "object from keys func c failed" + if (obj.a != "a_val") return "object from keys func a failed" + if (obj.b != "b_val") return "object from keys func b failed" + if (obj.c != "c_val") return "object from keys func c failed" }, // ============================================================================ @@ -3593,9 +3319,9 @@ return { var obj = {z: 30} obj.__proto__ = proto var flat = splat(obj) - if (flat.x != 10) throw "splat x failed" - if (flat.y != 20) throw "splat y failed" - if (flat.z != 30) throw "splat z failed" + if (flat.x != 10) return "splat x failed" + if (flat.y != 20) return "splat y failed" + if (flat.z != 30) return "splat z failed" }, // ============================================================================ @@ -3605,12 +3331,12 @@ return { test_reverse_array: function() { var arr = [1, 2, 3, 4, 5] var rev = reverse(arr) - if (rev[0] != 5) throw "reverse[0] failed" - if (rev[1] != 4) throw "reverse[1] failed" - if (rev[2] != 3) throw "reverse[2] failed" - if (rev[3] != 2) throw "reverse[3] failed" - if (rev[4] != 1) throw "reverse[4] failed" - if (arr[0] != 1) throw "reverse should not mutate original" + if (rev[0] != 5) return "reverse[0] failed" + if (rev[1] != 4) return "reverse[1] failed" + if (rev[2] != 3) return "reverse[2] failed" + if (rev[3] != 2) return "reverse[3] failed" + if (rev[4] != 1) return "reverse[4] failed" + if (arr[0] != 1) return "reverse should not mutate original" }, // ============================================================================ @@ -3620,19 +3346,19 @@ return { test_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) throw "apply with array args failed" + if (result != 6) return "apply with array args failed" }, test_apply_with_no_args: function() { def ret42 = function() { return 42 } var result = fn.apply(ret42) - if (result != 42) throw "apply with no args failed" + if (result != 42) return "apply with no args failed" }, test_apply_with_single_value: function() { def double = function(x) { return x * 2 } var result = fn.apply(double, 10) - if (result != 20) throw "apply with single value failed" + if (result != 20) return "apply with single value failed" }, // ============================================================================ @@ -3648,7 +3374,7 @@ return { // Now reverse each one - this tests re-chase after allocation for (var i = 0; i < 100; i = i + 1) { var rev = reverse(arrays[i]) - if (rev[0] != i+4) throw "gc reverse stress failed at " + text(i) + if (rev[0] != i+4) return "gc reverse stress failed at " + text(i) } }, @@ -3661,8 +3387,8 @@ return { // Select keys - tests re-chase in loop for (var i = 0; i < 100; i = i + 1) { var selected = object(objs[i], ["a", "c"]) - if (selected.a != i) throw "gc object select stress failed at " + text(i) - if (selected.c != i+2) throw "gc object select stress c failed at " + text(i) + if (selected.a != i) return "gc object select stress failed at " + text(i) + if (selected.c != i+2) return "gc object select stress c failed at " + text(i) } }, @@ -3676,7 +3402,7 @@ return { for (var i = 0; i < 50; i = i + 1) { var obj = object(keysets[i], function(k) { return k + "_value" }) var expected = "k" + text(i) + "_value" - if (obj["k" + text(i)] != expected) throw "gc object from keys func stress failed at " + text(i) + if (obj["k" + text(i)] != expected) return "gc object from keys func stress failed at " + text(i) } }, diff --git a/tests/underling_actor.ce b/tests/underling_actor.ce index e4fb6f26..96b15f90 100644 --- a/tests/underling_actor.ce +++ b/tests/underling_actor.ce @@ -1,7 +1,7 @@ var cmds = { stop: $stop, disrupt: _ => { - $delay(_ => { throw Error() }, 0.5) + $delay(_ => { disrupt }, 0.5) } } diff --git a/tests/use.cm b/tests/use.cm index 5d78bc95..050c1a74 100644 --- a/tests/use.cm +++ b/tests/use.cm @@ -1,5 +1,10 @@ -try { +var load_disrupted = false +var do_load = function() { var u = use('tests/use') -} catch(e) { - log.console(e) +} disruption { + load_disrupted = true +} +do_load() +if (load_disrupted) { + log.console("use self-load disrupted") }