173b193aeSPeter Klausler //===-- flang/unittests/Runtime/CommandTest.cpp ---------------------------===//
20c375296SDiana Picus //
30c375296SDiana Picus // Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
40c375296SDiana Picus // See https://llvm.org/LICENSE.txt for license information.
50c375296SDiana Picus // SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
60c375296SDiana Picus //
70c375296SDiana Picus //===----------------------------------------------------------------------===//
80c375296SDiana Picus 
9830c0b90SPeter Klausler #include "flang/Runtime/command.h"
1037089baeSDiana Picus #include "gmock/gmock.h"
110c375296SDiana Picus #include "gtest/gtest.h"
12830c0b90SPeter Klausler #include "flang/Runtime/descriptor.h"
13830c0b90SPeter Klausler #include "flang/Runtime/main.h"
14824bf908SDiana Picus #include <cstdlib>
150c375296SDiana Picus 
160c375296SDiana Picus using namespace Fortran::runtime;
170c375296SDiana Picus 
1837089baeSDiana Picus template <std::size_t n = 64>
CreateEmptyCharDescriptor()1937089baeSDiana Picus static OwningPtr<Descriptor> CreateEmptyCharDescriptor() {
2037089baeSDiana Picus   OwningPtr<Descriptor> descriptor{Descriptor::Create(
2137089baeSDiana Picus       sizeof(char), n, nullptr, 0, nullptr, CFI_attribute_allocatable)};
2237089baeSDiana Picus   if (descriptor->Allocate() != 0) {
2337089baeSDiana Picus     return nullptr;
2437089baeSDiana Picus   }
2537089baeSDiana Picus   return descriptor;
2637089baeSDiana Picus }
2737089baeSDiana Picus 
CharDescriptor(const char * value)28fc2ba5e5SDiana Picus static OwningPtr<Descriptor> CharDescriptor(const char *value) {
29fc2ba5e5SDiana Picus   std::size_t n{std::strlen(value)};
30fc2ba5e5SDiana Picus   OwningPtr<Descriptor> descriptor{Descriptor::Create(
31fc2ba5e5SDiana Picus       sizeof(char), n, nullptr, 0, nullptr, CFI_attribute_allocatable)};
32fc2ba5e5SDiana Picus   if (descriptor->Allocate() != 0) {
33fc2ba5e5SDiana Picus     return nullptr;
34fc2ba5e5SDiana Picus   }
35fc2ba5e5SDiana Picus   std::memcpy(descriptor->OffsetElement(), value, n);
36fc2ba5e5SDiana Picus   return descriptor;
37fc2ba5e5SDiana Picus }
38fc2ba5e5SDiana Picus 
39*873f081eSDiana Picus template <int kind = sizeof(std::int64_t)>
EmptyIntDescriptor()40*873f081eSDiana Picus static OwningPtr<Descriptor> EmptyIntDescriptor() {
41*873f081eSDiana Picus   OwningPtr<Descriptor> descriptor{Descriptor::Create(TypeCategory::Integer,
42*873f081eSDiana Picus       kind, nullptr, 0, nullptr, CFI_attribute_allocatable)};
43*873f081eSDiana Picus   if (descriptor->Allocate() != 0) {
44*873f081eSDiana Picus     return nullptr;
45*873f081eSDiana Picus   }
46*873f081eSDiana Picus   return descriptor;
47*873f081eSDiana Picus }
48*873f081eSDiana Picus 
49af63d179SDiana Picus class CommandFixture : public ::testing::Test {
50af63d179SDiana Picus protected:
CommandFixture(int argc,const char * argv[])51af63d179SDiana Picus   CommandFixture(int argc, const char *argv[]) {
52af63d179SDiana Picus     RTNAME(ProgramStart)(argc, argv, {});
53af63d179SDiana Picus   }
5437089baeSDiana Picus 
GetPaddedStr(const char * text,std::size_t len) const5537089baeSDiana Picus   std::string GetPaddedStr(const char *text, std::size_t len) const {
5637089baeSDiana Picus     std::string res{text};
5737089baeSDiana Picus     assert(res.length() <= len && "No room to pad");
5837089baeSDiana Picus     res.append(len - res.length(), ' ');
5937089baeSDiana Picus     return res;
6037089baeSDiana Picus   }
6137089baeSDiana Picus 
CheckDescriptorEqStr(const Descriptor * value,const std::string & expected) const6237089baeSDiana Picus   void CheckDescriptorEqStr(
6337089baeSDiana Picus       const Descriptor *value, const std::string &expected) const {
64*873f081eSDiana Picus     ASSERT_NE(value, nullptr);
6537089baeSDiana Picus     EXPECT_EQ(std::strncmp(value->OffsetElement(), expected.c_str(),
6637089baeSDiana Picus                   value->ElementBytes()),
679df0ba59SDiana Picus         0)
689df0ba59SDiana Picus         << "expected: " << expected << "\n"
699df0ba59SDiana Picus         << "value: "
709df0ba59SDiana Picus         << std::string{value->OffsetElement(), value->ElementBytes()};
7137089baeSDiana Picus   }
7237089baeSDiana Picus 
73*873f081eSDiana Picus   template <typename INT_T = std::int64_t>
CheckDescriptorEqInt(const Descriptor * value,const INT_T expected) const74*873f081eSDiana Picus   void CheckDescriptorEqInt(
75*873f081eSDiana Picus       const Descriptor *value, const INT_T expected) const {
76*873f081eSDiana Picus     if (expected != -1) {
77*873f081eSDiana Picus       ASSERT_NE(value, nullptr);
78*873f081eSDiana Picus       EXPECT_EQ(*value->OffsetElement<INT_T>(), expected);
79*873f081eSDiana Picus     }
80*873f081eSDiana Picus   }
81*873f081eSDiana Picus 
829df0ba59SDiana Picus   template <typename RuntimeCall>
CheckValue(RuntimeCall F,const char * expectedValue,std::int64_t expectedLength=-1,std::int32_t expectedStatus=0,const char * expectedErrMsg="shouldn't change") const839df0ba59SDiana Picus   void CheckValue(RuntimeCall F, const char *expectedValue,
84*873f081eSDiana Picus       std::int64_t expectedLength = -1, std::int32_t expectedStatus = 0,
859df0ba59SDiana Picus       const char *expectedErrMsg = "shouldn't change") const {
8637089baeSDiana Picus     OwningPtr<Descriptor> value{CreateEmptyCharDescriptor()};
8737089baeSDiana Picus     ASSERT_NE(value, nullptr);
8837089baeSDiana Picus 
89*873f081eSDiana Picus     OwningPtr<Descriptor> length{
90*873f081eSDiana Picus         expectedLength == -1 ? nullptr : EmptyIntDescriptor()};
91*873f081eSDiana Picus 
929df0ba59SDiana Picus     OwningPtr<Descriptor> errmsg{CharDescriptor(expectedErrMsg)};
93*873f081eSDiana Picus     ASSERT_NE(errmsg, nullptr);
9437089baeSDiana Picus 
959df0ba59SDiana Picus     std::string expectedValueStr{
969df0ba59SDiana Picus         GetPaddedStr(expectedValue, value->ElementBytes())};
979df0ba59SDiana Picus 
98*873f081eSDiana Picus     EXPECT_EQ(F(value.get(), length.get(), errmsg.get()), expectedStatus);
999df0ba59SDiana Picus     CheckDescriptorEqStr(value.get(), expectedValueStr);
100*873f081eSDiana Picus     CheckDescriptorEqInt(length.get(), expectedLength);
1019df0ba59SDiana Picus     CheckDescriptorEqStr(errmsg.get(), expectedErrMsg);
1029df0ba59SDiana Picus   }
1039df0ba59SDiana Picus 
CheckArgumentValue(const char * expectedValue,int n) const1049df0ba59SDiana Picus   void CheckArgumentValue(const char *expectedValue, int n) const {
1059df0ba59SDiana Picus     SCOPED_TRACE(n);
1069df0ba59SDiana Picus     SCOPED_TRACE("Checking argument:");
1079df0ba59SDiana Picus     CheckValue(
108*873f081eSDiana Picus         [&](const Descriptor *value, const Descriptor *,
109*873f081eSDiana Picus             const Descriptor *errmsg) {
1109df0ba59SDiana Picus           return RTNAME(ArgumentValue)(n, value, errmsg);
1119df0ba59SDiana Picus         },
1129df0ba59SDiana Picus         expectedValue);
1139df0ba59SDiana Picus   }
1149df0ba59SDiana Picus 
CheckCommandValue(const char * args[],int n) const115*873f081eSDiana Picus   void CheckCommandValue(const char *args[], int n) const {
116*873f081eSDiana Picus     SCOPED_TRACE("Checking command:");
117*873f081eSDiana Picus     ASSERT_GE(n, 1);
118*873f081eSDiana Picus     std::string expectedValue{args[0]};
119*873f081eSDiana Picus     for (int i = 1; i < n; i++) {
120*873f081eSDiana Picus       expectedValue += " " + std::string{args[i]};
121*873f081eSDiana Picus     }
122*873f081eSDiana Picus     CheckValue(
123*873f081eSDiana Picus         [&](const Descriptor *value, const Descriptor *length,
124*873f081eSDiana Picus             const Descriptor *errmsg) {
125*873f081eSDiana Picus           return RTNAME(GetCommand)(value, length, errmsg);
126*873f081eSDiana Picus         },
127*873f081eSDiana Picus         expectedValue.c_str(), expectedValue.size());
128*873f081eSDiana Picus   }
129*873f081eSDiana Picus 
CheckEnvVarValue(const char * expectedValue,const char * name,bool trimName=true) const1309df0ba59SDiana Picus   void CheckEnvVarValue(
1319df0ba59SDiana Picus       const char *expectedValue, const char *name, bool trimName = true) const {
1329df0ba59SDiana Picus     SCOPED_TRACE(name);
1339df0ba59SDiana Picus     SCOPED_TRACE("Checking environment variable");
1349df0ba59SDiana Picus     CheckValue(
135*873f081eSDiana Picus         [&](const Descriptor *value, const Descriptor *,
136*873f081eSDiana Picus             const Descriptor *errmsg) {
1379df0ba59SDiana Picus           return RTNAME(EnvVariableValue)(*CharDescriptor(name), value,
1389df0ba59SDiana Picus               trimName, errmsg, /*sourceFile=*/nullptr, /*line=*/0);
1399df0ba59SDiana Picus         },
1409df0ba59SDiana Picus         expectedValue);
1419df0ba59SDiana Picus   }
1429df0ba59SDiana Picus 
CheckMissingEnvVarValue(const char * name,bool trimName=true) const1439df0ba59SDiana Picus   void CheckMissingEnvVarValue(const char *name, bool trimName = true) const {
1449df0ba59SDiana Picus     SCOPED_TRACE(name);
1459df0ba59SDiana Picus     SCOPED_TRACE("Checking missing environment variable");
1469df0ba59SDiana Picus 
1479df0ba59SDiana Picus     ASSERT_EQ(nullptr, std::getenv(name))
1489df0ba59SDiana Picus         << "Environment variable " << name << " not expected to exist";
1499df0ba59SDiana Picus 
1509df0ba59SDiana Picus     OwningPtr<Descriptor> nameDescriptor{CharDescriptor(name)};
1519df0ba59SDiana Picus     EXPECT_EQ(0, RTNAME(EnvVariableLength)(*nameDescriptor, trimName));
1529df0ba59SDiana Picus     CheckValue(
153*873f081eSDiana Picus         [&](const Descriptor *value, const Descriptor *,
154*873f081eSDiana Picus             const Descriptor *errmsg) {
1559df0ba59SDiana Picus           return RTNAME(EnvVariableValue)(*nameDescriptor, value, trimName,
1569df0ba59SDiana Picus               errmsg, /*sourceFile=*/nullptr, /*line=*/0);
1579df0ba59SDiana Picus         },
158*873f081eSDiana Picus         "", -1, 1, "Missing environment variable");
15937089baeSDiana Picus   }
16037089baeSDiana Picus 
CheckMissingArgumentValue(int n,const char * errStr=nullptr) const16137089baeSDiana Picus   void CheckMissingArgumentValue(int n, const char *errStr = nullptr) const {
16237089baeSDiana Picus     OwningPtr<Descriptor> value{CreateEmptyCharDescriptor()};
16337089baeSDiana Picus     ASSERT_NE(value, nullptr);
16437089baeSDiana Picus 
16537089baeSDiana Picus     OwningPtr<Descriptor> err{errStr ? CreateEmptyCharDescriptor() : nullptr};
16637089baeSDiana Picus 
16737089baeSDiana Picus     EXPECT_GT(RTNAME(ArgumentValue)(n, value.get(), err.get()), 0);
16837089baeSDiana Picus 
16937089baeSDiana Picus     std::string spaces(value->ElementBytes(), ' ');
17037089baeSDiana Picus     CheckDescriptorEqStr(value.get(), spaces);
17137089baeSDiana Picus 
17237089baeSDiana Picus     if (errStr) {
17337089baeSDiana Picus       std::string paddedErrStr(GetPaddedStr(errStr, err->ElementBytes()));
17437089baeSDiana Picus       CheckDescriptorEqStr(err.get(), paddedErrStr);
17537089baeSDiana Picus     }
17637089baeSDiana Picus   }
177*873f081eSDiana Picus 
CheckMissingCommandValue(const char * errStr=nullptr) const178*873f081eSDiana Picus   void CheckMissingCommandValue(const char *errStr = nullptr) const {
179*873f081eSDiana Picus     OwningPtr<Descriptor> value{CreateEmptyCharDescriptor()};
180*873f081eSDiana Picus     ASSERT_NE(value, nullptr);
181*873f081eSDiana Picus 
182*873f081eSDiana Picus     OwningPtr<Descriptor> length{EmptyIntDescriptor()};
183*873f081eSDiana Picus     ASSERT_NE(length, nullptr);
184*873f081eSDiana Picus 
185*873f081eSDiana Picus     OwningPtr<Descriptor> err{errStr ? CreateEmptyCharDescriptor() : nullptr};
186*873f081eSDiana Picus 
187*873f081eSDiana Picus     EXPECT_GT(RTNAME(GetCommand)(value.get(), length.get(), err.get()), 0);
188*873f081eSDiana Picus 
189*873f081eSDiana Picus     std::string spaces(value->ElementBytes(), ' ');
190*873f081eSDiana Picus     CheckDescriptorEqStr(value.get(), spaces);
191*873f081eSDiana Picus 
192*873f081eSDiana Picus     CheckDescriptorEqInt(length.get(), 0);
193*873f081eSDiana Picus 
194*873f081eSDiana Picus     if (errStr) {
195*873f081eSDiana Picus       std::string paddedErrStr(GetPaddedStr(errStr, err->ElementBytes()));
196*873f081eSDiana Picus       CheckDescriptorEqStr(err.get(), paddedErrStr);
197*873f081eSDiana Picus     }
198*873f081eSDiana Picus   }
199af63d179SDiana Picus };
200af63d179SDiana Picus 
201*873f081eSDiana Picus class NoArgv : public CommandFixture {
202*873f081eSDiana Picus protected:
NoArgv()203*873f081eSDiana Picus   NoArgv() : CommandFixture(0, nullptr) {}
204*873f081eSDiana Picus };
205*873f081eSDiana Picus 
206*873f081eSDiana Picus // TODO: Test other intrinsics with this fixture.
207*873f081eSDiana Picus 
TEST_F(NoArgv,GetCommand)208*873f081eSDiana Picus TEST_F(NoArgv, GetCommand) { CheckMissingCommandValue(); }
209*873f081eSDiana Picus 
210af63d179SDiana Picus static const char *commandOnlyArgv[]{"aProgram"};
211af63d179SDiana Picus class ZeroArguments : public CommandFixture {
212af63d179SDiana Picus protected:
ZeroArguments()213af63d179SDiana Picus   ZeroArguments() : CommandFixture(1, commandOnlyArgv) {}
214af63d179SDiana Picus };
215af63d179SDiana Picus 
TEST_F(ZeroArguments,ArgumentCount)216af63d179SDiana Picus TEST_F(ZeroArguments, ArgumentCount) { EXPECT_EQ(0, RTNAME(ArgumentCount)()); }
217af63d179SDiana Picus 
TEST_F(ZeroArguments,ArgumentLength)218af63d179SDiana Picus TEST_F(ZeroArguments, ArgumentLength) {
219af63d179SDiana Picus   EXPECT_EQ(0, RTNAME(ArgumentLength)(-1));
220af63d179SDiana Picus   EXPECT_EQ(8, RTNAME(ArgumentLength)(0));
221af63d179SDiana Picus   EXPECT_EQ(0, RTNAME(ArgumentLength)(1));
2220c375296SDiana Picus }
2230c375296SDiana Picus 
TEST_F(ZeroArguments,ArgumentValue)22437089baeSDiana Picus TEST_F(ZeroArguments, ArgumentValue) {
2259df0ba59SDiana Picus   CheckArgumentValue(commandOnlyArgv[0], 0);
22637089baeSDiana Picus }
22737089baeSDiana Picus 
TEST_F(ZeroArguments,GetCommand)228*873f081eSDiana Picus TEST_F(ZeroArguments, GetCommand) { CheckCommandValue(commandOnlyArgv, 1); }
229*873f081eSDiana Picus 
230af63d179SDiana Picus static const char *oneArgArgv[]{"aProgram", "anArgumentOfLength20"};
231af63d179SDiana Picus class OneArgument : public CommandFixture {
232af63d179SDiana Picus protected:
OneArgument()233af63d179SDiana Picus   OneArgument() : CommandFixture(2, oneArgArgv) {}
234af63d179SDiana Picus };
235af63d179SDiana Picus 
TEST_F(OneArgument,ArgumentCount)236af63d179SDiana Picus TEST_F(OneArgument, ArgumentCount) { EXPECT_EQ(1, RTNAME(ArgumentCount)()); }
237af63d179SDiana Picus 
TEST_F(OneArgument,ArgumentLength)238af63d179SDiana Picus TEST_F(OneArgument, ArgumentLength) {
239af63d179SDiana Picus   EXPECT_EQ(0, RTNAME(ArgumentLength)(-1));
240af63d179SDiana Picus   EXPECT_EQ(8, RTNAME(ArgumentLength)(0));
241af63d179SDiana Picus   EXPECT_EQ(20, RTNAME(ArgumentLength)(1));
242af63d179SDiana Picus   EXPECT_EQ(0, RTNAME(ArgumentLength)(2));
2430c375296SDiana Picus }
2440c375296SDiana Picus 
TEST_F(OneArgument,ArgumentValue)24537089baeSDiana Picus TEST_F(OneArgument, ArgumentValue) {
2469df0ba59SDiana Picus   CheckArgumentValue(oneArgArgv[0], 0);
2479df0ba59SDiana Picus   CheckArgumentValue(oneArgArgv[1], 1);
24837089baeSDiana Picus }
24937089baeSDiana Picus 
TEST_F(OneArgument,GetCommand)250*873f081eSDiana Picus TEST_F(OneArgument, GetCommand) { CheckCommandValue(oneArgArgv, 2); }
251*873f081eSDiana Picus 
252af63d179SDiana Picus static const char *severalArgsArgv[]{
253af63d179SDiana Picus     "aProgram", "16-char-long-arg", "", "-22-character-long-arg", "o"};
254af63d179SDiana Picus class SeveralArguments : public CommandFixture {
255af63d179SDiana Picus protected:
SeveralArguments()256af63d179SDiana Picus   SeveralArguments()
257af63d179SDiana Picus       : CommandFixture(sizeof(severalArgsArgv) / sizeof(*severalArgsArgv),
258af63d179SDiana Picus             severalArgsArgv) {}
259af63d179SDiana Picus };
260af63d179SDiana Picus 
TEST_F(SeveralArguments,ArgumentCount)261af63d179SDiana Picus TEST_F(SeveralArguments, ArgumentCount) {
2620c375296SDiana Picus   EXPECT_EQ(4, RTNAME(ArgumentCount)());
2630c375296SDiana Picus }
264af63d179SDiana Picus 
TEST_F(SeveralArguments,ArgumentLength)265af63d179SDiana Picus TEST_F(SeveralArguments, ArgumentLength) {
266af63d179SDiana Picus   EXPECT_EQ(0, RTNAME(ArgumentLength)(-1));
267af63d179SDiana Picus   EXPECT_EQ(8, RTNAME(ArgumentLength)(0));
268af63d179SDiana Picus   EXPECT_EQ(16, RTNAME(ArgumentLength)(1));
269af63d179SDiana Picus   EXPECT_EQ(0, RTNAME(ArgumentLength)(2));
270af63d179SDiana Picus   EXPECT_EQ(22, RTNAME(ArgumentLength)(3));
271af63d179SDiana Picus   EXPECT_EQ(1, RTNAME(ArgumentLength)(4));
272af63d179SDiana Picus   EXPECT_EQ(0, RTNAME(ArgumentLength)(5));
273af63d179SDiana Picus }
27437089baeSDiana Picus 
TEST_F(SeveralArguments,ArgumentValue)27537089baeSDiana Picus TEST_F(SeveralArguments, ArgumentValue) {
2769df0ba59SDiana Picus   CheckArgumentValue(severalArgsArgv[0], 0);
2779df0ba59SDiana Picus   CheckArgumentValue(severalArgsArgv[1], 1);
2789df0ba59SDiana Picus   CheckArgumentValue(severalArgsArgv[3], 3);
2799df0ba59SDiana Picus   CheckArgumentValue(severalArgsArgv[4], 4);
28037089baeSDiana Picus }
28137089baeSDiana Picus 
TEST_F(SeveralArguments,NoArgumentValue)28237089baeSDiana Picus TEST_F(SeveralArguments, NoArgumentValue) {
28337089baeSDiana Picus   // Make sure we don't crash if the 'value' and 'error' parameters aren't
28437089baeSDiana Picus   // passed.
28537089baeSDiana Picus   EXPECT_EQ(RTNAME(ArgumentValue)(2, nullptr, nullptr), 0);
28637089baeSDiana Picus   EXPECT_GT(RTNAME(ArgumentValue)(-1, nullptr, nullptr), 0);
28737089baeSDiana Picus }
28837089baeSDiana Picus 
TEST_F(SeveralArguments,MissingArguments)28937089baeSDiana Picus TEST_F(SeveralArguments, MissingArguments) {
29037089baeSDiana Picus   CheckMissingArgumentValue(-1, "Invalid argument number");
29137089baeSDiana Picus   CheckMissingArgumentValue(2, "Missing argument");
29237089baeSDiana Picus   CheckMissingArgumentValue(5, "Invalid argument number");
29337089baeSDiana Picus   CheckMissingArgumentValue(5);
29437089baeSDiana Picus }
29537089baeSDiana Picus 
TEST_F(SeveralArguments,ArgValueTooShort)296*873f081eSDiana Picus TEST_F(SeveralArguments, ArgValueTooShort) {
29737089baeSDiana Picus   OwningPtr<Descriptor> tooShort{CreateEmptyCharDescriptor<15>()};
29837089baeSDiana Picus   ASSERT_NE(tooShort, nullptr);
29937089baeSDiana Picus   EXPECT_EQ(RTNAME(ArgumentValue)(1, tooShort.get(), nullptr), -1);
30037089baeSDiana Picus   CheckDescriptorEqStr(tooShort.get(), severalArgsArgv[1]);
30137089baeSDiana Picus 
30237089baeSDiana Picus   OwningPtr<Descriptor> errMsg{CreateEmptyCharDescriptor()};
30337089baeSDiana Picus   ASSERT_NE(errMsg, nullptr);
30437089baeSDiana Picus 
30537089baeSDiana Picus   EXPECT_EQ(RTNAME(ArgumentValue)(1, tooShort.get(), errMsg.get()), -1);
30637089baeSDiana Picus 
30737089baeSDiana Picus   std::string expectedErrMsg{
30837089baeSDiana Picus       GetPaddedStr("Value too short", errMsg->ElementBytes())};
30937089baeSDiana Picus   CheckDescriptorEqStr(errMsg.get(), expectedErrMsg);
31037089baeSDiana Picus }
31137089baeSDiana Picus 
TEST_F(SeveralArguments,ArgErrMsgTooShort)312*873f081eSDiana Picus TEST_F(SeveralArguments, ArgErrMsgTooShort) {
31337089baeSDiana Picus   OwningPtr<Descriptor> errMsg{CreateEmptyCharDescriptor<3>()};
31437089baeSDiana Picus   EXPECT_GT(RTNAME(ArgumentValue)(-1, nullptr, errMsg.get()), 0);
31537089baeSDiana Picus   CheckDescriptorEqStr(errMsg.get(), "Inv");
31637089baeSDiana Picus }
317fc2ba5e5SDiana Picus 
TEST_F(SeveralArguments,GetCommand)318*873f081eSDiana Picus TEST_F(SeveralArguments, GetCommand) {
319*873f081eSDiana Picus   CheckMissingCommandValue();
320*873f081eSDiana Picus   CheckMissingCommandValue("Missing argument");
321*873f081eSDiana Picus }
322*873f081eSDiana Picus 
TEST_F(SeveralArguments,CommandErrMsgTooShort)323*873f081eSDiana Picus TEST_F(SeveralArguments, CommandErrMsgTooShort) {
324*873f081eSDiana Picus   OwningPtr<Descriptor> value{CreateEmptyCharDescriptor()};
325*873f081eSDiana Picus   OwningPtr<Descriptor> length{EmptyIntDescriptor()};
326*873f081eSDiana Picus   OwningPtr<Descriptor> errMsg{CreateEmptyCharDescriptor<3>()};
327*873f081eSDiana Picus 
328*873f081eSDiana Picus   EXPECT_GT(RTNAME(GetCommand)(value.get(), length.get(), errMsg.get()), 0);
329*873f081eSDiana Picus 
330*873f081eSDiana Picus   std::string spaces(value->ElementBytes(), ' ');
331*873f081eSDiana Picus   CheckDescriptorEqStr(value.get(), spaces);
332*873f081eSDiana Picus   CheckDescriptorEqInt(length.get(), 0);
333*873f081eSDiana Picus   CheckDescriptorEqStr(errMsg.get(), "Mis");
334*873f081eSDiana Picus }
335*873f081eSDiana Picus 
TEST_F(SeveralArguments,GetCommandCanTakeNull)336*873f081eSDiana Picus TEST_F(SeveralArguments, GetCommandCanTakeNull) {
337*873f081eSDiana Picus   EXPECT_GT(RTNAME(GetCommand)(nullptr, nullptr, nullptr), 0);
338*873f081eSDiana Picus }
339*873f081eSDiana Picus 
340*873f081eSDiana Picus static const char *onlyValidArgsArgv[]{
341*873f081eSDiana Picus     "aProgram", "-f", "has/a/few/slashes", "has\\a\\few\\backslashes"};
342*873f081eSDiana Picus class OnlyValidArguments : public CommandFixture {
343*873f081eSDiana Picus protected:
OnlyValidArguments()344*873f081eSDiana Picus   OnlyValidArguments()
345*873f081eSDiana Picus       : CommandFixture(sizeof(onlyValidArgsArgv) / sizeof(*onlyValidArgsArgv),
346*873f081eSDiana Picus             onlyValidArgsArgv) {}
347*873f081eSDiana Picus };
348*873f081eSDiana Picus 
TEST_F(OnlyValidArguments,GetCommand)349*873f081eSDiana Picus TEST_F(OnlyValidArguments, GetCommand) {
350*873f081eSDiana Picus   CheckCommandValue(onlyValidArgsArgv, 4);
351*873f081eSDiana Picus }
352*873f081eSDiana Picus 
TEST_F(OnlyValidArguments,CommandValueTooShort)353*873f081eSDiana Picus TEST_F(OnlyValidArguments, CommandValueTooShort) {
354*873f081eSDiana Picus   OwningPtr<Descriptor> tooShort{CreateEmptyCharDescriptor<50>()};
355*873f081eSDiana Picus   ASSERT_NE(tooShort, nullptr);
356*873f081eSDiana Picus   OwningPtr<Descriptor> length{EmptyIntDescriptor()};
357*873f081eSDiana Picus   ASSERT_NE(length, nullptr);
358*873f081eSDiana Picus 
359*873f081eSDiana Picus   EXPECT_EQ(RTNAME(GetCommand)(tooShort.get(), length.get(), nullptr), -1);
360*873f081eSDiana Picus 
361*873f081eSDiana Picus   CheckDescriptorEqStr(
362*873f081eSDiana Picus       tooShort.get(), "aProgram -f has/a/few/slashes has\\a\\few\\backslashe");
363*873f081eSDiana Picus   CheckDescriptorEqInt(length.get(), 51);
364*873f081eSDiana Picus 
365*873f081eSDiana Picus   OwningPtr<Descriptor> errMsg{CreateEmptyCharDescriptor()};
366*873f081eSDiana Picus   ASSERT_NE(errMsg, nullptr);
367*873f081eSDiana Picus 
368*873f081eSDiana Picus   EXPECT_EQ(-1, RTNAME(GetCommand)(tooShort.get(), nullptr, errMsg.get()));
369*873f081eSDiana Picus 
370*873f081eSDiana Picus   std::string expectedErrMsg{
371*873f081eSDiana Picus       GetPaddedStr("Value too short", errMsg->ElementBytes())};
372*873f081eSDiana Picus   CheckDescriptorEqStr(errMsg.get(), expectedErrMsg);
373*873f081eSDiana Picus }
374*873f081eSDiana Picus 
TEST_F(OnlyValidArguments,GetCommandCanTakeNull)375*873f081eSDiana Picus TEST_F(OnlyValidArguments, GetCommandCanTakeNull) {
376*873f081eSDiana Picus   EXPECT_EQ(0, RTNAME(GetCommand)(nullptr, nullptr, nullptr));
377*873f081eSDiana Picus 
378*873f081eSDiana Picus   OwningPtr<Descriptor> value{CreateEmptyCharDescriptor()};
379*873f081eSDiana Picus   ASSERT_NE(value, nullptr);
380*873f081eSDiana Picus   OwningPtr<Descriptor> length{EmptyIntDescriptor()};
381*873f081eSDiana Picus   ASSERT_NE(length, nullptr);
382*873f081eSDiana Picus 
383*873f081eSDiana Picus   EXPECT_EQ(0, RTNAME(GetCommand)(value.get(), nullptr, nullptr));
384*873f081eSDiana Picus   CheckDescriptorEqStr(value.get(),
385*873f081eSDiana Picus       GetPaddedStr("aProgram -f has/a/few/slashes has\\a\\few\\backslashes",
386*873f081eSDiana Picus           value->ElementBytes()));
387*873f081eSDiana Picus 
388*873f081eSDiana Picus   EXPECT_EQ(0, RTNAME(GetCommand)(nullptr, length.get(), nullptr));
389*873f081eSDiana Picus   CheckDescriptorEqInt(length.get(), 51);
390*873f081eSDiana Picus }
391*873f081eSDiana Picus 
TEST_F(OnlyValidArguments,GetCommandShortLength)392*873f081eSDiana Picus TEST_F(OnlyValidArguments, GetCommandShortLength) {
393*873f081eSDiana Picus   OwningPtr<Descriptor> length{EmptyIntDescriptor<sizeof(short)>()};
394*873f081eSDiana Picus   ASSERT_NE(length, nullptr);
395*873f081eSDiana Picus 
396*873f081eSDiana Picus   EXPECT_EQ(0, RTNAME(GetCommand)(nullptr, length.get(), nullptr));
397*873f081eSDiana Picus   CheckDescriptorEqInt<short>(length.get(), 51);
398*873f081eSDiana Picus }
399*873f081eSDiana Picus 
400fc2ba5e5SDiana Picus class EnvironmentVariables : public CommandFixture {
401fc2ba5e5SDiana Picus protected:
EnvironmentVariables()402824bf908SDiana Picus   EnvironmentVariables() : CommandFixture(0, nullptr) {
403824bf908SDiana Picus     SetEnv("NAME", "VALUE");
4049df0ba59SDiana Picus     SetEnv("EMPTY", "");
405824bf908SDiana Picus   }
406824bf908SDiana Picus 
407824bf908SDiana Picus   // If we have access to setenv, we can run some more fine-grained tests.
408824bf908SDiana Picus   template <typename ParamType = char>
409824bf908SDiana Picus   void SetEnv(const ParamType *name, const ParamType *value,
410824bf908SDiana Picus       decltype(setenv(name, value, 1)) *Enabled = nullptr) {
411824bf908SDiana Picus     ASSERT_EQ(0, setenv(name, value, /*overwrite=*/1));
412824bf908SDiana Picus     canSetEnv = true;
413824bf908SDiana Picus   }
414824bf908SDiana Picus 
415824bf908SDiana Picus   // Fallback method if setenv is not available.
SetEnv(const void *,const void *)416824bf908SDiana Picus   template <typename Unused = void> void SetEnv(const void *, const void *) {}
417824bf908SDiana Picus 
EnableFineGrainedTests() const418824bf908SDiana Picus   bool EnableFineGrainedTests() const { return canSetEnv; }
419824bf908SDiana Picus 
420824bf908SDiana Picus private:
421824bf908SDiana Picus   bool canSetEnv{false};
422fc2ba5e5SDiana Picus };
423fc2ba5e5SDiana Picus 
TEST_F(EnvironmentVariables,Nonexistent)4249df0ba59SDiana Picus TEST_F(EnvironmentVariables, Nonexistent) {
4259df0ba59SDiana Picus   CheckMissingEnvVarValue("DOESNT_EXIST");
426fc2ba5e5SDiana Picus 
4279df0ba59SDiana Picus   CheckMissingEnvVarValue("      ");
4289df0ba59SDiana Picus   CheckMissingEnvVarValue("");
4299df0ba59SDiana Picus }
430824bf908SDiana Picus 
TEST_F(EnvironmentVariables,Basic)4319df0ba59SDiana Picus TEST_F(EnvironmentVariables, Basic) {
432824bf908SDiana Picus   // Test a variable that's expected to exist in the environment.
433824bf908SDiana Picus   char *path{std::getenv("PATH")};
434824bf908SDiana Picus   auto expectedLen{static_cast<int64_t>(std::strlen(path))};
435824bf908SDiana Picus   EXPECT_EQ(expectedLen, RTNAME(EnvVariableLength)(*CharDescriptor("PATH")));
4369df0ba59SDiana Picus }
437824bf908SDiana Picus 
TEST_F(EnvironmentVariables,Trim)4389df0ba59SDiana Picus TEST_F(EnvironmentVariables, Trim) {
439824bf908SDiana Picus   if (EnableFineGrainedTests()) {
440824bf908SDiana Picus     EXPECT_EQ(5, RTNAME(EnvVariableLength)(*CharDescriptor("NAME   ")));
4419df0ba59SDiana Picus     CheckEnvVarValue("VALUE", "NAME   ");
442824bf908SDiana Picus   }
443fc2ba5e5SDiana Picus }
4449df0ba59SDiana Picus 
TEST_F(EnvironmentVariables,NoTrim)4459df0ba59SDiana Picus TEST_F(EnvironmentVariables, NoTrim) {
4469df0ba59SDiana Picus   if (EnableFineGrainedTests()) {
4479df0ba59SDiana Picus     CheckMissingEnvVarValue("NAME      ", /*trim_name=*/false);
4489df0ba59SDiana Picus   }
4499df0ba59SDiana Picus }
4509df0ba59SDiana Picus 
TEST_F(EnvironmentVariables,Empty)4519df0ba59SDiana Picus TEST_F(EnvironmentVariables, Empty) {
4529df0ba59SDiana Picus   if (EnableFineGrainedTests()) {
4539df0ba59SDiana Picus     EXPECT_EQ(0, RTNAME(EnvVariableLength)(*CharDescriptor("EMPTY")));
4549df0ba59SDiana Picus     CheckEnvVarValue("", "EMPTY");
4559df0ba59SDiana Picus   }
4569df0ba59SDiana Picus }
4579df0ba59SDiana Picus 
TEST_F(EnvironmentVariables,NoValueOrErrmsg)4589df0ba59SDiana Picus TEST_F(EnvironmentVariables, NoValueOrErrmsg) {
4599df0ba59SDiana Picus   ASSERT_EQ(std::getenv("DOESNT_EXIST"), nullptr)
4609df0ba59SDiana Picus       << "Environment variable DOESNT_EXIST actually exists";
4619df0ba59SDiana Picus   EXPECT_EQ(RTNAME(EnvVariableValue)(*CharDescriptor("DOESNT_EXIST")), 1);
4629df0ba59SDiana Picus 
4639df0ba59SDiana Picus   if (EnableFineGrainedTests()) {
4649df0ba59SDiana Picus     EXPECT_EQ(RTNAME(EnvVariableValue)(*CharDescriptor("NAME")), 0);
4659df0ba59SDiana Picus   }
4669df0ba59SDiana Picus }
4679df0ba59SDiana Picus 
TEST_F(EnvironmentVariables,ValueTooShort)4689df0ba59SDiana Picus TEST_F(EnvironmentVariables, ValueTooShort) {
4699df0ba59SDiana Picus   if (EnableFineGrainedTests()) {
4709df0ba59SDiana Picus     OwningPtr<Descriptor> tooShort{CreateEmptyCharDescriptor<2>()};
4719df0ba59SDiana Picus     ASSERT_NE(tooShort, nullptr);
4729df0ba59SDiana Picus     EXPECT_EQ(RTNAME(EnvVariableValue)(*CharDescriptor("NAME"), tooShort.get(),
4739df0ba59SDiana Picus                   /*trim_name=*/true, nullptr),
4749df0ba59SDiana Picus         -1);
4759df0ba59SDiana Picus     CheckDescriptorEqStr(tooShort.get(), "VALUE");
4769df0ba59SDiana Picus 
4779df0ba59SDiana Picus     OwningPtr<Descriptor> errMsg{CreateEmptyCharDescriptor()};
4789df0ba59SDiana Picus     ASSERT_NE(errMsg, nullptr);
4799df0ba59SDiana Picus 
4809df0ba59SDiana Picus     EXPECT_EQ(RTNAME(EnvVariableValue)(*CharDescriptor("NAME"), tooShort.get(),
4819df0ba59SDiana Picus                   /*trim_name=*/true, errMsg.get()),
4829df0ba59SDiana Picus         -1);
4839df0ba59SDiana Picus 
4849df0ba59SDiana Picus     std::string expectedErrMsg{
4859df0ba59SDiana Picus         GetPaddedStr("Value too short", errMsg->ElementBytes())};
4869df0ba59SDiana Picus     CheckDescriptorEqStr(errMsg.get(), expectedErrMsg);
4879df0ba59SDiana Picus   }
4889df0ba59SDiana Picus }
4899df0ba59SDiana Picus 
TEST_F(EnvironmentVariables,ErrMsgTooShort)4909df0ba59SDiana Picus TEST_F(EnvironmentVariables, ErrMsgTooShort) {
4919df0ba59SDiana Picus   ASSERT_EQ(std::getenv("DOESNT_EXIST"), nullptr)
4929df0ba59SDiana Picus       << "Environment variable DOESNT_EXIST actually exists";
4939df0ba59SDiana Picus 
4949df0ba59SDiana Picus   OwningPtr<Descriptor> errMsg{CreateEmptyCharDescriptor<3>()};
4959df0ba59SDiana Picus   EXPECT_EQ(RTNAME(EnvVariableValue)(*CharDescriptor("DOESNT_EXIST"), nullptr,
4969df0ba59SDiana Picus                 /*trim_name=*/true, errMsg.get()),
4979df0ba59SDiana Picus       1);
4989df0ba59SDiana Picus   CheckDescriptorEqStr(errMsg.get(), "Mis");
4999df0ba59SDiana Picus }
500