xref: /llvm-project/flang/unittests/Runtime/CommandTest.cpp (revision e2b896aa640fec25f68d283948c1b44711087f0f)
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 // TODO: Test other intrinsics with this fixture.
236 
237 TEST_F(NoArgv, GetCommand) { CheckMissingCommandValue(); }
238 
239 static const char *commandOnlyArgv[]{"aProgram"};
240 class ZeroArguments : public CommandFixture {
241 protected:
242   ZeroArguments() : CommandFixture(1, commandOnlyArgv) {}
243 };
244 
245 TEST_F(ZeroArguments, ArgumentCount) { EXPECT_EQ(0, RTNAME(ArgumentCount)()); }
246 
247 TEST_F(ZeroArguments, GetCommandArgument) {
248   CheckMissingArgumentValue(-1);
249   CheckArgumentValue(commandOnlyArgv[0], 0);
250   CheckMissingArgumentValue(1);
251 }
252 
253 TEST_F(ZeroArguments, GetCommand) { CheckCommandValue(commandOnlyArgv, 1); }
254 
255 TEST_F(ZeroArguments, ECLValidCommandAndPadSync) {
256   OwningPtr<Descriptor> command{CharDescriptor("echo hi")};
257   bool wait{true};
258   OwningPtr<Descriptor> exitStat{EmptyIntDescriptor()};
259   OwningPtr<Descriptor> cmdStat{EmptyIntDescriptor()};
260   OwningPtr<Descriptor> cmdMsg{CharDescriptor("No change")};
261 
262   RTNAME(ExecuteCommandLine)
263   (*command.get(), wait, exitStat.get(), cmdStat.get(), cmdMsg.get());
264 
265   std::string spaces(cmdMsg->ElementBytes(), ' ');
266   CheckDescriptorEqInt(exitStat.get(), 0);
267   CheckDescriptorEqInt(cmdStat.get(), 0);
268   CheckDescriptorEqStr(cmdMsg.get(), "No change");
269 }
270 
271 TEST_F(ZeroArguments, ECLValidCommandStatusSetSync) {
272   OwningPtr<Descriptor> command{CharDescriptor("echo hi")};
273   bool wait{true};
274   OwningPtr<Descriptor> exitStat{IntDescriptor(404)};
275   OwningPtr<Descriptor> cmdStat{IntDescriptor(202)};
276   OwningPtr<Descriptor> cmdMsg{CharDescriptor("No change")};
277 
278   RTNAME(ExecuteCommandLine)
279   (*command.get(), wait, exitStat.get(), cmdStat.get(), cmdMsg.get());
280 
281   CheckDescriptorEqInt(exitStat.get(), 0);
282   CheckDescriptorEqInt(cmdStat.get(), 0);
283   CheckDescriptorEqStr(cmdMsg.get(), "No change");
284 }
285 
286 TEST_F(ZeroArguments, ECLInvalidCommandErrorSync) {
287   OwningPtr<Descriptor> command{CharDescriptor("InvalidCommand")};
288   bool wait{true};
289   OwningPtr<Descriptor> exitStat{IntDescriptor(404)};
290   OwningPtr<Descriptor> cmdStat{IntDescriptor(202)};
291   OwningPtr<Descriptor> cmdMsg{CharDescriptor("Message ChangedXXXXXXXXX")};
292 
293   RTNAME(ExecuteCommandLine)
294   (*command.get(), wait, exitStat.get(), cmdStat.get(), cmdMsg.get());
295 #ifdef _WIN32
296   CheckDescriptorEqInt(exitStat.get(), 1);
297 #else
298   CheckDescriptorEqInt(exitStat.get(), 127);
299 #endif
300   CheckDescriptorEqInt(cmdStat.get(), 3);
301   CheckDescriptorEqStr(cmdMsg.get(), "Invalid command lineXXXX");
302 }
303 
304 TEST_F(ZeroArguments, ECLInvalidCommandTerminatedSync) {
305   OwningPtr<Descriptor> command{CharDescriptor("InvalidCommand")};
306   bool wait{true};
307   OwningPtr<Descriptor> exitStat{IntDescriptor(404)};
308   OwningPtr<Descriptor> cmdMsg{CharDescriptor("No Change")};
309 
310 #ifdef _WIN32
311   EXPECT_DEATH(RTNAME(ExecuteCommandLine)(
312                    *command.get(), wait, exitStat.get(), nullptr, cmdMsg.get()),
313       "Invalid command quit with exit status code: 1");
314 #else
315   EXPECT_DEATH(RTNAME(ExecuteCommandLine)(
316                    *command.get(), wait, exitStat.get(), nullptr, cmdMsg.get()),
317       "Invalid command quit with exit status code: 127");
318 #endif
319   CheckDescriptorEqInt(exitStat.get(), 404);
320   CheckDescriptorEqStr(cmdMsg.get(), "No Change");
321 }
322 
323 TEST_F(ZeroArguments, ECLValidCommandAndExitStatNoChangeAndCMDStatusSetAsync) {
324   OwningPtr<Descriptor> command{CharDescriptor("echo hi")};
325   bool wait{false};
326   OwningPtr<Descriptor> exitStat{IntDescriptor(404)};
327   OwningPtr<Descriptor> cmdStat{IntDescriptor(202)};
328   OwningPtr<Descriptor> cmdMsg{CharDescriptor("No change")};
329 
330   RTNAME(ExecuteCommandLine)
331   (*command.get(), wait, exitStat.get(), cmdStat.get(), cmdMsg.get());
332 
333   CheckDescriptorEqInt(exitStat.get(), 404);
334   CheckDescriptorEqInt(cmdStat.get(), 0);
335   CheckDescriptorEqStr(cmdMsg.get(), "No change");
336 }
337 
338 TEST_F(ZeroArguments, ECLInvalidCommandParentNotTerminatedAsync) {
339   OwningPtr<Descriptor> command{CharDescriptor("InvalidCommand")};
340   bool wait{false};
341   OwningPtr<Descriptor> exitStat{IntDescriptor(404)};
342   OwningPtr<Descriptor> cmdMsg{CharDescriptor("No change")};
343 
344   EXPECT_NO_FATAL_FAILURE(RTNAME(ExecuteCommandLine)(
345       *command.get(), wait, exitStat.get(), nullptr, cmdMsg.get()));
346 
347   CheckDescriptorEqInt(exitStat.get(), 404);
348   CheckDescriptorEqStr(cmdMsg.get(), "No change");
349 }
350 
351 static const char *oneArgArgv[]{"aProgram", "anArgumentOfLength20"};
352 class OneArgument : public CommandFixture {
353 protected:
354   OneArgument() : CommandFixture(2, oneArgArgv) {}
355 };
356 
357 TEST_F(OneArgument, ArgumentCount) { EXPECT_EQ(1, RTNAME(ArgumentCount)()); }
358 
359 TEST_F(OneArgument, GetCommandArgument) {
360   CheckMissingArgumentValue(-1);
361   CheckArgumentValue(oneArgArgv[0], 0);
362   CheckArgumentValue(oneArgArgv[1], 1);
363   CheckMissingArgumentValue(2);
364 }
365 
366 TEST_F(OneArgument, GetCommand) { CheckCommandValue(oneArgArgv, 2); }
367 
368 static const char *severalArgsArgv[]{
369     "aProgram", "16-char-long-arg", "", "-22-character-long-arg", "o"};
370 class SeveralArguments : public CommandFixture {
371 protected:
372   SeveralArguments()
373       : CommandFixture(sizeof(severalArgsArgv) / sizeof(*severalArgsArgv),
374             severalArgsArgv) {}
375 };
376 
377 TEST_F(SeveralArguments, ArgumentCount) {
378   EXPECT_EQ(4, RTNAME(ArgumentCount)());
379 }
380 
381 TEST_F(SeveralArguments, GetCommandArgument) {
382   CheckArgumentValue(severalArgsArgv[0], 0);
383   CheckArgumentValue(severalArgsArgv[1], 1);
384   CheckArgumentValue(severalArgsArgv[3], 3);
385   CheckArgumentValue(severalArgsArgv[4], 4);
386 }
387 
388 TEST_F(SeveralArguments, NoArgumentValue) {
389   // Make sure we don't crash if the 'value', 'length' and 'error' parameters
390   // aren't passed.
391   EXPECT_GT(RTNAME(GetCommandArgument)(2), 0);
392   EXPECT_EQ(RTNAME(GetCommandArgument)(1), 0);
393   EXPECT_GT(RTNAME(GetCommandArgument)(-1), 0);
394 }
395 
396 TEST_F(SeveralArguments, MissingArguments) {
397   CheckMissingArgumentValue(-1, "Invalid argument number");
398   CheckMissingArgumentValue(2, "Missing argument");
399   CheckMissingArgumentValue(5, "Invalid argument number");
400   CheckMissingArgumentValue(5);
401 }
402 
403 TEST_F(SeveralArguments, ArgValueTooShort) {
404   OwningPtr<Descriptor> tooShort{CreateEmptyCharDescriptor<15>()};
405   ASSERT_NE(tooShort, nullptr);
406   EXPECT_EQ(RTNAME(GetCommandArgument)(1, tooShort.get()), -1);
407   CheckDescriptorEqStr(tooShort.get(), severalArgsArgv[1]);
408 
409   OwningPtr<Descriptor> length{EmptyIntDescriptor()};
410   ASSERT_NE(length, nullptr);
411   OwningPtr<Descriptor> errMsg{CreateEmptyCharDescriptor()};
412   ASSERT_NE(errMsg, nullptr);
413 
414   EXPECT_EQ(
415       RTNAME(GetCommandArgument)(1, tooShort.get(), length.get(), errMsg.get()),
416       -1);
417 
418   CheckDescriptorEqInt<std::int64_t>(length.get(), 16);
419   std::string expectedErrMsg{
420       GetPaddedStr("Value too short", errMsg->ElementBytes())};
421   CheckDescriptorEqStr(errMsg.get(), expectedErrMsg);
422 }
423 
424 TEST_F(SeveralArguments, ArgErrMsgTooShort) {
425   OwningPtr<Descriptor> errMsg{CreateEmptyCharDescriptor<3>()};
426   EXPECT_GT(RTNAME(GetCommandArgument)(-1, nullptr, nullptr, errMsg.get()), 0);
427   CheckDescriptorEqStr(errMsg.get(), "Inv");
428 }
429 
430 TEST_F(SeveralArguments, GetCommand) {
431   CheckMissingCommandValue();
432   CheckMissingCommandValue("Missing argument");
433 }
434 
435 TEST_F(SeveralArguments, CommandErrMsgTooShort) {
436   OwningPtr<Descriptor> value{CreateEmptyCharDescriptor()};
437   OwningPtr<Descriptor> length{EmptyIntDescriptor()};
438   OwningPtr<Descriptor> errMsg{CreateEmptyCharDescriptor<3>()};
439 
440   EXPECT_GT(RTNAME(GetCommand)(value.get(), length.get(), errMsg.get()), 0);
441 
442   std::string spaces(value->ElementBytes(), ' ');
443   CheckDescriptorEqStr(value.get(), spaces);
444   CheckDescriptorEqInt<std::int64_t>(length.get(), 0);
445   CheckDescriptorEqStr(errMsg.get(), "Mis");
446 }
447 
448 TEST_F(SeveralArguments, GetCommandCanTakeNull) {
449   EXPECT_GT(RTNAME(GetCommand)(nullptr, nullptr, nullptr), 0);
450 }
451 
452 static const char *onlyValidArgsArgv[]{
453     "aProgram", "-f", "has/a/few/slashes", "has\\a\\few\\backslashes"};
454 class OnlyValidArguments : public CommandFixture {
455 protected:
456   OnlyValidArguments()
457       : CommandFixture(sizeof(onlyValidArgsArgv) / sizeof(*onlyValidArgsArgv),
458             onlyValidArgsArgv) {}
459 };
460 
461 TEST_F(OnlyValidArguments, GetCommand) {
462   CheckCommandValue(onlyValidArgsArgv, 4);
463 }
464 
465 TEST_F(OnlyValidArguments, CommandValueTooShort) {
466   OwningPtr<Descriptor> tooShort{CreateEmptyCharDescriptor<50>()};
467   ASSERT_NE(tooShort, nullptr);
468   OwningPtr<Descriptor> length{EmptyIntDescriptor()};
469   ASSERT_NE(length, nullptr);
470 
471   EXPECT_EQ(RTNAME(GetCommand)(tooShort.get(), length.get(), nullptr), -1);
472 
473   CheckDescriptorEqStr(
474       tooShort.get(), "aProgram -f has/a/few/slashes has\\a\\few\\backslashe");
475   CheckDescriptorEqInt<std::int64_t>(length.get(), 51);
476 
477   OwningPtr<Descriptor> errMsg{CreateEmptyCharDescriptor()};
478   ASSERT_NE(errMsg, nullptr);
479 
480   EXPECT_EQ(-1, RTNAME(GetCommand)(tooShort.get(), nullptr, errMsg.get()));
481 
482   std::string expectedErrMsg{
483       GetPaddedStr("Value too short", errMsg->ElementBytes())};
484   CheckDescriptorEqStr(errMsg.get(), expectedErrMsg);
485 }
486 
487 TEST_F(OnlyValidArguments, GetCommandCanTakeNull) {
488   EXPECT_EQ(0, RTNAME(GetCommand)(nullptr, nullptr, nullptr));
489 
490   OwningPtr<Descriptor> value{CreateEmptyCharDescriptor()};
491   ASSERT_NE(value, nullptr);
492   OwningPtr<Descriptor> length{EmptyIntDescriptor()};
493   ASSERT_NE(length, nullptr);
494 
495   EXPECT_EQ(0, RTNAME(GetCommand)(value.get(), nullptr, nullptr));
496   CheckDescriptorEqStr(value.get(),
497       GetPaddedStr("aProgram -f has/a/few/slashes has\\a\\few\\backslashes",
498           value->ElementBytes()));
499 
500   EXPECT_EQ(0, RTNAME(GetCommand)(nullptr, length.get(), nullptr));
501   CheckDescriptorEqInt<std::int64_t>(length.get(), 51);
502 }
503 
504 TEST_F(OnlyValidArguments, GetCommandShortLength) {
505   OwningPtr<Descriptor> length{EmptyIntDescriptor<sizeof(short)>()};
506   ASSERT_NE(length, nullptr);
507 
508   EXPECT_EQ(0, RTNAME(GetCommand)(nullptr, length.get(), nullptr));
509   CheckDescriptorEqInt<short>(length.get(), 51);
510 }
511 
512 TEST_F(ZeroArguments, GetPID) {
513   // pid should always greater than 0, in both linux and windows
514   EXPECT_GT(RTNAME(GetPID)(), 0);
515 }
516 
517 class EnvironmentVariables : public CommandFixture {
518 protected:
519   EnvironmentVariables() : CommandFixture(0, nullptr) {
520     SetEnv("NAME", "VALUE");
521 #ifdef _WIN32
522     SetEnv("USERNAME", "loginName");
523 #else
524     SetEnv("LOGNAME", "loginName");
525 #endif
526     SetEnv("EMPTY", "");
527   }
528 
529   // If we have access to setenv, we can run some more fine-grained tests.
530   template <typename ParamType = char>
531   void SetEnv(const ParamType *name, const ParamType *value,
532       decltype(setenv(name, value, 1)) *Enabled = nullptr) {
533     ASSERT_EQ(0, setenv(name, value, /*overwrite=*/1));
534     canSetEnv = true;
535   }
536 
537   // Fallback method if setenv is not available.
538   template <typename Unused = void> void SetEnv(const void *, const void *) {}
539 
540   bool EnableFineGrainedTests() const { return canSetEnv; }
541 
542 private:
543   bool canSetEnv{false};
544 };
545 
546 TEST_F(EnvironmentVariables, Nonexistent) {
547   CheckMissingEnvVarValue("DOESNT_EXIST");
548   CheckMissingEnvVarValue("      ");
549   CheckMissingEnvVarValue("");
550 }
551 
552 TEST_F(EnvironmentVariables, Basic) {
553   // Test a variable that's expected to exist in the environment.
554   char *path{std::getenv("PATH")};
555   auto expectedLen{static_cast<int64_t>(std::strlen(path))};
556   OwningPtr<Descriptor> length{EmptyIntDescriptor()};
557   EXPECT_EQ(0,
558       RTNAME(GetEnvVariable)(*CharDescriptor("PATH"),
559           /*value=*/nullptr, length.get()));
560   CheckDescriptorEqInt(length.get(), expectedLen);
561 }
562 
563 TEST_F(EnvironmentVariables, Trim) {
564   if (EnableFineGrainedTests()) {
565     CheckEnvVarValue("VALUE", "NAME   ");
566   }
567 }
568 
569 TEST_F(EnvironmentVariables, NoTrim) {
570   if (EnableFineGrainedTests()) {
571     CheckMissingEnvVarValue("NAME      ", /*trim_name=*/false);
572   }
573 }
574 
575 TEST_F(EnvironmentVariables, Empty) {
576   if (EnableFineGrainedTests()) {
577     CheckEnvVarValue("", "EMPTY");
578   }
579 }
580 
581 TEST_F(EnvironmentVariables, NoValueOrErrmsg) {
582   ASSERT_EQ(std::getenv("DOESNT_EXIST"), nullptr)
583       << "Environment variable DOESNT_EXIST actually exists";
584   EXPECT_EQ(RTNAME(GetEnvVariable)(*CharDescriptor("DOESNT_EXIST")), 1);
585 
586   if (EnableFineGrainedTests()) {
587     EXPECT_EQ(RTNAME(GetEnvVariable)(*CharDescriptor("NAME")), 0);
588   }
589 }
590 
591 TEST_F(EnvironmentVariables, ValueTooShort) {
592   if (EnableFineGrainedTests()) {
593     OwningPtr<Descriptor> tooShort{CreateEmptyCharDescriptor<2>()};
594     ASSERT_NE(tooShort, nullptr);
595     EXPECT_EQ(RTNAME(GetEnvVariable)(*CharDescriptor("NAME"), tooShort.get(),
596                   /*length=*/nullptr, /*trim_name=*/true, nullptr),
597         -1);
598     CheckDescriptorEqStr(tooShort.get(), "VALUE");
599 
600     OwningPtr<Descriptor> errMsg{CreateEmptyCharDescriptor()};
601     ASSERT_NE(errMsg, nullptr);
602 
603     EXPECT_EQ(RTNAME(GetEnvVariable)(*CharDescriptor("NAME"), tooShort.get(),
604                   /*length=*/nullptr, /*trim_name=*/true, errMsg.get()),
605         -1);
606 
607     std::string expectedErrMsg{
608         GetPaddedStr("Value too short", errMsg->ElementBytes())};
609     CheckDescriptorEqStr(errMsg.get(), expectedErrMsg);
610   }
611 }
612 
613 TEST_F(EnvironmentVariables, ErrMsgTooShort) {
614   ASSERT_EQ(std::getenv("DOESNT_EXIST"), nullptr)
615       << "Environment variable DOESNT_EXIST actually exists";
616 
617   OwningPtr<Descriptor> errMsg{CreateEmptyCharDescriptor<3>()};
618   EXPECT_EQ(RTNAME(GetEnvVariable)(*CharDescriptor("DOESNT_EXIST"), nullptr,
619                 /*length=*/nullptr, /*trim_name=*/true, errMsg.get()),
620       1);
621   CheckDescriptorEqStr(errMsg.get(), "Mis");
622 }
623 
624 // username first char must not be null
625 TEST_F(EnvironmentVariables, GetlogGetName) {
626   const int charLen{3};
627   char input[charLen]{"\0\0"};
628 
629   FORTRAN_PROCEDURE_NAME(getlog)
630   (reinterpret_cast<std::byte *>(input), charLen);
631 
632   EXPECT_NE(input[0], '\0');
633 }
634 
635 #if _REENTRANT || _POSIX_C_SOURCE >= 199506L
636 TEST_F(EnvironmentVariables, GetlogPadSpace) {
637   // guarantee 1 char longer than max, last char should be pad space
638   const int charLen{LOGIN_NAME_MAX + 2};
639   char input[charLen];
640 
641   FORTRAN_PROCEDURE_NAME(getlog)
642   (reinterpret_cast<std::byte *>(input), charLen);
643 
644   EXPECT_EQ(input[charLen - 1], ' ');
645 }
646 #endif
647 
648 #ifdef _WIN32 // Test ability to get name from environment variable
649 TEST_F(EnvironmentVariables, GetlogEnvGetName) {
650   if (EnableFineGrainedTests()) {
651     ASSERT_NE(std::getenv("USERNAME"), nullptr)
652         << "Environment variable USERNAME does not exist";
653 
654     char input[]{"XXXXXXXXX"};
655     FORTRAN_PROCEDURE_NAME(getlog)
656     (reinterpret_cast<std::byte *>(input), sizeof(input));
657 
658     CheckCharEqStr(input, "loginName");
659   }
660 }
661 
662 TEST_F(EnvironmentVariables, GetlogEnvBufferShort) {
663   if (EnableFineGrainedTests()) {
664     ASSERT_NE(std::getenv("USERNAME"), nullptr)
665         << "Environment variable USERNAME does not exist";
666 
667     char input[]{"XXXXXX"};
668     FORTRAN_PROCEDURE_NAME(getlog)
669     (reinterpret_cast<std::byte *>(input), sizeof(input));
670 
671     CheckCharEqStr(input, "loginN");
672   }
673 }
674 
675 TEST_F(EnvironmentVariables, GetlogEnvPadSpace) {
676   if (EnableFineGrainedTests()) {
677     ASSERT_NE(std::getenv("USERNAME"), nullptr)
678         << "Environment variable USERNAME does not exist";
679 
680     char input[]{"XXXXXXXXXX"};
681     FORTRAN_PROCEDURE_NAME(getlog)
682     (reinterpret_cast<std::byte *>(input), sizeof(input));
683 
684     CheckCharEqStr(input, "loginName ");
685   }
686 }
687 #endif
688