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