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 << "expected: " << expected << "\n" 58 << "value: " 59 << std::string{value->OffsetElement(), value->ElementBytes()}; 60 } 61 62 template <typename RuntimeCall> 63 void CheckValue(RuntimeCall F, const char *expectedValue, 64 std::int32_t expectedStatus = 0, 65 const char *expectedErrMsg = "shouldn't change") const { 66 OwningPtr<Descriptor> value{CreateEmptyCharDescriptor()}; 67 ASSERT_NE(value, nullptr); 68 69 OwningPtr<Descriptor> errmsg{CharDescriptor(expectedErrMsg)}; 70 71 std::string expectedValueStr{ 72 GetPaddedStr(expectedValue, value->ElementBytes())}; 73 74 EXPECT_EQ(F(value.get(), errmsg.get()), expectedStatus); 75 CheckDescriptorEqStr(value.get(), expectedValueStr); 76 CheckDescriptorEqStr(errmsg.get(), expectedErrMsg); 77 } 78 79 void CheckArgumentValue(const char *expectedValue, int n) const { 80 SCOPED_TRACE(n); 81 SCOPED_TRACE("Checking argument:"); 82 CheckValue( 83 [&](const Descriptor *value, const Descriptor *errmsg) { 84 return RTNAME(ArgumentValue)(n, value, errmsg); 85 }, 86 expectedValue); 87 } 88 89 void CheckEnvVarValue( 90 const char *expectedValue, const char *name, bool trimName = true) const { 91 SCOPED_TRACE(name); 92 SCOPED_TRACE("Checking environment variable"); 93 CheckValue( 94 [&](const Descriptor *value, const Descriptor *errmsg) { 95 return RTNAME(EnvVariableValue)(*CharDescriptor(name), value, 96 trimName, errmsg, /*sourceFile=*/nullptr, /*line=*/0); 97 }, 98 expectedValue); 99 } 100 101 void CheckMissingEnvVarValue(const char *name, bool trimName = true) const { 102 SCOPED_TRACE(name); 103 SCOPED_TRACE("Checking missing environment variable"); 104 105 ASSERT_EQ(nullptr, std::getenv(name)) 106 << "Environment variable " << name << " not expected to exist"; 107 108 OwningPtr<Descriptor> nameDescriptor{CharDescriptor(name)}; 109 EXPECT_EQ(0, RTNAME(EnvVariableLength)(*nameDescriptor, trimName)); 110 CheckValue( 111 [&](const Descriptor *value, const Descriptor *errmsg) { 112 return RTNAME(EnvVariableValue)(*nameDescriptor, value, trimName, 113 errmsg, /*sourceFile=*/nullptr, /*line=*/0); 114 }, 115 "", 1, "Missing environment variable"); 116 } 117 118 void CheckMissingArgumentValue(int n, const char *errStr = nullptr) const { 119 OwningPtr<Descriptor> value{CreateEmptyCharDescriptor()}; 120 ASSERT_NE(value, nullptr); 121 122 OwningPtr<Descriptor> err{errStr ? CreateEmptyCharDescriptor() : nullptr}; 123 124 EXPECT_GT(RTNAME(ArgumentValue)(n, value.get(), err.get()), 0); 125 126 std::string spaces(value->ElementBytes(), ' '); 127 CheckDescriptorEqStr(value.get(), spaces); 128 129 if (errStr) { 130 std::string paddedErrStr(GetPaddedStr(errStr, err->ElementBytes())); 131 CheckDescriptorEqStr(err.get(), paddedErrStr); 132 } 133 } 134 }; 135 136 static const char *commandOnlyArgv[]{"aProgram"}; 137 class ZeroArguments : public CommandFixture { 138 protected: 139 ZeroArguments() : CommandFixture(1, commandOnlyArgv) {} 140 }; 141 142 TEST_F(ZeroArguments, ArgumentCount) { EXPECT_EQ(0, RTNAME(ArgumentCount)()); } 143 144 TEST_F(ZeroArguments, ArgumentLength) { 145 EXPECT_EQ(0, RTNAME(ArgumentLength)(-1)); 146 EXPECT_EQ(8, RTNAME(ArgumentLength)(0)); 147 EXPECT_EQ(0, RTNAME(ArgumentLength)(1)); 148 } 149 150 TEST_F(ZeroArguments, ArgumentValue) { 151 CheckArgumentValue(commandOnlyArgv[0], 0); 152 } 153 154 static const char *oneArgArgv[]{"aProgram", "anArgumentOfLength20"}; 155 class OneArgument : public CommandFixture { 156 protected: 157 OneArgument() : CommandFixture(2, oneArgArgv) {} 158 }; 159 160 TEST_F(OneArgument, ArgumentCount) { EXPECT_EQ(1, RTNAME(ArgumentCount)()); } 161 162 TEST_F(OneArgument, ArgumentLength) { 163 EXPECT_EQ(0, RTNAME(ArgumentLength)(-1)); 164 EXPECT_EQ(8, RTNAME(ArgumentLength)(0)); 165 EXPECT_EQ(20, RTNAME(ArgumentLength)(1)); 166 EXPECT_EQ(0, RTNAME(ArgumentLength)(2)); 167 } 168 169 TEST_F(OneArgument, ArgumentValue) { 170 CheckArgumentValue(oneArgArgv[0], 0); 171 CheckArgumentValue(oneArgArgv[1], 1); 172 } 173 174 static const char *severalArgsArgv[]{ 175 "aProgram", "16-char-long-arg", "", "-22-character-long-arg", "o"}; 176 class SeveralArguments : public CommandFixture { 177 protected: 178 SeveralArguments() 179 : CommandFixture(sizeof(severalArgsArgv) / sizeof(*severalArgsArgv), 180 severalArgsArgv) {} 181 }; 182 183 TEST_F(SeveralArguments, ArgumentCount) { 184 EXPECT_EQ(4, RTNAME(ArgumentCount)()); 185 } 186 187 TEST_F(SeveralArguments, ArgumentLength) { 188 EXPECT_EQ(0, RTNAME(ArgumentLength)(-1)); 189 EXPECT_EQ(8, RTNAME(ArgumentLength)(0)); 190 EXPECT_EQ(16, RTNAME(ArgumentLength)(1)); 191 EXPECT_EQ(0, RTNAME(ArgumentLength)(2)); 192 EXPECT_EQ(22, RTNAME(ArgumentLength)(3)); 193 EXPECT_EQ(1, RTNAME(ArgumentLength)(4)); 194 EXPECT_EQ(0, RTNAME(ArgumentLength)(5)); 195 } 196 197 TEST_F(SeveralArguments, ArgumentValue) { 198 CheckArgumentValue(severalArgsArgv[0], 0); 199 CheckArgumentValue(severalArgsArgv[1], 1); 200 CheckArgumentValue(severalArgsArgv[3], 3); 201 CheckArgumentValue(severalArgsArgv[4], 4); 202 } 203 204 TEST_F(SeveralArguments, NoArgumentValue) { 205 // Make sure we don't crash if the 'value' and 'error' parameters aren't 206 // passed. 207 EXPECT_EQ(RTNAME(ArgumentValue)(2, nullptr, nullptr), 0); 208 EXPECT_GT(RTNAME(ArgumentValue)(-1, nullptr, nullptr), 0); 209 } 210 211 TEST_F(SeveralArguments, MissingArguments) { 212 CheckMissingArgumentValue(-1, "Invalid argument number"); 213 CheckMissingArgumentValue(2, "Missing argument"); 214 CheckMissingArgumentValue(5, "Invalid argument number"); 215 CheckMissingArgumentValue(5); 216 } 217 218 TEST_F(SeveralArguments, ValueTooShort) { 219 OwningPtr<Descriptor> tooShort{CreateEmptyCharDescriptor<15>()}; 220 ASSERT_NE(tooShort, nullptr); 221 EXPECT_EQ(RTNAME(ArgumentValue)(1, tooShort.get(), nullptr), -1); 222 CheckDescriptorEqStr(tooShort.get(), severalArgsArgv[1]); 223 224 OwningPtr<Descriptor> errMsg{CreateEmptyCharDescriptor()}; 225 ASSERT_NE(errMsg, nullptr); 226 227 EXPECT_EQ(RTNAME(ArgumentValue)(1, tooShort.get(), errMsg.get()), -1); 228 229 std::string expectedErrMsg{ 230 GetPaddedStr("Value too short", errMsg->ElementBytes())}; 231 CheckDescriptorEqStr(errMsg.get(), expectedErrMsg); 232 } 233 234 TEST_F(SeveralArguments, ErrMsgTooShort) { 235 OwningPtr<Descriptor> errMsg{CreateEmptyCharDescriptor<3>()}; 236 EXPECT_GT(RTNAME(ArgumentValue)(-1, nullptr, errMsg.get()), 0); 237 CheckDescriptorEqStr(errMsg.get(), "Inv"); 238 } 239 240 class EnvironmentVariables : public CommandFixture { 241 protected: 242 EnvironmentVariables() : CommandFixture(0, nullptr) { 243 SetEnv("NAME", "VALUE"); 244 SetEnv("EMPTY", ""); 245 } 246 247 // If we have access to setenv, we can run some more fine-grained tests. 248 template <typename ParamType = char> 249 void SetEnv(const ParamType *name, const ParamType *value, 250 decltype(setenv(name, value, 1)) *Enabled = nullptr) { 251 ASSERT_EQ(0, setenv(name, value, /*overwrite=*/1)); 252 canSetEnv = true; 253 } 254 255 // Fallback method if setenv is not available. 256 template <typename Unused = void> void SetEnv(const void *, const void *) {} 257 258 bool EnableFineGrainedTests() const { return canSetEnv; } 259 260 private: 261 bool canSetEnv{false}; 262 }; 263 264 TEST_F(EnvironmentVariables, Nonexistent) { 265 CheckMissingEnvVarValue("DOESNT_EXIST"); 266 267 CheckMissingEnvVarValue(" "); 268 CheckMissingEnvVarValue(""); 269 } 270 271 TEST_F(EnvironmentVariables, Basic) { 272 // Test a variable that's expected to exist in the environment. 273 char *path{std::getenv("PATH")}; 274 auto expectedLen{static_cast<int64_t>(std::strlen(path))}; 275 EXPECT_EQ(expectedLen, RTNAME(EnvVariableLength)(*CharDescriptor("PATH"))); 276 } 277 278 TEST_F(EnvironmentVariables, Trim) { 279 if (EnableFineGrainedTests()) { 280 EXPECT_EQ(5, RTNAME(EnvVariableLength)(*CharDescriptor("NAME "))); 281 CheckEnvVarValue("VALUE", "NAME "); 282 } 283 } 284 285 TEST_F(EnvironmentVariables, NoTrim) { 286 if (EnableFineGrainedTests()) { 287 CheckMissingEnvVarValue("NAME ", /*trim_name=*/false); 288 } 289 } 290 291 TEST_F(EnvironmentVariables, Empty) { 292 if (EnableFineGrainedTests()) { 293 EXPECT_EQ(0, RTNAME(EnvVariableLength)(*CharDescriptor("EMPTY"))); 294 CheckEnvVarValue("", "EMPTY"); 295 } 296 } 297 298 TEST_F(EnvironmentVariables, NoValueOrErrmsg) { 299 ASSERT_EQ(std::getenv("DOESNT_EXIST"), nullptr) 300 << "Environment variable DOESNT_EXIST actually exists"; 301 EXPECT_EQ(RTNAME(EnvVariableValue)(*CharDescriptor("DOESNT_EXIST")), 1); 302 303 if (EnableFineGrainedTests()) { 304 EXPECT_EQ(RTNAME(EnvVariableValue)(*CharDescriptor("NAME")), 0); 305 } 306 } 307 308 TEST_F(EnvironmentVariables, ValueTooShort) { 309 if (EnableFineGrainedTests()) { 310 OwningPtr<Descriptor> tooShort{CreateEmptyCharDescriptor<2>()}; 311 ASSERT_NE(tooShort, nullptr); 312 EXPECT_EQ(RTNAME(EnvVariableValue)(*CharDescriptor("NAME"), tooShort.get(), 313 /*trim_name=*/true, nullptr), 314 -1); 315 CheckDescriptorEqStr(tooShort.get(), "VALUE"); 316 317 OwningPtr<Descriptor> errMsg{CreateEmptyCharDescriptor()}; 318 ASSERT_NE(errMsg, nullptr); 319 320 EXPECT_EQ(RTNAME(EnvVariableValue)(*CharDescriptor("NAME"), tooShort.get(), 321 /*trim_name=*/true, errMsg.get()), 322 -1); 323 324 std::string expectedErrMsg{ 325 GetPaddedStr("Value too short", errMsg->ElementBytes())}; 326 CheckDescriptorEqStr(errMsg.get(), expectedErrMsg); 327 } 328 } 329 330 TEST_F(EnvironmentVariables, ErrMsgTooShort) { 331 ASSERT_EQ(std::getenv("DOESNT_EXIST"), nullptr) 332 << "Environment variable DOESNT_EXIST actually exists"; 333 334 OwningPtr<Descriptor> errMsg{CreateEmptyCharDescriptor<3>()}; 335 EXPECT_EQ(RTNAME(EnvVariableValue)(*CharDescriptor("DOESNT_EXIST"), nullptr, 336 /*trim_name=*/true, errMsg.get()), 337 1); 338 CheckDescriptorEqStr(errMsg.get(), "Mis"); 339 } 340