this repo has no description
at trunk 479 lines 17 kB view raw
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