cheatah
Source

tests/purrc/os_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// System-level test for the cheatah `os` standard-library module.
4//
5// Unlike os_cr_test.cpp (one tiny program per function), this is a SINGLE
6// cohesive filesystem workflow that exercises EVERY public function in
7// stdlib/os/os.hpp in one program: it builds a nested directory tree, creates
8// and renames and removes files (writing content via os.system), queries them
9// (exists/isfile/isdir/getsize/listdir), changes directory, applies the
10// purely-lexical os.path helpers, round-trips an environment variable, and
11// reports the process/system facts. Everything happens under a unique /tmp
12// workspace that is torn down at the end.
13//
14// Output is DETERMINISTIC: filesystem state is fully controlled, and the
15// non-deterministic facts (getpid, getcwd, cpu_count, the exit status of
16// `false`) are asserted as PROPERTIES (pid > 0, cpu_count >= 1, cwd basename,
17// false != 0) rather than printed raw.
18//
19// Coverage (all 24 public functions):
20// os.* getcwd chdir listdir mkdir makedirs rmdir remove rename
21// getenv setenv cpu_count system getpid urandom
22// os.path.* join exists isfile isdir basename dirname abspath getsize
23// splitext normpath
24//
25// splitext returns a std::pair (not io.print-Streamable); it is read via the
26// pair's .first / .second members, matching os_cr_test.cpp.
27#include "e2e_harness.hpp"
29TEST(StdlibE2E, Os) {
30 e2e::expect_e2e("os_sys", R"PURR(import io
31import os
33# --- Setup: a unique workspace under /tmp -----------------------------------
34let root = "/tmp/cheatah_os_sys_ws"
35os.system("rm -rf /tmp/cheatah_os_sys_ws")
36os.mkdir(root)
38# Remember where we started so we can restore it.
39let start = os.getcwd()
41# --- makedirs: create a nested tree -----------------------------------------
42let nested = os.path.join(root, "a", "b", "c")
43os.makedirs(nested)
44io.print("isdir nested:", os.path.isdir(nested))
46# --- mkdir: create a sibling leaf directory ---------------------------------
47let data = os.path.join(root, "data")
48os.mkdir(data)
49io.print("isdir data:", os.path.isdir(data))
51# --- create files via os.system, then query them ----------------------------
52let f1 = os.path.join(data, "hello.txt")
53os.system("printf 'hello' > " + f1)
54io.print("exists f1:", os.path.exists(f1))
55io.print("isfile f1:", os.path.isfile(f1))
56io.print("isdir f1:", os.path.isdir(f1))
57io.print("getsize f1:", os.path.getsize(f1))
59# --- rename a file ----------------------------------------------------------
60let f2 = os.path.join(data, "renamed.txt")
61os.rename(f1, f2)
62io.print("isfile f2:", os.path.isfile(f2))
63io.print("isfile f1 gone:", os.path.isfile(f1))
65# --- listdir -----------------------------------------------------------------
66let entries = os.listdir(data)
67io.print("listdir len:", len(entries))
68io.print("listdir[0]:", entries[0])
70# --- chdir + getcwd: cd into data, confirm basename -------------------------
71os.chdir(data)
72io.print("cwd basename:", os.path.basename(os.getcwd()))
73os.chdir(start)
75# --- lexical path helpers ----------------------------------------------------
76io.print("join:", os.path.join("x", "y", "z.txt"))
77io.print("basename:", os.path.basename("/p/q/r.dat"))
78io.print("dirname:", os.path.dirname("/p/q/r.dat"))
79io.print("normpath:", os.path.normpath("/p/./q/../r"))
80io.print("abspath:", os.path.abspath("/already/abs"))
81let se = os.path.splitext("archive.tar.gz")
82io.print("splitext:", se.first, se.second)
84# --- environment round-trip --------------------------------------------------
85os.setenv("CHEATAH_OS_SYS_VAR", "purr")
86io.print("getenv set:", os.getenv("CHEATAH_OS_SYS_VAR"))
87io.print("getenv fallback:", os.getenv("CHEATAH_OS_SYS_MISSING_zzz", "none"))
89# --- process / system facts (asserted as properties) ------------------------
90io.print("pid positive:", os.getpid() > 0)
91io.print("cpu>=1:", os.cpu_count() >= 1)
92io.print("system true:", os.system("true"))
93io.print("system false nonzero:", os.system("false") != 0)
94io.print("urandom len:", len(os.urandom(32)))
95io.print("urandom varies:", os.urandom(16) != os.urandom(16))
97# --- teardown: remove files and dirs ----------------------------------------
98io.print("remove f2:", os.remove(f2))
99os.rmdir(data)
100os.rmdir(nested)
101os.rmdir(os.path.join(root, "a", "b"))
102os.rmdir(os.path.join(root, "a"))
103os.rmdir(root)
104io.print("root gone:", os.path.exists(root))
105)PURR",
106 "isdir nested: True\n"
107 "isdir data: True\n"
108 "exists f1: True\n"
109 "isfile f1: True\n"
110 "isdir f1: False\n"
111 "getsize f1: 5\n"
112 "isfile f2: True\n"
113 "isfile f1 gone: False\n"
114 "listdir len: 1\n"
115 "listdir[0]: renamed.txt\n"
116 "cwd basename: data\n"
117 "join: x/y/z.txt\n"
118 "basename: r.dat\n"
119 "dirname: /p/q\n"
120 "normpath: /p/r\n"
121 "abspath: /already/abs\n"
122 "splitext: archive.tar .gz\n"
123 "getenv set: purr\n"
124 "getenv fallback: none\n"
125 "pid positive: True\n"
126 "cpu>=1: True\n"
127 "system true: 0\n"
128 "system false nonzero: True\n"
129 "urandom len: 32\n"
130 "urandom varies: True\n"
131 "remove f2: True\n"
132 "root gone: False\n");