#ifndef _WIN32
#include "CrashHandlerFixture.h"
#include "gtest/gtest.h"
#include "flang/Runtime/extensions.h"
#include "llvm/ADT/Twine.h"
#include <fcntl.h>
#include <sys/stat.h>
#include <sys/types.h>
#include <unistd.h>
namespace {
struct AccessTests : public CrashHandlerFixture {};
struct AccessType {
bool read{false};
bool write{false};
bool execute{false};
bool exists{false};
};
}
static std::string addPIDSuffix(const char *name) {
std::stringstream ss;
ss << name;
ss << '.';
ss << getpid();
return ss.str();
}
static bool exists(const std::string &path) {
return access(path.c_str(), F_OK) == 0;
}
static const char *temp_directory_path() {
const char *env_paths[] = {"TMPDIR", "TMP", "TEMP", "TEMPDIR"};
const char *ret = nullptr;
for (auto &ep : env_paths) {
if ((ret = getenv(ep))) {
break;
}
}
if (ret == nullptr) {
#if defined(__ANDROID__)
ret = "/data/local/tmp";
#else
ret = "/tmp";
#endif
}
assert(exists(ret));
return ret;
}
static std::string createTemporaryFile(
const char *name, const AccessType &accessType) {
std::string path =
(llvm::Twine{temp_directory_path()} + "/" + addPIDSuffix(name)).str();
mode_t mode{0};
if (accessType.read) {
mode |= S_IRUSR;
}
if (accessType.write) {
mode |= S_IWUSR;
}
if (accessType.execute) {
mode |= S_IXUSR;
}
int file = open(path.c_str(), O_CREAT | O_EXCL, mode);
if (file == -1) {
return {};
}
close(file);
return path;
}
static std::int64_t callAccess(
const std::string &path, const AccessType &accessType) {
const char *cpath{path.c_str()};
std::int64_t pathlen = std::strlen(cpath);
std::string mode;
if (accessType.read) {
mode += 'r';
}
if (accessType.write) {
mode += 'w';
}
if (accessType.execute) {
mode += 'x';
}
if (accessType.exists) {
mode += ' ';
}
const char *cmode = mode.c_str();
std::int64_t modelen = std::strlen(cmode);
return FORTRAN_PROCEDURE_NAME(access)(cpath, pathlen, cmode, modelen);
}
TEST(AccessTests, TestExists) {
AccessType accessType;
accessType.exists = true;
std::string path = createTemporaryFile(__func__, accessType);
ASSERT_FALSE(path.empty());
std::int64_t res = callAccess(path, accessType);
ASSERT_EQ(unlink(path.c_str()), 0);
ASSERT_EQ(res, 0);
}
TEST(AccessTests, TestNotExists) {
std::string nonExistant{addPIDSuffix(__func__)};
ASSERT_FALSE(exists(nonExistant));
AccessType accessType;
accessType.exists = true;
std::int64_t res = callAccess(nonExistant, accessType);
ASSERT_NE(res, 0);
}
TEST(AccessTests, TestRead) {
AccessType accessType;
accessType.read = true;
std::string path = createTemporaryFile(__func__, accessType);
ASSERT_FALSE(path.empty());
std::int64_t res = callAccess(path, accessType);
ASSERT_EQ(unlink(path.c_str()), 0);
ASSERT_EQ(res, 0);
}
TEST(AccessTests, TestNotRead) {
AccessType accessType;
accessType.read = false;
std::string path = createTemporaryFile(__func__, accessType);
ASSERT_FALSE(path.empty());
accessType.read = true;
std::int64_t res = callAccess(path, accessType);
ASSERT_EQ(unlink(path.c_str()), 0);
ASSERT_NE(res, 0);
}
TEST(AccessTests, TestWrite) {
AccessType accessType;
accessType.write = true;
std::string path = createTemporaryFile(__func__, accessType);
ASSERT_FALSE(path.empty());
std::int64_t res = callAccess(path, accessType);
ASSERT_EQ(unlink(path.c_str()), 0);
ASSERT_EQ(res, 0);
}
TEST(AccessTests, TestNotWrite) {
AccessType accessType;
accessType.write = false;
std::string path = createTemporaryFile(__func__, accessType);
ASSERT_FALSE(path.empty());
accessType.write = true;
std::int64_t res = callAccess(path, accessType);
ASSERT_EQ(unlink(path.c_str()), 0);
ASSERT_NE(res, 0);
}
TEST(AccessTests, TestReadWrite) {
AccessType accessType;
accessType.read = true;
accessType.write = true;
std::string path = createTemporaryFile(__func__, accessType);
ASSERT_FALSE(path.empty());
std::int64_t res = callAccess(path, accessType);
ASSERT_EQ(unlink(path.c_str()), 0);
ASSERT_EQ(res, 0);
}
TEST(AccessTests, TestNotReadWrite0) {
AccessType accessType;
accessType.read = false;
accessType.write = false;
std::string path = createTemporaryFile(__func__, accessType);
ASSERT_FALSE(path.empty());
accessType.read = true;
accessType.write = true;
std::int64_t res = callAccess(path, accessType);
ASSERT_EQ(unlink(path.c_str()), 0);
ASSERT_NE(res, 0);
}
TEST(AccessTests, TestNotReadWrite1) {
AccessType accessType;
accessType.read = true;
accessType.write = false;
std::string path = createTemporaryFile(__func__, accessType);
ASSERT_FALSE(path.empty());
accessType.read = true;
accessType.write = true;
std::int64_t res = callAccess(path, accessType);
ASSERT_EQ(unlink(path.c_str()), 0);
ASSERT_NE(res, 0);
}
TEST(AccessTests, TestNotReadWrite2) {
AccessType accessType;
accessType.read = false;
accessType.write = true;
std::string path = createTemporaryFile(__func__, accessType);
ASSERT_FALSE(path.empty());
accessType.read = true;
accessType.write = true;
std::int64_t res = callAccess(path, accessType);
ASSERT_EQ(unlink(path.c_str()), 0);
ASSERT_NE(res, 0);
}
TEST(AccessTests, TestExecute) {
AccessType accessType;
accessType.execute = true;
std::string path = createTemporaryFile(__func__, accessType);
ASSERT_FALSE(path.empty());
std::int64_t res = callAccess(path, accessType);
ASSERT_EQ(unlink(path.c_str()), 0);
ASSERT_EQ(res, 0);
}
TEST(AccessTests, TestNotExecute) {
AccessType accessType;
accessType.execute = false;
std::string path = createTemporaryFile(__func__, accessType);
ASSERT_FALSE(path.empty());
accessType.execute = true;
std::int64_t res = callAccess(path, accessType);
ASSERT_EQ(unlink(path.c_str()), 0);
ASSERT_NE(res, 0);
}
TEST(AccessTests, TestRWX) {
AccessType accessType;
accessType.read = true;
accessType.write = true;
accessType.execute = true;
std::string path = createTemporaryFile(__func__, accessType);
ASSERT_FALSE(path.empty());
std::int64_t res = callAccess(path, accessType);
ASSERT_EQ(unlink(path.c_str()), 0);
ASSERT_EQ(res, 0);
}
TEST(AccessTests, TestNotRWX0) {
AccessType accessType;
accessType.read = false;
accessType.write = false;
accessType.execute = false;
std::string path = createTemporaryFile(__func__, accessType);
ASSERT_FALSE(path.empty());
accessType.read = true;
accessType.write = true;
accessType.execute = true;
std::int64_t res = callAccess(path, accessType);
ASSERT_EQ(unlink(path.c_str()), 0);
ASSERT_NE(res, 0);
}
TEST(AccessTests, TestNotRWX1) {
AccessType accessType;
accessType.read = true;
accessType.write = false;
accessType.execute = false;
std::string path = createTemporaryFile(__func__, accessType);
ASSERT_FALSE(path.empty());
accessType.read = true;
accessType.write = true;
accessType.execute = true;
std::int64_t res = callAccess(path, accessType);
ASSERT_EQ(unlink(path.c_str()), 0);
ASSERT_NE(res, 0);
}
TEST(AccessTests, TestNotRWX2) {
AccessType accessType;
accessType.read = true;
accessType.write = true;
accessType.execute = false;
std::string path = createTemporaryFile(__func__, accessType);
ASSERT_FALSE(path.empty());
accessType.read = true;
accessType.write = true;
accessType.execute = true;
std::int64_t res = callAccess(path, accessType);
ASSERT_EQ(unlink(path.c_str()), 0);
ASSERT_NE(res, 0);
}
TEST(AccessTests, TestNotRWX3) {
AccessType accessType;
accessType.read = true;
accessType.write = false;
accessType.execute = true;
std::string path = createTemporaryFile(__func__, accessType);
ASSERT_FALSE(path.empty());
accessType.read = true;
accessType.write = true;
accessType.execute = true;
std::int64_t res = callAccess(path, accessType);
ASSERT_EQ(unlink(path.c_str()), 0);
ASSERT_NE(res, 0);
}
TEST(AccessTests, TestNotRWX4) {
AccessType accessType;
accessType.read = false;
accessType.write = true;
accessType.execute = true;
std::string path = createTemporaryFile(__func__, accessType);
ASSERT_FALSE(path.empty());
accessType.read = true;
accessType.write = true;
accessType.execute = true;
std::int64_t res = callAccess(path, accessType);
ASSERT_EQ(unlink(path.c_str()), 0);
ASSERT_NE(res, 0);
}
#endif