xref: /llvm-project/flang/unittests/Runtime/CommandTest.cpp (revision af35e21cfe3f7cfc7ddd7a2f535e775e9205f61d)
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 
23 using namespace Fortran::runtime;
24 
25 template <std::size_t n = 64>
26 static 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 
35 static OwningPtr<Descriptor> CharDescriptor(const char *value) {
36   std::size_t n{std::strlen(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(descriptor->OffsetElement(), value, n);
43   return descriptor;
44 }
45 
46 template <int kind = sizeof(std::int64_t)>
47 static 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 
56 template <int kind = sizeof(std::int64_t)>
57 static 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(descriptor->OffsetElement<int>(), &value, sizeof(int));
64   return descriptor;
65 }
66 
67 class CommandFixture : public ::testing::Test {
68 protected:
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(len - res.length(), ' ');
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(expectedValue, 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         [&](const Descriptor *value, const Descriptor *length,
134             const Descriptor *errmsg) {
135           return RTNAME(GetCommandArgument)(n, value, length, errmsg);
136         },
137         expectedValue, std::strlen(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         [&](const Descriptor *value, const Descriptor *length,
149             const Descriptor *errmsg) {
150           return RTNAME(GetCommand)(value, length, errmsg);
151         },
152         expectedValue.c_str(), 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         [&](const Descriptor *value, const Descriptor *length,
161             const Descriptor *errmsg) {
162           return RTNAME(GetEnvVariable)(
163               *CharDescriptor(name), value, length, trimName, errmsg);
164         },
165         expectedValue, std::strlen(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         [&](const Descriptor *value, const Descriptor *length,
177             const Descriptor *errmsg) {
178           return RTNAME(GetEnvVariable)(
179               *CharDescriptor(name), value, length, trimName, errmsg);
180         },
181         "", 0, 1, "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(errStr, 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(errStr, err->ElementBytes()));
225       CheckDescriptorEqStr(err.get(), paddedErrStr);
226     }
227   }
228 };
229 
230 class NoArgv : public CommandFixture {
231 protected:
232   NoArgv() : CommandFixture(0, nullptr) {}
233 };
234 
235 #if _WIN32 || _POSIX_C_SOURCE >= 1 || _XOPEN_SOURCE || _BSD_SOURCE || \
236     _SVID_SOURCE || defined(_POSIX_SOURCE)
237 TEST_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 
258 TEST_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 
269 TEST_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
281 TEST_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 
293 TEST_F(NoArgv, GetCommand) { CheckMissingCommandValue(); }
294 
295 static const char *commandOnlyArgv[]{"aProgram"};
296 class ZeroArguments : public CommandFixture {
297 protected:
298   ZeroArguments() : CommandFixture(1, commandOnlyArgv) {}
299 };
300 
301 TEST_F(ZeroArguments, ArgumentCount) { EXPECT_EQ(0, RTNAME(ArgumentCount)()); }
302 
303 TEST_F(ZeroArguments, GetCommandArgument) {
304   CheckMissingArgumentValue(-1);
305   CheckArgumentValue(commandOnlyArgv[0], 0);
306   CheckMissingArgumentValue(1);
307 }
308 
309 TEST_F(ZeroArguments, GetCommand) { CheckCommandValue(commandOnlyArgv, 1); }
310 
311 TEST_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 
327 TEST_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 
342 TEST_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 
361 TEST_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 
392 TEST_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 
412 TEST_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 
429 TEST_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 
444 TEST_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 
454 TEST_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 
463 TEST_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 
472 TEST_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 
486 TEST_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 
504 TEST_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 
515 TEST_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 
526 static const char *oneArgArgv[]{"aProgram", "anArgumentOfLength20"};
527 class OneArgument : public CommandFixture {
528 protected:
529   OneArgument() : CommandFixture(2, oneArgArgv) {}
530 };
531 
532 TEST_F(OneArgument, ArgumentCount) { EXPECT_EQ(1, RTNAME(ArgumentCount)()); }
533 
534 TEST_F(OneArgument, GetCommandArgument) {
535   CheckMissingArgumentValue(-1);
536   CheckArgumentValue(oneArgArgv[0], 0);
537   CheckArgumentValue(oneArgArgv[1], 1);
538   CheckMissingArgumentValue(2);
539 }
540 
541 TEST_F(OneArgument, GetCommand) { CheckCommandValue(oneArgArgv, 2); }
542 
543 static const char *severalArgsArgv[]{
544     "aProgram", "16-char-long-arg", "", "-22-character-long-arg", "o"};
545 class SeveralArguments : public CommandFixture {
546 protected:
547   SeveralArguments()
548       : CommandFixture(sizeof(severalArgsArgv) / sizeof(*severalArgsArgv),
549             severalArgsArgv) {}
550 };
551 
552 TEST_F(SeveralArguments, ArgumentCount) {
553   EXPECT_EQ(4, RTNAME(ArgumentCount)());
554 }
555 
556 TEST_F(SeveralArguments, GetCommandArgument) {
557   CheckArgumentValue(severalArgsArgv[0], 0);
558   CheckArgumentValue(severalArgsArgv[1], 1);
559   CheckArgumentValue(severalArgsArgv[3], 3);
560   CheckArgumentValue(severalArgsArgv[4], 4);
561 }
562 
563 TEST_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 
571 TEST_F(SeveralArguments, MissingArguments) {
572   CheckMissingArgumentValue(-1, "Invalid argument number");
573   CheckMissingArgumentValue(2, "Missing argument");
574   CheckMissingArgumentValue(5, "Invalid argument number");
575   CheckMissingArgumentValue(5);
576 }
577 
578 TEST_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("Value too short", errMsg->ElementBytes())};
596   CheckDescriptorEqStr(errMsg.get(), expectedErrMsg);
597 }
598 
599 TEST_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 
605 TEST_F(SeveralArguments, GetCommand) {
606   CheckMissingCommandValue();
607   CheckMissingCommandValue("Missing argument");
608 }
609 
610 TEST_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 
623 TEST_F(SeveralArguments, GetCommandCanTakeNull) {
624   EXPECT_GT(RTNAME(GetCommand)(nullptr, nullptr, nullptr), 0);
625 }
626 
627 static const char *onlyValidArgsArgv[]{
628     "aProgram", "-f", "has/a/few/slashes", "has\\a\\few\\backslashes"};
629 class OnlyValidArguments : public CommandFixture {
630 protected:
631   OnlyValidArguments()
632       : CommandFixture(sizeof(onlyValidArgsArgv) / sizeof(*onlyValidArgsArgv),
633             onlyValidArgsArgv) {}
634 };
635 
636 TEST_F(OnlyValidArguments, GetCommand) {
637   CheckCommandValue(onlyValidArgsArgv, 4);
638 }
639 
640 TEST_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("Value too short", errMsg->ElementBytes())};
659   CheckDescriptorEqStr(errMsg.get(), expectedErrMsg);
660 }
661 
662 TEST_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 
679 TEST_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 
687 TEST_F(ZeroArguments, GetPID) {
688   // pid should always greater than 0, in both linux and windows
689   EXPECT_GT(RTNAME(GetPID)(), 0);
690 }
691 
692 class EnvironmentVariables : public CommandFixture {
693 protected:
694   EnvironmentVariables() : CommandFixture(0, nullptr) {
695     SetEnv("NAME", "VALUE");
696 #ifdef _WIN32
697     SetEnv("USERNAME", "loginName");
698 #else
699     SetEnv("LOGNAME", "loginName");
700 #endif
701     SetEnv("EMPTY", "");
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 
717 private:
718   bool canSetEnv{false};
719 };
720 
721 TEST_F(EnvironmentVariables, Nonexistent) {
722   CheckMissingEnvVarValue("DOESNT_EXIST");
723   CheckMissingEnvVarValue("      ");
724   CheckMissingEnvVarValue("");
725 }
726 
727 TEST_F(EnvironmentVariables, Basic) {
728   // Test a variable that's expected to exist in the environment.
729   char *path{std::getenv("PATH")};
730   auto expectedLen{static_cast<int64_t>(std::strlen(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 
738 TEST_F(EnvironmentVariables, Trim) {
739   if (EnableFineGrainedTests()) {
740     CheckEnvVarValue("VALUE", "NAME   ");
741   }
742 }
743 
744 TEST_F(EnvironmentVariables, NoTrim) {
745   if (EnableFineGrainedTests()) {
746     CheckMissingEnvVarValue("NAME      ", /*trim_name=*/false);
747   }
748 }
749 
750 TEST_F(EnvironmentVariables, Empty) {
751   if (EnableFineGrainedTests()) {
752     CheckEnvVarValue("", "EMPTY");
753   }
754 }
755 
756 TEST_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 
766 TEST_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("Value too short", errMsg->ElementBytes())};
784     CheckDescriptorEqStr(errMsg.get(), expectedErrMsg);
785   }
786 }
787 
788 TEST_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
800 TEST_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
808 TEST_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
825 TEST_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 
837 TEST_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 
849 TEST_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