#include #include #include #include #include #include #include "ship/resource/File.h" #include "ship/resource/archive/Archive.h" #include "ship/scripting/ScriptLoader.h" namespace fs = std::filesystem; // ============================================================ // ScriptLoader Tests // ============================================================ namespace Ship { class RamArchive final : virtual public Archive { public: RamArchive() : Archive("ram://") { } bool Open() override { return true; } bool Close() override { return true; } bool WriteFile(const std::string&, const std::vector&) override { return true; } std::shared_ptr LoadFile(uint64_t hash) override { return nullptr; } std::shared_ptr LoadFile(const std::string& filePath) override { auto file = std::make_shared(); std::string content; if (filePath == "manifest.json") { content = R"({ "name": "Test Script", "code_version": 1, "main": "build.gen" })"; } else if (filePath == "build.gen") { content = "test.c"; } else if (filePath == "test.c") { content = R"( #define HM_API __attribute__((visibility("default"))) static int status = -1; #ifdef __FIBx2__ HM_API int fib(int n) { if (n <= 1) return n * 2; return 2 * n; } #else HM_API int fib(int n) { if (n <= 1) return n; return fib(n - 1) + fib(n - 2); } #endif HM_API void ModInit() { status = 1; } HM_API void ModExit() { status = 0; } )"; } else { return nullptr; } file->Buffer = std::make_shared>(content.c_str(), content.c_str() + content.size()); file->IsLoaded = true; return file; } }; std::string FindLibTCC1Folder() { std::vector searchPaths = { "/usr/lib", "/usr/local/lib", "/lib" }; for (const auto& basePath : searchPaths) { if (!fs::exists(basePath)) continue; try { auto options = fs::directory_options::skip_permission_denied; for (const auto& entry : fs::recursive_directory_iterator(basePath, options)) { if (entry.is_regular_file() && entry.path().filename() == "libtcc1.a") { return entry.path().parent_path().string(); } } } catch (const fs::filesystem_error& e) { continue; } } return ""; } }; // namespace Ship TEST(ScriptLoader, FibFunction) { auto archive = std::make_shared(); auto libPath = Ship::FindLibTCC1Folder(); archive->Load(); Ship::ScriptLoader system({}, 1, "-g -Wl", {}, { libPath }, {}); ASSERT_NE(libPath, ""); system.Compile(archive); system.LoadAll(); auto fib = reinterpret_cast(system.GetFunction("Test Script", "fib")); ASSERT_NE(fib, nullptr) << "Failed to find 'fib' function"; EXPECT_EQ(fib(10), 55); } TEST(ScriptLoader, ModInitAndExit) { auto archive = std::make_shared(); archive->Load(); auto libPath = Ship::FindLibTCC1Folder(); Ship::ScriptLoader system({}, 1, "-g -Wl", {}, { libPath }, {}); ASSERT_NE(libPath, ""); system.Compile(archive); system.LoadAll(); auto init = reinterpret_cast(system.GetFunction("Test Script", "ModInit")); auto exit = reinterpret_cast(system.GetFunction("Test Script", "ModExit")); ASSERT_NE(init, nullptr); ASSERT_NE(exit, nullptr); init(); exit(); } TEST(ScriptLoader, CompileDefines) { auto archive = std::make_shared(); archive->Load(); auto libPath = Ship::FindLibTCC1Folder(); Ship::ScriptLoader system({ { "__FIBx2__", "1" } }, 1, "-g -Wl", {}, { libPath }, {}); ASSERT_NE(libPath, ""); system.Compile(archive); system.LoadAll(); auto fib = reinterpret_cast(system.GetFunction("Test Script", "fib")); ASSERT_NE(fib, nullptr); EXPECT_EQ(fib(10), 20); }