861 lines · cpp
1//===-- unittests/Runtime/CommandTest.cpp -----------------------*- C++ -*-===//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-exception6//7//===----------------------------------------------------------------------===//8 9#include "flang/Runtime/command.h"10#include "gmock/gmock.h"11#include "gtest/gtest.h"12#include "flang-rt/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 >= 199506L20#include <limits.h> // LOGIN_NAME_MAX used in getlog test21#endif22 23using namespace Fortran::runtime;24 25template <std::size_t n = 64>26static OwningPtr<Descriptor> CreateEmptyCharDescriptor() {27 OwningPtr<Descriptor> descriptor{Descriptor::Create(28 sizeof(char), n, nullptr, 0, nullptr, CFI_attribute_allocatable)};29 if (descriptor->Allocate(kNoAsyncObject) != 0) {30 return nullptr;31 }32 return descriptor;33}34 35static 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(kNoAsyncObject) != 0) {40 return nullptr;41 }42 std::memcpy(descriptor->OffsetElement(), value, n);43 return descriptor;44}45 46template <int kind = sizeof(std::int64_t)>47static OwningPtr<Descriptor> EmptyIntDescriptor() {48 OwningPtr<Descriptor> descriptor{Descriptor::Create(TypeCategory::Integer,49 kind, nullptr, 0, nullptr, CFI_attribute_allocatable)};50 if (descriptor->Allocate(kNoAsyncObject) != 0) {51 return nullptr;52 }53 return descriptor;54}55 56template <int kind = sizeof(std::int64_t)>57static 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(kNoAsyncObject) != 0) {61 return nullptr;62 }63 std::memcpy(descriptor->OffsetElement<int>(), &value, sizeof(int));64 return descriptor;65}66 67class CommandFixture : public ::testing::Test {68protected: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 230class NoArgv : public CommandFixture {231protected:232 NoArgv() : CommandFixture(0, nullptr) {}233};234 235#if _WIN32 || _POSIX_C_SOURCE >= 1 || _XOPEN_SOURCE || _BSD_SOURCE || \236 _SVID_SOURCE || defined(_POSIX_SOURCE)237TEST_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\0244 // index at 3, 7, 10, 19 should be space245 // when date is less than two digit, index 8 would be space246 // Tue May 6 21:51:03 2015\n\0247 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 258TEST_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 269TEST_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#else281TEST_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#endif290 291// TODO: Test other intrinsics with this fixture.292 293TEST_F(NoArgv, GetCommand) { CheckMissingCommandValue(); }294 295static const char *commandOnlyArgv[]{"aProgram"};296class ZeroArguments : public CommandFixture {297protected:298 ZeroArguments() : CommandFixture(1, commandOnlyArgv) {}299};300 301TEST_F(ZeroArguments, ArgumentCount) { EXPECT_EQ(0, RTNAME(ArgumentCount)()); }302 303TEST_F(ZeroArguments, GetCommandArgument) {304 CheckMissingArgumentValue(-1);305 CheckArgumentValue(commandOnlyArgv[0], 0);306 CheckMissingArgumentValue(1);307}308 309TEST_F(ZeroArguments, GetCommand) { CheckCommandValue(commandOnlyArgv, 1); }310 311TEST_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 327TEST_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 342TEST_F(ZeroArguments, ECLGeneralErrorCommandErrorSync) {343 OwningPtr<Descriptor> command{CharDescriptor(NOT_EXE)};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 CheckDescriptorEqInt<std::int64_t>(exitStat.get(), 1);352#if defined(_WIN32)353 CheckDescriptorEqInt<std::int64_t>(cmdStat.get(), 6);354 CheckDescriptorEqStr(cmdMsg.get(), "Invalid command lineXXXXXXXXX");355#else356 CheckDescriptorEqInt<std::int64_t>(cmdStat.get(), 3);357 CheckDescriptorEqStr(cmdMsg.get(), "Command line execution failed");358#endif359}360 361TEST_F(ZeroArguments, ECLNotExecutedCommandErrorSync) {362 OwningPtr<Descriptor> command{CharDescriptor(363 "touch NotExecutedCommandFile && chmod -x NotExecutedCommandFile && "364 "./NotExecutedCommandFile")};365 bool wait{true};366 OwningPtr<Descriptor> exitStat{IntDescriptor(404)};367 OwningPtr<Descriptor> cmdStat{IntDescriptor(202)};368 OwningPtr<Descriptor> cmdMsg{CharDescriptor("cmd msg buffer XXXXXXXX")};369 370 RTNAME(ExecuteCommandLine)371 (*command.get(), wait, exitStat.get(), cmdStat.get(), cmdMsg.get());372#ifdef _WIN32373 CheckDescriptorEqInt<std::int64_t>(exitStat.get(), 1);374 CheckDescriptorEqInt<std::int64_t>(cmdStat.get(), 6);375 CheckDescriptorEqStr(cmdMsg.get(), "Invalid command lineXXX");376#else377 CheckDescriptorEqInt<std::int64_t>(exitStat.get(), 126);378 CheckDescriptorEqInt<std::int64_t>(cmdStat.get(), 4);379 CheckDescriptorEqStr(cmdMsg.get(), "Command cannot be execu");380 // removing the file only on Linux (file is not created on Win)381 OwningPtr<Descriptor> commandClean{382 CharDescriptor("rm -f NotExecutedCommandFile")};383 OwningPtr<Descriptor> cmdMsgNoErr{CharDescriptor("No Error")};384 RTNAME(ExecuteCommandLine)385 (*commandClean.get(), wait, exitStat.get(), cmdStat.get(), cmdMsgNoErr.get());386 CheckDescriptorEqInt<std::int64_t>(exitStat.get(), 0);387 CheckDescriptorEqStr(cmdMsgNoErr.get(), "No Error");388 CheckDescriptorEqInt<std::int64_t>(cmdStat.get(), 0);389#endif390}391 392TEST_F(ZeroArguments, ECLNotFoundCommandErrorSync) {393 OwningPtr<Descriptor> command{CharDescriptor("NotFoundCommand")};394 bool wait{true};395 OwningPtr<Descriptor> exitStat{IntDescriptor(404)};396 OwningPtr<Descriptor> cmdStat{IntDescriptor(202)};397 OwningPtr<Descriptor> cmdMsg{CharDescriptor("unmodified buffer XXXXXXXXX")};398 399 RTNAME(ExecuteCommandLine)400 (*command.get(), wait, exitStat.get(), cmdStat.get(), cmdMsg.get());401#ifdef _WIN32402 CheckDescriptorEqInt<std::int64_t>(exitStat.get(), 1);403 CheckDescriptorEqInt<std::int64_t>(cmdStat.get(), 6);404 CheckDescriptorEqStr(cmdMsg.get(), "Invalid command lineXXXXXXX");405#else406 CheckDescriptorEqInt<std::int64_t>(exitStat.get(), 127);407 CheckDescriptorEqInt<std::int64_t>(cmdStat.get(), 5);408 CheckDescriptorEqStr(cmdMsg.get(), "Command not found with exit");409#endif410}411 412TEST_F(ZeroArguments, ECLInvalidCommandTerminatedSync) {413 OwningPtr<Descriptor> command{CharDescriptor("InvalidCommand")};414 bool wait{true};415 OwningPtr<Descriptor> cmdMsg{CharDescriptor("No Change")};416 417#ifdef _WIN32418 EXPECT_DEATH(RTNAME(ExecuteCommandLine)(419 *command.get(), wait, nullptr, nullptr, cmdMsg.get()),420 "Invalid command quit with exit status code: 1");421#else422 EXPECT_DEATH(RTNAME(ExecuteCommandLine)(423 *command.get(), wait, nullptr, nullptr, cmdMsg.get()),424 "Command not found with exit code: 127.");425#endif426 CheckDescriptorEqStr(cmdMsg.get(), "No Change");427}428 429TEST_F(ZeroArguments, ECLValidCommandAndExitStatNoChangeAndCMDStatusSetAsync) {430 OwningPtr<Descriptor> command{CharDescriptor("echo hi")};431 bool wait{false};432 OwningPtr<Descriptor> exitStat{IntDescriptor(404)};433 OwningPtr<Descriptor> cmdStat{IntDescriptor(202)};434 OwningPtr<Descriptor> cmdMsg{CharDescriptor("No change")};435 436 RTNAME(ExecuteCommandLine)437 (*command.get(), wait, exitStat.get(), cmdStat.get(), cmdMsg.get());438 439 CheckDescriptorEqInt(exitStat.get(), 404);440 CheckDescriptorEqInt<std::int64_t>(cmdStat.get(), 0);441 CheckDescriptorEqStr(cmdMsg.get(), "No change");442}443 444TEST_F(ZeroArguments, ECLInvalidCommandParentNotTerminatedAsync) {445 OwningPtr<Descriptor> command{CharDescriptor("InvalidCommand")};446 bool wait{false};447 OwningPtr<Descriptor> cmdMsg{CharDescriptor("No change")};448 449 EXPECT_NO_FATAL_FAILURE(RTNAME(ExecuteCommandLine)(450 *command.get(), wait, nullptr, nullptr, cmdMsg.get()));451 CheckDescriptorEqStr(cmdMsg.get(), "No change");452}453 454TEST_F(ZeroArguments, ECLInvalidCommandAsyncDontAffectSync) {455 OwningPtr<Descriptor> command{CharDescriptor("echo hi")};456 457 EXPECT_NO_FATAL_FAILURE(RTNAME(ExecuteCommandLine)(458 *command.get(), false, nullptr, nullptr, nullptr));459 EXPECT_NO_FATAL_FAILURE(RTNAME(ExecuteCommandLine)(460 *command.get(), true, nullptr, nullptr, nullptr));461}462 463TEST_F(ZeroArguments, ECLInvalidCommandAsyncDontAffectAsync) {464 OwningPtr<Descriptor> command{CharDescriptor("echo hi")};465 466 EXPECT_NO_FATAL_FAILURE(RTNAME(ExecuteCommandLine)(467 *command.get(), false, nullptr, nullptr, nullptr));468 EXPECT_NO_FATAL_FAILURE(RTNAME(ExecuteCommandLine)(469 *command.get(), false, nullptr, nullptr, nullptr));470}471 472TEST_F(ZeroArguments, SystemValidCommandExitStat) {473 // envrionment setup for SYSTEM from EXECUTE_COMMAND_LINE runtime474 OwningPtr<Descriptor> cmdStat{IntDescriptor(202)};475 bool wait{true};476 // setup finished477 478 OwningPtr<Descriptor> command{CharDescriptor("echo hi")};479 OwningPtr<Descriptor> exitStat{EmptyIntDescriptor()};480 481 RTNAME(ExecuteCommandLine)482 (*command.get(), wait, exitStat.get(), cmdStat.get(), nullptr);483 CheckDescriptorEqInt<std::int64_t>(exitStat.get(), 0);484}485 486TEST_F(ZeroArguments, SystemInvalidCommandExitStat) {487 // envrionment setup for SYSTEM from EXECUTE_COMMAND_LINE runtime488 OwningPtr<Descriptor> cmdStat{IntDescriptor(202)};489 bool wait{true};490 // setup finished491 492 OwningPtr<Descriptor> command{CharDescriptor("InvalidCommand")};493 OwningPtr<Descriptor> exitStat{EmptyIntDescriptor()};494 495 RTNAME(ExecuteCommandLine)496 (*command.get(), wait, exitStat.get(), cmdStat.get(), nullptr);497#ifdef _WIN32498 CheckDescriptorEqInt<std::int64_t>(exitStat.get(), 1);499#else500 CheckDescriptorEqInt<std::int64_t>(exitStat.get(), 127);501#endif502}503 504TEST_F(ZeroArguments, SystemValidCommandOptionalExitStat) {505 // envrionment setup for SYSTEM from EXECUTE_COMMAND_LINE runtime506 OwningPtr<Descriptor> cmdStat{IntDescriptor(202)};507 bool wait{true};508 // setup finished509 510 OwningPtr<Descriptor> command{CharDescriptor("echo hi")};511 EXPECT_NO_FATAL_FAILURE(RTNAME(ExecuteCommandLine)(512 *command.get(), wait, nullptr, cmdStat.get(), nullptr));513}514 515TEST_F(ZeroArguments, SystemInvalidCommandOptionalExitStat) {516 // envrionment setup for SYSTEM from EXECUTE_COMMAND_LINE runtime517 OwningPtr<Descriptor> cmdStat{IntDescriptor(202)};518 bool wait{true};519 // setup finished520 521 OwningPtr<Descriptor> command{CharDescriptor("InvalidCommand")};522 EXPECT_NO_FATAL_FAILURE(RTNAME(ExecuteCommandLine)(523 *command.get(), wait, nullptr, cmdStat.get(), nullptr););524}525 526static const char *oneArgArgv[]{"aProgram", "anArgumentOfLength20"};527class OneArgument : public CommandFixture {528protected:529 OneArgument() : CommandFixture(2, oneArgArgv) {}530};531 532TEST_F(OneArgument, ArgumentCount) { EXPECT_EQ(1, RTNAME(ArgumentCount)()); }533 534TEST_F(OneArgument, GetCommandArgument) {535 CheckMissingArgumentValue(-1);536 CheckArgumentValue(oneArgArgv[0], 0);537 CheckArgumentValue(oneArgArgv[1], 1);538 CheckMissingArgumentValue(2);539}540 541TEST_F(OneArgument, GetCommand) { CheckCommandValue(oneArgArgv, 2); }542 543static const char *severalArgsArgv[]{544 "aProgram", "16-char-long-arg", "", "-22-character-long-arg", "o"};545class SeveralArguments : public CommandFixture {546protected:547 SeveralArguments()548 : CommandFixture(sizeof(severalArgsArgv) / sizeof(*severalArgsArgv),549 severalArgsArgv) {}550};551 552TEST_F(SeveralArguments, ArgumentCount) {553 EXPECT_EQ(4, RTNAME(ArgumentCount)());554}555 556TEST_F(SeveralArguments, GetCommandArgument) {557 CheckArgumentValue(severalArgsArgv[0], 0);558 CheckArgumentValue(severalArgsArgv[1], 1);559 CheckArgumentValue(severalArgsArgv[3], 3);560 CheckArgumentValue(severalArgsArgv[4], 4);561}562 563TEST_F(SeveralArguments, NoArgumentValue) {564 // Make sure we don't crash if the 'value', 'length' and 'error' parameters565 // aren't passed.566 EXPECT_GT(RTNAME(GetCommandArgument)(2), 0);567 EXPECT_EQ(RTNAME(GetCommandArgument)(1), 0);568 EXPECT_GT(RTNAME(GetCommandArgument)(-1), 0);569}570 571TEST_F(SeveralArguments, MissingArguments) {572 CheckMissingArgumentValue(-1, "Invalid argument number");573 CheckMissingArgumentValue(2, "Missing argument");574 CheckMissingArgumentValue(5, "Invalid argument number");575 CheckMissingArgumentValue(5);576}577 578TEST_F(SeveralArguments, ArgValueTooShort) {579 OwningPtr<Descriptor> tooShort{CreateEmptyCharDescriptor<15>()};580 ASSERT_NE(tooShort, nullptr);581 EXPECT_EQ(RTNAME(GetCommandArgument)(1, tooShort.get()), -1);582 CheckDescriptorEqStr(tooShort.get(), severalArgsArgv[1]);583 584 OwningPtr<Descriptor> length{EmptyIntDescriptor()};585 ASSERT_NE(length, nullptr);586 OwningPtr<Descriptor> errMsg{CreateEmptyCharDescriptor()};587 ASSERT_NE(errMsg, nullptr);588 589 EXPECT_EQ(590 RTNAME(GetCommandArgument)(1, tooShort.get(), length.get(), errMsg.get()),591 -1);592 593 CheckDescriptorEqInt<std::int64_t>(length.get(), 16);594 std::string expectedErrMsg{595 GetPaddedStr("Value too short", errMsg->ElementBytes())};596 CheckDescriptorEqStr(errMsg.get(), expectedErrMsg);597}598 599TEST_F(SeveralArguments, ArgErrMsgTooShort) {600 OwningPtr<Descriptor> errMsg{CreateEmptyCharDescriptor<3>()};601 EXPECT_GT(RTNAME(GetCommandArgument)(-1, nullptr, nullptr, errMsg.get()), 0);602 CheckDescriptorEqStr(errMsg.get(), "Inv");603}604 605TEST_F(SeveralArguments, GetCommand) {606 CheckMissingCommandValue();607 CheckMissingCommandValue("Missing argument");608}609 610TEST_F(SeveralArguments, CommandErrMsgTooShort) {611 OwningPtr<Descriptor> value{CreateEmptyCharDescriptor()};612 OwningPtr<Descriptor> length{EmptyIntDescriptor()};613 OwningPtr<Descriptor> errMsg{CreateEmptyCharDescriptor<3>()};614 615 EXPECT_GT(RTNAME(GetCommand)(value.get(), length.get(), errMsg.get()), 0);616 617 std::string spaces(value->ElementBytes(), ' ');618 CheckDescriptorEqStr(value.get(), spaces);619 CheckDescriptorEqInt<std::int64_t>(length.get(), 0);620 CheckDescriptorEqStr(errMsg.get(), "Mis");621}622 623TEST_F(SeveralArguments, GetCommandCanTakeNull) {624 EXPECT_GT(RTNAME(GetCommand)(nullptr, nullptr, nullptr), 0);625}626 627static const char *onlyValidArgsArgv[]{628 "aProgram", "-f", "has/a/few/slashes", "has\\a\\few\\backslashes"};629class OnlyValidArguments : public CommandFixture {630protected:631 OnlyValidArguments()632 : CommandFixture(sizeof(onlyValidArgsArgv) / sizeof(*onlyValidArgsArgv),633 onlyValidArgsArgv) {}634};635 636TEST_F(OnlyValidArguments, GetCommand) {637 CheckCommandValue(onlyValidArgsArgv, 4);638}639 640TEST_F(OnlyValidArguments, CommandValueTooShort) {641 OwningPtr<Descriptor> tooShort{CreateEmptyCharDescriptor<50>()};642 ASSERT_NE(tooShort, nullptr);643 OwningPtr<Descriptor> length{EmptyIntDescriptor()};644 ASSERT_NE(length, nullptr);645 646 EXPECT_EQ(RTNAME(GetCommand)(tooShort.get(), length.get(), nullptr), -1);647 648 CheckDescriptorEqStr(649 tooShort.get(), "aProgram -f has/a/few/slashes has\\a\\few\\backslashe");650 CheckDescriptorEqInt<std::int64_t>(length.get(), 51);651 652 OwningPtr<Descriptor> errMsg{CreateEmptyCharDescriptor()};653 ASSERT_NE(errMsg, nullptr);654 655 EXPECT_EQ(-1, RTNAME(GetCommand)(tooShort.get(), nullptr, errMsg.get()));656 657 std::string expectedErrMsg{658 GetPaddedStr("Value too short", errMsg->ElementBytes())};659 CheckDescriptorEqStr(errMsg.get(), expectedErrMsg);660}661 662TEST_F(OnlyValidArguments, GetCommandCanTakeNull) {663 EXPECT_EQ(0, RTNAME(GetCommand)(nullptr, nullptr, nullptr));664 665 OwningPtr<Descriptor> value{CreateEmptyCharDescriptor()};666 ASSERT_NE(value, nullptr);667 OwningPtr<Descriptor> length{EmptyIntDescriptor()};668 ASSERT_NE(length, nullptr);669 670 EXPECT_EQ(0, RTNAME(GetCommand)(value.get(), nullptr, nullptr));671 CheckDescriptorEqStr(value.get(),672 GetPaddedStr("aProgram -f has/a/few/slashes has\\a\\few\\backslashes",673 value->ElementBytes()));674 675 EXPECT_EQ(0, RTNAME(GetCommand)(nullptr, length.get(), nullptr));676 CheckDescriptorEqInt<std::int64_t>(length.get(), 51);677}678 679TEST_F(OnlyValidArguments, GetCommandShortLength) {680 OwningPtr<Descriptor> length{EmptyIntDescriptor<sizeof(short)>()};681 ASSERT_NE(length, nullptr);682 683 EXPECT_EQ(0, RTNAME(GetCommand)(nullptr, length.get(), nullptr));684 CheckDescriptorEqInt<short>(length.get(), 51);685}686 687TEST_F(ZeroArguments, GetPID) {688 // pid should always greater than 0, in both linux and windows689 EXPECT_GT(RTNAME(GetPID)(), 0);690}691 692class EnvironmentVariables : public CommandFixture {693protected:694 EnvironmentVariables() : CommandFixture(0, nullptr) {695 SetEnv("NAME", "VALUE");696#ifdef _WIN32697 SetEnv("USERNAME", "loginName");698#else699 SetEnv("LOGNAME", "loginName");700#endif701 SetEnv("EMPTY", "");702 }703 704 // If we have access to setenv, we can run some more fine-grained tests.705 template <typename ParamType = char>706 void SetEnv(const ParamType *name, const ParamType *value,707 decltype(setenv(name, value, 1)) *Enabled = nullptr) {708 ASSERT_EQ(0, setenv(name, value, /*overwrite=*/1));709 canSetEnv = true;710 }711 712 // Fallback method if setenv is not available.713 template <typename Unused = void> void SetEnv(const void *, const void *) {}714 715 bool EnableFineGrainedTests() const { return canSetEnv; }716 717private:718 bool canSetEnv{false};719};720 721TEST_F(EnvironmentVariables, Nonexistent) {722 CheckMissingEnvVarValue("DOESNT_EXIST");723 CheckMissingEnvVarValue(" ");724 CheckMissingEnvVarValue("");725}726 727TEST_F(EnvironmentVariables, Basic) {728 // Test a variable that's expected to exist in the environment.729 char *path{std::getenv("PATH")};730 auto expectedLen{static_cast<int64_t>(std::strlen(path))};731 OwningPtr<Descriptor> length{EmptyIntDescriptor()};732 EXPECT_EQ(0,733 RTNAME(GetEnvVariable)(*CharDescriptor("PATH"),734 /*value=*/nullptr, length.get()));735 CheckDescriptorEqInt(length.get(), expectedLen);736}737 738TEST_F(EnvironmentVariables, Trim) {739 if (EnableFineGrainedTests()) {740 CheckEnvVarValue("VALUE", "NAME ");741 }742}743 744TEST_F(EnvironmentVariables, NoTrim) {745 if (EnableFineGrainedTests()) {746 CheckMissingEnvVarValue("NAME ", /*trim_name=*/false);747 }748}749 750TEST_F(EnvironmentVariables, Empty) {751 if (EnableFineGrainedTests()) {752 CheckEnvVarValue("", "EMPTY");753 }754}755 756TEST_F(EnvironmentVariables, NoValueOrErrmsg) {757 ASSERT_EQ(std::getenv("DOESNT_EXIST"), nullptr)758 << "Environment variable DOESNT_EXIST actually exists";759 EXPECT_EQ(RTNAME(GetEnvVariable)(*CharDescriptor("DOESNT_EXIST")), 1);760 761 if (EnableFineGrainedTests()) {762 EXPECT_EQ(RTNAME(GetEnvVariable)(*CharDescriptor("NAME")), 0);763 }764}765 766TEST_F(EnvironmentVariables, ValueTooShort) {767 if (EnableFineGrainedTests()) {768 OwningPtr<Descriptor> tooShort{CreateEmptyCharDescriptor<2>()};769 ASSERT_NE(tooShort, nullptr);770 EXPECT_EQ(RTNAME(GetEnvVariable)(*CharDescriptor("NAME"), tooShort.get(),771 /*length=*/nullptr, /*trim_name=*/true, nullptr),772 -1);773 CheckDescriptorEqStr(tooShort.get(), "VALUE");774 775 OwningPtr<Descriptor> errMsg{CreateEmptyCharDescriptor()};776 ASSERT_NE(errMsg, nullptr);777 778 EXPECT_EQ(RTNAME(GetEnvVariable)(*CharDescriptor("NAME"), tooShort.get(),779 /*length=*/nullptr, /*trim_name=*/true, errMsg.get()),780 -1);781 782 std::string expectedErrMsg{783 GetPaddedStr("Value too short", errMsg->ElementBytes())};784 CheckDescriptorEqStr(errMsg.get(), expectedErrMsg);785 }786}787 788TEST_F(EnvironmentVariables, ErrMsgTooShort) {789 ASSERT_EQ(std::getenv("DOESNT_EXIST"), nullptr)790 << "Environment variable DOESNT_EXIST actually exists";791 792 OwningPtr<Descriptor> errMsg{CreateEmptyCharDescriptor<3>()};793 EXPECT_EQ(RTNAME(GetEnvVariable)(*CharDescriptor("DOESNT_EXIST"), nullptr,794 /*length=*/nullptr, /*trim_name=*/true, errMsg.get()),795 1);796 CheckDescriptorEqStr(errMsg.get(), "Mis");797}798 799// username first char must not be null800TEST_F(EnvironmentVariables, GetlogGetName) {801 const int charLen{3};802 char input[charLen]{"\0\0"};803 FORTRAN_PROCEDURE_NAME(getlog)(input, charLen);804 EXPECT_NE(input[0], '\0');805}806 807#if _REENTRANT || _POSIX_C_SOURCE >= 199506L808TEST_F(EnvironmentVariables, GetlogPadSpace) {809 // guarantee 1 char longer than max, last char should be pad space810 int charLen;811#ifdef LOGIN_NAME_MAX812 charLen = LOGIN_NAME_MAX + 2;813#else814 charLen = sysconf(_SC_LOGIN_NAME_MAX) + 2;815 if (charLen == -1)816 charLen = _POSIX_LOGIN_NAME_MAX + 2;817#endif818 std::vector<char> input(charLen);819 FORTRAN_PROCEDURE_NAME(getlog)(input.data(), charLen);820 EXPECT_EQ(input[charLen - 1], ' ');821}822#endif823 824#ifdef _WIN32 // Test ability to get name from environment variable825TEST_F(EnvironmentVariables, GetlogEnvGetName) {826 if (EnableFineGrainedTests()) {827 ASSERT_NE(std::getenv("USERNAME"), nullptr)828 << "Environment variable USERNAME does not exist";829 830 char input[]{"XXXXXXXXX"};831 FORTRAN_PROCEDURE_NAME(getlog)(input, sizeof(input));832 833 CheckCharEqStr(input, "loginName");834 }835}836 837TEST_F(EnvironmentVariables, GetlogEnvBufferShort) {838 if (EnableFineGrainedTests()) {839 ASSERT_NE(std::getenv("USERNAME"), nullptr)840 << "Environment variable USERNAME does not exist";841 842 char input[]{"XXXXXX"};843 FORTRAN_PROCEDURE_NAME(getlog)(input, sizeof(input));844 845 CheckCharEqStr(input, "loginN");846 }847}848 849TEST_F(EnvironmentVariables, GetlogEnvPadSpace) {850 if (EnableFineGrainedTests()) {851 ASSERT_NE(std::getenv("USERNAME"), nullptr)852 << "Environment variable USERNAME does not exist";853 854 char input[]{"XXXXXXXXXX"};855 FORTRAN_PROCEDURE_NAME(getlog)(input, sizeof(input));856 857 CheckCharEqStr(input, "loginName ");858 }859}860#endif861