diff options
Diffstat (limited to 'indra/llcommon/tests/wrapllerrs.h')
-rw-r--r-- | indra/llcommon/tests/wrapllerrs.h | 129 |
1 files changed, 129 insertions, 0 deletions
diff --git a/indra/llcommon/tests/wrapllerrs.h b/indra/llcommon/tests/wrapllerrs.h index ffda84729b..a4d3a4e026 100644 --- a/indra/llcommon/tests/wrapllerrs.h +++ b/indra/llcommon/tests/wrapllerrs.h @@ -29,7 +29,22 @@ #if ! defined(LL_WRAPLLERRS_H) #define LL_WRAPLLERRS_H +#if LL_WINDOWS +#pragma warning (disable : 4355) // 'this' used in initializer list: yes, intentionally +#endif + +#include <tut/tut.hpp> #include "llerrorcontrol.h" +#include "stringize.h" +#include <boost/bind.hpp> +#include <boost/noncopyable.hpp> +#include <list> +#include <string> +#include <stdexcept> + +// statically reference the function in test.cpp... it's short, we could +// replicate, but better to reuse +extern void wouldHaveCrashed(const std::string& message); struct WrapLL_ERRS { @@ -70,4 +85,118 @@ struct WrapLL_ERRS LLError::FatalFunction mPriorFatal; }; +/** + * LLError::addRecorder() accepts ownership of the passed Recorder* -- it + * expects to be able to delete it later. CaptureLog isa Recorder whose + * pointer we want to be able to pass without any ownership implications. + * For such cases, instantiate a new RecorderProxy(yourRecorder) and pass + * that. Your heap RecorderProxy might later be deleted, but not yourRecorder. + */ +class RecorderProxy: public LLError::Recorder +{ +public: + RecorderProxy(LLError::Recorder* recorder): + mRecorder(recorder) + {} + + virtual void recordMessage(LLError::ELevel level, const std::string& message) + { + mRecorder->recordMessage(level, message); + } + + virtual bool wantsTime() + { + return mRecorder->wantsTime(); + } + +private: + LLError::Recorder* mRecorder; +}; + +/** + * Capture log messages. This is adapted (simplified) from the one in + * llerror_test.cpp. + */ +class CaptureLog : public LLError::Recorder, public boost::noncopyable +{ +public: + CaptureLog(LLError::ELevel level=LLError::LEVEL_DEBUG): + // Mostly what we're trying to accomplish by saving and resetting + // LLError::Settings is to bypass the default RecordToStderr and + // RecordToWinDebug Recorders. As these are visible only inside + // llerror.cpp, we can't just call LLError::removeRecorder() with + // each. For certain tests we need to produce, capture and examine + // DEBUG log messages -- but we don't want to spam the user's console + // with that output. If it turns out that saveAndResetSettings() has + // some bad effect, give up and just let the DEBUG level log messages + // display. + mOldSettings(LLError::saveAndResetSettings()), + mProxy(new RecorderProxy(this)) + { + LLError::setFatalFunction(wouldHaveCrashed); + LLError::setDefaultLevel(level); + LLError::addRecorder(mProxy); + } + + ~CaptureLog() + { + LLError::removeRecorder(mProxy); + delete mProxy; + LLError::restoreSettings(mOldSettings); + } + + void recordMessage(LLError::ELevel level, + const std::string& message) + { + mMessages.push_back(message); + } + + /// Don't assume the message we want is necessarily the LAST log message + /// emitted by the underlying code; search backwards through all messages + /// for the sought string. + std::string messageWith(const std::string& search, bool required=true) + { + for (MessageList::const_reverse_iterator rmi(mMessages.rbegin()), rmend(mMessages.rend()); + rmi != rmend; ++rmi) + { + if (rmi->find(search) != std::string::npos) + return *rmi; + } + // failed to find any such message + if (! required) + return std::string(); + + throw tut::failure(STRINGIZE("failed to find '" << search + << "' in captured log messages:\n" + << boost::ref(*this))); + } + + std::ostream& streamto(std::ostream& out) const + { + MessageList::const_iterator mi(mMessages.begin()), mend(mMessages.end()); + if (mi != mend) + { + // handle first message separately: it doesn't get a newline + out << *mi++; + for ( ; mi != mend; ++mi) + { + // every subsequent message gets a newline + out << '\n' << *mi; + } + } + return out; + } + + typedef std::list<std::string> MessageList; + MessageList mMessages; + LLError::Settings* mOldSettings; + LLError::Recorder* mProxy; +}; + +inline +std::ostream& operator<<(std::ostream& out, const CaptureLog& log) +{ + return log.streamto(out); +} + #endif /* ! defined(LL_WRAPLLERRS_H) */ |