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 // TODO: Test other intrinsics with this fixture. 236 237 TEST_F(NoArgv, GetCommand) { CheckMissingCommandValue(); } 238 239 static const char *commandOnlyArgv[]{"aProgram"}; 240 class ZeroArguments : public CommandFixture { 241 protected: 242 ZeroArguments() : CommandFixture(1, commandOnlyArgv) {} 243 }; 244 245 TEST_F(ZeroArguments, ArgumentCount) { EXPECT_EQ(0, RTNAME(ArgumentCount)()); } 246 247 TEST_F(ZeroArguments, GetCommandArgument) { 248 CheckMissingArgumentValue(-1); 249 CheckArgumentValue(commandOnlyArgv[0], 0); 250 CheckMissingArgumentValue(1); 251 } 252 253 TEST_F(ZeroArguments, GetCommand) { CheckCommandValue(commandOnlyArgv, 1); } 254 255 TEST_F(ZeroArguments, ECLValidCommandAndPadSync) { 256 OwningPtr<Descriptor> command{CharDescriptor("echo hi")}; 257 bool wait{true}; 258 OwningPtr<Descriptor> exitStat{EmptyIntDescriptor()}; 259 OwningPtr<Descriptor> cmdStat{EmptyIntDescriptor()}; 260 OwningPtr<Descriptor> cmdMsg{CharDescriptor("No change")}; 261 262 RTNAME(ExecuteCommandLine) 263 (*command.get(), wait, exitStat.get(), cmdStat.get(), cmdMsg.get()); 264 265 std::string spaces(cmdMsg->ElementBytes(), ' '); 266 CheckDescriptorEqInt(exitStat.get(), 0); 267 CheckDescriptorEqInt(cmdStat.get(), 0); 268 CheckDescriptorEqStr(cmdMsg.get(), "No change"); 269 } 270 271 TEST_F(ZeroArguments, ECLValidCommandStatusSetSync) { 272 OwningPtr<Descriptor> command{CharDescriptor("echo hi")}; 273 bool wait{true}; 274 OwningPtr<Descriptor> exitStat{IntDescriptor(404)}; 275 OwningPtr<Descriptor> cmdStat{IntDescriptor(202)}; 276 OwningPtr<Descriptor> cmdMsg{CharDescriptor("No change")}; 277 278 RTNAME(ExecuteCommandLine) 279 (*command.get(), wait, exitStat.get(), cmdStat.get(), cmdMsg.get()); 280 281 CheckDescriptorEqInt(exitStat.get(), 0); 282 CheckDescriptorEqInt(cmdStat.get(), 0); 283 CheckDescriptorEqStr(cmdMsg.get(), "No change"); 284 } 285 286 TEST_F(ZeroArguments, ECLInvalidCommandErrorSync) { 287 OwningPtr<Descriptor> command{CharDescriptor("InvalidCommand")}; 288 bool wait{true}; 289 OwningPtr<Descriptor> exitStat{IntDescriptor(404)}; 290 OwningPtr<Descriptor> cmdStat{IntDescriptor(202)}; 291 OwningPtr<Descriptor> cmdMsg{CharDescriptor("Message ChangedXXXXXXXXX")}; 292 293 RTNAME(ExecuteCommandLine) 294 (*command.get(), wait, exitStat.get(), cmdStat.get(), cmdMsg.get()); 295 #ifdef _WIN32 296 CheckDescriptorEqInt(exitStat.get(), 1); 297 #else 298 CheckDescriptorEqInt(exitStat.get(), 127); 299 #endif 300 CheckDescriptorEqInt(cmdStat.get(), 3); 301 CheckDescriptorEqStr(cmdMsg.get(), "Invalid command lineXXXX"); 302 } 303 304 TEST_F(ZeroArguments, ECLInvalidCommandTerminatedSync) { 305 OwningPtr<Descriptor> command{CharDescriptor("InvalidCommand")}; 306 bool wait{true}; 307 OwningPtr<Descriptor> exitStat{IntDescriptor(404)}; 308 OwningPtr<Descriptor> cmdMsg{CharDescriptor("No Change")}; 309 310 #ifdef _WIN32 311 EXPECT_DEATH(RTNAME(ExecuteCommandLine)( 312 *command.get(), wait, exitStat.get(), nullptr, cmdMsg.get()), 313 "Invalid command quit with exit status code: 1"); 314 #else 315 EXPECT_DEATH(RTNAME(ExecuteCommandLine)( 316 *command.get(), wait, exitStat.get(), nullptr, cmdMsg.get()), 317 "Invalid command quit with exit status code: 127"); 318 #endif 319 CheckDescriptorEqInt(exitStat.get(), 404); 320 CheckDescriptorEqStr(cmdMsg.get(), "No Change"); 321 } 322 323 TEST_F(ZeroArguments, ECLValidCommandAndExitStatNoChangeAndCMDStatusSetAsync) { 324 OwningPtr<Descriptor> command{CharDescriptor("echo hi")}; 325 bool wait{false}; 326 OwningPtr<Descriptor> exitStat{IntDescriptor(404)}; 327 OwningPtr<Descriptor> cmdStat{IntDescriptor(202)}; 328 OwningPtr<Descriptor> cmdMsg{CharDescriptor("No change")}; 329 330 RTNAME(ExecuteCommandLine) 331 (*command.get(), wait, exitStat.get(), cmdStat.get(), cmdMsg.get()); 332 333 CheckDescriptorEqInt(exitStat.get(), 404); 334 CheckDescriptorEqInt(cmdStat.get(), 0); 335 CheckDescriptorEqStr(cmdMsg.get(), "No change"); 336 } 337 338 TEST_F(ZeroArguments, ECLInvalidCommandParentNotTerminatedAsync) { 339 OwningPtr<Descriptor> command{CharDescriptor("InvalidCommand")}; 340 bool wait{false}; 341 OwningPtr<Descriptor> exitStat{IntDescriptor(404)}; 342 OwningPtr<Descriptor> cmdMsg{CharDescriptor("No change")}; 343 344 EXPECT_NO_FATAL_FAILURE(RTNAME(ExecuteCommandLine)( 345 *command.get(), wait, exitStat.get(), nullptr, cmdMsg.get())); 346 347 CheckDescriptorEqInt(exitStat.get(), 404); 348 CheckDescriptorEqStr(cmdMsg.get(), "No change"); 349 } 350 351 static const char *oneArgArgv[]{"aProgram", "anArgumentOfLength20"}; 352 class OneArgument : public CommandFixture { 353 protected: 354 OneArgument() : CommandFixture(2, oneArgArgv) {} 355 }; 356 357 TEST_F(OneArgument, ArgumentCount) { EXPECT_EQ(1, RTNAME(ArgumentCount)()); } 358 359 TEST_F(OneArgument, GetCommandArgument) { 360 CheckMissingArgumentValue(-1); 361 CheckArgumentValue(oneArgArgv[0], 0); 362 CheckArgumentValue(oneArgArgv[1], 1); 363 CheckMissingArgumentValue(2); 364 } 365 366 TEST_F(OneArgument, GetCommand) { CheckCommandValue(oneArgArgv, 2); } 367 368 static const char *severalArgsArgv[]{ 369 "aProgram", "16-char-long-arg", "", "-22-character-long-arg", "o"}; 370 class SeveralArguments : public CommandFixture { 371 protected: 372 SeveralArguments() 373 : CommandFixture(sizeof(severalArgsArgv) / sizeof(*severalArgsArgv), 374 severalArgsArgv) {} 375 }; 376 377 TEST_F(SeveralArguments, ArgumentCount) { 378 EXPECT_EQ(4, RTNAME(ArgumentCount)()); 379 } 380 381 TEST_F(SeveralArguments, GetCommandArgument) { 382 CheckArgumentValue(severalArgsArgv[0], 0); 383 CheckArgumentValue(severalArgsArgv[1], 1); 384 CheckArgumentValue(severalArgsArgv[3], 3); 385 CheckArgumentValue(severalArgsArgv[4], 4); 386 } 387 388 TEST_F(SeveralArguments, NoArgumentValue) { 389 // Make sure we don't crash if the 'value', 'length' and 'error' parameters 390 // aren't passed. 391 EXPECT_GT(RTNAME(GetCommandArgument)(2), 0); 392 EXPECT_EQ(RTNAME(GetCommandArgument)(1), 0); 393 EXPECT_GT(RTNAME(GetCommandArgument)(-1), 0); 394 } 395 396 TEST_F(SeveralArguments, MissingArguments) { 397 CheckMissingArgumentValue(-1, "Invalid argument number"); 398 CheckMissingArgumentValue(2, "Missing argument"); 399 CheckMissingArgumentValue(5, "Invalid argument number"); 400 CheckMissingArgumentValue(5); 401 } 402 403 TEST_F(SeveralArguments, ArgValueTooShort) { 404 OwningPtr<Descriptor> tooShort{CreateEmptyCharDescriptor<15>()}; 405 ASSERT_NE(tooShort, nullptr); 406 EXPECT_EQ(RTNAME(GetCommandArgument)(1, tooShort.get()), -1); 407 CheckDescriptorEqStr(tooShort.get(), severalArgsArgv[1]); 408 409 OwningPtr<Descriptor> length{EmptyIntDescriptor()}; 410 ASSERT_NE(length, nullptr); 411 OwningPtr<Descriptor> errMsg{CreateEmptyCharDescriptor()}; 412 ASSERT_NE(errMsg, nullptr); 413 414 EXPECT_EQ( 415 RTNAME(GetCommandArgument)(1, tooShort.get(), length.get(), errMsg.get()), 416 -1); 417 418 CheckDescriptorEqInt<std::int64_t>(length.get(), 16); 419 std::string expectedErrMsg{ 420 GetPaddedStr("Value too short", errMsg->ElementBytes())}; 421 CheckDescriptorEqStr(errMsg.get(), expectedErrMsg); 422 } 423 424 TEST_F(SeveralArguments, ArgErrMsgTooShort) { 425 OwningPtr<Descriptor> errMsg{CreateEmptyCharDescriptor<3>()}; 426 EXPECT_GT(RTNAME(GetCommandArgument)(-1, nullptr, nullptr, errMsg.get()), 0); 427 CheckDescriptorEqStr(errMsg.get(), "Inv"); 428 } 429 430 TEST_F(SeveralArguments, GetCommand) { 431 CheckMissingCommandValue(); 432 CheckMissingCommandValue("Missing argument"); 433 } 434 435 TEST_F(SeveralArguments, CommandErrMsgTooShort) { 436 OwningPtr<Descriptor> value{CreateEmptyCharDescriptor()}; 437 OwningPtr<Descriptor> length{EmptyIntDescriptor()}; 438 OwningPtr<Descriptor> errMsg{CreateEmptyCharDescriptor<3>()}; 439 440 EXPECT_GT(RTNAME(GetCommand)(value.get(), length.get(), errMsg.get()), 0); 441 442 std::string spaces(value->ElementBytes(), ' '); 443 CheckDescriptorEqStr(value.get(), spaces); 444 CheckDescriptorEqInt<std::int64_t>(length.get(), 0); 445 CheckDescriptorEqStr(errMsg.get(), "Mis"); 446 } 447 448 TEST_F(SeveralArguments, GetCommandCanTakeNull) { 449 EXPECT_GT(RTNAME(GetCommand)(nullptr, nullptr, nullptr), 0); 450 } 451 452 static const char *onlyValidArgsArgv[]{ 453 "aProgram", "-f", "has/a/few/slashes", "has\\a\\few\\backslashes"}; 454 class OnlyValidArguments : public CommandFixture { 455 protected: 456 OnlyValidArguments() 457 : CommandFixture(sizeof(onlyValidArgsArgv) / sizeof(*onlyValidArgsArgv), 458 onlyValidArgsArgv) {} 459 }; 460 461 TEST_F(OnlyValidArguments, GetCommand) { 462 CheckCommandValue(onlyValidArgsArgv, 4); 463 } 464 465 TEST_F(OnlyValidArguments, CommandValueTooShort) { 466 OwningPtr<Descriptor> tooShort{CreateEmptyCharDescriptor<50>()}; 467 ASSERT_NE(tooShort, nullptr); 468 OwningPtr<Descriptor> length{EmptyIntDescriptor()}; 469 ASSERT_NE(length, nullptr); 470 471 EXPECT_EQ(RTNAME(GetCommand)(tooShort.get(), length.get(), nullptr), -1); 472 473 CheckDescriptorEqStr( 474 tooShort.get(), "aProgram -f has/a/few/slashes has\\a\\few\\backslashe"); 475 CheckDescriptorEqInt<std::int64_t>(length.get(), 51); 476 477 OwningPtr<Descriptor> errMsg{CreateEmptyCharDescriptor()}; 478 ASSERT_NE(errMsg, nullptr); 479 480 EXPECT_EQ(-1, RTNAME(GetCommand)(tooShort.get(), nullptr, errMsg.get())); 481 482 std::string expectedErrMsg{ 483 GetPaddedStr("Value too short", errMsg->ElementBytes())}; 484 CheckDescriptorEqStr(errMsg.get(), expectedErrMsg); 485 } 486 487 TEST_F(OnlyValidArguments, GetCommandCanTakeNull) { 488 EXPECT_EQ(0, RTNAME(GetCommand)(nullptr, nullptr, nullptr)); 489 490 OwningPtr<Descriptor> value{CreateEmptyCharDescriptor()}; 491 ASSERT_NE(value, nullptr); 492 OwningPtr<Descriptor> length{EmptyIntDescriptor()}; 493 ASSERT_NE(length, nullptr); 494 495 EXPECT_EQ(0, RTNAME(GetCommand)(value.get(), nullptr, nullptr)); 496 CheckDescriptorEqStr(value.get(), 497 GetPaddedStr("aProgram -f has/a/few/slashes has\\a\\few\\backslashes", 498 value->ElementBytes())); 499 500 EXPECT_EQ(0, RTNAME(GetCommand)(nullptr, length.get(), nullptr)); 501 CheckDescriptorEqInt<std::int64_t>(length.get(), 51); 502 } 503 504 TEST_F(OnlyValidArguments, GetCommandShortLength) { 505 OwningPtr<Descriptor> length{EmptyIntDescriptor<sizeof(short)>()}; 506 ASSERT_NE(length, nullptr); 507 508 EXPECT_EQ(0, RTNAME(GetCommand)(nullptr, length.get(), nullptr)); 509 CheckDescriptorEqInt<short>(length.get(), 51); 510 } 511 512 TEST_F(ZeroArguments, GetPID) { 513 // pid should always greater than 0, in both linux and windows 514 EXPECT_GT(RTNAME(GetPID)(), 0); 515 } 516 517 class EnvironmentVariables : public CommandFixture { 518 protected: 519 EnvironmentVariables() : CommandFixture(0, nullptr) { 520 SetEnv("NAME", "VALUE"); 521 #ifdef _WIN32 522 SetEnv("USERNAME", "loginName"); 523 #else 524 SetEnv("LOGNAME", "loginName"); 525 #endif 526 SetEnv("EMPTY", ""); 527 } 528 529 // If we have access to setenv, we can run some more fine-grained tests. 530 template <typename ParamType = char> 531 void SetEnv(const ParamType *name, const ParamType *value, 532 decltype(setenv(name, value, 1)) *Enabled = nullptr) { 533 ASSERT_EQ(0, setenv(name, value, /*overwrite=*/1)); 534 canSetEnv = true; 535 } 536 537 // Fallback method if setenv is not available. 538 template <typename Unused = void> void SetEnv(const void *, const void *) {} 539 540 bool EnableFineGrainedTests() const { return canSetEnv; } 541 542 private: 543 bool canSetEnv{false}; 544 }; 545 546 TEST_F(EnvironmentVariables, Nonexistent) { 547 CheckMissingEnvVarValue("DOESNT_EXIST"); 548 CheckMissingEnvVarValue(" "); 549 CheckMissingEnvVarValue(""); 550 } 551 552 TEST_F(EnvironmentVariables, Basic) { 553 // Test a variable that's expected to exist in the environment. 554 char *path{std::getenv("PATH")}; 555 auto expectedLen{static_cast<int64_t>(std::strlen(path))}; 556 OwningPtr<Descriptor> length{EmptyIntDescriptor()}; 557 EXPECT_EQ(0, 558 RTNAME(GetEnvVariable)(*CharDescriptor("PATH"), 559 /*value=*/nullptr, length.get())); 560 CheckDescriptorEqInt(length.get(), expectedLen); 561 } 562 563 TEST_F(EnvironmentVariables, Trim) { 564 if (EnableFineGrainedTests()) { 565 CheckEnvVarValue("VALUE", "NAME "); 566 } 567 } 568 569 TEST_F(EnvironmentVariables, NoTrim) { 570 if (EnableFineGrainedTests()) { 571 CheckMissingEnvVarValue("NAME ", /*trim_name=*/false); 572 } 573 } 574 575 TEST_F(EnvironmentVariables, Empty) { 576 if (EnableFineGrainedTests()) { 577 CheckEnvVarValue("", "EMPTY"); 578 } 579 } 580 581 TEST_F(EnvironmentVariables, NoValueOrErrmsg) { 582 ASSERT_EQ(std::getenv("DOESNT_EXIST"), nullptr) 583 << "Environment variable DOESNT_EXIST actually exists"; 584 EXPECT_EQ(RTNAME(GetEnvVariable)(*CharDescriptor("DOESNT_EXIST")), 1); 585 586 if (EnableFineGrainedTests()) { 587 EXPECT_EQ(RTNAME(GetEnvVariable)(*CharDescriptor("NAME")), 0); 588 } 589 } 590 591 TEST_F(EnvironmentVariables, ValueTooShort) { 592 if (EnableFineGrainedTests()) { 593 OwningPtr<Descriptor> tooShort{CreateEmptyCharDescriptor<2>()}; 594 ASSERT_NE(tooShort, nullptr); 595 EXPECT_EQ(RTNAME(GetEnvVariable)(*CharDescriptor("NAME"), tooShort.get(), 596 /*length=*/nullptr, /*trim_name=*/true, nullptr), 597 -1); 598 CheckDescriptorEqStr(tooShort.get(), "VALUE"); 599 600 OwningPtr<Descriptor> errMsg{CreateEmptyCharDescriptor()}; 601 ASSERT_NE(errMsg, nullptr); 602 603 EXPECT_EQ(RTNAME(GetEnvVariable)(*CharDescriptor("NAME"), tooShort.get(), 604 /*length=*/nullptr, /*trim_name=*/true, errMsg.get()), 605 -1); 606 607 std::string expectedErrMsg{ 608 GetPaddedStr("Value too short", errMsg->ElementBytes())}; 609 CheckDescriptorEqStr(errMsg.get(), expectedErrMsg); 610 } 611 } 612 613 TEST_F(EnvironmentVariables, ErrMsgTooShort) { 614 ASSERT_EQ(std::getenv("DOESNT_EXIST"), nullptr) 615 << "Environment variable DOESNT_EXIST actually exists"; 616 617 OwningPtr<Descriptor> errMsg{CreateEmptyCharDescriptor<3>()}; 618 EXPECT_EQ(RTNAME(GetEnvVariable)(*CharDescriptor("DOESNT_EXIST"), nullptr, 619 /*length=*/nullptr, /*trim_name=*/true, errMsg.get()), 620 1); 621 CheckDescriptorEqStr(errMsg.get(), "Mis"); 622 } 623 624 // username first char must not be null 625 TEST_F(EnvironmentVariables, GetlogGetName) { 626 const int charLen{3}; 627 char input[charLen]{"\0\0"}; 628 629 FORTRAN_PROCEDURE_NAME(getlog) 630 (reinterpret_cast<std::byte *>(input), charLen); 631 632 EXPECT_NE(input[0], '\0'); 633 } 634 635 #if _REENTRANT || _POSIX_C_SOURCE >= 199506L 636 TEST_F(EnvironmentVariables, GetlogPadSpace) { 637 // guarantee 1 char longer than max, last char should be pad space 638 const int charLen{LOGIN_NAME_MAX + 2}; 639 char input[charLen]; 640 641 FORTRAN_PROCEDURE_NAME(getlog) 642 (reinterpret_cast<std::byte *>(input), charLen); 643 644 EXPECT_EQ(input[charLen - 1], ' '); 645 } 646 #endif 647 648 #ifdef _WIN32 // Test ability to get name from environment variable 649 TEST_F(EnvironmentVariables, GetlogEnvGetName) { 650 if (EnableFineGrainedTests()) { 651 ASSERT_NE(std::getenv("USERNAME"), nullptr) 652 << "Environment variable USERNAME does not exist"; 653 654 char input[]{"XXXXXXXXX"}; 655 FORTRAN_PROCEDURE_NAME(getlog) 656 (reinterpret_cast<std::byte *>(input), sizeof(input)); 657 658 CheckCharEqStr(input, "loginName"); 659 } 660 } 661 662 TEST_F(EnvironmentVariables, GetlogEnvBufferShort) { 663 if (EnableFineGrainedTests()) { 664 ASSERT_NE(std::getenv("USERNAME"), nullptr) 665 << "Environment variable USERNAME does not exist"; 666 667 char input[]{"XXXXXX"}; 668 FORTRAN_PROCEDURE_NAME(getlog) 669 (reinterpret_cast<std::byte *>(input), sizeof(input)); 670 671 CheckCharEqStr(input, "loginN"); 672 } 673 } 674 675 TEST_F(EnvironmentVariables, GetlogEnvPadSpace) { 676 if (EnableFineGrainedTests()) { 677 ASSERT_NE(std::getenv("USERNAME"), nullptr) 678 << "Environment variable USERNAME does not exist"; 679 680 char input[]{"XXXXXXXXXX"}; 681 FORTRAN_PROCEDURE_NAME(getlog) 682 (reinterpret_cast<std::byte *>(input), sizeof(input)); 683 684 CheckCharEqStr(input, "loginName "); 685 } 686 } 687 #endif 688