brintos

brintos / llvm-project-archived public Read only

0
0
Text · 28.4 KiB · 6919a98 Raw
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