fix tests; add comprehensive tests for functions and fix bugs in the mach VM regarding them.
This commit is contained in:
18
mcode.cm
18
mcode.cm
@@ -951,6 +951,19 @@ var mcode = function(ast) {
|
||||
return null
|
||||
}
|
||||
|
||||
// --- Helper: guard that reverse param is logical (or null) ---
|
||||
// Disrupts if rev_slot is not null and not a boolean.
|
||||
var emit_reverse_guard = function(rev_slot, msg) {
|
||||
var ok_label = gen_label("rev_ok")
|
||||
var g = alloc_slot()
|
||||
emit_jump_cond("jump_null", rev_slot, ok_label)
|
||||
emit_2("is_bool", g, rev_slot)
|
||||
emit_jump_cond("jump_true", g, ok_label)
|
||||
emit_log_error(msg)
|
||||
emit_0("disrupt")
|
||||
emit_label(ok_label)
|
||||
}
|
||||
|
||||
// --- Helper: forward loop scaffolding ---
|
||||
// L = {arr, len, i, check, item, one, loop_label, done_label}
|
||||
// body_fn(L) — called between element load and increment
|
||||
@@ -1068,6 +1081,7 @@ var mcode = function(ast) {
|
||||
if (nargs <= 2) {
|
||||
emit_forward_loop(fwd_L, body_fn)
|
||||
} else {
|
||||
emit_reverse_guard(args.rev, "arrfor: reverse must be a logical")
|
||||
emit_jump_cond("wary_true", args.rev, rev_label)
|
||||
emit_forward_loop(fwd_L, body_fn)
|
||||
emit_jump(final_label)
|
||||
@@ -1289,6 +1303,9 @@ var mcode = function(ast) {
|
||||
emit_2("int", zero, 0)
|
||||
emit_2("int", one, 1)
|
||||
emit_1("null", null_s)
|
||||
if (nargs > 2) {
|
||||
emit_reverse_guard(args.rev, "find: reverse must be a logical")
|
||||
}
|
||||
emit_2("is_func", is_fn, target)
|
||||
emit_jump_cond("jump_true", is_fn, fn_mode_label)
|
||||
// === Value mode ===
|
||||
@@ -1523,6 +1540,7 @@ var mcode = function(ast) {
|
||||
d2 = gen_label("reduce_d2")
|
||||
d3 = gen_label("reduce_d3")
|
||||
d4 = gen_label("reduce_d4")
|
||||
emit_reverse_guard(rev_slot, "reduce: reverse must be a logical")
|
||||
emit_2("is_null", check, init_slot)
|
||||
emit_jump_cond("jump_false", check, has_init)
|
||||
// No initial
|
||||
|
||||
@@ -267,6 +267,19 @@ JS_BOOL JS_IsBlob(JSValue v);
|
||||
JS_BOOL JS_IsText(JSValue v);
|
||||
JS_BOOL JS_IsStone(JSValue v);
|
||||
|
||||
/* Sensory function wrappers (require JSContext for string inspection) */
|
||||
JS_BOOL JS_IsDigit(JSContext *ctx, JSValue val);
|
||||
JS_BOOL JS_IsLetter(JSContext *ctx, JSValue val);
|
||||
JS_BOOL JS_IsLower(JSContext *ctx, JSValue val);
|
||||
JS_BOOL JS_IsUpper(JSContext *ctx, JSValue val);
|
||||
JS_BOOL JS_IsWhitespace(JSContext *ctx, JSValue val);
|
||||
JS_BOOL JS_IsCharacter(JSContext *ctx, JSValue val);
|
||||
JS_BOOL JS_IsFit(JSContext *ctx, JSValue val);
|
||||
JS_BOOL JS_IsData(JSValue val);
|
||||
static inline JS_BOOL JS_IsTrue(JSValue v) { return v == JS_TRUE; }
|
||||
static inline JS_BOOL JS_IsFalse(JSValue v) { return v == JS_FALSE; }
|
||||
JS_BOOL JS_IsActor(JSContext *ctx, JSValue val);
|
||||
|
||||
/* ============================================================
|
||||
GC References — no-ops with copying GC
|
||||
============================================================ */
|
||||
@@ -559,6 +572,14 @@ JSValue JS_CellObject (JSContext *ctx, JSValue proto, JSValue props);
|
||||
/* Format */
|
||||
JSValue JS_CellFormat (JSContext *ctx, JSValue text, JSValue collection, JSValue transformer);
|
||||
|
||||
/* Additional cell functions */
|
||||
JSValue JS_CellLogical (JSContext *ctx, JSValue val);
|
||||
JSValue JS_CellEvery (JSContext *ctx, JSValue arr, JSValue pred);
|
||||
JSValue JS_CellSome (JSContext *ctx, JSValue arr, JSValue pred);
|
||||
JSValue JS_CellStartsWith (JSContext *ctx, JSValue text, JSValue prefix);
|
||||
JSValue JS_CellEndsWith (JSContext *ctx, JSValue text, JSValue suffix);
|
||||
JSValue JS_CellNormalize (JSContext *ctx, JSValue text);
|
||||
|
||||
/* Output helpers */
|
||||
void JS_PrintText (JSContext *ctx, JSValue val);
|
||||
void JS_PrintTextLn (JSContext *ctx, JSValue val);
|
||||
|
||||
@@ -2486,14 +2486,32 @@ vm_dispatch:
|
||||
VM_CASE(MACH_IS_WS): {
|
||||
JSValue v = frame->slots[b];
|
||||
int result = 0;
|
||||
if (MIST_IsImmediateASCII(v) && MIST_GetImmediateASCIILen(v) == 1) {
|
||||
int ch = MIST_GetImmediateASCIIChar(v, 0);
|
||||
result = (ch == ' ' || ch == '\t' || ch == '\n'
|
||||
|| ch == '\r' || ch == '\f' || ch == '\v');
|
||||
} else if (mist_is_text(v) && js_string_value_len(v) == 1) {
|
||||
uint32_t ch = js_string_value_get(v, 0);
|
||||
result = (ch == ' ' || ch == '\t' || ch == '\n'
|
||||
|| ch == '\r' || ch == '\f' || ch == '\v');
|
||||
if (MIST_IsImmediateASCII(v)) {
|
||||
int len = MIST_GetImmediateASCIILen(v);
|
||||
if (len > 0) {
|
||||
result = 1;
|
||||
for (int i = 0; i < len; i++) {
|
||||
int ch = MIST_GetImmediateASCIIChar(v, i);
|
||||
if (!(ch == ' ' || ch == '\t' || ch == '\n'
|
||||
|| ch == '\r' || ch == '\f' || ch == '\v')) {
|
||||
result = 0;
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
} else if (mist_is_text(v)) {
|
||||
int len = js_string_value_len(v);
|
||||
if (len > 0) {
|
||||
result = 1;
|
||||
for (int i = 0; i < len; i++) {
|
||||
uint32_t ch = js_string_value_get(v, i);
|
||||
if (!(ch == ' ' || ch == '\t' || ch == '\n'
|
||||
|| ch == '\r' || ch == '\f' || ch == '\v')) {
|
||||
result = 0;
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
frame->slots[a] = JS_NewBool(ctx, result);
|
||||
VM_BREAK();
|
||||
|
||||
136
source/runtime.c
136
source/runtime.c
@@ -11503,15 +11503,20 @@ static JSValue js_cell_is_upper (JSContext *ctx, JSValue this_val, int argc, JSV
|
||||
return JS_NewBool (ctx, c >= 'A' && c <= 'Z');
|
||||
}
|
||||
|
||||
/* is_whitespace(val) - check if value is a single whitespace character */
|
||||
/* is_whitespace(val) - check if all characters are whitespace (non-empty) */
|
||||
static JSValue js_cell_is_whitespace (JSContext *ctx, JSValue this_val, int argc, JSValue *argv) {
|
||||
if (argc < 1) return JS_FALSE;
|
||||
JSValue val = argv[0];
|
||||
if (!JS_IsText (val)) return JS_FALSE;
|
||||
if (js_string_value_len (val) != 1) return JS_FALSE;
|
||||
uint32_t c = js_string_value_get (val, 0);
|
||||
return JS_NewBool (ctx, c == ' ' || c == '\t' || c == '\n'
|
||||
|| c == '\r' || c == '\f' || c == '\v');
|
||||
int len = js_string_value_len (val);
|
||||
if (len == 0) return JS_FALSE;
|
||||
for (int i = 0; i < len; i++) {
|
||||
uint32_t c = js_string_value_get (val, i);
|
||||
if (!(c == ' ' || c == '\t' || c == '\n'
|
||||
|| c == '\r' || c == '\f' || c == '\v'))
|
||||
return JS_FALSE;
|
||||
}
|
||||
return JS_TRUE;
|
||||
}
|
||||
|
||||
/* is_proto(val, master) - check if val has master in prototype chain */
|
||||
@@ -11571,6 +11576,47 @@ static JSValue js_cell_logical(JSContext *ctx, JSValue this_val, int argc, JSVal
|
||||
return JS_NULL;
|
||||
}
|
||||
|
||||
/* realloc wrapper for unicode_normalize */
|
||||
static void *normalize_realloc(void *opaque, void *ptr, size_t size) {
|
||||
(void)opaque;
|
||||
if (size == 0) {
|
||||
pjs_free(ptr);
|
||||
return NULL;
|
||||
}
|
||||
return pjs_realloc(ptr, size);
|
||||
}
|
||||
|
||||
/* normalize(text) — NFC normalization */
|
||||
static JSValue js_cell_normalize(JSContext *ctx, JSValue this_val, int argc, JSValue *argv) {
|
||||
if (argc < 1) return JS_NULL;
|
||||
JSValue val = argv[0];
|
||||
if (!JS_IsText(val)) return JS_NULL;
|
||||
int len = js_string_value_len(val);
|
||||
if (len == 0) return JS_NewString(ctx, "");
|
||||
uint32_t *src = pjs_malloc(len * sizeof(uint32_t));
|
||||
if (!src) return JS_EXCEPTION;
|
||||
for (int i = 0; i < len; i++)
|
||||
src[i] = js_string_value_get(val, i);
|
||||
uint32_t *dst = NULL;
|
||||
int dst_len = unicode_normalize(&dst, src, len, UNICODE_NFC, NULL,
|
||||
normalize_realloc);
|
||||
pjs_free(src);
|
||||
if (dst_len < 0) {
|
||||
pjs_free(dst);
|
||||
return JS_NULL;
|
||||
}
|
||||
JSText *str = js_alloc_string(ctx, dst_len);
|
||||
if (!str) {
|
||||
pjs_free(dst);
|
||||
return JS_EXCEPTION;
|
||||
}
|
||||
for (int i = 0; i < dst_len; i++)
|
||||
string_put(str, i, dst[i]);
|
||||
str->length = dst_len;
|
||||
pjs_free(dst);
|
||||
return pretext_end(ctx, str);
|
||||
}
|
||||
|
||||
/* starts_with(str, prefix) — search(str, prefix) == 0 */
|
||||
static JSValue js_cell_starts_with(JSContext *ctx, JSValue this_val, int argc, JSValue *argv) {
|
||||
if (argc < 2) return JS_NULL;
|
||||
@@ -11616,6 +11662,85 @@ static JSValue js_cell_some(JSContext *ctx, JSValue this_val, int argc, JSValue
|
||||
return JS_FALSE;
|
||||
}
|
||||
|
||||
/* C API: Type-check wrappers for sensory functions */
|
||||
JS_BOOL JS_IsDigit (JSContext *ctx, JSValue val) {
|
||||
JSValue r = js_cell_is_digit (ctx, JS_NULL, 1, &val);
|
||||
return JS_VALUE_GET_BOOL (r);
|
||||
}
|
||||
|
||||
JS_BOOL JS_IsLetter (JSContext *ctx, JSValue val) {
|
||||
JSValue r = js_cell_is_letter (ctx, JS_NULL, 1, &val);
|
||||
return JS_VALUE_GET_BOOL (r);
|
||||
}
|
||||
|
||||
JS_BOOL JS_IsLower (JSContext *ctx, JSValue val) {
|
||||
JSValue r = js_cell_is_lower (ctx, JS_NULL, 1, &val);
|
||||
return JS_VALUE_GET_BOOL (r);
|
||||
}
|
||||
|
||||
JS_BOOL JS_IsUpper (JSContext *ctx, JSValue val) {
|
||||
JSValue r = js_cell_is_upper (ctx, JS_NULL, 1, &val);
|
||||
return JS_VALUE_GET_BOOL (r);
|
||||
}
|
||||
|
||||
JS_BOOL JS_IsWhitespace (JSContext *ctx, JSValue val) {
|
||||
JSValue r = js_cell_is_whitespace (ctx, JS_NULL, 1, &val);
|
||||
return JS_VALUE_GET_BOOL (r);
|
||||
}
|
||||
|
||||
JS_BOOL JS_IsCharacter (JSContext *ctx, JSValue val) {
|
||||
JSValue r = js_cell_is_character (ctx, JS_NULL, 1, &val);
|
||||
return JS_VALUE_GET_BOOL (r);
|
||||
}
|
||||
|
||||
JS_BOOL JS_IsFit (JSContext *ctx, JSValue val) {
|
||||
JSValue r = js_cell_is_fit (ctx, JS_NULL, 1, &val);
|
||||
return JS_VALUE_GET_BOOL (r);
|
||||
}
|
||||
|
||||
JS_BOOL JS_IsData (JSValue val) {
|
||||
return !JS_IsNull (val) && !JS_IsFunction (val);
|
||||
}
|
||||
|
||||
JS_BOOL JS_IsActor (JSContext *ctx, JSValue val) {
|
||||
JSValue r = js_cell_is_actor (ctx, JS_NULL, 1, &val);
|
||||
return JS_VALUE_GET_BOOL (r);
|
||||
}
|
||||
|
||||
/* C API: logical(val) */
|
||||
JSValue JS_CellLogical (JSContext *ctx, JSValue val) {
|
||||
return js_cell_logical (ctx, JS_NULL, 1, &val);
|
||||
}
|
||||
|
||||
/* C API: every(arr, pred) */
|
||||
JSValue JS_CellEvery (JSContext *ctx, JSValue arr, JSValue pred) {
|
||||
JSValue argv[2] = { arr, pred };
|
||||
return js_cell_every (ctx, JS_NULL, 2, argv);
|
||||
}
|
||||
|
||||
/* C API: some(arr, pred) */
|
||||
JSValue JS_CellSome (JSContext *ctx, JSValue arr, JSValue pred) {
|
||||
JSValue argv[2] = { arr, pred };
|
||||
return js_cell_some (ctx, JS_NULL, 2, argv);
|
||||
}
|
||||
|
||||
/* C API: starts_with(text, prefix) */
|
||||
JSValue JS_CellStartsWith (JSContext *ctx, JSValue text, JSValue prefix) {
|
||||
JSValue argv[2] = { text, prefix };
|
||||
return js_cell_starts_with (ctx, JS_NULL, 2, argv);
|
||||
}
|
||||
|
||||
/* C API: ends_with(text, suffix) */
|
||||
JSValue JS_CellEndsWith (JSContext *ctx, JSValue text, JSValue suffix) {
|
||||
JSValue argv[2] = { text, suffix };
|
||||
return js_cell_ends_with (ctx, JS_NULL, 2, argv);
|
||||
}
|
||||
|
||||
/* C API: normalize(text) */
|
||||
JSValue JS_CellNormalize (JSContext *ctx, JSValue text) {
|
||||
return js_cell_normalize (ctx, JS_NULL, 1, &text);
|
||||
}
|
||||
|
||||
static void js_set_global_cfunc(JSContext *ctx, const char *name, JSCFunction *func, int length) {
|
||||
JSGCRef ref;
|
||||
JS_PushGCRef(ctx, &ref);
|
||||
@@ -11740,6 +11865,7 @@ static void JS_AddIntrinsicBaseObjects (JSContext *ctx) {
|
||||
js_set_global_cfunc(ctx, "ends_with", js_cell_ends_with, 2);
|
||||
js_set_global_cfunc(ctx, "every", js_cell_every, 2);
|
||||
js_set_global_cfunc(ctx, "some", js_cell_some, 2);
|
||||
js_set_global_cfunc(ctx, "normalize", js_cell_normalize, 1);
|
||||
|
||||
/* fn record with apply property */
|
||||
{
|
||||
|
||||
488
source/suite.c
488
source/suite.c
@@ -2187,6 +2187,424 @@ TEST(wota_encode_blob) {
|
||||
return 1;
|
||||
}
|
||||
|
||||
/* ============================================================================
|
||||
Sensory Function C API Tests
|
||||
============================================================================ */
|
||||
|
||||
TEST(is_true_true) {
|
||||
ASSERT(JS_IsTrue(JS_TRUE));
|
||||
return 1;
|
||||
}
|
||||
|
||||
TEST(is_true_false) {
|
||||
ASSERT(!JS_IsTrue(JS_FALSE));
|
||||
ASSERT(!JS_IsTrue(JS_NewInt32(ctx, 1)));
|
||||
ASSERT(!JS_IsTrue(JS_NULL));
|
||||
ASSERT(!JS_IsTrue(JS_NewString(ctx, "true")));
|
||||
return 1;
|
||||
}
|
||||
|
||||
TEST(is_false_false) {
|
||||
ASSERT(JS_IsFalse(JS_FALSE));
|
||||
return 1;
|
||||
}
|
||||
|
||||
TEST(is_false_other) {
|
||||
ASSERT(!JS_IsFalse(JS_TRUE));
|
||||
ASSERT(!JS_IsFalse(JS_NewInt32(ctx, 0)));
|
||||
ASSERT(!JS_IsFalse(JS_NULL));
|
||||
return 1;
|
||||
}
|
||||
|
||||
TEST(is_data_values) {
|
||||
ASSERT(JS_IsData(JS_NewInt32(ctx, 42)));
|
||||
ASSERT(JS_IsData(JS_NewString(ctx, "hello")));
|
||||
ASSERT(JS_IsData(JS_TRUE));
|
||||
ASSERT(JS_IsData(JS_NewArray(ctx)));
|
||||
ASSERT(JS_IsData(JS_NewObject(ctx)));
|
||||
return 1;
|
||||
}
|
||||
|
||||
TEST(is_data_non_data) {
|
||||
ASSERT(!JS_IsData(JS_NULL));
|
||||
return 1;
|
||||
}
|
||||
|
||||
TEST(is_fit_integers) {
|
||||
ASSERT(JS_IsFit(ctx, JS_NewInt32(ctx, 0)));
|
||||
ASSERT(JS_IsFit(ctx, JS_NewInt32(ctx, 42)));
|
||||
ASSERT(JS_IsFit(ctx, JS_NewInt32(ctx, -100)));
|
||||
ASSERT(JS_IsFit(ctx, JS_NewFloat64(ctx, 3.0)));
|
||||
return 1;
|
||||
}
|
||||
|
||||
TEST(is_fit_non_fit) {
|
||||
ASSERT(!JS_IsFit(ctx, JS_NewFloat64(ctx, 3.5)));
|
||||
ASSERT(!JS_IsFit(ctx, JS_NewString(ctx, "3")));
|
||||
ASSERT(!JS_IsFit(ctx, JS_NULL));
|
||||
ASSERT(!JS_IsFit(ctx, JS_TRUE));
|
||||
return 1;
|
||||
}
|
||||
|
||||
TEST(is_character_single) {
|
||||
ASSERT(JS_IsCharacter(ctx, JS_NewString(ctx, "a")));
|
||||
ASSERT(JS_IsCharacter(ctx, JS_NewString(ctx, "Z")));
|
||||
ASSERT(JS_IsCharacter(ctx, JS_NewString(ctx, "5")));
|
||||
return 1;
|
||||
}
|
||||
|
||||
TEST(is_character_non_char) {
|
||||
ASSERT(!JS_IsCharacter(ctx, JS_NewString(ctx, "ab")));
|
||||
ASSERT(!JS_IsCharacter(ctx, JS_NewString(ctx, "")));
|
||||
ASSERT(!JS_IsCharacter(ctx, JS_NewInt32(ctx, 65)));
|
||||
ASSERT(!JS_IsCharacter(ctx, JS_NULL));
|
||||
return 1;
|
||||
}
|
||||
|
||||
TEST(is_digit_digits) {
|
||||
ASSERT(JS_IsDigit(ctx, JS_NewString(ctx, "0")));
|
||||
ASSERT(JS_IsDigit(ctx, JS_NewString(ctx, "9")));
|
||||
return 1;
|
||||
}
|
||||
|
||||
TEST(is_digit_non_digit) {
|
||||
ASSERT(!JS_IsDigit(ctx, JS_NewString(ctx, "a")));
|
||||
ASSERT(!JS_IsDigit(ctx, JS_NewString(ctx, "55")));
|
||||
ASSERT(!JS_IsDigit(ctx, JS_NewInt32(ctx, 5)));
|
||||
ASSERT(!JS_IsDigit(ctx, JS_NULL));
|
||||
return 1;
|
||||
}
|
||||
|
||||
TEST(is_letter_letters) {
|
||||
ASSERT(JS_IsLetter(ctx, JS_NewString(ctx, "a")));
|
||||
ASSERT(JS_IsLetter(ctx, JS_NewString(ctx, "Z")));
|
||||
return 1;
|
||||
}
|
||||
|
||||
TEST(is_letter_non_letter) {
|
||||
ASSERT(!JS_IsLetter(ctx, JS_NewString(ctx, "5")));
|
||||
ASSERT(!JS_IsLetter(ctx, JS_NewString(ctx, "ab")));
|
||||
ASSERT(!JS_IsLetter(ctx, JS_NewInt32(ctx, 65)));
|
||||
return 1;
|
||||
}
|
||||
|
||||
TEST(is_lower_lowercase) {
|
||||
ASSERT(JS_IsLower(ctx, JS_NewString(ctx, "a")));
|
||||
ASSERT(JS_IsLower(ctx, JS_NewString(ctx, "z")));
|
||||
return 1;
|
||||
}
|
||||
|
||||
TEST(is_lower_non_lower) {
|
||||
ASSERT(!JS_IsLower(ctx, JS_NewString(ctx, "A")));
|
||||
ASSERT(!JS_IsLower(ctx, JS_NewString(ctx, "5")));
|
||||
ASSERT(!JS_IsLower(ctx, JS_NewString(ctx, "ab")));
|
||||
return 1;
|
||||
}
|
||||
|
||||
TEST(is_upper_uppercase) {
|
||||
ASSERT(JS_IsUpper(ctx, JS_NewString(ctx, "A")));
|
||||
ASSERT(JS_IsUpper(ctx, JS_NewString(ctx, "Z")));
|
||||
return 1;
|
||||
}
|
||||
|
||||
TEST(is_upper_non_upper) {
|
||||
ASSERT(!JS_IsUpper(ctx, JS_NewString(ctx, "a")));
|
||||
ASSERT(!JS_IsUpper(ctx, JS_NewString(ctx, "5")));
|
||||
ASSERT(!JS_IsUpper(ctx, JS_NewString(ctx, "AB")));
|
||||
return 1;
|
||||
}
|
||||
|
||||
TEST(is_whitespace_single) {
|
||||
ASSERT(JS_IsWhitespace(ctx, JS_NewString(ctx, " ")));
|
||||
ASSERT(JS_IsWhitespace(ctx, JS_NewString(ctx, "\t")));
|
||||
ASSERT(JS_IsWhitespace(ctx, JS_NewString(ctx, "\n")));
|
||||
return 1;
|
||||
}
|
||||
|
||||
TEST(is_whitespace_multi) {
|
||||
ASSERT(JS_IsWhitespace(ctx, JS_NewString(ctx, " ")));
|
||||
ASSERT(JS_IsWhitespace(ctx, JS_NewString(ctx, "\r\n")));
|
||||
ASSERT(JS_IsWhitespace(ctx, JS_NewString(ctx, " \t\n")));
|
||||
return 1;
|
||||
}
|
||||
|
||||
TEST(is_whitespace_non_ws) {
|
||||
ASSERT(!JS_IsWhitespace(ctx, JS_NewString(ctx, "a")));
|
||||
ASSERT(!JS_IsWhitespace(ctx, JS_NewString(ctx, "")));
|
||||
ASSERT(!JS_IsWhitespace(ctx, JS_NewInt32(ctx, 32)));
|
||||
return 1;
|
||||
}
|
||||
|
||||
TEST(is_actor_non_actor) {
|
||||
ASSERT(!JS_IsActor(ctx, JS_NewObject(ctx)));
|
||||
ASSERT(!JS_IsActor(ctx, JS_NULL));
|
||||
ASSERT(!JS_IsActor(ctx, JS_NewInt32(ctx, 42)));
|
||||
return 1;
|
||||
}
|
||||
|
||||
/* ============================================================================
|
||||
Logical, Whole, Fraction C API Tests
|
||||
============================================================================ */
|
||||
|
||||
TEST(logical_zero) {
|
||||
JSValue r = JS_CellLogical(ctx, JS_NewInt32(ctx, 0));
|
||||
ASSERT_FALSE(r);
|
||||
return 1;
|
||||
}
|
||||
|
||||
TEST(logical_one) {
|
||||
JSValue r = JS_CellLogical(ctx, JS_NewInt32(ctx, 1));
|
||||
ASSERT_TRUE(r);
|
||||
return 1;
|
||||
}
|
||||
|
||||
TEST(logical_false) {
|
||||
JSValue r = JS_CellLogical(ctx, JS_FALSE);
|
||||
ASSERT_FALSE(r);
|
||||
return 1;
|
||||
}
|
||||
|
||||
TEST(logical_true) {
|
||||
JSValue r = JS_CellLogical(ctx, JS_TRUE);
|
||||
ASSERT_TRUE(r);
|
||||
return 1;
|
||||
}
|
||||
|
||||
TEST(logical_string_false) {
|
||||
JSValue r = JS_CellLogical(ctx, JS_NewString(ctx, "false"));
|
||||
ASSERT_FALSE(r);
|
||||
return 1;
|
||||
}
|
||||
|
||||
TEST(logical_string_true) {
|
||||
JSValue r = JS_CellLogical(ctx, JS_NewString(ctx, "true"));
|
||||
ASSERT_TRUE(r);
|
||||
return 1;
|
||||
}
|
||||
|
||||
TEST(logical_null) {
|
||||
JSValue r = JS_CellLogical(ctx, JS_NULL);
|
||||
ASSERT_FALSE(r);
|
||||
return 1;
|
||||
}
|
||||
|
||||
TEST(logical_invalid) {
|
||||
JSValue r = JS_CellLogical(ctx, JS_NewInt32(ctx, 42));
|
||||
ASSERT_NULL(r);
|
||||
r = JS_CellLogical(ctx, JS_NewString(ctx, "maybe"));
|
||||
ASSERT_NULL(r);
|
||||
return 1;
|
||||
}
|
||||
|
||||
TEST(whole_positive) {
|
||||
JSValue r = JS_CellWhole(ctx, JS_NewFloat64(ctx, 3.7));
|
||||
ASSERT_FLOAT(r, 3.0, 0.001);
|
||||
return 1;
|
||||
}
|
||||
|
||||
TEST(whole_negative) {
|
||||
JSValue r = JS_CellWhole(ctx, JS_NewFloat64(ctx, -3.7));
|
||||
ASSERT_FLOAT(r, -3.0, 0.001);
|
||||
return 1;
|
||||
}
|
||||
|
||||
TEST(whole_integer) {
|
||||
JSValue r = JS_CellWhole(ctx, JS_NewInt32(ctx, 42));
|
||||
ASSERT_FLOAT(r, 42.0, 0.001);
|
||||
return 1;
|
||||
}
|
||||
|
||||
TEST(whole_non_number) {
|
||||
JSValue r = JS_CellWhole(ctx, JS_NewString(ctx, "hi"));
|
||||
ASSERT_NULL(r);
|
||||
return 1;
|
||||
}
|
||||
|
||||
TEST(fraction_positive) {
|
||||
JSValue r = JS_CellFraction(ctx, JS_NewFloat64(ctx, 3.7));
|
||||
ASSERT_FLOAT(r, 0.7, 0.001);
|
||||
return 1;
|
||||
}
|
||||
|
||||
TEST(fraction_negative) {
|
||||
JSValue r = JS_CellFraction(ctx, JS_NewFloat64(ctx, -3.7));
|
||||
ASSERT_FLOAT(r, -0.7, 0.001);
|
||||
return 1;
|
||||
}
|
||||
|
||||
TEST(fraction_integer) {
|
||||
JSValue r = JS_CellFraction(ctx, JS_NewInt32(ctx, 42));
|
||||
ASSERT_FLOAT(r, 0.0, 0.001);
|
||||
return 1;
|
||||
}
|
||||
|
||||
TEST(fraction_non_number) {
|
||||
JSValue r = JS_CellFraction(ctx, JS_NewString(ctx, "hi"));
|
||||
ASSERT_NULL(r);
|
||||
return 1;
|
||||
}
|
||||
|
||||
/* ============================================================================
|
||||
StartsWith / EndsWith C API Tests
|
||||
============================================================================ */
|
||||
|
||||
TEST(starts_with_match) {
|
||||
JSValue r = JS_CellStartsWith(ctx, JS_NewString(ctx, "hello world"), JS_NewString(ctx, "hello"));
|
||||
ASSERT_TRUE(r);
|
||||
return 1;
|
||||
}
|
||||
|
||||
TEST(starts_with_no_match) {
|
||||
JSValue r = JS_CellStartsWith(ctx, JS_NewString(ctx, "hello world"), JS_NewString(ctx, "world"));
|
||||
ASSERT_FALSE(r);
|
||||
return 1;
|
||||
}
|
||||
|
||||
TEST(starts_with_empty) {
|
||||
JSValue r = JS_CellStartsWith(ctx, JS_NewString(ctx, "hello"), JS_NewString(ctx, ""));
|
||||
ASSERT_TRUE(r);
|
||||
return 1;
|
||||
}
|
||||
|
||||
TEST(ends_with_match) {
|
||||
JSValue r = JS_CellEndsWith(ctx, JS_NewString(ctx, "hello world"), JS_NewString(ctx, "world"));
|
||||
ASSERT_TRUE(r);
|
||||
return 1;
|
||||
}
|
||||
|
||||
TEST(ends_with_no_match) {
|
||||
JSValue r = JS_CellEndsWith(ctx, JS_NewString(ctx, "hello world"), JS_NewString(ctx, "hello"));
|
||||
ASSERT_FALSE(r);
|
||||
return 1;
|
||||
}
|
||||
|
||||
TEST(ends_with_full_match) {
|
||||
JSValue r = JS_CellEndsWith(ctx, JS_NewString(ctx, "test"), JS_NewString(ctx, "test"));
|
||||
ASSERT_TRUE(r);
|
||||
return 1;
|
||||
}
|
||||
|
||||
/* ============================================================================
|
||||
Normalize C API Tests
|
||||
============================================================================ */
|
||||
|
||||
TEST(normalize_ascii) {
|
||||
JSValue r = JS_CellNormalize(ctx, JS_NewString(ctx, "hello"));
|
||||
ASSERT_STR(r, "hello");
|
||||
return 1;
|
||||
}
|
||||
|
||||
TEST(normalize_non_text) {
|
||||
JSValue r = JS_CellNormalize(ctx, JS_NewInt32(ctx, 42));
|
||||
ASSERT_NULL(r);
|
||||
return 1;
|
||||
}
|
||||
|
||||
TEST(normalize_null) {
|
||||
JSValue r = JS_CellNormalize(ctx, JS_NULL);
|
||||
ASSERT_NULL(r);
|
||||
return 1;
|
||||
}
|
||||
|
||||
TEST(normalize_empty) {
|
||||
JSValue r = JS_CellNormalize(ctx, JS_NewString(ctx, ""));
|
||||
ASSERT_STR(r, "");
|
||||
return 1;
|
||||
}
|
||||
|
||||
/* ============================================================================
|
||||
Format C API Tests
|
||||
============================================================================ */
|
||||
|
||||
TEST(format_array_placeholder) {
|
||||
JS_FRAME(ctx);
|
||||
JS_ROOT(arr, JS_NewArray(ctx));
|
||||
JSValue a = JS_NewString(ctx, "world");
|
||||
JS_SetPropertyNumber(ctx, arr.val, 0, a);
|
||||
JSValue r = JS_CellFormat(ctx, JS_NewString(ctx, "hello {0}"), arr.val, JS_NULL);
|
||||
ASSERT_STR(r, "hello world");
|
||||
JS_RETURN(JS_NewInt32(ctx, 1));
|
||||
}
|
||||
|
||||
TEST(format_record_placeholder) {
|
||||
JS_FRAME(ctx);
|
||||
JS_ROOT(rec, JS_NewObject(ctx));
|
||||
JSValue name = JS_NewString(ctx, "world");
|
||||
JS_SetPropertyStr(ctx, rec.val, "name", name);
|
||||
JSValue r = JS_CellFormat(ctx, JS_NewString(ctx, "hello {name}"), rec.val, JS_NULL);
|
||||
ASSERT_STR(r, "hello world");
|
||||
JS_RETURN(JS_NewInt32(ctx, 1));
|
||||
}
|
||||
|
||||
TEST(format_missing_key) {
|
||||
JS_FRAME(ctx);
|
||||
JS_ROOT(rec, JS_NewObject(ctx));
|
||||
JSValue r = JS_CellFormat(ctx, JS_NewString(ctx, "hello {name}"), rec.val, JS_NULL);
|
||||
ASSERT_STR(r, "hello null");
|
||||
JS_RETURN(JS_NewInt32(ctx, 1));
|
||||
}
|
||||
|
||||
/* ============================================================================
|
||||
Object C API Tests
|
||||
============================================================================ */
|
||||
|
||||
TEST(cell_object_null_null) {
|
||||
JSValue r = JS_CellObject(ctx, JS_NULL, JS_NULL);
|
||||
ASSERT_NULL(r);
|
||||
return 1;
|
||||
}
|
||||
|
||||
TEST(cell_object_copy) {
|
||||
JS_FRAME(ctx);
|
||||
JS_ROOT(orig, JS_NewObject(ctx));
|
||||
JS_SetPropertyStr(ctx, orig.val, "x", JS_NewInt32(ctx, 1));
|
||||
JSValue copy = JS_CellObject(ctx, orig.val, JS_NULL);
|
||||
ASSERT(JS_IsRecord(copy));
|
||||
JSValue x = JS_GetPropertyStr(ctx, copy, "x");
|
||||
ASSERT_INT(x, 1);
|
||||
JS_RETURN(JS_NewInt32(ctx, 1));
|
||||
}
|
||||
|
||||
TEST(cell_object_merge) {
|
||||
JS_FRAME(ctx);
|
||||
JS_ROOT(a, JS_NewObject(ctx));
|
||||
JS_SetPropertyStr(ctx, a.val, "x", JS_NewInt32(ctx, 1));
|
||||
JS_ROOT(b, JS_NewObject(ctx));
|
||||
JS_SetPropertyStr(ctx, b.val, "y", JS_NewInt32(ctx, 2));
|
||||
JSValue merged = JS_CellObject(ctx, a.val, b.val);
|
||||
ASSERT(JS_IsRecord(merged));
|
||||
JSValue x = JS_GetPropertyStr(ctx, merged, "x");
|
||||
JSValue y = JS_GetPropertyStr(ctx, merged, "y");
|
||||
ASSERT_INT(x, 1);
|
||||
ASSERT_INT(y, 2);
|
||||
JS_RETURN(JS_NewInt32(ctx, 1));
|
||||
}
|
||||
|
||||
/* ============================================================================
|
||||
Splat C API Tests
|
||||
============================================================================ */
|
||||
|
||||
TEST(cell_splat_non_object) {
|
||||
JSValue r = JS_CellSplat(ctx, JS_NewInt32(ctx, 42));
|
||||
ASSERT_NULL(r);
|
||||
return 1;
|
||||
}
|
||||
|
||||
TEST(cell_splat_null) {
|
||||
JSValue r = JS_CellSplat(ctx, JS_NULL);
|
||||
ASSERT_NULL(r);
|
||||
return 1;
|
||||
}
|
||||
|
||||
TEST(cell_splat_record) {
|
||||
JS_FRAME(ctx);
|
||||
JS_ROOT(obj, JS_NewObject(ctx));
|
||||
JS_SetPropertyStr(ctx, obj.val, "x", JS_NewInt32(ctx, 1));
|
||||
JSValue r = JS_CellSplat(ctx, obj.val);
|
||||
ASSERT(JS_IsRecord(r));
|
||||
JS_RETURN(JS_NewInt32(ctx, 1));
|
||||
}
|
||||
|
||||
/* ============================================================================
|
||||
MAIN TEST RUNNER
|
||||
============================================================================ */
|
||||
@@ -2408,6 +2826,76 @@ int run_c_test_suite(JSContext *ctx)
|
||||
RUN_TEST(wota_encode_nested_array);
|
||||
RUN_TEST(wota_encode_blob);
|
||||
|
||||
printf("\nSensory Function C API:\n");
|
||||
RUN_TEST(is_true_true);
|
||||
RUN_TEST(is_true_false);
|
||||
RUN_TEST(is_false_false);
|
||||
RUN_TEST(is_false_other);
|
||||
RUN_TEST(is_data_values);
|
||||
RUN_TEST(is_data_non_data);
|
||||
RUN_TEST(is_fit_integers);
|
||||
RUN_TEST(is_fit_non_fit);
|
||||
RUN_TEST(is_character_single);
|
||||
RUN_TEST(is_character_non_char);
|
||||
RUN_TEST(is_digit_digits);
|
||||
RUN_TEST(is_digit_non_digit);
|
||||
RUN_TEST(is_letter_letters);
|
||||
RUN_TEST(is_letter_non_letter);
|
||||
RUN_TEST(is_lower_lowercase);
|
||||
RUN_TEST(is_lower_non_lower);
|
||||
RUN_TEST(is_upper_uppercase);
|
||||
RUN_TEST(is_upper_non_upper);
|
||||
RUN_TEST(is_whitespace_single);
|
||||
RUN_TEST(is_whitespace_multi);
|
||||
RUN_TEST(is_whitespace_non_ws);
|
||||
RUN_TEST(is_actor_non_actor);
|
||||
|
||||
printf("\nLogical/Whole/Fraction:\n");
|
||||
RUN_TEST(logical_zero);
|
||||
RUN_TEST(logical_one);
|
||||
RUN_TEST(logical_false);
|
||||
RUN_TEST(logical_true);
|
||||
RUN_TEST(logical_string_false);
|
||||
RUN_TEST(logical_string_true);
|
||||
RUN_TEST(logical_null);
|
||||
RUN_TEST(logical_invalid);
|
||||
RUN_TEST(whole_positive);
|
||||
RUN_TEST(whole_negative);
|
||||
RUN_TEST(whole_integer);
|
||||
RUN_TEST(whole_non_number);
|
||||
RUN_TEST(fraction_positive);
|
||||
RUN_TEST(fraction_negative);
|
||||
RUN_TEST(fraction_integer);
|
||||
RUN_TEST(fraction_non_number);
|
||||
|
||||
printf("\nStartsWith/EndsWith:\n");
|
||||
RUN_TEST(starts_with_match);
|
||||
RUN_TEST(starts_with_no_match);
|
||||
RUN_TEST(starts_with_empty);
|
||||
RUN_TEST(ends_with_match);
|
||||
RUN_TEST(ends_with_no_match);
|
||||
RUN_TEST(ends_with_full_match);
|
||||
|
||||
printf("\nNormalize:\n");
|
||||
RUN_TEST(normalize_ascii);
|
||||
RUN_TEST(normalize_non_text);
|
||||
RUN_TEST(normalize_null);
|
||||
RUN_TEST(normalize_empty);
|
||||
|
||||
printf("\nFormat:\n");
|
||||
RUN_TEST(format_array_placeholder);
|
||||
RUN_TEST(format_record_placeholder);
|
||||
RUN_TEST(format_missing_key);
|
||||
|
||||
printf("\nObject:\n");
|
||||
RUN_TEST(cell_object_null_null);
|
||||
RUN_TEST(cell_object_copy);
|
||||
RUN_TEST(cell_object_merge);
|
||||
|
||||
printf("\nSplat:\n");
|
||||
RUN_TEST(cell_splat_non_object);
|
||||
RUN_TEST(cell_splat_null);
|
||||
RUN_TEST(cell_splat_record);
|
||||
|
||||
printf("\n=================================\n");
|
||||
printf("Results: %d passed, %d failed\n", tests_passed, tests_failed);
|
||||
|
||||
326
vm_suite.ce
326
vm_suite.ce
@@ -1070,7 +1070,10 @@ run("is_whitespace", function() {
|
||||
if (!is_whitespace("\t")) fail("is_whitespace tab should be true")
|
||||
if (!is_whitespace("\n")) fail("is_whitespace newline should be true")
|
||||
if (is_whitespace("a")) fail("is_whitespace a should be false")
|
||||
if (is_whitespace(" ")) fail("is_whitespace two spaces should be false")
|
||||
if (!is_whitespace(" ")) fail("is_whitespace two spaces should be true")
|
||||
if (!is_whitespace("\r\n")) fail("is_whitespace cr-lf should be true")
|
||||
if (!is_whitespace(" \t\n")) fail("is_whitespace mixed ws should be true")
|
||||
if (is_whitespace("")) fail("is_whitespace empty should be false")
|
||||
if (is_whitespace(42)) fail("is_whitespace number should be false")
|
||||
if (is_whitespace(null)) fail("is_whitespace null should be false")
|
||||
})
|
||||
@@ -8556,9 +8559,13 @@ run("is_whitespace edge cases", function() {
|
||||
assert_eq(is_whitespace(" "), true, "is_whitespace space")
|
||||
assert_eq(is_whitespace("\t"), true, "is_whitespace tab")
|
||||
assert_eq(is_whitespace("\n"), true, "is_whitespace newline")
|
||||
assert_eq(is_whitespace(" "), true, "is_whitespace multi space")
|
||||
assert_eq(is_whitespace("\r\n"), true, "is_whitespace crlf")
|
||||
assert_eq(is_whitespace(" \t\n"), true, "is_whitespace mixed")
|
||||
assert_eq(is_whitespace(""), false, "is_whitespace empty")
|
||||
assert_eq(is_whitespace("a"), false, "is_whitespace letter")
|
||||
assert_eq(is_whitespace(32), false, "is_whitespace number")
|
||||
assert_eq(is_whitespace(" a"), false, "is_whitespace space-letter")
|
||||
})
|
||||
|
||||
run("is_data edge cases", function() {
|
||||
@@ -8610,6 +8617,323 @@ run("arrfor with index", function() {
|
||||
assert_eq(indices[2], 2, "arrfor idx [2]")
|
||||
})
|
||||
|
||||
// ============================================================================
|
||||
// SENSORY FUNCTION COMPLETENESS
|
||||
// ============================================================================
|
||||
|
||||
run("is_true completeness", function() {
|
||||
assert_eq(is_true(true), true, "is_true true")
|
||||
assert_eq(is_true(false), false, "is_true false")
|
||||
assert_eq(is_true(1), false, "is_true 1")
|
||||
assert_eq(is_true(null), false, "is_true null")
|
||||
assert_eq(is_true("true"), false, "is_true text")
|
||||
})
|
||||
|
||||
run("is_false completeness", function() {
|
||||
assert_eq(is_false(false), true, "is_false false")
|
||||
assert_eq(is_false(true), false, "is_false true")
|
||||
assert_eq(is_false(0), false, "is_false 0")
|
||||
assert_eq(is_false(null), false, "is_false null")
|
||||
})
|
||||
|
||||
run("is_fit completeness", function() {
|
||||
assert_eq(is_fit(0), true, "is_fit 0")
|
||||
assert_eq(is_fit(42), true, "is_fit 42")
|
||||
assert_eq(is_fit(-100), true, "is_fit -100")
|
||||
assert_eq(is_fit(3.0), true, "is_fit 3.0")
|
||||
assert_eq(is_fit(3.5), false, "is_fit 3.5")
|
||||
assert_eq(is_fit("3"), false, "is_fit text")
|
||||
assert_eq(is_fit(null), false, "is_fit null")
|
||||
assert_eq(is_fit(true), false, "is_fit bool")
|
||||
})
|
||||
|
||||
run("is_character completeness", function() {
|
||||
assert_eq(is_character("a"), true, "is_character a")
|
||||
assert_eq(is_character("Z"), true, "is_character Z")
|
||||
assert_eq(is_character("ab"), false, "is_character ab")
|
||||
assert_eq(is_character(""), false, "is_character empty")
|
||||
assert_eq(is_character(65), false, "is_character number")
|
||||
assert_eq(is_character(null), false, "is_character null")
|
||||
})
|
||||
|
||||
run("is_digit completeness", function() {
|
||||
assert_eq(is_digit("0"), true, "is_digit 0")
|
||||
assert_eq(is_digit("9"), true, "is_digit 9")
|
||||
assert_eq(is_digit("a"), false, "is_digit a")
|
||||
assert_eq(is_digit("55"), false, "is_digit 55")
|
||||
assert_eq(is_digit(5), false, "is_digit num")
|
||||
assert_eq(is_digit(null), false, "is_digit null")
|
||||
})
|
||||
|
||||
run("is_letter completeness", function() {
|
||||
assert_eq(is_letter("a"), true, "is_letter a")
|
||||
assert_eq(is_letter("Z"), true, "is_letter Z")
|
||||
assert_eq(is_letter("5"), false, "is_letter 5")
|
||||
assert_eq(is_letter("ab"), false, "is_letter ab")
|
||||
assert_eq(is_letter(65), false, "is_letter number")
|
||||
})
|
||||
|
||||
run("is_lower completeness", function() {
|
||||
assert_eq(is_lower("a"), true, "is_lower a")
|
||||
assert_eq(is_lower("z"), true, "is_lower z")
|
||||
assert_eq(is_lower("A"), false, "is_lower A")
|
||||
assert_eq(is_lower("5"), false, "is_lower 5")
|
||||
assert_eq(is_lower("ab"), false, "is_lower ab")
|
||||
})
|
||||
|
||||
run("is_upper completeness", function() {
|
||||
assert_eq(is_upper("A"), true, "is_upper A")
|
||||
assert_eq(is_upper("Z"), true, "is_upper Z")
|
||||
assert_eq(is_upper("a"), false, "is_upper a")
|
||||
assert_eq(is_upper("5"), false, "is_upper 5")
|
||||
assert_eq(is_upper("AB"), false, "is_upper AB")
|
||||
})
|
||||
|
||||
// ============================================================================
|
||||
// CREATOR FUNCTION WRONG-ARGUMENT DISRUPTIONS
|
||||
// ============================================================================
|
||||
|
||||
run("array float index disrupts", function() {
|
||||
assert_eq(should_disrupt(function() { array([1,2,3], 1.5, 3) }), true, "array float from")
|
||||
assert_eq(should_disrupt(function() { array([1,2,3], 0, 2.5) }), true, "array float to")
|
||||
})
|
||||
|
||||
run("number invalid radix", function() {
|
||||
assert_eq(number("ff", 1), null, "radix 1 too low")
|
||||
assert_eq(number("ff", 38), null, "radix 38 too high")
|
||||
})
|
||||
|
||||
run("number invalid text", function() {
|
||||
assert_eq(number("abc"), null, "number abc")
|
||||
assert_eq(number(""), null, "number empty")
|
||||
})
|
||||
|
||||
run("object invalid input", function() {
|
||||
assert_eq(object(42), null, "object number")
|
||||
assert_eq(object(null), null, "object null")
|
||||
})
|
||||
|
||||
// ============================================================================
|
||||
// STANDARD FUNCTION EDGE CASES
|
||||
// ============================================================================
|
||||
|
||||
run("abs/sign/floor/ceiling with non-number", function() {
|
||||
assert_eq(abs("hello"), null, "abs text")
|
||||
assert_eq(sign("hello"), null, "sign text")
|
||||
assert_eq(floor("hello"), null, "floor text")
|
||||
assert_eq(ceiling("hello"), null, "ceiling text")
|
||||
assert_eq(round("hello"), null, "round text")
|
||||
assert_eq(trunc("hello"), null, "trunc text")
|
||||
})
|
||||
|
||||
run("min/max with non-number", function() {
|
||||
assert_eq(min(1, "b"), null, "min text arg")
|
||||
assert_eq(max(null, 5), null, "max null arg")
|
||||
})
|
||||
|
||||
run("not with non-logical", function() {
|
||||
assert_eq(not(42), null, "not number")
|
||||
assert_eq(not("true"), null, "not text")
|
||||
})
|
||||
|
||||
run("whole and fraction", function() {
|
||||
assert_eq(whole(3.7), 3, "whole 3.7")
|
||||
assert_eq(whole(-3.7), -3, "whole -3.7")
|
||||
assert_eq(whole(42), 42, "whole 42")
|
||||
assert_eq(whole("hi"), null, "whole text")
|
||||
var f1 = fraction(3.7)
|
||||
if (f1 < 0.69 || f1 > 0.71) fail("fraction 3.7 should be ~0.7")
|
||||
var f2 = fraction(-3.7)
|
||||
if (f2 > -0.69 || f2 < -0.71) fail("fraction -3.7 should be ~-0.7")
|
||||
assert_eq(fraction(42), 0, "fraction 42")
|
||||
assert_eq(fraction("hi"), null, "fraction text")
|
||||
})
|
||||
|
||||
run("logical comprehensive", function() {
|
||||
assert_eq(logical(0), false, "logical 0")
|
||||
assert_eq(logical(1), true, "logical 1")
|
||||
assert_eq(logical(false), false, "logical false")
|
||||
assert_eq(logical(true), true, "logical true")
|
||||
assert_eq(logical("false"), false, "logical text false")
|
||||
assert_eq(logical("true"), true, "logical text true")
|
||||
assert_eq(logical(null), false, "logical null")
|
||||
assert_eq(logical(42), null, "logical 42")
|
||||
assert_eq(logical("maybe"), null, "logical maybe")
|
||||
})
|
||||
|
||||
run("normalize basic", function() {
|
||||
assert_eq(normalize("hello"), "hello", "normalize ascii")
|
||||
assert_eq(normalize(42), null, "normalize non-text")
|
||||
assert_eq(normalize(null), null, "normalize null")
|
||||
assert_eq(normalize(""), "", "normalize empty")
|
||||
})
|
||||
|
||||
run("starts_with / ends_with comprehensive", function() {
|
||||
assert_eq(starts_with("hello world", "hello"), true, "starts_with match")
|
||||
assert_eq(starts_with("hello world", "world"), false, "starts_with no match")
|
||||
assert_eq(starts_with("hello", ""), true, "starts_with empty prefix")
|
||||
assert_eq(starts_with("hello", "hello"), true, "starts_with full match")
|
||||
assert_eq(ends_with("hello world", "world"), true, "ends_with match")
|
||||
assert_eq(ends_with("hello world", "hello"), false, "ends_with no match")
|
||||
assert_eq(ends_with("test", "test"), true, "ends_with full match")
|
||||
})
|
||||
|
||||
run("every and some", function() {
|
||||
var gt0 = function(x) { return x > 0 }
|
||||
assert_eq(every([1, 2, 3], gt0), true, "every all pass")
|
||||
assert_eq(every([1, -1, 3], gt0), false, "every one fail")
|
||||
assert_eq(every([], gt0), true, "every empty")
|
||||
assert_eq(some([1, 2, 3], gt0), true, "some one match")
|
||||
assert_eq(some([-1, -2, -3], gt0), false, "some no match")
|
||||
assert_eq(some([], gt0), false, "some empty")
|
||||
})
|
||||
|
||||
run("reduce edge cases", function() {
|
||||
assert_eq(reduce([], function(a, b) { return a + b }), null, "reduce empty")
|
||||
assert_eq(reduce([42], function(a, b) { return a + b }), 42, "reduce single")
|
||||
assert_eq(reduce([1, 2, 3], function(a, b) { return a + b }), 6, "reduce sum")
|
||||
})
|
||||
|
||||
run("filter on non-array disrupts", function() {
|
||||
assert_eq(should_disrupt(function() {
|
||||
filter(42, function(x) { return true })
|
||||
}), true, "filter non-array disrupts")
|
||||
})
|
||||
|
||||
run("find edge cases", function() {
|
||||
assert_eq(find([1, 2, 3], function(x) { return x == 2 }), 1, "find match index")
|
||||
assert_eq(find([1, 2, 3], function(x) { return x == 5 }), null, "find no match")
|
||||
assert_eq(find([], function(x) { return true }), null, "find empty")
|
||||
assert_eq(find([10, 20, 30], 20), 1, "find value mode index")
|
||||
})
|
||||
|
||||
run("format edge cases", function() {
|
||||
var r1 = format("{0} and {1}", ["hello", "world"])
|
||||
assert_eq(r1, "hello and world", "format array basic")
|
||||
var r2 = format("{name}", {name: "Alice"})
|
||||
assert_eq(r2, "Alice", "format record basic")
|
||||
var r3 = format("{missing}", {})
|
||||
assert_eq(r3, "null", "format missing key")
|
||||
})
|
||||
|
||||
// ============================================================================
|
||||
// DUAL-PATH TESTING (MCODE vs C FALLBACK)
|
||||
// ============================================================================
|
||||
|
||||
run("is_digit dual path", function() {
|
||||
// Direct call tests mcode-lowered opcode
|
||||
assert_eq(is_digit("5"), true, "is_digit direct true")
|
||||
assert_eq(is_digit("a"), false, "is_digit direct false")
|
||||
// As callback tests C fallback
|
||||
var items = ["5", "a", "3"]
|
||||
var result = filter(items, is_digit)
|
||||
assert_eq(length(result), 2, "is_digit filter count")
|
||||
assert_eq(result[0], "5", "is_digit filter [0]")
|
||||
assert_eq(result[1], "3", "is_digit filter [1]")
|
||||
})
|
||||
|
||||
run("is_letter dual path", function() {
|
||||
assert_eq(is_letter("a"), true, "is_letter direct true")
|
||||
assert_eq(is_letter("5"), false, "is_letter direct false")
|
||||
var items = ["a", "5", "Z"]
|
||||
var result = filter(items, is_letter)
|
||||
assert_eq(length(result), 2, "is_letter filter count")
|
||||
})
|
||||
|
||||
run("is_upper dual path", function() {
|
||||
assert_eq(is_upper("A"), true, "is_upper direct true")
|
||||
assert_eq(is_upper("a"), false, "is_upper direct false")
|
||||
var items = ["A", "a", "Z"]
|
||||
var result = filter(items, is_upper)
|
||||
assert_eq(length(result), 2, "is_upper filter count")
|
||||
})
|
||||
|
||||
run("is_lower dual path", function() {
|
||||
assert_eq(is_lower("a"), true, "is_lower direct true")
|
||||
assert_eq(is_lower("A"), false, "is_lower direct false")
|
||||
var items = ["a", "A", "z"]
|
||||
var result = filter(items, is_lower)
|
||||
assert_eq(length(result), 2, "is_lower filter count")
|
||||
})
|
||||
|
||||
run("is_whitespace dual path", function() {
|
||||
assert_eq(is_whitespace(" "), true, "is_whitespace direct true")
|
||||
assert_eq(is_whitespace("a"), false, "is_whitespace direct false")
|
||||
var items = [" ", "a", "\t", "b"]
|
||||
var result = filter(items, is_whitespace)
|
||||
assert_eq(length(result), 2, "is_whitespace filter count")
|
||||
})
|
||||
|
||||
run("is_number dual path", function() {
|
||||
assert_eq(is_number(42), true, "is_number direct true")
|
||||
assert_eq(is_number("42"), false, "is_number direct false")
|
||||
var items = [42, "hello", 3.14, null]
|
||||
var result = filter(items, is_number)
|
||||
assert_eq(length(result), 2, "is_number filter count")
|
||||
})
|
||||
|
||||
run("is_text dual path", function() {
|
||||
assert_eq(is_text("hello"), true, "is_text direct true")
|
||||
assert_eq(is_text(42), false, "is_text direct false")
|
||||
var items = ["a", 42, "b", null]
|
||||
var result = filter(items, is_text)
|
||||
assert_eq(length(result), 2, "is_text filter count")
|
||||
})
|
||||
|
||||
run("is_null dual path", function() {
|
||||
assert_eq(is_null(null), true, "is_null direct true")
|
||||
assert_eq(is_null(0), false, "is_null direct false")
|
||||
var items = [null, 1, null, "x"]
|
||||
var result = filter(items, is_null)
|
||||
assert_eq(length(result), 2, "is_null filter count")
|
||||
})
|
||||
|
||||
// ============================================================================
|
||||
// ARRFOR DISRUPTION TESTS
|
||||
// ============================================================================
|
||||
|
||||
run("arrfor reverse non-boolean disrupts", function() {
|
||||
assert_eq(should_disrupt(function() {
|
||||
arrfor([1, 2], function(x) {}, 1)
|
||||
}), true, "arrfor reverse must be logical")
|
||||
})
|
||||
|
||||
run("find reverse non-boolean disrupts", function() {
|
||||
assert_eq(should_disrupt(function() {
|
||||
find([1, 2], function(x) { return true }, 1)
|
||||
}), true, "find reverse must be logical")
|
||||
})
|
||||
|
||||
run("reduce reverse non-boolean disrupts", function() {
|
||||
assert_eq(should_disrupt(function() {
|
||||
reduce([1, 2, 3], function(a, b) { return a + b }, null, 1)
|
||||
}), true, "reduce reverse must be logical")
|
||||
})
|
||||
|
||||
// ============================================================================
|
||||
// IS_STONE BEHAVIOR
|
||||
// ============================================================================
|
||||
|
||||
run("is_stone primitives", function() {
|
||||
assert_eq(is_stone(42), true, "is_stone number")
|
||||
assert_eq(is_stone("hello"), true, "is_stone text")
|
||||
assert_eq(is_stone(null), true, "is_stone null")
|
||||
assert_eq(is_stone(true), true, "is_stone bool")
|
||||
assert_eq(is_stone(false), true, "is_stone false")
|
||||
})
|
||||
|
||||
run("is_stone mutable objects", function() {
|
||||
assert_eq(is_stone([1, 2]), false, "is_stone array")
|
||||
assert_eq(is_stone({x: 1}), false, "is_stone record")
|
||||
})
|
||||
|
||||
run("is_stone frozen objects", function() {
|
||||
var a = stone([1, 2])
|
||||
assert_eq(is_stone(a), true, "is_stone stoned array")
|
||||
var r = stone({x: 1})
|
||||
assert_eq(is_stone(r), true, "is_stone stoned record")
|
||||
})
|
||||
|
||||
// ============================================================================
|
||||
// SUMMARY
|
||||
// ============================================================================
|
||||
|
||||
Reference in New Issue
Block a user