1//===-- unittests/Runtime/CommandTest.cpp -----------------------*- C++ -*-===//
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-rt/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(kNoAsyncObject) != 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(kNoAsyncObject) != 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(kNoAsyncObject) != 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(kNoAsyncObject) != 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{IntDescriptor(404)};
315 OwningPtr<Descriptor> cmdStat{IntDescriptor(202)};
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, ECLGeneralErrorCommandErrorSync) {
343 OwningPtr<Descriptor> command{CharDescriptor(NOT_EXE)};
344 bool wait{true};
345 OwningPtr<Descriptor> exitStat{IntDescriptor(404)};
346 OwningPtr<Descriptor> cmdStat{IntDescriptor(202)};
347 OwningPtr<Descriptor> cmdMsg{CharDescriptor("cmd msg buffer XXXXXXXXXXXXXX")};
348
349 RTNAME(ExecuteCommandLine)
350 (*command.get(), wait, exitStat.get(), cmdStat.get(), cmdMsg.get());
351 CheckDescriptorEqInt<std::int64_t>(exitStat.get(), 1);
352#if defined(_WIN32)
353 CheckDescriptorEqInt<std::int64_t>(cmdStat.get(), 6);
354 CheckDescriptorEqStr(cmdMsg.get(), "Invalid command lineXXXXXXXXX");
355#else
356 CheckDescriptorEqInt<std::int64_t>(cmdStat.get(), 3);
357 CheckDescriptorEqStr(cmdMsg.get(), "Command line execution failed");
358#endif
359}
360
361TEST_F(ZeroArguments, ECLNotExecutedCommandErrorSync) {
362 OwningPtr<Descriptor> command{CharDescriptor(
363 "touch NotExecutedCommandFile && chmod -x NotExecutedCommandFile && "
364 "./NotExecutedCommandFile")};
365 bool wait{true};
366 OwningPtr<Descriptor> exitStat{IntDescriptor(404)};
367 OwningPtr<Descriptor> cmdStat{IntDescriptor(202)};
368 OwningPtr<Descriptor> cmdMsg{CharDescriptor("cmd msg buffer XXXXXXXX")};
369
370 RTNAME(ExecuteCommandLine)
371 (*command.get(), wait, exitStat.get(), cmdStat.get(), cmdMsg.get());
372#ifdef _WIN32
373 CheckDescriptorEqInt<std::int64_t>(exitStat.get(), 1);
374 CheckDescriptorEqInt<std::int64_t>(cmdStat.get(), 6);
375 CheckDescriptorEqStr(cmdMsg.get(), "Invalid command lineXXX");
376#else
377 CheckDescriptorEqInt<std::int64_t>(exitStat.get(), 126);
378 CheckDescriptorEqInt<std::int64_t>(cmdStat.get(), 4);
379 CheckDescriptorEqStr(cmdMsg.get(), "Command cannot be execu");
380 // removing the file only on Linux (file is not created on Win)
381 OwningPtr<Descriptor> commandClean{
382 CharDescriptor("rm -f NotExecutedCommandFile")};
383 OwningPtr<Descriptor> cmdMsgNoErr{CharDescriptor("No Error")};
384 RTNAME(ExecuteCommandLine)
385 (*commandClean.get(), wait, exitStat.get(), cmdStat.get(), cmdMsgNoErr.get());
386 CheckDescriptorEqInt<std::int64_t>(exitStat.get(), 0);
387 CheckDescriptorEqStr(cmdMsgNoErr.get(), "No Error");
388 CheckDescriptorEqInt<std::int64_t>(cmdStat.get(), 0);
389#endif
390}
391
392TEST_F(ZeroArguments, ECLNotFoundCommandErrorSync) {
393 OwningPtr<Descriptor> command{CharDescriptor("NotFoundCommand")};
394 bool wait{true};
395 OwningPtr<Descriptor> exitStat{IntDescriptor(404)};
396 OwningPtr<Descriptor> cmdStat{IntDescriptor(202)};
397 OwningPtr<Descriptor> cmdMsg{CharDescriptor("unmodified buffer XXXXXXXXX")};
398
399 RTNAME(ExecuteCommandLine)
400 (*command.get(), wait, exitStat.get(), cmdStat.get(), cmdMsg.get());
401#ifdef _WIN32
402 CheckDescriptorEqInt<std::int64_t>(exitStat.get(), 1);
403 CheckDescriptorEqInt<std::int64_t>(cmdStat.get(), 6);
404 CheckDescriptorEqStr(cmdMsg.get(), "Invalid command lineXXXXXXX");
405#else
406 CheckDescriptorEqInt<std::int64_t>(exitStat.get(), 127);
407 CheckDescriptorEqInt<std::int64_t>(cmdStat.get(), 5);
408 CheckDescriptorEqStr(cmdMsg.get(), "Command not found with exit");
409#endif
410}
411
412TEST_F(ZeroArguments, ECLInvalidCommandTerminatedSync) {
413 OwningPtr<Descriptor> command{CharDescriptor("InvalidCommand")};
414 bool wait{true};
415 OwningPtr<Descriptor> cmdMsg{CharDescriptor("No Change")};
416
417#ifdef _WIN32
418 EXPECT_DEATH(RTNAME(ExecuteCommandLine)(
419 *command.get(), wait, nullptr, nullptr, cmdMsg.get()),
420 "Invalid command quit with exit status code: 1");
421#else
422 EXPECT_DEATH(RTNAME(ExecuteCommandLine)(
423 *command.get(), wait, nullptr, nullptr, cmdMsg.get()),
424 "Command not found with exit code: 127.");
425#endif
426 CheckDescriptorEqStr(cmdMsg.get(), "No Change");
427}
428
429TEST_F(ZeroArguments, ECLValidCommandAndExitStatNoChangeAndCMDStatusSetAsync) {
430 OwningPtr<Descriptor> command{CharDescriptor("echo hi")};
431 bool wait{false};
432 OwningPtr<Descriptor> exitStat{IntDescriptor(404)};
433 OwningPtr<Descriptor> cmdStat{IntDescriptor(202)};
434 OwningPtr<Descriptor> cmdMsg{CharDescriptor("No change")};
435
436 RTNAME(ExecuteCommandLine)
437 (*command.get(), wait, exitStat.get(), cmdStat.get(), cmdMsg.get());
438
439 CheckDescriptorEqInt(exitStat.get(), 404);
440 CheckDescriptorEqInt<std::int64_t>(cmdStat.get(), 0);
441 CheckDescriptorEqStr(cmdMsg.get(), "No change");
442}
443
444TEST_F(ZeroArguments, ECLInvalidCommandParentNotTerminatedAsync) {
445 OwningPtr<Descriptor> command{CharDescriptor("InvalidCommand")};
446 bool wait{false};
447 OwningPtr<Descriptor> cmdMsg{CharDescriptor("No change")};
448
449 EXPECT_NO_FATAL_FAILURE(RTNAME(ExecuteCommandLine)(
450 *command.get(), wait, nullptr, nullptr, cmdMsg.get()));
451 CheckDescriptorEqStr(cmdMsg.get(), "No change");
452}
453
454TEST_F(ZeroArguments, ECLInvalidCommandAsyncDontAffectSync) {
455 OwningPtr<Descriptor> command{CharDescriptor("echo hi")};
456
457 EXPECT_NO_FATAL_FAILURE(RTNAME(ExecuteCommandLine)(
458 *command.get(), false, nullptr, nullptr, nullptr));
459 EXPECT_NO_FATAL_FAILURE(RTNAME(ExecuteCommandLine)(
460 *command.get(), true, nullptr, nullptr, nullptr));
461}
462
463TEST_F(ZeroArguments, ECLInvalidCommandAsyncDontAffectAsync) {
464 OwningPtr<Descriptor> command{CharDescriptor("echo hi")};
465
466 EXPECT_NO_FATAL_FAILURE(RTNAME(ExecuteCommandLine)(
467 *command.get(), false, nullptr, nullptr, nullptr));
468 EXPECT_NO_FATAL_FAILURE(RTNAME(ExecuteCommandLine)(
469 *command.get(), false, nullptr, nullptr, nullptr));
470}
471
472TEST_F(ZeroArguments, SystemValidCommandExitStat) {
473 // envrionment setup for SYSTEM from EXECUTE_COMMAND_LINE runtime
474 OwningPtr<Descriptor> cmdStat{IntDescriptor(202)};
475 bool wait{true};
476 // setup finished
477
478 OwningPtr<Descriptor> command{CharDescriptor("echo hi")};
479 OwningPtr<Descriptor> exitStat{EmptyIntDescriptor()};
480
481 RTNAME(ExecuteCommandLine)
482 (*command.get(), wait, exitStat.get(), cmdStat.get(), nullptr);
483 CheckDescriptorEqInt<std::int64_t>(exitStat.get(), 0);
484}
485
486TEST_F(ZeroArguments, SystemInvalidCommandExitStat) {
487 // envrionment setup for SYSTEM from EXECUTE_COMMAND_LINE runtime
488 OwningPtr<Descriptor> cmdStat{IntDescriptor(202)};
489 bool wait{true};
490 // setup finished
491
492 OwningPtr<Descriptor> command{CharDescriptor("InvalidCommand")};
493 OwningPtr<Descriptor> exitStat{EmptyIntDescriptor()};
494
495 RTNAME(ExecuteCommandLine)
496 (*command.get(), wait, exitStat.get(), cmdStat.get(), nullptr);
497#ifdef _WIN32
498 CheckDescriptorEqInt<std::int64_t>(exitStat.get(), 1);
499#else
500 CheckDescriptorEqInt<std::int64_t>(exitStat.get(), 127);
501#endif
502}
503
504TEST_F(ZeroArguments, SystemValidCommandOptionalExitStat) {
505 // envrionment setup for SYSTEM from EXECUTE_COMMAND_LINE runtime
506 OwningPtr<Descriptor> cmdStat{IntDescriptor(202)};
507 bool wait{true};
508 // setup finished
509
510 OwningPtr<Descriptor> command{CharDescriptor("echo hi")};
511 EXPECT_NO_FATAL_FAILURE(RTNAME(ExecuteCommandLine)(
512 *command.get(), wait, nullptr, cmdStat.get(), nullptr));
513}
514
515TEST_F(ZeroArguments, SystemInvalidCommandOptionalExitStat) {
516 // envrionment setup for SYSTEM from EXECUTE_COMMAND_LINE runtime
517 OwningPtr<Descriptor> cmdStat{IntDescriptor(202)};
518 bool wait{true};
519 // setup finished
520
521 OwningPtr<Descriptor> command{CharDescriptor("InvalidCommand")};
522 EXPECT_NO_FATAL_FAILURE(RTNAME(ExecuteCommandLine)(
523 *command.get(), wait, nullptr, cmdStat.get(), nullptr););
524}
525
526static const char *oneArgArgv[]{"aProgram", "anArgumentOfLength20"};
527class OneArgument : public CommandFixture {
528protected:
529 OneArgument() : CommandFixture(2, oneArgArgv) {}
530};
531
532TEST_F(OneArgument, ArgumentCount) { EXPECT_EQ(1, RTNAME(ArgumentCount)()); }
533
534TEST_F(OneArgument, GetCommandArgument) {
535 CheckMissingArgumentValue(n: -1);
536 CheckArgumentValue(expectedValue: oneArgArgv[0], n: 0);
537 CheckArgumentValue(expectedValue: oneArgArgv[1], n: 1);
538 CheckMissingArgumentValue(n: 2);
539}
540
541TEST_F(OneArgument, GetCommand) { CheckCommandValue(args: oneArgArgv, n: 2); }
542
543static const char *severalArgsArgv[]{
544 "aProgram", "16-char-long-arg", "", "-22-character-long-arg", "o"};
545class SeveralArguments : public CommandFixture {
546protected:
547 SeveralArguments()
548 : CommandFixture(sizeof(severalArgsArgv) / sizeof(*severalArgsArgv),
549 severalArgsArgv) {}
550};
551
552TEST_F(SeveralArguments, ArgumentCount) {
553 EXPECT_EQ(4, RTNAME(ArgumentCount)());
554}
555
556TEST_F(SeveralArguments, GetCommandArgument) {
557 CheckArgumentValue(expectedValue: severalArgsArgv[0], n: 0);
558 CheckArgumentValue(expectedValue: severalArgsArgv[1], n: 1);
559 CheckArgumentValue(expectedValue: severalArgsArgv[3], n: 3);
560 CheckArgumentValue(expectedValue: severalArgsArgv[4], n: 4);
561}
562
563TEST_F(SeveralArguments, NoArgumentValue) {
564 // Make sure we don't crash if the 'value', 'length' and 'error' parameters
565 // aren't passed.
566 EXPECT_GT(RTNAME(GetCommandArgument)(2), 0);
567 EXPECT_EQ(RTNAME(GetCommandArgument)(1), 0);
568 EXPECT_GT(RTNAME(GetCommandArgument)(-1), 0);
569}
570
571TEST_F(SeveralArguments, MissingArguments) {
572 CheckMissingArgumentValue(n: -1, errStr: "Invalid argument number");
573 CheckMissingArgumentValue(n: 2, errStr: "Missing argument");
574 CheckMissingArgumentValue(n: 5, errStr: "Invalid argument number");
575 CheckMissingArgumentValue(n: 5);
576}
577
578TEST_F(SeveralArguments, ArgValueTooShort) {
579 OwningPtr<Descriptor> tooShort{CreateEmptyCharDescriptor<15>()};
580 ASSERT_NE(tooShort, nullptr);
581 EXPECT_EQ(RTNAME(GetCommandArgument)(1, tooShort.get()), -1);
582 CheckDescriptorEqStr(tooShort.get(), severalArgsArgv[1]);
583
584 OwningPtr<Descriptor> length{EmptyIntDescriptor()};
585 ASSERT_NE(length, nullptr);
586 OwningPtr<Descriptor> errMsg{CreateEmptyCharDescriptor()};
587 ASSERT_NE(errMsg, nullptr);
588
589 EXPECT_EQ(
590 RTNAME(GetCommandArgument)(1, tooShort.get(), length.get(), errMsg.get()),
591 -1);
592
593 CheckDescriptorEqInt<std::int64_t>(length.get(), 16);
594 std::string expectedErrMsg{
595 GetPaddedStr(text: "Value too short", len: errMsg->ElementBytes())};
596 CheckDescriptorEqStr(errMsg.get(), expectedErrMsg);
597}
598
599TEST_F(SeveralArguments, ArgErrMsgTooShort) {
600 OwningPtr<Descriptor> errMsg{CreateEmptyCharDescriptor<3>()};
601 EXPECT_GT(RTNAME(GetCommandArgument)(-1, nullptr, nullptr, errMsg.get()), 0);
602 CheckDescriptorEqStr(errMsg.get(), "Inv");
603}
604
605TEST_F(SeveralArguments, GetCommand) {
606 CheckMissingCommandValue();
607 CheckMissingCommandValue(errStr: "Missing argument");
608}
609
610TEST_F(SeveralArguments, CommandErrMsgTooShort) {
611 OwningPtr<Descriptor> value{CreateEmptyCharDescriptor()};
612 OwningPtr<Descriptor> length{EmptyIntDescriptor()};
613 OwningPtr<Descriptor> errMsg{CreateEmptyCharDescriptor<3>()};
614
615 EXPECT_GT(RTNAME(GetCommand)(value.get(), length.get(), errMsg.get()), 0);
616
617 std::string spaces(value->ElementBytes(), ' ');
618 CheckDescriptorEqStr(value.get(), spaces);
619 CheckDescriptorEqInt<std::int64_t>(length.get(), 0);
620 CheckDescriptorEqStr(errMsg.get(), "Mis");
621}
622
623TEST_F(SeveralArguments, GetCommandCanTakeNull) {
624 EXPECT_GT(RTNAME(GetCommand)(nullptr, nullptr, nullptr), 0);
625}
626
627static const char *onlyValidArgsArgv[]{
628 "aProgram", "-f", "has/a/few/slashes", "has\\a\\few\\backslashes"};
629class OnlyValidArguments : public CommandFixture {
630protected:
631 OnlyValidArguments()
632 : CommandFixture(sizeof(onlyValidArgsArgv) / sizeof(*onlyValidArgsArgv),
633 onlyValidArgsArgv) {}
634};
635
636TEST_F(OnlyValidArguments, GetCommand) {
637 CheckCommandValue(args: onlyValidArgsArgv, n: 4);
638}
639
640TEST_F(OnlyValidArguments, CommandValueTooShort) {
641 OwningPtr<Descriptor> tooShort{CreateEmptyCharDescriptor<50>()};
642 ASSERT_NE(tooShort, nullptr);
643 OwningPtr<Descriptor> length{EmptyIntDescriptor()};
644 ASSERT_NE(length, nullptr);
645
646 EXPECT_EQ(RTNAME(GetCommand)(tooShort.get(), length.get(), nullptr), -1);
647
648 CheckDescriptorEqStr(
649 tooShort.get(), "aProgram -f has/a/few/slashes has\\a\\few\\backslashe");
650 CheckDescriptorEqInt<std::int64_t>(length.get(), 51);
651
652 OwningPtr<Descriptor> errMsg{CreateEmptyCharDescriptor()};
653 ASSERT_NE(errMsg, nullptr);
654
655 EXPECT_EQ(-1, RTNAME(GetCommand)(tooShort.get(), nullptr, errMsg.get()));
656
657 std::string expectedErrMsg{
658 GetPaddedStr(text: "Value too short", len: errMsg->ElementBytes())};
659 CheckDescriptorEqStr(errMsg.get(), expectedErrMsg);
660}
661
662TEST_F(OnlyValidArguments, GetCommandCanTakeNull) {
663 EXPECT_EQ(0, RTNAME(GetCommand)(nullptr, nullptr, nullptr));
664
665 OwningPtr<Descriptor> value{CreateEmptyCharDescriptor()};
666 ASSERT_NE(value, nullptr);
667 OwningPtr<Descriptor> length{EmptyIntDescriptor()};
668 ASSERT_NE(length, nullptr);
669
670 EXPECT_EQ(0, RTNAME(GetCommand)(value.get(), nullptr, nullptr));
671 CheckDescriptorEqStr(value.get(),
672 GetPaddedStr("aProgram -f has/a/few/slashes has\\a\\few\\backslashes",
673 value->ElementBytes()));
674
675 EXPECT_EQ(0, RTNAME(GetCommand)(nullptr, length.get(), nullptr));
676 CheckDescriptorEqInt<std::int64_t>(length.get(), 51);
677}
678
679TEST_F(OnlyValidArguments, GetCommandShortLength) {
680 OwningPtr<Descriptor> length{EmptyIntDescriptor<sizeof(short)>()};
681 ASSERT_NE(length, nullptr);
682
683 EXPECT_EQ(0, RTNAME(GetCommand)(nullptr, length.get(), nullptr));
684 CheckDescriptorEqInt<short>(length.get(), 51);
685}
686
687TEST_F(ZeroArguments, GetPID) {
688 // pid should always greater than 0, in both linux and windows
689 EXPECT_GT(RTNAME(GetPID)(), 0);
690}
691
692class EnvironmentVariables : public CommandFixture {
693protected:
694 EnvironmentVariables() : CommandFixture(0, nullptr) {
695 SetEnv(name: "NAME", value: "VALUE");
696#ifdef _WIN32
697 SetEnv("USERNAME", "loginName");
698#else
699 SetEnv(name: "LOGNAME", value: "loginName");
700#endif
701 SetEnv(name: "EMPTY", value: "");
702 }
703
704 // If we have access to setenv, we can run some more fine-grained tests.
705 template <typename ParamType = char>
706 void SetEnv(const ParamType *name, const ParamType *value,
707 decltype(setenv(name, value, 1)) *Enabled = nullptr) {
708 ASSERT_EQ(0, setenv(name, value, /*overwrite=*/1));
709 canSetEnv = true;
710 }
711
712 // Fallback method if setenv is not available.
713 template <typename Unused = void> void SetEnv(const void *, const void *) {}
714
715 bool EnableFineGrainedTests() const { return canSetEnv; }
716
717private:
718 bool canSetEnv{false};
719};
720
721TEST_F(EnvironmentVariables, Nonexistent) {
722 CheckMissingEnvVarValue(name: "DOESNT_EXIST");
723 CheckMissingEnvVarValue(name: " ");
724 CheckMissingEnvVarValue(name: "");
725}
726
727TEST_F(EnvironmentVariables, Basic) {
728 // Test a variable that's expected to exist in the environment.
729 char *path{std::getenv(name: "PATH")};
730 auto expectedLen{static_cast<int64_t>(std::strlen(s: path))};
731 OwningPtr<Descriptor> length{EmptyIntDescriptor()};
732 EXPECT_EQ(0,
733 RTNAME(GetEnvVariable)(*CharDescriptor("PATH"),
734 /*value=*/nullptr, length.get()));
735 CheckDescriptorEqInt(length.get(), expectedLen);
736}
737
738TEST_F(EnvironmentVariables, Trim) {
739 if (EnableFineGrainedTests()) {
740 CheckEnvVarValue(expectedValue: "VALUE", name: "NAME ");
741 }
742}
743
744TEST_F(EnvironmentVariables, NoTrim) {
745 if (EnableFineGrainedTests()) {
746 CheckMissingEnvVarValue(name: "NAME ", /*trim_name=*/trimName: false);
747 }
748}
749
750TEST_F(EnvironmentVariables, Empty) {
751 if (EnableFineGrainedTests()) {
752 CheckEnvVarValue(expectedValue: "", name: "EMPTY");
753 }
754}
755
756TEST_F(EnvironmentVariables, NoValueOrErrmsg) {
757 ASSERT_EQ(std::getenv("DOESNT_EXIST"), nullptr)
758 << "Environment variable DOESNT_EXIST actually exists";
759 EXPECT_EQ(RTNAME(GetEnvVariable)(*CharDescriptor("DOESNT_EXIST")), 1);
760
761 if (EnableFineGrainedTests()) {
762 EXPECT_EQ(RTNAME(GetEnvVariable)(*CharDescriptor("NAME")), 0);
763 }
764}
765
766TEST_F(EnvironmentVariables, ValueTooShort) {
767 if (EnableFineGrainedTests()) {
768 OwningPtr<Descriptor> tooShort{CreateEmptyCharDescriptor<2>()};
769 ASSERT_NE(tooShort, nullptr);
770 EXPECT_EQ(RTNAME(GetEnvVariable)(*CharDescriptor("NAME"), tooShort.get(),
771 /*length=*/nullptr, /*trim_name=*/true, nullptr),
772 -1);
773 CheckDescriptorEqStr(tooShort.get(), "VALUE");
774
775 OwningPtr<Descriptor> errMsg{CreateEmptyCharDescriptor()};
776 ASSERT_NE(errMsg, nullptr);
777
778 EXPECT_EQ(RTNAME(GetEnvVariable)(*CharDescriptor("NAME"), tooShort.get(),
779 /*length=*/nullptr, /*trim_name=*/true, errMsg.get()),
780 -1);
781
782 std::string expectedErrMsg{
783 GetPaddedStr(text: "Value too short", len: errMsg->ElementBytes())};
784 CheckDescriptorEqStr(errMsg.get(), expectedErrMsg);
785 }
786}
787
788TEST_F(EnvironmentVariables, ErrMsgTooShort) {
789 ASSERT_EQ(std::getenv("DOESNT_EXIST"), nullptr)
790 << "Environment variable DOESNT_EXIST actually exists";
791
792 OwningPtr<Descriptor> errMsg{CreateEmptyCharDescriptor<3>()};
793 EXPECT_EQ(RTNAME(GetEnvVariable)(*CharDescriptor("DOESNT_EXIST"), nullptr,
794 /*length=*/nullptr, /*trim_name=*/true, errMsg.get()),
795 1);
796 CheckDescriptorEqStr(errMsg.get(), "Mis");
797}
798
799// username first char must not be null
800TEST_F(EnvironmentVariables, GetlogGetName) {
801 const int charLen{3};
802 char input[charLen]{"\0\0"};
803 FORTRAN_PROCEDURE_NAME(getlog)(input, charLen);
804 EXPECT_NE(input[0], '\0');
805}
806
807#if _REENTRANT || _POSIX_C_SOURCE >= 199506L
808TEST_F(EnvironmentVariables, GetlogPadSpace) {
809 // guarantee 1 char longer than max, last char should be pad space
810 int charLen;
811#ifdef LOGIN_NAME_MAX
812 charLen = LOGIN_NAME_MAX + 2;
813#else
814 charLen = sysconf(_SC_LOGIN_NAME_MAX) + 2;
815 if (charLen == -1)
816 charLen = _POSIX_LOGIN_NAME_MAX + 2;
817#endif
818 std::vector<char> input(charLen);
819 FORTRAN_PROCEDURE_NAME(getlog)(input.data(), charLen);
820 EXPECT_EQ(input[charLen - 1], ' ');
821}
822#endif
823
824#ifdef _WIN32 // Test ability to get name from environment variable
825TEST_F(EnvironmentVariables, GetlogEnvGetName) {
826 if (EnableFineGrainedTests()) {
827 ASSERT_NE(std::getenv("USERNAME"), nullptr)
828 << "Environment variable USERNAME does not exist";
829
830 char input[]{"XXXXXXXXX"};
831 FORTRAN_PROCEDURE_NAME(getlog)(input, sizeof(input));
832
833 CheckCharEqStr(input, "loginName");
834 }
835}
836
837TEST_F(EnvironmentVariables, GetlogEnvBufferShort) {
838 if (EnableFineGrainedTests()) {
839 ASSERT_NE(std::getenv("USERNAME"), nullptr)
840 << "Environment variable USERNAME does not exist";
841
842 char input[]{"XXXXXX"};
843 FORTRAN_PROCEDURE_NAME(getlog)(input, sizeof(input));
844
845 CheckCharEqStr(input, "loginN");
846 }
847}
848
849TEST_F(EnvironmentVariables, GetlogEnvPadSpace) {
850 if (EnableFineGrainedTests()) {
851 ASSERT_NE(std::getenv("USERNAME"), nullptr)
852 << "Environment variable USERNAME does not exist";
853
854 char input[]{"XXXXXXXXXX"};
855 FORTRAN_PROCEDURE_NAME(getlog)(input, sizeof(input));
856
857 CheckCharEqStr(input, "loginName ");
858 }
859}
860#endif
861

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