1 //===-- flang/unittests/Runtime/CommandTest.cpp ---------------------------===//
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
7 //===----------------------------------------------------------------------===//
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"
16 using namespace Fortran::runtime
;
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) {
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) {
35 std::memcpy(descriptor
->OffsetElement(), value
, n
);
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) {
49 class CommandFixture
: public ::testing::Test
{
51 CommandFixture(int argc
, const char *argv
[]) {
52 RTNAME(ProgramStart
)(argc
, argv
, {}, {});
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(), ' ');
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()),
68 << "expected: " << expected
<< "\n"
70 << std::string
{value
->OffsetElement(), value
->ElementBytes()};
73 template <typename INT_T
= std::int64_t>
74 void CheckDescriptorEqInt(
75 const Descriptor
*value
, const INT_T expected
) const {
77 ASSERT_NE(value
, nullptr);
78 EXPECT_EQ(*value
->OffsetElement
<INT_T
>(), expected
);
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);
89 OwningPtr
<Descriptor
> length
{
90 expectedLength
== -1 ? nullptr : EmptyIntDescriptor()};
92 OwningPtr
<Descriptor
> errmsg
{CharDescriptor(expectedErrMsg
)};
93 ASSERT_NE(errmsg
, nullptr);
95 std::string expectedValueStr
{
96 GetPaddedStr(expectedValue
, value
->ElementBytes())};
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
);
104 void CheckArgumentValue(const char *expectedValue
, int n
) const {
106 SCOPED_TRACE("Checking argument:");
108 [&](const Descriptor
*value
, const Descriptor
*length
,
109 const Descriptor
*errmsg
) {
110 return RTNAME(GetCommandArgument
)(n
, value
, length
, errmsg
);
112 expectedValue
, std::strlen(expectedValue
));
115 void CheckCommandValue(const char *args
[], int n
) const {
116 SCOPED_TRACE("Checking command:");
118 std::string expectedValue
{args
[0]};
119 for (int i
= 1; i
< n
; i
++) {
120 expectedValue
+= " " + std::string
{args
[i
]};
123 [&](const Descriptor
*value
, const Descriptor
*length
,
124 const Descriptor
*errmsg
) {
125 return RTNAME(GetCommand
)(value
, length
, errmsg
);
127 expectedValue
.c_str(), expectedValue
.size());
130 void CheckEnvVarValue(
131 const char *expectedValue
, const char *name
, bool trimName
= true) const {
133 SCOPED_TRACE("Checking environment variable");
135 [&](const Descriptor
*value
, const Descriptor
*length
,
136 const Descriptor
*errmsg
) {
137 return RTNAME(GetEnvVariable
)(
138 *CharDescriptor(name
), value
, length
, trimName
, errmsg
);
140 expectedValue
, std::strlen(expectedValue
));
143 void CheckMissingEnvVarValue(const char *name
, bool trimName
= true) const {
145 SCOPED_TRACE("Checking missing environment variable");
147 ASSERT_EQ(nullptr, std::getenv(name
))
148 << "Environment variable " << name
<< " not expected to exist";
151 [&](const Descriptor
*value
, const Descriptor
*length
,
152 const Descriptor
*errmsg
) {
153 return RTNAME(GetEnvVariable
)(
154 *CharDescriptor(name
), value
, length
, trimName
, errmsg
);
156 "", 0, 1, "Missing environment variable");
159 void CheckMissingArgumentValue(int n
, const char *errStr
= nullptr) const {
160 OwningPtr
<Descriptor
> value
{CreateEmptyCharDescriptor()};
161 ASSERT_NE(value
, nullptr);
163 OwningPtr
<Descriptor
> length
{EmptyIntDescriptor()};
164 ASSERT_NE(length
, nullptr);
166 OwningPtr
<Descriptor
> err
{errStr
? CreateEmptyCharDescriptor() : nullptr};
169 RTNAME(GetCommandArgument
)(n
, value
.get(), length
.get(), err
.get()), 0);
171 std::string
spaces(value
->ElementBytes(), ' ');
172 CheckDescriptorEqStr(value
.get(), spaces
);
174 CheckDescriptorEqInt(length
.get(), 0);
177 std::string
paddedErrStr(GetPaddedStr(errStr
, err
->ElementBytes()));
178 CheckDescriptorEqStr(err
.get(), paddedErrStr
);
182 void CheckMissingCommandValue(const char *errStr
= nullptr) const {
183 OwningPtr
<Descriptor
> value
{CreateEmptyCharDescriptor()};
184 ASSERT_NE(value
, nullptr);
186 OwningPtr
<Descriptor
> length
{EmptyIntDescriptor()};
187 ASSERT_NE(length
, nullptr);
189 OwningPtr
<Descriptor
> err
{errStr
? CreateEmptyCharDescriptor() : nullptr};
191 EXPECT_GT(RTNAME(GetCommand
)(value
.get(), length
.get(), err
.get()), 0);
193 std::string
spaces(value
->ElementBytes(), ' ');
194 CheckDescriptorEqStr(value
.get(), spaces
);
196 CheckDescriptorEqInt(length
.get(), 0);
199 std::string
paddedErrStr(GetPaddedStr(errStr
, err
->ElementBytes()));
200 CheckDescriptorEqStr(err
.get(), paddedErrStr
);
205 class NoArgv
: public CommandFixture
{
207 NoArgv() : CommandFixture(0, nullptr) {}
210 // TODO: Test other intrinsics with this fixture.
212 TEST_F(NoArgv
, GetCommand
) { CheckMissingCommandValue(); }
214 static const char *commandOnlyArgv
[]{"aProgram"};
215 class ZeroArguments
: public CommandFixture
{
217 ZeroArguments() : CommandFixture(1, commandOnlyArgv
) {}
220 TEST_F(ZeroArguments
, ArgumentCount
) { EXPECT_EQ(0, RTNAME(ArgumentCount
)()); }
222 TEST_F(ZeroArguments
, GetCommandArgument
) {
223 CheckMissingArgumentValue(-1);
224 CheckArgumentValue(commandOnlyArgv
[0], 0);
225 CheckMissingArgumentValue(1);
228 TEST_F(ZeroArguments
, GetCommand
) { CheckCommandValue(commandOnlyArgv
, 1); }
230 static const char *oneArgArgv
[]{"aProgram", "anArgumentOfLength20"};
231 class OneArgument
: public CommandFixture
{
233 OneArgument() : CommandFixture(2, oneArgArgv
) {}
236 TEST_F(OneArgument
, ArgumentCount
) { EXPECT_EQ(1, RTNAME(ArgumentCount
)()); }
238 TEST_F(OneArgument
, GetCommandArgument
) {
239 CheckMissingArgumentValue(-1);
240 CheckArgumentValue(oneArgArgv
[0], 0);
241 CheckArgumentValue(oneArgArgv
[1], 1);
242 CheckMissingArgumentValue(2);
245 TEST_F(OneArgument
, GetCommand
) { CheckCommandValue(oneArgArgv
, 2); }
247 static const char *severalArgsArgv
[]{
248 "aProgram", "16-char-long-arg", "", "-22-character-long-arg", "o"};
249 class SeveralArguments
: public CommandFixture
{
252 : CommandFixture(sizeof(severalArgsArgv
) / sizeof(*severalArgsArgv
),
256 TEST_F(SeveralArguments
, ArgumentCount
) {
257 EXPECT_EQ(4, RTNAME(ArgumentCount
)());
260 TEST_F(SeveralArguments
, GetCommandArgument
) {
261 CheckArgumentValue(severalArgsArgv
[0], 0);
262 CheckArgumentValue(severalArgsArgv
[1], 1);
263 CheckArgumentValue(severalArgsArgv
[3], 3);
264 CheckArgumentValue(severalArgsArgv
[4], 4);
267 TEST_F(SeveralArguments
, NoArgumentValue
) {
268 // Make sure we don't crash if the 'value', 'length' and 'error' parameters
270 EXPECT_GT(RTNAME(GetCommandArgument
)(2), 0);
271 EXPECT_EQ(RTNAME(GetCommandArgument
)(1), 0);
272 EXPECT_GT(RTNAME(GetCommandArgument
)(-1), 0);
275 TEST_F(SeveralArguments
, MissingArguments
) {
276 CheckMissingArgumentValue(-1, "Invalid argument number");
277 CheckMissingArgumentValue(2, "Missing argument");
278 CheckMissingArgumentValue(5, "Invalid argument number");
279 CheckMissingArgumentValue(5);
282 TEST_F(SeveralArguments
, ArgValueTooShort
) {
283 OwningPtr
<Descriptor
> tooShort
{CreateEmptyCharDescriptor
<15>()};
284 ASSERT_NE(tooShort
, nullptr);
285 EXPECT_EQ(RTNAME(GetCommandArgument
)(1, tooShort
.get()), -1);
286 CheckDescriptorEqStr(tooShort
.get(), severalArgsArgv
[1]);
288 OwningPtr
<Descriptor
> length
{EmptyIntDescriptor()};
289 ASSERT_NE(length
, nullptr);
290 OwningPtr
<Descriptor
> errMsg
{CreateEmptyCharDescriptor()};
291 ASSERT_NE(errMsg
, nullptr);
294 RTNAME(GetCommandArgument
)(1, tooShort
.get(), length
.get(), errMsg
.get()),
297 CheckDescriptorEqInt(length
.get(), 16);
298 std::string expectedErrMsg
{
299 GetPaddedStr("Value too short", errMsg
->ElementBytes())};
300 CheckDescriptorEqStr(errMsg
.get(), expectedErrMsg
);
303 TEST_F(SeveralArguments
, ArgErrMsgTooShort
) {
304 OwningPtr
<Descriptor
> errMsg
{CreateEmptyCharDescriptor
<3>()};
305 EXPECT_GT(RTNAME(GetCommandArgument
)(-1, nullptr, nullptr, errMsg
.get()), 0);
306 CheckDescriptorEqStr(errMsg
.get(), "Inv");
309 TEST_F(SeveralArguments
, GetCommand
) {
310 CheckMissingCommandValue();
311 CheckMissingCommandValue("Missing argument");
314 TEST_F(SeveralArguments
, CommandErrMsgTooShort
) {
315 OwningPtr
<Descriptor
> value
{CreateEmptyCharDescriptor()};
316 OwningPtr
<Descriptor
> length
{EmptyIntDescriptor()};
317 OwningPtr
<Descriptor
> errMsg
{CreateEmptyCharDescriptor
<3>()};
319 EXPECT_GT(RTNAME(GetCommand
)(value
.get(), length
.get(), errMsg
.get()), 0);
321 std::string
spaces(value
->ElementBytes(), ' ');
322 CheckDescriptorEqStr(value
.get(), spaces
);
323 CheckDescriptorEqInt(length
.get(), 0);
324 CheckDescriptorEqStr(errMsg
.get(), "Mis");
327 TEST_F(SeveralArguments
, GetCommandCanTakeNull
) {
328 EXPECT_GT(RTNAME(GetCommand
)(nullptr, nullptr, nullptr), 0);
331 static const char *onlyValidArgsArgv
[]{
332 "aProgram", "-f", "has/a/few/slashes", "has\\a\\few\\backslashes"};
333 class OnlyValidArguments
: public CommandFixture
{
336 : CommandFixture(sizeof(onlyValidArgsArgv
) / sizeof(*onlyValidArgsArgv
),
337 onlyValidArgsArgv
) {}
340 TEST_F(OnlyValidArguments
, GetCommand
) {
341 CheckCommandValue(onlyValidArgsArgv
, 4);
344 TEST_F(OnlyValidArguments
, CommandValueTooShort
) {
345 OwningPtr
<Descriptor
> tooShort
{CreateEmptyCharDescriptor
<50>()};
346 ASSERT_NE(tooShort
, nullptr);
347 OwningPtr
<Descriptor
> length
{EmptyIntDescriptor()};
348 ASSERT_NE(length
, nullptr);
350 EXPECT_EQ(RTNAME(GetCommand
)(tooShort
.get(), length
.get(), nullptr), -1);
352 CheckDescriptorEqStr(
353 tooShort
.get(), "aProgram -f has/a/few/slashes has\\a\\few\\backslashe");
354 CheckDescriptorEqInt(length
.get(), 51);
356 OwningPtr
<Descriptor
> errMsg
{CreateEmptyCharDescriptor()};
357 ASSERT_NE(errMsg
, nullptr);
359 EXPECT_EQ(-1, RTNAME(GetCommand
)(tooShort
.get(), nullptr, errMsg
.get()));
361 std::string expectedErrMsg
{
362 GetPaddedStr("Value too short", errMsg
->ElementBytes())};
363 CheckDescriptorEqStr(errMsg
.get(), expectedErrMsg
);
366 TEST_F(OnlyValidArguments
, GetCommandCanTakeNull
) {
367 EXPECT_EQ(0, RTNAME(GetCommand
)(nullptr, nullptr, nullptr));
369 OwningPtr
<Descriptor
> value
{CreateEmptyCharDescriptor()};
370 ASSERT_NE(value
, nullptr);
371 OwningPtr
<Descriptor
> length
{EmptyIntDescriptor()};
372 ASSERT_NE(length
, nullptr);
374 EXPECT_EQ(0, RTNAME(GetCommand
)(value
.get(), nullptr, nullptr));
375 CheckDescriptorEqStr(value
.get(),
376 GetPaddedStr("aProgram -f has/a/few/slashes has\\a\\few\\backslashes",
377 value
->ElementBytes()));
379 EXPECT_EQ(0, RTNAME(GetCommand
)(nullptr, length
.get(), nullptr));
380 CheckDescriptorEqInt(length
.get(), 51);
383 TEST_F(OnlyValidArguments
, GetCommandShortLength
) {
384 OwningPtr
<Descriptor
> length
{EmptyIntDescriptor
<sizeof(short)>()};
385 ASSERT_NE(length
, nullptr);
387 EXPECT_EQ(0, RTNAME(GetCommand
)(nullptr, length
.get(), nullptr));
388 CheckDescriptorEqInt
<short>(length
.get(), 51);
391 class EnvironmentVariables
: public CommandFixture
{
393 EnvironmentVariables() : CommandFixture(0, nullptr) {
394 SetEnv("NAME", "VALUE");
398 // If we have access to setenv, we can run some more fine-grained tests.
399 template <typename ParamType
= char>
400 void SetEnv(const ParamType
*name
, const ParamType
*value
,
401 decltype(setenv(name
, value
, 1)) *Enabled
= nullptr) {
402 ASSERT_EQ(0, setenv(name
, value
, /*overwrite=*/1));
406 // Fallback method if setenv is not available.
407 template <typename Unused
= void> void SetEnv(const void *, const void *) {}
409 bool EnableFineGrainedTests() const { return canSetEnv
; }
412 bool canSetEnv
{false};
415 TEST_F(EnvironmentVariables
, Nonexistent
) {
416 CheckMissingEnvVarValue("DOESNT_EXIST");
417 CheckMissingEnvVarValue(" ");
418 CheckMissingEnvVarValue("");
421 TEST_F(EnvironmentVariables
, Basic
) {
422 // Test a variable that's expected to exist in the environment.
423 char *path
{std::getenv("PATH")};
424 auto expectedLen
{static_cast<int64_t>(std::strlen(path
))};
425 OwningPtr
<Descriptor
> length
{EmptyIntDescriptor()};
427 RTNAME(GetEnvVariable
)(*CharDescriptor("PATH"),
428 /*value=*/nullptr, length
.get()));
429 CheckDescriptorEqInt(length
.get(), expectedLen
);
432 TEST_F(EnvironmentVariables
, Trim
) {
433 if (EnableFineGrainedTests()) {
434 CheckEnvVarValue("VALUE", "NAME ");
438 TEST_F(EnvironmentVariables
, NoTrim
) {
439 if (EnableFineGrainedTests()) {
440 CheckMissingEnvVarValue("NAME ", /*trim_name=*/false);
444 TEST_F(EnvironmentVariables
, Empty
) {
445 if (EnableFineGrainedTests()) {
446 CheckEnvVarValue("", "EMPTY");
450 TEST_F(EnvironmentVariables
, NoValueOrErrmsg
) {
451 ASSERT_EQ(std::getenv("DOESNT_EXIST"), nullptr)
452 << "Environment variable DOESNT_EXIST actually exists";
453 EXPECT_EQ(RTNAME(GetEnvVariable
)(*CharDescriptor("DOESNT_EXIST")), 1);
455 if (EnableFineGrainedTests()) {
456 EXPECT_EQ(RTNAME(GetEnvVariable
)(*CharDescriptor("NAME")), 0);
460 TEST_F(EnvironmentVariables
, ValueTooShort
) {
461 if (EnableFineGrainedTests()) {
462 OwningPtr
<Descriptor
> tooShort
{CreateEmptyCharDescriptor
<2>()};
463 ASSERT_NE(tooShort
, nullptr);
464 EXPECT_EQ(RTNAME(GetEnvVariable
)(*CharDescriptor("NAME"), tooShort
.get(),
465 /*length=*/nullptr, /*trim_name=*/true, nullptr),
467 CheckDescriptorEqStr(tooShort
.get(), "VALUE");
469 OwningPtr
<Descriptor
> errMsg
{CreateEmptyCharDescriptor()};
470 ASSERT_NE(errMsg
, nullptr);
472 EXPECT_EQ(RTNAME(GetEnvVariable
)(*CharDescriptor("NAME"), tooShort
.get(),
473 /*length=*/nullptr, /*trim_name=*/true, errMsg
.get()),
476 std::string expectedErrMsg
{
477 GetPaddedStr("Value too short", errMsg
->ElementBytes())};
478 CheckDescriptorEqStr(errMsg
.get(), expectedErrMsg
);
482 TEST_F(EnvironmentVariables
, ErrMsgTooShort
) {
483 ASSERT_EQ(std::getenv("DOESNT_EXIST"), nullptr)
484 << "Environment variable DOESNT_EXIST actually exists";
486 OwningPtr
<Descriptor
> errMsg
{CreateEmptyCharDescriptor
<3>()};
487 EXPECT_EQ(RTNAME(GetEnvVariable
)(*CharDescriptor("DOESNT_EXIST"), nullptr,
488 /*length=*/nullptr, /*trim_name=*/true, errMsg
.get()),
490 CheckDescriptorEqStr(errMsg
.get(), "Mis");