]> git-server-git.apps.pok.os.sepia.ceph.com Git - ceph.git/commitdiff
test: port Ceph tests to Windows
authorLucian Petrut <lpetrut@cloudbasesolutions.com>
Thu, 19 Dec 2019 13:21:49 +0000 (13:21 +0000)
committerLucian Petrut <lpetrut@cloudbasesolutions.com>
Tue, 5 Jan 2021 12:56:11 +0000 (14:56 +0200)
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>
19 files changed:
src/log/test.cc
src/test/admin_socket.cc
src/test/admin_socket_output_tests.cc
src/test/bufferlist.cc
src/test/ceph_crypto.cc
src/test/common/test_hostname.cc
src/test/confutils.cc
src/test/lazy-omap-stats/lazy_omap_stats_test.cc
src/test/lazy-omap-stats/lazy_omap_stats_test.h
src/test/librados/misc.cc
src/test/librados/test_shared.h
src/test/librados_test_stub/TestClassHandler.cc
src/test/librbd/fsx.cc
src/test/system/cross_process_sem.cc
src/test/system/st_rados_create_pool.cc
src/test/system/systest_runnable.cc
src/test/system/systest_runnable.h
src/test/system/systest_settings.cc
src/test/test_subprocess.cc

index 5549e8d048bce98f41ae5b350d7ddccfc8f6c71c..a2169543b162f0af93be53531d63fd1c15e59a4d 100644 (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);
 }
 
index eeea9c20bdbe91477859950092ae7e3f4f857773..859328d9c78c5ea1b388c633610692b8a5ad35f8 100644 (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
index f91a186b834c4289073e8dbe42fb5145119827df..5125a7db271c0f7a8337774b471c39464691322f 100644 (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) {
index 37bacee1c61e53293e309ac26c65f167b79e8aa0..74d23de0bc181a7e45faadd8a26c1416443b44be 100644 (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);
 }
 
index 09c0df37eb90e1d8d248d3882138354ce79a752e..477d0c0db75d06acae6a46d22b805e83f0167e31 100644 (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);
index 4f9aeef4d29da94062ad30e96a6ffabe75b5852d..b0e631d20874021a01cb678b40ac1c9dc4b42bbf 100644 (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
   }
 }
index 88e5bf450ac40fa48f7c2cc91b0891889ad02c54..a46d347d9b105458133f5a1cedc5260e5e3f4611 100644 (file)
 #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();
   }
index dd461429f3003d05a6afacfb6801a6f952fda4f6..87084e941bd667a0cb963577647ce0b9b2ad8543 100644 (file)
@@ -26,6 +26,7 @@
 #include <vector>
 
 #include "lazy_omap_stats_test.h"
+#include "include/compat.h"
 
 using namespace std;
 namespace bp = boost::process;
index 28e194441e1ce647f057a6b3613ae797066a272d..020c72c7358adbfef2fee886917905cc2c9c0892 100644 (file)
@@ -19,6 +19,7 @@
 #include <regex>
 #include <string>
 
+#include "include/compat.h"
 #include "include/rados/librados.hpp"
 
 struct index_t {
index 8d22244d3264703527fad310ba29daf4e68f9d4f..bffab46cbe1f60934db1f2e7d1973d5571e05256 100644 (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 */
index 3c8ce7ed88ffb1e11d1f949a4add1c254ff8e0d7..6f3747e7b41c13cd2154bb6ce90b63ca3411b9e0 100644 (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,
index ff0441f30e05d0cde3db5e14163fc3bb5a7b1902..df830918fb5986914875a212452e7315e5410ef9 100644 (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);
index 1b86e2a74137e9c4b84ed4854e734810533bc558..b8be4708e2d01c9b1c8f9dfb46770c4a11c6ade4 100644 (file)
 #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);
 
index 93f10cbeae68d4e14bf645a8adaa7353e3bfd0a1..7438b882781563db8d0b6f0de17d45fc7880c85b 100644 (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;
 }
 
index 9188fafc6006c340cb5a153c36bd4805234a27ed..a802d9da4f4a0af073924b9008f4c6c2e8407d1c 100644 (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"
index 98cb1741a7768ae7b2edb0b9a2193ac0305c51f2..f7342aa7ded00a5727b6e2ea6d9fd926b5524b63 100644 (file)
 #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
   }
 }
 
index 72da4f4c10479b3f254931fa64ea0c2f390618a3..bfa9130ed7526936e560496f961e53d6e1705272 100644 (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;
index b1d14a259bf6946f48d35fbfd1c6e2db5ce2335d..787d763fedc707dc0b1169006a2a353cd38e8ff9 100644 (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::
index c316eb58a129c9327006de62454ccc58a518baff..f2adf51cb9e8754e9332346e1db5a9c833e730d3 100644 (file)
 #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