1 //===-- flang/unittests/RuntimeGTest/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/main.h" 14 #include <cstdlib> 15 16 using namespace Fortran::runtime; 17 18 template <std::size_t n = 64> 19 static OwningPtr<Descriptor> CreateEmptyCharDescriptor() { 20 OwningPtr<Descriptor> descriptor{Descriptor::Create( 21 sizeof(char), n, nullptr, 0, nullptr, CFI_attribute_allocatable)}; 22 if (descriptor->Allocate() != 0) { 23 return nullptr; 24 } 25 return descriptor; 26 } 27 28 static OwningPtr<Descriptor> CharDescriptor(const char *value) { 29 std::size_t n{std::strlen(value)}; 30 OwningPtr<Descriptor> descriptor{Descriptor::Create( 31 sizeof(char), n, nullptr, 0, nullptr, CFI_attribute_allocatable)}; 32 if (descriptor->Allocate() != 0) { 33 return nullptr; 34 } 35 std::memcpy(descriptor->OffsetElement(), value, n); 36 return descriptor; 37 } 38 39 class CommandFixture : public ::testing::Test { 40 protected: 41 CommandFixture(int argc, const char *argv[]) { 42 RTNAME(ProgramStart)(argc, argv, {}); 43 } 44 45 std::string GetPaddedStr(const char *text, std::size_t len) const { 46 std::string res{text}; 47 assert(res.length() <= len && "No room to pad"); 48 res.append(len - res.length(), ' '); 49 return res; 50 } 51 52 void CheckDescriptorEqStr( 53 const Descriptor *value, const std::string &expected) const { 54 EXPECT_EQ(std::strncmp(value->OffsetElement(), expected.c_str(), 55 value->ElementBytes()), 56 0); 57 } 58 59 void CheckArgumentValue(int n, const char *argv) const { 60 OwningPtr<Descriptor> value{CreateEmptyCharDescriptor()}; 61 ASSERT_NE(value, nullptr); 62 63 std::string expected{GetPaddedStr(argv, value->ElementBytes())}; 64 65 EXPECT_EQ(RTNAME(ArgumentValue)(n, value.get(), nullptr), 0); 66 CheckDescriptorEqStr(value.get(), expected); 67 } 68 69 void CheckMissingArgumentValue(int n, const char *errStr = nullptr) const { 70 OwningPtr<Descriptor> value{CreateEmptyCharDescriptor()}; 71 ASSERT_NE(value, nullptr); 72 73 OwningPtr<Descriptor> err{errStr ? CreateEmptyCharDescriptor() : nullptr}; 74 75 EXPECT_GT(RTNAME(ArgumentValue)(n, value.get(), err.get()), 0); 76 77 std::string spaces(value->ElementBytes(), ' '); 78 CheckDescriptorEqStr(value.get(), spaces); 79 80 if (errStr) { 81 std::string paddedErrStr(GetPaddedStr(errStr, err->ElementBytes())); 82 CheckDescriptorEqStr(err.get(), paddedErrStr); 83 } 84 } 85 }; 86 87 static const char *commandOnlyArgv[]{"aProgram"}; 88 class ZeroArguments : public CommandFixture { 89 protected: 90 ZeroArguments() : CommandFixture(1, commandOnlyArgv) {} 91 }; 92 93 TEST_F(ZeroArguments, ArgumentCount) { EXPECT_EQ(0, RTNAME(ArgumentCount)()); } 94 95 TEST_F(ZeroArguments, ArgumentLength) { 96 EXPECT_EQ(0, RTNAME(ArgumentLength)(-1)); 97 EXPECT_EQ(8, RTNAME(ArgumentLength)(0)); 98 EXPECT_EQ(0, RTNAME(ArgumentLength)(1)); 99 } 100 101 TEST_F(ZeroArguments, ArgumentValue) { 102 CheckArgumentValue(0, commandOnlyArgv[0]); 103 } 104 105 static const char *oneArgArgv[]{"aProgram", "anArgumentOfLength20"}; 106 class OneArgument : public CommandFixture { 107 protected: 108 OneArgument() : CommandFixture(2, oneArgArgv) {} 109 }; 110 111 TEST_F(OneArgument, ArgumentCount) { EXPECT_EQ(1, RTNAME(ArgumentCount)()); } 112 113 TEST_F(OneArgument, ArgumentLength) { 114 EXPECT_EQ(0, RTNAME(ArgumentLength)(-1)); 115 EXPECT_EQ(8, RTNAME(ArgumentLength)(0)); 116 EXPECT_EQ(20, RTNAME(ArgumentLength)(1)); 117 EXPECT_EQ(0, RTNAME(ArgumentLength)(2)); 118 } 119 120 TEST_F(OneArgument, ArgumentValue) { 121 CheckArgumentValue(0, oneArgArgv[0]); 122 CheckArgumentValue(1, oneArgArgv[1]); 123 } 124 125 static const char *severalArgsArgv[]{ 126 "aProgram", "16-char-long-arg", "", "-22-character-long-arg", "o"}; 127 class SeveralArguments : public CommandFixture { 128 protected: 129 SeveralArguments() 130 : CommandFixture(sizeof(severalArgsArgv) / sizeof(*severalArgsArgv), 131 severalArgsArgv) {} 132 }; 133 134 TEST_F(SeveralArguments, ArgumentCount) { 135 EXPECT_EQ(4, RTNAME(ArgumentCount)()); 136 } 137 138 TEST_F(SeveralArguments, ArgumentLength) { 139 EXPECT_EQ(0, RTNAME(ArgumentLength)(-1)); 140 EXPECT_EQ(8, RTNAME(ArgumentLength)(0)); 141 EXPECT_EQ(16, RTNAME(ArgumentLength)(1)); 142 EXPECT_EQ(0, RTNAME(ArgumentLength)(2)); 143 EXPECT_EQ(22, RTNAME(ArgumentLength)(3)); 144 EXPECT_EQ(1, RTNAME(ArgumentLength)(4)); 145 EXPECT_EQ(0, RTNAME(ArgumentLength)(5)); 146 } 147 148 TEST_F(SeveralArguments, ArgumentValue) { 149 CheckArgumentValue(0, severalArgsArgv[0]); 150 CheckArgumentValue(1, severalArgsArgv[1]); 151 CheckArgumentValue(3, severalArgsArgv[3]); 152 CheckArgumentValue(4, severalArgsArgv[4]); 153 } 154 155 TEST_F(SeveralArguments, NoArgumentValue) { 156 // Make sure we don't crash if the 'value' and 'error' parameters aren't 157 // passed. 158 EXPECT_EQ(RTNAME(ArgumentValue)(2, nullptr, nullptr), 0); 159 EXPECT_GT(RTNAME(ArgumentValue)(-1, nullptr, nullptr), 0); 160 } 161 162 TEST_F(SeveralArguments, MissingArguments) { 163 CheckMissingArgumentValue(-1, "Invalid argument number"); 164 CheckMissingArgumentValue(2, "Missing argument"); 165 CheckMissingArgumentValue(5, "Invalid argument number"); 166 CheckMissingArgumentValue(5); 167 } 168 169 TEST_F(SeveralArguments, ValueTooShort) { 170 OwningPtr<Descriptor> tooShort{CreateEmptyCharDescriptor<15>()}; 171 ASSERT_NE(tooShort, nullptr); 172 EXPECT_EQ(RTNAME(ArgumentValue)(1, tooShort.get(), nullptr), -1); 173 CheckDescriptorEqStr(tooShort.get(), severalArgsArgv[1]); 174 175 OwningPtr<Descriptor> errMsg{CreateEmptyCharDescriptor()}; 176 ASSERT_NE(errMsg, nullptr); 177 178 EXPECT_EQ(RTNAME(ArgumentValue)(1, tooShort.get(), errMsg.get()), -1); 179 180 std::string expectedErrMsg{ 181 GetPaddedStr("Value too short", errMsg->ElementBytes())}; 182 CheckDescriptorEqStr(errMsg.get(), expectedErrMsg); 183 } 184 185 TEST_F(SeveralArguments, ErrMsgTooShort) { 186 OwningPtr<Descriptor> errMsg{CreateEmptyCharDescriptor<3>()}; 187 EXPECT_GT(RTNAME(ArgumentValue)(-1, nullptr, errMsg.get()), 0); 188 CheckDescriptorEqStr(errMsg.get(), "Inv"); 189 } 190 191 class EnvironmentVariables : public CommandFixture { 192 protected: 193 EnvironmentVariables() : CommandFixture(0, nullptr) { 194 SetEnv("NAME", "VALUE"); 195 } 196 197 // If we have access to setenv, we can run some more fine-grained tests. 198 template <typename ParamType = char> 199 void SetEnv(const ParamType *name, const ParamType *value, 200 decltype(setenv(name, value, 1)) *Enabled = nullptr) { 201 ASSERT_EQ(0, setenv(name, value, /*overwrite=*/1)); 202 canSetEnv = true; 203 } 204 205 // Fallback method if setenv is not available. 206 template <typename Unused = void> void SetEnv(const void *, const void *) {} 207 208 bool EnableFineGrainedTests() const { return canSetEnv; } 209 210 private: 211 bool canSetEnv{false}; 212 }; 213 214 TEST_F(EnvironmentVariables, Length) { 215 EXPECT_EQ(0, RTNAME(EnvVariableLength)(*CharDescriptor("DOESNT_EXIST"))); 216 217 EXPECT_EQ(0, RTNAME(EnvVariableLength)(*CharDescriptor(" "))); 218 EXPECT_EQ(0, RTNAME(EnvVariableLength)(*CharDescriptor(""))); 219 220 // Test a variable that's expected to exist in the environment. 221 char *path{std::getenv("PATH")}; 222 auto expectedLen{static_cast<int64_t>(std::strlen(path))}; 223 EXPECT_EQ(expectedLen, RTNAME(EnvVariableLength)(*CharDescriptor("PATH"))); 224 225 if (EnableFineGrainedTests()) { 226 EXPECT_EQ(5, RTNAME(EnvVariableLength)(*CharDescriptor("NAME"))); 227 228 EXPECT_EQ(5, RTNAME(EnvVariableLength)(*CharDescriptor("NAME "))); 229 EXPECT_EQ(0, 230 RTNAME(EnvVariableLength)( 231 *CharDescriptor("NAME "), /*trim_name=*/false)); 232 } 233 } 234