xref: /llvm-project/flang/unittests/Runtime/CommandTest.cpp (revision 9df0ba599c3c777a37557ddcc567c85dae4c33d3)
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