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