// Copyright (c) 2016-2024, The Monero Project // // All rights reserved. // // Redistribution and use in source and binary forms, with or without modification, are // permitted provided that the following conditions are met: // // 1. Redistributions of source code must retain the above copyright notice, this list of // conditions and the following disclaimer. // // 2. Redistributions in binary form must reproduce the above copyright notice, this list // of conditions and the following disclaimer in the documentation and/or other // materials provided with the distribution. // // 3. Neither the name of the copyright holder nor the names of its contributors may be // used to endorse or promote products derived from this software without specific // prior written permission. // // THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND ANY // EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF // MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL // THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, // SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, // PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS // INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, // STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF // THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. // // Parts of this file are originally copyright (c) 2012-2013 The Cryptonote developers #include #include "gtest/gtest.h" #include "file_io_utils.h" #include "misc_log_ex.h" static std::string log_filename; static void init() { boost::filesystem::path p = boost::filesystem::temp_directory_path() / boost::filesystem::unique_path(); log_filename = p.string(); mlog_configure(log_filename, false, 0); } static void cleanup() { // windows does not let files be deleted if still in use, so leave droppings there #ifndef _WIN32 boost::filesystem::remove(log_filename); #endif } static size_t nlines(const std::string &str) { size_t n = 0; for (const char *ptr = str.c_str(); *ptr; ++ptr) if (*ptr == '\n') ++n; return n; } static bool load_log_to_string(const std::string &filename, std::string &str) { if (!epee::file_io_utils::load_file_to_string(filename, str)) return false; for (const char *ptr = str.c_str(); *ptr; ++ptr) { if (*ptr == '\n') { std::string prefix = std::string(str.c_str(), ptr - str.c_str()); if (prefix.find("New log categories:") != std::string::npos) { str = std::string(ptr + 1, strlen(ptr + 1)); break; } } } return true; } static void log() { MFATAL("fatal"); MERROR("error"); MWARNING("warning"); MINFO("info"); MDEBUG("debug"); MTRACE("trace"); MCINFO("a.b.c.d", "a.b.c.d"); MCINFO("a.b.c.e", "a.b.c.e"); MCINFO("global", "global"); MCINFO("x.y.z", "x.y.z"); MCINFO("y.y.z", "y.y.z"); MCINFO("x.y.x", "x.y.x"); } TEST(logging, no_logs) { init(); mlog_set_categories(""); log(); std::string str; ASSERT_TRUE(load_log_to_string(log_filename, str)); ASSERT_TRUE(str == ""); cleanup(); } TEST(logging, default) { init(); log(); std::string str; ASSERT_TRUE(load_log_to_string(log_filename, str)); ASSERT_TRUE(str.find("global") != std::string::npos); ASSERT_TRUE(str.find("fatal") != std::string::npos); ASSERT_TRUE(str.find("error") != std::string::npos); ASSERT_TRUE(str.find("debug") == std::string::npos); ASSERT_TRUE(str.find("trace") == std::string::npos); cleanup(); } TEST(logging, all) { init(); mlog_set_categories("*:TRACE"); log(); std::string str; ASSERT_TRUE(load_log_to_string(log_filename, str)); ASSERT_TRUE(str.find("global") != std::string::npos); ASSERT_TRUE(str.find("fatal") != std::string::npos); ASSERT_TRUE(str.find("error") != std::string::npos); ASSERT_TRUE(str.find("debug") != std::string::npos); ASSERT_TRUE(str.find("trace") != std::string::npos); cleanup(); } TEST(logging, glob_suffix) { init(); mlog_set_categories("x.y*:TRACE"); log(); std::string str; ASSERT_TRUE(load_log_to_string(log_filename, str)); ASSERT_TRUE(str.find("global") == std::string::npos); ASSERT_TRUE(str.find("x.y.z") != std::string::npos); ASSERT_TRUE(str.find("x.y.x") != std::string::npos); ASSERT_TRUE(str.find("y.y.z") == std::string::npos); cleanup(); } TEST(logging, glob_prefix) { init(); mlog_set_categories("*y.z:TRACE"); log(); std::string str; ASSERT_TRUE(load_log_to_string(log_filename, str)); ASSERT_TRUE(str.find("global") == std::string::npos); ASSERT_TRUE(str.find("x.y.z") != std::string::npos); ASSERT_TRUE(str.find("x.y.x") == std::string::npos); ASSERT_TRUE(str.find("y.y.z") != std::string::npos); cleanup(); } TEST(logging, last_precedence) { init(); mlog_set_categories("global:FATAL,glo*:DEBUG"); log(); std::string str; ASSERT_TRUE(load_log_to_string(log_filename, str)); ASSERT_TRUE(nlines(str) == 1); ASSERT_TRUE(str.find("global") != std::string::npos); ASSERT_TRUE(str.find("x.y.z") == std::string::npos); ASSERT_TRUE(str.find("x.y.x") == std::string::npos); ASSERT_TRUE(str.find("y.y.z") == std::string::npos); cleanup(); } TEST(logging, multiline) { init(); mlog_set_categories("global:INFO"); MGINFO("first\nsecond\nthird"); std::string str; ASSERT_TRUE(load_log_to_string(log_filename, str)); ASSERT_TRUE(nlines(str) == 3); ASSERT_TRUE(str.find("global") != std::string::npos); ASSERT_TRUE(str.find("first") != std::string::npos); ASSERT_TRUE(str.find("second") != std::string::npos); ASSERT_TRUE(str.find("third") != std::string::npos); ASSERT_TRUE(str.find("first\nsecond") == std::string::npos); ASSERT_TRUE(str.find("second\nthird") == std::string::npos); cleanup(); } class LoggingTermSupportsColorSuite : public testing::TestWithParam> {}; TEST_P(LoggingTermSupportsColorSuite, Detection) { std::tuple param = GetParam(); auto term = std::get<0>(param); auto is_color = std::get<1>(param); ASSERT_EQ(el::base::utils::OS::termSupportsColor(term), is_color) << term; } INSTANTIATE_TEST_SUITE_P( TerminalStrings, LoggingTermSupportsColorSuite, testing::Values( std::make_tuple("", false), // unrecognized terminals std::make_tuple("basic", false), std::make_tuple("vt100", false), // known color terminals std::make_tuple("xterm", true), std::make_tuple("screen", true), std::make_tuple("linux", true), std::make_tuple("cygwin", true), std::make_tuple("xterm-color", true), std::make_tuple("xterm-256color", true), std::make_tuple("screen-256color", true), std::make_tuple("screen.xterm-256color", true), // generic color terminal detection by suffix std::make_tuple("unrecognized-color", true), std::make_tuple("unrecognized-256color", true), std::make_tuple("basic-nocolor", false), std::make_tuple("basic-no256color", false), std::make_tuple("basic-color-unsupported", false), std::make_tuple("basic-256color-unsupported", false) )); // These operations might segfault TEST(logging, copy_ctor_segfault) { const el::Logger log1("id1", nullptr); const el::Logger log2(log1); } TEST(logging, operator_equals_segfault) { const el::Logger log1("id1", nullptr); el::Logger log2("id2", nullptr); log2 = log1; } TEST(logging, empty_configuration) { el::Logger log1("id1", nullptr); const el::Configurations cfg; EXPECT_NO_THROW(log1.configure(cfg)); EXPECT_EQ(log1.typedConfigurations()->filename(el::Level::Info), ""); }