cheatah
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>
11namespace sys = cheatah::sys;
13// The stable extern "C" hook the cheatah runtime resolves with dlsym and calls to
14// forward the program's arguments (defined in sys.cpp; not declared in sys.hpp
15// because it is never cheatah-visible).
16extern "C" void cheatah_set_argv(int argc, char** argv);
18namespace {
20// Build a mutable argv-style vector from string literals (C main() hands the
21// program non-const char*, so the test feeds set_argv the same shape).
22std::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;
29} // namespace
31TEST(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 preserved
41TEST(CheatahSys, ArgvCopiesRatherThanAliases) {
42 // set_argv must COPY the C strings: the runtime's argv storage is not owned by
43 // 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 buffer
49 ASSERT_EQ(sys::argv.size(), 1u);
50 EXPECT_EQ(sys::argv[0], "first");
53TEST(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 gone
61 EXPECT_EQ(sys::argv[0], "other");
64TEST(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());
73TEST(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());
82TEST(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 empty
88 EXPECT_TRUE(sys::argv.empty());
91TEST(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 hold
96 EXPECT_EQ(sys::argv[0], "prog");
97 EXPECT_EQ(sys::argv[1], "");
98 EXPECT_EQ(sys::argv[2], "after");
101TEST(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");