blob: ea7de55943e935cef0fc51517db525b7c38b1f9e [file] [log] [blame]
/*
* Copyright (C) 2016 The Android Open Source Project
*
*
* This software is licensed under the terms of the GNU General Public
* License version 2, as published by the Free Software Foundation, and
* may be copied, distributed, and modified under those terms.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
*/
#include <libgen.h>
#include <unistd.h>
#include <sys/stat.h>
#include <sys/types.h>
#include <gtest/gtest.h>
#include "RecursiveTemporaryDir.h"
static bool fileExists(const char *path) {
struct stat s;
return stat(path, &s) == 0;
}
class LtpTestcase : public ::testing::Test {
protected:
LtpTestcase() {}
virtual ~LtpTestcase() {}
void runTest(const std::string &testexe,
std::vector<std::string> extraArgs = {}) {
// Some tests (e.g. access02) drop to a lower-privileged user like
// nobody. Make sure they have permission to poke at the sandbox we've
// set up for them.
int err = chmod(tmpdir.path, S_IRWXU | S_IRWXG);
if (err < 0) {
perror("chmod() failed");
exit(1);
}
pid_t pid = fork();
ASSERT_NE(-1, pid);
if (pid == 0) {
runChild(testexe, extraArgs);
} else {
int status;
pid_t waited = waitpid(pid, &status, 0);
ASSERT_NE(-1, waited);
int exitStatus = WEXITSTATUS(status);
if (exitStatus == TCONF) {
GTEST_LOG_(WARNING) << "testcase skipped" << std::endl;
return;
}
EXPECT_EQ(TPASS, exitStatus);
}
}
std::string executableDirectory() {
char selfpath[PATH_MAX]{0};
ssize_t err = readlink("/proc/self/exe", selfpath, sizeof(selfpath));
if (err < 0) {
perror("realpath() failed");
return "";
}
return dirname(selfpath);
}
const RecursiveTemporaryDir tmpdir;
private:
void runChild(std::string testexe, std::vector<std::string> &extraArgs) {
auto err = chdir(tmpdir.path);
if (err < 0) {
perror("chdir() failed");
exit(1);
}
auto ltpRoot = executableDirectory();
if (ltpRoot == "")
exit(1);
auto binDir = ltpRoot + "/testcases/bin";
char cmdline[PATH_MAX];
snprintf(cmdline, sizeof(cmdline), "%s/%s", binDir.c_str(),
testexe.c_str());
if (!fileExists(cmdline)) {
/* Some test names have underscores in place of hyphens, since C++
* doesn't allow hyphens in class names
*/
std::replace(testexe.begin(), testexe.end(), '_', '-');
snprintf(cmdline, sizeof(cmdline), "%s/%s", binDir.c_str(),
testexe.c_str());
}
std::vector<char *> argv;
argv.push_back(cmdline);
for (auto &arg : extraArgs) {
while (true) {
auto ltpRootVar = arg.find("$LTPROOT");
if (ltpRootVar == std::string::npos)
break;
arg.replace(ltpRootVar, strlen("$LTPROOT"), ltpRoot);
}
argv.push_back(&arg[0]);
}
argv.push_back(nullptr);
char tmpdir_envp[PATH_MAX];
snprintf(tmpdir_envp, sizeof(tmpdir_envp), "TMPDIR=%s", tmpdir.path);
char tmp_envp[PATH_MAX];
snprintf(tmp_envp, sizeof(tmp_envp), "TMP=%s", tmpdir.path);
char ltproot_envp[PATH_MAX];
snprintf(ltproot_envp, sizeof(ltproot_envp), "LTPROOT=%s",
ltpRoot.c_str());
char path_envp[PATH_MAX];
snprintf(path_envp, sizeof(path_envp), "PATH=/system/bin:%s",
binDir.c_str());
char fs_type_envp[PATH_MAX];
snprintf(fs_type_envp, sizeof(fs_type_envp), "LTP_DEV_FS_TYPE=ext4");
char *envp[] = { tmpdir_envp, tmp_envp, ltproot_envp, path_envp, fs_type_envp, nullptr };
execve(cmdline, &argv[0], envp);
perror("execve() failed");
exit(1);
}
const int TPASS = 0;
const int TCONF = 32;
};
#define LTP_TESTSUITE(testsuite) \
class testsuite : public LtpTestcase { }
#define LTP_TESTCASE(testsuite, testname, testexe, ...) \
TEST_F(testsuite, testname) { \
ASSERT_NO_FATAL_FAILURE(runTest(#testexe, ##__VA_ARGS__)); \
}
#include "ltp-testcases.h"