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