/* * Copyright 2017-present Facebook, Inc. * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ #include #include #include using namespace folly; using std::make_shared; TEST(PrintfTest, printfStyleMacros) { LoggerDB db{LoggerDB::TESTING}; Logger logger{&db, "test"}; auto* category = logger.getCategory(); auto handler = make_shared(); category->addHandler(handler); category->setLevel(LogLevel::DBG, true); Logger foo{&db, "test.foo.bar"}; Logger foobar{&db, "test.foo.bar"}; Logger footest{&db, "test.foo.test"}; Logger footest1234{&db, "test.foo.test.1234"}; Logger other{&db, "test.other"}; db.setLevel("test", LogLevel::ERR); db.setLevel("test.foo", LogLevel::DBG2); db.setLevel("test.foo.test", LogLevel::DBG7); auto& messages = handler->getMessages(); // test.other's effective level should be INFO, so a DBG0 // message to it should be discarded FB_LOGC(other, DBG0, "this should be discarded: %d", 5); ASSERT_EQ(0, messages.size()); // Disabled log messages should not evaluate their arguments bool argumentEvaluated = false; auto getValue = [&] { argumentEvaluated = true; return 5; }; FB_LOGC(foobar, DBG3, "discarded message: %d", getValue()); EXPECT_FALSE(argumentEvaluated); FB_LOGC(foobar, DBG1, "this message should pass: %d", getValue()); ASSERT_EQ(1, messages.size()); EXPECT_EQ("this message should pass: 5", messages[0].first.getMessage()); EXPECT_TRUE(argumentEvaluated); messages.clear(); // The FB_LOGC() macro should work even if the format string does not contain // any format sequences. Ideally people would just use FB_LOG() if they // aren't actually formatting anything, but making FB_LOGC() work in this // scenario still makes it easier for people to switch legacy printf-style // code to FB_LOGC(). FB_LOGC(foobar, DBG1, "no actual format arguments"); ASSERT_EQ(1, messages.size()); EXPECT_EQ("no actual format arguments", messages[0].first.getMessage()); messages.clear(); // Similar checks with XLOGC() auto* xlogCategory = XLOG_GET_CATEGORY(); xlogCategory->addHandler(handler); xlogCategory->setLevel(LogLevel::DBG5, true); argumentEvaluated = false; XLOGC(DBG9, "failing log check: %d", getValue()); EXPECT_FALSE(argumentEvaluated); XLOGC(DBG5, "passing log: %03d", getValue()); ASSERT_EQ(1, messages.size()); EXPECT_EQ("passing log: 005", messages[0].first.getMessage()); EXPECT_TRUE(argumentEvaluated); messages.clear(); XLOGC(DBG1, "no xlog format arguments"); ASSERT_EQ(1, messages.size()); EXPECT_EQ("no xlog format arguments", messages[0].first.getMessage()); messages.clear(); XLOGC_IF(DBG1, false, "no xlog format arguments"); ASSERT_EQ(0, messages.size()); XLOGC_IF(DBG1, true, "xlog format arguments"); ASSERT_EQ(1, messages.size()); messages.clear(); argumentEvaluated = false; XLOGC_IF(DBG1, true, "xlog format string %d", getValue()); ASSERT_EQ(1, messages.size()); EXPECT_TRUE(argumentEvaluated); messages.clear(); argumentEvaluated = false; XLOGC_IF(DBG1, false, "xlog format string %d", getValue()); ASSERT_EQ(0, messages.size()); EXPECT_FALSE(argumentEvaluated); messages.clear(); // more complex conditional expressions std::array conds = {{false, true}}; for (unsigned i = 0; i < conds.size(); i++) { for (unsigned j = 0; j < conds.size(); j++) { argumentEvaluated = false; XLOGC_IF( DBG1, conds[i] && conds[j], "testing conditional %d", getValue()); EXPECT_EQ((conds[i] && conds[j]) ? 1 : 0, messages.size()); messages.clear(); if (conds[i] && conds[j]) { EXPECT_TRUE(argumentEvaluated); } else { EXPECT_FALSE(argumentEvaluated); } argumentEvaluated = false; XLOGC_IF( DBG1, conds[i] || conds[j], "testing conditional %d", getValue()); EXPECT_EQ((conds[i] || conds[j]) ? 1 : 0, messages.size()); messages.clear(); if (conds[i] || conds[j]) { EXPECT_TRUE(argumentEvaluated); } else { EXPECT_FALSE(argumentEvaluated); } } } XLOGC_IF(DBG1, 0x6 & 0x2, "More conditional 1"); EXPECT_EQ(1, messages.size()); messages.clear(); XLOGC_IF(DBG1, 0x6 | 0x2, "More conditional 2"); EXPECT_EQ(1, messages.size()); messages.clear(); XLOGC_IF(DBG1, 0x6 | 0x2 ? true : false, "More conditional 3"); EXPECT_EQ(1, messages.size()); messages.clear(); XLOGC_IF(DBG1, 0x6 | 0x2 ? true : false, "More conditional 3"); EXPECT_EQ(1, messages.size()); messages.clear(); XLOGC_IF(DBG1, 0x3 & 0x4 ? true : false, "More conditional 4"); EXPECT_EQ(0, messages.size()); messages.clear(); XLOGC_IF(DBG1, false ? true : false, "More conditional 5"); EXPECT_EQ(0, messages.size()); messages.clear(); // Errors attempting to format the message should not throw FB_LOGC(footest1234, ERR, "width overflow: %999999999999999999999d", 5); ASSERT_EQ(1, messages.size()); EXPECT_EQ( "error formatting printf-style log message: " "width overflow: %999999999999999999999d", messages[0].first.getMessage()); messages.clear(); }