// Copyright (C) 2011-2022 Internet Systems Consortium, Inc. ("ISC") // // This Source Code Form is subject to the terms of the Mozilla Public // License, v. 2.0. If a copy of the MPL was not distributed with this // file, You can obtain one at http://mozilla.org/MPL/2.0/. #include #include #include #include #include #include #include #include using namespace std; namespace { class FormatterTest : public ::testing::Test { protected: typedef pair Output; typedef isc::log::Formatter Formatter; vector outputs; public: void output(const isc::log::Severity& prefix, const string& message) { outputs.push_back(Output(prefix, message)); } // Just shortcut for new string boost::shared_ptr s(const char* text) { return (boost::make_shared(text)); } }; // Create an inactive formatter and check it doesn't produce any output TEST_F(FormatterTest, inactive) { Formatter(); EXPECT_EQ(0, outputs.size()); } // Create an active formatter and check it produces output. Does no arg // substitution yet TEST_F(FormatterTest, active) { Formatter(isc::log::INFO, s("Text of message"), this); ASSERT_EQ(1, outputs.size()); EXPECT_EQ(isc::log::INFO, outputs[0].first); EXPECT_EQ("Text of message", outputs[0].second); } // No output even when we have an arg on the inactive formatter TEST_F(FormatterTest, inactiveArg) { Formatter().arg("Hello"); EXPECT_EQ(0, outputs.size()); } // Create an active formatter and replace a placeholder with string TEST_F(FormatterTest, stringArg) { { SCOPED_TRACE("C++ string"); Formatter(isc::log::INFO, s("Hello %1"), this).arg(string("World")); ASSERT_EQ(1, outputs.size()); EXPECT_EQ(isc::log::INFO, outputs[0].first); EXPECT_EQ("Hello World", outputs[0].second); } { SCOPED_TRACE("C++ string"); Formatter(isc::log::INFO, s("Hello %1"), this).arg(string("Internet")); ASSERT_EQ(2, outputs.size()); EXPECT_EQ(isc::log::INFO, outputs[1].first); EXPECT_EQ("Hello Internet", outputs[1].second); } } // Test the .deactivate() method TEST_F(FormatterTest, deactivate) { Formatter(isc::log::INFO, s("Text of message"), this).deactivate(); // If there was no .deactivate, it should have output it. // But not now. ASSERT_EQ(0, outputs.size()); } // Can convert to string TEST_F(FormatterTest, intArg) { Formatter(isc::log::INFO, s("The answer is %1"), this).arg(42); ASSERT_EQ(1, outputs.size()); EXPECT_EQ(isc::log::INFO, outputs[0].first); EXPECT_EQ("The answer is 42", outputs[0].second); } // Can use multiple arguments at different places TEST_F(FormatterTest, multiArg) { Formatter(isc::log::INFO, s("The %2 are %1"), this).arg("switched"). arg("arguments"); ASSERT_EQ(1, outputs.size()); EXPECT_EQ(isc::log::INFO, outputs[0].first); EXPECT_EQ("The arguments are switched", outputs[0].second); } #ifdef ENABLE_LOGGER_CHECKS TEST_F(FormatterTest, mismatchedPlaceholders) { // Throws MismatchedPlaceholders exception if the placeholder is missing // for a supplied argument. EXPECT_THROW(Formatter(isc::log::INFO, s("Missing the second %1"), this). arg("argument").arg("missing"), isc::log::MismatchedPlaceholders); #ifdef EXPECT_DEATH // Likewise, if there's a redundant placeholder (or missing argument), the // check detects it and aborts the program. Due to the restriction of the // current implementation, it doesn't throw. if (!isc::util::unittests::runningOnValgrind()) { EXPECT_DEATH({ isc::util::unittests::dontCreateCoreDumps(); Formatter(isc::log::INFO, s("Too many arguments in %1 %2"), this). arg("only one"); }, ".*"); } #endif /* EXPECT_DEATH */ // Mixed case of above two: the exception will be thrown due to the missing // placeholder. The other check is disabled due to that. EXPECT_THROW(Formatter(isc::log::INFO, s("Missing the first %2"), this). arg("missing").arg("argument"), isc::log::MismatchedPlaceholders); } #else // If logger checks are not enabled, nothing is thrown TEST_F(FormatterTest, mismatchedPlaceholders) { Formatter(isc::log::INFO, s("Missing the second %1"), this). arg("argument").arg("missing"); ASSERT_EQ(1, outputs.size()); EXPECT_EQ(isc::log::INFO, outputs[0].first); EXPECT_EQ("Missing the second argument " "@@Missing logger placeholder '%2' for value 'missing'@@", outputs[0].second); EXPECT_NO_THROW(Formatter(isc::log::INFO, s("Too many arguments in %1 %2"), this). arg("only one")); ASSERT_EQ(2, outputs.size()); EXPECT_EQ(isc::log::INFO, outputs[1].first); EXPECT_EQ("Too many arguments in only one %2 " "@@Excess logger placeholder '%2' still exists@@", outputs[1].second); EXPECT_NO_THROW(Formatter(isc::log::INFO, s("Missing the first %2"), this). arg("missing").arg("argument")); ASSERT_EQ(3, outputs.size()); EXPECT_EQ(isc::log::INFO, outputs[2].first); EXPECT_EQ("Missing the first argument " "@@Missing logger placeholder '%1' for value 'missing'@@", outputs[2].second); } #endif /* ENABLE_LOGGER_CHECKS */ // Can replace multiple placeholders TEST_F(FormatterTest, multiPlaceholder) { Formatter(isc::log::INFO, s("The %1 is the %1"), this). arg("first rule of tautology club"); ASSERT_EQ(1, outputs.size()); EXPECT_EQ(isc::log::INFO, outputs[0].first); EXPECT_EQ("The first rule of tautology club is " "the first rule of tautology club", outputs[0].second); } // Test we can cope with replacement containing the placeholder TEST_F(FormatterTest, noRecurse) { // If we recurse, this will probably eat all the memory and crash Formatter(isc::log::INFO, s("%1"), this).arg("%1 %1"); ASSERT_EQ(1, outputs.size()); EXPECT_EQ(isc::log::INFO, outputs[0].first); EXPECT_EQ("%1 %1", outputs[0].second); } }