diff options
Diffstat (limited to 'lib/common/Test.h')
-rw-r--r-- | lib/common/Test.h | 318 |
1 files changed, 318 insertions, 0 deletions
diff --git a/lib/common/Test.h b/lib/common/Test.h new file mode 100644 index 00000000..0cdcabb2 --- /dev/null +++ b/lib/common/Test.h @@ -0,0 +1,318 @@ +// distribution boxbackup-0.10 (svn version: 494) +// +// Copyright (c) 2003 - 2006 +// Ben Summers and contributors. All rights reserved. +// +// Redistribution and use in source and binary forms, with or without +// modification, are permitted provided that the following conditions +// are met: +// 1. Redistributions of source code must retain the above copyright +// notice, this list of conditions and the following disclaimer. +// 2. Redistributions in binary form must reproduce the above copyright +// notice, this list of conditions and the following disclaimer in the +// documentation and/or other materials provided with the distribution. +// 3. All use of this software and associated advertising materials must +// display the following acknowledgment: +// This product includes software developed by Ben Summers. +// 4. The names of the Authors may not be used to endorse or promote +// products derived from this software without specific prior written +// permission. +// +// [Where legally impermissible the Authors do not disclaim liability for +// direct physical injury or death caused solely by defects in the software +// unless it is modified by a third party.] +// +// THIS SOFTWARE IS PROVIDED BY THE AUTHORS ``AS IS'' AND ANY EXPRESS OR +// IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED +// WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE +// DISCLAIMED. IN NO EVENT SHALL THE AUTHORS BE LIABLE FOR ANY DIRECT, +// INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES +// (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR +// SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) +// HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, +// STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN +// ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE +// POSSIBILITY OF SUCH DAMAGE. +// +// +// +// -------------------------------------------------------------------------- +// +// File +// Name: Test.h +// Purpose: Useful stuff for tests +// Created: 2003/07/11 +// +// -------------------------------------------------------------------------- + +#ifndef TEST__H +#define TEST__H + +#include <sys/types.h> +#include <sys/stat.h> +#include <stdlib.h> +#include <signal.h> + +#ifdef HAVE_UNISTD_H + #include <unistd.h> +#endif + +#include <stdio.h> + +extern int failures; + +#define TEST_FAIL_WITH_MESSAGE(msg) {failures++; printf("FAILURE: " msg " at " __FILE__ "(%d)\n", __LINE__);} +#define TEST_ABORT_WITH_MESSAGE(msg) {failures++; printf("FAILURE: " msg " at " __FILE__ "(%d)\n", __LINE__); return 1;} + +#define TEST_THAT(condition) {if(!(condition)) TEST_FAIL_WITH_MESSAGE("Condition [" #condition "] failed")} +#define TEST_THAT_ABORTONFAIL(condition) {if(!(condition)) TEST_ABORT_WITH_MESSAGE("Condition [" #condition "] failed")} + +// NOTE: The 0- bit it to allow this to work with stuff which has negative constants for flags (eg ConnectionException) +#define TEST_CHECK_THROWS(statement, excepttype, subtype) \ + { \ + bool didthrow = false; \ + try \ + { \ + statement; \ + } \ + catch(excepttype &e) \ + { \ + if(e.GetSubType() != ((unsigned int)excepttype::subtype) \ + && e.GetSubType() != (unsigned int)(0-excepttype::subtype)) \ + { \ + throw; \ + } \ + didthrow = true; \ + } \ + catch(...) \ + { \ + throw; \ + } \ + if(!didthrow) \ + { \ + TEST_FAIL_WITH_MESSAGE("Didn't throw exception " #excepttype "(" #subtype ")") \ + } \ + } + +inline bool TestFileExists(const char *Filename) +{ + struct stat st; + return ::stat(Filename, &st) == 0 && (st.st_mode & S_IFDIR) == 0; +} + +inline bool TestDirExists(const char *Filename) +{ + struct stat st; + return ::stat(Filename, &st) == 0 && (st.st_mode & S_IFDIR) == S_IFDIR; +} + +// -1 if doesn't exist +inline int TestGetFileSize(const char *Filename) +{ + struct stat st; + if(::stat(Filename, &st) == 0) + { + return st.st_size; + } + return -1; +} + +inline int LaunchServer(const char *CommandLine, const char *pidFile) +{ + if(::system(CommandLine) != 0) + { + printf("Server: %s\n", CommandLine); + TEST_FAIL_WITH_MESSAGE("Couldn't start server"); + return -1; + } + // time for it to start up + ::sleep(1); + + // read pid file + if(!TestFileExists(pidFile)) + { + printf("Server: %s\n", CommandLine); + TEST_FAIL_WITH_MESSAGE("Server didn't save PID file"); + return -1; + } + + FILE *f = fopen(pidFile, "r"); + int pid = -1; + if(f == NULL || fscanf(f, "%d", &pid) != 1) + { + printf("Server: %s (pidfile %s)\n", CommandLine, pidFile); + TEST_FAIL_WITH_MESSAGE("Couldn't read PID file"); + return -1; + } + fclose(f); + + return pid; +} + +#ifdef WIN32 + +#include "WinNamedPipeStream.h" +#include "IOStreamGetLine.h" +#include "BoxPortsAndFiles.h" + +bool SendCommands(const std::string& rCmd) +{ + WinNamedPipeStream connection; + + try + { + connection.Connect(BOX_NAMED_PIPE_NAME); + } + catch(...) + { + printf("Failed to connect to daemon control socket.\n"); + return false; + } + + // For receiving data + IOStreamGetLine getLine(connection); + + // Wait for the configuration summary + std::string configSummary; + if(!getLine.GetLine(configSummary)) + { + printf("Failed to receive configuration summary from daemon\n"); + return false; + } + + // Was the connection rejected by the server? + if(getLine.IsEOF()) + { + printf("Server rejected the connection.\n"); + return false; + } + + // Decode it + int autoBackup, updateStoreInterval, minimumFileAge, maxUploadWait; + if(::sscanf(configSummary.c_str(), "bbackupd: %d %d %d %d", + &autoBackup, &updateStoreInterval, + &minimumFileAge, &maxUploadWait) != 4) + { + printf("Config summary didn't decode\n"); + return false; + } + + std::string cmds; + bool expectResponse; + + if (rCmd != "") + { + cmds = rCmd; + cmds += "\nquit\n"; + expectResponse = true; + } + else + { + cmds = "quit\n"; + expectResponse = false; + } + + connection.Write(cmds.c_str(), cmds.size()); + + // Read the response + std::string line; + bool statusOk = !expectResponse; + + while (expectResponse && !getLine.IsEOF() && getLine.GetLine(line)) + { + // Is this an OK or error line? + if (line == "ok") + { + statusOk = true; + } + else if (line == "error") + { + printf("ERROR (%s)\n", rCmd.c_str()); + break; + } + else + { + printf("WARNING: Unexpected response to command '%s': " + "%s", rCmd.c_str(), line.c_str()); + } + } + + return statusOk; +} + +inline bool ServerIsAlive() +{ + return SendCommands(""); +} + +inline bool HUPServer(int pid) +{ + return SendCommands("reload"); +} + +inline bool KillServer(int pid) +{ + TEST_THAT(SendCommands("terminate")); + ::sleep(1); + return !ServerIsAlive(); +} + +#else // !WIN32 + +inline bool ServerIsAlive(int pid) +{ + if(pid == 0) return false; + return ::kill(pid, 0) != -1; +} + +inline bool HUPServer(int pid) +{ + if(pid == 0) return false; + return ::kill(pid, SIGHUP) != -1; +} + +inline bool KillServer(int pid) +{ + if(pid == 0 || pid == -1) return false; + bool KilledOK = ::kill(pid, SIGTERM) != -1; + TEST_THAT(KilledOK); + ::sleep(1); + return !ServerIsAlive(pid); +} + +#endif // WIN32 + +inline void TestRemoteProcessMemLeaks(const char *filename) +{ +#ifdef BOX_MEMORY_LEAK_TESTING + // Does the file exist? + if(!TestFileExists(filename)) + { + ++failures; + printf("FAILURE: MemLeak report not available (file %s)\n", filename); + } + else + { + // Is it empty? + if(TestGetFileSize(filename) > 0) + { + ++failures; + printf("FAILURE: Memory leaks found in other process (file %s)\n==========\n", filename); + FILE *f = fopen(filename, "r"); + char line[512]; + while(::fgets(line, sizeof(line), f) != 0) + { + printf("%s", line); + } + fclose(f); + printf("==========\n"); + } + + // Delete it + ::unlink(filename); + } +#endif +} + +#endif // TEST__H + |