From d84d880627bcc1e1898a8f96b861bc25863ec86c Mon Sep 17 00:00:00 2001 From: Jesse Gilles Date: Mon, 20 Apr 2015 17:14:31 -0500 Subject: initial commit --- test/CMakeLists.txt | 9 + test/TestRunnerClient.cpp | 566 ++++++++++++++++++++++++++++++++++++++++++++++ test/TestRunnerClient.h | 147 ++++++++++++ 3 files changed, 722 insertions(+) create mode 100644 test/CMakeLists.txt create mode 100644 test/TestRunnerClient.cpp create mode 100644 test/TestRunnerClient.h (limited to 'test') diff --git a/test/CMakeLists.txt b/test/CMakeLists.txt new file mode 100644 index 0000000..64428e9 --- /dev/null +++ b/test/CMakeLists.txt @@ -0,0 +1,9 @@ +project (TestRunnerClient) + +include_directories (../include) +link_directories (..) + +set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} -std=c++0x -DCPPUNIT_MAIN=main") + +add_executable (TestRunnerClient TestRunnerClient.cpp ) +target_link_libraries (TestRunnerClient cppunit mts mts_io ssl rt pthread) diff --git a/test/TestRunnerClient.cpp b/test/TestRunnerClient.cpp new file mode 100644 index 0000000..fcb9c75 --- /dev/null +++ b/test/TestRunnerClient.cpp @@ -0,0 +1,566 @@ +/******************************************************************************* + * Copyright (c) 2008 Gerhard Leonhartsberger. + * All rights reserved. This program and the accompanying materials + * are made available under the terms of the Eclipse Public License v1.0 + * which accompanies this distribution, and is available at + * http://www.eclipse.org/legal/epl-v10.html + *******************************************************************************/ + +#include "TestRunnerClient.h" + +#ifdef CPPUNIT_MAIN + +#include "cppunit/XmlOutputter.h" +#include "cppunit/TextOutputter.h" +#include "cppunit/TestSuite.h" +#include "cppunit/TestResult.h" +#include "cppunit/TestFailure.h" +#include "cppunit/SourceLine.h" +#include "cppunit/Exception.h" +#include "cppunit/extensions/TestFactoryRegistry.h" +#include "cppunit/extensions/TestDecorator.h" +#include "cppunit/ui/text/TestRunner.h" + +#include +#include +#include +#include + +#include +#include +#include + +#ifdef _WIN32 // Bugzilla 40710 +#include +#include +#include +#else +#include +#include +#include +#include +#endif + +#define MAX_HOSTNAME_SIZE 255 + +/* + * CppUnitServer protocol constants + */ +static const std::string TRACE_START = "%TRACES "; +static const std::string TRACE_END = "%TRACEE "; +static const std::string TEST_RUN_START = "%TESTC "; +static const std::string TEST_START = "%TESTS "; +static const std::string TEST_END = "%TESTE "; +static const std::string TEST_ERROR = "%ERROR "; +static const std::string TEST_FAILED = "%FAILED "; +static const std::string TEST_RUN_END = "%RUNTIME"; +static const std::string TEST_STOPPED = "%TSTSTP "; +static const std::string TEST_TREE = "%TSTTREE"; + +TestRunnerClient::TestRunnerClient() +{ + fTestResult = 0; + fClientSocket = -1; + fPort = 0; + fKeepAlive = 0; + fDebugMode = 0; + + fHost = (char *) malloc(MAX_HOSTNAME_SIZE); + strcpy(fHost, ""); +} + +TestRunnerClient::~TestRunnerClient() { + + if (fHost != NULL) { + free(fHost); + } +} + +int TestRunnerClient::Run() +{ + if (fDebugMode) + { + std::cerr << "TestRunnerClient: Starting client." << std::endl; + } + + if (Connect() == -1) { + return -1; + } + + InstallListeners(); + + RunTests(); + + UninstallListeners(); + + if(fTestResult != NULL) + { + fTestResult->stop(); + fTestResult= NULL; + } + + ShutDown(); + + return 0; +} + +void TestRunnerClient::Init(int n, char *args[]) +{ + ParseCommandLine(n, args); + DefineHostName(); +} + +void TestRunnerClient::ParseCommandLine(int n, char *args[]) +{ + // parse all arguments passed by args + for(int i = 0; i < n; i++) + { + std::string arg(args[i]); + + // port option + std::string portOption("-port="); + int pos = arg.find(portOption); + if(pos> -1) + { + std::string v = arg.substr(pos + portOption.length(), arg.length()); + fPort = atoi(v.c_str()); + } + + // debug option + std::string debugOption("-debug"); + pos = arg.find(debugOption); + if(pos> - 1) + { + fDebugMode = 1; + } + } +} + +void TestRunnerClient::DefineHostName() +{ + // set fHost to hostname or localhost + int ret = gethostname(fHost, MAX_HOSTNAME_SIZE); + if (ret == -1) + { + strcpy(fHost, "localhost"); + } +} + +int TestRunnerClient::Connect() +{ + +#ifdef _WIN32 // Bugzilla 40710 + if (fDebugMode) + { + std::cerr << "TestRunnerClient: Starting Windows Sockets WSAStartup()." << std:endl; + } + + // start up Windows Sockets + WSADATA WSAData; + int result = WSAStartup (MAKEWORD(1, 1), &WSAData); + if (result != NO_ERROR) + { + std::cerr << "TestRunnerClient: WSAStartup() failed! Error code: " << result << std::endl; + return -1; + } +#endif + + if (fDebugMode) + { + std::cerr << "TestRunnerClient: Trying to connect to " << fHost << ":" << fPort << std::endl; + } + + fClientSocket = socket(AF_INET, SOCK_STREAM, 0); + if (fClientSocket == -1) + { + std::cerr << "TestRunnerClient: Socket creation failed! error code: " << fClientSocket << std::endl; + return -1; + } + + struct hostent *host = gethostbyname(fHost); + if (host == NULL) + { + std::cerr << "TestRunnerClient: Cannot find host address for " << fHost << "." << std::endl; + fClientSocket = -1; + return -1; + } + + struct sockaddr_in name; + memset((void *)&name, 0, sizeof(struct sockaddr_in)); + name.sin_family = AF_INET; + name.sin_port = htons(fPort); + + memcpy(&name.sin_addr, host->h_addr, host->h_length); + + if (fDebugMode) { + std::cerr << "TestRunnerClient: Waiting for the JVM to listen ... (trying 3 times)" << std::endl; + } + + int ret = -1; + int j = 0; + while ((j < 3) && (ret == -1)) + { + ret = ::connect(fClientSocket, (struct sockaddr *) &name, sizeof(struct sockaddr_in)); + if (ret == -1) + { + if (fDebugMode) { + std::cerr << "TestRunnerClient: Connection request, waiting 1 second. " + << ((j-3)*-1) << " times left." << std::endl; + } + PrivateSleep(1000); + j++; + } + } + if (ret == -1) + { + std::cerr << "TestRunnerClient: No connection established. Error code: " << errno << std::endl; + fClientSocket = -1; + return -1; + } + + if (fDebugMode) { + std::cerr << "TestRunnerClient: Connection established." << std::endl; + } + return 0; +} + +void TestRunnerClient::InstallListeners() +{ + fTestResult = new CppUnit::TestResult(); + fTestResult->addListener(this); + fTestResult->addListener(&resultCollector); +} + +void TestRunnerClient::UninstallListeners() +{ + fTestResult->removeListener(this); +} + +void TestRunnerClient::RunTests() +{ + + CppUnit::TestFactoryRegistry ®istry = CppUnit::TestFactoryRegistry::getRegistry(); + CppUnit::Test *suite = registry.makeTest(); + int count = suite->countTestCases(); + NotifyTestRunStarted(count); + + if (count == 0) + { + NotifyTestRunEnded(0); + } + + long startTime = CurrentTimeMillis(); + if (fDebugMode) + { + std::cerr <<"TestRunnerClient: Start sending test case tree ..." << std::endl; + } + + SendTestTree(suite); + + int elapsedTime = CurrentTimeMillis() - startTime; + if (fDebugMode) { + std::cerr << "TestRunnerClient: Done sending test case tree. Elapsed time is " + << elapsedTime << "ms." << std::endl; + } + + long testStartTime = CurrentTimeMillis(); + if (fDebugMode) { + std::cerr << "TestRunnerClient: Test start time is " << testStartTime + << "ms." << std::endl; + } + + suite->run(fTestResult); + + if (fTestResult == NULL || fTestResult->shouldStop()) + { + NotifyTestRunStopped(CurrentTimeMillis() - testStartTime); + } + else + { + NotifyTestRunEnded(CurrentTimeMillis() - testStartTime); + } +} + +void TestRunnerClient::ShutDown() +{ + if (fClientSocket != -1) + { + if (fDebugMode) { + std::cerr << "TestRunnerClient: Closing connection to CppUnit sever at " + << fHost << ":" << fPort << std::endl; + } + +#ifdef _WIN32 // Bugzilla 40710 + // TODO: std:err output for error return codes + closesocket(fClientSocket); + WSACleanup(); +#else + int result = close(fClientSocket); + if (result != 0) + { + std::cerr << "TestRunnerClient: Close connection error: " << errno << std::endl; + } +#endif + + fClientSocket = -1; + } +} + +void TestRunnerClient::Stop() +{ + if (fTestResult != NULL) + { + fTestResult->stop(); + } +} + +void TestRunnerClient::SendTestTree(CppUnit::Test *test) +{ + if (typeid(*test) == typeid(CppUnit::TestDecorator)) + { + class TmpClass : public CppUnit::TestDecorator { + + public: + TmpClass(Test *t):CppUnit::TestDecorator(t) + { + // nothing to do + } + + ~TmpClass() // Bugzilla 39894 + { + // nothing to do + } + + CppUnit::Test *getTest() + { + return m_test; + } + }; + + TmpClass *t = (TmpClass *)test; + SendTestTree(t->getTest()); + } + else if (typeid(*test) == typeid(CppUnit::TestSuite)) + { + CppUnit::TestSuite *suite = (CppUnit::TestSuite *)test; + const std::vector &x = suite->getTests(); + + std::ostringstream os; + os << suite->getName() << ",true," << x.size(); + NotifyTestTreeEntry(os.str()); + + for(unsigned int i=0; i < x.size(); i++) + { + SendTestTree(x[i]); + } + } + else + { + std::ostringstream os; + os << test->getName() << ",false," << test->countTestCases(); + NotifyTestTreeEntry(os.str()); + } +} + +void TestRunnerClient::SendMessage(std::string msg) +{ + if (fClientSocket == -1) + { + return; + } + +#ifdef _WIN32 // Bugzilla 40710 + send (fClientSocket, msg.c_str(), msg.length(), 0); + send (fClientSocket, "\n", 1, 0); +#else + write(fClientSocket, msg.c_str(), msg.length()); + write(fClientSocket, "\n", 1); +#endif + + if (fDebugMode) + { + std::cerr << "TestRunnerClient: Sent message \"" << msg << "\"" + << std::endl; + } +} + +void TestRunnerClient::NotifyTestRunStarted(int testCount) +{ + std::ostringstream os; + os << TEST_RUN_START << testCount; + SendMessage(os.str()); +} + +void TestRunnerClient::NotifyTestRunEnded(long elapsedTime) +{ + std::ostringstream os; + os << TEST_RUN_END << elapsedTime; + SendMessage(os.str()); +} + +void TestRunnerClient::NotifyTestRunStopped(long elapsedTime) +{ + std::ostringstream os; + os << TEST_STOPPED << elapsedTime; + SendMessage(os.str()); +} + +void TestRunnerClient::NotifyTestTreeEntry(std::string treeEntry) +{ + SendMessage(TEST_TREE + treeEntry); +} + +void TestRunnerClient::NotifyTestStarted(std::string testName) +{ + SendMessage(TEST_START + testName); +} + +void TestRunnerClient::NotifyTestEnded(std::string testName) +{ + SendMessage(TEST_END + testName); +} + +void TestRunnerClient::NotifyTestFailed(std::string status, std::string testName, std::string trace) +{ + SendMessage(status + testName); + SendMessage(TRACE_START); + SendMessage(trace); + SendMessage(TRACE_END); +} + +// From TestListener +void TestRunnerClient::startTest(CppUnit::Test *test) +{ + NotifyTestStarted(test->getName()); +} + +// From TestListener +void TestRunnerClient::addFailure(const CppUnit::TestFailure &failure) +{ + if(failure.isError()) + { + NotifyTestFailed(TEST_ERROR,failure.failedTestName(),GetTrace(failure)); + } + else + { + NotifyTestFailed(TEST_FAILED,failure.failedTestName(),GetTrace(failure)); + } +} + +// From TestListener +void TestRunnerClient::endTest(CppUnit::Test *test) +{ + NotifyTestEnded(test->getName()); +} + +std::string TestRunnerClient::GetTrace(const CppUnit::TestFailure &failure) +{ + std::ostringstream os; + + CppUnit::Exception *e=failure.thrownException(); + if(e->sourceLine().lineNumber()!=-1) + { + os << "File " << e->sourceLine().fileName() << ":" << e->sourceLine().lineNumber() << "\n"; + } + else + { + os << "File Unknown:1\n"; + } + /* TODO: expected, actual value implementation + if(typeid(*e)==typeid(CppUnit::NotEqualException)) + { + CppUnit::NotEqualException *ne=(CppUnit::NotEqualException *)e; + + os << "Expected Value: " << ne->expectedValue() << "\n"; + os << "Actual Value: " << ne->expectedValue() << "\n"; + os << "Additional Message: " << ne->additionalMessage() << "\n"; + } + else + { + End */ + os << "Message: " << std::string(e->what()) << "\n"; + /* } */ + + return(os.str()); +} + +long TestRunnerClient::CurrentTimeMillis() +{ +#ifdef _WIN32 // Bugzilla 40710 + unsigned long long p; + __asm__ __volatile__ ("rdtsc" : "=A" (p)); + return (unsigned long)p; +#else + struct timeval tv; + gettimeofday(&tv, NULL); + + return((long)(tv.tv_sec*1000) + (tv.tv_usec/1000)); +#endif +} + +void TestRunnerClient::PrivateSleep(int millisecs) +{ + struct timeval delta; + delta.tv_sec = (millisecs * 1000L) / 1000000L; + delta.tv_usec = (millisecs * 1000L) % 1000000L; + select (0, NULL, NULL, NULL, &delta); +} + +CppUnit::TestResultCollector& TestRunnerClient::getResultCollector() { + return resultCollector; +} + +/*! + * This is the main routine. The TestRunnerClient is initialized and run. The + * CppUnit tests are created, executed, and sent to the CppUnitServer. + * If no connection to the CppUnitServer was established the CppUnit tests are + * displayed on the console. + * + * @return 0 if the results of the CppUnit tests were sent to the + * CppUnitServer successfully. + * -1 if a connection could not be established to the + * CppUnitServer. + */ +int CPPUNIT_MAIN(int n, char *arg[]) +{ + std::ofstream xmlFileOut("cppunit_results.xml"); + + TestRunnerClient client; + client.Init(n, arg); + int ret = client.Run(); + if (ret == -1) + { + //The Test Runner Client has failed + //Create the event manager and test controller + + CppUnit::TestResult controller; + + // Add a listener that collects test result + CppUnit::TestResultCollector result; + controller.addListener ( &result ); + + CppUnit::XmlOutputter xmlOutputter ( &result, xmlFileOut ); + CppUnit::TestFactoryRegistry ®istry = CppUnit::TestFactoryRegistry::getRegistry(); + CppUnit::Test *suite = registry.makeTest(); + + CppUnit::TextUi::TestRunner *runner = new CppUnit::TextUi::TestRunner(); + runner->addTest(suite); + runner->run(controller); + + // Output to XML + xmlOutputter.write(); + } else { + + CppUnit::TextOutputter textOutputter (&client.getResultCollector(), std::cout); + textOutputter.write(); + + // Output to XML + CppUnit::XmlOutputter xmlOutputter ( &client.getResultCollector(), xmlFileOut ); + xmlOutputter.write(); + } + + + xmlFileOut.close(); +} + +#endif /*CPPUNIT_MAIN*/ diff --git a/test/TestRunnerClient.h b/test/TestRunnerClient.h new file mode 100644 index 0000000..4dc4460 --- /dev/null +++ b/test/TestRunnerClient.h @@ -0,0 +1,147 @@ +/******************************************************************************* + * Copyright (c) 2008 Gerhard Leonhartsberger. + * All rights reserved. This program and the accompanying materials + * are made available under the terms of the Eclipse Public License v1.0 + * which accompanies this distribution, and is available at + * http://www.eclipse.org/legal/epl-v10.html + *******************************************************************************/ + +#ifndef TESTRUNNERSERVER_H_ +#define TESTRUNNERSERVER_H_ + +#ifdef CPPUNIT_MAIN + +#include "cppunit/TestResultCollector.h" +#include "cppunit/TestListener.h" + +#include +#include +#include +#include + + +/*! + * Class TestRunnerClient handles the network connection to the + * CppUnitServer and executes all registered CppUnit tests. + *

