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