llvm-project/flang/unittests/Runtime/CommandTest.cpp

Ignoring revisions in .git-blame-ignore-revs. Click here to bypass and see the normal blame view.

234 lines
7.5 KiB
C++
Raw Normal View History

//===-- flang/unittests/RuntimeGTest/CommandTest.cpp ----------------------===//
//
// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
// See https://llvm.org/LICENSE.txt for license information.
// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
//
//===----------------------------------------------------------------------===//
#include "flang/Runtime/command.h"
#include "gmock/gmock.h"
#include "gtest/gtest.h"
#include "flang/Runtime/descriptor.h"
#include "flang/Runtime/main.h"
#include <cstdlib>
using namespace Fortran::runtime;
template <std::size_t n = 64>
static OwningPtr<Descriptor> CreateEmptyCharDescriptor() {
OwningPtr<Descriptor> descriptor{Descriptor::Create(
sizeof(char), n, nullptr, 0, nullptr, CFI_attribute_allocatable)};
if (descriptor->Allocate() != 0) {
return nullptr;
}
return descriptor;
}
static OwningPtr<Descriptor> CharDescriptor(const char *value) {
std::size_t n{std::strlen(value)};
OwningPtr<Descriptor> descriptor{Descriptor::Create(
sizeof(char), n, nullptr, 0, nullptr, CFI_attribute_allocatable)};
if (descriptor->Allocate() != 0) {
return nullptr;
}
std::memcpy(descriptor->OffsetElement(), value, n);
return descriptor;
}
class CommandFixture : public ::testing::Test {
protected:
CommandFixture(int argc, const char *argv[]) {
RTNAME(ProgramStart)(argc, argv, {});
}
std::string GetPaddedStr(const char *text, std::size_t len) const {
std::string res{text};
assert(res.length() <= len && "No room to pad");
res.append(len - res.length(), ' ');
return res;
}
void CheckDescriptorEqStr(
const Descriptor *value, const std::string &expected) const {
EXPECT_EQ(std::strncmp(value->OffsetElement(), expected.c_str(),
value->ElementBytes()),
0);
}
void CheckArgumentValue(int n, const char *argv) const {
OwningPtr<Descriptor> value{CreateEmptyCharDescriptor()};
ASSERT_NE(value, nullptr);
std::string expected{GetPaddedStr(argv, value->ElementBytes())};
EXPECT_EQ(RTNAME(ArgumentValue)(n, value.get(), nullptr), 0);
CheckDescriptorEqStr(value.get(), expected);
}
void CheckMissingArgumentValue(int n, const char *errStr = nullptr) const {
OwningPtr<Descriptor> value{CreateEmptyCharDescriptor()};
ASSERT_NE(value, nullptr);
OwningPtr<Descriptor> err{errStr ? CreateEmptyCharDescriptor() : nullptr};
EXPECT_GT(RTNAME(ArgumentValue)(n, value.get(), err.get()), 0);
std::string spaces(value->ElementBytes(), ' ');
CheckDescriptorEqStr(value.get(), spaces);
if (errStr) {
std::string paddedErrStr(GetPaddedStr(errStr, err->ElementBytes()));
CheckDescriptorEqStr(err.get(), paddedErrStr);
}
}
};
static const char *commandOnlyArgv[]{"aProgram"};
class ZeroArguments : public CommandFixture {
protected:
ZeroArguments() : CommandFixture(1, commandOnlyArgv) {}
};
TEST_F(ZeroArguments, ArgumentCount) { EXPECT_EQ(0, RTNAME(ArgumentCount)()); }
TEST_F(ZeroArguments, ArgumentLength) {
EXPECT_EQ(0, RTNAME(ArgumentLength)(-1));
EXPECT_EQ(8, RTNAME(ArgumentLength)(0));
EXPECT_EQ(0, RTNAME(ArgumentLength)(1));
}
TEST_F(ZeroArguments, ArgumentValue) {
CheckArgumentValue(0, commandOnlyArgv[0]);
}
static const char *oneArgArgv[]{"aProgram", "anArgumentOfLength20"};
class OneArgument : public CommandFixture {
protected:
OneArgument() : CommandFixture(2, oneArgArgv) {}
};
TEST_F(OneArgument, ArgumentCount) { EXPECT_EQ(1, RTNAME(ArgumentCount)()); }
TEST_F(OneArgument, ArgumentLength) {
EXPECT_EQ(0, RTNAME(ArgumentLength)(-1));
EXPECT_EQ(8, RTNAME(ArgumentLength)(0));
EXPECT_EQ(20, RTNAME(ArgumentLength)(1));
EXPECT_EQ(0, RTNAME(ArgumentLength)(2));
}
TEST_F(OneArgument, ArgumentValue) {
CheckArgumentValue(0, oneArgArgv[0]);
CheckArgumentValue(1, oneArgArgv[1]);
}
static const char *severalArgsArgv[]{
"aProgram", "16-char-long-arg", "", "-22-character-long-arg", "o"};
class SeveralArguments : public CommandFixture {
protected:
SeveralArguments()
: CommandFixture(sizeof(severalArgsArgv) / sizeof(*severalArgsArgv),
severalArgsArgv) {}
};
TEST_F(SeveralArguments, ArgumentCount) {
EXPECT_EQ(4, RTNAME(ArgumentCount)());
}
TEST_F(SeveralArguments, ArgumentLength) {
EXPECT_EQ(0, RTNAME(ArgumentLength)(-1));
EXPECT_EQ(8, RTNAME(ArgumentLength)(0));
EXPECT_EQ(16, RTNAME(ArgumentLength)(1));
EXPECT_EQ(0, RTNAME(ArgumentLength)(2));
EXPECT_EQ(22, RTNAME(ArgumentLength)(3));
EXPECT_EQ(1, RTNAME(ArgumentLength)(4));
EXPECT_EQ(0, RTNAME(ArgumentLength)(5));
}
TEST_F(SeveralArguments, ArgumentValue) {
CheckArgumentValue(0, severalArgsArgv[0]);
CheckArgumentValue(1, severalArgsArgv[1]);
CheckArgumentValue(3, severalArgsArgv[3]);
CheckArgumentValue(4, severalArgsArgv[4]);
}
TEST_F(SeveralArguments, NoArgumentValue) {
// Make sure we don't crash if the 'value' and 'error' parameters aren't
// passed.
EXPECT_EQ(RTNAME(ArgumentValue)(2, nullptr, nullptr), 0);
EXPECT_GT(RTNAME(ArgumentValue)(-1, nullptr, nullptr), 0);
}
TEST_F(SeveralArguments, MissingArguments) {
CheckMissingArgumentValue(-1, "Invalid argument number");
CheckMissingArgumentValue(2, "Missing argument");
CheckMissingArgumentValue(5, "Invalid argument number");
CheckMissingArgumentValue(5);
}
TEST_F(SeveralArguments, ValueTooShort) {
OwningPtr<Descriptor> tooShort{CreateEmptyCharDescriptor<15>()};
ASSERT_NE(tooShort, nullptr);
EXPECT_EQ(RTNAME(ArgumentValue)(1, tooShort.get(), nullptr), -1);
CheckDescriptorEqStr(tooShort.get(), severalArgsArgv[1]);
OwningPtr<Descriptor> errMsg{CreateEmptyCharDescriptor()};
ASSERT_NE(errMsg, nullptr);
EXPECT_EQ(RTNAME(ArgumentValue)(1, tooShort.get(), errMsg.get()), -1);
std::string expectedErrMsg{
GetPaddedStr("Value too short", errMsg->ElementBytes())};
CheckDescriptorEqStr(errMsg.get(), expectedErrMsg);
}
TEST_F(SeveralArguments, ErrMsgTooShort) {
OwningPtr<Descriptor> errMsg{CreateEmptyCharDescriptor<3>()};
EXPECT_GT(RTNAME(ArgumentValue)(-1, nullptr, errMsg.get()), 0);
CheckDescriptorEqStr(errMsg.get(), "Inv");
}
class EnvironmentVariables : public CommandFixture {
protected:
EnvironmentVariables() : CommandFixture(0, nullptr) {
SetEnv("NAME", "VALUE");
}
// If we have access to setenv, we can run some more fine-grained tests.
template <typename ParamType = char>
void SetEnv(const ParamType *name, const ParamType *value,
decltype(setenv(name, value, 1)) *Enabled = nullptr) {
ASSERT_EQ(0, setenv(name, value, /*overwrite=*/1));
canSetEnv = true;
}
// Fallback method if setenv is not available.
template <typename Unused = void> void SetEnv(const void *, const void *) {}
bool EnableFineGrainedTests() const { return canSetEnv; }
private:
bool canSetEnv{false};
};
TEST_F(EnvironmentVariables, Length) {
EXPECT_EQ(0, RTNAME(EnvVariableLength)(*CharDescriptor("DOESNT_EXIST")));
EXPECT_EQ(0, RTNAME(EnvVariableLength)(*CharDescriptor(" ")));
EXPECT_EQ(0, RTNAME(EnvVariableLength)(*CharDescriptor("")));
// Test a variable that's expected to exist in the environment.
char *path{std::getenv("PATH")};
auto expectedLen{static_cast<int64_t>(std::strlen(path))};
EXPECT_EQ(expectedLen, RTNAME(EnvVariableLength)(*CharDescriptor("PATH")));
if (EnableFineGrainedTests()) {
EXPECT_EQ(5, RTNAME(EnvVariableLength)(*CharDescriptor("NAME")));
EXPECT_EQ(5, RTNAME(EnvVariableLength)(*CharDescriptor("NAME ")));
EXPECT_EQ(0,
RTNAME(EnvVariableLength)(
*CharDescriptor("NAME "), /*trim_name=*/false));
}
}