+ * The meta data of the CppUnit tests and the test results are sent to the + * CppUnitServer for displaying. + *

+ *

+ * For debugging purposes class TestRunnerClient displays debug + * messages on std.cerr. The debug modus is activated by specifying + * debug command line option. The command line is parsed by method + * Init(). + *

+ *

+ * Note: This class is not intended to be subclassed by clients. This class is + * based on the original RemoteTestRunner class provided by + * org.eclipse.cdt.cppunit. + *

+ * + * @author Gerhard Leonhartsberger + */ +class TestRunnerClient: public CppUnit::TestListener +{ +private: + CppUnit::TestResult *fTestResult; + CppUnit::TestResultCollector resultCollector; + int fClientSocket; + char *fHost; + int fPort; + int fDebugMode; + int fKeepAlive; + +public: + TestRunnerClient(); + virtual ~TestRunnerClient(); + + CppUnit::TestResultCollector& getResultCollector(); + + /*! + * Initializes the TestRunnerClient. + *

+ * The given args are parsed. The syntax for the arguments is + * defined in EBNF as follows: {-option=value} + *

+ * + * @param n The number of arguments. + * @param args The argument values. Valid options are: + *
  • -debug When present the TestRunnerClient + * is run in debug modus and displays debug messages.
  • + *
  • -port=number Defines the port where + * CppUnitServer is listening for client connections.
  • + */ + void Init(int n,char *args[]); + + /*! + * Runs the TestRunnerClient. A trial to connect to the CppUnitServer is done. + * The test results are sent to the CppUnitServer. + * + * @return The return value is 0 when the CppUnitServer was connected successfully + * otherwise the return value is -1. + */ + int Run(); + + /*! + * Stops processing CppUnit from executing tests. + */ + void Stop(); + + /*! + * Method defined in CppUnit::TestListener. + */ + void startTest(CppUnit::Test *test); + + /*! + * Method defined in CppUnit::TestListener. + */ + void addFailure(const CppUnit::TestFailure &failure); + + /*! + * Method defined in CppUnit::TestListener. + */ + void endTest(CppUnit::Test *test); + +private: + int Connect(); + void RunTests(); + void ShutDown(); + + // utility methods + void ParseCommandLine(int n, char *args[]); + void DefineHostName(); + void InstallListeners(); + void UninstallListeners(); + + /*! + * Sends the given test to the CppUnitView. + *

    + * In case of test is of type CppUnit::Test the following protocol is sent: + * TSTTREE name, false, testCaseCount + *

    + *

    + * In case of test is of type CppUnit::TestSuite the following protocol is sent: + * TSTTREE name, true, testCount + *

    + * @param test the CppUnit test + */ + void SendTestTree(CppUnit::Test *test); + void SendMessage(std::string msg); + + // Notification methods + void NotifyTestRunStarted(int testCount); + void NotifyTestRunEnded(long elapsedTime); + void NotifyTestRunStopped(long elapsedTime); + void NotifyTestTreeEntry(std::string treeEntry); + void NotifyTestStarted(std::string testName); + void NotifyTestEnded(std::string testName); + void NotifyTestFailed(std::string status, std::string testName, std::string trace); + + std::string GetTrace(const CppUnit::TestFailure &failure); + long CurrentTimeMillis(); + void PrivateSleep(int millisecs); +}; + +#endif /*CPPUNIT_MAIN*/ +#endif /*TESTRUNNERSERVER_H_*/ -- cgit v1.2.3