xref: /llvm-project/flang/unittests/Runtime/CommandTest.cpp (revision fb09447132cb192a0ef5082d4a4bae30f893e501)
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 || _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{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(exitStat.get(), 0);
323   CheckDescriptorEqInt(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(exitStat.get(), 0);
338   CheckDescriptorEqInt(cmdStat.get(), 0);
339   CheckDescriptorEqStr(cmdMsg.get(), "No change");
340 }
341 
342 TEST_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(exitStat.get(), 127);
355 #endif
356   CheckDescriptorEqInt(cmdStat.get(), 3);
357   CheckDescriptorEqStr(cmdMsg.get(), "Invalid command lineXXXX");
358 }
359 
360 TEST_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 
379 TEST_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(cmdStat.get(), 0);
391   CheckDescriptorEqStr(cmdMsg.get(), "No change");
392 }
393 
394 TEST_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 
407 static const char *oneArgArgv[]{"aProgram", "anArgumentOfLength20"};
408 class OneArgument : public CommandFixture {
409 protected:
410   OneArgument() : CommandFixture(2, oneArgArgv) {}
411 };
412 
413 TEST_F(OneArgument, ArgumentCount) { EXPECT_EQ(1, RTNAME(ArgumentCount)()); }
414 
415 TEST_F(OneArgument, GetCommandArgument) {
416   CheckMissingArgumentValue(-1);
417   CheckArgumentValue(oneArgArgv[0], 0);
418   CheckArgumentValue(oneArgArgv[1], 1);
419   CheckMissingArgumentValue(2);
420 }
421 
422 TEST_F(OneArgument, GetCommand) { CheckCommandValue(oneArgArgv, 2); }
423 
424 static const char *severalArgsArgv[]{
425     "aProgram", "16-char-long-arg", "", "-22-character-long-arg", "o"};
426 class SeveralArguments : public CommandFixture {
427 protected:
428   SeveralArguments()
429       : CommandFixture(sizeof(severalArgsArgv) / sizeof(*severalArgsArgv),
430             severalArgsArgv) {}
431 };
432 
433 TEST_F(SeveralArguments, ArgumentCount) {
434   EXPECT_EQ(4, RTNAME(ArgumentCount)());
435 }
436 
437 TEST_F(SeveralArguments, GetCommandArgument) {
438   CheckArgumentValue(severalArgsArgv[0], 0);
439   CheckArgumentValue(severalArgsArgv[1], 1);
440   CheckArgumentValue(severalArgsArgv[3], 3);
441   CheckArgumentValue(severalArgsArgv[4], 4);
442 }
443 
444 TEST_F(SeveralArguments, NoArgumentValue) {
445   // Make sure we don't crash if the 'value', 'length' and 'error' parameters
446   // aren't passed.
447   EXPECT_GT(RTNAME(GetCommandArgument)(2), 0);
448   EXPECT_EQ(RTNAME(GetCommandArgument)(1), 0);
449   EXPECT_GT(RTNAME(GetCommandArgument)(-1), 0);
450 }
451 
452 TEST_F(SeveralArguments, MissingArguments) {
453   CheckMissingArgumentValue(-1, "Invalid argument number");
454   CheckMissingArgumentValue(2, "Missing argument");
455   CheckMissingArgumentValue(5, "Invalid argument number");
456   CheckMissingArgumentValue(5);
457 }
458 
459 TEST_F(SeveralArguments, ArgValueTooShort) {
460   OwningPtr<Descriptor> tooShort{CreateEmptyCharDescriptor<15>()};
461   ASSERT_NE(tooShort, nullptr);
462   EXPECT_EQ(RTNAME(GetCommandArgument)(1, tooShort.get()), -1);
463   CheckDescriptorEqStr(tooShort.get(), severalArgsArgv[1]);
464 
465   OwningPtr<Descriptor> length{EmptyIntDescriptor()};
466   ASSERT_NE(length, nullptr);
467   OwningPtr<Descriptor> errMsg{CreateEmptyCharDescriptor()};
468   ASSERT_NE(errMsg, nullptr);
469 
470   EXPECT_EQ(
471       RTNAME(GetCommandArgument)(1, tooShort.get(), length.get(), errMsg.get()),
472       -1);
473 
474   CheckDescriptorEqInt<std::int64_t>(length.get(), 16);
475   std::string expectedErrMsg{
476       GetPaddedStr("Value too short", errMsg->ElementBytes())};
477   CheckDescriptorEqStr(errMsg.get(), expectedErrMsg);
478 }
479 
480 TEST_F(SeveralArguments, ArgErrMsgTooShort) {
481   OwningPtr<Descriptor> errMsg{CreateEmptyCharDescriptor<3>()};
482   EXPECT_GT(RTNAME(GetCommandArgument)(-1, nullptr, nullptr, errMsg.get()), 0);
483   CheckDescriptorEqStr(errMsg.get(), "Inv");
484 }
485 
486 TEST_F(SeveralArguments, GetCommand) {
487   CheckMissingCommandValue();
488   CheckMissingCommandValue("Missing argument");
489 }
490 
491 TEST_F(SeveralArguments, CommandErrMsgTooShort) {
492   OwningPtr<Descriptor> value{CreateEmptyCharDescriptor()};
493   OwningPtr<Descriptor> length{EmptyIntDescriptor()};
494   OwningPtr<Descriptor> errMsg{CreateEmptyCharDescriptor<3>()};
495 
496   EXPECT_GT(RTNAME(GetCommand)(value.get(), length.get(), errMsg.get()), 0);
497 
498   std::string spaces(value->ElementBytes(), ' ');
499   CheckDescriptorEqStr(value.get(), spaces);
500   CheckDescriptorEqInt<std::int64_t>(length.get(), 0);
501   CheckDescriptorEqStr(errMsg.get(), "Mis");
502 }
503 
504 TEST_F(SeveralArguments, GetCommandCanTakeNull) {
505   EXPECT_GT(RTNAME(GetCommand)(nullptr, nullptr, nullptr), 0);
506 }
507 
508 static const char *onlyValidArgsArgv[]{
509     "aProgram", "-f", "has/a/few/slashes", "has\\a\\few\\backslashes"};
510 class OnlyValidArguments : public CommandFixture {
511 protected:
512   OnlyValidArguments()
513       : CommandFixture(sizeof(onlyValidArgsArgv) / sizeof(*onlyValidArgsArgv),
514             onlyValidArgsArgv) {}
515 };
516 
517 TEST_F(OnlyValidArguments, GetCommand) {
518   CheckCommandValue(onlyValidArgsArgv, 4);
519 }
520 
521 TEST_F(OnlyValidArguments, CommandValueTooShort) {
522   OwningPtr<Descriptor> tooShort{CreateEmptyCharDescriptor<50>()};
523   ASSERT_NE(tooShort, nullptr);
524   OwningPtr<Descriptor> length{EmptyIntDescriptor()};
525   ASSERT_NE(length, nullptr);
526 
527   EXPECT_EQ(RTNAME(GetCommand)(tooShort.get(), length.get(), nullptr), -1);
528 
529   CheckDescriptorEqStr(
530       tooShort.get(), "aProgram -f has/a/few/slashes has\\a\\few\\backslashe");
531   CheckDescriptorEqInt<std::int64_t>(length.get(), 51);
532 
533   OwningPtr<Descriptor> errMsg{CreateEmptyCharDescriptor()};
534   ASSERT_NE(errMsg, nullptr);
535 
536   EXPECT_EQ(-1, RTNAME(GetCommand)(tooShort.get(), nullptr, errMsg.get()));
537 
538   std::string expectedErrMsg{
539       GetPaddedStr("Value too short", errMsg->ElementBytes())};
540   CheckDescriptorEqStr(errMsg.get(), expectedErrMsg);
541 }
542 
543 TEST_F(OnlyValidArguments, GetCommandCanTakeNull) {
544   EXPECT_EQ(0, RTNAME(GetCommand)(nullptr, nullptr, nullptr));
545 
546   OwningPtr<Descriptor> value{CreateEmptyCharDescriptor()};
547   ASSERT_NE(value, nullptr);
548   OwningPtr<Descriptor> length{EmptyIntDescriptor()};
549   ASSERT_NE(length, nullptr);
550 
551   EXPECT_EQ(0, RTNAME(GetCommand)(value.get(), nullptr, nullptr));
552   CheckDescriptorEqStr(value.get(),
553       GetPaddedStr("aProgram -f has/a/few/slashes has\\a\\few\\backslashes",
554           value->ElementBytes()));
555 
556   EXPECT_EQ(0, RTNAME(GetCommand)(nullptr, length.get(), nullptr));
557   CheckDescriptorEqInt<std::int64_t>(length.get(), 51);
558 }
559 
560 TEST_F(OnlyValidArguments, GetCommandShortLength) {
561   OwningPtr<Descriptor> length{EmptyIntDescriptor<sizeof(short)>()};
562   ASSERT_NE(length, nullptr);
563 
564   EXPECT_EQ(0, RTNAME(GetCommand)(nullptr, length.get(), nullptr));
565   CheckDescriptorEqInt<short>(length.get(), 51);
566 }
567 
568 TEST_F(ZeroArguments, GetPID) {
569   // pid should always greater than 0, in both linux and windows
570   EXPECT_GT(RTNAME(GetPID)(), 0);
571 }
572 
573 class EnvironmentVariables : public CommandFixture {
574 protected:
575   EnvironmentVariables() : CommandFixture(0, nullptr) {
576     SetEnv("NAME", "VALUE");
577 #ifdef _WIN32
578     SetEnv("USERNAME", "loginName");
579 #else
580     SetEnv("LOGNAME", "loginName");
581 #endif
582     SetEnv("EMPTY", "");
583   }
584 
585   // If we have access to setenv, we can run some more fine-grained tests.
586   template <typename ParamType = char>
587   void SetEnv(const ParamType *name, const ParamType *value,
588       decltype(setenv(name, value, 1)) *Enabled = nullptr) {
589     ASSERT_EQ(0, setenv(name, value, /*overwrite=*/1));
590     canSetEnv = true;
591   }
592 
593   // Fallback method if setenv is not available.
594   template <typename Unused = void> void SetEnv(const void *, const void *) {}
595 
596   bool EnableFineGrainedTests() const { return canSetEnv; }
597 
598 private:
599   bool canSetEnv{false};
600 };
601 
602 TEST_F(EnvironmentVariables, Nonexistent) {
603   CheckMissingEnvVarValue("DOESNT_EXIST");
604   CheckMissingEnvVarValue("      ");
605   CheckMissingEnvVarValue("");
606 }
607 
608 TEST_F(EnvironmentVariables, Basic) {
609   // Test a variable that's expected to exist in the environment.
610   char *path{std::getenv("PATH")};
611   auto expectedLen{static_cast<int64_t>(std::strlen(path))};
612   OwningPtr<Descriptor> length{EmptyIntDescriptor()};
613   EXPECT_EQ(0,
614       RTNAME(GetEnvVariable)(*CharDescriptor("PATH"),
615           /*value=*/nullptr, length.get()));
616   CheckDescriptorEqInt(length.get(), expectedLen);
617 }
618 
619 TEST_F(EnvironmentVariables, Trim) {
620   if (EnableFineGrainedTests()) {
621     CheckEnvVarValue("VALUE", "NAME   ");
622   }
623 }
624 
625 TEST_F(EnvironmentVariables, NoTrim) {
626   if (EnableFineGrainedTests()) {
627     CheckMissingEnvVarValue("NAME      ", /*trim_name=*/false);
628   }
629 }
630 
631 TEST_F(EnvironmentVariables, Empty) {
632   if (EnableFineGrainedTests()) {
633     CheckEnvVarValue("", "EMPTY");
634   }
635 }
636 
637 TEST_F(EnvironmentVariables, NoValueOrErrmsg) {
638   ASSERT_EQ(std::getenv("DOESNT_EXIST"), nullptr)
639       << "Environment variable DOESNT_EXIST actually exists";
640   EXPECT_EQ(RTNAME(GetEnvVariable)(*CharDescriptor("DOESNT_EXIST")), 1);
641 
642   if (EnableFineGrainedTests()) {
643     EXPECT_EQ(RTNAME(GetEnvVariable)(*CharDescriptor("NAME")), 0);
644   }
645 }
646 
647 TEST_F(EnvironmentVariables, ValueTooShort) {
648   if (EnableFineGrainedTests()) {
649     OwningPtr<Descriptor> tooShort{CreateEmptyCharDescriptor<2>()};
650     ASSERT_NE(tooShort, nullptr);
651     EXPECT_EQ(RTNAME(GetEnvVariable)(*CharDescriptor("NAME"), tooShort.get(),
652                   /*length=*/nullptr, /*trim_name=*/true, nullptr),
653         -1);
654     CheckDescriptorEqStr(tooShort.get(), "VALUE");
655 
656     OwningPtr<Descriptor> errMsg{CreateEmptyCharDescriptor()};
657     ASSERT_NE(errMsg, nullptr);
658 
659     EXPECT_EQ(RTNAME(GetEnvVariable)(*CharDescriptor("NAME"), tooShort.get(),
660                   /*length=*/nullptr, /*trim_name=*/true, errMsg.get()),
661         -1);
662 
663     std::string expectedErrMsg{
664         GetPaddedStr("Value too short", errMsg->ElementBytes())};
665     CheckDescriptorEqStr(errMsg.get(), expectedErrMsg);
666   }
667 }
668 
669 TEST_F(EnvironmentVariables, ErrMsgTooShort) {
670   ASSERT_EQ(std::getenv("DOESNT_EXIST"), nullptr)
671       << "Environment variable DOESNT_EXIST actually exists";
672 
673   OwningPtr<Descriptor> errMsg{CreateEmptyCharDescriptor<3>()};
674   EXPECT_EQ(RTNAME(GetEnvVariable)(*CharDescriptor("DOESNT_EXIST"), nullptr,
675                 /*length=*/nullptr, /*trim_name=*/true, errMsg.get()),
676       1);
677   CheckDescriptorEqStr(errMsg.get(), "Mis");
678 }
679 
680 // username first char must not be null
681 TEST_F(EnvironmentVariables, GetlogGetName) {
682   const int charLen{3};
683   char input[charLen]{"\0\0"};
684 
685   FORTRAN_PROCEDURE_NAME(getlog)
686   (reinterpret_cast<std::byte *>(input), charLen);
687 
688   EXPECT_NE(input[0], '\0');
689 }
690 
691 #if _REENTRANT || _POSIX_C_SOURCE >= 199506L
692 TEST_F(EnvironmentVariables, GetlogPadSpace) {
693   // guarantee 1 char longer than max, last char should be pad space
694   int charLen;
695 #ifdef LOGIN_NAME_MAX
696   charLen = LOGIN_NAME_MAX + 2;
697 #else
698   charLen = sysconf(_SC_LOGIN_NAME_MAX) + 2;
699   if (charLen == -1)
700     charLen = _POSIX_LOGIN_NAME_MAX + 2;
701 #endif
702   std::vector<char> input(charLen);
703 
704   FORTRAN_PROCEDURE_NAME(getlog)
705   (reinterpret_cast<std::byte *>(input.data()), charLen);
706 
707   EXPECT_EQ(input[charLen - 1], ' ');
708 }
709 #endif
710 
711 #ifdef _WIN32 // Test ability to get name from environment variable
712 TEST_F(EnvironmentVariables, GetlogEnvGetName) {
713   if (EnableFineGrainedTests()) {
714     ASSERT_NE(std::getenv("USERNAME"), nullptr)
715         << "Environment variable USERNAME does not exist";
716 
717     char input[]{"XXXXXXXXX"};
718     FORTRAN_PROCEDURE_NAME(getlog)
719     (reinterpret_cast<std::byte *>(input), sizeof(input));
720 
721     CheckCharEqStr(input, "loginName");
722   }
723 }
724 
725 TEST_F(EnvironmentVariables, GetlogEnvBufferShort) {
726   if (EnableFineGrainedTests()) {
727     ASSERT_NE(std::getenv("USERNAME"), nullptr)
728         << "Environment variable USERNAME does not exist";
729 
730     char input[]{"XXXXXX"};
731     FORTRAN_PROCEDURE_NAME(getlog)
732     (reinterpret_cast<std::byte *>(input), sizeof(input));
733 
734     CheckCharEqStr(input, "loginN");
735   }
736 }
737 
738 TEST_F(EnvironmentVariables, GetlogEnvPadSpace) {
739   if (EnableFineGrainedTests()) {
740     ASSERT_NE(std::getenv("USERNAME"), nullptr)
741         << "Environment variable USERNAME does not exist";
742 
743     char input[]{"XXXXXXXXXX"};
744     FORTRAN_PROCEDURE_NAME(getlog)
745     (reinterpret_cast<std::byte *>(input), sizeof(input));
746 
747     CheckCharEqStr(input, "loginName ");
748   }
749 }
750 #endif
751