OILS / cpp / core_test.cc View on Github | oilshell.org

361 lines, 236 significant
1#include "cpp/core.h"
2
3#include <errno.h> // errno
4#include <fcntl.h> // O_RDWR
5#include <signal.h> // SIG*, kill()
6#include <sys/stat.h> // stat
7#include <sys/utsname.h> // uname
8#include <sys/wait.h> // waitpid
9#include <unistd.h> // getpid(), getuid(), environ
10
11#include "cpp/embedded_file.h"
12#include "cpp/stdlib.h" // posix::getcwd
13#include "mycpp/gc_builtins.h" // IOError_OSError
14#include "mycpp/gc_iolib.h" // iolib
15#include "vendor/greatest.h"
16
17TEST for_test_coverage() {
18 pyos::FlushStdout();
19
20 PASS();
21}
22
23GLOBAL_STR(v1, "v1");
24GLOBAL_STR(v2, "v2");
25
26TextFile gTmp[] = {
27 {.rel_path = "k1", .contents = v1},
28 {.rel_path = "k2", .contents = v2},
29 {.rel_path = nullptr, .contents = nullptr},
30};
31
32TextFile* gEmbeddedFiles = gTmp; // turn array into pointer
33
34TEST loader_test() {
35 auto loader = pyutil::GetResourceLoader();
36
37 BigStr* version = pyutil::GetVersion(loader);
38 ASSERT(len(version) > 3);
39
40 pyutil::PrintVersionDetails(loader);
41
42 ASSERT_EQ(v1, loader->Get(StrFromC("k1")));
43 ASSERT_EQ(v2, loader->Get(StrFromC("k2")));
44
45 bool caught = false;
46 try {
47 loader->Get(kEmptyString);
48 } catch (IOError*) {
49 caught = true;
50 }
51 ASSERT(caught);
52
53 PASS();
54}
55
56TEST exceptions_test() {
57 bool caught = false;
58 try {
59 throw Alloc<pyos::ReadError>(0);
60 } catch (pyos::ReadError* e) {
61 log("e %p", e);
62 caught = true;
63 }
64
65 ASSERT(caught);
66
67 PASS();
68}
69
70TEST environ_test() {
71 Dict<BigStr*, BigStr*>* env = pyos::Environ();
72 BigStr* p = env->get(StrFromC("PATH"));
73 ASSERT(p != nullptr);
74 log("PATH = %s", p->data_);
75
76 PASS();
77}
78
79TEST user_home_dir_test() {
80 uid_t uid = getuid();
81 BigStr* username = pyos::GetUserName(uid);
82 ASSERT(username != nullptr);
83
84 BigStr* dir0 = pyos::GetMyHomeDir();
85 ASSERT(dir0 != nullptr);
86
87 BigStr* dir1 = pyos::GetHomeDir(username);
88 ASSERT(dir1 != nullptr);
89
90 ASSERT(str_equals(dir0, dir1));
91
92 PASS();
93}
94
95TEST uname_test() {
96 BigStr* os_type = pyos::OsType();
97 ASSERT(os_type != nullptr);
98
99 utsname un = {};
100 ASSERT(uname(&un) == 0);
101 ASSERT(str_equals(StrFromC(un.sysname), os_type));
102
103 PASS();
104}
105
106TEST pyos_readbyte_test() {
107 // Write 2 bytes to this file
108 const char* tmp_name = "pyos_ReadByte";
109 int fd = ::open(tmp_name, O_CREAT | O_RDWR, 0644);
110 if (fd < 0) {
111 printf("1. ERROR %s\n", strerror(errno));
112 }
113 ASSERT(fd > 0);
114 write(fd, "SH", 2);
115 close(fd);
116
117 fd = ::open(tmp_name, O_CREAT | O_RDWR, 0644);
118 if (fd < 0) {
119 printf("2. ERROR %s\n", strerror(errno));
120 }
121
122 Tuple2<int, int> tup = pyos::ReadByte(fd);
123 ASSERT_EQ_FMT(0, tup.at1(), "%d"); // error code
124 ASSERT_EQ_FMT('S', tup.at0(), "%d");
125
126 tup = pyos::ReadByte(fd);
127 ASSERT_EQ_FMT(0, tup.at1(), "%d"); // error code
128 ASSERT_EQ_FMT('H', tup.at0(), "%d");
129
130 tup = pyos::ReadByte(fd);
131 ASSERT_EQ_FMT(0, tup.at1(), "%d"); // error code
132 ASSERT_EQ_FMT(pyos::EOF_SENTINEL, tup.at0(), "%d");
133
134 close(fd);
135
136 PASS();
137}
138
139TEST pyos_read_test() {
140 const char* tmp_name = "pyos_Read";
141 int fd = ::open(tmp_name, O_CREAT | O_RDWR, 0644);
142 if (fd < 0) {
143 printf("3. ERROR %s\n", strerror(errno));
144 }
145 ASSERT(fd > 0);
146 write(fd, "SH", 2);
147 close(fd);
148
149 // open needs an absolute path for some reason? _tmp/pyos doesn't work
150 fd = ::open(tmp_name, O_CREAT | O_RDWR, 0644);
151 if (fd < 0) {
152 printf("4. ERROR %s\n", strerror(errno));
153 }
154
155 List<BigStr*>* chunks = NewList<BigStr*>();
156 Tuple2<int, int> tup = pyos::Read(fd, 4096, chunks);
157 ASSERT_EQ_FMT(2, tup.at0(), "%d"); // error code
158 ASSERT_EQ_FMT(0, tup.at1(), "%d");
159 ASSERT_EQ_FMT(1, len(chunks), "%d");
160
161 tup = pyos::Read(fd, 4096, chunks);
162 ASSERT_EQ_FMT(0, tup.at0(), "%d"); // error code
163 ASSERT_EQ_FMT(0, tup.at1(), "%d");
164 ASSERT_EQ_FMT(1, len(chunks), "%d");
165
166 close(fd);
167
168 PASS();
169}
170
171TEST pyos_test() {
172 Tuple3<double, double, double> t = pyos::Time();
173 ASSERT(t.at0() > 0.0);
174 ASSERT(t.at1() >= 0.0);
175 ASSERT(t.at2() >= 0.0);
176
177 Tuple2<int, int> result = pyos::WaitPid(0);
178 ASSERT_EQ(-1, result.at0()); // no children to wait on
179
180 // This test isn't hermetic but it should work in most places, including in a
181 // container
182
183 BigStr* current = posix::getcwd();
184
185 int err_num = pyos::Chdir(StrFromC("/"));
186 ASSERT(err_num == 0);
187
188 err_num = pyos::Chdir(StrFromC("/nonexistent__"));
189 ASSERT(err_num != 0);
190
191 err_num = pyos::Chdir(current);
192 ASSERT(err_num == 0);
193
194 PASS();
195}
196
197TEST pyutil_test() {
198 ASSERT_EQ(true, pyutil::IsValidCharEscape(StrFromC("#")));
199 ASSERT_EQ(false, pyutil::IsValidCharEscape(StrFromC("a")));
200
201 // OK this seems to work
202 BigStr* escaped =
203 pyutil::BackslashEscape(StrFromC("'foo bar'"), StrFromC(" '"));
204 ASSERT(str_equals(escaped, StrFromC("\\'foo\\ bar\\'")));
205
206 BigStr* escaped2 = pyutil::BackslashEscape(StrFromC(""), StrFromC(" '"));
207 ASSERT(str_equals(escaped2, StrFromC("")));
208
209 BigStr* s = pyutil::ChArrayToString(NewList<int>({65}));
210 ASSERT(str_equals(s, StrFromC("A")));
211 ASSERT_EQ_FMT(1, len(s), "%d");
212
213 BigStr* s2 = pyutil::ChArrayToString(NewList<int>({102, 111, 111}));
214 ASSERT(str_equals(s2, StrFromC("foo")));
215 ASSERT_EQ_FMT(3, len(s2), "%d");
216
217 BigStr* s3 = pyutil::ChArrayToString(NewList<int>({45, 206, 188, 45}));
218 ASSERT(str_equals(s3, StrFromC("-\xce\xbc-"))); // mu char
219 ASSERT_EQ_FMT(4, len(s3), "%d");
220
221 pyos::PrintTimes();
222
223 PASS();
224}
225
226TEST strerror_test() {
227 IOError_OSError err(EINVAL);
228 BigStr* s1 = pyutil::strerror(&err);
229 ASSERT(s1 != nullptr);
230
231 BigStr* s2 = StrFromC(strerror(EINVAL));
232 ASSERT(s2 != nullptr);
233
234 ASSERT(str_equals(s1, s2));
235
236 PASS();
237}
238
239TEST passwd_test() {
240 uid_t my_uid = getuid();
241 BigStr* username = pyos::GetUserName(my_uid);
242 ASSERT(username != nullptr);
243
244 List<pyos::PasswdEntry*>* entries = pyos::GetAllUsers();
245 if (len(entries) == 0) {
246 fprintf(stderr, "No *pwent() functions, skipping tests\n");
247 PASS();
248 }
249
250 pyos::PasswdEntry* me = nullptr;
251 for (ListIter<pyos::PasswdEntry*> it(entries); !it.Done(); it.Next()) {
252 pyos::PasswdEntry* entry = it.Value();
253 if (entry->pw_uid == static_cast<int>(my_uid)) {
254 me = entry;
255 break;
256 }
257 }
258 ASSERT(me != nullptr);
259 ASSERT(me->pw_name != nullptr);
260 ASSERT(str_equals(username, me->pw_name));
261
262 PASS();
263}
264
265TEST dir_cache_key_test() {
266 struct stat st;
267 ASSERT(::stat("/", &st) == 0);
268
269 Tuple2<BigStr*, int>* key = pyos::MakeDirCacheKey(StrFromC("/"));
270 ASSERT(str_equals(key->at0(), StrFromC("/")));
271 ASSERT(key->at1() == st.st_mtime);
272
273 int ec = -1;
274 try {
275 pyos::MakeDirCacheKey(StrFromC("nonexistent_ZZ"));
276 } catch (IOError_OSError* e) {
277 ec = e->errno_;
278 }
279 ASSERT(ec == ENOENT);
280
281 PASS();
282}
283
284// Test the theory that LeakSanitizer tests for reachability from global
285// variables.
286struct Node {
287 Node* next;
288};
289Node* gNode;
290
291TEST asan_global_leak_test() {
292 // NOT reported as a leak
293 gNode = static_cast<Node*>(malloc(sizeof(Node)));
294 gNode->next = static_cast<Node*>(malloc(sizeof(Node)));
295
296 // Turn this on and ASAN will report a leak!
297 if (0) {
298 free(gNode);
299 }
300 PASS();
301}
302
303// manual demo
304TEST waitpid_demo() {
305 iolib::InitSignalSafe();
306 iolib::RegisterSignalInterest(SIGINT);
307
308 int result = fork();
309 if (result < 0) {
310 FAIL();
311 } else if (result == 0) {
312 // child
313
314 log("sleeping in child, pid = %d", getpid());
315 char* argv[] = {"sleep", "5", nullptr};
316 char* env[] = {nullptr};
317 int e = execvpe("sleep", argv, env);
318 log("execve failed %d", e);
319
320 } else {
321 // parent
322
323 int wstatus;
324 log("waiting in parent");
325 int result = ::waitpid(-1, &wstatus, 0);
326 log("waitpid = %d, status = %d", result, wstatus);
327 }
328
329 PASS();
330}
331
332GREATEST_MAIN_DEFS();
333
334int main(int argc, char** argv) {
335 gHeap.Init();
336
337 GREATEST_MAIN_BEGIN();
338
339 RUN_TEST(for_test_coverage);
340 RUN_TEST(loader_test);
341 RUN_TEST(exceptions_test);
342 RUN_TEST(environ_test);
343 RUN_TEST(user_home_dir_test);
344 RUN_TEST(uname_test);
345 RUN_TEST(pyos_readbyte_test);
346 RUN_TEST(pyos_read_test);
347 RUN_TEST(pyos_test); // non-hermetic
348 RUN_TEST(pyutil_test);
349 RUN_TEST(strerror_test);
350
351 RUN_TEST(passwd_test);
352 RUN_TEST(dir_cache_key_test);
353 RUN_TEST(asan_global_leak_test);
354
355 // RUN_TEST(waitpid_demo);
356
357 gHeap.CleanProcessExit();
358
359 GREATEST_MAIN_END(); /* display results */
360 return 0;
361}