diff --git a/src/Makefile.test.include b/src/Makefile.test.include --- a/src/Makefile.test.include +++ b/src/Makefile.test.include @@ -52,8 +52,10 @@ BITCOIN_TEST_SUITE = \ test/jsonutil.cpp \ - test/lib/transaction_utils.h \ + test/lib/logging.cpp \ + test/lib/logging.h \ test/lib/transaction_utils.cpp \ + test/lib/transaction_utils.h \ test/main.cpp \ test/scriptflags.cpp \ test/sigutil.cpp \ diff --git a/src/logging.h b/src/logging.h --- a/src/logging.h +++ b/src/logging.h @@ -82,6 +82,10 @@ std::string LogTimestampStr(const std::string &str); + /** Slots that connect to the print signal */ + std::list> + m_print_callbacks /* GUARDED_BY(m_cs) */ {}; + public: bool m_print_to_console = false; bool m_print_to_file = false; @@ -101,7 +105,23 @@ /** Returns whether logs will be written to any output */ bool Enabled() const { std::lock_guard scoped_lock(m_cs); - return m_buffering || m_print_to_console || m_print_to_file; + return m_buffering || m_print_to_console || m_print_to_file || + !m_print_callbacks.empty(); + } + + /** Connect a slot to the print signal and return the connection */ + std::list>::iterator + PushBackCallback(std::function fun) { + std::lock_guard scoped_lock(m_cs); + m_print_callbacks.push_back(std::move(fun)); + return --m_print_callbacks.end(); + } + + /** Delete a connection */ + void DeleteCallback( + std::list>::iterator it) { + std::lock_guard scoped_lock(m_cs); + m_print_callbacks.erase(it); } /** Start logging (and flush all buffered messages) */ diff --git a/src/logging.cpp b/src/logging.cpp --- a/src/logging.cpp +++ b/src/logging.cpp @@ -70,6 +70,9 @@ if (m_print_to_console) { fwrite(s.data(), 1, s.size(), stdout); } + for (const auto &cb : m_print_callbacks) { + cb(s); + } m_msgs_before_open.pop_front(); } @@ -87,6 +90,7 @@ fclose(m_fileout); } m_fileout = nullptr; + m_print_callbacks.clear(); } struct CLogCategoryDesc { @@ -224,6 +228,9 @@ fwrite(str_prefixed.data(), 1, str_prefixed.size(), stdout); fflush(stdout); } + for (const auto &cb : m_print_callbacks) { + cb(str_prefixed); + } if (m_print_to_file) { assert(m_fileout != nullptr); diff --git a/src/noui.h b/src/noui.h --- a/src/noui.h +++ b/src/noui.h @@ -20,11 +20,15 @@ /** Connect all bitcoind signal handlers */ void noui_connect(); -/** Suppress all bitcoind signal handlers. Used to suppress output during test - * runs that produce expected errors */ -void noui_suppress(); +/** + * Redirect all bitcoind signal handlers to LogPrintf. Used to check or suppress + * output during test runs that produce expected errors. + */ +void noui_test_redirect(); -/** Reconnects the regular Non-GUI handlers after having used noui_suppress */ +/** + * Reconnects the regular Non-GUI handlers after having used noui_test_redirect. + */ void noui_reconnect(); #endif // BITCOIN_NOUI_H diff --git a/src/noui.cpp b/src/noui.cpp --- a/src/noui.cpp +++ b/src/noui.cpp @@ -68,31 +68,35 @@ noui_InitMessageConn = uiInterface.InitMessage_connect(noui_InitMessage); } -bool noui_ThreadSafeMessageBoxSuppressed(const std::string &message, - const std::string &caption, - unsigned int style) { +bool noui_ThreadSafeMessageBoxRedirect(const std::string &message, + const std::string &caption, + unsigned int style) { + LogPrintf("%s: %s\n", caption, message); return false; } -bool noui_ThreadSafeQuestionSuppressed( +bool noui_ThreadSafeQuestionRedirect( const std::string & /* ignored interactive message */, const std::string &message, const std::string &caption, unsigned int style) { + LogPrintf("%s: %s\n", caption, message); return false; } -void noui_InitMessageSuppressed(const std::string &message) {} +void noui_InitMessageRedirect(const std::string &message) { + LogPrintf("init message: %s\n", message); +} -void noui_suppress() { +void noui_test_redirect() { noui_ThreadSafeMessageBoxConn.disconnect(); noui_ThreadSafeQuestionConn.disconnect(); noui_InitMessageConn.disconnect(); noui_ThreadSafeMessageBoxConn = uiInterface.ThreadSafeMessageBox_connect( - noui_ThreadSafeMessageBoxSuppressed); - noui_ThreadSafeQuestionConn = uiInterface.ThreadSafeQuestion_connect( - noui_ThreadSafeQuestionSuppressed); + noui_ThreadSafeMessageBoxRedirect); + noui_ThreadSafeQuestionConn = + uiInterface.ThreadSafeQuestion_connect(noui_ThreadSafeQuestionRedirect); noui_InitMessageConn = - uiInterface.InitMessage_connect(noui_InitMessageSuppressed); + uiInterface.InitMessage_connect(noui_InitMessageRedirect); } void noui_reconnect() { diff --git a/src/test/CMakeLists.txt b/src/test/CMakeLists.txt --- a/src/test/CMakeLists.txt +++ b/src/test/CMakeLists.txt @@ -74,6 +74,7 @@ add_boost_unit_tests_to_suite(bitcoin test_bitcoin jsonutil.cpp + lib/logging.cpp lib/transaction_utils.cpp main.cpp scriptflags.cpp diff --git a/src/test/lib/logging.h b/src/test/lib/logging.h new file mode 100644 --- /dev/null +++ b/src/test/lib/logging.h @@ -0,0 +1,30 @@ +// Copyright (c) 2019 The Bitcoin Core developers +// Distributed under the MIT software license, see the accompanying +// file COPYING or http://www.opensource.org/licenses/mit-license.php. + +#ifndef BITCOIN_TEST_LIB_LOGGING_H +#define BITCOIN_TEST_LIB_LOGGING_H + +#include + +#include +#include +#include + +class DebugLogHelper { + const std::string m_message; + bool m_found{false}; + std::list>::iterator + m_print_connection; + + void check_found(); + +public: + DebugLogHelper(std::string message); + ~DebugLogHelper() { check_found(); } +}; + +#define ASSERT_DEBUG_LOG(message) \ + DebugLogHelper PASTE2(debugloghelper, __COUNTER__)(message) + +#endif // BITCOIN_TEST_LIB_LOGGING_H diff --git a/src/test/lib/logging.cpp b/src/test/lib/logging.cpp new file mode 100644 --- /dev/null +++ b/src/test/lib/logging.cpp @@ -0,0 +1,32 @@ +// Copyright (c) 2019 The Bitcoin Core developers +// Distributed under the MIT software license, see the accompanying +// file COPYING or http://www.opensource.org/licenses/mit-license.php. + +#include + +#include +#include +#include + +#include + +DebugLogHelper::DebugLogHelper(std::string message) + : m_message{std::move(message)} { + m_print_connection = + LogInstance().PushBackCallback([this](const std::string &s) { + if (m_found) { + return; + } + m_found = s.find(m_message) != std::string::npos; + }); + noui_test_redirect(); +} + +void DebugLogHelper::check_found() { + noui_reconnect(); + LogInstance().DeleteCallback(m_print_connection); + if (!m_found) { + throw std::runtime_error( + strprintf("'%s' not found in debug log\n", m_message)); + } +} diff --git a/src/test/timedata_tests.cpp b/src/test/timedata_tests.cpp --- a/src/test/timedata_tests.cpp +++ b/src/test/timedata_tests.cpp @@ -6,14 +6,15 @@ #include #include +#include #include #include -#include - #include +#include + BOOST_FIXTURE_TEST_SUITE(timedata_tests, BasicTestingSetup) BOOST_AUTO_TEST_CASE(util_MedianFilter) { @@ -58,10 +59,12 @@ // Filter size is 1 + 3 = 4: It is always initialized with a single element // (offset 0) - noui_suppress(); - // filter size 5 - MultiAddTimeData(1, DEFAULT_MAX_TIME_ADJUSTMENT + 1); - noui_reconnect(); + { + ASSERT_DEBUG_LOG( + "Please check that your computer's date and time are correct!"); + // filter size 5 + MultiAddTimeData(1, DEFAULT_MAX_TIME_ADJUSTMENT + 1); + } BOOST_CHECK(GetWarnings("gui").find("clock is wrong") != std::string::npos); diff --git a/src/wallet/test/init_tests.cpp b/src/wallet/test/init_tests.cpp --- a/src/wallet/test/init_tests.cpp +++ b/src/wallet/test/init_tests.cpp @@ -5,11 +5,12 @@ #include #include #include +#include +#include #include #include #include -#include #include #include @@ -36,26 +37,29 @@ BOOST_AUTO_TEST_CASE(walletinit_verify_walletdir_does_not_exist) { SetWalletDir(m_walletdir_path_cases["nonexistent"]); - noui_suppress(); - bool result = m_chain_client->verify(Params()); - noui_reconnect(); - BOOST_CHECK(result == false); + { + ASSERT_DEBUG_LOG("does not exist"); + bool result = m_chain_client->verify(Params()); + BOOST_CHECK(result == false); + } } BOOST_AUTO_TEST_CASE(walletinit_verify_walletdir_is_not_directory) { SetWalletDir(m_walletdir_path_cases["file"]); - noui_suppress(); - bool result = m_chain_client->verify(Params()); - noui_reconnect(); - BOOST_CHECK(result == false); + { + ASSERT_DEBUG_LOG("is not a directory"); + bool result = m_chain_client->verify(Params()); + BOOST_CHECK(result == false); + } } BOOST_AUTO_TEST_CASE(walletinit_verify_walletdir_is_not_relative) { SetWalletDir(m_walletdir_path_cases["relative"]); - noui_suppress(); - bool result = m_chain_client->verify(Params()); - noui_reconnect(); - BOOST_CHECK(result == false); + { + ASSERT_DEBUG_LOG("is a relative path"); + bool result = m_chain_client->verify(Params()); + BOOST_CHECK(result == false); + } } BOOST_AUTO_TEST_CASE(walletinit_verify_walletdir_no_trailing) {