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/execute.h"
14#include "flang/Runtime/extensions.h"
15#include "flang/Runtime/main.h"
16#include <cstddef>
17#include <cstdlib>
18
19#if _REENTRANT || _POSIX_C_SOURCE >= 199506L
20#include <limits.h> // LOGIN_NAME_MAX used in getlog test
21#endif
22
23using namespace Fortran::runtime;
24
25template <std::size_t n = 64>
26static OwningPtr<Descriptor> CreateEmptyCharDescriptor() {
27 OwningPtr<Descriptor> descriptor{Descriptor::Create(
28 sizeof(char), n, nullptr, 0, nullptr, CFI_attribute_allocatable)};
29 if (descriptor->Allocate() != 0) {
30 return nullptr;
31 }
32 return descriptor;
33}
34
35static OwningPtr<Descriptor> CharDescriptor(const char *value) {
36 std::size_t n{std::strlen(s: value)};
37 OwningPtr<Descriptor> descriptor{Descriptor::Create(
38 sizeof(char), n, nullptr, 0, nullptr, CFI_attribute_allocatable)};
39 if (descriptor->Allocate() != 0) {
40 return nullptr;
41 }
42 std::memcpy(dest: descriptor->OffsetElement(), src: value, n: n);
43 return descriptor;
44}
45
46template <int kind = sizeof(std::int64_t)>
47static OwningPtr<Descriptor> EmptyIntDescriptor() {
48 OwningPtr<Descriptor> descriptor{Descriptor::Create(TypeCategory::Integer,
49 kind, nullptr, 0, nullptr, CFI_attribute_allocatable)};
50 if (descriptor->Allocate() != 0) {
51 return nullptr;
52 }
53 return descriptor;
54}
55
56template <int kind = sizeof(std::int64_t)>
57static OwningPtr<Descriptor> IntDescriptor(const int &value) {
58 OwningPtr<Descriptor> descriptor{Descriptor::Create(TypeCategory::Integer,
59 kind, nullptr, 0, nullptr, CFI_attribute_allocatable)};
60 if (descriptor->Allocate() != 0) {
61 return nullptr;
62 }
63 std::memcpy(dest: descriptor->OffsetElement<int>(), src: &value, n: sizeof(int));
64 return descriptor;
65}
66
67class CommandFixture : public ::testing::Test {
68protected:
69 CommandFixture(int argc, const char *argv[]) {
70 RTNAME(ProgramStart)(argc, argv, {}, {});
71 }
72
73 std::string GetPaddedStr(const char *text, std::size_t len) const {
74 std::string res{text};
75 assert(res.length() <= len && "No room to pad");
76 res.append(n: len - res.length(), c: ' ');
77 return res;
78 }
79
80 void CheckCharEqStr(const char *value, const std::string &expected) const {
81 ASSERT_NE(value, nullptr);
82 EXPECT_EQ(std::strncmp(value, expected.c_str(), expected.size()), 0)
83 << "expected: " << expected << "\n"
84 << "value: " << value;
85 }
86
87 void CheckDescriptorEqStr(
88 const Descriptor *value, const std::string &expected) const {
89 ASSERT_NE(value, nullptr);
90 EXPECT_EQ(std::strncmp(value->OffsetElement(), expected.c_str(),
91 value->ElementBytes()),
92 0)
93 << "expected: " << expected << "\n"
94 << "value: "
95 << std::string{value->OffsetElement(), value->ElementBytes()};
96 }
97
98 template <typename INT_T = std::int64_t>
99 void CheckDescriptorEqInt(
100 const Descriptor *value, const INT_T expected) const {
101 if (expected != -1) {
102 ASSERT_NE(value, nullptr);
103 EXPECT_EQ(*value->OffsetElement<INT_T>(), expected);
104 }
105 }
106
107 template <typename RuntimeCall>
108 void CheckValue(RuntimeCall F, const char *expectedValue,
109 std::int64_t expectedLength = -1, std::int32_t expectedStatus = 0,
110 const char *expectedErrMsg = "shouldn't change") const {
111 OwningPtr<Descriptor> value{CreateEmptyCharDescriptor()};
112 ASSERT_NE(value, nullptr);
113
114 OwningPtr<Descriptor> length{
115 expectedLength == -1 ? nullptr : EmptyIntDescriptor()};
116
117 OwningPtr<Descriptor> errmsg{CharDescriptor(expectedErrMsg)};
118 ASSERT_NE(errmsg, nullptr);
119
120 std::string expectedValueStr{
121 GetPaddedStr(text: expectedValue, len: value->ElementBytes())};
122
123 EXPECT_EQ(F(value.get(), length.get(), errmsg.get()), expectedStatus);
124 CheckDescriptorEqStr(value.get(), expectedValueStr);
125 CheckDescriptorEqInt(length.get(), expectedLength);
126 CheckDescriptorEqStr(errmsg.get(), expectedErrMsg);
127 }
128
129 void CheckArgumentValue(const char *expectedValue, int n) const {
130 SCOPED_TRACE(n);
131 SCOPED_TRACE("Checking argument:");
132 CheckValue(
133 F: [&](const Descriptor *value, const Descriptor *length,
134 const Descriptor *errmsg) {
135 return RTNAME(GetCommandArgument)(n, value, length, errmsg);
136 },
137 expectedValue, expectedLength: std::strlen(s: expectedValue));
138 }
139
140 void CheckCommandValue(const char *args[], int n) const {
141 SCOPED_TRACE("Checking command:");
142 ASSERT_GE(n, 1);
143 std::string expectedValue{args[0]};
144 for (int i = 1; i < n; i++) {
145 expectedValue += " " + std::string{args[i]};
146 }
147 CheckValue(
148 F: [&](const Descriptor *value, const Descriptor *length,
149 const Descriptor *errmsg) {
150 return RTNAME(GetCommand)(value, length, errmsg);
151 },
152 expectedValue: expectedValue.c_str(), expectedLength: expectedValue.size());
153 }
154
155 void CheckEnvVarValue(
156 const char *expectedValue, const char *name, bool trimName = true) const {
157 SCOPED_TRACE(name);
158 SCOPED_TRACE("Checking environment variable");
159 CheckValue(
160 F: [&](const Descriptor *value, const Descriptor *length,
161 const Descriptor *errmsg) {
162 return RTNAME(GetEnvVariable)(
163 *CharDescriptor(name), value, length, trimName, errmsg);
164 },
165 expectedValue, expectedLength: std::strlen(s: expectedValue));
166 }
167
168 void CheckMissingEnvVarValue(const char *name, bool trimName = true) const {
169 SCOPED_TRACE(name);
170 SCOPED_TRACE("Checking missing environment variable");
171
172 ASSERT_EQ(nullptr, std::getenv(name))
173 << "Environment variable " << name << " not expected to exist";
174
175 CheckValue(
176 F: [&](const Descriptor *value, const Descriptor *length,
177 const Descriptor *errmsg) {
178 return RTNAME(GetEnvVariable)(
179 *CharDescriptor(name), value, length, trimName, errmsg);
180 },
181 expectedValue: "", expectedLength: 0, expectedStatus: 1, expectedErrMsg: "Missing environment variable");
182 }
183
184 void CheckMissingArgumentValue(int n, const char *errStr = nullptr) const {
185 OwningPtr<Descriptor> value{CreateEmptyCharDescriptor()};
186 ASSERT_NE(value, nullptr);
187
188 OwningPtr<Descriptor> length{EmptyIntDescriptor()};
189 ASSERT_NE(length, nullptr);
190
191 OwningPtr<Descriptor> err{errStr ? CreateEmptyCharDescriptor() : nullptr};
192
193 EXPECT_GT(
194 RTNAME(GetCommandArgument)(n, value.get(), length.get(), err.get()), 0);
195
196 std::string spaces(value->ElementBytes(), ' ');
197 CheckDescriptorEqStr(value.get(), spaces);
198
199 CheckDescriptorEqInt<std::int64_t>(length.get(), 0);
200
201 if (errStr) {
202 std::string paddedErrStr(GetPaddedStr(text: errStr, len: err->ElementBytes()));
203 CheckDescriptorEqStr(err.get(), paddedErrStr);
204 }
205 }
206
207 void CheckMissingCommandValue(const char *errStr = nullptr) const {
208 OwningPtr<Descriptor> value{CreateEmptyCharDescriptor()};
209 ASSERT_NE(value, nullptr);
210
211 OwningPtr<Descriptor> length{EmptyIntDescriptor()};
212 ASSERT_NE(length, nullptr);
213
214 OwningPtr<Descriptor> err{errStr ? CreateEmptyCharDescriptor() : nullptr};
215
216 EXPECT_GT(RTNAME(GetCommand)(value.get(), length.get(), err.get()), 0);
217
218 std::string spaces(value->ElementBytes(), ' ');
219 CheckDescriptorEqStr(value.get(), spaces);
220
221 CheckDescriptorEqInt<std::int64_t>(length.get(), 0);
222
223 if (errStr) {
224 std::string paddedErrStr(GetPaddedStr(text: errStr, len: err->ElementBytes()));
225 CheckDescriptorEqStr(err.get(), paddedErrStr);
226 }
227 }
228};
229
230class NoArgv : public CommandFixture {
231protected:
232 NoArgv() : CommandFixture(0, nullptr) {}
233};
234
235#if _WIN32 || _POSIX_C_SOURCE >= 1 || _XOPEN_SOURCE || _BSD_SOURCE || \
236 _SVID_SOURCE || defined(_POSIX_SOURCE)
237TEST_F(NoArgv, FdateGetDate) {
238 char input[]{"24LengthCharIsJustRight"};
239 const std::size_t charLen = sizeof(input);
240
241 FORTRAN_PROCEDURE_NAME(fdate)(input, charLen);
242
243 // Tue May 26 21:51:03 2015\n\0
244 // index at 3, 7, 10, 19 should be space
245 // when date is less than two digit, index 8 would be space
246 // Tue May 6 21:51:03 2015\n\0
247 for (std::size_t i{0}; i < charLen; i++) {
248 if (i == 8)
249 continue;
250 if (i == 3 || i == 7 || i == 10 || i == 19) {
251 EXPECT_EQ(input[i], ' ');
252 continue;
253 }
254 EXPECT_NE(input[i], ' ');
255 }
256}
257
258TEST_F(NoArgv, FdateGetDateTooShort) {
259 char input[]{"TooShortAllPadSpace"};
260 const std::size_t charLen = sizeof(input);
261
262 FORTRAN_PROCEDURE_NAME(fdate)(input, charLen);
263
264 for (std::size_t i{0}; i < charLen; i++) {
265 EXPECT_EQ(input[i], ' ');
266 }
267}
268
269TEST_F(NoArgv, FdateGetDatePadSpace) {
270 char input[]{"All char after 23 pad spaces"};
271 const std::size_t charLen = sizeof(input);
272
273 FORTRAN_PROCEDURE_NAME(fdate)(input, charLen);
274
275 for (std::size_t i{24}; i < charLen; i++) {
276 EXPECT_EQ(input[i], ' ');
277 }
278}
279
280#else
281TEST_F(NoArgv, FdateNotSupported) {
282 char input[]{"No change due to crash"};
283
284 EXPECT_DEATH(FORTRAN_PROCEDURE_NAME(fdate)(input, sizeof(input)),
285 "fdate is not supported.");
286
287 CheckCharEqStr(input, "No change due to crash");
288}
289#endif
290
291// TODO: Test other intrinsics with this fixture.
292
293TEST_F(NoArgv, GetCommand) { CheckMissingCommandValue(); }
294
295static const char *commandOnlyArgv[]{"aProgram"};
296class ZeroArguments : public CommandFixture {
297protected:
298 ZeroArguments() : CommandFixture(1, commandOnlyArgv) {}
299};
300
301TEST_F(ZeroArguments, ArgumentCount) { EXPECT_EQ(0, RTNAME(ArgumentCount)()); }
302
303TEST_F(ZeroArguments, GetCommandArgument) {
304 CheckMissingArgumentValue(n: -1);
305 CheckArgumentValue(expectedValue: commandOnlyArgv[0], n: 0);
306 CheckMissingArgumentValue(n: 1);
307}
308
309TEST_F(ZeroArguments, GetCommand) { CheckCommandValue(args: commandOnlyArgv, n: 1); }
310
311TEST_F(ZeroArguments, ECLValidCommandAndPadSync) {
312 OwningPtr<Descriptor> command{CharDescriptor("echo hi")};
313 bool wait{true};
314 OwningPtr<Descriptor> exitStat{EmptyIntDescriptor()};
315 OwningPtr<Descriptor> cmdStat{EmptyIntDescriptor()};
316 OwningPtr<Descriptor> cmdMsg{CharDescriptor("No change")};
317
318 RTNAME(ExecuteCommandLine)
319 (*command.get(), wait, exitStat.get(), cmdStat.get(), cmdMsg.get());
320
321 std::string spaces(cmdMsg->ElementBytes(), ' ');
322 CheckDescriptorEqInt<std::int64_t>(exitStat.get(), 0);
323 CheckDescriptorEqInt<std::int64_t>(cmdStat.get(), 0);
324 CheckDescriptorEqStr(cmdMsg.get(), "No change");
325}
326
327TEST_F(ZeroArguments, ECLValidCommandStatusSetSync) {
328 OwningPtr<Descriptor> command{CharDescriptor("echo hi")};
329 bool wait{true};
330 OwningPtr<Descriptor> exitStat{IntDescriptor(404)};
331 OwningPtr<Descriptor> cmdStat{IntDescriptor(202)};
332 OwningPtr<Descriptor> cmdMsg{CharDescriptor("No change")};
333
334 RTNAME(ExecuteCommandLine)
335 (*command.get(), wait, exitStat.get(), cmdStat.get(), cmdMsg.get());
336
337 CheckDescriptorEqInt<std::int64_t>(exitStat.get(), 0);
338 CheckDescriptorEqInt<std::int64_t>(cmdStat.get(), 0);
339 CheckDescriptorEqStr(cmdMsg.get(), "No change");
340}
341
342TEST_F(ZeroArguments, ECLInvalidCommandErrorSync) {
343 OwningPtr<Descriptor> command{CharDescriptor("InvalidCommand")};
344 bool wait{true};
345 OwningPtr<Descriptor> exitStat{IntDescriptor(404)};
346 OwningPtr<Descriptor> cmdStat{IntDescriptor(202)};
347 OwningPtr<Descriptor> cmdMsg{CharDescriptor("Message ChangedXXXXXXXXX")};
348
349 RTNAME(ExecuteCommandLine)
350 (*command.get(), wait, exitStat.get(), cmdStat.get(), cmdMsg.get());
351#ifdef _WIN32
352 CheckDescriptorEqInt(exitStat.get(), 1);
353#else
354 CheckDescriptorEqInt<std::int64_t>(exitStat.get(), 127);
355#endif
356 CheckDescriptorEqInt<std::int64_t>(cmdStat.get(), 3);
357 CheckDescriptorEqStr(cmdMsg.get(), "Invalid command lineXXXX");
358}
359
360TEST_F(ZeroArguments, ECLInvalidCommandTerminatedSync) {
361 OwningPtr<Descriptor> command{CharDescriptor("InvalidCommand")};
362 bool wait{true};
363 OwningPtr<Descriptor> exitStat{IntDescriptor(404)};
364 OwningPtr<Descriptor> cmdMsg{CharDescriptor("No Change")};
365
366#ifdef _WIN32
367 EXPECT_DEATH(RTNAME(ExecuteCommandLine)(
368 *command.get(), wait, exitStat.get(), nullptr, cmdMsg.get()),
369 "Invalid command quit with exit status code: 1");
370#else
371 EXPECT_DEATH(RTNAME(ExecuteCommandLine)(
372 *command.get(), wait, exitStat.get(), nullptr, cmdMsg.get()),
373 "Invalid command quit with exit status code: 127");
374#endif
375 CheckDescriptorEqInt(exitStat.get(), 404);
376 CheckDescriptorEqStr(cmdMsg.get(), "No Change");
377}
378
379TEST_F(ZeroArguments, ECLValidCommandAndExitStatNoChangeAndCMDStatusSetAsync) {
380 OwningPtr<Descriptor> command{CharDescriptor("echo hi")};
381 bool wait{false};
382 OwningPtr<Descriptor> exitStat{IntDescriptor(404)};
383 OwningPtr<Descriptor> cmdStat{IntDescriptor(202)};
384 OwningPtr<Descriptor> cmdMsg{CharDescriptor("No change")};
385
386 RTNAME(ExecuteCommandLine)
387 (*command.get(), wait, exitStat.get(), cmdStat.get(), cmdMsg.get());
388
389 CheckDescriptorEqInt(exitStat.get(), 404);
390 CheckDescriptorEqInt<std::int64_t>(cmdStat.get(), 0);
391 CheckDescriptorEqStr(cmdMsg.get(), "No change");
392}
393
394TEST_F(ZeroArguments, ECLInvalidCommandParentNotTerminatedAsync) {
395 OwningPtr<Descriptor> command{CharDescriptor("InvalidCommand")};
396 bool wait{false};
397 OwningPtr<Descriptor> exitStat{IntDescriptor(404)};
398 OwningPtr<Descriptor> cmdMsg{CharDescriptor("No change")};
399
400 EXPECT_NO_FATAL_FAILURE(RTNAME(ExecuteCommandLine)(
401 *command.get(), wait, exitStat.get(), nullptr, cmdMsg.get()));
402
403 CheckDescriptorEqInt(exitStat.get(), 404);
404 CheckDescriptorEqStr(cmdMsg.get(), "No change");
405}
406
407TEST_F(ZeroArguments, ECLInvalidCommandAsyncDontAffectSync) {
408 OwningPtr<Descriptor> command{CharDescriptor("echo hi")};
409
410 EXPECT_NO_FATAL_FAILURE(RTNAME(ExecuteCommandLine)(
411 *command.get(), false, nullptr, nullptr, nullptr));
412 EXPECT_NO_FATAL_FAILURE(RTNAME(ExecuteCommandLine)(
413 *command.get(), true, nullptr, nullptr, nullptr));
414}
415
416TEST_F(ZeroArguments, ECLInvalidCommandAsyncDontAffectAsync) {
417 OwningPtr<Descriptor> command{CharDescriptor("echo hi")};
418
419 EXPECT_NO_FATAL_FAILURE(RTNAME(ExecuteCommandLine)(
420 *command.get(), false, nullptr, nullptr, nullptr));
421 EXPECT_NO_FATAL_FAILURE(RTNAME(ExecuteCommandLine)(
422 *command.get(), false, nullptr, nullptr, nullptr));
423}
424
425TEST_F(ZeroArguments, SystemValidCommandExitStat) {
426 // envrionment setup for SYSTEM from EXECUTE_COMMAND_LINE runtime
427 OwningPtr<Descriptor> cmdStat{IntDescriptor(202)};
428 bool wait{true};
429 // setup finished
430
431 OwningPtr<Descriptor> command{CharDescriptor("echo hi")};
432 OwningPtr<Descriptor> exitStat{EmptyIntDescriptor()};
433
434 RTNAME(ExecuteCommandLine)
435 (*command.get(), wait, exitStat.get(), cmdStat.get(), nullptr);
436 CheckDescriptorEqInt<std::int64_t>(exitStat.get(), 0);
437}
438
439TEST_F(ZeroArguments, SystemInvalidCommandExitStat) {
440 // envrionment setup for SYSTEM from EXECUTE_COMMAND_LINE runtime
441 OwningPtr<Descriptor> cmdStat{IntDescriptor(202)};
442 bool wait{true};
443 // setup finished
444
445 OwningPtr<Descriptor> command{CharDescriptor("InvalidCommand")};
446 OwningPtr<Descriptor> exitStat{EmptyIntDescriptor()};
447
448 RTNAME(ExecuteCommandLine)
449 (*command.get(), wait, exitStat.get(), cmdStat.get(), nullptr);
450#ifdef _WIN32
451 CheckDescriptorEqInt<std::int64_t>(exitStat.get(), 1);
452#else
453 CheckDescriptorEqInt<std::int64_t>(exitStat.get(), 127);
454#endif
455}
456
457TEST_F(ZeroArguments, SystemValidCommandOptionalExitStat) {
458 // envrionment setup for SYSTEM from EXECUTE_COMMAND_LINE runtime
459 OwningPtr<Descriptor> cmdStat{IntDescriptor(202)};
460 bool wait{true};
461 // setup finished
462
463 OwningPtr<Descriptor> command{CharDescriptor("echo hi")};
464 EXPECT_NO_FATAL_FAILURE(RTNAME(ExecuteCommandLine)(
465 *command.get(), wait, nullptr, cmdStat.get(), nullptr));
466}
467
468TEST_F(ZeroArguments, SystemInvalidCommandOptionalExitStat) {
469 // envrionment setup for SYSTEM from EXECUTE_COMMAND_LINE runtime
470 OwningPtr<Descriptor> cmdStat{IntDescriptor(202)};
471 bool wait{true};
472 // setup finished
473
474 OwningPtr<Descriptor> command{CharDescriptor("InvalidCommand")};
475 EXPECT_NO_FATAL_FAILURE(RTNAME(ExecuteCommandLine)(
476 *command.get(), wait, nullptr, cmdStat.get(), nullptr););
477}
478
479static const char *oneArgArgv[]{"aProgram", "anArgumentOfLength20"};
480class OneArgument : public CommandFixture {
481protected:
482 OneArgument() : CommandFixture(2, oneArgArgv) {}
483};
484
485TEST_F(OneArgument, ArgumentCount) { EXPECT_EQ(1, RTNAME(ArgumentCount)()); }
486
487TEST_F(OneArgument, GetCommandArgument) {
488 CheckMissingArgumentValue(n: -1);
489 CheckArgumentValue(expectedValue: oneArgArgv[0], n: 0);
490 CheckArgumentValue(expectedValue: oneArgArgv[1], n: 1);
491 CheckMissingArgumentValue(n: 2);
492}
493
494TEST_F(OneArgument, GetCommand) { CheckCommandValue(args: oneArgArgv, n: 2); }
495
496static const char *severalArgsArgv[]{
497 "aProgram", "16-char-long-arg", "", "-22-character-long-arg", "o"};
498class SeveralArguments : public CommandFixture {
499protected:
500 SeveralArguments()
501 : CommandFixture(sizeof(severalArgsArgv) / sizeof(*severalArgsArgv),
502 severalArgsArgv) {}
503};
504
505TEST_F(SeveralArguments, ArgumentCount) {
506 EXPECT_EQ(4, RTNAME(ArgumentCount)());
507}
508
509TEST_F(SeveralArguments, GetCommandArgument) {
510 CheckArgumentValue(expectedValue: severalArgsArgv[0], n: 0);
511 CheckArgumentValue(expectedValue: severalArgsArgv[1], n: 1);
512 CheckArgumentValue(expectedValue: severalArgsArgv[3], n: 3);
513 CheckArgumentValue(expectedValue: severalArgsArgv[4], n: 4);
514}
515
516TEST_F(SeveralArguments, NoArgumentValue) {
517 // Make sure we don't crash if the 'value', 'length' and 'error' parameters
518 // aren't passed.
519 EXPECT_GT(RTNAME(GetCommandArgument)(2), 0);
520 EXPECT_EQ(RTNAME(GetCommandArgument)(1), 0);
521 EXPECT_GT(RTNAME(GetCommandArgument)(-1), 0);
522}
523
524TEST_F(SeveralArguments, MissingArguments) {
525 CheckMissingArgumentValue(n: -1, errStr: "Invalid argument number");
526 CheckMissingArgumentValue(n: 2, errStr: "Missing argument");
527 CheckMissingArgumentValue(n: 5, errStr: "Invalid argument number");
528 CheckMissingArgumentValue(n: 5);
529}
530
531TEST_F(SeveralArguments, ArgValueTooShort) {
532 OwningPtr<Descriptor> tooShort{CreateEmptyCharDescriptor<15>()};
533 ASSERT_NE(tooShort, nullptr);
534 EXPECT_EQ(RTNAME(GetCommandArgument)(1, tooShort.get()), -1);
535 CheckDescriptorEqStr(tooShort.get(), severalArgsArgv[1]);
536
537 OwningPtr<Descriptor> length{EmptyIntDescriptor()};
538 ASSERT_NE(length, nullptr);
539 OwningPtr<Descriptor> errMsg{CreateEmptyCharDescriptor()};
540 ASSERT_NE(errMsg, nullptr);
541
542 EXPECT_EQ(
543 RTNAME(GetCommandArgument)(1, tooShort.get(), length.get(), errMsg.get()),
544 -1);
545
546 CheckDescriptorEqInt<std::int64_t>(length.get(), 16);
547 std::string expectedErrMsg{
548 GetPaddedStr(text: "Value too short", len: errMsg->ElementBytes())};
549 CheckDescriptorEqStr(errMsg.get(), expectedErrMsg);
550}
551
552TEST_F(SeveralArguments, ArgErrMsgTooShort) {
553 OwningPtr<Descriptor> errMsg{CreateEmptyCharDescriptor<3>()};
554 EXPECT_GT(RTNAME(GetCommandArgument)(-1, nullptr, nullptr, errMsg.get()), 0);
555 CheckDescriptorEqStr(errMsg.get(), "Inv");
556}
557
558TEST_F(SeveralArguments, GetCommand) {
559 CheckMissingCommandValue();
560 CheckMissingCommandValue(errStr: "Missing argument");
561}
562
563TEST_F(SeveralArguments, CommandErrMsgTooShort) {
564 OwningPtr<Descriptor> value{CreateEmptyCharDescriptor()};
565 OwningPtr<Descriptor> length{EmptyIntDescriptor()};
566 OwningPtr<Descriptor> errMsg{CreateEmptyCharDescriptor<3>()};
567
568 EXPECT_GT(RTNAME(GetCommand)(value.get(), length.get(), errMsg.get()), 0);
569
570 std::string spaces(value->ElementBytes(), ' ');
571 CheckDescriptorEqStr(value.get(), spaces);
572 CheckDescriptorEqInt<std::int64_t>(length.get(), 0);
573 CheckDescriptorEqStr(errMsg.get(), "Mis");
574}
575
576TEST_F(SeveralArguments, GetCommandCanTakeNull) {
577 EXPECT_GT(RTNAME(GetCommand)(nullptr, nullptr, nullptr), 0);
578}
579
580static const char *onlyValidArgsArgv[]{
581 "aProgram", "-f", "has/a/few/slashes", "has\\a\\few\\backslashes"};
582class OnlyValidArguments : public CommandFixture {
583protected:
584 OnlyValidArguments()
585 : CommandFixture(sizeof(onlyValidArgsArgv) / sizeof(*onlyValidArgsArgv),
586 onlyValidArgsArgv) {}
587};
588
589TEST_F(OnlyValidArguments, GetCommand) {
590 CheckCommandValue(args: onlyValidArgsArgv, n: 4);
591}
592
593TEST_F(OnlyValidArguments, CommandValueTooShort) {
594 OwningPtr<Descriptor> tooShort{CreateEmptyCharDescriptor<50>()};
595 ASSERT_NE(tooShort, nullptr);
596 OwningPtr<Descriptor> length{EmptyIntDescriptor()};
597 ASSERT_NE(length, nullptr);
598
599 EXPECT_EQ(RTNAME(GetCommand)(tooShort.get(), length.get(), nullptr), -1);
600
601 CheckDescriptorEqStr(
602 tooShort.get(), "aProgram -f has/a/few/slashes has\\a\\few\\backslashe");
603 CheckDescriptorEqInt<std::int64_t>(length.get(), 51);
604
605 OwningPtr<Descriptor> errMsg{CreateEmptyCharDescriptor()};
606 ASSERT_NE(errMsg, nullptr);
607
608 EXPECT_EQ(-1, RTNAME(GetCommand)(tooShort.get(), nullptr, errMsg.get()));
609
610 std::string expectedErrMsg{
611 GetPaddedStr(text: "Value too short", len: errMsg->ElementBytes())};
612 CheckDescriptorEqStr(errMsg.get(), expectedErrMsg);
613}
614
615TEST_F(OnlyValidArguments, GetCommandCanTakeNull) {
616 EXPECT_EQ(0, RTNAME(GetCommand)(nullptr, nullptr, nullptr));
617
618 OwningPtr<Descriptor> value{CreateEmptyCharDescriptor()};
619 ASSERT_NE(value, nullptr);
620 OwningPtr<Descriptor> length{EmptyIntDescriptor()};
621 ASSERT_NE(length, nullptr);
622
623 EXPECT_EQ(0, RTNAME(GetCommand)(value.get(), nullptr, nullptr));
624 CheckDescriptorEqStr(value.get(),
625 GetPaddedStr("aProgram -f has/a/few/slashes has\\a\\few\\backslashes",
626 value->ElementBytes()));
627
628 EXPECT_EQ(0, RTNAME(GetCommand)(nullptr, length.get(), nullptr));
629 CheckDescriptorEqInt<std::int64_t>(length.get(), 51);
630}
631
632TEST_F(OnlyValidArguments, GetCommandShortLength) {
633 OwningPtr<Descriptor> length{EmptyIntDescriptor<sizeof(short)>()};
634 ASSERT_NE(length, nullptr);
635
636 EXPECT_EQ(0, RTNAME(GetCommand)(nullptr, length.get(), nullptr));
637 CheckDescriptorEqInt<short>(length.get(), 51);
638}
639
640TEST_F(ZeroArguments, GetPID) {
641 // pid should always greater than 0, in both linux and windows
642 EXPECT_GT(RTNAME(GetPID)(), 0);
643}
644
645class EnvironmentVariables : public CommandFixture {
646protected:
647 EnvironmentVariables() : CommandFixture(0, nullptr) {
648 SetEnv(name: "NAME", value: "VALUE");
649#ifdef _WIN32
650 SetEnv("USERNAME", "loginName");
651#else
652 SetEnv(name: "LOGNAME", value: "loginName");
653#endif
654 SetEnv(name: "EMPTY", value: "");
655 }
656
657 // If we have access to setenv, we can run some more fine-grained tests.
658 template <typename ParamType = char>
659 void SetEnv(const ParamType *name, const ParamType *value,
660 decltype(setenv(name, value, 1)) *Enabled = nullptr) {
661 ASSERT_EQ(0, setenv(name, value, /*overwrite=*/1));
662 canSetEnv = true;
663 }
664
665 // Fallback method if setenv is not available.
666 template <typename Unused = void> void SetEnv(const void *, const void *) {}
667
668 bool EnableFineGrainedTests() const { return canSetEnv; }
669
670private:
671 bool canSetEnv{false};
672};
673
674TEST_F(EnvironmentVariables, Nonexistent) {
675 CheckMissingEnvVarValue(name: "DOESNT_EXIST");
676 CheckMissingEnvVarValue(name: " ");
677 CheckMissingEnvVarValue(name: "");
678}
679
680TEST_F(EnvironmentVariables, Basic) {
681 // Test a variable that's expected to exist in the environment.
682 char *path{std::getenv(name: "PATH")};
683 auto expectedLen{static_cast<int64_t>(std::strlen(s: path))};
684 OwningPtr<Descriptor> length{EmptyIntDescriptor()};
685 EXPECT_EQ(0,
686 RTNAME(GetEnvVariable)(*CharDescriptor("PATH"),
687 /*value=*/nullptr, length.get()));
688 CheckDescriptorEqInt(length.get(), expectedLen);
689}
690
691TEST_F(EnvironmentVariables, Trim) {
692 if (EnableFineGrainedTests()) {
693 CheckEnvVarValue(expectedValue: "VALUE", name: "NAME ");
694 }
695}
696
697TEST_F(EnvironmentVariables, NoTrim) {
698 if (EnableFineGrainedTests()) {
699 CheckMissingEnvVarValue(name: "NAME ", /*trim_name=*/trimName: false);
700 }
701}
702
703TEST_F(EnvironmentVariables, Empty) {
704 if (EnableFineGrainedTests()) {
705 CheckEnvVarValue(expectedValue: "", name: "EMPTY");
706 }
707}
708
709TEST_F(EnvironmentVariables, NoValueOrErrmsg) {
710 ASSERT_EQ(std::getenv("DOESNT_EXIST"), nullptr)
711 << "Environment variable DOESNT_EXIST actually exists";
712 EXPECT_EQ(RTNAME(GetEnvVariable)(*CharDescriptor("DOESNT_EXIST")), 1);
713
714 if (EnableFineGrainedTests()) {
715 EXPECT_EQ(RTNAME(GetEnvVariable)(*CharDescriptor("NAME")), 0);
716 }
717}
718
719TEST_F(EnvironmentVariables, ValueTooShort) {
720 if (EnableFineGrainedTests()) {
721 OwningPtr<Descriptor> tooShort{CreateEmptyCharDescriptor<2>()};
722 ASSERT_NE(tooShort, nullptr);
723 EXPECT_EQ(RTNAME(GetEnvVariable)(*CharDescriptor("NAME"), tooShort.get(),
724 /*length=*/nullptr, /*trim_name=*/true, nullptr),
725 -1);
726 CheckDescriptorEqStr(tooShort.get(), "VALUE");
727
728 OwningPtr<Descriptor> errMsg{CreateEmptyCharDescriptor()};
729 ASSERT_NE(errMsg, nullptr);
730
731 EXPECT_EQ(RTNAME(GetEnvVariable)(*CharDescriptor("NAME"), tooShort.get(),
732 /*length=*/nullptr, /*trim_name=*/true, errMsg.get()),
733 -1);
734
735 std::string expectedErrMsg{
736 GetPaddedStr(text: "Value too short", len: errMsg->ElementBytes())};
737 CheckDescriptorEqStr(errMsg.get(), expectedErrMsg);
738 }
739}
740
741TEST_F(EnvironmentVariables, ErrMsgTooShort) {
742 ASSERT_EQ(std::getenv("DOESNT_EXIST"), nullptr)
743 << "Environment variable DOESNT_EXIST actually exists";
744
745 OwningPtr<Descriptor> errMsg{CreateEmptyCharDescriptor<3>()};
746 EXPECT_EQ(RTNAME(GetEnvVariable)(*CharDescriptor("DOESNT_EXIST"), nullptr,
747 /*length=*/nullptr, /*trim_name=*/true, errMsg.get()),
748 1);
749 CheckDescriptorEqStr(errMsg.get(), "Mis");
750}
751
752// username first char must not be null
753TEST_F(EnvironmentVariables, GetlogGetName) {
754 const int charLen{3};
755 char input[charLen]{"\0\0"};
756 FORTRAN_PROCEDURE_NAME(getlog)(input, charLen);
757 EXPECT_NE(input[0], '\0');
758}
759
760#if _REENTRANT || _POSIX_C_SOURCE >= 199506L
761TEST_F(EnvironmentVariables, GetlogPadSpace) {
762 // guarantee 1 char longer than max, last char should be pad space
763 int charLen;
764#ifdef LOGIN_NAME_MAX
765 charLen = LOGIN_NAME_MAX + 2;
766#else
767 charLen = sysconf(_SC_LOGIN_NAME_MAX) + 2;
768 if (charLen == -1)
769 charLen = _POSIX_LOGIN_NAME_MAX + 2;
770#endif
771 std::vector<char> input(charLen);
772 FORTRAN_PROCEDURE_NAME(getlog)(input.data(), charLen);
773 EXPECT_EQ(input[charLen - 1], ' ');
774}
775#endif
776
777#ifdef _WIN32 // Test ability to get name from environment variable
778TEST_F(EnvironmentVariables, GetlogEnvGetName) {
779 if (EnableFineGrainedTests()) {
780 ASSERT_NE(std::getenv("USERNAME"), nullptr)
781 << "Environment variable USERNAME does not exist";
782
783 char input[]{"XXXXXXXXX"};
784 FORTRAN_PROCEDURE_NAME(getlog)(input, sizeof(input));
785
786 CheckCharEqStr(input, "loginName");
787 }
788}
789
790TEST_F(EnvironmentVariables, GetlogEnvBufferShort) {
791 if (EnableFineGrainedTests()) {
792 ASSERT_NE(std::getenv("USERNAME"), nullptr)
793 << "Environment variable USERNAME does not exist";
794
795 char input[]{"XXXXXX"};
796 FORTRAN_PROCEDURE_NAME(getlog)(input, sizeof(input));
797
798 CheckCharEqStr(input, "loginN");
799 }
800}
801
802TEST_F(EnvironmentVariables, GetlogEnvPadSpace) {
803 if (EnableFineGrainedTests()) {
804 ASSERT_NE(std::getenv("USERNAME"), nullptr)
805 << "Environment variable USERNAME does not exist";
806
807 char input[]{"XXXXXXXXXX"};
808 FORTRAN_PROCEDURE_NAME(getlog)(input, sizeof(input));
809
810 CheckCharEqStr(input, "loginName ");
811 }
812}
813#endif
814

source code of flang/unittests/Runtime/CommandTest.cpp