this repo has no description
1// Copyright (c) Facebook, Inc. and its affiliates. (http://www.facebook.com)
2#include <fcntl.h>
3#include <sys/stat.h>
4#include <unistd.h>
5
6#include "gtest/gtest.h"
7
8#include "builtins.h"
9#include "file.h"
10#include "test-utils.h"
11
12namespace py {
13namespace testing {
14using UnderOsModuleTest = RuntimeFixture;
15
16TEST_F(UnderOsModuleTest, AccessWithFileAndFokReturnsExpectedValues) {
17 TemporaryDirectory tempdir;
18 std::string valid_file_path = tempdir.path + "foo.py";
19 writeFile(valid_file_path, "");
20 std::string invalid_file_path = tempdir.path + "doesnotexist";
21
22 HandleScope scope(thread_);
23 Object valid_path(&scope, runtime_->newStrFromCStr(valid_file_path.c_str()));
24 Object invalid_path(&scope,
25 runtime_->newStrFromCStr(invalid_file_path.c_str()));
26 Object f_ok_mode(&scope, SmallInt::fromWord(F_OK));
27
28 Object valid_result(&scope,
29 runBuiltin(FUNC(_os, access), valid_path, f_ok_mode));
30 ASSERT_TRUE(valid_result.isBool());
31 EXPECT_TRUE(Bool::cast(*valid_result).value());
32
33 Object invalid_result(&scope,
34 runBuiltin(FUNC(_os, access), invalid_path, f_ok_mode));
35 ASSERT_TRUE(invalid_result.isBool());
36 EXPECT_FALSE(Bool::cast(*invalid_result).value());
37}
38
39TEST_F(UnderOsModuleTest, AccessWithFileAndRokReturnsExpectedValues) {
40 TemporaryDirectory tempdir;
41 std::string readable_path = tempdir.path + "foo.py";
42 writeFile(readable_path, "");
43 ::chmod(readable_path.c_str(), S_IREAD);
44 std::string non_readable_path = tempdir.path + "bar.py";
45 writeFile(non_readable_path, "");
46 ::chmod(non_readable_path.c_str(), ~S_IREAD);
47
48 HandleScope scope(thread_);
49 Object readable(&scope, runtime_->newStrFromCStr(readable_path.c_str()));
50 Object non_readable(&scope,
51 runtime_->newStrFromCStr(non_readable_path.c_str()));
52 Object r_ok_mode(&scope, SmallInt::fromWord(R_OK));
53
54 Object valid_result(&scope,
55 runBuiltin(FUNC(_os, access), readable, r_ok_mode));
56 ASSERT_TRUE(valid_result.isBool());
57 EXPECT_TRUE(Bool::cast(*valid_result).value());
58
59 Object invalid_result(&scope,
60 runBuiltin(FUNC(_os, access), non_readable, r_ok_mode));
61 ASSERT_TRUE(invalid_result.isBool());
62 EXPECT_FALSE(Bool::cast(*invalid_result).value());
63}
64
65TEST_F(UnderOsModuleTest, AccessWithFileAndWokReturnsExpectedValues) {
66 TemporaryDirectory tempdir;
67 std::string writable_path = tempdir.path + "foo.py";
68 writeFile(writable_path, "");
69 ::chmod(writable_path.c_str(), S_IWRITE);
70 std::string non_writable_path = tempdir.path + "bar.py";
71 writeFile(non_writable_path, "");
72 ::chmod(non_writable_path.c_str(), ~S_IWRITE);
73
74 HandleScope scope(thread_);
75 Object writable(&scope, runtime_->newStrFromCStr(writable_path.c_str()));
76 Object non_writable(&scope,
77 runtime_->newStrFromCStr(non_writable_path.c_str()));
78 Object w_ok_mode(&scope, SmallInt::fromWord(W_OK));
79
80 Object valid_result(&scope,
81 runBuiltin(FUNC(_os, access), writable, w_ok_mode));
82 ASSERT_TRUE(valid_result.isBool());
83 EXPECT_TRUE(Bool::cast(*valid_result).value());
84
85 Object invalid_result(&scope,
86 runBuiltin(FUNC(_os, access), non_writable, w_ok_mode));
87 ASSERT_TRUE(invalid_result.isBool());
88 EXPECT_FALSE(Bool::cast(*invalid_result).value());
89}
90
91TEST_F(UnderOsModuleTest, AccessWithFileAndXokReturnsExpectedValues) {
92 TemporaryDirectory tempdir;
93 std::string executable_path = tempdir.path + "foo.py";
94 writeFile(executable_path, "");
95 ::chmod(executable_path.c_str(), S_IEXEC);
96 std::string non_executable_path = tempdir.path + "bar.py";
97 writeFile(non_executable_path, "");
98 ::chmod(non_executable_path.c_str(), ~S_IEXEC);
99
100 HandleScope scope(thread_);
101 Object executable(&scope, runtime_->newStrFromCStr(executable_path.c_str()));
102 Object non_executable(&scope,
103 runtime_->newStrFromCStr(non_executable_path.c_str()));
104 Object r_ok_mode(&scope, SmallInt::fromWord(X_OK));
105
106 Object valid_result(&scope,
107 runBuiltin(FUNC(_os, access), executable, r_ok_mode));
108 ASSERT_TRUE(valid_result.isBool());
109 EXPECT_TRUE(Bool::cast(*valid_result).value());
110
111 Object invalid_result(
112 &scope, runBuiltin(FUNC(_os, access), non_executable, r_ok_mode));
113 ASSERT_TRUE(invalid_result.isBool());
114 EXPECT_FALSE(Bool::cast(*invalid_result).value());
115}
116
117TEST_F(UnderOsModuleTest, AccessWithFileAndMultipleFlagsReturnsExpectedValue) {
118 TemporaryDirectory tempdir;
119 std::string readable_executable_path = tempdir.path + "foo.py";
120 writeFile(readable_executable_path, "");
121 ::chmod(readable_executable_path.c_str(), S_IREAD | S_IEXEC);
122
123 HandleScope scope(thread_);
124 Object readable_executable(
125 &scope, runtime_->newStrFromCStr(readable_executable_path.c_str()));
126 Object r_x_ok_mode(&scope, SmallInt::fromWord(R_OK | X_OK));
127 Object r_w_ok_mode(&scope, SmallInt::fromWord(R_OK | W_OK));
128
129 Object valid_result(
130 &scope, runBuiltin(FUNC(_os, access), readable_executable, r_x_ok_mode));
131 ASSERT_TRUE(valid_result.isBool());
132 EXPECT_TRUE(Bool::cast(*valid_result).value());
133
134 Object invalid_result(
135 &scope, runBuiltin(FUNC(_os, access), readable_executable, r_w_ok_mode));
136 ASSERT_TRUE(invalid_result.isBool());
137 EXPECT_FALSE(Bool::cast(*invalid_result).value());
138}
139
140TEST_F(UnderOsModuleTest, CloseWithBadFdRaisesOsError) {
141 HandleScope scope(thread_);
142 Object fd_obj(&scope, SmallInt::fromWord(-1));
143 EXPECT_TRUE(raised(runBuiltin(FUNC(_os, close), fd_obj), LayoutId::kOSError));
144}
145
146static void createDummyFdWithContents(const char* c_str, int* fd) {
147 word length = std::strlen(c_str);
148 int fds[2];
149 int result = ::pipe(fds);
150 ASSERT_EQ(result, 0);
151 result = ::write(fds[1], c_str, length);
152 ASSERT_EQ(result, length);
153 result = ::close(fds[1]);
154 ASSERT_NE(result, -1);
155 *fd = fds[0];
156}
157
158TEST_F(UnderOsModuleTest, CloseReturnsNone) {
159 HandleScope scope(thread_);
160 int fd;
161 ASSERT_NO_FATAL_FAILURE(createDummyFdWithContents("hello", &fd));
162 Object fd_obj(&scope, SmallInt::fromWord(fd));
163 EXPECT_TRUE(runBuiltin(FUNC(_os, close), fd_obj).isNoneType());
164 EXPECT_TRUE(raised(runBuiltin(FUNC(_os, close), fd_obj), LayoutId::kOSError));
165}
166
167TEST_F(UnderOsModuleTest, FstatSizeWithBadFdRaisesOSError) {
168 HandleScope scope(thread_);
169 int fd = 99999;
170 Object fd_obj(&scope, SmallInt::fromWord(fd));
171 EXPECT_TRUE(
172 raised(runBuiltin(FUNC(_os, fstat_size), fd_obj), LayoutId::kOSError));
173}
174
175TEST_F(UnderOsModuleTest, FstatSizeReturnsSizeOfFile) {
176 HandleScope scope(thread_);
177 TemporaryDirectory directory;
178 std::string path = directory.path + "test.txt";
179 const char contents[] = "hello world";
180 writeFile(path, contents);
181 int fd = File::open(path.c_str(), O_RDONLY, 0755);
182 ASSERT_GT(fd, 0);
183 Object fd_obj(&scope, SmallInt::fromWord(fd));
184 EXPECT_TRUE(isIntEqualsWord(runBuiltin(FUNC(_os, fstat_size), fd_obj),
185 std::strlen(contents)));
186}
187
188TEST_F(UnderOsModuleTest, FtruncateWithBadFdRaisesOSError) {
189 HandleScope scope(thread_);
190 int fd = 99999;
191 Object fd_obj(&scope, SmallInt::fromWord(fd));
192 Object size(&scope, SmallInt::fromWord(0));
193 EXPECT_TRUE(raised(runBuiltin(FUNC(_os, ftruncate), fd_obj, size),
194 LayoutId::kOSError));
195}
196
197TEST_F(UnderOsModuleTest, FtruncateSetsSize) {
198 HandleScope scope(thread_);
199 TemporaryDirectory directory;
200 std::string path = directory.path + "test.txt";
201 const char contents[] = "hello world";
202 word initial_size = std::strlen(contents);
203 writeFile(path, contents);
204 int fd = File::open(path.c_str(), O_RDWR, 0755);
205 ASSERT_GT(fd, 0);
206 EXPECT_EQ(File::size(fd), initial_size);
207 Object fd_obj(&scope, SmallInt::fromWord(fd));
208 Object size_obj(&scope, SmallInt::fromWord(5));
209 EXPECT_TRUE(runBuiltin(FUNC(_os, ftruncate), fd_obj, size_obj).isNoneType());
210}
211
212TEST_F(UnderOsModuleTest, IsattyWithBadFdReturnsFalse) {
213 HandleScope scope(thread_);
214 int fd = 99999;
215 Object fd_obj(&scope, SmallInt::fromWord(fd));
216 EXPECT_EQ(runBuiltin(FUNC(_os, isatty), fd_obj), Bool::falseObj());
217}
218
219TEST_F(UnderOsModuleTest, IsattyWithNonTtyReturnsFalse) {
220 HandleScope scope(thread_);
221 int fd = ::open("/dev/null", O_RDONLY);
222 ASSERT_GE(fd, 0);
223 Object fd_obj(&scope, SmallInt::fromWord(fd));
224 EXPECT_EQ(runBuiltin(FUNC(_os, isatty), fd_obj), Bool::falseObj());
225 EXPECT_EQ(::close(fd), 0);
226}
227
228TEST_F(UnderOsModuleTest, IsdirWithFileReturnsFalse) {
229 HandleScope scope(thread_);
230 int fd = ::open("/dev/null", O_RDONLY);
231 ASSERT_GE(fd, 0);
232 Object fd_obj(&scope, SmallInt::fromWord(fd));
233 EXPECT_EQ(runBuiltin(FUNC(_os, isdir), fd_obj), Bool::falseObj());
234 EXPECT_EQ(::close(fd), 0);
235}
236
237TEST_F(UnderOsModuleTest, IsdirWithNonExistentFdRaisesOSError) {
238 HandleScope scope(thread_);
239 int fd = 99999;
240 Object fd_obj(&scope, SmallInt::fromWord(fd));
241 EXPECT_TRUE(raised(runBuiltin(FUNC(_os, isdir), fd_obj), LayoutId::kOSError));
242}
243
244TEST_F(UnderOsModuleTest, IsdirWithDirectoryReturnsTrue) {
245 HandleScope scope(thread_);
246 int fd = ::open("/", O_RDONLY);
247 Object fd_obj(&scope, SmallInt::fromWord(fd));
248 EXPECT_EQ(runBuiltin(FUNC(_os, isdir), fd_obj), Bool::trueObj());
249}
250
251TEST_F(UnderOsModuleTest, LseekWithBadFdRaisesOSError) {
252 HandleScope scope(thread_);
253 int fd = 99999;
254 Object fd_obj(&scope, SmallInt::fromWord(fd));
255 Object offset(&scope, SmallInt::fromWord(0));
256 Object whence(&scope, SmallInt::fromWord(SEEK_SET));
257 EXPECT_TRUE(raised(runBuiltin(FUNC(_os, lseek), fd_obj, offset, whence),
258 LayoutId::kOSError));
259}
260
261TEST_F(UnderOsModuleTest, LseekChangesPosition) {
262 HandleScope scope(thread_);
263 TemporaryDirectory directory;
264 std::string path = directory.path + "test.txt";
265 const char contents[] = "hello world";
266 writeFile(path, contents);
267 int fd = File::open(path.c_str(), O_RDONLY, 0755);
268 ASSERT_GE(fd, 0);
269 Object fd_obj(&scope, SmallInt::fromWord(fd));
270 word offset = 10;
271 Object offset_obj(&scope, SmallInt::fromWord(offset));
272 Object whence(&scope, SmallInt::fromWord(SEEK_SET));
273 Object result(&scope,
274 runBuiltin(FUNC(_os, lseek), fd_obj, offset_obj, whence));
275 EXPECT_TRUE(isIntEqualsWord(*result, offset));
276 // Get the current position
277 EXPECT_EQ(::lseek(fd, 0, SEEK_CUR), offset);
278 EXPECT_EQ(::close(fd), 0);
279}
280
281TEST_F(UnderOsModuleTest, OpenWithNonExistentFileRaisesOSError) {
282 HandleScope scope(thread_);
283 Str path(&scope, runtime_->newStrFromCStr("/i-should-not-exist"));
284 Object flags(&scope, SmallInt::fromWord(0));
285 Object mode(&scope, SmallInt::fromWord(0));
286 Object dir_fd(&scope, NoneType::object());
287 EXPECT_TRUE(raised(runBuiltin(FUNC(_os, open), path, flags, mode, dir_fd),
288 LayoutId::kOSError));
289}
290
291TEST_F(UnderOsModuleTest, OpenReturnsInt) {
292 HandleScope scope(thread_);
293 TemporaryDirectory directory;
294 std::string path = directory.path + "test.txt";
295 Str path_obj(&scope, runtime_->newStrFromCStr(path.c_str()));
296 Object flags_obj(&scope, SmallInt::fromWord(O_RDWR | O_CREAT));
297 Object mode_obj(&scope, SmallInt::fromWord(0755));
298 Object dir_fd(&scope, NoneType::object());
299 Object result_obj(&scope, runBuiltin(FUNC(_os, open), path_obj, flags_obj,
300 mode_obj, dir_fd));
301 ASSERT_TRUE(result_obj.isSmallInt());
302 word fd = SmallInt::cast(*result_obj).value();
303 ASSERT_GE(fd, 0);
304
305 // It set the right flags
306 int flags = ::fcntl(fd, F_GETFL);
307 ASSERT_NE(flags, -1);
308 EXPECT_EQ(flags & O_ACCMODE, O_RDWR);
309
310 // It set the right mode
311 struct stat statbuf;
312 int result = ::fstat(fd, &statbuf);
313 ASSERT_EQ(result, 0);
314 EXPECT_EQ(statbuf.st_mode & 0777, mode_t{0755});
315
316 // It set no inheritable
317 flags = ::fcntl(fd, F_GETFD);
318 ASSERT_NE(flags, -1);
319 EXPECT_TRUE(flags & FD_CLOEXEC);
320
321 ::close(fd);
322}
323
324TEST_F(UnderOsModuleTest, OpenWithBytesPathReturnsInt) {
325 HandleScope scope(thread_);
326 TemporaryDirectory directory;
327 std::string path = directory.path + "test.txt";
328 Bytes path_obj(&scope, runtime_->newBytesWithAll(
329 {reinterpret_cast<const byte*>(path.c_str()),
330 static_cast<long>(path.length())}));
331 Object flags_obj(&scope, SmallInt::fromWord(O_RDWR | O_CREAT));
332 Object mode_obj(&scope, SmallInt::fromWord(0755));
333 Object dir_fd(&scope, NoneType::object());
334 Object result_obj(&scope, runBuiltin(FUNC(_os, open), path_obj, flags_obj,
335 mode_obj, dir_fd));
336 ASSERT_TRUE(result_obj.isSmallInt());
337 word fd = SmallInt::cast(*result_obj).value();
338 ASSERT_GE(fd, 0);
339
340 // It set the right flags
341 int flags = ::fcntl(fd, F_GETFL);
342 ASSERT_NE(flags, -1);
343 EXPECT_EQ(flags & O_ACCMODE, O_RDWR);
344
345 // It set the right mode
346 struct stat statbuf;
347 int result = ::fstat(fd, &statbuf);
348 ASSERT_EQ(result, 0);
349 EXPECT_EQ(statbuf.st_mode & 0777, mode_t{0755});
350
351 // It set no inheritable
352 flags = ::fcntl(fd, F_GETFD);
353 ASSERT_NE(flags, -1);
354 EXPECT_TRUE(flags & FD_CLOEXEC);
355
356 ::close(fd);
357}
358
359TEST_F(UnderOsModuleTest, ParseModeWithXSetsExclAndCreat) {
360 HandleScope scope(thread_);
361 Object mode(&scope, runtime_->newStrFromCStr("x"));
362 EXPECT_TRUE(
363 isIntEqualsWord(runBuiltin(FUNC(_os, parse_mode), mode),
364 O_EXCL | O_CREAT | File::kNoInheritFlag | O_WRONLY));
365}
366
367TEST_F(UnderOsModuleTest, ParseModeWithRSetsRdonly) {
368 HandleScope scope(thread_);
369 Object mode(&scope, runtime_->newStrFromCStr("r"));
370 EXPECT_TRUE(isIntEqualsWord(runBuiltin(FUNC(_os, parse_mode), mode),
371 File::kNoInheritFlag | O_RDONLY));
372}
373
374TEST_F(UnderOsModuleTest, ParseModeWithASetsAppendAndCreat) {
375 HandleScope scope(thread_);
376 Object mode(&scope, runtime_->newStrFromCStr("a"));
377 EXPECT_TRUE(
378 isIntEqualsWord(runBuiltin(FUNC(_os, parse_mode), mode),
379 O_APPEND | O_CREAT | File::kNoInheritFlag | O_WRONLY));
380}
381
382TEST_F(UnderOsModuleTest, ParseModeWithRPlusSetsRdWr) {
383 HandleScope scope(thread_);
384 Object mode(&scope, runtime_->newStrFromCStr("r+"));
385 EXPECT_TRUE(isIntEqualsWord(runBuiltin(FUNC(_os, parse_mode), mode),
386 File::kNoInheritFlag | O_RDWR));
387}
388
389TEST_F(UnderOsModuleTest, ParseModeWithWPlusSetsRdWr) {
390 HandleScope scope(thread_);
391 Object mode(&scope, runtime_->newStrFromCStr("w+"));
392 EXPECT_TRUE(
393 isIntEqualsWord(runBuiltin(FUNC(_os, parse_mode), mode),
394 O_CREAT | O_TRUNC | File::kNoInheritFlag | O_RDWR));
395}
396
397TEST_F(UnderOsModuleTest, ParseModeWithAPlusSetsRdWr) {
398 HandleScope scope(thread_);
399 Object mode(&scope, runtime_->newStrFromCStr("a+"));
400 EXPECT_TRUE(
401 isIntEqualsWord(runBuiltin(FUNC(_os, parse_mode), mode),
402 O_APPEND | O_CREAT | File::kNoInheritFlag | O_RDWR));
403}
404
405TEST_F(UnderOsModuleTest, ReadWithBadFdRaisesOSError) {
406 HandleScope scope(thread_);
407 Object fd_obj(&scope, SmallInt::fromWord(-1));
408 Object size(&scope, SmallInt::fromWord(5));
409 EXPECT_TRUE(
410 raised(runBuiltin(FUNC(_os, read), fd_obj, size), LayoutId::kOSError));
411}
412
413TEST_F(UnderOsModuleTest, ReadWithFdNotOpenedForReadingRaisesOSError) {
414 HandleScope scope(thread_);
415 int fds[2];
416 int result = ::pipe(fds);
417 ASSERT_EQ(result, 0);
418 Object fd_obj(&scope, SmallInt::fromWord(fds[1]));
419 Object size(&scope, SmallInt::fromWord(5));
420 EXPECT_TRUE(
421 raised(runBuiltin(FUNC(_os, read), fd_obj, size), LayoutId::kOSError));
422 ::close(fds[0]);
423 ::close(fds[1]);
424}
425
426TEST_F(UnderOsModuleTest,
427 ReadWithFewerThanSizeBytesAvailableReadsFewerThanSizeBytes) {
428 HandleScope scope(thread_);
429 int fd;
430 ASSERT_NO_FATAL_FAILURE(createDummyFdWithContents("h", &fd));
431 Object fd_obj(&scope, SmallInt::fromWord(fd));
432 Object size(&scope, SmallInt::fromWord(5));
433 Object result(&scope, runBuiltin(FUNC(_os, read), fd_obj, size));
434 EXPECT_TRUE(isBytesEqualsCStr(result, "h"));
435 ::close(fd);
436}
437
438TEST_F(UnderOsModuleTest, ReadWithZeroCountReturnsEmptyBytes) {
439 HandleScope scope(thread_);
440 int fd;
441 ASSERT_NO_FATAL_FAILURE(createDummyFdWithContents("hello, world!", &fd));
442 Object fd_obj(&scope, SmallInt::fromWord(fd));
443 Object size(&scope, SmallInt::fromWord(0));
444 Object result(&scope, runBuiltin(FUNC(_os, read), fd_obj, size));
445 EXPECT_TRUE(isBytesEqualsCStr(result, ""));
446 ::close(fd);
447}
448
449TEST_F(UnderOsModuleTest, ReadReadsSizeBytes) {
450 HandleScope scope(thread_);
451 int fd;
452 ASSERT_NO_FATAL_FAILURE(createDummyFdWithContents("hello, world!", &fd));
453 Object fd_obj(&scope, SmallInt::fromWord(fd));
454 Object size(&scope, SmallInt::fromWord(5));
455 Object result(&scope, runBuiltin(FUNC(_os, read), fd_obj, size));
456 EXPECT_TRUE(isBytesEqualsCStr(result, "hello"));
457 ::close(fd);
458}
459
460TEST_F(UnderOsModuleTest, SetNoInheritableWithBadFdRaisesOSError) {
461 HandleScope scope(thread_);
462 Object fd_obj(&scope, SmallInt::fromWord(-1));
463 EXPECT_TRUE(raised(runBuiltin(FUNC(_os, set_noinheritable), fd_obj),
464 LayoutId::kOSError));
465}
466
467TEST_F(UnderOsModuleTest, SetNoInheritableWithFdSetsNoInheritable) {
468 HandleScope scope(thread_);
469 int fd;
470 ASSERT_NO_FATAL_FAILURE(createDummyFdWithContents("hello, world!", &fd));
471 Object fd_obj(&scope, SmallInt::fromWord(fd));
472 Object result(&scope, runBuiltin(FUNC(_os, set_noinheritable), fd_obj));
473 EXPECT_TRUE(result.isNoneType());
474 EXPECT_EQ(File::isInheritable(fd), 1);
475 ::close(fd);
476}
477
478} // namespace testing
479} // namespace py