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 "vendor/greatest.h"
|
15 |
|
16 | TEST for_test_coverage() {
|
17 | pyos::FlushStdout();
|
18 |
|
19 | PASS();
|
20 | }
|
21 |
|
22 | GLOBAL_STR(v1, "v1");
|
23 | GLOBAL_STR(v2, "v2");
|
24 |
|
25 | TextFile gTmp[] = {
|
26 | {.rel_path = "k1", .contents = v1},
|
27 | {.rel_path = "k2", .contents = v2},
|
28 | {.rel_path = nullptr, .contents = nullptr},
|
29 | };
|
30 |
|
31 | TextFile* gEmbeddedFiles = gTmp; // turn array into pointer
|
32 |
|
33 | TEST loader_test() {
|
34 | auto loader = pyutil::GetResourceLoader();
|
35 |
|
36 | BigStr* version = pyutil::GetVersion(loader);
|
37 | ASSERT(len(version) > 3);
|
38 |
|
39 | pyutil::PrintVersionDetails(loader);
|
40 |
|
41 | ASSERT_EQ(v1, loader->Get(StrFromC("k1")));
|
42 | ASSERT_EQ(v2, loader->Get(StrFromC("k2")));
|
43 |
|
44 | bool caught = false;
|
45 | try {
|
46 | loader->Get(kEmptyString);
|
47 | } catch (IOError*) {
|
48 | caught = true;
|
49 | }
|
50 | ASSERT(caught);
|
51 |
|
52 | PASS();
|
53 | }
|
54 |
|
55 | TEST exceptions_test() {
|
56 | bool caught = false;
|
57 | try {
|
58 | throw Alloc<pyos::ReadError>(0);
|
59 | } catch (pyos::ReadError* e) {
|
60 | log("e %p", e);
|
61 | caught = true;
|
62 | }
|
63 |
|
64 | ASSERT(caught);
|
65 |
|
66 | PASS();
|
67 | }
|
68 |
|
69 | TEST environ_test() {
|
70 | Dict<BigStr*, BigStr*>* env = pyos::Environ();
|
71 | BigStr* p = env->get(StrFromC("PATH"));
|
72 | ASSERT(p != nullptr);
|
73 | log("PATH = %s", p->data_);
|
74 |
|
75 | PASS();
|
76 | }
|
77 |
|
78 | TEST user_home_dir_test() {
|
79 | uid_t uid = getuid();
|
80 | BigStr* username = pyos::GetUserName(uid);
|
81 | ASSERT(username != nullptr);
|
82 |
|
83 | BigStr* dir0 = pyos::GetMyHomeDir();
|
84 | ASSERT(dir0 != nullptr);
|
85 |
|
86 | BigStr* dir1 = pyos::GetHomeDir(username);
|
87 | ASSERT(dir1 != nullptr);
|
88 |
|
89 | ASSERT(str_equals(dir0, dir1));
|
90 |
|
91 | PASS();
|
92 | }
|
93 |
|
94 | TEST uname_test() {
|
95 | BigStr* os_type = pyos::OsType();
|
96 | ASSERT(os_type != nullptr);
|
97 |
|
98 | utsname un = {};
|
99 | ASSERT(uname(&un) == 0);
|
100 | ASSERT(str_equals(StrFromC(un.sysname), os_type));
|
101 |
|
102 | PASS();
|
103 | }
|
104 |
|
105 | TEST pyos_readbyte_test() {
|
106 | // Write 2 bytes to this file
|
107 | const char* tmp_name = "pyos_ReadByte";
|
108 | int fd = ::open(tmp_name, O_CREAT | O_RDWR, 0644);
|
109 | if (fd < 0) {
|
110 | printf("1. ERROR %s\n", strerror(errno));
|
111 | }
|
112 | ASSERT(fd > 0);
|
113 | write(fd, "SH", 2);
|
114 | close(fd);
|
115 |
|
116 | fd = ::open(tmp_name, O_CREAT | O_RDWR, 0644);
|
117 | if (fd < 0) {
|
118 | printf("2. ERROR %s\n", strerror(errno));
|
119 | }
|
120 |
|
121 | Tuple2<int, int> tup = pyos::ReadByte(fd);
|
122 | ASSERT_EQ_FMT(0, tup.at1(), "%d"); // error code
|
123 | ASSERT_EQ_FMT('S', tup.at0(), "%d");
|
124 |
|
125 | tup = pyos::ReadByte(fd);
|
126 | ASSERT_EQ_FMT(0, tup.at1(), "%d"); // error code
|
127 | ASSERT_EQ_FMT('H', tup.at0(), "%d");
|
128 |
|
129 | tup = pyos::ReadByte(fd);
|
130 | ASSERT_EQ_FMT(0, tup.at1(), "%d"); // error code
|
131 | ASSERT_EQ_FMT(pyos::EOF_SENTINEL, tup.at0(), "%d");
|
132 |
|
133 | close(fd);
|
134 |
|
135 | PASS();
|
136 | }
|
137 |
|
138 | TEST pyos_read_test() {
|
139 | const char* tmp_name = "pyos_Read";
|
140 | int fd = ::open(tmp_name, O_CREAT | O_RDWR, 0644);
|
141 | if (fd < 0) {
|
142 | printf("3. ERROR %s\n", strerror(errno));
|
143 | }
|
144 | ASSERT(fd > 0);
|
145 | write(fd, "SH", 2);
|
146 | close(fd);
|
147 |
|
148 | // open needs an absolute path for some reason? _tmp/pyos doesn't work
|
149 | fd = ::open(tmp_name, O_CREAT | O_RDWR, 0644);
|
150 | if (fd < 0) {
|
151 | printf("4. ERROR %s\n", strerror(errno));
|
152 | }
|
153 |
|
154 | List<BigStr*>* chunks = NewList<BigStr*>();
|
155 | Tuple2<int, int> tup = pyos::Read(fd, 4096, chunks);
|
156 | ASSERT_EQ_FMT(2, tup.at0(), "%d"); // error code
|
157 | ASSERT_EQ_FMT(0, tup.at1(), "%d");
|
158 | ASSERT_EQ_FMT(1, len(chunks), "%d");
|
159 |
|
160 | tup = pyos::Read(fd, 4096, chunks);
|
161 | ASSERT_EQ_FMT(0, tup.at0(), "%d"); // error code
|
162 | ASSERT_EQ_FMT(0, tup.at1(), "%d");
|
163 | ASSERT_EQ_FMT(1, len(chunks), "%d");
|
164 |
|
165 | close(fd);
|
166 |
|
167 | PASS();
|
168 | }
|
169 |
|
170 | TEST pyos_test() {
|
171 | Tuple3<double, double, double> t = pyos::Time();
|
172 | ASSERT(t.at0() > 0.0);
|
173 | ASSERT(t.at1() >= 0.0);
|
174 | ASSERT(t.at2() >= 0.0);
|
175 |
|
176 | Tuple2<int, int> result = pyos::WaitPid(0);
|
177 | ASSERT_EQ(-1, result.at0()); // no children to wait on
|
178 |
|
179 | // This test isn't hermetic but it should work in most places, including in a
|
180 | // container
|
181 |
|
182 | BigStr* current = posix::getcwd();
|
183 |
|
184 | int err_num = pyos::Chdir(StrFromC("/"));
|
185 | ASSERT(err_num == 0);
|
186 |
|
187 | err_num = pyos::Chdir(StrFromC("/nonexistent__"));
|
188 | ASSERT(err_num != 0);
|
189 |
|
190 | err_num = pyos::Chdir(current);
|
191 | ASSERT(err_num == 0);
|
192 |
|
193 | PASS();
|
194 | }
|
195 |
|
196 | TEST pyutil_test() {
|
197 | ASSERT_EQ(true, pyutil::IsValidCharEscape(StrFromC("#")));
|
198 | ASSERT_EQ(false, pyutil::IsValidCharEscape(StrFromC("a")));
|
199 |
|
200 | // OK this seems to work
|
201 | BigStr* escaped =
|
202 | pyutil::BackslashEscape(StrFromC("'foo bar'"), StrFromC(" '"));
|
203 | ASSERT(str_equals(escaped, StrFromC("\\'foo\\ bar\\'")));
|
204 |
|
205 | BigStr* escaped2 = pyutil::BackslashEscape(StrFromC(""), StrFromC(" '"));
|
206 | ASSERT(str_equals(escaped2, StrFromC("")));
|
207 |
|
208 | BigStr* s = pyutil::ChArrayToString(NewList<int>({65}));
|
209 | ASSERT(str_equals(s, StrFromC("A")));
|
210 | ASSERT_EQ_FMT(1, len(s), "%d");
|
211 |
|
212 | BigStr* s2 = pyutil::ChArrayToString(NewList<int>({102, 111, 111}));
|
213 | ASSERT(str_equals(s2, StrFromC("foo")));
|
214 | ASSERT_EQ_FMT(3, len(s2), "%d");
|
215 |
|
216 | BigStr* s3 = pyutil::ChArrayToString(NewList<int>({45, 206, 188, 45}));
|
217 | ASSERT(str_equals(s3, StrFromC("-\xce\xbc-"))); // mu char
|
218 | ASSERT_EQ_FMT(4, len(s3), "%d");
|
219 |
|
220 | pyos::PrintTimes();
|
221 |
|
222 | PASS();
|
223 | }
|
224 |
|
225 | TEST strerror_test() {
|
226 | IOError_OSError err(EINVAL);
|
227 | BigStr* s1 = pyutil::strerror(&err);
|
228 | ASSERT(s1 != nullptr);
|
229 |
|
230 | BigStr* s2 = StrFromC(strerror(EINVAL));
|
231 | ASSERT(s2 != nullptr);
|
232 |
|
233 | ASSERT(str_equals(s1, s2));
|
234 |
|
235 | PASS();
|
236 | }
|
237 |
|
238 | TEST signal_test() {
|
239 | pyos::SignalSafe* signal_safe = pyos::InitSignalSafe();
|
240 |
|
241 | {
|
242 | List<int>* q = signal_safe->TakePendingSignals();
|
243 | ASSERT(q != nullptr);
|
244 | ASSERT_EQ(0, len(q));
|
245 | signal_safe->ReuseEmptyList(q);
|
246 | }
|
247 |
|
248 | pid_t mypid = getpid();
|
249 |
|
250 | pyos::RegisterSignalInterest(SIGUSR1);
|
251 | pyos::RegisterSignalInterest(SIGUSR2);
|
252 |
|
253 | kill(mypid, SIGUSR1);
|
254 | ASSERT_EQ(SIGUSR1, signal_safe->LastSignal());
|
255 |
|
256 | kill(mypid, SIGUSR2);
|
257 | ASSERT_EQ(SIGUSR2, signal_safe->LastSignal());
|
258 |
|
259 | {
|
260 | List<int>* q = signal_safe->TakePendingSignals();
|
261 | ASSERT(q != nullptr);
|
262 | ASSERT_EQ(2, len(q));
|
263 | ASSERT_EQ(SIGUSR1, q->at(0));
|
264 | ASSERT_EQ(SIGUSR2, q->at(1));
|
265 |
|
266 | q->clear();
|
267 | signal_safe->ReuseEmptyList(q);
|
268 | }
|
269 |
|
270 | pyos::sigaction(SIGUSR1, SIG_IGN);
|
271 | kill(mypid, SIGUSR1);
|
272 | {
|
273 | List<int>* q = signal_safe->TakePendingSignals();
|
274 | ASSERT(q != nullptr);
|
275 | ASSERT(len(q) == 0);
|
276 | signal_safe->ReuseEmptyList(q);
|
277 | }
|
278 | pyos::sigaction(SIGUSR2, SIG_IGN);
|
279 |
|
280 | pyos::RegisterSignalInterest(SIGWINCH);
|
281 |
|
282 | kill(mypid, SIGWINCH);
|
283 | ASSERT_EQ(pyos::UNTRAPPED_SIGWINCH, signal_safe->LastSignal());
|
284 |
|
285 | signal_safe->SetSigWinchCode(SIGWINCH);
|
286 |
|
287 | kill(mypid, SIGWINCH);
|
288 | ASSERT_EQ(SIGWINCH, signal_safe->LastSignal());
|
289 | {
|
290 | List<int>* q = signal_safe->TakePendingSignals();
|
291 | ASSERT(q != nullptr);
|
292 | ASSERT_EQ(2, len(q));
|
293 | ASSERT_EQ(SIGWINCH, q->at(0));
|
294 | ASSERT_EQ(SIGWINCH, q->at(1));
|
295 | }
|
296 |
|
297 | PASS();
|
298 | }
|
299 |
|
300 | TEST signal_safe_test() {
|
301 | pyos::SignalSafe signal_safe;
|
302 |
|
303 | List<int>* received = signal_safe.TakePendingSignals();
|
304 |
|
305 | // We got now signals
|
306 | ASSERT_EQ_FMT(0, len(received), "%d");
|
307 |
|
308 | // The existing queue is of length 0
|
309 | ASSERT_EQ_FMT(0, len(signal_safe.pending_signals_), "%d");
|
310 |
|
311 | // Capacity is a ROUND NUMBER from the allocator's POV
|
312 | // There's no convenient way to test the obj_len we pass to gHeap.Allocate,
|
313 | // but it should be (1022 + 2) * 4.
|
314 | ASSERT_EQ_FMT(1022, signal_safe.pending_signals_->capacity_, "%d");
|
315 |
|
316 | // Register too many signals
|
317 | for (int i = 0; i < pyos::kMaxPendingSignals + 10; ++i) {
|
318 | signal_safe.UpdateFromSignalHandler(SIGINT);
|
319 | }
|
320 |
|
321 | PASS();
|
322 | }
|
323 |
|
324 | TEST passwd_test() {
|
325 | uid_t my_uid = getuid();
|
326 | BigStr* username = pyos::GetUserName(my_uid);
|
327 | ASSERT(username != nullptr);
|
328 |
|
329 | List<pyos::PasswdEntry*>* entries = pyos::GetAllUsers();
|
330 | if (len(entries) == 0) {
|
331 | fprintf(stderr, "No *pwent() functions, skipping tests\n");
|
332 | PASS();
|
333 | }
|
334 |
|
335 | pyos::PasswdEntry* me = nullptr;
|
336 | for (ListIter<pyos::PasswdEntry*> it(entries); !it.Done(); it.Next()) {
|
337 | pyos::PasswdEntry* entry = it.Value();
|
338 | if (entry->pw_uid == static_cast<int>(my_uid)) {
|
339 | me = entry;
|
340 | break;
|
341 | }
|
342 | }
|
343 | ASSERT(me != nullptr);
|
344 | ASSERT(me->pw_name != nullptr);
|
345 | ASSERT(str_equals(username, me->pw_name));
|
346 |
|
347 | PASS();
|
348 | }
|
349 |
|
350 | TEST dir_cache_key_test() {
|
351 | struct stat st;
|
352 | ASSERT(::stat("/", &st) == 0);
|
353 |
|
354 | Tuple2<BigStr*, int>* key = pyos::MakeDirCacheKey(StrFromC("/"));
|
355 | ASSERT(str_equals(key->at0(), StrFromC("/")));
|
356 | ASSERT(key->at1() == st.st_mtime);
|
357 |
|
358 | int ec = -1;
|
359 | try {
|
360 | pyos::MakeDirCacheKey(StrFromC("nonexistent_ZZ"));
|
361 | } catch (IOError_OSError* e) {
|
362 | ec = e->errno_;
|
363 | }
|
364 | ASSERT(ec == ENOENT);
|
365 |
|
366 | PASS();
|
367 | }
|
368 |
|
369 | // Test the theory that LeakSanitizer tests for reachability from global
|
370 | // variables.
|
371 | struct Node {
|
372 | Node* next;
|
373 | };
|
374 | Node* gNode;
|
375 |
|
376 | TEST asan_global_leak_test() {
|
377 | // NOT reported as a leak
|
378 | gNode = static_cast<Node*>(malloc(sizeof(Node)));
|
379 | gNode->next = static_cast<Node*>(malloc(sizeof(Node)));
|
380 |
|
381 | // Turn this on and ASAN will report a leak!
|
382 | if (0) {
|
383 | free(gNode);
|
384 | }
|
385 | PASS();
|
386 | }
|
387 |
|
388 | // manual demo
|
389 | TEST waitpid_demo() {
|
390 | pyos::InitSignalSafe();
|
391 | pyos::RegisterSignalInterest(SIGINT);
|
392 |
|
393 | int result = fork();
|
394 | if (result < 0) {
|
395 | FAIL();
|
396 | } else if (result == 0) {
|
397 | // child
|
398 |
|
399 | log("sleeping in child, pid = %d", getpid());
|
400 | char* argv[] = {"sleep", "5", nullptr};
|
401 | char* env[] = {nullptr};
|
402 | int e = execvpe("sleep", argv, env);
|
403 | log("execve failed %d", e);
|
404 |
|
405 | } else {
|
406 | // parent
|
407 |
|
408 | int wstatus;
|
409 | log("waiting in parent");
|
410 | int result = ::waitpid(-1, &wstatus, 0);
|
411 | log("waitpid = %d, status = %d", result, wstatus);
|
412 | }
|
413 |
|
414 | PASS();
|
415 | }
|
416 |
|
417 | GREATEST_MAIN_DEFS();
|
418 |
|
419 | int main(int argc, char** argv) {
|
420 | gHeap.Init();
|
421 |
|
422 | GREATEST_MAIN_BEGIN();
|
423 |
|
424 | RUN_TEST(for_test_coverage);
|
425 | RUN_TEST(loader_test);
|
426 | RUN_TEST(exceptions_test);
|
427 | RUN_TEST(environ_test);
|
428 | RUN_TEST(user_home_dir_test);
|
429 | RUN_TEST(uname_test);
|
430 | RUN_TEST(pyos_readbyte_test);
|
431 | RUN_TEST(pyos_read_test);
|
432 | RUN_TEST(pyos_test); // non-hermetic
|
433 | RUN_TEST(pyutil_test);
|
434 | RUN_TEST(strerror_test);
|
435 |
|
436 | RUN_TEST(signal_test);
|
437 | RUN_TEST(signal_safe_test);
|
438 |
|
439 | RUN_TEST(passwd_test);
|
440 | RUN_TEST(dir_cache_key_test);
|
441 | RUN_TEST(asan_global_leak_test);
|
442 |
|
443 | // RUN_TEST(waitpid_demo);
|
444 |
|
445 | gHeap.CleanProcessExit();
|
446 |
|
447 | GREATEST_MAIN_END(); /* display results */
|
448 | return 0;
|
449 | }
|