1 //===-- flang/unittests/RuntimeGTest/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/main.h"
14 
15 using namespace Fortran::runtime;
16 
17 template <std::size_t n = 64>
18 static OwningPtr<Descriptor> CreateEmptyCharDescriptor() {
19   OwningPtr<Descriptor> descriptor{Descriptor::Create(
20       sizeof(char), n, nullptr, 0, nullptr, CFI_attribute_allocatable)};
21   if (descriptor->Allocate() != 0) {
22     return nullptr;
23   }
24   return descriptor;
25 }
26 
27 static OwningPtr<Descriptor> CharDescriptor(const char *value) {
28   std::size_t n{std::strlen(value)};
29   OwningPtr<Descriptor> descriptor{Descriptor::Create(
30       sizeof(char), n, nullptr, 0, nullptr, CFI_attribute_allocatable)};
31   if (descriptor->Allocate() != 0) {
32     return nullptr;
33   }
34   std::memcpy(descriptor->OffsetElement(), value, n);
35   return descriptor;
36 }
37 
38 class CommandFixture : public ::testing::Test {
39 protected:
40   CommandFixture(int argc, const char *argv[]) {
41     RTNAME(ProgramStart)(argc, argv, {});
42   }
43 
44   CommandFixture(const char *envp[]) { RTNAME(ProgramStart)(0, nullptr, envp); }
45 
46   std::string GetPaddedStr(const char *text, std::size_t len) const {
47     std::string res{text};
48     assert(res.length() <= len && "No room to pad");
49     res.append(len - res.length(), ' ');
50     return res;
51   }
52 
53   void CheckDescriptorEqStr(
54       const Descriptor *value, const std::string &expected) const {
55     EXPECT_EQ(std::strncmp(value->OffsetElement(), expected.c_str(),
56                   value->ElementBytes()),
57         0);
58   }
59 
60   void CheckArgumentValue(int n, const char *argv) const {
61     OwningPtr<Descriptor> value{CreateEmptyCharDescriptor()};
62     ASSERT_NE(value, nullptr);
63 
64     std::string expected{GetPaddedStr(argv, value->ElementBytes())};
65 
66     EXPECT_EQ(RTNAME(ArgumentValue)(n, value.get(), nullptr), 0);
67     CheckDescriptorEqStr(value.get(), expected);
68   }
69 
70   void CheckMissingArgumentValue(int n, const char *errStr = nullptr) const {
71     OwningPtr<Descriptor> value{CreateEmptyCharDescriptor()};
72     ASSERT_NE(value, nullptr);
73 
74     OwningPtr<Descriptor> err{errStr ? CreateEmptyCharDescriptor() : nullptr};
75 
76     EXPECT_GT(RTNAME(ArgumentValue)(n, value.get(), err.get()), 0);
77 
78     std::string spaces(value->ElementBytes(), ' ');
79     CheckDescriptorEqStr(value.get(), spaces);
80 
81     if (errStr) {
82       std::string paddedErrStr(GetPaddedStr(errStr, err->ElementBytes()));
83       CheckDescriptorEqStr(err.get(), paddedErrStr);
84     }
85   }
86 };
87 
88 static const char *commandOnlyArgv[]{"aProgram"};
89 class ZeroArguments : public CommandFixture {
90 protected:
91   ZeroArguments() : CommandFixture(1, commandOnlyArgv) {}
92 };
93 
94 TEST_F(ZeroArguments, ArgumentCount) { EXPECT_EQ(0, RTNAME(ArgumentCount)()); }
95 
96 TEST_F(ZeroArguments, ArgumentLength) {
97   EXPECT_EQ(0, RTNAME(ArgumentLength)(-1));
98   EXPECT_EQ(8, RTNAME(ArgumentLength)(0));
99   EXPECT_EQ(0, RTNAME(ArgumentLength)(1));
100 }
101 
102 TEST_F(ZeroArguments, ArgumentValue) {
103   CheckArgumentValue(0, commandOnlyArgv[0]);
104 }
105 
106 static const char *oneArgArgv[]{"aProgram", "anArgumentOfLength20"};
107 class OneArgument : public CommandFixture {
108 protected:
109   OneArgument() : CommandFixture(2, oneArgArgv) {}
110 };
111 
112 TEST_F(OneArgument, ArgumentCount) { EXPECT_EQ(1, RTNAME(ArgumentCount)()); }
113 
114 TEST_F(OneArgument, ArgumentLength) {
115   EXPECT_EQ(0, RTNAME(ArgumentLength)(-1));
116   EXPECT_EQ(8, RTNAME(ArgumentLength)(0));
117   EXPECT_EQ(20, RTNAME(ArgumentLength)(1));
118   EXPECT_EQ(0, RTNAME(ArgumentLength)(2));
119 }
120 
121 TEST_F(OneArgument, ArgumentValue) {
122   CheckArgumentValue(0, oneArgArgv[0]);
123   CheckArgumentValue(1, oneArgArgv[1]);
124 }
125 
126 static const char *severalArgsArgv[]{
127     "aProgram", "16-char-long-arg", "", "-22-character-long-arg", "o"};
128 class SeveralArguments : public CommandFixture {
129 protected:
130   SeveralArguments()
131       : CommandFixture(sizeof(severalArgsArgv) / sizeof(*severalArgsArgv),
132             severalArgsArgv) {}
133 };
134 
135 TEST_F(SeveralArguments, ArgumentCount) {
136   EXPECT_EQ(4, RTNAME(ArgumentCount)());
137 }
138 
139 TEST_F(SeveralArguments, ArgumentLength) {
140   EXPECT_EQ(0, RTNAME(ArgumentLength)(-1));
141   EXPECT_EQ(8, RTNAME(ArgumentLength)(0));
142   EXPECT_EQ(16, RTNAME(ArgumentLength)(1));
143   EXPECT_EQ(0, RTNAME(ArgumentLength)(2));
144   EXPECT_EQ(22, RTNAME(ArgumentLength)(3));
145   EXPECT_EQ(1, RTNAME(ArgumentLength)(4));
146   EXPECT_EQ(0, RTNAME(ArgumentLength)(5));
147 }
148 
149 TEST_F(SeveralArguments, ArgumentValue) {
150   CheckArgumentValue(0, severalArgsArgv[0]);
151   CheckArgumentValue(1, severalArgsArgv[1]);
152   CheckArgumentValue(3, severalArgsArgv[3]);
153   CheckArgumentValue(4, severalArgsArgv[4]);
154 }
155 
156 TEST_F(SeveralArguments, NoArgumentValue) {
157   // Make sure we don't crash if the 'value' and 'error' parameters aren't
158   // passed.
159   EXPECT_EQ(RTNAME(ArgumentValue)(2, nullptr, nullptr), 0);
160   EXPECT_GT(RTNAME(ArgumentValue)(-1, nullptr, nullptr), 0);
161 }
162 
163 TEST_F(SeveralArguments, MissingArguments) {
164   CheckMissingArgumentValue(-1, "Invalid argument number");
165   CheckMissingArgumentValue(2, "Missing argument");
166   CheckMissingArgumentValue(5, "Invalid argument number");
167   CheckMissingArgumentValue(5);
168 }
169 
170 TEST_F(SeveralArguments, ValueTooShort) {
171   OwningPtr<Descriptor> tooShort{CreateEmptyCharDescriptor<15>()};
172   ASSERT_NE(tooShort, nullptr);
173   EXPECT_EQ(RTNAME(ArgumentValue)(1, tooShort.get(), nullptr), -1);
174   CheckDescriptorEqStr(tooShort.get(), severalArgsArgv[1]);
175 
176   OwningPtr<Descriptor> errMsg{CreateEmptyCharDescriptor()};
177   ASSERT_NE(errMsg, nullptr);
178 
179   EXPECT_EQ(RTNAME(ArgumentValue)(1, tooShort.get(), errMsg.get()), -1);
180 
181   std::string expectedErrMsg{
182       GetPaddedStr("Value too short", errMsg->ElementBytes())};
183   CheckDescriptorEqStr(errMsg.get(), expectedErrMsg);
184 }
185 
186 TEST_F(SeveralArguments, ErrMsgTooShort) {
187   OwningPtr<Descriptor> errMsg{CreateEmptyCharDescriptor<3>()};
188   EXPECT_GT(RTNAME(ArgumentValue)(-1, nullptr, errMsg.get()), 0);
189   CheckDescriptorEqStr(errMsg.get(), "Inv");
190 }
191 
192 static const char *env[]{"NAME=value", nullptr};
193 class EnvironmentVariables : public CommandFixture {
194 protected:
195   EnvironmentVariables() : CommandFixture(env) {}
196 };
197 
198 TEST_F(EnvironmentVariables, Length) {
199   EXPECT_EQ(5, RTNAME(EnvVariableLength)(*CharDescriptor("NAME")));
200   EXPECT_EQ(0, RTNAME(EnvVariableLength)(*CharDescriptor("DOESNT_EXIST")));
201 
202   EXPECT_EQ(5, RTNAME(EnvVariableLength)(*CharDescriptor("NAME  ")));
203   EXPECT_EQ(0,
204       RTNAME(EnvVariableLength)(*CharDescriptor("NAME "), /*trim_name=*/false));
205 
206   EXPECT_EQ(0, RTNAME(EnvVariableLength)(*CharDescriptor("     ")));
207 }
208