diff --git a/test_suite/CMakeLists.txt b/test_suite/CMakeLists.txt index 566930197..01f3aa355 100644 --- a/test_suite/CMakeLists.txt +++ b/test_suite/CMakeLists.txt @@ -16,6 +16,7 @@ set(TESTSUITE_SOURCES fgTestListener.cxx fgTestRunner.cxx formatting.cxx + logging.cxx testSuite.cxx ) set(TESTSUITE_HEADERS @@ -24,6 +25,7 @@ set(TESTSUITE_HEADERS fgTestListener.hxx fgTestRunner.hxx formatting.hxx + logging.hxx ) # The test suite output directory. diff --git a/test_suite/fgCompilerOutputter.cxx b/test_suite/fgCompilerOutputter.cxx index 26006d24c..fc0d48c77 100644 --- a/test_suite/fgCompilerOutputter.cxx +++ b/test_suite/fgCompilerOutputter.cxx @@ -26,6 +26,7 @@ #include "fgCompilerOutputter.hxx" #include "formatting.hxx" +#include "logging.hxx" using namespace std; @@ -59,15 +60,16 @@ void fgCompilerOutputter::printFailureDetail(CppUnit::TestFailure *failure) if (test_iter != io_capt->end()) test_io = *test_iter; - // Default IO streams. - fg_stream << string(WIDTH_DIVIDER, '-') << endl; - fg_stream << "STDOUT and STDERR:" << endl; - fg_stream << test_io.stdio << endl; - // SG_LOG IO streams. - fg_stream << string(WIDTH_DIVIDER, '-') << endl; - fg_stream << "SG_LOG:" << endl; - fg_stream << test_io.sg_log << endl; + fgCompilerOutputter::printIOStreamMessages("SG_LOG, SG_ALL class, SG_BULK priority", test_io.sg_bulk, true); + fgCompilerOutputter::printIOStreamMessages("SG_LOG, SG_ALL class, SG_BULK only priority", test_io.sg_bulk_only); + fgCompilerOutputter::printIOStreamMessages("SG_LOG, SG_ALL class, SG_DEBUG only priority", test_io.sg_debug_only); + fgCompilerOutputter::printIOStreamMessages("SG_LOG, SG_ALL class, SG_INFO only priority", test_io.sg_info_only); + fgCompilerOutputter::printIOStreamMessages("SG_LOG, SG_ALL class, SG_WARN only priority", test_io.sg_warn_only); + fgCompilerOutputter::printIOStreamMessages("SG_LOG, SG_ALL class, SG_ALERT only priority", test_io.sg_alert_only); + + // Default IO streams. + fgCompilerOutputter::printIOStreamMessages("STDOUT and STDERR", test_io.stdio); } @@ -86,6 +88,28 @@ void fgCompilerOutputter::printFailureReport() } +void fgCompilerOutputter::printIOStreamMessages(const char *heading, string messages, bool empty) +{ + // Silence. + if (!empty && messages.size() == 0) + return; + + // Divider. + fg_stream << string(WIDTH_DIVIDER, '-') << endl; + + // Heading. + fg_stream << "# " << heading << endl << endl; + + // Nothing to do + if (messages.size() == 0) + fg_stream << "(empty)" << endl << endl; + + // The IO stream contents. + else + fg_stream << messages << endl; +} + + // Printout of the test suite stats. void fgCompilerOutputter::printSuiteStats() { diff --git a/test_suite/fgCompilerOutputter.hxx b/test_suite/fgCompilerOutputter.hxx index 650f4ce8e..60cf5a182 100644 --- a/test_suite/fgCompilerOutputter.hxx +++ b/test_suite/fgCompilerOutputter.hxx @@ -71,6 +71,10 @@ class fgCompilerOutputter : public CppUnit::CompilerOutputter // The test suite time, in clock ticks. const clock_t *suite_timer; + + // Simgear logstream IO printout. + void printIOStreamMessages(const char *heading, std::string messages, bool empty); + void printIOStreamMessages(const char *heading, std::string messages) {printIOStreamMessages(heading, messages, false);} }; diff --git a/test_suite/fgTestListener.cxx b/test_suite/fgTestListener.cxx index 0a8dbc734..d2671236d 100644 --- a/test_suite/fgTestListener.cxx +++ b/test_suite/fgTestListener.cxx @@ -23,6 +23,7 @@ #include #include "fgTestListener.hxx" +#include "logging.hxx" using namespace std; @@ -62,6 +63,15 @@ void fgTestListener::endTest(CppUnit::Test *test) // Standard IO. test_io.stdio = capt.str(); + // The simgear logstreams. + capturedIO &obj = getIOstreams(); + test_io.sg_bulk = obj.sg_bulk.str(); + test_io.sg_bulk_only = obj.sg_bulk_only.str(); + test_io.sg_debug_only = obj.sg_debug_only.str(); + test_io.sg_info_only = obj.sg_info_only.str(); + test_io.sg_warn_only = obj.sg_warn_only.str(); + test_io.sg_alert_only = obj.sg_alert_only.str(); + // Add the test's IO to the list. io_capt.push_back(test_io); } @@ -71,6 +81,15 @@ void fgTestListener::endTest(CppUnit::Test *test) // Override the base class function to capture IO streams. void fgTestListener::startTest(CppUnit::Test *test) { + // Clear the simgear logstream buffers. + capturedIO &obj = getIOstreams(); + obj.sg_bulk.str(""); + obj.sg_bulk_only.str(""); + obj.sg_debug_only.str(""); + obj.sg_info_only.str(""); + obj.sg_warn_only.str(""); + obj.sg_alert_only.str(""); + // Store the original STDOUT and STDERR for restoring later on. orig_cout = cout.rdbuf(); orig_cerr = cerr.rdbuf(); diff --git a/test_suite/fgTestListener.hxx b/test_suite/fgTestListener.hxx index e8a1b8a40..2896937db 100644 --- a/test_suite/fgTestListener.hxx +++ b/test_suite/fgTestListener.hxx @@ -30,7 +30,12 @@ struct TestIOCapt { std::string name; std::string stdio; - std::string sg_log; + std::string sg_bulk; + std::string sg_bulk_only; + std::string sg_debug_only; + std::string sg_info_only; + std::string sg_warn_only; + std::string sg_alert_only; }; diff --git a/test_suite/logging.cxx b/test_suite/logging.cxx new file mode 100644 index 000000000..7fdb49fd1 --- /dev/null +++ b/test_suite/logging.cxx @@ -0,0 +1,105 @@ +/* + * Copyright (C) 2016 Edward d'Auvergne + * + * This file is part of the program FlightGear. + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see . + */ + + +#include "logging.hxx" + + +// The global stream capture data structure. +static capturedIO *_iostreams = NULL; + + +// capturedIO constructor. +capturedIO::capturedIO() +{ + callback_bulk = new StreamLogCallback(sg_bulk, SG_ALL, SG_BULK, false); + callback_bulk_only = new StreamLogCallback(sg_bulk_only, SG_ALL, SG_BULK, true); + callback_debug_only = new StreamLogCallback(sg_debug_only, SG_ALL, SG_DEBUG, true); + callback_info_only = new StreamLogCallback(sg_info_only, SG_ALL, SG_INFO, true); + callback_warn_only = new StreamLogCallback(sg_warn_only, SG_ALL, SG_WARN, true); + callback_alert_only = new StreamLogCallback(sg_alert_only, SG_ALL, SG_ALERT, true); +} + +// capturedIO destructor. +capturedIO::~capturedIO() +{ + // Destroy the callback objects. + delete callback_bulk; + delete callback_bulk_only; + delete callback_debug_only; + delete callback_info_only; + delete callback_warn_only; + delete callback_alert_only; +} + + +// Return the global stream capture data structure, creating it if needed. +capturedIO & getIOstreams() +{ + // Initialise the global stream capture data structure, if needed. + if (!_iostreams) + _iostreams = new capturedIO(); + + // Return a pointer to the global object. + return *_iostreams; +} + + +// Set up to capture all the simgear logging priorities as separate streams. +void setupLogging() +{ + // Get the single logstream instance. + logstream &log = sglog(); + + // Set up the logstream testing mode. + log.setTestingMode(true); + + // IO capture. + capturedIO &obj = getIOstreams(); + log.addCallback(obj.callback_bulk); + log.addCallback(obj.callback_bulk_only); + log.addCallback(obj.callback_debug_only); + log.addCallback(obj.callback_info_only); + log.addCallback(obj.callback_warn_only); + log.addCallback(obj.callback_alert_only); +} + + +// Deactivate all the simgear logging priority IO captures. +void stopLogging() +{ + // Get the single logstream instance. + logstream &log = sglog(); + + // IO decapture. + capturedIO &obj = getIOstreams(); + log.removeCallback(obj.callback_bulk); + log.removeCallback(obj.callback_bulk_only); + log.removeCallback(obj.callback_debug_only); + log.removeCallback(obj.callback_info_only); + log.removeCallback(obj.callback_warn_only); + log.removeCallback(obj.callback_alert_only); + + // Clean up the IO stream object. + delete _iostreams; + _iostreams = NULL; + + // Stop the simgear logstream. + simgear::shutdownLogging(); +} diff --git a/test_suite/logging.hxx b/test_suite/logging.hxx new file mode 100644 index 000000000..b7e0b0d27 --- /dev/null +++ b/test_suite/logging.hxx @@ -0,0 +1,96 @@ + +/* + * Copyright (C) 2016 Edward d'Auvergne + * + * This file is part of the program FlightGear. + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see . + */ + + +#ifndef _FG_TEST_SUITE_LOGGING_HXX +#define _FG_TEST_SUITE_LOGGING_HXX + + +#include + +#include + + +// A logstream callback based on the simgear FileLogCallback class. +class StreamLogCallback : public simgear::LogCallback +{ +public: + StreamLogCallback(std::ostringstream& stream, sgDebugClass c, sgDebugPriority p, bool priorityMatch): + simgear::LogCallback(c, p), + m_priority(p), + m_stream(stream), + m_priorityMatch(priorityMatch) + { + } + + virtual void operator()(sgDebugClass c, sgDebugPriority p, const char* file, int line, const std::string& message) + { + if (m_priorityMatch && p != m_priority) return; + if (!shouldLog(c, p)) return; + if (m_priorityMatch) + m_stream << simgear::LogCallback::debugClassToString(c) << ":" << file << ":" << line << ": " << message << std::endl; + else + m_stream << simgear::LogCallback::debugClassToString(c) << ":" << (int) p << ":" << file << ":" << line << ": " << message << std::endl; + } + +private: + sgDebugPriority m_priority; + std::ostringstream &m_stream; + bool m_priorityMatch; +}; + + +// All of the captured IO streams. +class capturedIO +{ + public: + // Constructor and destructor. + capturedIO(); + ~capturedIO(); + + // The IO streams. + std::ostringstream sg_bulk; + std::ostringstream sg_bulk_only; + std::ostringstream sg_debug_only; + std::ostringstream sg_info_only; + std::ostringstream sg_warn_only; + std::ostringstream sg_alert_only; + + // The callback objects. + StreamLogCallback *callback_bulk; + StreamLogCallback *callback_bulk_only; + StreamLogCallback *callback_debug_only; + StreamLogCallback *callback_info_only; + StreamLogCallback *callback_warn_only; + StreamLogCallback *callback_alert_only; +}; + + +// Return the global stream capture data structure, creating it if needed. +capturedIO & getIOstreams(); + +// Set up to capture all the simgear logging priorities as separate streams. +void setupLogging(); + +// Deactivate all the simgear logging priority IO captures. +void stopLogging(); + + +#endif // _FG_TEST_SUITE_LOGGING_HXX diff --git a/test_suite/testSuite.cxx b/test_suite/testSuite.cxx index 462e35fa4..f3b9f13aa 100644 --- a/test_suite/testSuite.cxx +++ b/test_suite/testSuite.cxx @@ -21,6 +21,7 @@ #include "fgTestRunner.hxx" #include "formatting.hxx" +#include "logging.hxx" using namespace std; @@ -67,6 +68,10 @@ int main(void) // Declarations. int status_gui, status_simgear, status_system, status_unit; + // Set up logging. + sglog().setDeveloperMode(true); + setupLogging(); + // Execute each of the test suite categories. status_system = testRunner("System tests"); status_unit = testRunner("Unit tests"); @@ -76,6 +81,9 @@ int main(void) // Summary printout. summary(cerr, status_system, status_unit, status_gui, status_simgear); + // Deactivate the logging. + stopLogging(); + // Failure. if (status_system > 0) return 1;