Source
stdlib/tests/sys_test.cpp
1
// Copyright (c) 2026 BigBrain LLC. MIT-licensed (see LICENSE).2
// Original work; see ACKNOWLEDGMENTS.md for the open-source ideas we build upon.3
#include "sys.hpp"5
#include <array>6
#include <string>7
#include <vector>9
#include <gtest/gtest.h>11
namespace sys = cheatah::sys;13
// The stable extern "C" hook the cheatah runtime resolves with dlsym and calls to14
// forward the program's arguments (defined in sys.cpp; not declared in sys.hpp15
// because it is never cheatah-visible).16
extern "C" void cheatah_set_argv(int argc, char** argv);18
namespace {20
// Build a mutable argv-style vector from string literals (C main() hands the21
// program non-const char*, so the test feeds set_argv the same shape).22
std::vector<char*> make_argv(std::initializer_list<const char*> args) {23
std::vector<char*> v;24
v.reserve(args.size());25
for (const char* a : args) v.push_back(const_cast<char*>(a));26
return v;27
}29
} // namespace31
TEST(CheatahSys, Argv) {32
auto raw = make_argv({"prog", "alpha", "beta gamma"});33
sys::set_argv(static_cast<int>(raw.size()), raw.data());35
ASSERT_EQ(sys::argv.size(), 3u);36
EXPECT_EQ(sys::argv[0], "prog");37
EXPECT_EQ(sys::argv[1], "alpha");38
EXPECT_EQ(sys::argv[2], "beta gamma"); // one argument, embedded space preserved39
}41
TEST(CheatahSys, ArgvCopiesRatherThanAliases) {42
// set_argv must COPY the C strings: the runtime's argv storage is not owned by43
// the module, so sys.argv must stay valid after the originals change.44
std::array<char, 8> mutable_arg = {'f', 'i', 'r', 's', 't', '\0'};45
std::array<char*, 1> raw = {mutable_arg.data()};46
sys::set_argv(1, raw.data());47
mutable_arg[0] = 'w'; // clobber the source buffer49
ASSERT_EQ(sys::argv.size(), 1u);50
EXPECT_EQ(sys::argv[0], "first");51
}53
TEST(CheatahSys, SetArgvReplacesPreviousArguments) {54
auto first = make_argv({"prog", "one", "two"});55
sys::set_argv(static_cast<int>(first.size()), first.data());56
ASSERT_EQ(sys::argv.size(), 3u);58
auto second = make_argv({"other"});59
sys::set_argv(static_cast<int>(second.size()), second.data());60
ASSERT_EQ(sys::argv.size(), 1u); // stale entries from the first call are gone61
EXPECT_EQ(sys::argv[0], "other");62
}64
TEST(CheatahSys, SetArgvZeroCountYieldsEmptyArgv) {65
auto raw = make_argv({"prog"});66
sys::set_argv(static_cast<int>(raw.size()), raw.data());67
ASSERT_FALSE(sys::argv.empty());69
sys::set_argv(0, raw.data());70
EXPECT_TRUE(sys::argv.empty());71
}73
TEST(CheatahSys, SetArgvNegativeCountYieldsEmptyArgv) {74
auto raw = make_argv({"prog", "arg"});75
sys::set_argv(static_cast<int>(raw.size()), raw.data());76
ASSERT_FALSE(sys::argv.empty());78
sys::set_argv(-1, raw.data()); // hostile count: clears, never reads argv_79
EXPECT_TRUE(sys::argv.empty());80
}82
TEST(CheatahSys, SetArgvNullVectorYieldsEmptyArgv) {83
auto raw = make_argv({"prog"});84
sys::set_argv(static_cast<int>(raw.size()), raw.data());85
ASSERT_FALSE(sys::argv.empty());87
sys::set_argv(2, nullptr); // claimed count with no storage: fail-safe to empty88
EXPECT_TRUE(sys::argv.empty());89
}91
TEST(CheatahSys, SetArgvNullEntryBecomesEmptyString) {92
auto raw = make_argv({"prog", nullptr, "after"});93
sys::set_argv(static_cast<int>(raw.size()), raw.data());95
ASSERT_EQ(sys::argv.size(), 3u); // the hole is kept, not dropped, so indexes hold96
EXPECT_EQ(sys::argv[0], "prog");97
EXPECT_EQ(sys::argv[1], "");98
EXPECT_EQ(sys::argv[2], "after");99
}101
TEST(CheatahSys, ExportedRuntimeHookForwardsToSetArgv) {102
auto raw = make_argv({"module.so", "user-arg"});103
cheatah_set_argv(static_cast<int>(raw.size()), raw.data());105
ASSERT_EQ(sys::argv.size(), 2u);106
EXPECT_EQ(sys::argv[0], "module.so");107
EXPECT_EQ(sys::argv[1], "user-arg");108
}