Update.
This commit is contained in:
parent
e953861621
commit
e2e0788953
216
loader/loader.c
Normal file
216
loader/loader.c
Normal file
@ -0,0 +1,216 @@
|
|||||||
|
#include "loader.h"
|
||||||
|
#include <stdlib.h>
|
||||||
|
#include <string.h>
|
||||||
|
#include <dlfcn.h>
|
||||||
|
|
||||||
|
RavaNativeRegistry_t* rava_native_registry_create(void) {
|
||||||
|
RavaNativeRegistry_t *registry = calloc(1, sizeof(RavaNativeRegistry_t));
|
||||||
|
if (!registry) return NULL;
|
||||||
|
registry->library_count = 0;
|
||||||
|
registry->cache_count = 0;
|
||||||
|
return registry;
|
||||||
|
}
|
||||||
|
|
||||||
|
void rava_native_registry_destroy(RavaNativeRegistry_t *registry) {
|
||||||
|
if (!registry) return;
|
||||||
|
for (size_t i = 0; i < registry->library_count; i++) {
|
||||||
|
rava_native_library_unload(registry, registry->libraries[i]);
|
||||||
|
}
|
||||||
|
free(registry);
|
||||||
|
}
|
||||||
|
|
||||||
|
static uint32_t _hash_method_name(const char *class_name, const char *method_name) {
|
||||||
|
uint32_t hash = 5381;
|
||||||
|
for (const char *p = class_name; *p; p++) {
|
||||||
|
hash = ((hash << 5) + hash) + (uint32_t)*p;
|
||||||
|
}
|
||||||
|
hash = ((hash << 5) + hash) + '.';
|
||||||
|
for (const char *p = method_name; *p; p++) {
|
||||||
|
hash = ((hash << 5) + hash) + (uint32_t)*p;
|
||||||
|
}
|
||||||
|
return hash % RAVA_MAX_NATIVE_METHODS;
|
||||||
|
}
|
||||||
|
|
||||||
|
RavaNativeLibrary_t* rava_native_library_load(RavaNativeRegistry_t *registry, const char *path) {
|
||||||
|
if (!registry || !path) return NULL;
|
||||||
|
if (registry->library_count >= RAVA_MAX_NATIVE_LIBRARIES) return NULL;
|
||||||
|
|
||||||
|
void *handle = dlopen(path, RTLD_NOW | RTLD_LOCAL);
|
||||||
|
if (!handle) {
|
||||||
|
return NULL;
|
||||||
|
}
|
||||||
|
|
||||||
|
RavaNativeLibrary_t *lib = calloc(1, sizeof(RavaNativeLibrary_t));
|
||||||
|
if (!lib) {
|
||||||
|
dlclose(handle);
|
||||||
|
return NULL;
|
||||||
|
}
|
||||||
|
|
||||||
|
lib->path = strdup(path);
|
||||||
|
lib->handle = handle;
|
||||||
|
lib->method_count = 0;
|
||||||
|
lib->method_capacity = 16;
|
||||||
|
lib->methods = calloc(lib->method_capacity, sizeof(RavaNativeMethod_t));
|
||||||
|
|
||||||
|
const char *name_start = strrchr(path, '/');
|
||||||
|
if (name_start) {
|
||||||
|
name_start++;
|
||||||
|
} else {
|
||||||
|
name_start = path;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (strncmp(name_start, "lib", 3) == 0) {
|
||||||
|
name_start += 3;
|
||||||
|
}
|
||||||
|
|
||||||
|
size_t name_len = strlen(name_start);
|
||||||
|
if (name_len > 3 && strcmp(name_start + name_len - 3, ".so") == 0) {
|
||||||
|
lib->name = strndup(name_start, name_len - 3);
|
||||||
|
} else {
|
||||||
|
lib->name = strdup(name_start);
|
||||||
|
}
|
||||||
|
|
||||||
|
RavaLibraryRegisterFn register_fn = (RavaLibraryRegisterFn)dlsym(handle, RAVA_LIBRARY_REGISTER_SYMBOL);
|
||||||
|
if (register_fn) {
|
||||||
|
register_fn(registry);
|
||||||
|
}
|
||||||
|
|
||||||
|
registry->libraries[registry->library_count++] = lib;
|
||||||
|
return lib;
|
||||||
|
}
|
||||||
|
|
||||||
|
void rava_native_library_unload(RavaNativeRegistry_t *registry, RavaNativeLibrary_t *lib) {
|
||||||
|
if (!registry || !lib) return;
|
||||||
|
|
||||||
|
for (size_t i = 0; i < lib->method_count; i++) {
|
||||||
|
free(lib->methods[i].class_name);
|
||||||
|
free(lib->methods[i].method_name);
|
||||||
|
}
|
||||||
|
free(lib->methods);
|
||||||
|
|
||||||
|
if (lib->handle) {
|
||||||
|
dlclose(lib->handle);
|
||||||
|
}
|
||||||
|
|
||||||
|
free(lib->name);
|
||||||
|
free(lib->path);
|
||||||
|
free(lib);
|
||||||
|
}
|
||||||
|
|
||||||
|
bool rava_native_register_method(RavaNativeRegistry_t *registry,
|
||||||
|
const char *class_name,
|
||||||
|
const char *method_name,
|
||||||
|
RavaNativeType_e return_type,
|
||||||
|
RavaNativeType_e *param_types,
|
||||||
|
size_t param_count,
|
||||||
|
RavaNativeFunction_fn function,
|
||||||
|
void *user_data) {
|
||||||
|
if (!registry || !class_name || !method_name || !function) return false;
|
||||||
|
if (param_count > RAVA_MAX_METHOD_PARAMS) return false;
|
||||||
|
if (registry->cache_count >= RAVA_MAX_NATIVE_METHODS) return false;
|
||||||
|
|
||||||
|
RavaNativeMethod_t *method = calloc(1, sizeof(RavaNativeMethod_t));
|
||||||
|
if (!method) return false;
|
||||||
|
|
||||||
|
method->class_name = strdup(class_name);
|
||||||
|
method->method_name = strdup(method_name);
|
||||||
|
method->return_type = return_type;
|
||||||
|
method->param_count = param_count;
|
||||||
|
method->function = function;
|
||||||
|
method->user_data = user_data;
|
||||||
|
|
||||||
|
for (size_t i = 0; i < param_count; i++) {
|
||||||
|
method->param_types[i] = param_types[i];
|
||||||
|
}
|
||||||
|
|
||||||
|
uint32_t hash = _hash_method_name(class_name, method_name);
|
||||||
|
|
||||||
|
while (registry->method_cache[hash] != NULL) {
|
||||||
|
hash = (hash + 1) % RAVA_MAX_NATIVE_METHODS;
|
||||||
|
}
|
||||||
|
|
||||||
|
registry->method_cache[hash] = method;
|
||||||
|
registry->cache_count++;
|
||||||
|
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
RavaNativeMethod_t* rava_native_find_method(RavaNativeRegistry_t *registry,
|
||||||
|
const char *class_name,
|
||||||
|
const char *method_name) {
|
||||||
|
if (!registry || !class_name || !method_name) return NULL;
|
||||||
|
|
||||||
|
uint32_t hash = _hash_method_name(class_name, method_name);
|
||||||
|
uint32_t start = hash;
|
||||||
|
|
||||||
|
do {
|
||||||
|
RavaNativeMethod_t *method = registry->method_cache[hash];
|
||||||
|
if (!method) return NULL;
|
||||||
|
|
||||||
|
if (strcmp(method->class_name, class_name) == 0 &&
|
||||||
|
strcmp(method->method_name, method_name) == 0) {
|
||||||
|
return method;
|
||||||
|
}
|
||||||
|
|
||||||
|
hash = (hash + 1) % RAVA_MAX_NATIVE_METHODS;
|
||||||
|
} while (hash != start);
|
||||||
|
|
||||||
|
return NULL;
|
||||||
|
}
|
||||||
|
|
||||||
|
RavaNativeValue_t rava_native_invoke(RavaNativeMethod_t *method,
|
||||||
|
RavaNativeValue_t *args,
|
||||||
|
size_t arg_count) {
|
||||||
|
if (!method || !method->function) {
|
||||||
|
return rava_native_void();
|
||||||
|
}
|
||||||
|
return method->function(args, arg_count, method->user_data);
|
||||||
|
}
|
||||||
|
|
||||||
|
RavaNativeValue_t rava_native_int(int32_t val) {
|
||||||
|
RavaNativeValue_t native;
|
||||||
|
native.type = RAVA_NATIVE_INT;
|
||||||
|
native.data.int_val = val;
|
||||||
|
return native;
|
||||||
|
}
|
||||||
|
|
||||||
|
RavaNativeValue_t rava_native_long(int64_t val) {
|
||||||
|
RavaNativeValue_t native;
|
||||||
|
native.type = RAVA_NATIVE_LONG;
|
||||||
|
native.data.long_val = val;
|
||||||
|
return native;
|
||||||
|
}
|
||||||
|
|
||||||
|
RavaNativeValue_t rava_native_double(double val) {
|
||||||
|
RavaNativeValue_t native;
|
||||||
|
native.type = RAVA_NATIVE_DOUBLE;
|
||||||
|
native.data.double_val = val;
|
||||||
|
return native;
|
||||||
|
}
|
||||||
|
|
||||||
|
RavaNativeValue_t rava_native_boolean(bool val) {
|
||||||
|
RavaNativeValue_t native;
|
||||||
|
native.type = RAVA_NATIVE_BOOLEAN;
|
||||||
|
native.data.bool_val = val;
|
||||||
|
return native;
|
||||||
|
}
|
||||||
|
|
||||||
|
RavaNativeValue_t rava_native_string(const char *val) {
|
||||||
|
RavaNativeValue_t native;
|
||||||
|
native.type = RAVA_NATIVE_STRING;
|
||||||
|
native.data.string_val = val ? strdup(val) : NULL;
|
||||||
|
return native;
|
||||||
|
}
|
||||||
|
|
||||||
|
RavaNativeValue_t rava_native_void(void) {
|
||||||
|
RavaNativeValue_t native;
|
||||||
|
native.type = RAVA_NATIVE_VOID;
|
||||||
|
return native;
|
||||||
|
}
|
||||||
|
|
||||||
|
RavaNativeValue_t rava_native_null(void) {
|
||||||
|
RavaNativeValue_t native;
|
||||||
|
native.type = RAVA_NATIVE_OBJECT;
|
||||||
|
native.data.object_val = NULL;
|
||||||
|
return native;
|
||||||
|
}
|
||||||
99
loader/loader.h
Normal file
99
loader/loader.h
Normal file
@ -0,0 +1,99 @@
|
|||||||
|
#ifndef RAVA_LOADER_H
|
||||||
|
#define RAVA_LOADER_H
|
||||||
|
|
||||||
|
#include <stddef.h>
|
||||||
|
#include <stdbool.h>
|
||||||
|
#include <stdint.h>
|
||||||
|
|
||||||
|
#define RAVA_MAX_NATIVE_METHODS 256
|
||||||
|
#define RAVA_MAX_NATIVE_LIBRARIES 32
|
||||||
|
#define RAVA_MAX_METHOD_PARAMS 16
|
||||||
|
|
||||||
|
typedef enum {
|
||||||
|
RAVA_NATIVE_VOID,
|
||||||
|
RAVA_NATIVE_INT,
|
||||||
|
RAVA_NATIVE_LONG,
|
||||||
|
RAVA_NATIVE_DOUBLE,
|
||||||
|
RAVA_NATIVE_BOOLEAN,
|
||||||
|
RAVA_NATIVE_STRING,
|
||||||
|
RAVA_NATIVE_OBJECT,
|
||||||
|
RAVA_NATIVE_ARRAY
|
||||||
|
} RavaNativeType_e;
|
||||||
|
|
||||||
|
typedef struct {
|
||||||
|
RavaNativeType_e type;
|
||||||
|
union {
|
||||||
|
int32_t int_val;
|
||||||
|
int64_t long_val;
|
||||||
|
double double_val;
|
||||||
|
bool bool_val;
|
||||||
|
char *string_val;
|
||||||
|
void *object_val;
|
||||||
|
void *array_val;
|
||||||
|
} data;
|
||||||
|
} RavaNativeValue_t;
|
||||||
|
|
||||||
|
typedef RavaNativeValue_t (*RavaNativeFunction_fn)(RavaNativeValue_t *args, size_t arg_count, void *user_data);
|
||||||
|
|
||||||
|
typedef struct {
|
||||||
|
char *class_name;
|
||||||
|
char *method_name;
|
||||||
|
RavaNativeType_e return_type;
|
||||||
|
RavaNativeType_e param_types[RAVA_MAX_METHOD_PARAMS];
|
||||||
|
size_t param_count;
|
||||||
|
RavaNativeFunction_fn function;
|
||||||
|
void *user_data;
|
||||||
|
} RavaNativeMethod_t;
|
||||||
|
|
||||||
|
typedef struct {
|
||||||
|
char *name;
|
||||||
|
char *path;
|
||||||
|
void *handle;
|
||||||
|
RavaNativeMethod_t *methods;
|
||||||
|
size_t method_count;
|
||||||
|
size_t method_capacity;
|
||||||
|
} RavaNativeLibrary_t;
|
||||||
|
|
||||||
|
typedef struct {
|
||||||
|
RavaNativeLibrary_t *libraries[RAVA_MAX_NATIVE_LIBRARIES];
|
||||||
|
size_t library_count;
|
||||||
|
RavaNativeMethod_t *method_cache[RAVA_MAX_NATIVE_METHODS];
|
||||||
|
size_t cache_count;
|
||||||
|
} RavaNativeRegistry_t;
|
||||||
|
|
||||||
|
RavaNativeRegistry_t* rava_native_registry_create(void);
|
||||||
|
void rava_native_registry_destroy(RavaNativeRegistry_t *registry);
|
||||||
|
|
||||||
|
RavaNativeLibrary_t* rava_native_library_load(RavaNativeRegistry_t *registry, const char *path);
|
||||||
|
void rava_native_library_unload(RavaNativeRegistry_t *registry, RavaNativeLibrary_t *lib);
|
||||||
|
|
||||||
|
bool rava_native_register_method(RavaNativeRegistry_t *registry,
|
||||||
|
const char *class_name,
|
||||||
|
const char *method_name,
|
||||||
|
RavaNativeType_e return_type,
|
||||||
|
RavaNativeType_e *param_types,
|
||||||
|
size_t param_count,
|
||||||
|
RavaNativeFunction_fn function,
|
||||||
|
void *user_data);
|
||||||
|
|
||||||
|
RavaNativeMethod_t* rava_native_find_method(RavaNativeRegistry_t *registry,
|
||||||
|
const char *class_name,
|
||||||
|
const char *method_name);
|
||||||
|
|
||||||
|
RavaNativeValue_t rava_native_invoke(RavaNativeMethod_t *method,
|
||||||
|
RavaNativeValue_t *args,
|
||||||
|
size_t arg_count);
|
||||||
|
|
||||||
|
|
||||||
|
RavaNativeValue_t rava_native_int(int32_t val);
|
||||||
|
RavaNativeValue_t rava_native_long(int64_t val);
|
||||||
|
RavaNativeValue_t rava_native_double(double val);
|
||||||
|
RavaNativeValue_t rava_native_boolean(bool val);
|
||||||
|
RavaNativeValue_t rava_native_string(const char *val);
|
||||||
|
RavaNativeValue_t rava_native_void(void);
|
||||||
|
RavaNativeValue_t rava_native_null(void);
|
||||||
|
|
||||||
|
typedef void (*RavaLibraryRegisterFn)(RavaNativeRegistry_t *registry);
|
||||||
|
#define RAVA_LIBRARY_REGISTER_SYMBOL "rava_library_register"
|
||||||
|
|
||||||
|
#endif
|
||||||
91
tests/test_autobox.c
Normal file
91
tests/test_autobox.c
Normal file
@ -0,0 +1,91 @@
|
|||||||
|
#include "test_utils.h"
|
||||||
|
|
||||||
|
UnittestTestResult_t* test_autobox_int_to_integer(void) {
|
||||||
|
UNITTEST_BEGIN_TEST("TestAutobox", "test_autobox_int_to_integer");
|
||||||
|
|
||||||
|
RAVA_TEST_RUN(_unittest_result,
|
||||||
|
"public class Test {\n"
|
||||||
|
" public static int main() {\n"
|
||||||
|
" Integer x = 42;\n"
|
||||||
|
" return x.hashCode();\n"
|
||||||
|
" }\n"
|
||||||
|
"}\n",
|
||||||
|
"Test", "main", 42, "int should autobox to Integer");
|
||||||
|
|
||||||
|
UNITTEST_END_TEST();
|
||||||
|
}
|
||||||
|
|
||||||
|
UnittestTestResult_t* test_unbox_integer_to_int(void) {
|
||||||
|
UNITTEST_BEGIN_TEST("TestAutobox", "test_unbox_integer_to_int");
|
||||||
|
|
||||||
|
RAVA_TEST_RUN(_unittest_result,
|
||||||
|
"public class Test {\n"
|
||||||
|
" public static int main() {\n"
|
||||||
|
" Integer x = 10;\n"
|
||||||
|
" int y = x;\n"
|
||||||
|
" return y + 5;\n"
|
||||||
|
" }\n"
|
||||||
|
"}\n",
|
||||||
|
"Test", "main", 15, "Integer should unbox to int");
|
||||||
|
|
||||||
|
UNITTEST_END_TEST();
|
||||||
|
}
|
||||||
|
|
||||||
|
UnittestTestResult_t* test_autobox_long(void) {
|
||||||
|
UNITTEST_BEGIN_TEST("TestAutobox", "test_autobox_long");
|
||||||
|
|
||||||
|
RAVA_TEST_RUN(_unittest_result,
|
||||||
|
"public class Test {\n"
|
||||||
|
" public static int main() {\n"
|
||||||
|
" Long x = 100L;\n"
|
||||||
|
" long y = x;\n"
|
||||||
|
" return (int)y;\n"
|
||||||
|
" }\n"
|
||||||
|
"}\n",
|
||||||
|
"Test", "main", 100, "long should autobox/unbox with Long");
|
||||||
|
|
||||||
|
UNITTEST_END_TEST();
|
||||||
|
}
|
||||||
|
|
||||||
|
UnittestTestResult_t* test_autobox_double(void) {
|
||||||
|
UNITTEST_BEGIN_TEST("TestAutobox", "test_autobox_double");
|
||||||
|
|
||||||
|
RAVA_TEST_RUN(_unittest_result,
|
||||||
|
"public class Test {\n"
|
||||||
|
" public static int main() {\n"
|
||||||
|
" Double x = 3.0;\n"
|
||||||
|
" double y = x;\n"
|
||||||
|
" return (int)(y + 2.0);\n"
|
||||||
|
" }\n"
|
||||||
|
"}\n",
|
||||||
|
"Test", "main", 5, "double should autobox/unbox with Double");
|
||||||
|
|
||||||
|
UNITTEST_END_TEST();
|
||||||
|
}
|
||||||
|
|
||||||
|
int main(int argc, char **argv) {
|
||||||
|
UnittestConfig_t *config = unittest_config_create();
|
||||||
|
config->verbosity = 2;
|
||||||
|
|
||||||
|
if (argc > 1 && strcmp(argv[1], "--json") == 0) {
|
||||||
|
config->output_format = UNITTEST_FORMAT_JSON;
|
||||||
|
config->use_colors = false;
|
||||||
|
}
|
||||||
|
|
||||||
|
UnittestTestSuite_t *suite = unittest_test_suite_create("Autoboxing Tests");
|
||||||
|
|
||||||
|
UnittestTestCase_t *tc = unittest_test_case_create("TestAutobox");
|
||||||
|
unittest_test_case_add_result(tc, test_autobox_int_to_integer());
|
||||||
|
unittest_test_case_add_result(tc, test_unbox_integer_to_int());
|
||||||
|
unittest_test_case_add_result(tc, test_autobox_long());
|
||||||
|
unittest_test_case_add_result(tc, test_autobox_double());
|
||||||
|
unittest_test_suite_add_test_case(suite, tc);
|
||||||
|
|
||||||
|
unittest_generate_report(suite, config);
|
||||||
|
|
||||||
|
int failures = suite->total_failed + suite->total_errors;
|
||||||
|
unittest_test_suite_destroy(suite);
|
||||||
|
unittest_config_destroy(config);
|
||||||
|
|
||||||
|
return failures > 0 ? 1 : 0;
|
||||||
|
}
|
||||||
233
tests/test_loader.c
Normal file
233
tests/test_loader.c
Normal file
@ -0,0 +1,233 @@
|
|||||||
|
#include "test_utils.h"
|
||||||
|
#include "../loader/loader.h"
|
||||||
|
|
||||||
|
static RavaNativeValue_t test_add(RavaNativeValue_t *args, size_t arg_count, void *user_data) {
|
||||||
|
(void)user_data;
|
||||||
|
if (arg_count != 2) return rava_native_int(0);
|
||||||
|
int32_t a = args[0].data.int_val;
|
||||||
|
int32_t b = args[1].data.int_val;
|
||||||
|
return rava_native_int(a + b);
|
||||||
|
}
|
||||||
|
|
||||||
|
static RavaNativeValue_t test_multiply(RavaNativeValue_t *args, size_t arg_count, void *user_data) {
|
||||||
|
(void)user_data;
|
||||||
|
if (arg_count != 2) return rava_native_int(0);
|
||||||
|
int32_t a = args[0].data.int_val;
|
||||||
|
int32_t b = args[1].data.int_val;
|
||||||
|
return rava_native_int(a * b);
|
||||||
|
}
|
||||||
|
|
||||||
|
static RavaNativeValue_t test_greet(RavaNativeValue_t *args, size_t arg_count, void *user_data) {
|
||||||
|
(void)user_data;
|
||||||
|
if (arg_count != 1) return rava_native_string("");
|
||||||
|
const char *name = args[0].data.string_val;
|
||||||
|
char buffer[256];
|
||||||
|
snprintf(buffer, sizeof(buffer), "Hello, %s!", name ? name : "World");
|
||||||
|
return rava_native_string(buffer);
|
||||||
|
}
|
||||||
|
|
||||||
|
UnittestTestResult_t* test_registry_create_destroy(void) {
|
||||||
|
UNITTEST_BEGIN_TEST("TestLoader", "test_registry_create_destroy");
|
||||||
|
|
||||||
|
RavaNativeRegistry_t *registry = rava_native_registry_create();
|
||||||
|
UNITTEST_ASSERT_NOT_NULL(_unittest_result, registry, "registry should be created");
|
||||||
|
UNITTEST_ASSERT_EQUAL_INT(_unittest_result, 0, (int)registry->library_count, "library count should be 0");
|
||||||
|
UNITTEST_ASSERT_EQUAL_INT(_unittest_result, 0, (int)registry->cache_count, "cache count should be 0");
|
||||||
|
|
||||||
|
rava_native_registry_destroy(registry);
|
||||||
|
UNITTEST_ASSERT_PASS(_unittest_result, "registry destroyed without crash");
|
||||||
|
|
||||||
|
UNITTEST_END_TEST();
|
||||||
|
}
|
||||||
|
|
||||||
|
UnittestTestResult_t* test_register_method(void) {
|
||||||
|
UNITTEST_BEGIN_TEST("TestLoader", "test_register_method");
|
||||||
|
|
||||||
|
RavaNativeRegistry_t *registry = rava_native_registry_create();
|
||||||
|
|
||||||
|
RavaNativeType_e params[] = {RAVA_NATIVE_INT, RAVA_NATIVE_INT};
|
||||||
|
bool result = rava_native_register_method(registry, "Math", "add",
|
||||||
|
RAVA_NATIVE_INT, params, 2,
|
||||||
|
test_add, NULL);
|
||||||
|
UNITTEST_ASSERT_TRUE(_unittest_result, result, "method registration should succeed");
|
||||||
|
UNITTEST_ASSERT_EQUAL_INT(_unittest_result, 1, (int)registry->cache_count, "cache count should be 1");
|
||||||
|
|
||||||
|
rava_native_registry_destroy(registry);
|
||||||
|
|
||||||
|
UNITTEST_END_TEST();
|
||||||
|
}
|
||||||
|
|
||||||
|
UnittestTestResult_t* test_find_method(void) {
|
||||||
|
UNITTEST_BEGIN_TEST("TestLoader", "test_find_method");
|
||||||
|
|
||||||
|
RavaNativeRegistry_t *registry = rava_native_registry_create();
|
||||||
|
|
||||||
|
RavaNativeType_e params[] = {RAVA_NATIVE_INT, RAVA_NATIVE_INT};
|
||||||
|
rava_native_register_method(registry, "Math", "add",
|
||||||
|
RAVA_NATIVE_INT, params, 2,
|
||||||
|
test_add, NULL);
|
||||||
|
|
||||||
|
RavaNativeMethod_t *method = rava_native_find_method(registry, "Math", "add");
|
||||||
|
UNITTEST_ASSERT_NOT_NULL(_unittest_result, method, "method should be found");
|
||||||
|
UNITTEST_ASSERT_EQUAL_STR(_unittest_result, "Math", method->class_name, "class name should match");
|
||||||
|
UNITTEST_ASSERT_EQUAL_STR(_unittest_result, "add", method->method_name, "method name should match");
|
||||||
|
UNITTEST_ASSERT_EQUAL_INT(_unittest_result, RAVA_NATIVE_INT, (int)method->return_type, "return type should be INT");
|
||||||
|
UNITTEST_ASSERT_EQUAL_INT(_unittest_result, 2, (int)method->param_count, "param count should be 2");
|
||||||
|
|
||||||
|
RavaNativeMethod_t *not_found = rava_native_find_method(registry, "Math", "subtract");
|
||||||
|
UNITTEST_ASSERT_NULL(_unittest_result, not_found, "non-existent method should return NULL");
|
||||||
|
|
||||||
|
rava_native_registry_destroy(registry);
|
||||||
|
|
||||||
|
UNITTEST_END_TEST();
|
||||||
|
}
|
||||||
|
|
||||||
|
UnittestTestResult_t* test_invoke_method(void) {
|
||||||
|
UNITTEST_BEGIN_TEST("TestLoader", "test_invoke_method");
|
||||||
|
|
||||||
|
RavaNativeRegistry_t *registry = rava_native_registry_create();
|
||||||
|
|
||||||
|
RavaNativeType_e params[] = {RAVA_NATIVE_INT, RAVA_NATIVE_INT};
|
||||||
|
rava_native_register_method(registry, "Math", "add",
|
||||||
|
RAVA_NATIVE_INT, params, 2,
|
||||||
|
test_add, NULL);
|
||||||
|
|
||||||
|
RavaNativeMethod_t *method = rava_native_find_method(registry, "Math", "add");
|
||||||
|
|
||||||
|
RavaNativeValue_t args[2];
|
||||||
|
args[0] = rava_native_int(10);
|
||||||
|
args[1] = rava_native_int(20);
|
||||||
|
|
||||||
|
RavaNativeValue_t result = rava_native_invoke(method, args, 2);
|
||||||
|
UNITTEST_ASSERT_EQUAL_INT(_unittest_result, RAVA_NATIVE_INT, (int)result.type, "result should be INT");
|
||||||
|
UNITTEST_ASSERT_EQUAL_INT(_unittest_result, 30, result.data.int_val, "10 + 20 = 30");
|
||||||
|
|
||||||
|
rava_native_registry_destroy(registry);
|
||||||
|
|
||||||
|
UNITTEST_END_TEST();
|
||||||
|
}
|
||||||
|
|
||||||
|
UnittestTestResult_t* test_multiple_methods(void) {
|
||||||
|
UNITTEST_BEGIN_TEST("TestLoader", "test_multiple_methods");
|
||||||
|
|
||||||
|
RavaNativeRegistry_t *registry = rava_native_registry_create();
|
||||||
|
|
||||||
|
RavaNativeType_e int_params[] = {RAVA_NATIVE_INT, RAVA_NATIVE_INT};
|
||||||
|
rava_native_register_method(registry, "Math", "add",
|
||||||
|
RAVA_NATIVE_INT, int_params, 2,
|
||||||
|
test_add, NULL);
|
||||||
|
rava_native_register_method(registry, "Math", "multiply",
|
||||||
|
RAVA_NATIVE_INT, int_params, 2,
|
||||||
|
test_multiply, NULL);
|
||||||
|
|
||||||
|
RavaNativeType_e string_params[] = {RAVA_NATIVE_STRING};
|
||||||
|
rava_native_register_method(registry, "Greeter", "greet",
|
||||||
|
RAVA_NATIVE_STRING, string_params, 1,
|
||||||
|
test_greet, NULL);
|
||||||
|
|
||||||
|
UNITTEST_ASSERT_EQUAL_INT(_unittest_result, 3, (int)registry->cache_count, "should have 3 methods");
|
||||||
|
|
||||||
|
RavaNativeMethod_t *add = rava_native_find_method(registry, "Math", "add");
|
||||||
|
RavaNativeMethod_t *mul = rava_native_find_method(registry, "Math", "multiply");
|
||||||
|
RavaNativeMethod_t *greet = rava_native_find_method(registry, "Greeter", "greet");
|
||||||
|
|
||||||
|
UNITTEST_ASSERT_NOT_NULL(_unittest_result, add, "add should exist");
|
||||||
|
UNITTEST_ASSERT_NOT_NULL(_unittest_result, mul, "multiply should exist");
|
||||||
|
UNITTEST_ASSERT_NOT_NULL(_unittest_result, greet, "greet should exist");
|
||||||
|
|
||||||
|
RavaNativeValue_t args[2];
|
||||||
|
args[0] = rava_native_int(5);
|
||||||
|
args[1] = rava_native_int(7);
|
||||||
|
|
||||||
|
RavaNativeValue_t add_result = rava_native_invoke(add, args, 2);
|
||||||
|
UNITTEST_ASSERT_EQUAL_INT(_unittest_result, 12, add_result.data.int_val, "5 + 7 = 12");
|
||||||
|
|
||||||
|
RavaNativeValue_t mul_result = rava_native_invoke(mul, args, 2);
|
||||||
|
UNITTEST_ASSERT_EQUAL_INT(_unittest_result, 35, mul_result.data.int_val, "5 * 7 = 35");
|
||||||
|
|
||||||
|
rava_native_registry_destroy(registry);
|
||||||
|
|
||||||
|
UNITTEST_END_TEST();
|
||||||
|
}
|
||||||
|
|
||||||
|
UnittestTestResult_t* test_value_conversion(void) {
|
||||||
|
UNITTEST_BEGIN_TEST("TestLoader", "test_value_conversion");
|
||||||
|
|
||||||
|
RavaValue_t int_val = rava_value_int(42);
|
||||||
|
RavaNativeValue_t native_int = rava_value_to_native(int_val);
|
||||||
|
UNITTEST_ASSERT_EQUAL_INT(_unittest_result, RAVA_NATIVE_INT, (int)native_int.type, "type should be INT");
|
||||||
|
UNITTEST_ASSERT_EQUAL_INT(_unittest_result, 42, native_int.data.int_val, "value should be 42");
|
||||||
|
|
||||||
|
RavaValue_t back_to_rava = rava_native_to_value(native_int);
|
||||||
|
UNITTEST_ASSERT_EQUAL_INT(_unittest_result, RAVA_VAL_INT, (int)back_to_rava.type, "type should be RAVA_VAL_INT");
|
||||||
|
UNITTEST_ASSERT_EQUAL_INT(_unittest_result, 42, back_to_rava.data.int_val, "value should be 42");
|
||||||
|
|
||||||
|
RavaValue_t long_val = rava_value_long(123456789012LL);
|
||||||
|
RavaNativeValue_t native_long = rava_value_to_native(long_val);
|
||||||
|
UNITTEST_ASSERT_EQUAL_INT(_unittest_result, RAVA_NATIVE_LONG, (int)native_long.type, "type should be LONG");
|
||||||
|
UNITTEST_ASSERT_EQUAL_LONG(_unittest_result, 123456789012LL, native_long.data.long_val, "long value should match");
|
||||||
|
|
||||||
|
RavaValue_t bool_val = rava_value_boolean(true);
|
||||||
|
RavaNativeValue_t native_bool = rava_value_to_native(bool_val);
|
||||||
|
UNITTEST_ASSERT_EQUAL_INT(_unittest_result, RAVA_NATIVE_BOOLEAN, (int)native_bool.type, "type should be BOOLEAN");
|
||||||
|
UNITTEST_ASSERT_TRUE(_unittest_result, native_bool.data.bool_val, "bool should be true");
|
||||||
|
|
||||||
|
UNITTEST_END_TEST();
|
||||||
|
}
|
||||||
|
|
||||||
|
UnittestTestResult_t* test_string_method(void) {
|
||||||
|
UNITTEST_BEGIN_TEST("TestLoader", "test_string_method");
|
||||||
|
|
||||||
|
RavaNativeRegistry_t *registry = rava_native_registry_create();
|
||||||
|
|
||||||
|
RavaNativeType_e string_params[] = {RAVA_NATIVE_STRING};
|
||||||
|
rava_native_register_method(registry, "Greeter", "greet",
|
||||||
|
RAVA_NATIVE_STRING, string_params, 1,
|
||||||
|
test_greet, NULL);
|
||||||
|
|
||||||
|
RavaNativeMethod_t *greet = rava_native_find_method(registry, "Greeter", "greet");
|
||||||
|
UNITTEST_ASSERT_NOT_NULL(_unittest_result, greet, "greet should exist");
|
||||||
|
|
||||||
|
RavaNativeValue_t args[1];
|
||||||
|
args[0] = rava_native_string("Rava");
|
||||||
|
|
||||||
|
RavaNativeValue_t result = rava_native_invoke(greet, args, 1);
|
||||||
|
UNITTEST_ASSERT_EQUAL_INT(_unittest_result, RAVA_NATIVE_STRING, (int)result.type, "result should be STRING");
|
||||||
|
UNITTEST_ASSERT_EQUAL_STR(_unittest_result, "Hello, Rava!", result.data.string_val, "greeting should match");
|
||||||
|
|
||||||
|
free(args[0].data.string_val);
|
||||||
|
free(result.data.string_val);
|
||||||
|
rava_native_registry_destroy(registry);
|
||||||
|
|
||||||
|
UNITTEST_END_TEST();
|
||||||
|
}
|
||||||
|
|
||||||
|
int main(int argc, char **argv) {
|
||||||
|
UnittestConfig_t *config = unittest_config_create();
|
||||||
|
config->verbosity = 2;
|
||||||
|
|
||||||
|
if (argc > 1 && strcmp(argv[1], "--json") == 0) {
|
||||||
|
config->output_format = UNITTEST_FORMAT_JSON;
|
||||||
|
config->use_colors = false;
|
||||||
|
}
|
||||||
|
|
||||||
|
UnittestTestSuite_t *suite = unittest_test_suite_create("Dynamic Library Loader Tests");
|
||||||
|
|
||||||
|
UnittestTestCase_t *tc = unittest_test_case_create("TestLoader");
|
||||||
|
unittest_test_case_add_result(tc, test_registry_create_destroy());
|
||||||
|
unittest_test_case_add_result(tc, test_register_method());
|
||||||
|
unittest_test_case_add_result(tc, test_find_method());
|
||||||
|
unittest_test_case_add_result(tc, test_invoke_method());
|
||||||
|
unittest_test_case_add_result(tc, test_multiple_methods());
|
||||||
|
unittest_test_case_add_result(tc, test_value_conversion());
|
||||||
|
unittest_test_case_add_result(tc, test_string_method());
|
||||||
|
unittest_test_suite_add_test_case(suite, tc);
|
||||||
|
|
||||||
|
unittest_generate_report(suite, config);
|
||||||
|
|
||||||
|
int failures = suite->total_failed + suite->total_errors;
|
||||||
|
unittest_test_suite_destroy(suite);
|
||||||
|
unittest_config_destroy(config);
|
||||||
|
|
||||||
|
return failures > 0 ? 1 : 0;
|
||||||
|
}
|
||||||
163
tests/test_object_methods.c
Normal file
163
tests/test_object_methods.c
Normal file
@ -0,0 +1,163 @@
|
|||||||
|
#include "test_utils.h"
|
||||||
|
|
||||||
|
UnittestTestResult_t* test_hashcode_int(void) {
|
||||||
|
UNITTEST_BEGIN_TEST("TestObjectMethods", "test_hashcode_int");
|
||||||
|
|
||||||
|
RAVA_TEST_RUN(_unittest_result,
|
||||||
|
"public class Test {\n"
|
||||||
|
" public static int main() {\n"
|
||||||
|
" int x = 42;\n"
|
||||||
|
" return x.hashCode();\n"
|
||||||
|
" }\n"
|
||||||
|
"}\n",
|
||||||
|
"Test", "main", 42, "int.hashCode() should return the int value");
|
||||||
|
|
||||||
|
UNITTEST_END_TEST();
|
||||||
|
}
|
||||||
|
|
||||||
|
UnittestTestResult_t* test_hashcode_string(void) {
|
||||||
|
UNITTEST_BEGIN_TEST("TestObjectMethods", "test_hashcode_string");
|
||||||
|
|
||||||
|
RAVA_TEST_RUN(_unittest_result,
|
||||||
|
"public class Test {\n"
|
||||||
|
" public static int main() {\n"
|
||||||
|
" String s = \"hello\";\n"
|
||||||
|
" int h = s.hashCode();\n"
|
||||||
|
" return h != 0 ? 1 : 0;\n"
|
||||||
|
" }\n"
|
||||||
|
"}\n",
|
||||||
|
"Test", "main", 1, "String.hashCode() should return non-zero for non-empty string");
|
||||||
|
|
||||||
|
UNITTEST_END_TEST();
|
||||||
|
}
|
||||||
|
|
||||||
|
UnittestTestResult_t* test_equals_strings(void) {
|
||||||
|
UNITTEST_BEGIN_TEST("TestObjectMethods", "test_equals_strings");
|
||||||
|
|
||||||
|
RAVA_TEST_RUN(_unittest_result,
|
||||||
|
"public class Test {\n"
|
||||||
|
" public static int main() {\n"
|
||||||
|
" String a = \"hello\";\n"
|
||||||
|
" String b = \"hello\";\n"
|
||||||
|
" return a.equals(b) ? 1 : 0;\n"
|
||||||
|
" }\n"
|
||||||
|
"}\n",
|
||||||
|
"Test", "main", 1, "String.equals() should return true for equal strings");
|
||||||
|
|
||||||
|
UNITTEST_END_TEST();
|
||||||
|
}
|
||||||
|
|
||||||
|
UnittestTestResult_t* test_equals_different_strings(void) {
|
||||||
|
UNITTEST_BEGIN_TEST("TestObjectMethods", "test_equals_different_strings");
|
||||||
|
|
||||||
|
RAVA_TEST_RUN(_unittest_result,
|
||||||
|
"public class Test {\n"
|
||||||
|
" public static int main() {\n"
|
||||||
|
" String a = \"hello\";\n"
|
||||||
|
" String b = \"world\";\n"
|
||||||
|
" return a.equals(b) ? 1 : 0;\n"
|
||||||
|
" }\n"
|
||||||
|
"}\n",
|
||||||
|
"Test", "main", 0, "String.equals() should return false for different strings");
|
||||||
|
|
||||||
|
UNITTEST_END_TEST();
|
||||||
|
}
|
||||||
|
|
||||||
|
UnittestTestResult_t* test_tostring_int(void) {
|
||||||
|
UNITTEST_BEGIN_TEST("TestObjectMethods", "test_tostring_int");
|
||||||
|
|
||||||
|
RAVA_TEST_RUN(_unittest_result,
|
||||||
|
"public class Test {\n"
|
||||||
|
" public static int main() {\n"
|
||||||
|
" int x = 42;\n"
|
||||||
|
" return x.toString().equals(\"42\") ? 1 : 0;\n"
|
||||||
|
" }\n"
|
||||||
|
"}\n",
|
||||||
|
"Test", "main", 1, "int.toString() should return correct string");
|
||||||
|
|
||||||
|
UNITTEST_END_TEST();
|
||||||
|
}
|
||||||
|
|
||||||
|
UnittestTestResult_t* test_getclass_string(void) {
|
||||||
|
UNITTEST_BEGIN_TEST("TestObjectMethods", "test_getclass_string");
|
||||||
|
|
||||||
|
RAVA_TEST_RUN(_unittest_result,
|
||||||
|
"public class Test {\n"
|
||||||
|
" public static int main() {\n"
|
||||||
|
" String s = \"hello\";\n"
|
||||||
|
" return s.getClass().equals(\"String\") ? 1 : 0;\n"
|
||||||
|
" }\n"
|
||||||
|
"}\n",
|
||||||
|
"Test", "main", 1, "String.getClass() should return \"String\"");
|
||||||
|
|
||||||
|
UNITTEST_END_TEST();
|
||||||
|
}
|
||||||
|
|
||||||
|
UnittestTestResult_t* test_object_equals_same(void) {
|
||||||
|
UNITTEST_BEGIN_TEST("TestObjectMethods", "test_object_equals_same");
|
||||||
|
|
||||||
|
RAVA_TEST_RUN(_unittest_result,
|
||||||
|
"public class Obj {\n"
|
||||||
|
" public int x;\n"
|
||||||
|
"}\n"
|
||||||
|
"public class Test {\n"
|
||||||
|
" public static int main() {\n"
|
||||||
|
" Obj a = new Obj();\n"
|
||||||
|
" a.x = 10;\n"
|
||||||
|
" return a.equals(a) ? 1 : 0;\n"
|
||||||
|
" }\n"
|
||||||
|
"}\n",
|
||||||
|
"Test", "main", 1, "Object.equals() should return true for same reference");
|
||||||
|
|
||||||
|
UNITTEST_END_TEST();
|
||||||
|
}
|
||||||
|
|
||||||
|
UnittestTestResult_t* test_object_equals_different(void) {
|
||||||
|
UNITTEST_BEGIN_TEST("TestObjectMethods", "test_object_equals_different");
|
||||||
|
|
||||||
|
RAVA_TEST_RUN(_unittest_result,
|
||||||
|
"public class Obj {\n"
|
||||||
|
" public int x;\n"
|
||||||
|
"}\n"
|
||||||
|
"public class Test {\n"
|
||||||
|
" public static int main() {\n"
|
||||||
|
" Obj a = new Obj();\n"
|
||||||
|
" Obj b = new Obj();\n"
|
||||||
|
" return a.equals(b) ? 1 : 0;\n"
|
||||||
|
" }\n"
|
||||||
|
"}\n",
|
||||||
|
"Test", "main", 0, "Object.equals() should return false for different references");
|
||||||
|
|
||||||
|
UNITTEST_END_TEST();
|
||||||
|
}
|
||||||
|
|
||||||
|
int main(int argc, char **argv) {
|
||||||
|
UnittestConfig_t *config = unittest_config_create();
|
||||||
|
config->verbosity = 2;
|
||||||
|
|
||||||
|
if (argc > 1 && strcmp(argv[1], "--json") == 0) {
|
||||||
|
config->output_format = UNITTEST_FORMAT_JSON;
|
||||||
|
config->use_colors = false;
|
||||||
|
}
|
||||||
|
|
||||||
|
UnittestTestSuite_t *suite = unittest_test_suite_create("Object Methods Tests");
|
||||||
|
|
||||||
|
UnittestTestCase_t *tc = unittest_test_case_create("TestObjectMethods");
|
||||||
|
unittest_test_case_add_result(tc, test_hashcode_int());
|
||||||
|
unittest_test_case_add_result(tc, test_hashcode_string());
|
||||||
|
unittest_test_case_add_result(tc, test_equals_strings());
|
||||||
|
unittest_test_case_add_result(tc, test_equals_different_strings());
|
||||||
|
unittest_test_case_add_result(tc, test_tostring_int());
|
||||||
|
unittest_test_case_add_result(tc, test_getclass_string());
|
||||||
|
unittest_test_case_add_result(tc, test_object_equals_same());
|
||||||
|
unittest_test_case_add_result(tc, test_object_equals_different());
|
||||||
|
unittest_test_suite_add_test_case(suite, tc);
|
||||||
|
|
||||||
|
unittest_generate_report(suite, config);
|
||||||
|
|
||||||
|
int failures = suite->total_failed + suite->total_errors;
|
||||||
|
unittest_test_suite_destroy(suite);
|
||||||
|
unittest_config_destroy(config);
|
||||||
|
|
||||||
|
return failures > 0 ? 1 : 0;
|
||||||
|
}
|
||||||
Loading…
Reference in New Issue
Block a user