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