Files
libultraship/tests/script_test.cpp
LywxandKenix Whisperwind 787ec0bde4 Implemented dynamic loading (#1051)
* Fully migrated event system into lus

* Fixed tidy

* Last tidy error

* Added some more tidy stuff

* Last pass of tidy stuff

* Implemented dynamic module loading among o2r metadata

* Cleanup some things and moved to built-in libtcc

* Reverted CI dependencies

* Trying to fix libtcc compilation

* Another attempt at trying to fix this

* Fixed GetPlatform error

* Attempt at fixing cross-compilation for libtcc

* Another attempt for ios this time

* Disabled codesign on ios

* Addressed tidy comments

* Implemented keystore system for signature validation

* Renamed some things to manifest

* Fixed small compilation issue

* More tidy fixes

* Fully tested checksum and signature validations

* Added a sign tool

* Renamed missing function

* Removed leftover

* Fixed scripts not loading

* Implemented LoadAll based on dependencies

* Small fix so mods can load high resolution textures

* Added line fixer so it says the real path

* Fixed an issue related to f3d modding

* Fixed tidy

* Shifted some opcodes by one

* Fixed weird rebase shenanigans

* Moved to throw when loading untrusted scripts

* Fixed tidy

* Splitted compile and load for better info on ports

* Fixed tidy again

* Fixed wrong types on event system

* Fixed returning an invalid id when sorting, implemented an internal id tracker among optimizing the sort when registering a new listener

* Removed an unused queue

* Moved HexToBytes into StringHelper and renamed KeystoreSystem to Keystore

* Moved ScriptLoader data to be private

* Replaced default license

* Exported symbols on windows, added variables to set more things on libtcc and changed cmake to tell cmake to export a .lib

* Fixed wrong macro

* Fixed an error when generating .a files

* Fixed tidy

* Fixed more tidy

* I hate tidy

* Disabled readability-identifier-naming for original c extern

* Fixed wrong define

* Removed segmented guard because of false positives

* Fixed validation to verify if address comes from a DLL

* Fixed comment

* Fixed bad cast

* Fixed wrong cast again

* Implemented script loading test

* Fixed missing virtual function

* Added prints to tests

* Fix tests

* Fixed Script System tests

* Fixed last error and a better way to test ModInit and ModExit

* Updated dependencies among adding a tool to generate a keypair

* Added a key load based on user-prompt, trying to fix linux test and updated sign tool to add public_key

* Applied formatting on script_test

* Fixed StringHelper format

* Added missing header

* Updated test to be a ptr on status

* Removed unnecessary stuff

* Fully validated and fixed keystore saving and loading

* Updated geenerate keys script

* Fixed CompileAll

* Addressed latest comments

* Updated autogenerated keys script

* Updated untrusted handler to expose key too

* Added an small warning when not using ed25519 keys

* Added missing python dependencies

* Fixed tidy and updated actions

* Another attempt at fixing windows builds

* Removed search by listener validation because sometimes the same callback is used

* Added an error handler just to avoid crashing the program

* Added better errors for compiler

* Splitted logs into better fitted spdlog calls

* Kinda non-scope but i fixed a big performance issue on shaders

* Added KeyOrigin to know if it comes from the game, from lus or from the user

* Renamed from ScriptSystem to ScriptLoader

* Implemented cleanup on context destroy

* Finish some cleaning

* Wrapped dll load code into an optional ifder

* Fixed linux tests

* Enabled dll loading in test

* Inverted define

* Populates initial key list

* Moves keys to root of LUS

---------

Co-authored-by: Kenix Whisperwind <kenixwhisperwind@gmail.com>
2026-04-13 22:40:35 -04:00

156 lines
4.4 KiB
C++

#include <gtest/gtest.h>
#include <stdexcept>
#include <iostream>
#include <string>
#include <vector>
#include <filesystem>
#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<uint8_t>&) override {
return true;
}
std::shared_ptr<File> LoadFile(uint64_t hash) override {
return nullptr;
}
std::shared_ptr<File> LoadFile(const std::string& filePath) override {
auto file = std::make_shared<File>();
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<std::vector<char>>(content.c_str(), content.c_str() + content.size());
file->IsLoaded = true;
return file;
}
};
std::string FindLibTCC1Folder() {
std::vector<std::string> 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<Ship::RamArchive>();
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<int (*)(int)>(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<Ship::RamArchive>();
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<void (*)()>(system.GetFunction("Test Script", "ModInit"));
auto exit = reinterpret_cast<void (*)()>(system.GetFunction("Test Script", "ModExit"));
ASSERT_NE(init, nullptr);
ASSERT_NE(exit, nullptr);
init();
exit();
}
TEST(ScriptLoader, CompileDefines) {
auto archive = std::make_shared<Ship::RamArchive>();
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<int (*)(int)>(system.GetFunction("Test Script", "fib"));
ASSERT_NE(fib, nullptr);
EXPECT_EQ(fib(10), 20);
}