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