1 //===-- flang/unittests/Runtime/CommandTest.cpp ---------------------------===// 2 // 3 // Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions. 4 // See https://llvm.org/LICENSE.txt for license information. 5 // SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception 6 // 7 //===----------------------------------------------------------------------===// 8 9 #include "flang/Runtime/command.h" 10 #include "gmock/gmock.h" 11 #include "gtest/gtest.h" 12 #include "flang/Runtime/descriptor.h" 13 #include "flang/Runtime/execute.h" 14 #include "flang/Runtime/extensions.h" 15 #include "flang/Runtime/main.h" 16 #include <cstddef> 17 #include <cstdlib> 18 19 #if _REENTRANT || _POSIX_C_SOURCE >= 199506L 20 #include <limits.h> // LOGIN_NAME_MAX used in getlog test 21 #endif 22 23 using namespace Fortran::runtime; 24 25 template <std::size_t n = 64> 26 static OwningPtr<Descriptor> CreateEmptyCharDescriptor() { 27 OwningPtr<Descriptor> descriptor{Descriptor::Create( 28 sizeof(char), n, nullptr, 0, nullptr, CFI_attribute_allocatable)}; 29 if (descriptor->Allocate() != 0) { 30 return nullptr; 31 } 32 return descriptor; 33 } 34 35 static OwningPtr<Descriptor> CharDescriptor(const char *value) { 36 std::size_t n{std::strlen(value)}; 37 OwningPtr<Descriptor> descriptor{Descriptor::Create( 38 sizeof(char), n, nullptr, 0, nullptr, CFI_attribute_allocatable)}; 39 if (descriptor->Allocate() != 0) { 40 return nullptr; 41 } 42 std::memcpy(descriptor->OffsetElement(), value, n); 43 return descriptor; 44 } 45 46 template <int kind = sizeof(std::int64_t)> 47 static OwningPtr<Descriptor> EmptyIntDescriptor() { 48 OwningPtr<Descriptor> descriptor{Descriptor::Create(TypeCategory::Integer, 49 kind, nullptr, 0, nullptr, CFI_attribute_allocatable)}; 50 if (descriptor->Allocate() != 0) { 51 return nullptr; 52 } 53 return descriptor; 54 } 55 56 template <int kind = sizeof(std::int64_t)> 57 static OwningPtr<Descriptor> IntDescriptor(const int &value) { 58 OwningPtr<Descriptor> descriptor{Descriptor::Create(TypeCategory::Integer, 59 kind, nullptr, 0, nullptr, CFI_attribute_allocatable)}; 60 if (descriptor->Allocate() != 0) { 61 return nullptr; 62 } 63 std::memcpy(descriptor->OffsetElement<int>(), &value, sizeof(int)); 64 return descriptor; 65 } 66 67 class CommandFixture : public ::testing::Test { 68 protected: 69 CommandFixture(int argc, const char *argv[]) { 70 RTNAME(ProgramStart)(argc, argv, {}, {}); 71 } 72 73 std::string GetPaddedStr(const char *text, std::size_t len) const { 74 std::string res{text}; 75 assert(res.length() <= len && "No room to pad"); 76 res.append(len - res.length(), ' '); 77 return res; 78 } 79 80 void CheckCharEqStr(const char *value, const std::string &expected) const { 81 ASSERT_NE(value, nullptr); 82 EXPECT_EQ(std::strncmp(value, expected.c_str(), expected.size()), 0) 83 << "expected: " << expected << "\n" 84 << "value: " << value; 85 } 86 87 void CheckDescriptorEqStr( 88 const Descriptor *value, const std::string &expected) const { 89 ASSERT_NE(value, nullptr); 90 EXPECT_EQ(std::strncmp(value->OffsetElement(), expected.c_str(), 91 value->ElementBytes()), 92 0) 93 << "expected: " << expected << "\n" 94 << "value: " 95 << std::string{value->OffsetElement(), value->ElementBytes()}; 96 } 97 98 template <typename INT_T = std::int64_t> 99 void CheckDescriptorEqInt( 100 const Descriptor *value, const INT_T expected) const { 101 if (expected != -1) { 102 ASSERT_NE(value, nullptr); 103 EXPECT_EQ(*value->OffsetElement<INT_T>(), expected); 104 } 105 } 106 107 template <typename RuntimeCall> 108 void CheckValue(RuntimeCall F, const char *expectedValue, 109 std::int64_t expectedLength = -1, std::int32_t expectedStatus = 0, 110 const char *expectedErrMsg = "shouldn't change") const { 111 OwningPtr<Descriptor> value{CreateEmptyCharDescriptor()}; 112 ASSERT_NE(value, nullptr); 113 114 OwningPtr<Descriptor> length{ 115 expectedLength == -1 ? nullptr : EmptyIntDescriptor()}; 116 117 OwningPtr<Descriptor> errmsg{CharDescriptor(expectedErrMsg)}; 118 ASSERT_NE(errmsg, nullptr); 119 120 std::string expectedValueStr{ 121 GetPaddedStr(expectedValue, value->ElementBytes())}; 122 123 EXPECT_EQ(F(value.get(), length.get(), errmsg.get()), expectedStatus); 124 CheckDescriptorEqStr(value.get(), expectedValueStr); 125 CheckDescriptorEqInt(length.get(), expectedLength); 126 CheckDescriptorEqStr(errmsg.get(), expectedErrMsg); 127 } 128 129 void CheckArgumentValue(const char *expectedValue, int n) const { 130 SCOPED_TRACE(n); 131 SCOPED_TRACE("Checking argument:"); 132 CheckValue( 133 [&](const Descriptor *value, const Descriptor *length, 134 const Descriptor *errmsg) { 135 return RTNAME(GetCommandArgument)(n, value, length, errmsg); 136 }, 137 expectedValue, std::strlen(expectedValue)); 138 } 139 140 void CheckCommandValue(const char *args[], int n) const { 141 SCOPED_TRACE("Checking command:"); 142 ASSERT_GE(n, 1); 143 std::string expectedValue{args[0]}; 144 for (int i = 1; i < n; i++) { 145 expectedValue += " " + std::string{args[i]}; 146 } 147 CheckValue( 148 [&](const Descriptor *value, const Descriptor *length, 149 const Descriptor *errmsg) { 150 return RTNAME(GetCommand)(value, length, errmsg); 151 }, 152 expectedValue.c_str(), expectedValue.size()); 153 } 154 155 void CheckEnvVarValue( 156 const char *expectedValue, const char *name, bool trimName = true) const { 157 SCOPED_TRACE(name); 158 SCOPED_TRACE("Checking environment variable"); 159 CheckValue( 160 [&](const Descriptor *value, const Descriptor *length, 161 const Descriptor *errmsg) { 162 return RTNAME(GetEnvVariable)( 163 *CharDescriptor(name), value, length, trimName, errmsg); 164 }, 165 expectedValue, std::strlen(expectedValue)); 166 } 167 168 void CheckMissingEnvVarValue(const char *name, bool trimName = true) const { 169 SCOPED_TRACE(name); 170 SCOPED_TRACE("Checking missing environment variable"); 171 172 ASSERT_EQ(nullptr, std::getenv(name)) 173 << "Environment variable " << name << " not expected to exist"; 174 175 CheckValue( 176 [&](const Descriptor *value, const Descriptor *length, 177 const Descriptor *errmsg) { 178 return RTNAME(GetEnvVariable)( 179 *CharDescriptor(name), value, length, trimName, errmsg); 180 }, 181 "", 0, 1, "Missing environment variable"); 182 } 183 184 void CheckMissingArgumentValue(int n, const char *errStr = nullptr) const { 185 OwningPtr<Descriptor> value{CreateEmptyCharDescriptor()}; 186 ASSERT_NE(value, nullptr); 187 188 OwningPtr<Descriptor> length{EmptyIntDescriptor()}; 189 ASSERT_NE(length, nullptr); 190 191 OwningPtr<Descriptor> err{errStr ? CreateEmptyCharDescriptor() : nullptr}; 192 193 EXPECT_GT( 194 RTNAME(GetCommandArgument)(n, value.get(), length.get(), err.get()), 0); 195 196 std::string spaces(value->ElementBytes(), ' '); 197 CheckDescriptorEqStr(value.get(), spaces); 198 199 CheckDescriptorEqInt<std::int64_t>(length.get(), 0); 200 201 if (errStr) { 202 std::string paddedErrStr(GetPaddedStr(errStr, err->ElementBytes())); 203 CheckDescriptorEqStr(err.get(), paddedErrStr); 204 } 205 } 206 207 void CheckMissingCommandValue(const char *errStr = nullptr) const { 208 OwningPtr<Descriptor> value{CreateEmptyCharDescriptor()}; 209 ASSERT_NE(value, nullptr); 210 211 OwningPtr<Descriptor> length{EmptyIntDescriptor()}; 212 ASSERT_NE(length, nullptr); 213 214 OwningPtr<Descriptor> err{errStr ? CreateEmptyCharDescriptor() : nullptr}; 215 216 EXPECT_GT(RTNAME(GetCommand)(value.get(), length.get(), err.get()), 0); 217 218 std::string spaces(value->ElementBytes(), ' '); 219 CheckDescriptorEqStr(value.get(), spaces); 220 221 CheckDescriptorEqInt<std::int64_t>(length.get(), 0); 222 223 if (errStr) { 224 std::string paddedErrStr(GetPaddedStr(errStr, err->ElementBytes())); 225 CheckDescriptorEqStr(err.get(), paddedErrStr); 226 } 227 } 228 }; 229 230 class NoArgv : public CommandFixture { 231 protected: 232 NoArgv() : CommandFixture(0, nullptr) {} 233 }; 234 235 #if _WIN32 || _POSIX_C_SOURCE >= 1 || _XOPEN_SOURCE || _BSD_SOURCE || \ 236 _SVID_SOURCE || defined(_POSIX_SOURCE) 237 TEST_F(NoArgv, FdateGetDate) { 238 char input[]{"24LengthCharIsJustRight"}; 239 const std::size_t charLen = sizeof(input); 240 241 FORTRAN_PROCEDURE_NAME(fdate)(input, charLen); 242 243 // Tue May 26 21:51:03 2015\n\0 244 // index at 3, 7, 10, 19 should be space 245 // when date is less than two digit, index 8 would be space 246 // Tue May 6 21:51:03 2015\n\0 247 for (std::size_t i{0}; i < charLen; i++) { 248 if (i == 8) 249 continue; 250 if (i == 3 || i == 7 || i == 10 || i == 19) { 251 EXPECT_EQ(input[i], ' '); 252 continue; 253 } 254 EXPECT_NE(input[i], ' '); 255 } 256 } 257 258 TEST_F(NoArgv, FdateGetDateTooShort) { 259 char input[]{"TooShortAllPadSpace"}; 260 const std::size_t charLen = sizeof(input); 261 262 FORTRAN_PROCEDURE_NAME(fdate)(input, charLen); 263 264 for (std::size_t i{0}; i < charLen; i++) { 265 EXPECT_EQ(input[i], ' '); 266 } 267 } 268 269 TEST_F(NoArgv, FdateGetDatePadSpace) { 270 char input[]{"All char after 23 pad spaces"}; 271 const std::size_t charLen = sizeof(input); 272 273 FORTRAN_PROCEDURE_NAME(fdate)(input, charLen); 274 275 for (std::size_t i{24}; i < charLen; i++) { 276 EXPECT_EQ(input[i], ' '); 277 } 278 } 279 280 #else 281 TEST_F(NoArgv, FdateNotSupported) { 282 char input[]{"No change due to crash"}; 283 284 EXPECT_DEATH(FORTRAN_PROCEDURE_NAME(fdate)(input, sizeof(input)), 285 "fdate is not supported."); 286 287 CheckCharEqStr(input, "No change due to crash"); 288 } 289 #endif 290 291 // TODO: Test other intrinsics with this fixture. 292 293 TEST_F(NoArgv, GetCommand) { CheckMissingCommandValue(); } 294 295 static const char *commandOnlyArgv[]{"aProgram"}; 296 class ZeroArguments : public CommandFixture { 297 protected: 298 ZeroArguments() : CommandFixture(1, commandOnlyArgv) {} 299 }; 300 301 TEST_F(ZeroArguments, ArgumentCount) { EXPECT_EQ(0, RTNAME(ArgumentCount)()); } 302 303 TEST_F(ZeroArguments, GetCommandArgument) { 304 CheckMissingArgumentValue(-1); 305 CheckArgumentValue(commandOnlyArgv[0], 0); 306 CheckMissingArgumentValue(1); 307 } 308 309 TEST_F(ZeroArguments, GetCommand) { CheckCommandValue(commandOnlyArgv, 1); } 310 311 TEST_F(ZeroArguments, ECLValidCommandAndPadSync) { 312 OwningPtr<Descriptor> command{CharDescriptor("echo hi")}; 313 bool wait{true}; 314 OwningPtr<Descriptor> exitStat{IntDescriptor(404)}; 315 OwningPtr<Descriptor> cmdStat{IntDescriptor(202)}; 316 OwningPtr<Descriptor> cmdMsg{CharDescriptor("No change")}; 317 318 RTNAME(ExecuteCommandLine) 319 (*command.get(), wait, exitStat.get(), cmdStat.get(), cmdMsg.get()); 320 321 std::string spaces(cmdMsg->ElementBytes(), ' '); 322 CheckDescriptorEqInt<std::int64_t>(exitStat.get(), 0); 323 CheckDescriptorEqInt<std::int64_t>(cmdStat.get(), 0); 324 CheckDescriptorEqStr(cmdMsg.get(), "No change"); 325 } 326 327 TEST_F(ZeroArguments, ECLValidCommandStatusSetSync) { 328 OwningPtr<Descriptor> command{CharDescriptor("echo hi")}; 329 bool wait{true}; 330 OwningPtr<Descriptor> exitStat{IntDescriptor(404)}; 331 OwningPtr<Descriptor> cmdStat{IntDescriptor(202)}; 332 OwningPtr<Descriptor> cmdMsg{CharDescriptor("No change")}; 333 334 RTNAME(ExecuteCommandLine) 335 (*command.get(), wait, exitStat.get(), cmdStat.get(), cmdMsg.get()); 336 337 CheckDescriptorEqInt<std::int64_t>(exitStat.get(), 0); 338 CheckDescriptorEqInt<std::int64_t>(cmdStat.get(), 0); 339 CheckDescriptorEqStr(cmdMsg.get(), "No change"); 340 } 341 342 TEST_F(ZeroArguments, ECLGeneralErrorCommandErrorSync) { 343 OwningPtr<Descriptor> command{CharDescriptor("cat GeneralErrorCommand")}; 344 bool wait{true}; 345 OwningPtr<Descriptor> exitStat{IntDescriptor(404)}; 346 OwningPtr<Descriptor> cmdStat{IntDescriptor(202)}; 347 OwningPtr<Descriptor> cmdMsg{CharDescriptor("cmd msg buffer XXXXXXXXXXXXXX")}; 348 349 RTNAME(ExecuteCommandLine) 350 (*command.get(), wait, exitStat.get(), cmdStat.get(), cmdMsg.get()); 351 #if defined(_WIN32) 352 CheckDescriptorEqInt<std::int64_t>(exitStat.get(), 1); 353 CheckDescriptorEqInt<std::int64_t>(cmdStat.get(), 6); 354 CheckDescriptorEqStr(cmdMsg.get(), "Invalid command lineXXXXXXXXX"); 355 #elif defined(_AIX) 356 CheckDescriptorEqInt<std::int64_t>(exitStat.get(), 2); 357 CheckDescriptorEqInt<std::int64_t>(cmdStat.get(), 6); 358 CheckDescriptorEqStr(cmdMsg.get(), "Invalid command lineXXXXXXXXX"); 359 #else 360 CheckDescriptorEqInt<std::int64_t>(exitStat.get(), 1); 361 CheckDescriptorEqInt<std::int64_t>(cmdStat.get(), 3); 362 CheckDescriptorEqStr(cmdMsg.get(), "Command line execution failed"); 363 #endif 364 } 365 366 TEST_F(ZeroArguments, ECLNotExecutedCommandErrorSync) { 367 OwningPtr<Descriptor> command{CharDescriptor( 368 "touch NotExecutedCommandFile && chmod -x NotExecutedCommandFile && " 369 "./NotExecutedCommandFile")}; 370 bool wait{true}; 371 OwningPtr<Descriptor> exitStat{IntDescriptor(404)}; 372 OwningPtr<Descriptor> cmdStat{IntDescriptor(202)}; 373 OwningPtr<Descriptor> cmdMsg{CharDescriptor("cmd msg buffer XXXXXXXX")}; 374 375 RTNAME(ExecuteCommandLine) 376 (*command.get(), wait, exitStat.get(), cmdStat.get(), cmdMsg.get()); 377 #ifdef _WIN32 378 CheckDescriptorEqInt<std::int64_t>(exitStat.get(), 1); 379 CheckDescriptorEqInt<std::int64_t>(cmdStat.get(), 6); 380 CheckDescriptorEqStr(cmdMsg.get(), "Invalid command lineXXX"); 381 #else 382 CheckDescriptorEqInt<std::int64_t>(exitStat.get(), 126); 383 CheckDescriptorEqInt<std::int64_t>(cmdStat.get(), 4); 384 CheckDescriptorEqStr(cmdMsg.get(), "Command cannot be execu"); 385 // removing the file only on Linux (file is not created on Win) 386 OwningPtr<Descriptor> commandClean{ 387 CharDescriptor("rm -f NotExecutedCommandFile")}; 388 OwningPtr<Descriptor> cmdMsgNoErr{CharDescriptor("No Error")}; 389 RTNAME(ExecuteCommandLine) 390 (*commandClean.get(), wait, exitStat.get(), cmdStat.get(), cmdMsgNoErr.get()); 391 CheckDescriptorEqInt<std::int64_t>(exitStat.get(), 0); 392 CheckDescriptorEqStr(cmdMsgNoErr.get(), "No Error"); 393 CheckDescriptorEqInt<std::int64_t>(cmdStat.get(), 0); 394 #endif 395 } 396 397 TEST_F(ZeroArguments, ECLNotFoundCommandErrorSync) { 398 OwningPtr<Descriptor> command{CharDescriptor("NotFoundCommand")}; 399 bool wait{true}; 400 OwningPtr<Descriptor> exitStat{IntDescriptor(404)}; 401 OwningPtr<Descriptor> cmdStat{IntDescriptor(202)}; 402 OwningPtr<Descriptor> cmdMsg{CharDescriptor("unmodified buffer XXXXXXXXX")}; 403 404 RTNAME(ExecuteCommandLine) 405 (*command.get(), wait, exitStat.get(), cmdStat.get(), cmdMsg.get()); 406 #ifdef _WIN32 407 CheckDescriptorEqInt<std::int64_t>(exitStat.get(), 1); 408 CheckDescriptorEqInt<std::int64_t>(cmdStat.get(), 6); 409 CheckDescriptorEqStr(cmdMsg.get(), "Invalid command lineXXXXXXX"); 410 #else 411 CheckDescriptorEqInt<std::int64_t>(exitStat.get(), 127); 412 CheckDescriptorEqInt<std::int64_t>(cmdStat.get(), 5); 413 CheckDescriptorEqStr(cmdMsg.get(), "Command not found with exit"); 414 #endif 415 } 416 417 TEST_F(ZeroArguments, ECLInvalidCommandTerminatedSync) { 418 OwningPtr<Descriptor> command{CharDescriptor("InvalidCommand")}; 419 bool wait{true}; 420 OwningPtr<Descriptor> cmdMsg{CharDescriptor("No Change")}; 421 422 #ifdef _WIN32 423 EXPECT_DEATH(RTNAME(ExecuteCommandLine)( 424 *command.get(), wait, nullptr, nullptr, cmdMsg.get()), 425 "Invalid command quit with exit status code: 1"); 426 #else 427 EXPECT_DEATH(RTNAME(ExecuteCommandLine)( 428 *command.get(), wait, nullptr, nullptr, cmdMsg.get()), 429 "Command not found with exit code: 127."); 430 #endif 431 CheckDescriptorEqStr(cmdMsg.get(), "No Change"); 432 } 433 434 TEST_F(ZeroArguments, ECLValidCommandAndExitStatNoChangeAndCMDStatusSetAsync) { 435 OwningPtr<Descriptor> command{CharDescriptor("echo hi")}; 436 bool wait{false}; 437 OwningPtr<Descriptor> exitStat{IntDescriptor(404)}; 438 OwningPtr<Descriptor> cmdStat{IntDescriptor(202)}; 439 OwningPtr<Descriptor> cmdMsg{CharDescriptor("No change")}; 440 441 RTNAME(ExecuteCommandLine) 442 (*command.get(), wait, exitStat.get(), cmdStat.get(), cmdMsg.get()); 443 444 CheckDescriptorEqInt(exitStat.get(), 404); 445 CheckDescriptorEqInt<std::int64_t>(cmdStat.get(), 0); 446 CheckDescriptorEqStr(cmdMsg.get(), "No change"); 447 } 448 449 TEST_F(ZeroArguments, ECLInvalidCommandParentNotTerminatedAsync) { 450 OwningPtr<Descriptor> command{CharDescriptor("InvalidCommand")}; 451 bool wait{false}; 452 OwningPtr<Descriptor> cmdMsg{CharDescriptor("No change")}; 453 454 EXPECT_NO_FATAL_FAILURE(RTNAME(ExecuteCommandLine)( 455 *command.get(), wait, nullptr, nullptr, cmdMsg.get())); 456 CheckDescriptorEqStr(cmdMsg.get(), "No change"); 457 } 458 459 TEST_F(ZeroArguments, ECLInvalidCommandAsyncDontAffectSync) { 460 OwningPtr<Descriptor> command{CharDescriptor("echo hi")}; 461 462 EXPECT_NO_FATAL_FAILURE(RTNAME(ExecuteCommandLine)( 463 *command.get(), false, nullptr, nullptr, nullptr)); 464 EXPECT_NO_FATAL_FAILURE(RTNAME(ExecuteCommandLine)( 465 *command.get(), true, nullptr, nullptr, nullptr)); 466 } 467 468 TEST_F(ZeroArguments, ECLInvalidCommandAsyncDontAffectAsync) { 469 OwningPtr<Descriptor> command{CharDescriptor("echo hi")}; 470 471 EXPECT_NO_FATAL_FAILURE(RTNAME(ExecuteCommandLine)( 472 *command.get(), false, nullptr, nullptr, nullptr)); 473 EXPECT_NO_FATAL_FAILURE(RTNAME(ExecuteCommandLine)( 474 *command.get(), false, nullptr, nullptr, nullptr)); 475 } 476 477 TEST_F(ZeroArguments, SystemValidCommandExitStat) { 478 // envrionment setup for SYSTEM from EXECUTE_COMMAND_LINE runtime 479 OwningPtr<Descriptor> cmdStat{IntDescriptor(202)}; 480 bool wait{true}; 481 // setup finished 482 483 OwningPtr<Descriptor> command{CharDescriptor("echo hi")}; 484 OwningPtr<Descriptor> exitStat{EmptyIntDescriptor()}; 485 486 RTNAME(ExecuteCommandLine) 487 (*command.get(), wait, exitStat.get(), cmdStat.get(), nullptr); 488 CheckDescriptorEqInt<std::int64_t>(exitStat.get(), 0); 489 } 490 491 TEST_F(ZeroArguments, SystemInvalidCommandExitStat) { 492 // envrionment setup for SYSTEM from EXECUTE_COMMAND_LINE runtime 493 OwningPtr<Descriptor> cmdStat{IntDescriptor(202)}; 494 bool wait{true}; 495 // setup finished 496 497 OwningPtr<Descriptor> command{CharDescriptor("InvalidCommand")}; 498 OwningPtr<Descriptor> exitStat{EmptyIntDescriptor()}; 499 500 RTNAME(ExecuteCommandLine) 501 (*command.get(), wait, exitStat.get(), cmdStat.get(), nullptr); 502 #ifdef _WIN32 503 CheckDescriptorEqInt<std::int64_t>(exitStat.get(), 1); 504 #else 505 CheckDescriptorEqInt<std::int64_t>(exitStat.get(), 127); 506 #endif 507 } 508 509 TEST_F(ZeroArguments, SystemValidCommandOptionalExitStat) { 510 // envrionment setup for SYSTEM from EXECUTE_COMMAND_LINE runtime 511 OwningPtr<Descriptor> cmdStat{IntDescriptor(202)}; 512 bool wait{true}; 513 // setup finished 514 515 OwningPtr<Descriptor> command{CharDescriptor("echo hi")}; 516 EXPECT_NO_FATAL_FAILURE(RTNAME(ExecuteCommandLine)( 517 *command.get(), wait, nullptr, cmdStat.get(), nullptr)); 518 } 519 520 TEST_F(ZeroArguments, SystemInvalidCommandOptionalExitStat) { 521 // envrionment setup for SYSTEM from EXECUTE_COMMAND_LINE runtime 522 OwningPtr<Descriptor> cmdStat{IntDescriptor(202)}; 523 bool wait{true}; 524 // setup finished 525 526 OwningPtr<Descriptor> command{CharDescriptor("InvalidCommand")}; 527 EXPECT_NO_FATAL_FAILURE(RTNAME(ExecuteCommandLine)( 528 *command.get(), wait, nullptr, cmdStat.get(), nullptr);); 529 } 530 531 static const char *oneArgArgv[]{"aProgram", "anArgumentOfLength20"}; 532 class OneArgument : public CommandFixture { 533 protected: 534 OneArgument() : CommandFixture(2, oneArgArgv) {} 535 }; 536 537 TEST_F(OneArgument, ArgumentCount) { EXPECT_EQ(1, RTNAME(ArgumentCount)()); } 538 539 TEST_F(OneArgument, GetCommandArgument) { 540 CheckMissingArgumentValue(-1); 541 CheckArgumentValue(oneArgArgv[0], 0); 542 CheckArgumentValue(oneArgArgv[1], 1); 543 CheckMissingArgumentValue(2); 544 } 545 546 TEST_F(OneArgument, GetCommand) { CheckCommandValue(oneArgArgv, 2); } 547 548 static const char *severalArgsArgv[]{ 549 "aProgram", "16-char-long-arg", "", "-22-character-long-arg", "o"}; 550 class SeveralArguments : public CommandFixture { 551 protected: 552 SeveralArguments() 553 : CommandFixture(sizeof(severalArgsArgv) / sizeof(*severalArgsArgv), 554 severalArgsArgv) {} 555 }; 556 557 TEST_F(SeveralArguments, ArgumentCount) { 558 EXPECT_EQ(4, RTNAME(ArgumentCount)()); 559 } 560 561 TEST_F(SeveralArguments, GetCommandArgument) { 562 CheckArgumentValue(severalArgsArgv[0], 0); 563 CheckArgumentValue(severalArgsArgv[1], 1); 564 CheckArgumentValue(severalArgsArgv[3], 3); 565 CheckArgumentValue(severalArgsArgv[4], 4); 566 } 567 568 TEST_F(SeveralArguments, NoArgumentValue) { 569 // Make sure we don't crash if the 'value', 'length' and 'error' parameters 570 // aren't passed. 571 EXPECT_GT(RTNAME(GetCommandArgument)(2), 0); 572 EXPECT_EQ(RTNAME(GetCommandArgument)(1), 0); 573 EXPECT_GT(RTNAME(GetCommandArgument)(-1), 0); 574 } 575 576 TEST_F(SeveralArguments, MissingArguments) { 577 CheckMissingArgumentValue(-1, "Invalid argument number"); 578 CheckMissingArgumentValue(2, "Missing argument"); 579 CheckMissingArgumentValue(5, "Invalid argument number"); 580 CheckMissingArgumentValue(5); 581 } 582 583 TEST_F(SeveralArguments, ArgValueTooShort) { 584 OwningPtr<Descriptor> tooShort{CreateEmptyCharDescriptor<15>()}; 585 ASSERT_NE(tooShort, nullptr); 586 EXPECT_EQ(RTNAME(GetCommandArgument)(1, tooShort.get()), -1); 587 CheckDescriptorEqStr(tooShort.get(), severalArgsArgv[1]); 588 589 OwningPtr<Descriptor> length{EmptyIntDescriptor()}; 590 ASSERT_NE(length, nullptr); 591 OwningPtr<Descriptor> errMsg{CreateEmptyCharDescriptor()}; 592 ASSERT_NE(errMsg, nullptr); 593 594 EXPECT_EQ( 595 RTNAME(GetCommandArgument)(1, tooShort.get(), length.get(), errMsg.get()), 596 -1); 597 598 CheckDescriptorEqInt<std::int64_t>(length.get(), 16); 599 std::string expectedErrMsg{ 600 GetPaddedStr("Value too short", errMsg->ElementBytes())}; 601 CheckDescriptorEqStr(errMsg.get(), expectedErrMsg); 602 } 603 604 TEST_F(SeveralArguments, ArgErrMsgTooShort) { 605 OwningPtr<Descriptor> errMsg{CreateEmptyCharDescriptor<3>()}; 606 EXPECT_GT(RTNAME(GetCommandArgument)(-1, nullptr, nullptr, errMsg.get()), 0); 607 CheckDescriptorEqStr(errMsg.get(), "Inv"); 608 } 609 610 TEST_F(SeveralArguments, GetCommand) { 611 CheckMissingCommandValue(); 612 CheckMissingCommandValue("Missing argument"); 613 } 614 615 TEST_F(SeveralArguments, CommandErrMsgTooShort) { 616 OwningPtr<Descriptor> value{CreateEmptyCharDescriptor()}; 617 OwningPtr<Descriptor> length{EmptyIntDescriptor()}; 618 OwningPtr<Descriptor> errMsg{CreateEmptyCharDescriptor<3>()}; 619 620 EXPECT_GT(RTNAME(GetCommand)(value.get(), length.get(), errMsg.get()), 0); 621 622 std::string spaces(value->ElementBytes(), ' '); 623 CheckDescriptorEqStr(value.get(), spaces); 624 CheckDescriptorEqInt<std::int64_t>(length.get(), 0); 625 CheckDescriptorEqStr(errMsg.get(), "Mis"); 626 } 627 628 TEST_F(SeveralArguments, GetCommandCanTakeNull) { 629 EXPECT_GT(RTNAME(GetCommand)(nullptr, nullptr, nullptr), 0); 630 } 631 632 static const char *onlyValidArgsArgv[]{ 633 "aProgram", "-f", "has/a/few/slashes", "has\\a\\few\\backslashes"}; 634 class OnlyValidArguments : public CommandFixture { 635 protected: 636 OnlyValidArguments() 637 : CommandFixture(sizeof(onlyValidArgsArgv) / sizeof(*onlyValidArgsArgv), 638 onlyValidArgsArgv) {} 639 }; 640 641 TEST_F(OnlyValidArguments, GetCommand) { 642 CheckCommandValue(onlyValidArgsArgv, 4); 643 } 644 645 TEST_F(OnlyValidArguments, CommandValueTooShort) { 646 OwningPtr<Descriptor> tooShort{CreateEmptyCharDescriptor<50>()}; 647 ASSERT_NE(tooShort, nullptr); 648 OwningPtr<Descriptor> length{EmptyIntDescriptor()}; 649 ASSERT_NE(length, nullptr); 650 651 EXPECT_EQ(RTNAME(GetCommand)(tooShort.get(), length.get(), nullptr), -1); 652 653 CheckDescriptorEqStr( 654 tooShort.get(), "aProgram -f has/a/few/slashes has\\a\\few\\backslashe"); 655 CheckDescriptorEqInt<std::int64_t>(length.get(), 51); 656 657 OwningPtr<Descriptor> errMsg{CreateEmptyCharDescriptor()}; 658 ASSERT_NE(errMsg, nullptr); 659 660 EXPECT_EQ(-1, RTNAME(GetCommand)(tooShort.get(), nullptr, errMsg.get())); 661 662 std::string expectedErrMsg{ 663 GetPaddedStr("Value too short", errMsg->ElementBytes())}; 664 CheckDescriptorEqStr(errMsg.get(), expectedErrMsg); 665 } 666 667 TEST_F(OnlyValidArguments, GetCommandCanTakeNull) { 668 EXPECT_EQ(0, RTNAME(GetCommand)(nullptr, nullptr, nullptr)); 669 670 OwningPtr<Descriptor> value{CreateEmptyCharDescriptor()}; 671 ASSERT_NE(value, nullptr); 672 OwningPtr<Descriptor> length{EmptyIntDescriptor()}; 673 ASSERT_NE(length, nullptr); 674 675 EXPECT_EQ(0, RTNAME(GetCommand)(value.get(), nullptr, nullptr)); 676 CheckDescriptorEqStr(value.get(), 677 GetPaddedStr("aProgram -f has/a/few/slashes has\\a\\few\\backslashes", 678 value->ElementBytes())); 679 680 EXPECT_EQ(0, RTNAME(GetCommand)(nullptr, length.get(), nullptr)); 681 CheckDescriptorEqInt<std::int64_t>(length.get(), 51); 682 } 683 684 TEST_F(OnlyValidArguments, GetCommandShortLength) { 685 OwningPtr<Descriptor> length{EmptyIntDescriptor<sizeof(short)>()}; 686 ASSERT_NE(length, nullptr); 687 688 EXPECT_EQ(0, RTNAME(GetCommand)(nullptr, length.get(), nullptr)); 689 CheckDescriptorEqInt<short>(length.get(), 51); 690 } 691 692 TEST_F(ZeroArguments, GetPID) { 693 // pid should always greater than 0, in both linux and windows 694 EXPECT_GT(RTNAME(GetPID)(), 0); 695 } 696 697 class EnvironmentVariables : public CommandFixture { 698 protected: 699 EnvironmentVariables() : CommandFixture(0, nullptr) { 700 SetEnv("NAME", "VALUE"); 701 #ifdef _WIN32 702 SetEnv("USERNAME", "loginName"); 703 #else 704 SetEnv("LOGNAME", "loginName"); 705 #endif 706 SetEnv("EMPTY", ""); 707 } 708 709 // If we have access to setenv, we can run some more fine-grained tests. 710 template <typename ParamType = char> 711 void SetEnv(const ParamType *name, const ParamType *value, 712 decltype(setenv(name, value, 1)) *Enabled = nullptr) { 713 ASSERT_EQ(0, setenv(name, value, /*overwrite=*/1)); 714 canSetEnv = true; 715 } 716 717 // Fallback method if setenv is not available. 718 template <typename Unused = void> void SetEnv(const void *, const void *) {} 719 720 bool EnableFineGrainedTests() const { return canSetEnv; } 721 722 private: 723 bool canSetEnv{false}; 724 }; 725 726 TEST_F(EnvironmentVariables, Nonexistent) { 727 CheckMissingEnvVarValue("DOESNT_EXIST"); 728 CheckMissingEnvVarValue(" "); 729 CheckMissingEnvVarValue(""); 730 } 731 732 TEST_F(EnvironmentVariables, Basic) { 733 // Test a variable that's expected to exist in the environment. 734 char *path{std::getenv("PATH")}; 735 auto expectedLen{static_cast<int64_t>(std::strlen(path))}; 736 OwningPtr<Descriptor> length{EmptyIntDescriptor()}; 737 EXPECT_EQ(0, 738 RTNAME(GetEnvVariable)(*CharDescriptor("PATH"), 739 /*value=*/nullptr, length.get())); 740 CheckDescriptorEqInt(length.get(), expectedLen); 741 } 742 743 TEST_F(EnvironmentVariables, Trim) { 744 if (EnableFineGrainedTests()) { 745 CheckEnvVarValue("VALUE", "NAME "); 746 } 747 } 748 749 TEST_F(EnvironmentVariables, NoTrim) { 750 if (EnableFineGrainedTests()) { 751 CheckMissingEnvVarValue("NAME ", /*trim_name=*/false); 752 } 753 } 754 755 TEST_F(EnvironmentVariables, Empty) { 756 if (EnableFineGrainedTests()) { 757 CheckEnvVarValue("", "EMPTY"); 758 } 759 } 760 761 TEST_F(EnvironmentVariables, NoValueOrErrmsg) { 762 ASSERT_EQ(std::getenv("DOESNT_EXIST"), nullptr) 763 << "Environment variable DOESNT_EXIST actually exists"; 764 EXPECT_EQ(RTNAME(GetEnvVariable)(*CharDescriptor("DOESNT_EXIST")), 1); 765 766 if (EnableFineGrainedTests()) { 767 EXPECT_EQ(RTNAME(GetEnvVariable)(*CharDescriptor("NAME")), 0); 768 } 769 } 770 771 TEST_F(EnvironmentVariables, ValueTooShort) { 772 if (EnableFineGrainedTests()) { 773 OwningPtr<Descriptor> tooShort{CreateEmptyCharDescriptor<2>()}; 774 ASSERT_NE(tooShort, nullptr); 775 EXPECT_EQ(RTNAME(GetEnvVariable)(*CharDescriptor("NAME"), tooShort.get(), 776 /*length=*/nullptr, /*trim_name=*/true, nullptr), 777 -1); 778 CheckDescriptorEqStr(tooShort.get(), "VALUE"); 779 780 OwningPtr<Descriptor> errMsg{CreateEmptyCharDescriptor()}; 781 ASSERT_NE(errMsg, nullptr); 782 783 EXPECT_EQ(RTNAME(GetEnvVariable)(*CharDescriptor("NAME"), tooShort.get(), 784 /*length=*/nullptr, /*trim_name=*/true, errMsg.get()), 785 -1); 786 787 std::string expectedErrMsg{ 788 GetPaddedStr("Value too short", errMsg->ElementBytes())}; 789 CheckDescriptorEqStr(errMsg.get(), expectedErrMsg); 790 } 791 } 792 793 TEST_F(EnvironmentVariables, ErrMsgTooShort) { 794 ASSERT_EQ(std::getenv("DOESNT_EXIST"), nullptr) 795 << "Environment variable DOESNT_EXIST actually exists"; 796 797 OwningPtr<Descriptor> errMsg{CreateEmptyCharDescriptor<3>()}; 798 EXPECT_EQ(RTNAME(GetEnvVariable)(*CharDescriptor("DOESNT_EXIST"), nullptr, 799 /*length=*/nullptr, /*trim_name=*/true, errMsg.get()), 800 1); 801 CheckDescriptorEqStr(errMsg.get(), "Mis"); 802 } 803 804 // username first char must not be null 805 TEST_F(EnvironmentVariables, GetlogGetName) { 806 const int charLen{3}; 807 char input[charLen]{"\0\0"}; 808 FORTRAN_PROCEDURE_NAME(getlog)(input, charLen); 809 EXPECT_NE(input[0], '\0'); 810 } 811 812 #if _REENTRANT || _POSIX_C_SOURCE >= 199506L 813 TEST_F(EnvironmentVariables, GetlogPadSpace) { 814 // guarantee 1 char longer than max, last char should be pad space 815 int charLen; 816 #ifdef LOGIN_NAME_MAX 817 charLen = LOGIN_NAME_MAX + 2; 818 #else 819 charLen = sysconf(_SC_LOGIN_NAME_MAX) + 2; 820 if (charLen == -1) 821 charLen = _POSIX_LOGIN_NAME_MAX + 2; 822 #endif 823 std::vector<char> input(charLen); 824 FORTRAN_PROCEDURE_NAME(getlog)(input.data(), charLen); 825 EXPECT_EQ(input[charLen - 1], ' '); 826 } 827 #endif 828 829 #ifdef _WIN32 // Test ability to get name from environment variable 830 TEST_F(EnvironmentVariables, GetlogEnvGetName) { 831 if (EnableFineGrainedTests()) { 832 ASSERT_NE(std::getenv("USERNAME"), nullptr) 833 << "Environment variable USERNAME does not exist"; 834 835 char input[]{"XXXXXXXXX"}; 836 FORTRAN_PROCEDURE_NAME(getlog)(input, sizeof(input)); 837 838 CheckCharEqStr(input, "loginName"); 839 } 840 } 841 842 TEST_F(EnvironmentVariables, GetlogEnvBufferShort) { 843 if (EnableFineGrainedTests()) { 844 ASSERT_NE(std::getenv("USERNAME"), nullptr) 845 << "Environment variable USERNAME does not exist"; 846 847 char input[]{"XXXXXX"}; 848 FORTRAN_PROCEDURE_NAME(getlog)(input, sizeof(input)); 849 850 CheckCharEqStr(input, "loginN"); 851 } 852 } 853 854 TEST_F(EnvironmentVariables, GetlogEnvPadSpace) { 855 if (EnableFineGrainedTests()) { 856 ASSERT_NE(std::getenv("USERNAME"), nullptr) 857 << "Environment variable USERNAME does not exist"; 858 859 char input[]{"XXXXXXXXXX"}; 860 FORTRAN_PROCEDURE_NAME(getlog)(input, sizeof(input)); 861 862 CheckCharEqStr(input, "loginName "); 863 } 864 } 865 #endif 866