// Copyright (c) 2020-2022, NVIDIA CORPORATION. All rights reserved.
//
// NVIDIA CORPORATION and its licensors retain all intellectual property
// and proprietary rights in and to this software, related documentation
// and any modifications thereto. Any use, reproduction, disclosure or
// distribution of this software and related documentation without an express
// license agreement from NVIDIA CORPORATION is strictly prohibited.
//
#include "../common/TestHelpers.h"
#include "../omni.core/ScopedTypeFactory.h"
#include "ExamplePublicHeader.h"
#include <omni/log/ILog.h>
#include <omni/log/LogChannelFilterUtils.h>
#include <carb/dictionary/ISerializer.h>
#include <carb/thread/Util.h>
OMNI_LOG_ADD_CHANNEL(kLogTestChannel, "test.unit.logtest", "Log test message.");
OMNI_LOG_ADD_CHANNEL(kAlphaTestChannel, "test.unit.logtest.alpha", "Log test message (alpha).");
OMNI_LOG_ADD_CHANNEL(kBetaTestChannel, "test.unit.logtest.beta", "Log test message (beta).");
OMNI_LOG_ADD_CHANNEL(kGoodTestChannel, "test.unit.logtest.alpha.good", "Log test message (alpha-good).");
OMNI_LOG_ADD_CHANNEL(kBadTestChannel, "test.unit.logtest.alpha.bad", "Log test message (alpha-bad).");
OMNI_LOG_DEFINE_CHANNEL(kExtraTestChannel, "test.unit.logtest.alpha.extra", "Log test message (alpha-extra).");
OMNI_LOG_ADD_CHANNEL(kLogChannel, "omni.log", "Log system.");
namespace omni
{
using namespace omni::core;
using namespace omni::log;
} // namespace omni
struct Message
{
std::string msg;
omni::log::Level level;
};
class MessageConsumer : public omni::core::Implements<omni::log::ILogMessageConsumer>
{
public:
MessageConsumer(const char* channel, std::vector<Message> truth) : m_channel{ channel }, m_truth{ std::move(truth) }
{
}
~MessageConsumer()
{
CHECK(m_truth.empty());
}
protected:
void onMessage_abi(const char* channel,
omni::log::Level level,
const char* moduleName,
const char* fileName,
const char* functionName,
uint32_t lineNumber,
const char* msg,
uint32_t pid,
uint32_t tid,
uint64_t timestamp) noexcept override
{
if (m_channel == channel)
{
CHECK(!m_truth.empty());
if (m_truth.empty())
return;
auto& desired = m_truth.front();
CHECK(desired.msg == msg);
CHECK(desired.level == level);
m_truth.erase(m_truth.begin());
}
}
private:
std::string m_channel;
std::vector<Message> m_truth;
};
class LogSettingsSaver
{
public:
LogSettingsSaver(omni::log::ILog* log)
{
m_log = log;
m_level = log->getLevel();
m_enabled = log->isEnabled();
}
~LogSettingsSaver()
{
m_log->setEnabled(m_enabled);
m_log->setLevel(m_level);
}
private:
omni::log::ILog* m_log;
omni::log::Level m_level;
bool m_enabled;
};
static uint32_t countMatchingConsumers(omni::log::ILog* log, omni::log::ILogMessageConsumer* consumer)
{
auto consumers = log->getMessageConsumers();
uint32_t foundCount = 0;
for (auto& c : consumers)
{
if (c == consumer)
{
++foundCount;
}
}
return foundCount;
}
static uint32_t countMatchingChannels(omni::log::ILog* log, const char* channel)
{
auto channels = log->getChannelNames();
uint32_t foundCount = 0;
for (auto& c : channels)
{
if (0 == std::strcmp(c->getBuffer(), channel))
{
++foundCount;
}
}
return foundCount;
}
TEST_CASE("omni::log::ILog::addMessageConsumer()", "ncournia", "[omni::log::ILog]")
{
test::ScopedTypeFactory scopedFactory;
auto log = omniGetLogWithoutAcquire();
REQUIRE(log);
LogSettingsSaver logSettingsSaver{ log };
log->setEnabled(false);
REQUIRE(!log->isEnabled());
// set/get channel level
log->setChannelLevel(kLogTestChannel, omni::log::Level::eInfo, omni::log::SettingBehavior::eOverride);
constexpr omni::log::Level bogusObservedLevel = omni::log::Level(0xBADC0DE);
constexpr omni::log::SettingBehavior bogusObservedBehavior = omni::log::SettingBehavior(0xBADC0DF);
omni::log::Level observedLevel;
omni::log::SettingBehavior observedBehavior;
REQUIRE(omni::kResultSuccess == log->getChannelLevel(kLogTestChannel, &observedLevel, &observedBehavior));
REQUIRE(omni::log::Level::eInfo == observedLevel);
REQUIRE(omni::log::SettingBehavior::eOverride == observedBehavior);
observedLevel = bogusObservedLevel;
observedBehavior = bogusObservedBehavior;
REQUIRE(omni::kResultNotFound == log->getChannelLevel("bogus.channel", &observedLevel, &observedBehavior));
REQUIRE(bogusObservedLevel == observedLevel);
REQUIRE(bogusObservedBehavior == observedBehavior);
REQUIRE(omni::kResultInvalidArgument == log->getChannelLevel(nullptr, &observedLevel, &observedBehavior));
REQUIRE(bogusObservedLevel == observedLevel);
REQUIRE(bogusObservedBehavior == observedBehavior);
REQUIRE(omni::kResultInvalidArgument == log->getChannelLevel("bogus.channel", nullptr, &observedBehavior));
REQUIRE(bogusObservedLevel == observedLevel);
REQUIRE(bogusObservedBehavior == observedBehavior);
REQUIRE(omni::kResultInvalidArgument == log->getChannelLevel("bogus.channel", &observedLevel, nullptr));
REQUIRE(bogusObservedLevel == observedLevel);
REQUIRE(bogusObservedBehavior == observedBehavior);
// set/get channel enabled
log->setChannelEnabled(kLogTestChannel, true, omni::log::SettingBehavior::eOverride);
bool observedEnabled = false;
REQUIRE(omni::kResultSuccess == log->getChannelEnabled(kLogTestChannel, &observedEnabled, &observedBehavior));
REQUIRE(observedEnabled);
REQUIRE(omni::log::SettingBehavior::eOverride == observedBehavior);
observedEnabled = false;
observedBehavior = bogusObservedBehavior;
REQUIRE(omni::kResultNotFound == log->getChannelEnabled("bogus.channel", &observedEnabled, &observedBehavior));
REQUIRE(!observedEnabled);
REQUIRE(bogusObservedBehavior == observedBehavior);
REQUIRE(omni::kResultInvalidArgument == log->getChannelEnabled(nullptr, &observedEnabled, &observedBehavior));
REQUIRE(!observedEnabled);
REQUIRE(bogusObservedBehavior == observedBehavior);
REQUIRE(omni::kResultInvalidArgument == log->getChannelEnabled("bogus.channel", nullptr, &observedBehavior));
REQUIRE(!observedEnabled);
REQUIRE(bogusObservedBehavior == observedBehavior);
REQUIRE(omni::kResultInvalidArgument == log->getChannelEnabled("bogus.channel", &observedEnabled, nullptr));
REQUIRE(!observedEnabled);
REQUIRE(bogusObservedBehavior == observedBehavior);
// add/removeMessageConsumer
omni::ObjectPtr<omni::log::ILogMessageConsumer> consumer{
new MessageConsumer{ kLogTestChannel.name,
{
{ "Test: Error msg", omni::log::Level::eError },
{ "Test: Fatal msg, 6", omni::log::Level::eFatal },
{ "Test: Info msg 3 hi 0x0000002A", omni::log::Level::eInfo },
{ "Test: This is a message.", omni::log::Level::eInfo },
} },
omni::kSteal
};
log->addMessageConsumer(consumer);
REQUIRE(1 == countMatchingConsumers(log, consumer.get()));
OMNI_LOG_ERROR(kLogTestChannel, "Test: %s msg", "Error");
OMNI_LOG_FATAL(kLogTestChannel, "Test: Fatal msg, %d", 6);
OMNI_LOG_INFO(kLogTestChannel, "Test: Info msg %d %s 0x%08X", 3, "hi", 42);
OMNI_LOG_VERBOSE(kLogTestChannel, "Test: I'm an ignored verbose message.");
constexpr const char* statement = "Test: This is a message.";
log->log(kLogTestChannel.name, omni::log::Level::eInfo, omniGetModuleFilename(), __FILE__, __func__, __LINE__,
statement, uint32_t(std::strlen(statement)));
log->removeMessageConsumer(consumer.get());
REQUIRE(0 == countMatchingConsumers(log, consumer.get()));
}
constexpr const char* kAddChannelDesc = "Channel for testing ILog::addChannel.";
OMNI_LOG_DEFINE_CHANNEL(kAddChannel, "test.unit.logtest.addChannel", kAddChannelDesc);
TEST_CASE("omni::log::ILog::addChannel()", "ncournia", "[omni::log::ILog]")
{
test::ScopedTypeFactory scopedFactory;
auto log = omniGetLogWithoutAcquire();
REQUIRE(log);
log->setEnabled(false);
REQUIRE(!log->isEnabled());
omni::core::ObjectPtr<omni::str::IReadOnlyCString> desc;
// addChannel
REQUIRE(0 == countMatchingChannels(log, kAddChannel.name));
log->addChannel(kAddChannel.name, reinterpret_cast<omni::log::Level*>(&(kAddChannel.level)), nullptr);
REQUIRE(1 == countMatchingChannels(log, kAddChannel.name));
// null channel description
REQUIRE(omni::core::kResultSuccess == log->getChannelDescription(kAddChannel.name, desc.put()));
REQUIRE(!desc.get());
// channel with description
omni::log::Level level;
log->addChannel(kAddChannel.name, &level, kAddChannelDesc);
REQUIRE(omni::core::kResultSuccess == log->getChannelDescription(kAddChannel.name, desc.put()));
REQUIRE(desc.get());
REQUIRE(desc->getBuffer());
REQUIRE(0 == std::strcmp(desc->getBuffer(), kAddChannelDesc));
log->removeChannel(kAddChannel.name, &level);
// get description of bogus channel
desc = nullptr;
REQUIRE(omni::core::kResultNotFound == log->getChannelDescription("bogus.channel", desc.put()));
// log to channel
log->setChannelLevel(kAddChannel, omni::log::Level::eInfo, omni::log::SettingBehavior::eOverride);
log->setChannelEnabled(kAddChannel, true, omni::log::SettingBehavior::eOverride);
omni::ObjectPtr<omni::log::ILogMessageConsumer> consumer{ new MessageConsumer{
kAddChannel.name,
{
{ "Test: Info msg", omni::log::Level::eInfo },
} },
omni::kSteal };
log->addMessageConsumer(consumer);
OMNI_LOG_INFO(kAddChannel, "Test: Info msg");
OMNI_LOG_VERBOSE(kAddChannel, "Test: I'm an ignored verbose message.");
log->removeChannel(kAddChannel.name, reinterpret_cast<omni::log::Level*>(&(kAddChannel.level)));
log->removeMessageConsumer(consumer.get());
}
TEST_CASE("omni::log::ILog::setChannelLevel() adds missing channel", "ncournia", "[omni::log::ILog]")
{
test::ScopedTypeFactory scopedFactory;
auto log = omniGetLogWithoutAcquire();
REQUIRE(log);
REQUIRE(0 == countMatchingChannels(log, kAddChannel.name));
log->setChannelLevel(kAddChannel, omni::log::Level::eInfo, omni::log::SettingBehavior::eOverride);
REQUIRE(1 == countMatchingChannels(log, kAddChannel.name));
log->addChannel(kAddChannel.name, reinterpret_cast<omni::log::Level*>(&(kAddChannel.level)), nullptr);
omni::log::Level level = omni::log::Level(0xBADC0DE);
omni::log::SettingBehavior levelBehavior = omni::log::SettingBehavior(0xBADC0DF);
REQUIRE(omni::core::kResultSuccess == log->getChannelLevel(kAddChannel, &level, &levelBehavior));
REQUIRE(omni::log::Level::eInfo == level);
REQUIRE(omni::log::SettingBehavior::eOverride == levelBehavior);
}
TEST_CASE("omni::log::ILog::setChannelEnabled() adds missing channel", "ncournia", "[omni::log::ILog]")
{
test::ScopedTypeFactory scopedFactory;
auto log = omniGetLogWithoutAcquire();
REQUIRE(log);
REQUIRE(0 == countMatchingChannels(log, kAddChannel.name));
log->setChannelEnabled(kAddChannel, true, omni::log::SettingBehavior::eOverride);
REQUIRE(1 == countMatchingChannels(log, kAddChannel.name));
log->addChannel(kAddChannel.name, reinterpret_cast<omni::log::Level*>(&(kAddChannel.level)), nullptr);
bool enabled = false;
omni::log::SettingBehavior enabledBehavior = omni::log::SettingBehavior(0xBADC0DE);
REQUIRE(omni::core::kResultSuccess == log->getChannelEnabled(kAddChannel, &enabled, &enabledBehavior));
REQUIRE(enabled);
REQUIRE(omni::log::SettingBehavior::eOverride == enabledBehavior);
}
TEST_CASE("add channel callback", "ncournia", "[omni::log::ILog]")
{
test::ScopedTypeFactory scopedFactory;
auto log = omniGetLogWithoutAcquire();
REQUIRE(log);
uint32_t updateCount = 0;
auto consumer = log->addChannelUpdateConsumer(
[&updateCount](omni::log::ILog* log, omni::str::IReadOnlyCString* name, omni::log::ChannelUpdateReason reason) {
if (omni::log::ChannelUpdateReason::eChannelAdded != reason)
{
return;
}
CHECK(log);
CHECK(0 == std::strcmp(name->getBuffer(), "just.added.channel"));
++updateCount;
// call back into the log (this shouldn't deadlock)
log->isEnabled();
// spawn a thread to access the log. this shouldn't deadlock
auto thread = std::thread([&]() {
log->getLevel(); // shouldn't deadlock
});
thread.join();
});
REQUIRE(0 == updateCount);
log->setChannelEnabled("just.added.channel", true, omni::log::SettingBehavior::eOverride);
REQUIRE(1 == updateCount);
log->removeChannelUpdateConsumer(consumer);
}
TEST_CASE("log channel updated callback", "ncournia", "[omni::log::ILog]")
{
test::ScopedTypeFactory scopedFactory;
auto log = omniGetLogWithoutAcquire();
REQUIRE(log);
uint32_t updateCount = 0;
auto consumer = log->addChannelUpdateConsumer(
[&updateCount](omni::log::ILog* log, omni::str::IReadOnlyCString* name, omni::log::ChannelUpdateReason reason) {
if ((omni::log::ChannelUpdateReason::eLevelUpdated != reason) &&
(omni::log::ChannelUpdateReason::eEnabledUpdated != reason))
{
return;
}
CHECK(log);
CHECK(0 == std::strcmp(name->getBuffer(), kAddChannel.name));
bool enabled;
omni::log::SettingBehavior enabledBehavior;
omni::log::Level level;
omni::log::SettingBehavior levelBehavior;
// call back into the log (this shouldn't deadlock)
CHECK(omni::core::kResultSuccess == log->getChannelEnabled(name->getBuffer(), &enabled, &enabledBehavior));
CHECK(omni::core::kResultSuccess == log->getChannelLevel(name->getBuffer(), &level, &levelBehavior));
if (0 == updateCount)
{
CHECK(level == omni::log::Level::eWarn);
CHECK(levelBehavior == omni::log::SettingBehavior::eInherit);
CHECK(enabled);
CHECK(enabledBehavior == omni::log::SettingBehavior::eOverride);
}
else
{
CHECK(level == omni::log::Level::eVerbose);
CHECK(levelBehavior == omni::log::SettingBehavior::eOverride);
CHECK(enabled);
CHECK(enabledBehavior == omni::log::SettingBehavior::eOverride);
}
++updateCount;
// spawn a thread to access the log. this shouldn't deadlock
auto thread = std::thread([&]() {
log->getLevel(); // shouldn't deadlock
});
thread.join();
});
log->addChannel(kAddChannel.name, reinterpret_cast<omni::log::Level*>(&(kAddChannel.level)), nullptr);
REQUIRE(0 == updateCount);
log->setChannelEnabled(kAddChannel, true, omni::log::SettingBehavior::eOverride);
REQUIRE(1 == updateCount);
log->setChannelEnabled(kAddChannel, true, omni::log::SettingBehavior::eOverride); // no change
REQUIRE(1 == updateCount);
log->setChannelLevel(kAddChannel, omni::log::Level::eVerbose, omni::log::SettingBehavior::eOverride);
REQUIRE(2 == updateCount);
log->setChannelLevel(kAddChannel, omni::log::Level::eVerbose, omni::log::SettingBehavior::eOverride); // change
REQUIRE(2 == updateCount);
log->removeChannelUpdateConsumer(consumer);
}
static carb::settings::ISettings* parseConfig(const char* config)
{
auto* f = carb::getFramework();
REQUIRE(f);
carb::dictionary::IDictionary* dict = f->acquireInterface<carb::dictionary::IDictionary>();
REQUIRE(dict);
carb::settings::ISettings* settings = f->acquireInterface<carb::settings::ISettings>();
REQUIRE(settings);
carb::dictionary::ISerializer* serializer =
f->acquireInterface<carb::dictionary::ISerializer>("carb.dictionary.serializer-json.plugin");
REQUIRE(serializer);
carb::dictionary::Item* dictItem = serializer->createDictionaryFromStringBuffer(config);
REQUIRE(dictItem);
settings->initializeFromDictionary(dictItem);
dict->destroyItem(dictItem);
return settings;
}
TEST_CASE("channel wildcards in config files",
"ncournia",
"[omni::log::ILogChannelFilter][omni::log::ILogChannelFilterList]")
{
test::ScopedTypeFactory scopedFactory;
scopedFactory.loadCarbPlugins(
{ "carb.dictionary.plugin", "carb.settings.plugin", "carb.dictionary.serializer-json.plugin" });
auto log = omniGetLogWithoutAcquire();
REQUIRE(log);
SECTION("simple config")
{
const char* kConfigStr = R"(
{
"log": {
"channels": {
"test.unit.logtest" : "verbose"
}
}
})";
omni::log::configureLogChannelFilterList(parseConfig(kConfigStr));
omni::ObjectPtr<omni::log::ILogMessageConsumer> consumer{
new MessageConsumer{ kLogTestChannel.name,
{
{ "Test: Error msg", omni::log::Level::eError },
{ "Test: Fatal msg", omni::log::Level::eFatal },
{ "Test: Info msg", omni::log::Level::eInfo },
{ "Test: Verbose msg", omni::log::Level::eVerbose },
} },
omni::kSteal
};
log->addMessageConsumer(consumer);
OMNI_LOG_ERROR(kLogTestChannel, "Test: Error msg");
OMNI_LOG_FATAL(kLogTestChannel, "Test: Fatal msg");
OMNI_LOG_INFO(kLogTestChannel, "Test: Info msg");
OMNI_LOG_VERBOSE(kLogTestChannel, "Test: Verbose msg");
log->removeMessageConsumer(consumer);
}
SECTION("config with fatal level")
{
const char* kConfigStr = R"(
{
"log": {
"channels": {
"test.unit.logtest" : "fatal"
}
}
})";
omni::log::configureLogChannelFilterList(parseConfig(kConfigStr));
omni::ObjectPtr<omni::log::ILogMessageConsumer> consumer{
new MessageConsumer{ kLogTestChannel.name,
{
{ "Test: Fatal msg", omni::log::Level::eFatal },
} },
omni::kSteal
};
log->addMessageConsumer(consumer);
OMNI_LOG_ERROR(kLogTestChannel, "Test: Error msg");
OMNI_LOG_FATAL(kLogTestChannel, "Test: Fatal msg");
OMNI_LOG_INFO(kLogTestChannel, "Test: Info msg");
OMNI_LOG_VERBOSE(kLogTestChannel, "Test: Verbose msg");
log->removeMessageConsumer(consumer);
}
SECTION("disable one subchannel")
{
const char* kConfigStr = R"(
{
"log": {
"channels": {
"test.unit.logtest.alpha.bad" : "disable",
"test.unit.logtest.alpha.*" : "info"
}
}
})";
omni::log::configureLogChannelFilterList(parseConfig(kConfigStr));
omni::ObjectPtr<omni::log::ILogMessageConsumer> goodConsumer{
new MessageConsumer{ kGoodTestChannel.name,
{
{ "Test: Error msg", omni::log::Level::eError },
{ "Test: Fatal msg", omni::log::Level::eFatal },
{ "Test: Info msg", omni::log::Level::eInfo },
} },
omni::kSteal
};
log->addMessageConsumer(goodConsumer);
omni::ObjectPtr<omni::log::ILogMessageConsumer> badConsumer{ new MessageConsumer{ kBadTestChannel.name, {} },
omni::kSteal };
log->addMessageConsumer(badConsumer);
omni::ObjectPtr<omni::log::ILogMessageConsumer> extraConsumer{
new MessageConsumer{ kGoodTestChannel.name,
{
{ "Test: Error msg", omni::log::Level::eError },
{ "Test: Fatal msg", omni::log::Level::eFatal },
{ "Test: Info msg", omni::log::Level::eInfo },
} },
omni::kSteal
};
log->addMessageConsumer(extraConsumer);
// channel should pick up "Info" level from "test.unit.logtest.alpha.*"
log->addChannel(kExtraTestChannel.name, reinterpret_cast<omni::log::Level*>(&(kExtraTestChannel.level)),
kExtraTestChannel.description);
OMNI_LOG_ERROR(kGoodTestChannel, "Test: Error msg");
OMNI_LOG_FATAL(kGoodTestChannel, "Test: Fatal msg");
OMNI_LOG_INFO(kGoodTestChannel, "Test: Info msg");
OMNI_LOG_VERBOSE(kGoodTestChannel, "Test: Verbose msg");
OMNI_LOG_ERROR(kBadTestChannel, "Test: Error msg");
OMNI_LOG_FATAL(kBadTestChannel, "Test: Fatal msg");
OMNI_LOG_INFO(kBadTestChannel, "Test: Info msg");
OMNI_LOG_VERBOSE(kBadTestChannel, "Test: Verbose msg");
OMNI_LOG_ERROR(kExtraTestChannel, "Test: Error msg");
OMNI_LOG_FATAL(kExtraTestChannel, "Test: Fatal msg");
OMNI_LOG_INFO(kExtraTestChannel, "Test: Info msg");
OMNI_LOG_VERBOSE(kExtraTestChannel, "Test: Verbose msg");
log->removeMessageConsumer(extraConsumer);
log->removeMessageConsumer(badConsumer);
log->removeMessageConsumer(goodConsumer);
}
}
bool checkFilterList(omni::log::ILogChannelFilterList* list, const std::vector<std::string>& truth)
{
auto filters = list->getFilters();
if (filters.size() != truth.size())
{
return false;
}
for (size_t i = 0; i < truth.size(); ++i)
{
if (truth[i] != filters[i]->getFilter())
{
return false;
}
}
return true;
}
omni::core::ObjectPtr<omni::log::ILogChannelFilter> getFilterAt(omni::log::ILogChannelFilterList* list, uint32_t index)
{
auto filters = list->getFilters();
REQUIRE(index < filters.size());
return filters[index];
}
TEST_CASE("ILogChanneFilterList manipulation", "ncournia", "[omni::log::ILogChannelFilterList]")
{
test::ScopedTypeFactory scopedFactory;
auto list = omni::core::createType<omni::log::ILogChannelFilterList>();
// add to end of list
list->append(omni::log::WildcardLogChannelFilter::create("a"));
list->append(omni::log::WildcardLogChannelFilter::create("b"));
list->append(omni::log::WildcardLogChannelFilter::create("d"));
list->append(omni::log::WildcardLogChannelFilter::create("e"));
REQUIRE(checkFilterList(list.get(), { "a", "b", "d", "e" }));
// add to middle of list
list->insert(2, omni::log::WildcardLogChannelFilter::create("c"));
REQUIRE(checkFilterList(list.get(), { "a", "b", "c", "d", "e" }));
// remove an item in middle of list
auto b = getFilterAt(list.get(), 1);
REQUIRE(OMNI_SUCCEEDED(list->remove(1, b)));
REQUIRE(checkFilterList(list.get(), { "a", "c", "d", "e" }));
// remove an item that has moved
REQUIRE(omni::core::kResultInvalidState == list->remove(1, b));
REQUIRE(checkFilterList(list.get(), { "a", "c", "d", "e" }));
// replace an item
auto d = getFilterAt(list.get(), 2);
REQUIRE(OMNI_SUCCEEDED(list->replace(2, d, omni::log::WildcardLogChannelFilter::create("D"))));
REQUIRE(checkFilterList(list.get(), { "a", "c", "D", "e" }));
// replace an item that has moved
REQUIRE(omni::core::kResultInvalidState == list->replace(2, d, omni::log::WildcardLogChannelFilter::create("D")));
REQUIRE(checkFilterList(list.get(), { "a", "c", "D", "e" }));
// remove invalid index item
auto e = getFilterAt(list.get(), 3);
REQUIRE(omni::core::kResultInvalidState == list->remove(4, e));
REQUIRE(checkFilterList(list.get(), { "a", "c", "D", "e" }));
// remove from end
REQUIRE(OMNI_SUCCEEDED(list->remove(3, e)));
REQUIRE(checkFilterList(list.get(), { "a", "c", "D" }));
// add duplicate
auto c = getFilterAt(list.get(), 1);
list->insert(1, c);
REQUIRE(checkFilterList(list.get(), { "a", "c", "c", "D" }));
// clear
list->clear();
REQUIRE(checkFilterList(list.get(), {}));
}
EXAMPLE_ADD_CHANNELS();
TEST_CASE("redefine OMNI_LOG_DEFAULT_CHANNEL", "ncournia", "[omni::log::ILog]")
{
test::ScopedTypeFactory scopedFactory;
auto log = omniGetLogWithoutAcquire();
log->setLevel(omni::log::Level::eInfo);
carb::thread::ThreadId expectedTid = carb::this_thread::getId();
carb::process::ProcessId expectedPid = carb::this_process::getId();
uint64_t minTimeStamp =
std::chrono::duration_cast<std::chrono::nanoseconds>(std::chrono::high_resolution_clock::now().time_since_epoch())
.count();
uint32_t messagesSeen = 0;
log->addMessageConsumer([&messagesSeen, expectedTid, expectedPid, minTimeStamp](
const char* channel, omni::log::Level level, const char* moduleName,
const char* fileName, const char* functionName, uint32_t lineNumber, const char* msg,
uint32_t pid, uint32_t tid, uint64_t timestamp) noexcept {
// clang complains about the 'name' value for kDefaultChannel not being defined
// immediately here in this translation unit. It is defined elsewhere so the
// linking will be correct still. The other channels are defined locally here
// so those aren't complained about.
CARB_IGNOREWARNING_CLANG_WITH_PUSH("-Wundefined-var-template")
if (0 == std::strcmp(channel, kAlphaTestChannel.name))
{
CHECK(0 == std::strcmp("i'm logged to the alpha channel", msg));
++messagesSeen;
}
else if (0 == std::strcmp(channel, kBetaTestChannel.name))
{
CHECK(0 == std::strcmp("i'm logged to the beta channel", msg));
++messagesSeen;
}
else if (0 == std::strcmp(channel, kDefaultChannel.name))
{
CHECK(0 == std::strcmp("i'm logged to the previous default channel", msg));
++messagesSeen;
}
else if (0 == std::strcmp(channel, kExampleUsefulChannel.name))
{
CHECK(0 == std::strcmp("i'm helping", msg));
++messagesSeen;
}
else if (0 == std::strcmp(channel, kExampleUselessChannel.name))
{
CHECK(0 == std::strcmp("i'm not helping", msg));
++messagesSeen;
}
CHECK(pid == expectedPid);
CHECK(tid == expectedTid);
CHECK(timestamp >= minTimeStamp);
CARB_IGNOREWARNING_CLANG_POP
});
// example-begin redefine-default-channel
OMNI_LOG_ERROR("i'm logged to the previous default channel");
#undef OMNI_LOG_DEFAULT_CHANNEL
#define OMNI_LOG_DEFAULT_CHANNEL kAlphaTestChannel
OMNI_LOG_ERROR("i'm logged to the alpha %s", "channel");
#undef OMNI_LOG_DEFAULT_CHANNEL
#define OMNI_LOG_DEFAULT_CHANNEL kBetaTestChannel
OMNI_LOG_ERROR("i'm logged to the beta channel");
// example-end redefine-default-channel
example::myUsefulFunction();
example::myUselessFunction();
REQUIRE(5 == messagesSeen);
}
static uint32_t sConstructionCounterCount = 0;
struct ConstructionCounter
{
ConstructionCounter()
{
++sConstructionCounterCount;
}
const char* txt()
{
return "i'm expensive";
}
};
TEST_CASE("lazy evaluation of log macro arguments", "ncournia", "[omni::log::ILog]")
{
test::ScopedTypeFactory scopedFactory;
auto log = omniGetLogWithoutAcquire();
sConstructionCounterCount = 0;
OMNI_LOG_ERROR("%s", ConstructionCounter().txt());
REQUIRE(1 == sConstructionCounterCount);
sConstructionCounterCount = 0;
OMNI_LOG_VERBOSE("%s", ConstructionCounter().txt());
REQUIRE(0 == sConstructionCounterCount);
log->setChannelLevel(kAlphaTestChannel, omni::log::Level::eWarn, omni::log::SettingBehavior::eInherit);
sConstructionCounterCount = 0;
OMNI_LOG_ERROR(kAlphaTestChannel, "%s", ConstructionCounter().txt());
REQUIRE(1 == sConstructionCounterCount);
sConstructionCounterCount = 0;
OMNI_LOG_VERBOSE(kAlphaTestChannel, "%s", ConstructionCounter().txt());
REQUIRE(0 == sConstructionCounterCount);
}
TEST_CASE("log macro scoping", "evanbeurden", "[omni::log::ILog][scoping]")
{
test::ScopedTypeFactory scopedFactory;
auto log = omniGetLogWithoutAcquire();
int32_t value = rand();
log->setLevel(omni::log::Level::eVerbose);
omni::ObjectPtr<omni::log::ILogMessageConsumer> consumer{ new MessageConsumer{ kLogTestChannel.name, {} },
omni::kSteal };
log->addMessageConsumer(consumer);
CHECK(countMatchingConsumers(log, consumer.get()) == 1);
if (value < 0)
OMNI_LOG_ERROR(kLogTestChannel, "Test: %s msg", "Error");
if (value < 0)
OMNI_LOG_FATAL(kLogTestChannel, "Test: Fatal msg, %d", 6);
if (value < 0)
OMNI_LOG_WARN(kLogTestChannel, "Test: Warning msg, %d", 2);
if (value < 0)
OMNI_LOG_INFO(kLogTestChannel, "Test: Info msg %d %s 0x%08X", 3, "hi", 42);
if (value < 0)
OMNI_LOG_VERBOSE(kLogTestChannel, "Test: I'm an ignored verbose message.");
log->removeMessageConsumer(consumer.get());
CHECK(0 == countMatchingConsumers(log, consumer.get()));
}