diff --git a/loader/loader.c b/loader/loader.c new file mode 100644 index 0000000..01aaabb --- /dev/null +++ b/loader/loader.c @@ -0,0 +1,216 @@ +#include "loader.h" +#include +#include +#include + +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; +} diff --git a/loader/loader.h b/loader/loader.h new file mode 100644 index 0000000..585e106 --- /dev/null +++ b/loader/loader.h @@ -0,0 +1,99 @@ +#ifndef RAVA_LOADER_H +#define RAVA_LOADER_H + +#include +#include +#include + +#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 diff --git a/tests/test_autobox.c b/tests/test_autobox.c new file mode 100644 index 0000000..82d5ce7 --- /dev/null +++ b/tests/test_autobox.c @@ -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; +} diff --git a/tests/test_loader.c b/tests/test_loader.c new file mode 100644 index 0000000..3e2f27b --- /dev/null +++ b/tests/test_loader.c @@ -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; +} diff --git a/tests/test_object_methods.c b/tests/test_object_methods.c new file mode 100644 index 0000000..1b41a3a --- /dev/null +++ b/tests/test_object_methods.c @@ -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; +}