Files
OpenRoyale/native/test/test_reconstruction.cpp
2026-07-09 07:40:42 +02:00

122 lines
5.4 KiB
C++

// test_reconstruction.cpp - behavioral tests for the reconstructed libg native code.
//
// Compiled for the HOST (x86_64) and run directly. Because the reconstruction is arch-independent
// C++, this exercises the exact logic that ships in the arm64/v7a .so - so it validates the
// *function-matching* claim behaviourally, not just that the code compiles. Covers the String class
// (SSO + heap) and the dual-mode FileHandle (stdio backend; the AAsset backend needs a device).
#include "engine_structs.h"
#include "file_handle.h"
#include <cstdio>
#include <cstring>
#include <cstdlib>
using namespace titan;
// Host stubs for the NDK AAsset C API. These tests exercise the stdio (FILE*) backend only, so the
// asset branch is never taken; the stubs merely satisfy the linker (on device these live in
// libandroid.so). Signatures must match the forward declarations in file_handle.h.
extern "C" {
int AAsset_read(AAsset*, void*, size_t) { return -1; }
long AAsset_seek(AAsset*, long, int) { return -1; }
long AAsset_getLength(AAsset*) { return 0; }
void AAsset_close(AAsset*) {}
AAsset* AAssetManager_open(AAssetManager*, const char*, int) { return nullptr; }
}
// Reconstructed String methods (defined in titan_string.cpp / sub_1fe_cluster.cpp).
namespace titan {
struct_unknown_1* sub_1fe43c(struct_unknown_1*);
int sub_1f7160(const struct_unknown_1*);
const char* fcn_001f712c(const struct_unknown_1*);
bool fcn_00200064(const struct_unknown_1*, const void*, int, int);
bool fcn_0020010c(const struct_unknown_1*, const char*);
bool fcn_002000d8(const struct_unknown_1*, const struct_unknown_1*);
struct_unknown_1* fcn_001fea7c(struct_unknown_1*, const char*);
struct_unknown_1* fcn_001feb70(struct_unknown_1*, const char*);
struct_unknown_1* fcn_001fe9b8(struct_unknown_1*);
struct_unknown_1* fcn_001feb9c(struct_unknown_1*, const struct_unknown_1*);
const char* fcn_001f7044(struct_unknown_1*);
}
static int g_fail = 0, g_pass = 0;
#define CHECK(cond, msg) do { if (cond) { ++g_pass; } else { ++g_fail; \
std::printf(" FAIL: %s\n", msg); } } while (0)
static void test_string_sso() {
std::printf("[String] short string (SSO path)\n");
struct_unknown_1 s;
fcn_001feb70(&s, "hello"); // String("hello")
CHECK(titan_string_length(&s) == 5, "length==5");
CHECK(sub_1f7160(&s) == 1, "isSSO (len<8)");
CHECK(std::strcmp(fcn_001f712c(&s), "hello") == 0, "data()==\"hello\"");
CHECK(std::strcmp(fcn_001f7044(&s), "hello") == 0, "c_str()==\"hello\"");
CHECK(fcn_0020010c(&s, "he") == true, "startsWith(\"he\")");
CHECK(fcn_0020010c(&s, "xy") == false, "!startsWith(\"xy\")");
CHECK(fcn_00200064(&s, "ll", 2, 2) == true, "matchAt(2,\"ll\")");
fcn_001fe9b8(&s); // ~String (no heap to free)
}
static void test_string_heap() {
std::printf("[String] long string (heap path)\n");
struct_unknown_1 s;
const char* L = "this_is_a_long_string_over_8_chars";
fcn_001feb70(&s, L);
CHECK(titan_string_length(&s) == (int)std::strlen(L), "length matches");
CHECK(sub_1f7160(&s) == 0, "!isSSO (len>=8 -> heap)");
CHECK(std::strcmp(fcn_001f712c(&s), L) == 0, "heap data() matches");
CHECK(fcn_0020010c(&s, "this_is") == true, "startsWith prefix");
struct_unknown_1 c; // copy-ctor
sub_1fe43c(&c);
fcn_001feb9c(&c, &s);
CHECK(std::strcmp(fcn_001f712c(&c), L) == 0, "copy has same content");
CHECK(fcn_002000d8(&s, &c) == true, "startsWith(String copy)");
fcn_001fe9b8(&c);
fcn_001fe9b8(&s); // frees heap (no crash / leak-clean)
std::printf(" (heap alloc/free exercised)\n");
}
static void test_filehandle_stdio() {
std::printf("[FileHandle] stdio backend (read/seek/getLength/getc/close)\n");
char tmpl[] = "/tmp/titan_fh_XXXXXX";
int fd = mkstemp(tmpl);
CHECK(fd >= 0, "temp file created");
const char* payload = "Hello\nWorld"; // 11 bytes
FILE* w = fdopen(fd, "wb"); std::fwrite(payload, 1, 11, w); std::fclose(w);
FileHandle fh;
fcn_0022af94(&fh, tmpl, "rb"); // open (falls to fopen: real file)
CHECK(fcn_002293dc(&fh) == true, "isOpen after open");
CHECK(fh.fp != nullptr && fh.asset == nullptr, "stdio backend selected");
CHECK(fcn_00229410(&fh) == 11, "getLength==11");
char buf[6] = {0};
int n = fcn_00229484(&fh, buf, 1, 5); // read 5 bytes
CHECK(n == 5 && std::memcmp(buf, "Hello", 5) == 0, "read 5 == \"Hello\"");
fcn_0022b2f4(&fh, 6, SEEK_SET); // seek past the '\n'
CHECK(fcn_0022b18c(&fh) == 'W', "getc after seek == 'W'");
fcn_002294e0(&fh); // closeImpl
CHECK(fh.fp == nullptr, "fp nulled after close");
std::remove(tmpl);
}
static void test_filehandle_missing() {
std::printf("[FileHandle] missing file (no backend, isOpen false)\n");
FileHandle fh;
fcn_0022af94(&fh, "/no/such/titan/path.dat", "rb"); // fopen fails; no asset mgr -> nothing
CHECK(fcn_002293dc(&fh) == false, "isOpen false for missing file");
fcn_0022b204(&fh); // close+reset is safe on empty
}
int main() {
std::printf("== reconstructed libg behavioral tests ==\n");
test_string_sso();
test_string_heap();
test_filehandle_stdio();
test_filehandle_missing();
std::printf("\n== %d passed, %d failed ==\n", g_pass, g_fail);
return g_fail == 0 ? 0 : 1;
}