diff options
Diffstat (limited to 'indra')
-rw-r--r-- | indra/llcommon/llerror.cpp | 6 | ||||
-rw-r--r-- | indra/llcommon/llerrorcontrol.h | 3 | ||||
-rw-r--r-- | indra/test/test.cpp | 126 |
3 files changed, 119 insertions, 16 deletions
diff --git a/indra/llcommon/llerror.cpp b/indra/llcommon/llerror.cpp index e4381dbbd6..7e6eee0f3c 100644 --- a/indra/llcommon/llerror.cpp +++ b/indra/llcommon/llerror.cpp @@ -654,9 +654,7 @@ namespace LLError g.invalidateCallSites(); s.tagLevelMap[tag_name] = level; } -} -namespace { LLError::ELevel decodeLevel(std::string name) { static LevelMap level_names; @@ -681,7 +679,9 @@ namespace { return i->second; } - +} + +namespace { void setLevels(LevelMap& map, const LLSD& list, LLError::ELevel level) { LLSD::array_const_iterator i, end; diff --git a/indra/llcommon/llerrorcontrol.h b/indra/llcommon/llerrorcontrol.h index ed9de002f5..1be49cebc8 100644 --- a/indra/llcommon/llerrorcontrol.h +++ b/indra/llcommon/llerrorcontrol.h @@ -80,7 +80,8 @@ namespace LLError LL_COMMON_API void setClassLevel(const std::string& class_name, LLError::ELevel); LL_COMMON_API void setFileLevel(const std::string& file_name, LLError::ELevel); LL_COMMON_API void setTagLevel(const std::string& file_name, LLError::ELevel); - + + LL_COMMON_API LLError::ELevel decodeLevel(std::string name); LL_COMMON_API void configure(const LLSD&); // the LLSD can configure all of the settings // usually read automatically from the live errorlog.xml file diff --git a/indra/test/test.cpp b/indra/test/test.cpp index 1adcfb6f45..128d84e428 100644 --- a/indra/test/test.cpp +++ b/indra/test/test.cpp @@ -38,6 +38,7 @@ #include "llerrorcontrol.h" #include "lltut.h" #include "stringize.h" +#include "namedtempfile.h" #include "apr_pools.h" #include "apr_getopt.h" @@ -69,17 +70,79 @@ #include <boost/foreach.hpp> #include <boost/lambda/lambda.hpp> +#include <fstream> + +void wouldHaveCrashed(const std::string& message); + namespace tut { std::string sSourceDir; - - test_runner_singleton runner; + + test_runner_singleton runner; } +class LLReplayLog +{ +public: + LLReplayLog() {} + virtual ~LLReplayLog() {} + + virtual void reset() {} + virtual void replay(std::ostream&) {} +}; + +class LLReplayLogReal: public LLReplayLog, public LLError::Recorder +{ +public: + LLReplayLogReal(LLError::ELevel level, apr_pool_t* pool): + mOldSettings(LLError::saveAndResetSettings()), + mTempFile("log", "", pool), // create file + mFile(mTempFile.getName().c_str()) // open it + { + LLError::setFatalFunction(wouldHaveCrashed); + LLError::setDefaultLevel(level); + LLError::addRecorder(this); + } + + virtual ~LLReplayLogReal() + { + LLError::removeRecorder(this); + LLError::restoreSettings(mOldSettings); + } + + virtual void recordMessage(LLError::ELevel level, const std::string& message) + { + mFile << message << std::endl; + } + + virtual void reset() + { + mFile.close(); + mFile.open(mTempFile.getName().c_str()); + } + + virtual void replay(std::ostream& out) + { + mFile.close(); + std::ifstream inf(mTempFile.getName().c_str()); + std::string line; + while (std::getline(inf, line)) + { + out << line << std::endl; + } + } + +private: + LLError::Settings* mOldSettings; + NamedTempFile mTempFile; + std::ofstream mFile; +}; + class LLTestCallback : public tut::callback { public: - LLTestCallback(bool verbose_mode, std::ostream *stream) : + LLTestCallback(bool verbose_mode, std::ostream *stream, + boost::shared_ptr<LLReplayLog> replayer) : mVerboseMode(verbose_mode), mTotalTests(0), mPassedTests(0), @@ -87,7 +150,8 @@ public: mSkippedTests(0), // By default, capture a shared_ptr to std::cout, with a no-op "deleter" // so that destroying the shared_ptr makes no attempt to delete std::cout. - mStream(boost::shared_ptr<std::ostream>(&std::cout, boost::lambda::_1)) + mStream(boost::shared_ptr<std::ostream>(&std::cout, boost::lambda::_1)), + mReplayer(replayer) { if (stream) { @@ -125,6 +189,16 @@ public: virtual void test_completed(const tut::test_result& tr) { ++mTotalTests; + + // If this test failed, dump requested log messages BEFORE stating the + // test result. + if (tr.result != tut::test_result::ok && tr.result != tut::test_result::skip) + { + mReplayer->replay(*mStream); + } + // Either way, clear stored messages in preparation for next test. + mReplayer->reset(); + std::ostringstream out; out << "[" << tr.group << ", " << tr.test; if (! tr.name.empty()) @@ -205,6 +279,7 @@ protected: int mFailedTests; int mSkippedTests; boost::shared_ptr<std::ostream> mStream; + boost::shared_ptr<LLReplayLog> mReplayer; }; // TeamCity specific class which emits service messages @@ -213,8 +288,9 @@ protected: class LLTCTestCallback : public LLTestCallback { public: - LLTCTestCallback(bool verbose_mode, std::ostream *stream) : - LLTestCallback(verbose_mode, stream) + LLTCTestCallback(bool verbose_mode, std::ostream *stream, + boost::shared_ptr<LLReplayLog> replayer) : + LLTestCallback(verbose_mode, stream, replayer) { } @@ -355,6 +431,14 @@ void stream_usage(std::ostream& s, const char* app) ++option; } + s << app << " is also sensitive to environment variables:\n" + << "LOGTEST=level : for all tests, emit log messages at level 'level'\n" + << "LOGFAIL=level : only for failed tests, emit log messages at level 'level'\n" + << "where 'level' is one of ALL, DEBUG, INFO, WARN, ERROR, NONE.\n" + << "--debug is like LOGTEST=DEBUG, but --debug overrides LOGTEST.\n" + << "Setting LOGFAIL overrides both LOGTEST and --debug: the only log\n" + << "messages you will see will be for failed tests.\n\n"; + s << "Examples:" << std::endl; s << " " << app << " --verbose" << std::endl; s << "\tRun all the tests and report all results." << std::endl; @@ -391,8 +475,14 @@ int main(int argc, char **argv) LLError::initForApplication("."); LLError::setFatalFunction(wouldHaveCrashed); LLError::setDefaultLevel(LLError::LEVEL_ERROR); - //< *TODO: should come from error config file. Note that we - // have a command line option that sets this to debug. + // ^ possibly overridden by --debug, LOGTEST or LOGFAIL + + // LOGTEST overrides default, but can be overridden by --debug or LOGFAIL. + const char* LOGTEST = getenv("LOGTEST"); + if (LOGTEST) + { + LLError::setDefaultLevel(LLError::decodeLevel(LOGTEST)); + } #ifdef CTYPE_WORKAROUND ctype_workaround(); @@ -467,8 +557,6 @@ int main(int argc, char **argv) wait_at_exit = true; break; case 'd': - // *TODO: should come from error config file. We set it to - // ERROR by default, so this allows full debug levels. LLError::setDefaultLevel(LLError::LEVEL_DEBUG); break; case 'x': @@ -483,14 +571,28 @@ int main(int argc, char **argv) // run the tests + const char* LOGFAIL = getenv("LOGFAIL"); + boost::shared_ptr<LLReplayLog> replayer; + // As described in stream_usage(), LOGFAIL overrides both --debug and + // LOGTEST. + if (LOGFAIL) + { + LLError::ELevel level = LLError::decodeLevel(LOGFAIL); + replayer.reset(new LLReplayLogReal(level, pool)); + } + else + { + replayer.reset(new LLReplayLog()); + } + LLTestCallback* mycallback; if (getenv("TEAMCITY_PROJECT_NAME")) { - mycallback = new LLTCTestCallback(verbose_mode, output); + mycallback = new LLTCTestCallback(verbose_mode, output, replayer); } else { - mycallback = new LLTestCallback(verbose_mode, output); + mycallback = new LLTestCallback(verbose_mode, output, replayer); } tut::runner.get().set_callback(mycallback); |