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/main.h"
14 #include <cstdlib>
15
16 using namespace Fortran::runtime;
17
18 template <std::size_t n = 64>
CreateEmptyCharDescriptor()19 static OwningPtr<Descriptor> CreateEmptyCharDescriptor() {
20 OwningPtr<Descriptor> descriptor{Descriptor::Create(
21 sizeof(char), n, nullptr, 0, nullptr, CFI_attribute_allocatable)};
22 if (descriptor->Allocate() != 0) {
23 return nullptr;
24 }
25 return descriptor;
26 }
27
CharDescriptor(const char * value)28 static OwningPtr<Descriptor> CharDescriptor(const char *value) {
29 std::size_t n{std::strlen(value)};
30 OwningPtr<Descriptor> descriptor{Descriptor::Create(
31 sizeof(char), n, nullptr, 0, nullptr, CFI_attribute_allocatable)};
32 if (descriptor->Allocate() != 0) {
33 return nullptr;
34 }
35 std::memcpy(descriptor->OffsetElement(), value, n);
36 return descriptor;
37 }
38
39 template <int kind = sizeof(std::int64_t)>
EmptyIntDescriptor()40 static OwningPtr<Descriptor> EmptyIntDescriptor() {
41 OwningPtr<Descriptor> descriptor{Descriptor::Create(TypeCategory::Integer,
42 kind, nullptr, 0, nullptr, CFI_attribute_allocatable)};
43 if (descriptor->Allocate() != 0) {
44 return nullptr;
45 }
46 return descriptor;
47 }
48
49 class CommandFixture : public ::testing::Test {
50 protected:
CommandFixture(int argc,const char * argv[])51 CommandFixture(int argc, const char *argv[]) {
52 RTNAME(ProgramStart)(argc, argv, {});
53 }
54
GetPaddedStr(const char * text,std::size_t len) const55 std::string GetPaddedStr(const char *text, std::size_t len) const {
56 std::string res{text};
57 assert(res.length() <= len && "No room to pad");
58 res.append(len - res.length(), ' ');
59 return res;
60 }
61
CheckDescriptorEqStr(const Descriptor * value,const std::string & expected) const62 void CheckDescriptorEqStr(
63 const Descriptor *value, const std::string &expected) const {
64 ASSERT_NE(value, nullptr);
65 EXPECT_EQ(std::strncmp(value->OffsetElement(), expected.c_str(),
66 value->ElementBytes()),
67 0)
68 << "expected: " << expected << "\n"
69 << "value: "
70 << std::string{value->OffsetElement(), value->ElementBytes()};
71 }
72
73 template <typename INT_T = std::int64_t>
CheckDescriptorEqInt(const Descriptor * value,const INT_T expected) const74 void CheckDescriptorEqInt(
75 const Descriptor *value, const INT_T expected) const {
76 if (expected != -1) {
77 ASSERT_NE(value, nullptr);
78 EXPECT_EQ(*value->OffsetElement<INT_T>(), expected);
79 }
80 }
81
82 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") const83 void CheckValue(RuntimeCall F, const char *expectedValue,
84 std::int64_t expectedLength = -1, std::int32_t expectedStatus = 0,
85 const char *expectedErrMsg = "shouldn't change") const {
86 OwningPtr<Descriptor> value{CreateEmptyCharDescriptor()};
87 ASSERT_NE(value, nullptr);
88
89 OwningPtr<Descriptor> length{
90 expectedLength == -1 ? nullptr : EmptyIntDescriptor()};
91
92 OwningPtr<Descriptor> errmsg{CharDescriptor(expectedErrMsg)};
93 ASSERT_NE(errmsg, nullptr);
94
95 std::string expectedValueStr{
96 GetPaddedStr(expectedValue, value->ElementBytes())};
97
98 EXPECT_EQ(F(value.get(), length.get(), errmsg.get()), expectedStatus);
99 CheckDescriptorEqStr(value.get(), expectedValueStr);
100 CheckDescriptorEqInt(length.get(), expectedLength);
101 CheckDescriptorEqStr(errmsg.get(), expectedErrMsg);
102 }
103
CheckArgumentValue(const char * expectedValue,int n) const104 void CheckArgumentValue(const char *expectedValue, int n) const {
105 SCOPED_TRACE(n);
106 SCOPED_TRACE("Checking argument:");
107 CheckValue(
108 [&](const Descriptor *value, const Descriptor *,
109 const Descriptor *errmsg) {
110 return RTNAME(ArgumentValue)(n, value, errmsg);
111 },
112 expectedValue);
113 }
114
CheckCommandValue(const char * args[],int n) const115 void CheckCommandValue(const char *args[], int n) const {
116 SCOPED_TRACE("Checking command:");
117 ASSERT_GE(n, 1);
118 std::string expectedValue{args[0]};
119 for (int i = 1; i < n; i++) {
120 expectedValue += " " + std::string{args[i]};
121 }
122 CheckValue(
123 [&](const Descriptor *value, const Descriptor *length,
124 const Descriptor *errmsg) {
125 return RTNAME(GetCommand)(value, length, errmsg);
126 },
127 expectedValue.c_str(), expectedValue.size());
128 }
129
CheckEnvVarValue(const char * expectedValue,const char * name,bool trimName=true) const130 void CheckEnvVarValue(
131 const char *expectedValue, const char *name, bool trimName = true) const {
132 SCOPED_TRACE(name);
133 SCOPED_TRACE("Checking environment variable");
134 CheckValue(
135 [&](const Descriptor *value, const Descriptor *,
136 const Descriptor *errmsg) {
137 return RTNAME(EnvVariableValue)(*CharDescriptor(name), value,
138 trimName, errmsg, /*sourceFile=*/nullptr, /*line=*/0);
139 },
140 expectedValue);
141 }
142
CheckMissingEnvVarValue(const char * name,bool trimName=true) const143 void CheckMissingEnvVarValue(const char *name, bool trimName = true) const {
144 SCOPED_TRACE(name);
145 SCOPED_TRACE("Checking missing environment variable");
146
147 ASSERT_EQ(nullptr, std::getenv(name))
148 << "Environment variable " << name << " not expected to exist";
149
150 OwningPtr<Descriptor> nameDescriptor{CharDescriptor(name)};
151 EXPECT_EQ(0, RTNAME(EnvVariableLength)(*nameDescriptor, trimName));
152 CheckValue(
153 [&](const Descriptor *value, const Descriptor *,
154 const Descriptor *errmsg) {
155 return RTNAME(EnvVariableValue)(*nameDescriptor, value, trimName,
156 errmsg, /*sourceFile=*/nullptr, /*line=*/0);
157 },
158 "", -1, 1, "Missing environment variable");
159 }
160
CheckMissingArgumentValue(int n,const char * errStr=nullptr) const161 void CheckMissingArgumentValue(int n, const char *errStr = nullptr) const {
162 OwningPtr<Descriptor> value{CreateEmptyCharDescriptor()};
163 ASSERT_NE(value, nullptr);
164
165 OwningPtr<Descriptor> err{errStr ? CreateEmptyCharDescriptor() : nullptr};
166
167 EXPECT_GT(RTNAME(ArgumentValue)(n, value.get(), err.get()), 0);
168
169 std::string spaces(value->ElementBytes(), ' ');
170 CheckDescriptorEqStr(value.get(), spaces);
171
172 if (errStr) {
173 std::string paddedErrStr(GetPaddedStr(errStr, err->ElementBytes()));
174 CheckDescriptorEqStr(err.get(), paddedErrStr);
175 }
176 }
177
CheckMissingCommandValue(const char * errStr=nullptr) const178 void CheckMissingCommandValue(const char *errStr = nullptr) const {
179 OwningPtr<Descriptor> value{CreateEmptyCharDescriptor()};
180 ASSERT_NE(value, nullptr);
181
182 OwningPtr<Descriptor> length{EmptyIntDescriptor()};
183 ASSERT_NE(length, nullptr);
184
185 OwningPtr<Descriptor> err{errStr ? CreateEmptyCharDescriptor() : nullptr};
186
187 EXPECT_GT(RTNAME(GetCommand)(value.get(), length.get(), err.get()), 0);
188
189 std::string spaces(value->ElementBytes(), ' ');
190 CheckDescriptorEqStr(value.get(), spaces);
191
192 CheckDescriptorEqInt(length.get(), 0);
193
194 if (errStr) {
195 std::string paddedErrStr(GetPaddedStr(errStr, err->ElementBytes()));
196 CheckDescriptorEqStr(err.get(), paddedErrStr);
197 }
198 }
199 };
200
201 class NoArgv : public CommandFixture {
202 protected:
NoArgv()203 NoArgv() : CommandFixture(0, nullptr) {}
204 };
205
206 // TODO: Test other intrinsics with this fixture.
207
TEST_F(NoArgv,GetCommand)208 TEST_F(NoArgv, GetCommand) { CheckMissingCommandValue(); }
209
210 static const char *commandOnlyArgv[]{"aProgram"};
211 class ZeroArguments : public CommandFixture {
212 protected:
ZeroArguments()213 ZeroArguments() : CommandFixture(1, commandOnlyArgv) {}
214 };
215
TEST_F(ZeroArguments,ArgumentCount)216 TEST_F(ZeroArguments, ArgumentCount) { EXPECT_EQ(0, RTNAME(ArgumentCount)()); }
217
TEST_F(ZeroArguments,ArgumentLength)218 TEST_F(ZeroArguments, ArgumentLength) {
219 EXPECT_EQ(0, RTNAME(ArgumentLength)(-1));
220 EXPECT_EQ(8, RTNAME(ArgumentLength)(0));
221 EXPECT_EQ(0, RTNAME(ArgumentLength)(1));
222 }
223
TEST_F(ZeroArguments,ArgumentValue)224 TEST_F(ZeroArguments, ArgumentValue) {
225 CheckArgumentValue(commandOnlyArgv[0], 0);
226 }
227
TEST_F(ZeroArguments,GetCommand)228 TEST_F(ZeroArguments, GetCommand) { CheckCommandValue(commandOnlyArgv, 1); }
229
230 static const char *oneArgArgv[]{"aProgram", "anArgumentOfLength20"};
231 class OneArgument : public CommandFixture {
232 protected:
OneArgument()233 OneArgument() : CommandFixture(2, oneArgArgv) {}
234 };
235
TEST_F(OneArgument,ArgumentCount)236 TEST_F(OneArgument, ArgumentCount) { EXPECT_EQ(1, RTNAME(ArgumentCount)()); }
237
TEST_F(OneArgument,ArgumentLength)238 TEST_F(OneArgument, ArgumentLength) {
239 EXPECT_EQ(0, RTNAME(ArgumentLength)(-1));
240 EXPECT_EQ(8, RTNAME(ArgumentLength)(0));
241 EXPECT_EQ(20, RTNAME(ArgumentLength)(1));
242 EXPECT_EQ(0, RTNAME(ArgumentLength)(2));
243 }
244
TEST_F(OneArgument,ArgumentValue)245 TEST_F(OneArgument, ArgumentValue) {
246 CheckArgumentValue(oneArgArgv[0], 0);
247 CheckArgumentValue(oneArgArgv[1], 1);
248 }
249
TEST_F(OneArgument,GetCommand)250 TEST_F(OneArgument, GetCommand) { CheckCommandValue(oneArgArgv, 2); }
251
252 static const char *severalArgsArgv[]{
253 "aProgram", "16-char-long-arg", "", "-22-character-long-arg", "o"};
254 class SeveralArguments : public CommandFixture {
255 protected:
SeveralArguments()256 SeveralArguments()
257 : CommandFixture(sizeof(severalArgsArgv) / sizeof(*severalArgsArgv),
258 severalArgsArgv) {}
259 };
260
TEST_F(SeveralArguments,ArgumentCount)261 TEST_F(SeveralArguments, ArgumentCount) {
262 EXPECT_EQ(4, RTNAME(ArgumentCount)());
263 }
264
TEST_F(SeveralArguments,ArgumentLength)265 TEST_F(SeveralArguments, ArgumentLength) {
266 EXPECT_EQ(0, RTNAME(ArgumentLength)(-1));
267 EXPECT_EQ(8, RTNAME(ArgumentLength)(0));
268 EXPECT_EQ(16, RTNAME(ArgumentLength)(1));
269 EXPECT_EQ(0, RTNAME(ArgumentLength)(2));
270 EXPECT_EQ(22, RTNAME(ArgumentLength)(3));
271 EXPECT_EQ(1, RTNAME(ArgumentLength)(4));
272 EXPECT_EQ(0, RTNAME(ArgumentLength)(5));
273 }
274
TEST_F(SeveralArguments,ArgumentValue)275 TEST_F(SeveralArguments, ArgumentValue) {
276 CheckArgumentValue(severalArgsArgv[0], 0);
277 CheckArgumentValue(severalArgsArgv[1], 1);
278 CheckArgumentValue(severalArgsArgv[3], 3);
279 CheckArgumentValue(severalArgsArgv[4], 4);
280 }
281
TEST_F(SeveralArguments,NoArgumentValue)282 TEST_F(SeveralArguments, NoArgumentValue) {
283 // Make sure we don't crash if the 'value' and 'error' parameters aren't
284 // passed.
285 EXPECT_EQ(RTNAME(ArgumentValue)(2, nullptr, nullptr), 0);
286 EXPECT_GT(RTNAME(ArgumentValue)(-1, nullptr, nullptr), 0);
287 }
288
TEST_F(SeveralArguments,MissingArguments)289 TEST_F(SeveralArguments, MissingArguments) {
290 CheckMissingArgumentValue(-1, "Invalid argument number");
291 CheckMissingArgumentValue(2, "Missing argument");
292 CheckMissingArgumentValue(5, "Invalid argument number");
293 CheckMissingArgumentValue(5);
294 }
295
TEST_F(SeveralArguments,ArgValueTooShort)296 TEST_F(SeveralArguments, ArgValueTooShort) {
297 OwningPtr<Descriptor> tooShort{CreateEmptyCharDescriptor<15>()};
298 ASSERT_NE(tooShort, nullptr);
299 EXPECT_EQ(RTNAME(ArgumentValue)(1, tooShort.get(), nullptr), -1);
300 CheckDescriptorEqStr(tooShort.get(), severalArgsArgv[1]);
301
302 OwningPtr<Descriptor> errMsg{CreateEmptyCharDescriptor()};
303 ASSERT_NE(errMsg, nullptr);
304
305 EXPECT_EQ(RTNAME(ArgumentValue)(1, tooShort.get(), errMsg.get()), -1);
306
307 std::string expectedErrMsg{
308 GetPaddedStr("Value too short", errMsg->ElementBytes())};
309 CheckDescriptorEqStr(errMsg.get(), expectedErrMsg);
310 }
311
TEST_F(SeveralArguments,ArgErrMsgTooShort)312 TEST_F(SeveralArguments, ArgErrMsgTooShort) {
313 OwningPtr<Descriptor> errMsg{CreateEmptyCharDescriptor<3>()};
314 EXPECT_GT(RTNAME(ArgumentValue)(-1, nullptr, errMsg.get()), 0);
315 CheckDescriptorEqStr(errMsg.get(), "Inv");
316 }
317
TEST_F(SeveralArguments,GetCommand)318 TEST_F(SeveralArguments, GetCommand) {
319 CheckMissingCommandValue();
320 CheckMissingCommandValue("Missing argument");
321 }
322
TEST_F(SeveralArguments,CommandErrMsgTooShort)323 TEST_F(SeveralArguments, CommandErrMsgTooShort) {
324 OwningPtr<Descriptor> value{CreateEmptyCharDescriptor()};
325 OwningPtr<Descriptor> length{EmptyIntDescriptor()};
326 OwningPtr<Descriptor> errMsg{CreateEmptyCharDescriptor<3>()};
327
328 EXPECT_GT(RTNAME(GetCommand)(value.get(), length.get(), errMsg.get()), 0);
329
330 std::string spaces(value->ElementBytes(), ' ');
331 CheckDescriptorEqStr(value.get(), spaces);
332 CheckDescriptorEqInt(length.get(), 0);
333 CheckDescriptorEqStr(errMsg.get(), "Mis");
334 }
335
TEST_F(SeveralArguments,GetCommandCanTakeNull)336 TEST_F(SeveralArguments, GetCommandCanTakeNull) {
337 EXPECT_GT(RTNAME(GetCommand)(nullptr, nullptr, nullptr), 0);
338 }
339
340 static const char *onlyValidArgsArgv[]{
341 "aProgram", "-f", "has/a/few/slashes", "has\\a\\few\\backslashes"};
342 class OnlyValidArguments : public CommandFixture {
343 protected:
OnlyValidArguments()344 OnlyValidArguments()
345 : CommandFixture(sizeof(onlyValidArgsArgv) / sizeof(*onlyValidArgsArgv),
346 onlyValidArgsArgv) {}
347 };
348
TEST_F(OnlyValidArguments,GetCommand)349 TEST_F(OnlyValidArguments, GetCommand) {
350 CheckCommandValue(onlyValidArgsArgv, 4);
351 }
352
TEST_F(OnlyValidArguments,CommandValueTooShort)353 TEST_F(OnlyValidArguments, CommandValueTooShort) {
354 OwningPtr<Descriptor> tooShort{CreateEmptyCharDescriptor<50>()};
355 ASSERT_NE(tooShort, nullptr);
356 OwningPtr<Descriptor> length{EmptyIntDescriptor()};
357 ASSERT_NE(length, nullptr);
358
359 EXPECT_EQ(RTNAME(GetCommand)(tooShort.get(), length.get(), nullptr), -1);
360
361 CheckDescriptorEqStr(
362 tooShort.get(), "aProgram -f has/a/few/slashes has\\a\\few\\backslashe");
363 CheckDescriptorEqInt(length.get(), 51);
364
365 OwningPtr<Descriptor> errMsg{CreateEmptyCharDescriptor()};
366 ASSERT_NE(errMsg, nullptr);
367
368 EXPECT_EQ(-1, RTNAME(GetCommand)(tooShort.get(), nullptr, errMsg.get()));
369
370 std::string expectedErrMsg{
371 GetPaddedStr("Value too short", errMsg->ElementBytes())};
372 CheckDescriptorEqStr(errMsg.get(), expectedErrMsg);
373 }
374
TEST_F(OnlyValidArguments,GetCommandCanTakeNull)375 TEST_F(OnlyValidArguments, GetCommandCanTakeNull) {
376 EXPECT_EQ(0, RTNAME(GetCommand)(nullptr, nullptr, nullptr));
377
378 OwningPtr<Descriptor> value{CreateEmptyCharDescriptor()};
379 ASSERT_NE(value, nullptr);
380 OwningPtr<Descriptor> length{EmptyIntDescriptor()};
381 ASSERT_NE(length, nullptr);
382
383 EXPECT_EQ(0, RTNAME(GetCommand)(value.get(), nullptr, nullptr));
384 CheckDescriptorEqStr(value.get(),
385 GetPaddedStr("aProgram -f has/a/few/slashes has\\a\\few\\backslashes",
386 value->ElementBytes()));
387
388 EXPECT_EQ(0, RTNAME(GetCommand)(nullptr, length.get(), nullptr));
389 CheckDescriptorEqInt(length.get(), 51);
390 }
391
TEST_F(OnlyValidArguments,GetCommandShortLength)392 TEST_F(OnlyValidArguments, GetCommandShortLength) {
393 OwningPtr<Descriptor> length{EmptyIntDescriptor<sizeof(short)>()};
394 ASSERT_NE(length, nullptr);
395
396 EXPECT_EQ(0, RTNAME(GetCommand)(nullptr, length.get(), nullptr));
397 CheckDescriptorEqInt<short>(length.get(), 51);
398 }
399
400 class EnvironmentVariables : public CommandFixture {
401 protected:
EnvironmentVariables()402 EnvironmentVariables() : CommandFixture(0, nullptr) {
403 SetEnv("NAME", "VALUE");
404 SetEnv("EMPTY", "");
405 }
406
407 // If we have access to setenv, we can run some more fine-grained tests.
408 template <typename ParamType = char>
409 void SetEnv(const ParamType *name, const ParamType *value,
410 decltype(setenv(name, value, 1)) *Enabled = nullptr) {
411 ASSERT_EQ(0, setenv(name, value, /*overwrite=*/1));
412 canSetEnv = true;
413 }
414
415 // Fallback method if setenv is not available.
SetEnv(const void *,const void *)416 template <typename Unused = void> void SetEnv(const void *, const void *) {}
417
EnableFineGrainedTests() const418 bool EnableFineGrainedTests() const { return canSetEnv; }
419
420 private:
421 bool canSetEnv{false};
422 };
423
TEST_F(EnvironmentVariables,Nonexistent)424 TEST_F(EnvironmentVariables, Nonexistent) {
425 CheckMissingEnvVarValue("DOESNT_EXIST");
426
427 CheckMissingEnvVarValue(" ");
428 CheckMissingEnvVarValue("");
429 }
430
TEST_F(EnvironmentVariables,Basic)431 TEST_F(EnvironmentVariables, Basic) {
432 // Test a variable that's expected to exist in the environment.
433 char *path{std::getenv("PATH")};
434 auto expectedLen{static_cast<int64_t>(std::strlen(path))};
435 EXPECT_EQ(expectedLen, RTNAME(EnvVariableLength)(*CharDescriptor("PATH")));
436 }
437
TEST_F(EnvironmentVariables,Trim)438 TEST_F(EnvironmentVariables, Trim) {
439 if (EnableFineGrainedTests()) {
440 EXPECT_EQ(5, RTNAME(EnvVariableLength)(*CharDescriptor("NAME ")));
441 CheckEnvVarValue("VALUE", "NAME ");
442 }
443 }
444
TEST_F(EnvironmentVariables,NoTrim)445 TEST_F(EnvironmentVariables, NoTrim) {
446 if (EnableFineGrainedTests()) {
447 CheckMissingEnvVarValue("NAME ", /*trim_name=*/false);
448 }
449 }
450
TEST_F(EnvironmentVariables,Empty)451 TEST_F(EnvironmentVariables, Empty) {
452 if (EnableFineGrainedTests()) {
453 EXPECT_EQ(0, RTNAME(EnvVariableLength)(*CharDescriptor("EMPTY")));
454 CheckEnvVarValue("", "EMPTY");
455 }
456 }
457
TEST_F(EnvironmentVariables,NoValueOrErrmsg)458 TEST_F(EnvironmentVariables, NoValueOrErrmsg) {
459 ASSERT_EQ(std::getenv("DOESNT_EXIST"), nullptr)
460 << "Environment variable DOESNT_EXIST actually exists";
461 EXPECT_EQ(RTNAME(EnvVariableValue)(*CharDescriptor("DOESNT_EXIST")), 1);
462
463 if (EnableFineGrainedTests()) {
464 EXPECT_EQ(RTNAME(EnvVariableValue)(*CharDescriptor("NAME")), 0);
465 }
466 }
467
TEST_F(EnvironmentVariables,ValueTooShort)468 TEST_F(EnvironmentVariables, ValueTooShort) {
469 if (EnableFineGrainedTests()) {
470 OwningPtr<Descriptor> tooShort{CreateEmptyCharDescriptor<2>()};
471 ASSERT_NE(tooShort, nullptr);
472 EXPECT_EQ(RTNAME(EnvVariableValue)(*CharDescriptor("NAME"), tooShort.get(),
473 /*trim_name=*/true, nullptr),
474 -1);
475 CheckDescriptorEqStr(tooShort.get(), "VALUE");
476
477 OwningPtr<Descriptor> errMsg{CreateEmptyCharDescriptor()};
478 ASSERT_NE(errMsg, nullptr);
479
480 EXPECT_EQ(RTNAME(EnvVariableValue)(*CharDescriptor("NAME"), tooShort.get(),
481 /*trim_name=*/true, errMsg.get()),
482 -1);
483
484 std::string expectedErrMsg{
485 GetPaddedStr("Value too short", errMsg->ElementBytes())};
486 CheckDescriptorEqStr(errMsg.get(), expectedErrMsg);
487 }
488 }
489
TEST_F(EnvironmentVariables,ErrMsgTooShort)490 TEST_F(EnvironmentVariables, ErrMsgTooShort) {
491 ASSERT_EQ(std::getenv("DOESNT_EXIST"), nullptr)
492 << "Environment variable DOESNT_EXIST actually exists";
493
494 OwningPtr<Descriptor> errMsg{CreateEmptyCharDescriptor<3>()};
495 EXPECT_EQ(RTNAME(EnvVariableValue)(*CharDescriptor("DOESNT_EXIST"), nullptr,
496 /*trim_name=*/true, errMsg.get()),
497 1);
498 CheckDescriptorEqStr(errMsg.get(), "Mis");
499 }
500