fix tests; add comprehensive tests for functions and fix bugs in the mach VM regarding them.

This commit is contained in:
2026-02-24 17:41:18 -06:00
parent c2f57d1dae
commit 33d9013409
6 changed files with 1009 additions and 14 deletions

View File

@@ -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);

View File

@@ -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();

View File

@@ -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 */
{

View File

@@ -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);