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/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 template <int kind = sizeof(std::int64_t)> 40 static OwningPtr<Descriptor> EmptyIntDescriptor() { 41 OwningPtr<Descriptor> descriptor{Descriptor::Create(TypeCategory::Integer, 42 kind, nullptr, 0, nullptr, CFI_attribute_allocatable)}; 43 if (descriptor->Allocate() != 0) { 44 return nullptr; 45 } 46 return descriptor; 47 } 48 49 class CommandFixture : public ::testing::Test { 50 protected: 51 CommandFixture(int argc, const char *argv[]) { 52 RTNAME(ProgramStart)(argc, argv, {}); 53 } 54 55 std::string GetPaddedStr(const char *text, std::size_t len) const { 56 std::string res{text}; 57 assert(res.length() <= len && "No room to pad"); 58 res.append(len - res.length(), ' '); 59 return res; 60 } 61 62 void CheckDescriptorEqStr( 63 const Descriptor *value, const std::string &expected) const { 64 ASSERT_NE(value, nullptr); 65 EXPECT_EQ(std::strncmp(value->OffsetElement(), expected.c_str(), 66 value->ElementBytes()), 67 0) 68 << "expected: " << expected << "\n" 69 << "value: " 70 << std::string{value->OffsetElement(), value->ElementBytes()}; 71 } 72 73 template <typename INT_T = std::int64_t> 74 void CheckDescriptorEqInt( 75 const Descriptor *value, const INT_T expected) const { 76 if (expected != -1) { 77 ASSERT_NE(value, nullptr); 78 EXPECT_EQ(*value->OffsetElement<INT_T>(), expected); 79 } 80 } 81 82 template <typename RuntimeCall> 83 void CheckValue(RuntimeCall F, const char *expectedValue, 84 std::int64_t expectedLength = -1, std::int32_t expectedStatus = 0, 85 const char *expectedErrMsg = "shouldn't change") const { 86 OwningPtr<Descriptor> value{CreateEmptyCharDescriptor()}; 87 ASSERT_NE(value, nullptr); 88 89 OwningPtr<Descriptor> length{ 90 expectedLength == -1 ? nullptr : EmptyIntDescriptor()}; 91 92 OwningPtr<Descriptor> errmsg{CharDescriptor(expectedErrMsg)}; 93 ASSERT_NE(errmsg, nullptr); 94 95 std::string expectedValueStr{ 96 GetPaddedStr(expectedValue, value->ElementBytes())}; 97 98 EXPECT_EQ(F(value.get(), length.get(), errmsg.get()), expectedStatus); 99 CheckDescriptorEqStr(value.get(), expectedValueStr); 100 CheckDescriptorEqInt(length.get(), expectedLength); 101 CheckDescriptorEqStr(errmsg.get(), expectedErrMsg); 102 } 103 104 void CheckArgumentValue(const char *expectedValue, int n) const { 105 SCOPED_TRACE(n); 106 SCOPED_TRACE("Checking argument:"); 107 CheckValue( 108 [&](const Descriptor *value, const Descriptor *, 109 const Descriptor *errmsg) { 110 return RTNAME(ArgumentValue)(n, value, errmsg); 111 }, 112 expectedValue); 113 } 114 115 void CheckCommandValue(const char *args[], int n) const { 116 SCOPED_TRACE("Checking command:"); 117 ASSERT_GE(n, 1); 118 std::string expectedValue{args[0]}; 119 for (int i = 1; i < n; i++) { 120 expectedValue += " " + std::string{args[i]}; 121 } 122 CheckValue( 123 [&](const Descriptor *value, const Descriptor *length, 124 const Descriptor *errmsg) { 125 return RTNAME(GetCommand)(value, length, errmsg); 126 }, 127 expectedValue.c_str(), expectedValue.size()); 128 } 129 130 void CheckEnvVarValue( 131 const char *expectedValue, const char *name, bool trimName = true) const { 132 SCOPED_TRACE(name); 133 SCOPED_TRACE("Checking environment variable"); 134 CheckValue( 135 [&](const Descriptor *value, const Descriptor *, 136 const Descriptor *errmsg) { 137 return RTNAME(EnvVariableValue)(*CharDescriptor(name), value, 138 trimName, errmsg, /*sourceFile=*/nullptr, /*line=*/0); 139 }, 140 expectedValue); 141 } 142 143 void CheckMissingEnvVarValue(const char *name, bool trimName = true) const { 144 SCOPED_TRACE(name); 145 SCOPED_TRACE("Checking missing environment variable"); 146 147 ASSERT_EQ(nullptr, std::getenv(name)) 148 << "Environment variable " << name << " not expected to exist"; 149 150 OwningPtr<Descriptor> nameDescriptor{CharDescriptor(name)}; 151 EXPECT_EQ(0, RTNAME(EnvVariableLength)(*nameDescriptor, trimName)); 152 CheckValue( 153 [&](const Descriptor *value, const Descriptor *, 154 const Descriptor *errmsg) { 155 return RTNAME(EnvVariableValue)(*nameDescriptor, value, trimName, 156 errmsg, /*sourceFile=*/nullptr, /*line=*/0); 157 }, 158 "", -1, 1, "Missing environment variable"); 159 } 160 161 void CheckMissingArgumentValue(int n, const char *errStr = nullptr) const { 162 OwningPtr<Descriptor> value{CreateEmptyCharDescriptor()}; 163 ASSERT_NE(value, nullptr); 164 165 OwningPtr<Descriptor> err{errStr ? CreateEmptyCharDescriptor() : nullptr}; 166 167 EXPECT_GT(RTNAME(ArgumentValue)(n, value.get(), err.get()), 0); 168 169 std::string spaces(value->ElementBytes(), ' '); 170 CheckDescriptorEqStr(value.get(), spaces); 171 172 if (errStr) { 173 std::string paddedErrStr(GetPaddedStr(errStr, err->ElementBytes())); 174 CheckDescriptorEqStr(err.get(), paddedErrStr); 175 } 176 } 177 178 void CheckMissingCommandValue(const char *errStr = nullptr) const { 179 OwningPtr<Descriptor> value{CreateEmptyCharDescriptor()}; 180 ASSERT_NE(value, nullptr); 181 182 OwningPtr<Descriptor> length{EmptyIntDescriptor()}; 183 ASSERT_NE(length, nullptr); 184 185 OwningPtr<Descriptor> err{errStr ? CreateEmptyCharDescriptor() : nullptr}; 186 187 EXPECT_GT(RTNAME(GetCommand)(value.get(), length.get(), err.get()), 0); 188 189 std::string spaces(value->ElementBytes(), ' '); 190 CheckDescriptorEqStr(value.get(), spaces); 191 192 CheckDescriptorEqInt(length.get(), 0); 193 194 if (errStr) { 195 std::string paddedErrStr(GetPaddedStr(errStr, err->ElementBytes())); 196 CheckDescriptorEqStr(err.get(), paddedErrStr); 197 } 198 } 199 }; 200 201 class NoArgv : public CommandFixture { 202 protected: 203 NoArgv() : CommandFixture(0, nullptr) {} 204 }; 205 206 // TODO: Test other intrinsics with this fixture. 207 208 TEST_F(NoArgv, GetCommand) { CheckMissingCommandValue(); } 209 210 static const char *commandOnlyArgv[]{"aProgram"}; 211 class ZeroArguments : public CommandFixture { 212 protected: 213 ZeroArguments() : CommandFixture(1, commandOnlyArgv) {} 214 }; 215 216 TEST_F(ZeroArguments, ArgumentCount) { EXPECT_EQ(0, RTNAME(ArgumentCount)()); } 217 218 TEST_F(ZeroArguments, ArgumentLength) { 219 EXPECT_EQ(0, RTNAME(ArgumentLength)(-1)); 220 EXPECT_EQ(8, RTNAME(ArgumentLength)(0)); 221 EXPECT_EQ(0, RTNAME(ArgumentLength)(1)); 222 } 223 224 TEST_F(ZeroArguments, ArgumentValue) { 225 CheckArgumentValue(commandOnlyArgv[0], 0); 226 } 227 228 TEST_F(ZeroArguments, GetCommand) { CheckCommandValue(commandOnlyArgv, 1); } 229 230 static const char *oneArgArgv[]{"aProgram", "anArgumentOfLength20"}; 231 class OneArgument : public CommandFixture { 232 protected: 233 OneArgument() : CommandFixture(2, oneArgArgv) {} 234 }; 235 236 TEST_F(OneArgument, ArgumentCount) { EXPECT_EQ(1, RTNAME(ArgumentCount)()); } 237 238 TEST_F(OneArgument, ArgumentLength) { 239 EXPECT_EQ(0, RTNAME(ArgumentLength)(-1)); 240 EXPECT_EQ(8, RTNAME(ArgumentLength)(0)); 241 EXPECT_EQ(20, RTNAME(ArgumentLength)(1)); 242 EXPECT_EQ(0, RTNAME(ArgumentLength)(2)); 243 } 244 245 TEST_F(OneArgument, ArgumentValue) { 246 CheckArgumentValue(oneArgArgv[0], 0); 247 CheckArgumentValue(oneArgArgv[1], 1); 248 } 249 250 TEST_F(OneArgument, GetCommand) { CheckCommandValue(oneArgArgv, 2); } 251 252 static const char *severalArgsArgv[]{ 253 "aProgram", "16-char-long-arg", "", "-22-character-long-arg", "o"}; 254 class SeveralArguments : public CommandFixture { 255 protected: 256 SeveralArguments() 257 : CommandFixture(sizeof(severalArgsArgv) / sizeof(*severalArgsArgv), 258 severalArgsArgv) {} 259 }; 260 261 TEST_F(SeveralArguments, ArgumentCount) { 262 EXPECT_EQ(4, RTNAME(ArgumentCount)()); 263 } 264 265 TEST_F(SeveralArguments, ArgumentLength) { 266 EXPECT_EQ(0, RTNAME(ArgumentLength)(-1)); 267 EXPECT_EQ(8, RTNAME(ArgumentLength)(0)); 268 EXPECT_EQ(16, RTNAME(ArgumentLength)(1)); 269 EXPECT_EQ(0, RTNAME(ArgumentLength)(2)); 270 EXPECT_EQ(22, RTNAME(ArgumentLength)(3)); 271 EXPECT_EQ(1, RTNAME(ArgumentLength)(4)); 272 EXPECT_EQ(0, RTNAME(ArgumentLength)(5)); 273 } 274 275 TEST_F(SeveralArguments, ArgumentValue) { 276 CheckArgumentValue(severalArgsArgv[0], 0); 277 CheckArgumentValue(severalArgsArgv[1], 1); 278 CheckArgumentValue(severalArgsArgv[3], 3); 279 CheckArgumentValue(severalArgsArgv[4], 4); 280 } 281 282 TEST_F(SeveralArguments, NoArgumentValue) { 283 // Make sure we don't crash if the 'value' and 'error' parameters aren't 284 // passed. 285 EXPECT_EQ(RTNAME(ArgumentValue)(2, nullptr, nullptr), 0); 286 EXPECT_GT(RTNAME(ArgumentValue)(-1, nullptr, nullptr), 0); 287 } 288 289 TEST_F(SeveralArguments, MissingArguments) { 290 CheckMissingArgumentValue(-1, "Invalid argument number"); 291 CheckMissingArgumentValue(2, "Missing argument"); 292 CheckMissingArgumentValue(5, "Invalid argument number"); 293 CheckMissingArgumentValue(5); 294 } 295 296 TEST_F(SeveralArguments, ArgValueTooShort) { 297 OwningPtr<Descriptor> tooShort{CreateEmptyCharDescriptor<15>()}; 298 ASSERT_NE(tooShort, nullptr); 299 EXPECT_EQ(RTNAME(ArgumentValue)(1, tooShort.get(), nullptr), -1); 300 CheckDescriptorEqStr(tooShort.get(), severalArgsArgv[1]); 301 302 OwningPtr<Descriptor> errMsg{CreateEmptyCharDescriptor()}; 303 ASSERT_NE(errMsg, nullptr); 304 305 EXPECT_EQ(RTNAME(ArgumentValue)(1, tooShort.get(), errMsg.get()), -1); 306 307 std::string expectedErrMsg{ 308 GetPaddedStr("Value too short", errMsg->ElementBytes())}; 309 CheckDescriptorEqStr(errMsg.get(), expectedErrMsg); 310 } 311 312 TEST_F(SeveralArguments, ArgErrMsgTooShort) { 313 OwningPtr<Descriptor> errMsg{CreateEmptyCharDescriptor<3>()}; 314 EXPECT_GT(RTNAME(ArgumentValue)(-1, nullptr, errMsg.get()), 0); 315 CheckDescriptorEqStr(errMsg.get(), "Inv"); 316 } 317 318 TEST_F(SeveralArguments, GetCommand) { 319 CheckMissingCommandValue(); 320 CheckMissingCommandValue("Missing argument"); 321 } 322 323 TEST_F(SeveralArguments, CommandErrMsgTooShort) { 324 OwningPtr<Descriptor> value{CreateEmptyCharDescriptor()}; 325 OwningPtr<Descriptor> length{EmptyIntDescriptor()}; 326 OwningPtr<Descriptor> errMsg{CreateEmptyCharDescriptor<3>()}; 327 328 EXPECT_GT(RTNAME(GetCommand)(value.get(), length.get(), errMsg.get()), 0); 329 330 std::string spaces(value->ElementBytes(), ' '); 331 CheckDescriptorEqStr(value.get(), spaces); 332 CheckDescriptorEqInt(length.get(), 0); 333 CheckDescriptorEqStr(errMsg.get(), "Mis"); 334 } 335 336 TEST_F(SeveralArguments, GetCommandCanTakeNull) { 337 EXPECT_GT(RTNAME(GetCommand)(nullptr, nullptr, nullptr), 0); 338 } 339 340 static const char *onlyValidArgsArgv[]{ 341 "aProgram", "-f", "has/a/few/slashes", "has\\a\\few\\backslashes"}; 342 class OnlyValidArguments : public CommandFixture { 343 protected: 344 OnlyValidArguments() 345 : CommandFixture(sizeof(onlyValidArgsArgv) / sizeof(*onlyValidArgsArgv), 346 onlyValidArgsArgv) {} 347 }; 348 349 TEST_F(OnlyValidArguments, GetCommand) { 350 CheckCommandValue(onlyValidArgsArgv, 4); 351 } 352 353 TEST_F(OnlyValidArguments, CommandValueTooShort) { 354 OwningPtr<Descriptor> tooShort{CreateEmptyCharDescriptor<50>()}; 355 ASSERT_NE(tooShort, nullptr); 356 OwningPtr<Descriptor> length{EmptyIntDescriptor()}; 357 ASSERT_NE(length, nullptr); 358 359 EXPECT_EQ(RTNAME(GetCommand)(tooShort.get(), length.get(), nullptr), -1); 360 361 CheckDescriptorEqStr( 362 tooShort.get(), "aProgram -f has/a/few/slashes has\\a\\few\\backslashe"); 363 CheckDescriptorEqInt(length.get(), 51); 364 365 OwningPtr<Descriptor> errMsg{CreateEmptyCharDescriptor()}; 366 ASSERT_NE(errMsg, nullptr); 367 368 EXPECT_EQ(-1, RTNAME(GetCommand)(tooShort.get(), nullptr, errMsg.get())); 369 370 std::string expectedErrMsg{ 371 GetPaddedStr("Value too short", errMsg->ElementBytes())}; 372 CheckDescriptorEqStr(errMsg.get(), expectedErrMsg); 373 } 374 375 TEST_F(OnlyValidArguments, GetCommandCanTakeNull) { 376 EXPECT_EQ(0, RTNAME(GetCommand)(nullptr, nullptr, nullptr)); 377 378 OwningPtr<Descriptor> value{CreateEmptyCharDescriptor()}; 379 ASSERT_NE(value, nullptr); 380 OwningPtr<Descriptor> length{EmptyIntDescriptor()}; 381 ASSERT_NE(length, nullptr); 382 383 EXPECT_EQ(0, RTNAME(GetCommand)(value.get(), nullptr, nullptr)); 384 CheckDescriptorEqStr(value.get(), 385 GetPaddedStr("aProgram -f has/a/few/slashes has\\a\\few\\backslashes", 386 value->ElementBytes())); 387 388 EXPECT_EQ(0, RTNAME(GetCommand)(nullptr, length.get(), nullptr)); 389 CheckDescriptorEqInt(length.get(), 51); 390 } 391 392 TEST_F(OnlyValidArguments, GetCommandShortLength) { 393 OwningPtr<Descriptor> length{EmptyIntDescriptor<sizeof(short)>()}; 394 ASSERT_NE(length, nullptr); 395 396 EXPECT_EQ(0, RTNAME(GetCommand)(nullptr, length.get(), nullptr)); 397 CheckDescriptorEqInt<short>(length.get(), 51); 398 } 399 400 class EnvironmentVariables : public CommandFixture { 401 protected: 402 EnvironmentVariables() : CommandFixture(0, nullptr) { 403 SetEnv("NAME", "VALUE"); 404 SetEnv("EMPTY", ""); 405 } 406 407 // If we have access to setenv, we can run some more fine-grained tests. 408 template <typename ParamType = char> 409 void SetEnv(const ParamType *name, const ParamType *value, 410 decltype(setenv(name, value, 1)) *Enabled = nullptr) { 411 ASSERT_EQ(0, setenv(name, value, /*overwrite=*/1)); 412 canSetEnv = true; 413 } 414 415 // Fallback method if setenv is not available. 416 template <typename Unused = void> void SetEnv(const void *, const void *) {} 417 418 bool EnableFineGrainedTests() const { return canSetEnv; } 419 420 private: 421 bool canSetEnv{false}; 422 }; 423 424 TEST_F(EnvironmentVariables, Nonexistent) { 425 CheckMissingEnvVarValue("DOESNT_EXIST"); 426 427 CheckMissingEnvVarValue(" "); 428 CheckMissingEnvVarValue(""); 429 } 430 431 TEST_F(EnvironmentVariables, Basic) { 432 // Test a variable that's expected to exist in the environment. 433 char *path{std::getenv("PATH")}; 434 auto expectedLen{static_cast<int64_t>(std::strlen(path))}; 435 EXPECT_EQ(expectedLen, RTNAME(EnvVariableLength)(*CharDescriptor("PATH"))); 436 } 437 438 TEST_F(EnvironmentVariables, Trim) { 439 if (EnableFineGrainedTests()) { 440 EXPECT_EQ(5, RTNAME(EnvVariableLength)(*CharDescriptor("NAME "))); 441 CheckEnvVarValue("VALUE", "NAME "); 442 } 443 } 444 445 TEST_F(EnvironmentVariables, NoTrim) { 446 if (EnableFineGrainedTests()) { 447 CheckMissingEnvVarValue("NAME ", /*trim_name=*/false); 448 } 449 } 450 451 TEST_F(EnvironmentVariables, Empty) { 452 if (EnableFineGrainedTests()) { 453 EXPECT_EQ(0, RTNAME(EnvVariableLength)(*CharDescriptor("EMPTY"))); 454 CheckEnvVarValue("", "EMPTY"); 455 } 456 } 457 458 TEST_F(EnvironmentVariables, NoValueOrErrmsg) { 459 ASSERT_EQ(std::getenv("DOESNT_EXIST"), nullptr) 460 << "Environment variable DOESNT_EXIST actually exists"; 461 EXPECT_EQ(RTNAME(EnvVariableValue)(*CharDescriptor("DOESNT_EXIST")), 1); 462 463 if (EnableFineGrainedTests()) { 464 EXPECT_EQ(RTNAME(EnvVariableValue)(*CharDescriptor("NAME")), 0); 465 } 466 } 467 468 TEST_F(EnvironmentVariables, ValueTooShort) { 469 if (EnableFineGrainedTests()) { 470 OwningPtr<Descriptor> tooShort{CreateEmptyCharDescriptor<2>()}; 471 ASSERT_NE(tooShort, nullptr); 472 EXPECT_EQ(RTNAME(EnvVariableValue)(*CharDescriptor("NAME"), tooShort.get(), 473 /*trim_name=*/true, nullptr), 474 -1); 475 CheckDescriptorEqStr(tooShort.get(), "VALUE"); 476 477 OwningPtr<Descriptor> errMsg{CreateEmptyCharDescriptor()}; 478 ASSERT_NE(errMsg, nullptr); 479 480 EXPECT_EQ(RTNAME(EnvVariableValue)(*CharDescriptor("NAME"), tooShort.get(), 481 /*trim_name=*/true, errMsg.get()), 482 -1); 483 484 std::string expectedErrMsg{ 485 GetPaddedStr("Value too short", errMsg->ElementBytes())}; 486 CheckDescriptorEqStr(errMsg.get(), expectedErrMsg); 487 } 488 } 489 490 TEST_F(EnvironmentVariables, ErrMsgTooShort) { 491 ASSERT_EQ(std::getenv("DOESNT_EXIST"), nullptr) 492 << "Environment variable DOESNT_EXIST actually exists"; 493 494 OwningPtr<Descriptor> errMsg{CreateEmptyCharDescriptor<3>()}; 495 EXPECT_EQ(RTNAME(EnvVariableValue)(*CharDescriptor("DOESNT_EXIST"), nullptr, 496 /*trim_name=*/true, errMsg.get()), 497 1); 498 CheckDescriptorEqStr(errMsg.get(), "Mis"); 499 } 500