test: port Ceph tests to Windows

We're porting to Windows some of the Ceph tests, mostly related to rados
and rbd.

Overall, this is about using compat.h functions and types as well as avoiding
unsupported functionality.

The systest framework will have to be limitted to threads instead of separate
processes until we cover the process spawning and communication.

Signed-off-by: Lucian Petrut <lpetrut@cloudbasesolutions.com>
This commit is contained in:
Lucian Petrut 2019-12-19 13:21:49 +00:00
parent 2fa311a462
commit 9abf2388f2
19 changed files with 145 additions and 32 deletions

View File

@ -362,7 +362,7 @@ TEST(Log, GarbleRecovery)
log.flush();
log.stop();
struct stat file_status;
ASSERT_EQ(lstat(test_file, &file_status), 0);
ASSERT_EQ(stat(test_file, &file_status), 0);
ASSERT_GT(file_status.st_size, 2000);
}

View File

@ -225,20 +225,29 @@ TEST(AdminSocketClient, Ping) {
{
bool ok;
std::string result = client.ping(&ok);
#ifndef _WIN32
// TODO: convert WSA errors.
EXPECT_NE(std::string::npos, result.find("No such file or directory"));
#endif
ASSERT_FALSE(ok);
}
// file exists but does not allow connections (no process, wrong type...)
ASSERT_TRUE(::creat(path.c_str(), 0777));
int fd = ::creat(path.c_str(), 0777);
ASSERT_TRUE(fd);
// On Windows, we won't be able to remove the file unless we close it
// first.
ASSERT_FALSE(::close(fd));
{
bool ok;
std::string result = client.ping(&ok);
#ifndef _WIN32
#if defined(__APPLE__) || defined(__FreeBSD__)
const char* errmsg = "Socket operation on non-socket";
#else
const char* errmsg = "Connection refused";
#endif
EXPECT_NE(std::string::npos, result.find(errmsg));
#endif /* _WIN32 */
ASSERT_FALSE(ok);
}
// a daemon is connected to the socket
@ -259,7 +268,9 @@ TEST(AdminSocketClient, Ping) {
ASSERT_TRUE(asoct.init(path));
bool ok;
std::string result = client.ping(&ok);
#ifndef _WIN32
EXPECT_NE(std::string::npos, result.find("Resource temporarily unavailable"));
#endif
ASSERT_FALSE(ok);
{
std::lock_guard l{blocking->_lock};
@ -282,18 +293,22 @@ TEST(AdminSocket, bind_and_listen) {
message = asoct.bind_and_listen(path, &fd);
ASSERT_NE(0, fd);
ASSERT_EQ("", message);
ASSERT_EQ(0, ::close(fd));
ASSERT_EQ(0, ::compat_closesocket(fd));
ASSERT_EQ(0, ::unlink(path.c_str()));
}
// silently discard an existing file
{
int fd = 0;
string message;
ASSERT_TRUE(::creat(path.c_str(), 0777));
int fd2 = ::creat(path.c_str(), 0777);
ASSERT_TRUE(fd2);
// On Windows, we won't be able to remove the file unless we close it
// first.
ASSERT_FALSE(::close(fd2));
message = asoct.bind_and_listen(path, &fd);
ASSERT_NE(0, fd);
ASSERT_EQ("", message);
ASSERT_EQ(0, ::close(fd));
ASSERT_EQ(0, ::compat_closesocket(fd));
ASSERT_EQ(0, ::unlink(path.c_str()));
}
// do not take over a live socket

View File

@ -42,7 +42,7 @@ bool test_dump_pgstate_history(std::string &output) {
return false;
}
uint total = 0;
unsigned int total = 0;
if ((*iterone)->get_name() == "pgs") {
JSONObjIter iter = (*(*iterone)->find_first())->find_first();
for (; !iter.end(); ++iter) {

View File

@ -25,6 +25,7 @@
#include "include/buffer.h"
#include "include/buffer_raw.h"
#include "include/compat.h"
#include "include/utime.h"
#include "include/coredumpctl.h"
#include "include/encoding.h"
@ -2298,13 +2299,17 @@ TEST(BufferList, read_file) {
bufferlist bl;
::unlink(FILENAME);
EXPECT_EQ(-ENOENT, bl.read_file("UNLIKELY", &error));
snprintf(cmd, sizeof(cmd), "echo ABC > %s ; chmod 0 %s", FILENAME, FILENAME);
snprintf(cmd, sizeof(cmd), "echo ABC> %s", FILENAME);
EXPECT_EQ(0, ::system(cmd));
#ifndef _WIN32
snprintf(cmd, sizeof(cmd), "chmod 0 %s", FILENAME);
EXPECT_EQ(0, ::system(cmd));
if (getuid() != 0) {
EXPECT_EQ(-EACCES, bl.read_file(FILENAME, &error));
}
snprintf(cmd, sizeof(cmd), "chmod +r %s", FILENAME);
EXPECT_EQ(0, ::system(cmd));
#endif /* _WIN32 */
EXPECT_EQ(0, bl.read_file(FILENAME, &error));
::unlink(FILENAME);
EXPECT_EQ((unsigned)4, bl.length());
@ -2339,7 +2344,9 @@ TEST(BufferList, write_file) {
struct stat st;
memset(&st, 0, sizeof(st));
ASSERT_EQ(0, ::stat(FILENAME, &st));
#ifndef _WIN32
EXPECT_EQ((unsigned)(mode | S_IFREG), st.st_mode);
#endif
::unlink(FILENAME);
}

View File

@ -268,11 +268,11 @@ void do_simple_crypto() {
exit(0);
}
#if GTEST_HAS_DEATH_TEST
#if GTEST_HAS_DEATH_TEST && !defined(_WIN32)
TEST_F(ForkDeathTest, MD5) {
ASSERT_EXIT(do_simple_crypto(), ::testing::ExitedWithCode(0), "^$");
}
#endif //GTEST_HAS_DEATH_TEST
#endif // GTEST_HAS_DEATH_TEST && !defined(_WIN32)
int main(int argc, char **argv) {
std::vector<const char*> args(argv, argv + argc);

View File

@ -62,6 +62,10 @@ TEST(Hostname, short) {
<< ", skipping test because env var may or may not be short form"
<< std::endl;
} else {
ASSERT_EQ(shn, exec("hostname -s")) ;
#ifdef _WIN32
ASSERT_EQ(shn, exec("hostname"));
#else
ASSERT_EQ(shn, exec("hostname -s"));
#endif
}
}

View File

@ -17,6 +17,14 @@
#include "gtest/gtest.h"
#include "include/buffer.h"
#if __has_include(<filesystem>)
#include <filesystem>
namespace fs = std::filesystem;
#elif __has_include(<experimental/filesystem>)
#include <experimental/filesystem>
namespace fs = std::experimental::filesystem;
#endif
#include <errno.h>
#include <iostream>
#include <stdlib.h>
@ -47,10 +55,14 @@ static std::string get_temp_dir()
ostringstream oss;
oss << tmpdir << "/confutils_test_dir." << rand() << "." << getpid();
umask(022);
int res = mkdir(oss.str().c_str(), 01777);
if (res) {
cerr << "failed to create temp directory '" << temp_dir << "'" << std::endl;
return "";
if (!fs::exists(oss.str())) {
std::error_code ec;
if (!fs::create_directory(oss.str(), ec)) {
cerr << "failed to create temp directory '" << temp_dir << "' "
<< ec.message() << std::endl;
return "";
}
fs::permissions(oss.str(), fs::perms::sticky_bit | fs::perms::all);
}
temp_dir = oss.str();
}

View File

@ -26,6 +26,7 @@
#include <vector>
#include "lazy_omap_stats_test.h"
#include "include/compat.h"
using namespace std;
namespace bp = boost::process;

View File

@ -19,6 +19,7 @@
#include <regex>
#include <string>
#include "include/compat.h"
#include "include/rados/librados.hpp"
struct index_t {

View File

@ -17,7 +17,9 @@
#include "test/librados/TestCase.h"
#include "gtest/gtest.h"
#include <sys/time.h>
#ifndef _WIN32
#include <sys/resource.h>
#endif
#include <errno.h>
#include <map>
@ -328,6 +330,7 @@ static void shutdown_racer_func()
}
}
#ifndef _WIN32
// See trackers #20988 and #42026
TEST_F(LibRadosMisc, ShutdownRace)
{
@ -348,3 +351,4 @@ TEST_F(LibRadosMisc, ShutdownRace)
threads[i].join();
ASSERT_EQ(setrlimit(RLIMIT_NOFILE, &rold), 0);
}
#endif /* _WIN32 */

View File

@ -19,12 +19,21 @@ void assert_eq_sparse(ceph::bufferlist& expected,
class TestAlarm
{
public:
#ifndef _WIN32
TestAlarm() {
alarm(1200);
}
~TestAlarm() {
alarm(0);
}
#else
// TODO: add a timeout mechanism for Windows as well, possibly by using
// CreateTimerQueueTimer.
TestAlarm() {
}
~TestAlarm() {
}
#endif
};
template<class Rep, class Period, typename Func, typename... Args,

View File

@ -4,12 +4,12 @@
#include "test/librados_test_stub/TestClassHandler.h"
#include "test/librados_test_stub/TestIoCtxImpl.h"
#include <boost/algorithm/string/predicate.hpp>
#include <dlfcn.h>
#include <errno.h>
#include <stdlib.h>
#include <string.h>
#include "common/debug.h"
#include "include/ceph_assert.h"
#include "include/dlfcn_compat.h"
#define dout_context g_ceph_context
#define dout_subsys ceph_subsys_rados
@ -35,16 +35,12 @@ void TestClassHandler::open_class(const std::string& name,
return;
}
// clear any existing error
dlerror();
// initialize
void (*cls_init)() = reinterpret_cast<void (*)()>(
dlsym(handle, "__cls_init"));
char* error = nullptr;
if ((error = dlerror()) != nullptr) {
std::cerr << "Error locating initializer: " << error << std::endl;
if (!cls_init) {
std::cerr << "Error locating initializer: " << dlerror() << std::endl;
} else if (cls_init) {
m_class_handles.push_back(handle);
cls_init();
@ -71,7 +67,7 @@ void TestClassHandler::open_all_classes() {
while ((pde = ::readdir(dir))) {
std::string name(pde->d_name);
if (!boost::algorithm::starts_with(name, "libcls_") ||
!boost::algorithm::ends_with(name, ".so")) {
!boost::algorithm::ends_with(name, SHARED_LIB_SUFFIX)) {
continue;
}
names.insert(name);

View File

@ -25,11 +25,13 @@
#endif
#include <sys/file.h>
#include <sys/stat.h>
#ifndef _WIN32
#include <sys/mman.h>
#include <sys/ioctl.h>
#endif
#if defined(__linux__)
#include <linux/fs.h>
#endif
#include <sys/ioctl.h>
#ifdef HAVE_ERR_H
#include <err.h>
#endif
@ -132,7 +134,7 @@ int logcount = 0; /* total ops */
#define OP_SKIPPED 101
#undef PAGE_SIZE
#define PAGE_SIZE getpagesize()
#define PAGE_SIZE get_page_size()
#undef PAGE_MASK
#define PAGE_MASK (PAGE_SIZE - 1)
@ -3070,7 +3072,7 @@ main(int argc, char **argv)
goodfile[0] = 0;
logfile[0] = 0;
page_size = getpagesize();
page_size = PAGE_SIZE;
page_mask = page_size - 1;
mmap_mask = page_mask;
@ -3271,7 +3273,9 @@ main(int argc, char **argv)
fprintf(stdout, "mapped writes DISABLED\n");
break;
case 'Z':
#ifdef O_DIRECT
o_direct = O_DIRECT;
#endif
break;
default:
usage();
@ -3285,6 +3289,7 @@ main(int argc, char **argv)
pool = argv[0];
iname = argv[1];
#ifndef _WIN32
signal(SIGHUP, cleanup);
signal(SIGINT, cleanup);
signal(SIGPIPE, cleanup);
@ -3295,6 +3300,7 @@ main(int argc, char **argv)
signal(SIGVTALRM, cleanup);
signal(SIGUSR1, cleanup);
signal(SIGUSR2, cleanup);
#endif
random_generator.seed(seed);

View File

@ -17,7 +17,9 @@
#include <errno.h>
#include <semaphore.h>
#include <stdlib.h>
#ifndef _WIN32
#include <sys/mman.h>
#endif
#include "include/ceph_assert.h"
@ -36,6 +38,7 @@ struct cross_process_sem_data_t
int CrossProcessSem::
create(int initial_val, CrossProcessSem** res)
{
#ifndef _WIN32
struct cross_process_sem_data_t *data = static_cast < cross_process_sem_data_t*> (
mmap(NULL, sizeof(struct cross_process_sem_data_t),
PROT_READ|PROT_WRITE, MAP_SHARED|MAP_ANONYMOUS, -1, 0));
@ -43,6 +46,11 @@ create(int initial_val, CrossProcessSem** res)
int err = errno;
return err;
}
#else
// We can't use multiple processes on Windows for the time being.
struct cross_process_sem_data_t *data = (cross_process_sem_data_t*)malloc(
sizeof(cross_process_sem_data_t));
#endif /* _WIN32 */
int ret = sem_init(&data->sem, 1, initial_val);
if (ret) {
return ret;
@ -54,7 +62,11 @@ create(int initial_val, CrossProcessSem** res)
CrossProcessSem::
~CrossProcessSem()
{
#ifndef _WIN32
munmap(m_data, sizeof(struct cross_process_sem_data_t));
#else
free(m_data);
#endif
m_data = NULL;
}

View File

@ -13,6 +13,7 @@
*/
#include "cross_process_sem.h"
#include "include/ceph_assert.h"
#include "include/rados/librados.h"
#include "st_rados_create_pool.h"
#include "systest_runnable.h"

View File

@ -25,9 +25,11 @@
#include <stdlib.h>
#include <string.h>
#include <string>
#ifndef _WIN32
#include <sys/syscall.h>
#include <sys/types.h>
#include <sys/wait.h>
#endif
#include <sys/types.h>
#include <unistd.h>
#include <atomic>
#include <limits>
@ -40,6 +42,8 @@ static pid_t do_gettid(void)
{
#if defined(__linux__)
return static_cast < pid_t >(syscall(SYS_gettid));
#elif defined(_WIN32)
return static_cast < pid_t >(GetCurrentThreadId());
#else
return static_cast < pid_t >(pthread_getthreadid_np());
#endif
@ -87,6 +91,10 @@ start()
return ret;
m_started = true;
} else {
#ifdef _WIN32
printf("Using separate processes is not supported on Windows.\n");
return -1;
#else
std::string err_msg;
ret = preforker.prefork(err_msg);
if (ret < 0)
@ -99,6 +107,7 @@ start()
} else {
m_started = true;
}
#endif
}
return 0;
}
@ -127,9 +136,13 @@ join()
}
return "";
} else {
#ifdef _WIN32
return "Using separate processes is not supported on Windows.\n";
#else
std::string err_msg;
ret = preforker.parent_wait(err_msg);
return err_msg;
#endif
}
}

View File

@ -20,7 +20,9 @@
#include <string>
#include <vector>
#ifndef _WIN32
#include "common/Preforker.h"
#endif
#define RETURN1_IF_NOT_VAL(expected, expr) \
do {\
@ -79,7 +81,9 @@ private:
friend void* systest_runnable_pthread_helper(void *arg);
#ifndef _WIN32
Preforker preforker;
#endif
const char **m_argv_orig;
bool m_started;
int m_id;

View File

@ -33,7 +33,14 @@ inst()
bool SysTestSettings::
use_threads() const
{
#ifdef _WIN32
// We can't use multiple processes on Windows for the time being.
// We'd need a mechanism for spawning those procecesses and also handle
// the inter-process communication.
return true;
#else
return m_use_threads;
#endif
}
std::string SysTestSettings::

View File

@ -23,6 +23,14 @@
#include "gtest/gtest.h"
#include "common/fork_function.h"
#ifdef _WIN32
// Some of the tests expect GNU binaries to be available. We'll just rely on
// the ones provided by Msys (which also comes with Git for Windows).
#define SHELL "bash.exe"
#else
#define SHELL "/bin/sh"
#endif
bool read_from_fd(int fd, std::string &out) {
out.clear();
char buf[1024];
@ -53,6 +61,10 @@ TEST(SubProcess, False)
TEST(SubProcess, NotFound)
{
SubProcess p("NOTEXISTENTBINARY", SubProcess::CLOSE, SubProcess::CLOSE, SubProcess::PIPE);
#ifdef _WIN32
// Windows will error out early.
ASSERT_EQ(p.spawn(), -1);
#else
ASSERT_EQ(p.spawn(), 0);
std::string buf;
ASSERT_TRUE(read_from_fd(p.get_stderr(), buf));
@ -60,6 +72,7 @@ TEST(SubProcess, NotFound)
ASSERT_EQ(p.join(), 1);
std::cerr << "err: " << p.err() << std::endl;
ASSERT_FALSE(p.err().c_str()[0] == '\0');
#endif
}
TEST(SubProcess, Echo)
@ -121,6 +134,7 @@ TEST(SubProcess, Killed)
ASSERT_FALSE(cat.err().c_str()[0] == '\0');
}
#ifndef _WIN32
TEST(SubProcess, CatWithArgs)
{
SubProcess cat("cat", SubProcess::PIPE, SubProcess::PIPE, SubProcess::PIPE);
@ -142,15 +156,16 @@ TEST(SubProcess, CatWithArgs)
std::cerr << "err: " << cat.err() << std::endl;
ASSERT_FALSE(cat.err().c_str()[0] == '\0');
}
#endif
TEST(SubProcess, Subshell)
{
SubProcess sh("/bin/sh", SubProcess::PIPE, SubProcess::PIPE, SubProcess::PIPE);
SubProcess sh(SHELL, SubProcess::PIPE, SubProcess::PIPE, SubProcess::PIPE);
sh.add_cmd_args("-c",
"sleep 0; "
"cat; "
"echo 'error from subshell' >&2; "
"/bin/sh -c 'exit 13'", NULL);
SHELL " -c 'exit 13'", NULL);
ASSERT_EQ(sh.spawn(), 0);
std::string msg("hello via subshell");
int n = write(sh.get_stdin(), msg.c_str(), msg.size());
@ -210,8 +225,10 @@ TEST(SubProcessTimed, SleepTimedout)
ASSERT_EQ(sleep.spawn(), 0);
std::string buf;
ASSERT_TRUE(read_from_fd(sleep.get_stderr(), buf));
#ifndef _WIN32
std::cerr << "stderr: " << buf;
ASSERT_FALSE(buf.empty());
#endif
ASSERT_EQ(sleep.join(), 128 + SIGKILL);
std::cerr << "err: " << sleep.err() << std::endl;
ASSERT_FALSE(sleep.err().c_str()[0] == '\0');
@ -219,7 +236,7 @@ TEST(SubProcessTimed, SleepTimedout)
TEST(SubProcessTimed, SubshellNoTimeout)
{
SubProcessTimed sh("/bin/sh", SubProcess::PIPE, SubProcess::PIPE, SubProcess::PIPE, 0);
SubProcessTimed sh(SHELL, SubProcess::PIPE, SubProcess::PIPE, SubProcess::PIPE, 0);
sh.add_cmd_args("-c", "cat >&2", NULL);
ASSERT_EQ(sh.spawn(), 0);
std::string msg("the quick brown fox jumps over the lazy dog");
@ -239,8 +256,8 @@ TEST(SubProcessTimed, SubshellNoTimeout)
TEST(SubProcessTimed, SubshellKilled)
{
SubProcessTimed sh("/bin/sh", SubProcess::PIPE, SubProcess::PIPE, SubProcess::PIPE, 10);
sh.add_cmd_args("-c", "sh -c cat", NULL);
SubProcessTimed sh(SHELL, SubProcess::PIPE, SubProcess::PIPE, SubProcess::PIPE, 10);
sh.add_cmd_args("-c", SHELL "-c cat", NULL);
ASSERT_EQ(sh.spawn(), 0);
std::string msg("etaoin shrdlu");
int n = write(sh.get_stdin(), msg.c_str(), msg.size());
@ -256,18 +273,21 @@ TEST(SubProcessTimed, SubshellKilled)
TEST(SubProcessTimed, SubshellTimedout)
{
SubProcessTimed sh("/bin/sh", SubProcess::PIPE, SubProcess::PIPE, SubProcess::PIPE, 1, SIGTERM);
SubProcessTimed sh(SHELL, SubProcess::PIPE, SubProcess::PIPE, SubProcess::PIPE, 1, SIGTERM);
sh.add_cmd_args("-c", "sleep 1000& cat; NEVER REACHED", NULL);
ASSERT_EQ(sh.spawn(), 0);
std::string buf;
#ifndef _WIN32
ASSERT_TRUE(read_from_fd(sh.get_stderr(), buf));
std::cerr << "stderr: " << buf;
ASSERT_FALSE(buf.empty());
#endif
ASSERT_EQ(sh.join(), 128 + SIGTERM);
std::cerr << "err: " << sh.err() << std::endl;
ASSERT_FALSE(sh.err().c_str()[0] == '\0');
}
#ifndef _WIN32
TEST(fork_function, normal)
{
ASSERT_EQ(0, fork_function(10, std::cerr, [&]() { return 0; }));
@ -291,3 +311,4 @@ TEST(fork_function, timeout)
sleep(60);
return -111; }));
}
#endif