fix str
This commit is contained in:
@@ -19,6 +19,9 @@
|
||||
#include <unistd.h>
|
||||
#include <sys/stat.h>
|
||||
|
||||
/* Test suite declaration */
|
||||
int run_c_test_suite(JSContext *ctx);
|
||||
|
||||
cell_rt *root_cell = NULL;
|
||||
static char *core_path = NULL;
|
||||
|
||||
@@ -196,12 +199,43 @@ static void signal_handler(int sig)
|
||||
}
|
||||
#endif
|
||||
if (!str) return;
|
||||
|
||||
|
||||
exit_handler();
|
||||
}
|
||||
|
||||
/* Run the C test suite with minimal runtime setup */
|
||||
static int run_test_suite(void)
|
||||
{
|
||||
JSRuntime *rt = JS_NewRuntime();
|
||||
if (!rt) {
|
||||
printf("Failed to create JS runtime\n");
|
||||
return 1;
|
||||
}
|
||||
|
||||
JSContext *ctx = JS_NewContextRaw(rt);
|
||||
if (!ctx) {
|
||||
printf("Failed to create JS context\n");
|
||||
JS_FreeRuntime(rt);
|
||||
return 1;
|
||||
}
|
||||
|
||||
JS_AddIntrinsicBaseObjects(ctx);
|
||||
|
||||
int result = run_c_test_suite(ctx);
|
||||
|
||||
JS_FreeContext(ctx);
|
||||
JS_FreeRuntime(rt);
|
||||
|
||||
return result;
|
||||
}
|
||||
|
||||
int cell_init(int argc, char **argv)
|
||||
{
|
||||
/* Check for --test flag to run C test suite */
|
||||
if (argc >= 2 && strcmp(argv[1], "--test") == 0) {
|
||||
return run_test_suite();
|
||||
}
|
||||
|
||||
int script_start = 1;
|
||||
|
||||
/* Find the cell shop at ~/.cell */
|
||||
|
||||
@@ -2791,18 +2791,6 @@ static JSText *pretext_concat_value (JSContext *ctx, JSText *s, JSValue v) {
|
||||
return s;
|
||||
}
|
||||
|
||||
static JSText *pretext_concat_value_free (JSContext *ctx, JSText *s, JSValue v) {
|
||||
JSText *p;
|
||||
|
||||
if (unlikely (JS_VALUE_GET_TAG (v) != JS_TAG_STRING)) {
|
||||
v = JS_ToString (ctx, v);
|
||||
if (JS_IsException (v)) return NULL;
|
||||
}
|
||||
p = JS_VALUE_GET_STRING (v);
|
||||
s = pretext_concat (ctx, s, p, 0, (uint32_t)JSText_len (p));
|
||||
return s;
|
||||
}
|
||||
|
||||
/* Finalize a pretext into an immutable JSValue string */
|
||||
static JSValue pretext_end (JSContext *ctx, JSText *s) {
|
||||
if (!s) return JS_EXCEPTION;
|
||||
@@ -2889,23 +2877,23 @@ JSValue JS_NewStringLen (JSContext *ctx, const char *buf, size_t buf_len) {
|
||||
|
||||
static JSValue JS_ConcatString3 (JSContext *ctx, const char *str1, JSValue str2, const char *str3) {
|
||||
JSText *b;
|
||||
int len1, len3;
|
||||
JSText *p;
|
||||
int len1, len3, str2_len;
|
||||
|
||||
if (unlikely (JS_VALUE_GET_TAG (str2) != JS_TAG_STRING)) {
|
||||
if (!JS_IsText (str2)) {
|
||||
str2 = JS_ToString (ctx, str2);
|
||||
if (JS_IsException (str2)) goto fail;
|
||||
}
|
||||
p = JS_VALUE_GET_STRING (str2);
|
||||
|
||||
str2_len = js_string_value_len (str2);
|
||||
len1 = strlen (str1);
|
||||
len3 = strlen (str3);
|
||||
|
||||
b = pretext_init (ctx, len1 + (int)JSText_len (p) + len3);
|
||||
b = pretext_init (ctx, len1 + str2_len + len3);
|
||||
if (!b) goto fail;
|
||||
|
||||
b = pretext_write8 (ctx, b, (const uint8_t *)str1, len1);
|
||||
if (!b) goto fail;
|
||||
b = pretext_concat (ctx, b, p, 0, (uint32_t)JSText_len (p));
|
||||
b = pretext_concat_value (ctx, b, str2);
|
||||
if (!b) goto fail;
|
||||
b = pretext_write8 (ctx, b, (const uint8_t *)str3, len3);
|
||||
if (!b) goto fail;
|
||||
@@ -2922,19 +2910,32 @@ fail:
|
||||
* sequences */
|
||||
const char *JS_ToCStringLen2 (JSContext *ctx, size_t *plen, JSValue val1, BOOL cesu8) {
|
||||
JSValue val;
|
||||
JSText *str;
|
||||
char *q, *ret;
|
||||
size_t size;
|
||||
int i, len;
|
||||
|
||||
if (JS_VALUE_GET_TAG (val1) != JS_TAG_STRING) {
|
||||
if (!JS_IsText (val1)) {
|
||||
val = JS_ToString (ctx, val1);
|
||||
if (JS_IsException (val)) goto fail;
|
||||
} else {
|
||||
val = val1;
|
||||
}
|
||||
|
||||
str = JS_VALUE_GET_STRING (val);
|
||||
/* Handle immediate ASCII strings */
|
||||
if (MIST_IsImmediateASCII (val)) {
|
||||
len = MIST_GetImmediateASCIILen (val);
|
||||
ret = js_malloc (ctx, len + 1);
|
||||
if (!ret) goto fail;
|
||||
for (i = 0; i < len; i++) {
|
||||
ret[i] = MIST_GetImmediateASCIIChar (val, i);
|
||||
}
|
||||
ret[len] = '\0';
|
||||
if (plen) *plen = len;
|
||||
return ret;
|
||||
}
|
||||
|
||||
/* Handle heap strings (JSText) */
|
||||
JSText *str = JS_VALUE_GET_STRING (val);
|
||||
len = (int)JSText_len (str);
|
||||
|
||||
/* Calculate UTF-8 size */
|
||||
@@ -4350,9 +4351,8 @@ static BOOL js_object_has_name (JSContext *ctx, JSValue obj) {
|
||||
int slot = rec_find_slot (rec, name_key);
|
||||
if (slot <= 0) return FALSE;
|
||||
JSValue val = rec->slots[slot].val;
|
||||
if (JS_VALUE_GET_TAG (val) != JS_TAG_STRING) return TRUE;
|
||||
JSText *p = JS_VALUE_GET_STRING (val);
|
||||
return (JSText_len (p) != 0);
|
||||
if (!JS_IsText (val)) return TRUE; /* has name but it's not a string = truthy */
|
||||
return (js_string_value_len (val) != 0);
|
||||
}
|
||||
|
||||
static int JS_DefineObjectName (JSContext *ctx, JSValue obj, JSValue name) {
|
||||
@@ -4942,7 +4942,7 @@ int JS_ToFloat64 (JSContext *ctx, double *pres, JSValue val) {
|
||||
uint32_t tag;
|
||||
|
||||
tag = JS_VALUE_GET_TAG (val);
|
||||
if (tag <= JS_TAG_NULL) {
|
||||
if (tag == JS_TAG_INT) {
|
||||
*pres = JS_VALUE_GET_INT (val);
|
||||
return 0;
|
||||
} else if (JS_TAG_IS_FLOAT64 (tag)) {
|
||||
@@ -5344,16 +5344,8 @@ static void js_dump_char (JSPrintValueState *s, int c, int sep) {
|
||||
}
|
||||
|
||||
static void js_print_string_rec (JSPrintValueState *s, JSValue val, int sep, uint32_t pos) {
|
||||
if (JS_VALUE_GET_TAG (val) == JS_TAG_STRING) {
|
||||
JSText *p = JS_VALUE_GET_STRING (val);
|
||||
uint32_t i, len;
|
||||
if (pos < s->options.max_string_length) {
|
||||
len = min_uint32 ((uint32_t)JSText_len (p), s->options.max_string_length - pos);
|
||||
for (i = 0; i < len; i++) {
|
||||
js_dump_char (s, string_get (p, i), sep);
|
||||
}
|
||||
}
|
||||
} else if (MIST_IsImmediateASCII (val)) {
|
||||
if (MIST_IsImmediateASCII (val)) {
|
||||
/* Immediate ASCII string */
|
||||
int len = MIST_GetImmediateASCIILen (val);
|
||||
if (pos < s->options.max_string_length) {
|
||||
uint32_t i, l;
|
||||
@@ -5362,6 +5354,16 @@ static void js_print_string_rec (JSPrintValueState *s, JSValue val, int sep, uin
|
||||
js_dump_char (s, MIST_GetImmediateASCIIChar (val, i), sep);
|
||||
}
|
||||
}
|
||||
} else if (JS_IsPtr (val) && objhdr_type (*(objhdr_t *)JS_VALUE_GET_PTR (val)) == OBJ_TEXT) {
|
||||
/* Heap text (JSText) */
|
||||
JSText *p = (JSText *)JS_VALUE_GET_PTR (val);
|
||||
uint32_t i, len;
|
||||
if (pos < s->options.max_string_length) {
|
||||
len = min_uint32 ((uint32_t)JSText_len (p), s->options.max_string_length - pos);
|
||||
for (i = 0; i < len; i++) {
|
||||
js_dump_char (s, string_get (p, i), sep);
|
||||
}
|
||||
}
|
||||
} else {
|
||||
js_printf (s, "<invalid string tag %d>", (int)JS_VALUE_GET_TAG (val));
|
||||
}
|
||||
@@ -18944,9 +18946,8 @@ static JSValue js_regexp_constructor_internal (JSContext *ctx, JSValue pattern,
|
||||
JSRecord *p;
|
||||
JSRegExp *re;
|
||||
|
||||
/* sanity check */
|
||||
if (JS_VALUE_GET_TAG (bc) != JS_TAG_STRING
|
||||
|| JS_VALUE_GET_TAG (pattern) != JS_TAG_STRING) {
|
||||
/* sanity check - need heap strings for pattern and bytecode */
|
||||
if (!JS_IsText (bc) || !JS_IsText (pattern)) {
|
||||
JS_ThrowTypeError (ctx, "string expected");
|
||||
fail:
|
||||
return JS_EXCEPTION;
|
||||
@@ -18959,8 +18960,9 @@ static JSValue js_regexp_constructor_internal (JSContext *ctx, JSValue pattern,
|
||||
re = js_malloc (ctx, sizeof(JSRegExp));
|
||||
if (!re) goto fail;
|
||||
REC_SET_OPAQUE(p, re);
|
||||
re->pattern = JS_VALUE_GET_STRING (pattern);
|
||||
re->bytecode = JS_VALUE_GET_STRING (bc);
|
||||
/* Store pattern and bytecode - need to handle both immediate and heap strings */
|
||||
re->pattern = MIST_IsImmediateASCII (pattern) ? NULL : (JSText *)JS_VALUE_GET_PTR (pattern);
|
||||
re->bytecode = MIST_IsImmediateASCII (bc) ? NULL : (JSText *)JS_VALUE_GET_PTR (bc);
|
||||
{
|
||||
JSValue key = JS_KEY_STR (ctx, "lastIndex");
|
||||
JS_SetPropertyInternal (ctx, obj, key, JS_NewInt32 (ctx, 0));
|
||||
@@ -19873,7 +19875,7 @@ concat_primitive:
|
||||
case JS_TAG_BOOL:
|
||||
case JS_TAG_NULL:
|
||||
concat_value:
|
||||
jsc->b = pretext_concat_value_free (ctx, jsc->b, val);
|
||||
jsc->b = pretext_concat_value (ctx, jsc->b, val);
|
||||
return jsc->b ? 0 : -1;
|
||||
default:
|
||||
return 0;
|
||||
@@ -20928,7 +20930,7 @@ static JSValue js_cell_text (JSContext *ctx, JSValue this_val, int argc, JSValue
|
||||
JSValue item_str = JS_ToString (ctx, item);
|
||||
if (JS_IsException (item_str)) goto array_fail;
|
||||
|
||||
b = pretext_concat_value_free (ctx, b, item_str);
|
||||
b = pretext_concat_value (ctx, b, item_str);
|
||||
if (!b) goto array_fail;
|
||||
}
|
||||
|
||||
@@ -21120,7 +21122,7 @@ static JSValue js_cell_text_codepoint (JSContext *ctx, JSValue this_val, int arg
|
||||
static JSText *pt_concat_value_to_string_free (JSContext *ctx, JSText *b, JSValue v) {
|
||||
JSValue s = JS_ToString (ctx, v);
|
||||
if (JS_IsException (s)) return NULL;
|
||||
b = pretext_concat_value_free (ctx, b, s);
|
||||
b = pretext_concat_value (ctx, b, s);
|
||||
return b;
|
||||
}
|
||||
|
||||
@@ -21239,7 +21241,7 @@ static JSValue js_cell_text_replace (JSContext *ctx, JSValue this_val, int argc,
|
||||
if (boundary < len) {
|
||||
JSValue ch = js_sub_string (ctx, sp, boundary, boundary + 1);
|
||||
if (JS_IsException (ch)) goto fail_str_target;
|
||||
b = pretext_concat_value_free (ctx, b, ch);
|
||||
b = pretext_concat_value (ctx, b, ch);
|
||||
if (!b) goto fail_str_target;
|
||||
}
|
||||
}
|
||||
@@ -21264,7 +21266,7 @@ static JSValue js_cell_text_replace (JSContext *ctx, JSValue this_val, int argc,
|
||||
if (found > pos) {
|
||||
JSValue sub = js_sub_string (ctx, sp, pos, found);
|
||||
if (JS_IsException (sub)) goto fail_str_target;
|
||||
b = pretext_concat_value_free (ctx, b, sub);
|
||||
b = pretext_concat_value (ctx, b, sub);
|
||||
if (!b) goto fail_str_target;
|
||||
}
|
||||
|
||||
@@ -21288,7 +21290,7 @@ static JSValue js_cell_text_replace (JSContext *ctx, JSValue this_val, int argc,
|
||||
if (pos < len) {
|
||||
JSValue sub = js_sub_string (ctx, sp, pos, len);
|
||||
if (JS_IsException (sub)) goto fail_str_target;
|
||||
b = pretext_concat_value_free (ctx, b, sub);
|
||||
b = pretext_concat_value (ctx, b, sub);
|
||||
if (!b) goto fail_str_target;
|
||||
}
|
||||
|
||||
@@ -21360,7 +21362,7 @@ static JSValue js_cell_text_replace (JSContext *ctx, JSValue this_val, int argc,
|
||||
if (found > pos) {
|
||||
JSValue prefix = js_sub_string (ctx, sp, pos, found);
|
||||
if (JS_IsException (prefix)) goto fail_rx;
|
||||
b = pretext_concat_value_free (ctx, b, prefix);
|
||||
b = pretext_concat_value (ctx, b, prefix);
|
||||
if (!b) goto fail_rx;
|
||||
}
|
||||
|
||||
@@ -21387,7 +21389,7 @@ static JSValue js_cell_text_replace (JSContext *ctx, JSValue this_val, int argc,
|
||||
if (pos < len) {
|
||||
JSValue tail = js_sub_string (ctx, sp, pos, len);
|
||||
if (JS_IsException (tail)) goto fail_rx;
|
||||
b = pretext_concat_value_free (ctx, b, tail);
|
||||
b = pretext_concat_value (ctx, b, tail);
|
||||
if (!b) goto fail_rx;
|
||||
}
|
||||
|
||||
|
||||
578
source/suite.c
Normal file
578
source/suite.c
Normal file
@@ -0,0 +1,578 @@
|
||||
/*
|
||||
* C-level test suite for cell runtime
|
||||
* Tests core object representation using C API exclusively
|
||||
* Bypasses parser/bytecode to verify low-level operations
|
||||
*/
|
||||
|
||||
#include "quickjs.h"
|
||||
#include <stdio.h>
|
||||
#include <string.h>
|
||||
#include <math.h>
|
||||
|
||||
static int tests_passed = 0;
|
||||
static int tests_failed = 0;
|
||||
|
||||
#define TEST(name) static int test_##name(JSContext *ctx)
|
||||
#define RUN_TEST(name) do { \
|
||||
printf(" %-50s ", #name); \
|
||||
if (test_##name(ctx)) { \
|
||||
printf("PASS\n"); \
|
||||
tests_passed++; \
|
||||
} else { \
|
||||
printf("FAIL\n"); \
|
||||
tests_failed++; \
|
||||
} \
|
||||
} while(0)
|
||||
|
||||
#define ASSERT(cond) do { if (!(cond)) { printf("[line %d] ", __LINE__); return 0; } } while(0)
|
||||
#define ASSERT_INT(v, expected) do { \
|
||||
if (!JS_IsInt(v)) { printf("[line %d: not int] ", __LINE__); return 0; } \
|
||||
if (JS_VALUE_GET_INT(v) != (expected)) { printf("[line %d: %d != %d] ", __LINE__, JS_VALUE_GET_INT(v), expected); return 0; } \
|
||||
} while(0)
|
||||
|
||||
/* ============================================================================
|
||||
NUMBER TESTS
|
||||
============================================================================ */
|
||||
|
||||
TEST(int_creation) {
|
||||
JSValue v = JS_NewInt32(ctx, 42);
|
||||
ASSERT(JS_IsInt(v));
|
||||
ASSERT(JS_VALUE_GET_INT(v) == 42);
|
||||
return 1;
|
||||
}
|
||||
|
||||
TEST(int_zero) {
|
||||
JSValue v = JS_NewInt32(ctx, 0);
|
||||
ASSERT(JS_IsInt(v));
|
||||
ASSERT(JS_VALUE_GET_INT(v) == 0);
|
||||
return 1;
|
||||
}
|
||||
|
||||
TEST(int_negative) {
|
||||
JSValue v = JS_NewInt32(ctx, -100);
|
||||
ASSERT(JS_IsInt(v));
|
||||
ASSERT(JS_VALUE_GET_INT(v) == -100);
|
||||
return 1;
|
||||
}
|
||||
|
||||
TEST(int_max) {
|
||||
JSValue v = JS_NewInt32(ctx, INT32_MAX);
|
||||
ASSERT(JS_IsInt(v));
|
||||
ASSERT(JS_VALUE_GET_INT(v) == INT32_MAX);
|
||||
return 1;
|
||||
}
|
||||
|
||||
TEST(int_min) {
|
||||
JSValue v = JS_NewInt32(ctx, INT32_MIN);
|
||||
ASSERT(JS_IsInt(v));
|
||||
ASSERT(JS_VALUE_GET_INT(v) == INT32_MIN);
|
||||
return 1;
|
||||
}
|
||||
|
||||
TEST(float_creation) {
|
||||
JSValue v = JS_NewFloat64(ctx, 3.14159);
|
||||
ASSERT(JS_IsNumber(v));
|
||||
double d;
|
||||
JS_ToFloat64(ctx, &d, v);
|
||||
ASSERT(fabs(d - 3.14159) < 0.00001);
|
||||
return 1;
|
||||
}
|
||||
|
||||
TEST(float_zero_becomes_int) {
|
||||
JSValue v = JS_NewFloat64(ctx, 0.0);
|
||||
ASSERT(JS_IsInt(v));
|
||||
ASSERT(JS_VALUE_GET_INT(v) == 0);
|
||||
return 1;
|
||||
}
|
||||
|
||||
TEST(float_whole_becomes_int) {
|
||||
JSValue v = JS_NewFloat64(ctx, 42.0);
|
||||
ASSERT(JS_IsInt(v));
|
||||
ASSERT(JS_VALUE_GET_INT(v) == 42);
|
||||
return 1;
|
||||
}
|
||||
|
||||
TEST(float_negative) {
|
||||
JSValue v = JS_NewFloat64(ctx, -2.5);
|
||||
ASSERT(JS_IsNumber(v));
|
||||
double d;
|
||||
JS_ToFloat64(ctx, &d, v);
|
||||
ASSERT(fabs(d - (-2.5)) < 0.00001);
|
||||
return 1;
|
||||
}
|
||||
|
||||
/* ============================================================================
|
||||
BOOLEAN TESTS
|
||||
============================================================================ */
|
||||
|
||||
TEST(bool_true) {
|
||||
JSValue v = JS_TRUE;
|
||||
ASSERT(JS_IsBool(v));
|
||||
ASSERT(JS_VALUE_GET_BOOL(v) == 1);
|
||||
return 1;
|
||||
}
|
||||
|
||||
TEST(bool_false) {
|
||||
JSValue v = JS_FALSE;
|
||||
ASSERT(JS_IsBool(v));
|
||||
ASSERT(JS_VALUE_GET_BOOL(v) == 0);
|
||||
return 1;
|
||||
}
|
||||
|
||||
TEST(bool_new_true) {
|
||||
JSValue v = JS_NewBool(ctx, 1);
|
||||
ASSERT(JS_IsBool(v));
|
||||
ASSERT(JS_VALUE_GET_BOOL(v) == 1);
|
||||
return 1;
|
||||
}
|
||||
|
||||
TEST(bool_new_false) {
|
||||
JSValue v = JS_NewBool(ctx, 0);
|
||||
ASSERT(JS_IsBool(v));
|
||||
ASSERT(JS_VALUE_GET_BOOL(v) == 0);
|
||||
return 1;
|
||||
}
|
||||
|
||||
/* ============================================================================
|
||||
NULL TESTS
|
||||
============================================================================ */
|
||||
|
||||
TEST(null_value) {
|
||||
JSValue v = JS_NULL;
|
||||
ASSERT(JS_IsNull(v));
|
||||
ASSERT(!JS_IsBool(v));
|
||||
ASSERT(!JS_IsNumber(v));
|
||||
return 1;
|
||||
}
|
||||
|
||||
/* ============================================================================
|
||||
STRING TESTS - IMMEDIATE ASCII
|
||||
============================================================================ */
|
||||
|
||||
TEST(string_immediate_short) {
|
||||
JSValue v = JS_NewString(ctx, "hello");
|
||||
ASSERT(JS_IsText(v));
|
||||
ASSERT(MIST_IsImmediateASCII(v));
|
||||
ASSERT(MIST_GetImmediateASCIILen(v) == 5);
|
||||
ASSERT(MIST_GetImmediateASCIIChar(v, 0) == 'h');
|
||||
ASSERT(MIST_GetImmediateASCIIChar(v, 4) == 'o');
|
||||
return 1;
|
||||
}
|
||||
|
||||
TEST(string_immediate_empty) {
|
||||
JSValue v = JS_NewString(ctx, "");
|
||||
ASSERT(JS_IsText(v));
|
||||
ASSERT(MIST_IsImmediateASCII(v));
|
||||
ASSERT(MIST_GetImmediateASCIILen(v) == 0);
|
||||
return 1;
|
||||
}
|
||||
|
||||
TEST(string_immediate_max) {
|
||||
JSValue v = JS_NewString(ctx, "1234567"); /* 7 chars = max immediate */
|
||||
ASSERT(JS_IsText(v));
|
||||
ASSERT(MIST_IsImmediateASCII(v));
|
||||
ASSERT(MIST_GetImmediateASCIILen(v) == 7);
|
||||
return 1;
|
||||
}
|
||||
|
||||
TEST(string_heap_long) {
|
||||
JSValue v = JS_NewString(ctx, "12345678"); /* 8 chars = heap allocated */
|
||||
ASSERT(JS_IsText(v));
|
||||
ASSERT(!MIST_IsImmediateASCII(v));
|
||||
return 1;
|
||||
}
|
||||
|
||||
TEST(string_to_cstring) {
|
||||
JSValue v = JS_NewString(ctx, "test");
|
||||
const char *str = JS_ToCString(ctx, v);
|
||||
ASSERT(str != NULL);
|
||||
ASSERT(strcmp(str, "test") == 0);
|
||||
JS_FreeCString(ctx, str);
|
||||
return 1;
|
||||
}
|
||||
|
||||
TEST(string_heap_to_cstring) {
|
||||
JSValue v = JS_NewString(ctx, "this is a longer string");
|
||||
const char *str = JS_ToCString(ctx, v);
|
||||
ASSERT(str != NULL);
|
||||
ASSERT(strcmp(str, "this is a longer string") == 0);
|
||||
JS_FreeCString(ctx, str);
|
||||
return 1;
|
||||
}
|
||||
|
||||
/* ============================================================================
|
||||
OBJECT/RECORD TESTS
|
||||
============================================================================ */
|
||||
|
||||
TEST(object_create) {
|
||||
JSValue obj = JS_NewObject(ctx);
|
||||
ASSERT(JS_IsObject(obj));
|
||||
ASSERT(JS_IsRecord(obj));
|
||||
return 1;
|
||||
}
|
||||
|
||||
TEST(object_set_get_property) {
|
||||
JSValue obj = JS_NewObject(ctx);
|
||||
int r = JS_SetPropertyStr(ctx, obj, "foo", JS_NewInt32(ctx, 42));
|
||||
ASSERT(r >= 0);
|
||||
JSValue val = JS_GetPropertyStr(ctx, obj, "foo");
|
||||
ASSERT_INT(val, 42);
|
||||
return 1;
|
||||
}
|
||||
|
||||
TEST(object_multiple_properties) {
|
||||
JSValue obj = JS_NewObject(ctx);
|
||||
JS_SetPropertyStr(ctx, obj, "a", JS_NewInt32(ctx, 1));
|
||||
JS_SetPropertyStr(ctx, obj, "b", JS_NewInt32(ctx, 2));
|
||||
JS_SetPropertyStr(ctx, obj, "c", JS_NewInt32(ctx, 3));
|
||||
|
||||
JSValue a = JS_GetPropertyStr(ctx, obj, "a");
|
||||
JSValue b = JS_GetPropertyStr(ctx, obj, "b");
|
||||
JSValue c = JS_GetPropertyStr(ctx, obj, "c");
|
||||
|
||||
ASSERT_INT(a, 1);
|
||||
ASSERT_INT(b, 2);
|
||||
ASSERT_INT(c, 3);
|
||||
return 1;
|
||||
}
|
||||
|
||||
TEST(object_overwrite_property) {
|
||||
JSValue obj = JS_NewObject(ctx);
|
||||
JS_SetPropertyStr(ctx, obj, "x", JS_NewInt32(ctx, 10));
|
||||
JS_SetPropertyStr(ctx, obj, "x", JS_NewInt32(ctx, 20));
|
||||
|
||||
JSValue x = JS_GetPropertyStr(ctx, obj, "x");
|
||||
ASSERT_INT(x, 20);
|
||||
return 1;
|
||||
}
|
||||
|
||||
TEST(object_missing_property_is_null) {
|
||||
JSValue obj = JS_NewObject(ctx);
|
||||
JSValue val = JS_GetPropertyStr(ctx, obj, "nonexistent");
|
||||
ASSERT(JS_IsNull(val));
|
||||
return 1;
|
||||
}
|
||||
|
||||
TEST(object_string_property) {
|
||||
JSValue obj = JS_NewObject(ctx);
|
||||
JSValue str = JS_NewString(ctx, "hello");
|
||||
JS_SetPropertyStr(ctx, obj, "msg", str);
|
||||
|
||||
JSValue val = JS_GetPropertyStr(ctx, obj, "msg");
|
||||
ASSERT(JS_IsText(val));
|
||||
const char *s = JS_ToCString(ctx, val);
|
||||
ASSERT(strcmp(s, "hello") == 0);
|
||||
JS_FreeCString(ctx, s);
|
||||
return 1;
|
||||
}
|
||||
|
||||
TEST(object_nested) {
|
||||
JSValue outer = JS_NewObject(ctx);
|
||||
JSValue inner = JS_NewObject(ctx);
|
||||
JS_SetPropertyStr(ctx, inner, "value", JS_NewInt32(ctx, 99));
|
||||
JS_SetPropertyStr(ctx, outer, "inner", inner);
|
||||
|
||||
JSValue gotInner = JS_GetPropertyStr(ctx, outer, "inner");
|
||||
ASSERT(JS_IsRecord(gotInner));
|
||||
JSValue val = JS_GetPropertyStr(ctx, gotInner, "value");
|
||||
ASSERT_INT(val, 99);
|
||||
return 1;
|
||||
}
|
||||
|
||||
TEST(object_many_properties_resize) {
|
||||
JSValue obj = JS_NewObject(ctx);
|
||||
/* Add many properties to trigger resize */
|
||||
for (int i = 0; i < 100; i++) {
|
||||
char key[16];
|
||||
snprintf(key, sizeof(key), "prop%d", i);
|
||||
JS_SetPropertyStr(ctx, obj, key, JS_NewInt32(ctx, i * 10));
|
||||
}
|
||||
|
||||
/* Verify all properties */
|
||||
for (int i = 0; i < 100; i++) {
|
||||
char key[16];
|
||||
snprintf(key, sizeof(key), "prop%d", i);
|
||||
JSValue val = JS_GetPropertyStr(ctx, obj, key);
|
||||
ASSERT_INT(val, i * 10);
|
||||
}
|
||||
return 1;
|
||||
}
|
||||
|
||||
/* ============================================================================
|
||||
ARRAY TESTS
|
||||
============================================================================ */
|
||||
|
||||
TEST(array_create) {
|
||||
JSValue arr = JS_NewArray(ctx);
|
||||
ASSERT(JS_IsObject(arr));
|
||||
ASSERT(JS_IsArray(arr));
|
||||
return 1;
|
||||
}
|
||||
|
||||
TEST(array_push_and_length) {
|
||||
JSValue arr = JS_NewArray(ctx);
|
||||
JS_ArrayPush(ctx, arr, JS_NewInt32(ctx, 10));
|
||||
JS_ArrayPush(ctx, arr, JS_NewInt32(ctx, 20));
|
||||
JS_ArrayPush(ctx, arr, JS_NewInt32(ctx, 30));
|
||||
|
||||
int64_t len;
|
||||
JS_GetLength(ctx, arr, &len);
|
||||
ASSERT(len == 3);
|
||||
return 1;
|
||||
}
|
||||
|
||||
TEST(array_get_by_index) {
|
||||
JSValue arr = JS_NewArray(ctx);
|
||||
JS_ArrayPush(ctx, arr, JS_NewInt32(ctx, 100));
|
||||
JS_ArrayPush(ctx, arr, JS_NewInt32(ctx, 200));
|
||||
JS_ArrayPush(ctx, arr, JS_NewInt32(ctx, 300));
|
||||
|
||||
JSValue v0 = JS_GetPropertyUint32(ctx, arr, 0);
|
||||
JSValue v1 = JS_GetPropertyUint32(ctx, arr, 1);
|
||||
JSValue v2 = JS_GetPropertyUint32(ctx, arr, 2);
|
||||
|
||||
ASSERT_INT(v0, 100);
|
||||
ASSERT_INT(v1, 200);
|
||||
ASSERT_INT(v2, 300);
|
||||
return 1;
|
||||
}
|
||||
|
||||
TEST(array_set_by_index) {
|
||||
JSValue arr = JS_NewArray(ctx);
|
||||
JS_ArrayPush(ctx, arr, JS_NewInt32(ctx, 0));
|
||||
JS_ArrayPush(ctx, arr, JS_NewInt32(ctx, 0));
|
||||
|
||||
JS_SetPropertyUint32(ctx, arr, 0, JS_NewInt32(ctx, 55));
|
||||
JS_SetPropertyUint32(ctx, arr, 1, JS_NewInt32(ctx, 66));
|
||||
|
||||
JSValue v0 = JS_GetPropertyUint32(ctx, arr, 0);
|
||||
JSValue v1 = JS_GetPropertyUint32(ctx, arr, 1);
|
||||
|
||||
ASSERT_INT(v0, 55);
|
||||
ASSERT_INT(v1, 66);
|
||||
return 1;
|
||||
}
|
||||
|
||||
TEST(array_pop) {
|
||||
JSValue arr = JS_NewArray(ctx);
|
||||
JS_ArrayPush(ctx, arr, JS_NewInt32(ctx, 1));
|
||||
JS_ArrayPush(ctx, arr, JS_NewInt32(ctx, 2));
|
||||
JS_ArrayPush(ctx, arr, JS_NewInt32(ctx, 3));
|
||||
|
||||
JSValue popped = JS_ArrayPop(ctx, arr);
|
||||
ASSERT_INT(popped, 3);
|
||||
|
||||
int64_t len;
|
||||
JS_GetLength(ctx, arr, &len);
|
||||
ASSERT(len == 2);
|
||||
return 1;
|
||||
}
|
||||
|
||||
TEST(array_out_of_bounds_is_null) {
|
||||
JSValue arr = JS_NewArray(ctx);
|
||||
JS_ArrayPush(ctx, arr, JS_NewInt32(ctx, 1));
|
||||
|
||||
JSValue val = JS_GetPropertyUint32(ctx, arr, 999);
|
||||
ASSERT(JS_IsNull(val));
|
||||
return 1;
|
||||
}
|
||||
|
||||
TEST(array_mixed_types) {
|
||||
JSValue arr = JS_NewArray(ctx);
|
||||
JS_ArrayPush(ctx, arr, JS_NewInt32(ctx, 42));
|
||||
JS_ArrayPush(ctx, arr, JS_NewString(ctx, "hello"));
|
||||
JS_ArrayPush(ctx, arr, JS_TRUE);
|
||||
JS_ArrayPush(ctx, arr, JS_NULL);
|
||||
|
||||
JSValue v0 = JS_GetPropertyUint32(ctx, arr, 0);
|
||||
JSValue v1 = JS_GetPropertyUint32(ctx, arr, 1);
|
||||
JSValue v2 = JS_GetPropertyUint32(ctx, arr, 2);
|
||||
JSValue v3 = JS_GetPropertyUint32(ctx, arr, 3);
|
||||
|
||||
ASSERT(JS_IsInt(v0));
|
||||
ASSERT(JS_IsText(v1));
|
||||
ASSERT(JS_IsBool(v2));
|
||||
ASSERT(JS_IsNull(v3));
|
||||
return 1;
|
||||
}
|
||||
|
||||
TEST(array_many_elements_resize) {
|
||||
JSValue arr = JS_NewArray(ctx);
|
||||
for (int i = 0; i < 1000; i++) {
|
||||
JS_ArrayPush(ctx, arr, JS_NewInt32(ctx, i));
|
||||
}
|
||||
|
||||
int64_t len;
|
||||
JS_GetLength(ctx, arr, &len);
|
||||
ASSERT(len == 1000);
|
||||
|
||||
/* Verify some values */
|
||||
JSValue v0 = JS_GetPropertyUint32(ctx, arr, 0);
|
||||
JSValue v500 = JS_GetPropertyUint32(ctx, arr, 500);
|
||||
JSValue v999 = JS_GetPropertyUint32(ctx, arr, 999);
|
||||
|
||||
ASSERT_INT(v0, 0);
|
||||
ASSERT_INT(v500, 500);
|
||||
ASSERT_INT(v999, 999);
|
||||
return 1;
|
||||
}
|
||||
|
||||
/* ============================================================================
|
||||
TYPE CHECK TESTS
|
||||
============================================================================ */
|
||||
|
||||
TEST(type_checks) {
|
||||
JSValue num = JS_NewInt32(ctx, 42);
|
||||
JSValue flt = JS_NewFloat64(ctx, 3.14);
|
||||
JSValue str = JS_NewString(ctx, "test");
|
||||
JSValue obj = JS_NewObject(ctx);
|
||||
JSValue arr = JS_NewArray(ctx);
|
||||
JSValue boo = JS_TRUE;
|
||||
JSValue nul = JS_NULL;
|
||||
|
||||
ASSERT(JS_IsNumber(num));
|
||||
ASSERT(JS_IsNumber(flt));
|
||||
ASSERT(JS_IsText(str));
|
||||
ASSERT(JS_IsRecord(obj));
|
||||
ASSERT(JS_IsArray(arr));
|
||||
ASSERT(JS_IsBool(boo));
|
||||
ASSERT(JS_IsNull(nul));
|
||||
|
||||
ASSERT(!JS_IsText(num));
|
||||
ASSERT(!JS_IsNumber(str));
|
||||
ASSERT(!JS_IsArray(obj));
|
||||
ASSERT(!JS_IsRecord(arr));
|
||||
|
||||
return 1;
|
||||
}
|
||||
|
||||
/* ============================================================================
|
||||
EQUALITY TESTS
|
||||
============================================================================ */
|
||||
|
||||
TEST(strict_eq_ints) {
|
||||
JSValue a = JS_NewInt32(ctx, 42);
|
||||
JSValue b = JS_NewInt32(ctx, 42);
|
||||
JSValue c = JS_NewInt32(ctx, 43);
|
||||
|
||||
ASSERT(JS_StrictEq(ctx, a, b));
|
||||
ASSERT(!JS_StrictEq(ctx, a, c));
|
||||
return 1;
|
||||
}
|
||||
|
||||
TEST(strict_eq_strings) {
|
||||
JSValue a = JS_NewString(ctx, "hello");
|
||||
JSValue b = JS_NewString(ctx, "hello");
|
||||
JSValue c = JS_NewString(ctx, "world");
|
||||
|
||||
ASSERT(JS_StrictEq(ctx, a, b));
|
||||
ASSERT(!JS_StrictEq(ctx, a, c));
|
||||
return 1;
|
||||
}
|
||||
|
||||
TEST(strict_eq_null) {
|
||||
JSValue a = JS_NULL;
|
||||
JSValue b = JS_NULL;
|
||||
JSValue c = JS_NewInt32(ctx, 0);
|
||||
|
||||
ASSERT(JS_StrictEq(ctx, a, b));
|
||||
ASSERT(!JS_StrictEq(ctx, a, c));
|
||||
return 1;
|
||||
}
|
||||
|
||||
TEST(strict_eq_bool) {
|
||||
ASSERT(JS_StrictEq(ctx, JS_TRUE, JS_TRUE));
|
||||
ASSERT(JS_StrictEq(ctx, JS_FALSE, JS_FALSE));
|
||||
ASSERT(!JS_StrictEq(ctx, JS_TRUE, JS_FALSE));
|
||||
return 1;
|
||||
}
|
||||
|
||||
/* ============================================================================
|
||||
GLOBAL OBJECT TEST
|
||||
============================================================================ */
|
||||
|
||||
TEST(global_object) {
|
||||
JSValue global = JS_GetGlobalObject(ctx);
|
||||
ASSERT(JS_IsRecord(global));
|
||||
|
||||
/* Set something on global */
|
||||
JS_SetPropertyStr(ctx, global, "testGlobal", JS_NewInt32(ctx, 777));
|
||||
JSValue val = JS_GetPropertyStr(ctx, global, "testGlobal");
|
||||
ASSERT_INT(val, 777);
|
||||
return 1;
|
||||
}
|
||||
|
||||
/* ============================================================================
|
||||
MAIN TEST RUNNER
|
||||
============================================================================ */
|
||||
|
||||
int run_c_test_suite(JSContext *ctx)
|
||||
{
|
||||
printf("\n=== Cell Runtime C Test Suite ===\n\n");
|
||||
|
||||
printf("Numbers:\n");
|
||||
RUN_TEST(int_creation);
|
||||
RUN_TEST(int_zero);
|
||||
RUN_TEST(int_negative);
|
||||
RUN_TEST(int_max);
|
||||
RUN_TEST(int_min);
|
||||
RUN_TEST(float_creation);
|
||||
RUN_TEST(float_zero_becomes_int);
|
||||
RUN_TEST(float_whole_becomes_int);
|
||||
RUN_TEST(float_negative);
|
||||
|
||||
printf("\nBooleans:\n");
|
||||
RUN_TEST(bool_true);
|
||||
RUN_TEST(bool_false);
|
||||
RUN_TEST(bool_new_true);
|
||||
RUN_TEST(bool_new_false);
|
||||
|
||||
printf("\nNull:\n");
|
||||
RUN_TEST(null_value);
|
||||
|
||||
printf("\nStrings:\n");
|
||||
RUN_TEST(string_immediate_short);
|
||||
RUN_TEST(string_immediate_empty);
|
||||
RUN_TEST(string_immediate_max);
|
||||
RUN_TEST(string_heap_long);
|
||||
RUN_TEST(string_to_cstring);
|
||||
RUN_TEST(string_heap_to_cstring);
|
||||
|
||||
printf("\nObjects/Records:\n");
|
||||
RUN_TEST(object_create);
|
||||
RUN_TEST(object_set_get_property);
|
||||
RUN_TEST(object_multiple_properties);
|
||||
RUN_TEST(object_overwrite_property);
|
||||
RUN_TEST(object_missing_property_is_null);
|
||||
RUN_TEST(object_string_property);
|
||||
RUN_TEST(object_nested);
|
||||
RUN_TEST(object_many_properties_resize);
|
||||
|
||||
printf("\nArrays:\n");
|
||||
RUN_TEST(array_create);
|
||||
RUN_TEST(array_push_and_length);
|
||||
RUN_TEST(array_get_by_index);
|
||||
RUN_TEST(array_set_by_index);
|
||||
RUN_TEST(array_pop);
|
||||
RUN_TEST(array_out_of_bounds_is_null);
|
||||
RUN_TEST(array_mixed_types);
|
||||
RUN_TEST(array_many_elements_resize);
|
||||
|
||||
printf("\nType Checks:\n");
|
||||
RUN_TEST(type_checks);
|
||||
|
||||
printf("\nEquality:\n");
|
||||
RUN_TEST(strict_eq_ints);
|
||||
RUN_TEST(strict_eq_strings);
|
||||
RUN_TEST(strict_eq_null);
|
||||
RUN_TEST(strict_eq_bool);
|
||||
|
||||
printf("\nGlobal Object:\n");
|
||||
RUN_TEST(global_object);
|
||||
|
||||
printf("\n=================================\n");
|
||||
printf("Results: %d passed, %d failed\n", tests_passed, tests_failed);
|
||||
printf("=================================\n\n");
|
||||
|
||||
return tests_failed == 0 ? 0 : 1;
|
||||
}
|
||||
Reference in New Issue
Block a user