From a9bc5800d14f6353e1dce98c5e707c4e856df70d Mon Sep 17 00:00:00 2001 From: Chris Wilson Date: Mon, 14 Jan 2019 22:33:54 +0000 Subject: [PATCH] Move LaunchServer and WaitForServerStartup to lib/server/ServerControl.cpp --- lib/backupstore/StoreTestUtils.cpp | 16 +-- lib/backupstore/StoreTestUtils.h | 5 +- lib/common/BoxException.h | 3 + lib/common/Test.cpp | 127 ------------------- lib/common/Test.h | 3 +- lib/server/ServerControl.cpp | 177 ++++++++++++++++++++++++++- lib/server/ServerControl.h | 9 +- test/basicserver/testbasicserver.cpp | 13 +- 8 files changed, 203 insertions(+), 150 deletions(-) diff --git a/lib/backupstore/StoreTestUtils.cpp b/lib/backupstore/StoreTestUtils.cpp index d64cb3f7a..5cd5caab4 100644 --- a/lib/backupstore/StoreTestUtils.cpp +++ b/lib/backupstore/StoreTestUtils.cpp @@ -266,11 +266,11 @@ bool check_reference_counts() return counts_ok; } -bool StartServer() +bool StartServer(const std::string& daemon_args) { - bbstored_pid = StartDaemon(bbstored_pid, - BBSTORED " " + bbstored_args + " testfiles/bbstored.conf", - "testfiles/bbstored.pid"); + const std::string& daemon_args_final(daemon_args.size() ? daemon_args : bbstored_args); + bbstored_pid = StartDaemon(bbstored_pid, BBSTORED " " + daemon_args_final + + " testfiles/bbstored.conf", "testfiles/bbstored.pid", 22011); return bbstored_pid != 0; } @@ -282,11 +282,11 @@ bool StopServer(bool wait_for_process) return result; } -bool StartClient(const std::string& bbackupd_conf_file) +bool StartClient(const std::string& bbackupd_conf_file, const std::string& daemon_args) { - bbackupd_pid = StartDaemon(bbackupd_pid, - BBACKUPD " " + bbackupd_args + " " + bbackupd_conf_file, - "testfiles/bbackupd.pid"); + const std::string& daemon_args_final(daemon_args.size() ? daemon_args : bbackupd_args); + bbackupd_pid = StartDaemon(bbackupd_pid, BBACKUPD " " + daemon_args_final + " -c " + + bbackupd_conf_file, "testfiles/bbackupd.pid", 0, "testfiles/bbackupd.sock"); return bbackupd_pid != 0; } diff --git a/lib/backupstore/StoreTestUtils.h b/lib/backupstore/StoreTestUtils.h index 762e23992..794fbd44f 100644 --- a/lib/backupstore/StoreTestUtils.h +++ b/lib/backupstore/StoreTestUtils.h @@ -66,13 +66,14 @@ bool run_housekeeping_and_check_account(); bool check_reference_counts(); //! Starts the bbstored test server running, which must not already be running. -bool StartServer(); +bool StartServer(const std::string& daemon_args = ""); //! Stops the currently running bbstored test server. bool StopServer(bool wait_for_process = false); //! Starts the bbackupd client running, which must not already be running. -bool StartClient(const std::string& bbackupd_conf_file = "testfiles/bbackupd.conf"); +bool StartClient(const std::string& bbackupd_conf_file = "testfiles/bbackupd.conf", + const std::string& daemon_args = ""); //! Stops the currently running bbackupd client. bool StopClient(bool wait_for_process = false); diff --git a/lib/common/BoxException.h b/lib/common/BoxException.h index 361f04e89..e3a2268a0 100644 --- a/lib/common/BoxException.h +++ b/lib/common/BoxException.h @@ -34,6 +34,9 @@ class BoxException : public std::exception private: }; +#define EXCEPTION_IS_TYPE(exception_obj, type, subtype) \ + (exception_obj.GetType() == type::ExceptionType && \ + exception_obj.GetSubType() == type::subtype) #endif // BOXEXCEPTION__H diff --git a/lib/common/Test.cpp b/lib/common/Test.cpp index 2c51cd61a..b7b69fd4e 100644 --- a/lib/common/Test.cpp +++ b/lib/common/Test.cpp @@ -382,133 +382,6 @@ int ReadPidFile(const char *pidFile) return pid; } -int LaunchServer(const std::string& rCommandLine, const char *pidFile) -{ - BOX_INFO("Starting server: " << rCommandLine); - -#ifdef WIN32 - - PROCESS_INFORMATION procInfo; - - STARTUPINFO startInfo; - startInfo.cb = sizeof(startInfo); - startInfo.lpReserved = NULL; - startInfo.lpDesktop = NULL; - startInfo.lpTitle = NULL; - startInfo.dwFlags = 0; - startInfo.cbReserved2 = 0; - startInfo.lpReserved2 = NULL; - - std::string cmd = ConvertPaths(rCommandLine); - CHAR* tempCmd = strdup(cmd.c_str()); - - DWORD result = CreateProcess - ( - NULL, // lpApplicationName, naughty! - tempCmd, // lpCommandLine - NULL, // lpProcessAttributes - NULL, // lpThreadAttributes - false, // bInheritHandles - 0, // dwCreationFlags - NULL, // lpEnvironment - NULL, // lpCurrentDirectory - &startInfo, // lpStartupInfo - &procInfo // lpProcessInformation - ); - - free(tempCmd); - - TEST_THAT_OR(result != 0, - BOX_LOG_WIN_ERROR("Launch failed: " << rCommandLine); - return -1; - ); - - CloseHandle(procInfo.hProcess); - CloseHandle(procInfo.hThread); - - return WaitForServerStartup(pidFile, (int)procInfo.dwProcessId); - -#else // !WIN32 - - TEST_THAT_OR(RunCommand(rCommandLine) == 0, - TEST_FAIL_WITH_MESSAGE("Failed to start server: " << rCommandLine); - return -1; - ) - - return WaitForServerStartup(pidFile, 0); - -#endif // WIN32 -} - -int WaitForServerStartup(const char *pidFile, int pidIfKnown) -{ - #ifdef WIN32 - if (pidFile == NULL) - { - return pidIfKnown; - } - #else - // on other platforms there is no other way to get - // the PID, so a NULL pidFile doesn't make sense. - ASSERT(pidFile != NULL); - #endif - - // time for it to start up - BOX_TRACE("Waiting for server to start"); - - for (int i = 0; i < 15; i++) - { - if (TestFileNotEmpty(pidFile)) - { - break; - } - - if (pidIfKnown && !ServerIsAlive(pidIfKnown)) - { - break; - } - - ::sleep(1); - } - - // on Win32 we can check whether the process is alive - // without even checking the PID file - - if (pidIfKnown && !ServerIsAlive(pidIfKnown)) - { - TEST_FAIL_WITH_MESSAGE("Server died!"); - return -1; - } - - if (!TestFileNotEmpty(pidFile)) - { - TEST_FAIL_WITH_MESSAGE("Server didn't save PID file"); - return -1; - } - - BOX_TRACE("Server started"); - - // wait a second for the pid to be written to the file - ::sleep(1); - - // read pid file - int pid = ReadPidFile(pidFile); - - // On Win32 we can check whether the PID in the pidFile matches - // the one returned by the system, which it always should. - - if (pidIfKnown && pid != pidIfKnown) - { - BOX_ERROR("Server wrote wrong pid to file (" << pidFile << - "): expected " << pidIfKnown << " but found " << - pid); - TEST_FAIL_WITH_MESSAGE("Server wrote wrong pid to file"); - return -1; - } - - return pid; -} - void TestRemoteProcessMemLeaksFunc(const char *filename, const char* file, int line) { diff --git a/lib/common/Test.h b/lib/common/Test.h index e3dab62a9..1f6479d89 100644 --- a/lib/common/Test.h +++ b/lib/common/Test.h @@ -219,6 +219,7 @@ int finish_test_suite(); bool TestFileExists(const char *Filename); bool TestDirExists(const char *Filename); +bool TestFileNotEmpty(const char *Filename); // -1 if doesn't exist int TestGetFileSize(const std::string& Filename); @@ -226,8 +227,6 @@ std::string ConvertPaths(const std::string& rOriginal); int RunCommand(const std::string& rCommandLine); bool ServerIsAlive(int pid); int ReadPidFile(const char *pidFile); -int LaunchServer(const std::string& rCommandLine, const char *pidFile); -int WaitForServerStartup(const char *pidFile, int pidIfKnown); #define TestRemoteProcessMemLeaks(filename) \ TestRemoteProcessMemLeaksFunc(filename, __FILE__, __LINE__) diff --git a/lib/server/ServerControl.cpp b/lib/server/ServerControl.cpp index f1a718dff..40a9387bd 100644 --- a/lib/server/ServerControl.cpp +++ b/lib/server/ServerControl.cpp @@ -18,7 +18,9 @@ #include "BoxTime.h" #include "IOStreamGetLine.h" #include "ServerControl.h" +#include "SocketStream.h" #include "Test.h" +#include "autogen_ServerException.h" #ifdef WIN32 @@ -251,11 +253,182 @@ bool KillServer(std::string pid_file, bool WaitForProcess) return status; } -int StartDaemon(int current_pid, const std::string& cmd_line, const char* pid_file) +int LaunchServer(const std::string& rCommandLine, const char *pidFile, int port, + const std::string& socket_path) +{ + BOX_INFO("Starting server: " << rCommandLine); + +#ifdef WIN32 + + // Use a Windows "Job Object" as a container for all our child + // processes. The test runner will create this job object when + // it starts, and close the handle (killing any running daemons) + // when it exits. This is the best way to avoid daemons hanging + // around and causing subsequent tests to fail, and/or the test + // runner to hang waiting for a daemon that will never terminate. + + PROCESS_INFORMATION procInfo; + + STARTUPINFO startInfo; + startInfo.cb = sizeof(startInfo); + startInfo.lpReserved = NULL; + startInfo.lpDesktop = NULL; + startInfo.lpTitle = NULL; + startInfo.dwFlags = 0; + startInfo.cbReserved2 = 0; + startInfo.lpReserved2 = NULL; + + std::string cmd = ConvertPaths(rCommandLine); + CHAR* tempCmd = strdup(cmd.c_str()); + + DWORD result = CreateProcess + ( + NULL, // lpApplicationName, naughty! + tempCmd, // lpCommandLine + NULL, // lpProcessAttributes + NULL, // lpThreadAttributes + false, // bInheritHandles + 0, // dwCreationFlags + NULL, // lpEnvironment + NULL, // lpCurrentDirectory + &startInfo, // lpStartupInfo + &procInfo // lpProcessInformation + ); + + free(tempCmd); + + TEST_THAT_OR(result != 0, + BOX_LOG_WIN_ERROR("Failed to CreateProcess: " << rCommandLine); + return -1; + ); + + if(sTestChildDaemonJobObject != INVALID_HANDLE_VALUE) + { + if(!AssignProcessToJobObject(sTestChildDaemonJobObject, procInfo.hProcess)) + { + BOX_LOG_WIN_WARNING("Failed to add child process to job object"); + } + } + + CloseHandle(procInfo.hProcess); + CloseHandle(procInfo.hThread); + + return WaitForServerStartup(pidFile, (int)procInfo.dwProcessId, port, socket_path); + +#else // !WIN32 + + TEST_THAT_OR(RunCommand(rCommandLine) == 0, + TEST_FAIL_WITH_MESSAGE("Failed to start server: " << rCommandLine); + return -1; + ) + + return WaitForServerStartup(pidFile, 0, port, socket_path); + +#endif // WIN32 +} + +int WaitForServerStartup(const char *pidFile, int pidIfKnown, int port, + const std::string& socket_path) +{ +#ifdef WIN32 + if(pidFile == NULL && port == 0 && socket_path == "") + { + return pidIfKnown; + } +#else + // On other platforms there is no other way to get the PID, so a NULL pidFile doesn't + // make sense. + ASSERT(pidFile != NULL); +#endif + + // time for it to start up + BOX_TRACE("Waiting for server to start"); + + for (int i = 150; i >= 0; i--) + { + if(i == 0) + { + // ran out of time waiting + TEST_FAIL_WITH_MESSAGE("Server didn't start within expected time"); + return -1; + } + + ShortSleep(MilliSecondsToBoxTime(100), false); + + if(!TestFileNotEmpty(pidFile)) + { + // Hasn't written a complete PID file yet, go round again + continue; + } + + // Once we know what PID the process has/had, we can check if it has died during or + // shortly after startup: + if (pidIfKnown && !ServerIsAlive(pidIfKnown)) + { + TEST_FAIL_WITH_MESSAGE("Server died!"); + return -1; + } + + if(port != 0 || socket_path != "") + { + try + { + if(port != 0) + { + SocketStream conn; + conn.Open(Socket::TypeINET, "localhost", port); + } + + if(socket_path != "") + { + SocketStream conn; + conn.Open(Socket::TypeUNIX, socket_path); + } + } + catch(ServerException &e) + { + if(EXCEPTION_IS_TYPE(e, ServerException, SocketOpenError)) + { + // not listening on port, go round again + continue; + } + else + { + // something bad happened, break + throw; + } + } + } + + // All tests that we can do have passed, looks good! + break; + } + + BOX_TRACE("Server started"); + + // read pid file + int pid = ReadPidFile(pidFile); + + // On Win32 we can check whether the PID in the pidFile matches + // the one returned by the system, which it always should. + if (pidIfKnown && pid != pidIfKnown) + { + BOX_ERROR("Server wrote wrong pid to file (" << pidFile << + "): expected " << pidIfKnown << " but found " << + pid); + TEST_FAIL_WITH_MESSAGE("Server wrote wrong pid to file"); + return -1; + } + + return pid; +} + +int StartDaemon(int current_pid, const std::string& cmd_line, const char* pid_file, int port, + const std::string& socket_path) { TEST_THAT_OR(current_pid == 0, return 0); - int new_pid = LaunchServer(cmd_line, pid_file); + int new_pid = LaunchServer(cmd_line, pid_file, port, socket_path); TEST_THAT_OR(new_pid != -1 && new_pid != 0, return 0); ::sleep(1); diff --git a/lib/server/ServerControl.h b/lib/server/ServerControl.h index be2464c19..2a0293324 100644 --- a/lib/server/ServerControl.h +++ b/lib/server/ServerControl.h @@ -5,10 +5,15 @@ bool HUPServer(int pid); bool KillServer(int pid, bool WaitForProcess = false); -bool KillServer(std::string pid_file, bool WaitForProcess = false); -int StartDaemon(int current_pid, const std::string& cmd_line, const char* pid_file); +bool KillServer(const std::string& pid_file, bool WaitForProcess = false); +int StartDaemon(int current_pid, const std::string& cmd_line, const char* pid_file, int port = 0, + const std::string& socket_path = ""); bool StopDaemon(int current_pid, const std::string& pid_file, const std::string& memleaks_file, bool wait_for_process); +int LaunchServer(const std::string& rCommandLine, const char *pidFile, int port = 0, + const std::string& socket_path = ""); +int WaitForServerStartup(const char *pidFile, int pidIfKnown, int port = 0, + const std::string& socket_path = ""); #ifdef WIN32 #include "WinNamedPipeStream.h" diff --git a/test/basicserver/testbasicserver.cpp b/test/basicserver/testbasicserver.cpp index 6f2def54a..afb1132db 100644 --- a/test/basicserver/testbasicserver.cpp +++ b/test/basicserver/testbasicserver.cpp @@ -15,18 +15,17 @@ #include -#include "Test.h" -#include "Daemon.h" +#include "CollectInBufferStream.h" #include "Configuration.h" -#include "ServerStream.h" -#include "SocketStream.h" +#include "Daemon.h" #include "IOStreamGetLine.h" +#include "ServerControl.h" +#include "ServerStream.h" #include "ServerTLS.h" -#include "CollectInBufferStream.h" - +#include "SocketStream.h" +#include "Test.h" #include "TestContext.h" #include "autogen_TestProtocol.h" -#include "ServerControl.h" #include "MemLeakFindOn.h"