mirror of
https://github.com/opencv/opencv.git
synced 2024-11-24 03:00:14 +08:00
Merge pull request #13909 from kinchungwong:logging_20190220
OE-11 Logging revamp (#13909) * Initial commit for log tag support. Part of #11003, incomplete. Should pass build. Moved LogLevel enum to logger.defines.hpp LogTag struct used to convey both name and log level threshold as one argument to the new logging macro. See logtag.hpp file, and CV_LOG_WITH_TAG macro. Global log level is now associated with a global log tag, when a logging statement doesn't specify any log tag. See getLogLevel and getGlobalLogTag functions. A macro CV_LOGTAG_FALLBACK is allowed to be re-defined by other modules or compilation units, internally, so that logging statements inside that unit that specify NULL as tag will fall back to the re-defined tag. Line-of-code information (file name, line number, function name), together with tag name, are passed into the new log message sink. See writeLogMessageEx function. Fixed old incorrect CV_LOG_VERBOSE usage in ocl4dnn_conv_spatial.cpp. * Implemented tag-based log filtering Added LogTagManager. This is an initial version, using standard C++ approach as much as possible, to allow easier code review. Will optimize later. A workaround for all static dynamic initialization issues is implemented. Refer to code comments. * Added LogTagConfigParser. Note: new code does not fully handle old log config parsing behavior. * Fix log tag config vs registering ordering issue. * Started testing LogTagConfigParser, incomplete. The intention of this commit is to illustrate the capabilities of the current design of LogTagConfigParser. The test contained in this commit is not complete. Also, design changes may require throwing away this commit and rewriting test code from scratch. Does not test whitespace segmentation (multiple tags on the config); will do in next commit. * Added CV_LOGTAG_EXPAND_NAME macro This macro allows to be re-defined locally in other compilation units to apply a prefix to whatever argument is passed as the "tag" argument into CV_LOG_WITH_TAG. The default definition in logger.hpp does not modify the argument. It is recommended to include the address-of operator (ampersand) when re-defined locally. * Added a few tests for LogTagManager, some fail. See test_logtagmanager.cpp Failed tests are: non-global ("something"), setting level by name-part (first part or any part) has no effect at all. * LogTagManagerTests substring non-confusion tests * Fix major bugs in LogTagManager The code change is intended to approximate the spec documented in https://gist.github.com/kinchungwong/ec25bc1eba99142e0be4509b0f67d0c6 Refer to test suite in test_logtagmanager.cpp Filter test result in "opencv_test_core" ... with gtest_filter "LogTagManager*" To see the test code that finds the bugs, refer to original commits (before rebase; might be gone) .. f3451208 (2019-03-03T19:45:17Z) .... LogTagManagerTests substring non-confusion tests .. 1b848f5f (2019-03-03T01:55:18Z) .... Added a few tests for LogTagManager, some fail. * Added LogTagManagerNamePartNonConfusionTest. See test_logtagmanager.cpp in modules/core/test. * Added LogTagAuto for auto registration in ctor * Rewritten LogTagManager to resolve issues. * Resolves code review issues around 2019-04-10 LogTagConfigParser::parseLogLevel - as part of resolving code review issues, this function is rewritten to simplify control flow and to improve conformance with legacy usage (for string values "OFF", "DISABLED", and "WARNINGS").
This commit is contained in:
parent
f439fc4306
commit
8af96248bf
@ -17,6 +17,26 @@
|
||||
#define CV_LOG_LEVEL_DEBUG 5 //!< Debug message. Disabled in the "Release" build.
|
||||
#define CV_LOG_LEVEL_VERBOSE 6 //!< Verbose (trace) messages. Requires verbosity level. Disabled in the "Release" build.
|
||||
|
||||
namespace cv {
|
||||
namespace utils {
|
||||
namespace logging {
|
||||
|
||||
//! Supported logging levels and their semantic
|
||||
enum LogLevel {
|
||||
LOG_LEVEL_SILENT = 0, //!< for using in setLogVevel() call
|
||||
LOG_LEVEL_FATAL = 1, //!< Fatal (critical) error (unrecoverable internal error)
|
||||
LOG_LEVEL_ERROR = 2, //!< Error message
|
||||
LOG_LEVEL_WARNING = 3, //!< Warning message
|
||||
LOG_LEVEL_INFO = 4, //!< Info message
|
||||
LOG_LEVEL_DEBUG = 5, //!< Debug message. Disabled in the "Release" build.
|
||||
LOG_LEVEL_VERBOSE = 6, //!< Verbose (trace) messages. Requires verbosity level. Disabled in the "Release" build.
|
||||
#ifndef CV_DOXYGEN
|
||||
ENUM_LOG_LEVEL_FORCE_INT = INT_MAX
|
||||
#endif
|
||||
};
|
||||
|
||||
}}} // namespace
|
||||
|
||||
//! @}
|
||||
|
||||
#endif // OPENCV_LOGGER_DEFINES_HPP
|
||||
|
@ -10,6 +10,7 @@
|
||||
#include <limits.h> // INT_MAX
|
||||
|
||||
#include "logger.defines.hpp"
|
||||
#include "logtag.hpp"
|
||||
|
||||
//! @addtogroup core_logging
|
||||
// This section describes OpenCV logging utilities.
|
||||
@ -20,20 +21,6 @@ namespace cv {
|
||||
namespace utils {
|
||||
namespace logging {
|
||||
|
||||
//! Supported logging levels and their semantic
|
||||
enum LogLevel {
|
||||
LOG_LEVEL_SILENT = 0, //!< for using in setLogVevel() call
|
||||
LOG_LEVEL_FATAL = 1, //!< Fatal (critical) error (unrecoverable internal error)
|
||||
LOG_LEVEL_ERROR = 2, //!< Error message
|
||||
LOG_LEVEL_WARNING = 3, //!< Warning message
|
||||
LOG_LEVEL_INFO = 4, //!< Info message
|
||||
LOG_LEVEL_DEBUG = 5, //!< Debug message. Disabled in the "Release" build.
|
||||
LOG_LEVEL_VERBOSE = 6, //!< Verbose (trace) messages. Requires verbosity level. Disabled in the "Release" build.
|
||||
#ifndef CV_DOXYGEN
|
||||
ENUM_LOG_LEVEL_FORCE_INT = INT_MAX
|
||||
#endif
|
||||
};
|
||||
|
||||
/** Set global logging level
|
||||
@return previous logging level
|
||||
*/
|
||||
@ -41,15 +28,39 @@ CV_EXPORTS LogLevel setLogLevel(LogLevel logLevel);
|
||||
/** Get global logging level */
|
||||
CV_EXPORTS LogLevel getLogLevel();
|
||||
|
||||
CV_EXPORTS void registerLogTag(cv::utils::logging::LogTag* plogtag);
|
||||
|
||||
CV_EXPORTS void setLogTagLevel(const char* tag, cv::utils::logging::LogLevel level);
|
||||
|
||||
CV_EXPORTS cv::utils::logging::LogLevel getLogTagLevel(const char* tag);
|
||||
|
||||
namespace internal {
|
||||
|
||||
/** Get global log tag */
|
||||
CV_EXPORTS cv::utils::logging::LogTag* getGlobalLogTag();
|
||||
|
||||
/** Write log message */
|
||||
CV_EXPORTS void writeLogMessage(LogLevel logLevel, const char* message);
|
||||
|
||||
/** Write log message */
|
||||
CV_EXPORTS void writeLogMessageEx(LogLevel logLevel, const char* tag, const char* file, int line, const char* func, const char* message);
|
||||
|
||||
} // namespace
|
||||
|
||||
struct LogTagAuto
|
||||
: public LogTag
|
||||
{
|
||||
inline LogTagAuto(const char* _name, LogLevel _level)
|
||||
: LogTag(_name, _level)
|
||||
{
|
||||
registerLogTag(this);
|
||||
}
|
||||
};
|
||||
|
||||
/**
|
||||
* \def CV_LOG_STRIP_LEVEL
|
||||
*
|
||||
* Define CV_LOG_STRIP_LEVEL=CV_LOG_LEVEL_[DEBUG|INFO|WARN|ERROR|FATAL|DISABLED] to compile out anything at that and before that logging level
|
||||
* Define CV_LOG_STRIP_LEVEL=CV_LOG_LEVEL_[DEBUG|INFO|WARN|ERROR|FATAL|SILENT] to compile out anything at that and before that logging level
|
||||
*/
|
||||
#ifndef CV_LOG_STRIP_LEVEL
|
||||
# if defined NDEBUG
|
||||
@ -59,26 +70,83 @@ CV_EXPORTS void writeLogMessage(LogLevel logLevel, const char* message);
|
||||
# endif
|
||||
#endif
|
||||
|
||||
#define CV_LOGTAG_PTR_CAST(expr) static_cast<const cv::utils::logging::LogTag*>(expr)
|
||||
|
||||
// CV_LOGTAG_EXPAND_NAME is intended to be re-defined (undef and then define again)
|
||||
// to allows logging users to use a shorter name argument when calling
|
||||
// CV_LOG_WITH_TAG or its related macros such as CV_LOG_INFO.
|
||||
//
|
||||
// This macro is intended to modify the tag argument as a string (token), via
|
||||
// preprocessor token pasting or metaprogramming techniques. A typical usage
|
||||
// is to apply a prefix, such as
|
||||
// ...... #define CV_LOGTAG_EXPAND_NAME(tag) cv_logtag_##tag
|
||||
//
|
||||
// It is permitted to re-define to a hard-coded expression, ignoring the tag.
|
||||
// This would work identically like the CV_LOGTAG_FALLBACK macro.
|
||||
//
|
||||
// Important: When the logging macro is called with tag being NULL, a user-defined
|
||||
// CV_LOGTAG_EXPAND_NAME may expand it into cv_logtag_0, cv_logtag_NULL, or
|
||||
// cv_logtag_nullptr. Use with care. Also be mindful of C++ symbol redefinitions.
|
||||
//
|
||||
// If there is significant amount of logging code with tag being NULL, it is
|
||||
// recommended to use (re-define) CV_LOGTAG_FALLBACK to inject locally a default
|
||||
// tag at the beginning of a compilation unit, to minimize lines of code changes.
|
||||
//
|
||||
#define CV_LOGTAG_EXPAND_NAME(tag) tag
|
||||
|
||||
// CV_LOGTAG_FALLBACK is intended to be re-defined (undef and then define again)
|
||||
// by any other compilation units to provide a log tag when the logging statement
|
||||
// does not specify one. The macro needs to expand into a C++ expression that can
|
||||
// be static_cast into (cv::utils::logging::LogTag*). Null (nullptr) is permitted.
|
||||
#define CV_LOGTAG_FALLBACK nullptr
|
||||
|
||||
// CV_LOGTAG_GLOBAL is the tag used when a log tag is not specified in the logging
|
||||
// statement nor the compilation unit. The macro needs to expand into a C++
|
||||
// expression that can be static_cast into (cv::utils::logging::LogTag*). Must be
|
||||
// non-null. Do not re-define.
|
||||
#define CV_LOGTAG_GLOBAL cv::utils::logging::internal::getGlobalLogTag()
|
||||
|
||||
#define CV_LOG_WITH_TAG(tag, msgLevel, ...) \
|
||||
for(;;) { \
|
||||
const auto cv_temp_msglevel = (cv::utils::logging::LogLevel)(msgLevel); \
|
||||
if (cv_temp_msglevel >= (CV_LOG_STRIP_LEVEL)) break; \
|
||||
auto cv_temp_logtagptr = CV_LOGTAG_PTR_CAST(CV_LOGTAG_EXPAND_NAME(tag)); \
|
||||
if (!cv_temp_logtagptr) cv_temp_logtagptr = CV_LOGTAG_PTR_CAST(CV_LOGTAG_FALLBACK); \
|
||||
if (!cv_temp_logtagptr) cv_temp_logtagptr = CV_LOGTAG_PTR_CAST(CV_LOGTAG_GLOBAL); \
|
||||
if (cv_temp_logtagptr && (cv_temp_msglevel > cv_temp_logtagptr->level)) break; \
|
||||
std::stringstream cv_temp_logstream; \
|
||||
cv_temp_logstream << __VA_ARGS__; \
|
||||
cv::utils::logging::internal::writeLogMessageEx( \
|
||||
cv_temp_msglevel, \
|
||||
(cv_temp_logtagptr ? cv_temp_logtagptr->name : nullptr), \
|
||||
__FILE__, \
|
||||
__LINE__, \
|
||||
CV_Func, \
|
||||
cv_temp_logstream.str().c_str()); \
|
||||
break; \
|
||||
}
|
||||
|
||||
#define CV_LOG_FATAL(tag, ...) CV_LOG_WITH_TAG(tag, cv::utils::logging::LOG_LEVEL_FATAL, __VA_ARGS__)
|
||||
#define CV_LOG_ERROR(tag, ...) CV_LOG_WITH_TAG(tag, cv::utils::logging::LOG_LEVEL_ERROR, __VA_ARGS__)
|
||||
#define CV_LOG_WARNING(tag, ...) CV_LOG_WITH_TAG(tag, cv::utils::logging::LOG_LEVEL_WARNING, __VA_ARGS__)
|
||||
#define CV_LOG_INFO(tag, ...) CV_LOG_WITH_TAG(tag, cv::utils::logging::LOG_LEVEL_INFO, __VA_ARGS__)
|
||||
#define CV_LOG_DEBUG(tag, ...) CV_LOG_WITH_TAG(tag, cv::utils::logging::LOG_LEVEL_DEBUG, __VA_ARGS__)
|
||||
#define CV_LOG_VERBOSE(tag, v, ...) CV_LOG_WITH_TAG(tag, (cv::utils::logging::LOG_LEVEL_VERBOSE + (int)(v)), __VA_ARGS__)
|
||||
|
||||
#define CV_LOG_FATAL(tag, ...) for(;;) { if (cv::utils::logging::getLogLevel() < cv::utils::logging::LOG_LEVEL_FATAL) break; std::stringstream ss; ss << __VA_ARGS__; cv::utils::logging::internal::writeLogMessage(cv::utils::logging::LOG_LEVEL_FATAL, ss.str().c_str()); break; }
|
||||
#define CV_LOG_ERROR(tag, ...) for(;;) { if (cv::utils::logging::getLogLevel() < cv::utils::logging::LOG_LEVEL_ERROR) break; std::stringstream ss; ss << __VA_ARGS__; cv::utils::logging::internal::writeLogMessage(cv::utils::logging::LOG_LEVEL_ERROR, ss.str().c_str()); break; }
|
||||
#define CV_LOG_WARNING(tag, ...) for(;;) { if (cv::utils::logging::getLogLevel() < cv::utils::logging::LOG_LEVEL_WARNING) break; std::stringstream ss; ss << __VA_ARGS__; cv::utils::logging::internal::writeLogMessage(cv::utils::logging::LOG_LEVEL_WARNING, ss.str().c_str()); break; }
|
||||
#if CV_LOG_STRIP_LEVEL <= CV_LOG_LEVEL_INFO
|
||||
#define CV_LOG_INFO(tag, ...)
|
||||
#else
|
||||
#define CV_LOG_INFO(tag, ...) for(;;) { if (cv::utils::logging::getLogLevel() < cv::utils::logging::LOG_LEVEL_INFO) break; std::stringstream ss; ss << __VA_ARGS__; cv::utils::logging::internal::writeLogMessage(cv::utils::logging::LOG_LEVEL_INFO, ss.str().c_str()); break; }
|
||||
#endif
|
||||
#if CV_LOG_STRIP_LEVEL <= CV_LOG_LEVEL_DEBUG
|
||||
#define CV_LOG_DEBUG(tag, ...)
|
||||
#else
|
||||
#define CV_LOG_DEBUG(tag, ...) for(;;) { if (cv::utils::logging::getLogLevel() < cv::utils::logging::LOG_LEVEL_DEBUG) break; std::stringstream ss; ss << __VA_ARGS__; cv::utils::logging::internal::writeLogMessage(cv::utils::logging::LOG_LEVEL_DEBUG, ss.str().c_str()); break; }
|
||||
#endif
|
||||
#if CV_LOG_STRIP_LEVEL <= CV_LOG_LEVEL_VERBOSE
|
||||
#define CV_LOG_VERBOSE(tag, v, ...)
|
||||
#else
|
||||
#define CV_LOG_VERBOSE(tag, v, ...) for(;;) { if (cv::utils::logging::getLogLevel() < cv::utils::logging::LOG_LEVEL_VERBOSE) break; std::stringstream ss; ss << "[VERB" << v << ":" << cv::utils::getThreadID() << "] " << __VA_ARGS__; cv::utils::logging::internal::writeLogMessage(cv::utils::logging::LOG_LEVEL_VERBOSE, ss.str().c_str()); break; }
|
||||
# undef CV_LOG_INFO
|
||||
# define CV_LOG_INFO(tag, ...)
|
||||
#endif
|
||||
|
||||
#if CV_LOG_STRIP_LEVEL <= CV_LOG_LEVEL_DEBUG
|
||||
# undef CV_LOG_DEBUG
|
||||
# define CV_LOG_DEBUG(tag, ...)
|
||||
#endif
|
||||
|
||||
#if CV_LOG_STRIP_LEVEL <= CV_LOG_LEVEL_VERBOSE
|
||||
# undef CV_LOG_VERBOSE
|
||||
# define CV_LOG_VERBOSE(tag, v, ...)
|
||||
#endif
|
||||
|
||||
}}} // namespace
|
||||
|
||||
|
28
modules/core/include/opencv2/core/utils/logtag.hpp
Normal file
28
modules/core/include/opencv2/core/utils/logtag.hpp
Normal file
@ -0,0 +1,28 @@
|
||||
// This file is part of OpenCV project.
|
||||
// It is subject to the license terms in the LICENSE file found in the top-level directory
|
||||
// of this distribution and at http://opencv.org/license.html.
|
||||
|
||||
#ifndef OPENCV_CORE_LOGTAG_HPP
|
||||
#define OPENCV_CORE_LOGTAG_HPP
|
||||
|
||||
#include "opencv2/core/cvstd.hpp"
|
||||
#include "logger.defines.hpp"
|
||||
|
||||
namespace cv {
|
||||
namespace utils {
|
||||
namespace logging {
|
||||
|
||||
struct LogTag
|
||||
{
|
||||
const char* name;
|
||||
LogLevel level;
|
||||
|
||||
inline constexpr LogTag(const char* _name, LogLevel _level)
|
||||
: name(_name)
|
||||
, level(_level)
|
||||
{}
|
||||
};
|
||||
|
||||
}}}
|
||||
|
||||
#endif
|
@ -6,6 +6,8 @@
|
||||
|
||||
#include <opencv2/core/utils/configuration.private.hpp>
|
||||
#include <opencv2/core/utils/logger.hpp>
|
||||
#include "utils/logtagmanager.hpp"
|
||||
#include "utils/logtagconfigparser.hpp"
|
||||
|
||||
#include <sstream>
|
||||
#include <iostream>
|
||||
@ -19,52 +21,156 @@ namespace cv {
|
||||
namespace utils {
|
||||
namespace logging {
|
||||
|
||||
static LogLevel parseLogLevelConfiguration()
|
||||
namespace internal
|
||||
{
|
||||
static cv::String param_log_level = utils::getConfigurationParameterString("OPENCV_LOG_LEVEL",
|
||||
|
||||
// Combining several things that require static dynamic initialization in a
|
||||
// well-defined order into a struct.
|
||||
//
|
||||
struct GlobalLoggingInitStruct
|
||||
{
|
||||
public:
|
||||
#if defined NDEBUG
|
||||
"WARNING"
|
||||
static constexpr bool m_isDebugBuild = false;
|
||||
#else
|
||||
"INFO"
|
||||
static constexpr bool m_isDebugBuild = true;
|
||||
#endif
|
||||
);
|
||||
if (param_log_level == "DISABLED" || param_log_level == "disabled" ||
|
||||
param_log_level == "0" || param_log_level == "OFF" || param_log_level == "off")
|
||||
return LOG_LEVEL_SILENT;
|
||||
if (param_log_level == "FATAL" || param_log_level == "fatal")
|
||||
return LOG_LEVEL_FATAL;
|
||||
if (param_log_level == "ERROR" || param_log_level == "error")
|
||||
return LOG_LEVEL_ERROR;
|
||||
if (param_log_level == "WARNING" || param_log_level == "warning" ||
|
||||
param_log_level == "WARNINGS" || param_log_level == "warnings" ||
|
||||
param_log_level == "WARN" || param_log_level == "warn")
|
||||
return LOG_LEVEL_WARNING;
|
||||
if (param_log_level == "INFO" || param_log_level == "info")
|
||||
return LOG_LEVEL_INFO;
|
||||
if (param_log_level == "DEBUG" || param_log_level == "debug")
|
||||
return LOG_LEVEL_DEBUG;
|
||||
if (param_log_level == "VERBOSE" || param_log_level == "verbose")
|
||||
return LOG_LEVEL_VERBOSE;
|
||||
std::cerr << "ERROR: Unexpected logging level value: " << param_log_level << std::endl;
|
||||
return LOG_LEVEL_INFO;
|
||||
|
||||
public:
|
||||
static constexpr LogLevel m_defaultUnconfiguredGlobalLevel =
|
||||
m_isDebugBuild ? LOG_LEVEL_DEBUG : LOG_LEVEL_WARNING;
|
||||
|
||||
public:
|
||||
LogTagManager logTagManager;
|
||||
|
||||
GlobalLoggingInitStruct()
|
||||
: logTagManager(m_defaultUnconfiguredGlobalLevel)
|
||||
{
|
||||
applyConfigString();
|
||||
handleMalformed();
|
||||
}
|
||||
|
||||
private:
|
||||
void applyConfigString()
|
||||
{
|
||||
logTagManager.setConfigString(utils::getConfigurationParameterString("OPENCV_LOG_LEVEL", ""));
|
||||
}
|
||||
|
||||
void handleMalformed()
|
||||
{
|
||||
// need to print warning for malformed log tag config strings?
|
||||
if (m_isDebugBuild)
|
||||
{
|
||||
const auto& parser = logTagManager.getConfigParser();
|
||||
if (parser.hasMalformed())
|
||||
{
|
||||
const auto& malformedList = parser.getMalformed();
|
||||
for (const auto& malformed : malformedList)
|
||||
{
|
||||
std::cout << "Malformed log level config: \"" << malformed << "\"\n";
|
||||
}
|
||||
std::cout.flush();
|
||||
}
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
// Static dynamic initialization guard function for the combined struct
|
||||
// just defined above
|
||||
//
|
||||
// An initialization guard function guarantees that outside code cannot
|
||||
// accidentally see not-yet-dynamically-initialized data, by routing
|
||||
// all outside access request to this function, so that this function
|
||||
// has a chance to run the initialization code if necessary.
|
||||
//
|
||||
// An initialization guard function only guarantees initialization upon
|
||||
// the first call to this function.
|
||||
//
|
||||
static GlobalLoggingInitStruct& getGlobalLoggingInitStruct()
|
||||
{
|
||||
static GlobalLoggingInitStruct globalLoggingInitInstance;
|
||||
return globalLoggingInitInstance;
|
||||
}
|
||||
|
||||
// To ensure that the combined struct defined above is initialized even
|
||||
// if the initialization guard function wasn't called, a dummy static
|
||||
// instance of a struct is defined below, which will call the
|
||||
// initialization guard function.
|
||||
//
|
||||
struct GlobalLoggingInitCall
|
||||
{
|
||||
GlobalLoggingInitCall()
|
||||
{
|
||||
getGlobalLoggingInitStruct();
|
||||
}
|
||||
};
|
||||
|
||||
static GlobalLoggingInitCall globalLoggingInitCall;
|
||||
|
||||
static LogTagManager& getLogTagManager()
|
||||
{
|
||||
static LogTagManager& logTagManagerInstance = getGlobalLoggingInitStruct().logTagManager;
|
||||
return logTagManagerInstance;
|
||||
}
|
||||
|
||||
static LogLevel& getLogLevelVariable()
|
||||
{
|
||||
static LogLevel g_logLevel = parseLogLevelConfiguration();
|
||||
return g_logLevel;
|
||||
static LogLevel& refGlobalLogLevel = getGlobalLogTag()->level;
|
||||
return refGlobalLogLevel;
|
||||
}
|
||||
|
||||
LogTag* getGlobalLogTag()
|
||||
{
|
||||
static LogTag* globalLogTagPtr = getGlobalLoggingInitStruct().logTagManager.get("global");
|
||||
return globalLogTagPtr;
|
||||
}
|
||||
|
||||
} // namespace
|
||||
|
||||
void registerLogTag(LogTag* plogtag)
|
||||
{
|
||||
if (!plogtag || !plogtag->name)
|
||||
{
|
||||
return;
|
||||
}
|
||||
internal::getLogTagManager().assign(plogtag->name, plogtag);
|
||||
}
|
||||
|
||||
void setLogTagLevel(const char* tag, LogLevel level)
|
||||
{
|
||||
if (!tag)
|
||||
{
|
||||
return;
|
||||
}
|
||||
internal::getLogTagManager().setLevelByFullName(std::string(tag), level);
|
||||
}
|
||||
|
||||
LogLevel getLogTagLevel(const char* tag)
|
||||
{
|
||||
if (!tag)
|
||||
{
|
||||
return getLogLevel();
|
||||
}
|
||||
const LogTag* ptr = internal::getLogTagManager().get(std::string(tag));
|
||||
if (!ptr)
|
||||
{
|
||||
return getLogLevel();
|
||||
}
|
||||
return ptr->level;
|
||||
}
|
||||
|
||||
LogLevel setLogLevel(LogLevel logLevel)
|
||||
{
|
||||
LogLevel old = getLogLevelVariable();
|
||||
getLogLevelVariable() = logLevel;
|
||||
// note: not thread safe, use sparingly and do not critically depend on outcome
|
||||
LogLevel& refGlobalLevel = internal::getLogLevelVariable();
|
||||
const LogLevel old = refGlobalLevel;
|
||||
refGlobalLevel = logLevel;
|
||||
return old;
|
||||
}
|
||||
|
||||
LogLevel getLogLevel()
|
||||
{
|
||||
return getLogLevelVariable();
|
||||
return internal::getLogLevelVariable();
|
||||
}
|
||||
|
||||
namespace internal {
|
||||
@ -105,6 +211,29 @@ void writeLogMessage(LogLevel logLevel, const char* message)
|
||||
(*out) << std::flush;
|
||||
}
|
||||
|
||||
void writeLogMessageEx(LogLevel logLevel, const char* tag, const char* file, int line, const char* func, const char* message)
|
||||
{
|
||||
std::ostringstream strm;
|
||||
if (tag)
|
||||
{
|
||||
strm << tag << " ";
|
||||
}
|
||||
if (file)
|
||||
{
|
||||
strm << file << " ";
|
||||
}
|
||||
if (line > 0)
|
||||
{
|
||||
strm << "(" << line << ") ";
|
||||
}
|
||||
if (func)
|
||||
{
|
||||
strm << func << " ";
|
||||
}
|
||||
strm << message;
|
||||
writeLogMessage(logLevel, strm.str().c_str());
|
||||
}
|
||||
|
||||
} // namespace
|
||||
|
||||
}}} // namespace
|
||||
|
51
modules/core/src/utils/logtagconfig.hpp
Normal file
51
modules/core/src/utils/logtagconfig.hpp
Normal file
@ -0,0 +1,51 @@
|
||||
// This file is part of OpenCV project.
|
||||
// It is subject to the license terms in the LICENSE file found in the top-level directory
|
||||
// of this distribution and at http://opencv.org/license.html.
|
||||
|
||||
#ifndef OPENCV_CORE_LOGTAGCONFIG_HPP
|
||||
#define OPENCV_CORE_LOGTAGCONFIG_HPP
|
||||
|
||||
#if 1 // if not already in precompiled headers
|
||||
#include <opencv2/core/utils/logger.defines.hpp>
|
||||
#include <string>
|
||||
#endif
|
||||
|
||||
namespace cv {
|
||||
namespace utils {
|
||||
namespace logging {
|
||||
|
||||
struct LogTagConfig
|
||||
{
|
||||
std::string namePart;
|
||||
LogLevel level;
|
||||
bool isGlobal;
|
||||
bool hasPrefixWildcard;
|
||||
bool hasSuffixWildcard;
|
||||
|
||||
LogTagConfig()
|
||||
: namePart()
|
||||
, level()
|
||||
, isGlobal()
|
||||
, hasPrefixWildcard()
|
||||
, hasSuffixWildcard()
|
||||
{
|
||||
}
|
||||
|
||||
LogTagConfig(const std::string& _namePart, LogLevel _level, bool _isGlobal = false,
|
||||
bool _hasPrefixWildcard = false, bool _hasSuffixWildcard = false)
|
||||
: namePart(_namePart)
|
||||
, level(_level)
|
||||
, isGlobal(_isGlobal)
|
||||
, hasPrefixWildcard(_hasPrefixWildcard)
|
||||
, hasSuffixWildcard(_hasSuffixWildcard)
|
||||
{
|
||||
}
|
||||
|
||||
LogTagConfig(const LogTagConfig&) = default;
|
||||
LogTagConfig(LogTagConfig&&) = default;
|
||||
~LogTagConfig() = default;
|
||||
};
|
||||
|
||||
}}}
|
||||
|
||||
#endif
|
305
modules/core/src/utils/logtagconfigparser.cpp
Normal file
305
modules/core/src/utils/logtagconfigparser.cpp
Normal file
@ -0,0 +1,305 @@
|
||||
// This file is part of OpenCV project.
|
||||
// It is subject to the license terms in the LICENSE file found in the top-level directory
|
||||
// of this distribution and at http://opencv.org/license.html.
|
||||
|
||||
#include "../precomp.hpp"
|
||||
#include "logtagconfigparser.hpp"
|
||||
|
||||
namespace cv {
|
||||
namespace utils {
|
||||
namespace logging {
|
||||
|
||||
LogTagConfigParser::LogTagConfigParser()
|
||||
{
|
||||
m_parsedGlobal.namePart = "global";
|
||||
m_parsedGlobal.isGlobal = true;
|
||||
m_parsedGlobal.hasPrefixWildcard = false;
|
||||
m_parsedGlobal.hasSuffixWildcard = false;
|
||||
m_parsedGlobal.level = LOG_LEVEL_VERBOSE;
|
||||
}
|
||||
|
||||
LogTagConfigParser::LogTagConfigParser(const std::string& input)
|
||||
{
|
||||
parse(input);
|
||||
}
|
||||
|
||||
LogTagConfigParser::~LogTagConfigParser()
|
||||
{
|
||||
}
|
||||
|
||||
bool LogTagConfigParser::parse(const std::string& input)
|
||||
{
|
||||
m_input = input;
|
||||
segmentTokens();
|
||||
return (m_malformed.empty());
|
||||
}
|
||||
|
||||
bool LogTagConfigParser::hasMalformed() const
|
||||
{
|
||||
return !m_malformed.empty();
|
||||
}
|
||||
|
||||
const LogTagConfig& LogTagConfigParser::getGlobalConfig() const
|
||||
{
|
||||
return m_parsedGlobal;
|
||||
}
|
||||
|
||||
const std::vector<LogTagConfig>& LogTagConfigParser::getFullNameConfigs() const
|
||||
{
|
||||
return m_parsedFullName;
|
||||
}
|
||||
|
||||
const std::vector<LogTagConfig>& LogTagConfigParser::getFirstPartConfigs() const
|
||||
{
|
||||
return m_parsedFirstPart;
|
||||
}
|
||||
|
||||
const std::vector<LogTagConfig>& LogTagConfigParser::getAnyPartConfigs() const
|
||||
{
|
||||
return m_parsedAnyPart;
|
||||
}
|
||||
|
||||
const std::vector<std::string>& LogTagConfigParser::getMalformed() const
|
||||
{
|
||||
return m_malformed;
|
||||
}
|
||||
|
||||
void LogTagConfigParser::segmentTokens()
|
||||
{
|
||||
const size_t len = m_input.length();
|
||||
std::vector<std::pair<size_t, size_t>> startStops;
|
||||
bool wasSeparator = true;
|
||||
for (size_t pos = 0u; pos < len; ++pos)
|
||||
{
|
||||
char c = m_input[pos];
|
||||
bool isSeparator = (c == ' ' || c == '\t' || c == ';');
|
||||
if (!isSeparator)
|
||||
{
|
||||
if (wasSeparator)
|
||||
{
|
||||
startStops.emplace_back(pos, pos + 1u);
|
||||
}
|
||||
else
|
||||
{
|
||||
startStops.back().second = pos + 1u;
|
||||
}
|
||||
}
|
||||
wasSeparator = isSeparator;
|
||||
}
|
||||
for (const auto& startStop : startStops)
|
||||
{
|
||||
const auto s = m_input.substr(startStop.first, startStop.second - startStop.first);
|
||||
parseNameAndLevel(s);
|
||||
}
|
||||
}
|
||||
|
||||
void LogTagConfigParser::parseNameAndLevel(const std::string& s)
|
||||
{
|
||||
const size_t npos = std::string::npos;
|
||||
const size_t len = s.length();
|
||||
size_t colonIdx = s.find_first_of(":=");
|
||||
if (colonIdx == npos)
|
||||
{
|
||||
// See if the whole string is a log level
|
||||
auto parsedLevel = parseLogLevel(s);
|
||||
if (parsedLevel.second)
|
||||
{
|
||||
// If it is, assume the log level is for global
|
||||
parseWildcard("", parsedLevel.first);
|
||||
return;
|
||||
}
|
||||
else
|
||||
{
|
||||
// not sure what to do.
|
||||
m_malformed.push_back(s);
|
||||
return;
|
||||
}
|
||||
}
|
||||
if (colonIdx == 0u || colonIdx + 1u == len)
|
||||
{
|
||||
// malformed (colon or equal sign at beginning or end), cannot do anything
|
||||
m_malformed.push_back(s);
|
||||
return;
|
||||
}
|
||||
size_t colonIdx2 = s.find_first_of(":=", colonIdx + 1u);
|
||||
if (colonIdx2 != npos)
|
||||
{
|
||||
// malformed (more than one colon or equal sign), cannot do anything
|
||||
m_malformed.push_back(s);
|
||||
return;
|
||||
}
|
||||
auto parsedLevel = parseLogLevel(s.substr(colonIdx + 1u));
|
||||
if (parsedLevel.second)
|
||||
{
|
||||
parseWildcard(s.substr(0u, colonIdx), parsedLevel.first);
|
||||
return;
|
||||
}
|
||||
else
|
||||
{
|
||||
// Cannot recognize the right side of the colon or equal sign.
|
||||
// Not sure what to do.
|
||||
m_malformed.push_back(s);
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
||||
void LogTagConfigParser::parseWildcard(const std::string& name, LogLevel level)
|
||||
{
|
||||
constexpr size_t npos = std::string::npos;
|
||||
const size_t len = name.length();
|
||||
if (len == 0u)
|
||||
{
|
||||
m_parsedGlobal.level = level;
|
||||
return;
|
||||
}
|
||||
const bool hasPrefixWildcard = (name[0u] == '*');
|
||||
if (hasPrefixWildcard && len == 1u)
|
||||
{
|
||||
m_parsedGlobal.level = level;
|
||||
return;
|
||||
}
|
||||
const size_t firstNonWildcard = name.find_first_not_of("*.");
|
||||
if (hasPrefixWildcard && firstNonWildcard == npos)
|
||||
{
|
||||
m_parsedGlobal.level = level;
|
||||
return;
|
||||
}
|
||||
const bool hasSuffixWildcard = (name[len - 1u] == '*');
|
||||
const size_t lastNonWildcard = name.find_last_not_of("*.");
|
||||
std::string trimmedNamePart = name.substr(firstNonWildcard, lastNonWildcard - firstNonWildcard + 1u);
|
||||
// The case of a single asterisk has been handled above;
|
||||
// here we only handle the explicit use of "global" in the log config string.
|
||||
const bool isGlobal = (trimmedNamePart == "global");
|
||||
if (isGlobal)
|
||||
{
|
||||
m_parsedGlobal.level = level;
|
||||
return;
|
||||
}
|
||||
LogTagConfig result(trimmedNamePart, level, false, hasPrefixWildcard, hasSuffixWildcard);
|
||||
if (hasPrefixWildcard)
|
||||
{
|
||||
m_parsedAnyPart.emplace_back(std::move(result));
|
||||
}
|
||||
else if (hasSuffixWildcard)
|
||||
{
|
||||
m_parsedFirstPart.emplace_back(std::move(result));
|
||||
}
|
||||
else
|
||||
{
|
||||
m_parsedFullName.emplace_back(std::move(result));
|
||||
}
|
||||
}
|
||||
|
||||
std::pair<LogLevel, bool> LogTagConfigParser::parseLogLevel(const std::string& s)
|
||||
{
|
||||
const auto falseDontCare = std::make_pair(LOG_LEVEL_VERBOSE, false);
|
||||
const auto make_parsed_result = [](LogLevel lev) -> std::pair<LogLevel, bool>
|
||||
{
|
||||
return std::make_pair(lev, true);
|
||||
};
|
||||
const size_t len = s.length();
|
||||
if (len >= 1u)
|
||||
{
|
||||
const char c = (char)std::toupper(s[0]);
|
||||
switch (c)
|
||||
{
|
||||
case '0':
|
||||
if (len == 1u)
|
||||
{
|
||||
return make_parsed_result(LOG_LEVEL_SILENT);
|
||||
}
|
||||
break;
|
||||
case 'D':
|
||||
if (len == 1u ||
|
||||
(len == 5u && cv::toUpperCase(s) == "DEBUG"))
|
||||
{
|
||||
return make_parsed_result(LOG_LEVEL_DEBUG);
|
||||
}
|
||||
if ((len == 7u && cv::toUpperCase(s) == "DISABLE") ||
|
||||
(len == 8u && cv::toUpperCase(s) == "DISABLED"))
|
||||
{
|
||||
return make_parsed_result(LOG_LEVEL_SILENT);
|
||||
}
|
||||
break;
|
||||
case 'E':
|
||||
if (len == 1u ||
|
||||
(len == 5u && cv::toUpperCase(s) == "ERROR"))
|
||||
{
|
||||
return make_parsed_result(LOG_LEVEL_ERROR);
|
||||
}
|
||||
break;
|
||||
case 'F':
|
||||
if (len == 1u ||
|
||||
(len == 5u && cv::toUpperCase(s) == "FATAL"))
|
||||
{
|
||||
return make_parsed_result(LOG_LEVEL_FATAL);
|
||||
}
|
||||
break;
|
||||
case 'I':
|
||||
if (len == 1u ||
|
||||
(len == 4u && cv::toUpperCase(s) == "INFO"))
|
||||
{
|
||||
return make_parsed_result(LOG_LEVEL_INFO);
|
||||
}
|
||||
break;
|
||||
case 'O':
|
||||
if (len == 3u && cv::toUpperCase(s) == "OFF")
|
||||
{
|
||||
return make_parsed_result(LOG_LEVEL_SILENT);
|
||||
}
|
||||
break;
|
||||
case 'S':
|
||||
if (len == 1u ||
|
||||
(len == 6u && cv::toUpperCase(s) == "SILENT"))
|
||||
{
|
||||
return make_parsed_result(LOG_LEVEL_SILENT);
|
||||
}
|
||||
break;
|
||||
case 'V':
|
||||
if (len == 1u ||
|
||||
(len == 7u && cv::toUpperCase(s) == "VERBOSE"))
|
||||
{
|
||||
return make_parsed_result(LOG_LEVEL_VERBOSE);
|
||||
}
|
||||
break;
|
||||
case 'W':
|
||||
if (len == 1u ||
|
||||
(len == 4u && cv::toUpperCase(s) == "WARN") ||
|
||||
(len == 7u && cv::toUpperCase(s) == "WARNING") ||
|
||||
(len == 8u && cv::toUpperCase(s) == "WARNINGS"))
|
||||
{
|
||||
return make_parsed_result(LOG_LEVEL_WARNING);
|
||||
}
|
||||
break;
|
||||
default:
|
||||
break;
|
||||
}
|
||||
// fall through
|
||||
}
|
||||
return falseDontCare;
|
||||
}
|
||||
|
||||
std::string LogTagConfigParser::toString(LogLevel level)
|
||||
{
|
||||
switch (level)
|
||||
{
|
||||
case LOG_LEVEL_SILENT:
|
||||
return "SILENT";
|
||||
case LOG_LEVEL_FATAL:
|
||||
return "FATAL";
|
||||
case LOG_LEVEL_ERROR:
|
||||
return "ERROR";
|
||||
case LOG_LEVEL_WARNING:
|
||||
return "WARNING";
|
||||
case LOG_LEVEL_INFO:
|
||||
return "INFO";
|
||||
case LOG_LEVEL_DEBUG:
|
||||
return "DEBUG";
|
||||
case LOG_LEVEL_VERBOSE:
|
||||
return "VERBOSE";
|
||||
default:
|
||||
return std::to_string((int)level);
|
||||
}
|
||||
}
|
||||
|
||||
}}} //namespace
|
55
modules/core/src/utils/logtagconfigparser.hpp
Normal file
55
modules/core/src/utils/logtagconfigparser.hpp
Normal file
@ -0,0 +1,55 @@
|
||||
// This file is part of OpenCV project.
|
||||
// It is subject to the license terms in the LICENSE file found in the top-level directory
|
||||
// of this distribution and at http://opencv.org/license.html.
|
||||
|
||||
#ifndef OPENCV_CORE_LOGTAGCONFIGPARSER_HPP
|
||||
#define OPENCV_CORE_LOGTAGCONFIGPARSER_HPP
|
||||
|
||||
#if 1 // if not already in precompiled headers
|
||||
#include <string>
|
||||
#include <vector>
|
||||
#include <functional>
|
||||
#endif
|
||||
|
||||
#include <opencv2/core/utils/logtag.hpp>
|
||||
#include "logtagconfig.hpp"
|
||||
|
||||
namespace cv {
|
||||
namespace utils {
|
||||
namespace logging {
|
||||
|
||||
class LogTagConfigParser
|
||||
{
|
||||
public:
|
||||
LogTagConfigParser();
|
||||
explicit LogTagConfigParser(const std::string& input);
|
||||
~LogTagConfigParser();
|
||||
|
||||
public:
|
||||
bool parse(const std::string& input);
|
||||
bool hasMalformed() const;
|
||||
const LogTagConfig& getGlobalConfig() const;
|
||||
const std::vector<LogTagConfig>& getFullNameConfigs() const;
|
||||
const std::vector<LogTagConfig>& getFirstPartConfigs() const;
|
||||
const std::vector<LogTagConfig>& getAnyPartConfigs() const;
|
||||
const std::vector<std::string>& getMalformed() const;
|
||||
|
||||
private:
|
||||
void segmentTokens();
|
||||
void parseNameAndLevel(const std::string& s);
|
||||
void parseWildcard(const std::string& name, LogLevel level);
|
||||
static std::pair<LogLevel, bool> parseLogLevel(const std::string& s);
|
||||
static std::string toString(LogLevel level);
|
||||
|
||||
private:
|
||||
std::string m_input;
|
||||
LogTagConfig m_parsedGlobal;
|
||||
std::vector<LogTagConfig> m_parsedFullName;
|
||||
std::vector<LogTagConfig> m_parsedFirstPart;
|
||||
std::vector<LogTagConfig> m_parsedAnyPart;
|
||||
std::vector<std::string> m_malformed;
|
||||
};
|
||||
|
||||
}}} //namespace
|
||||
|
||||
#endif
|
425
modules/core/src/utils/logtagmanager.cpp
Normal file
425
modules/core/src/utils/logtagmanager.cpp
Normal file
@ -0,0 +1,425 @@
|
||||
// This file is part of OpenCV project.
|
||||
// It is subject to the license terms in the LICENSE file found in the top-level directory
|
||||
// of this distribution and at http://opencv.org/license.html.
|
||||
|
||||
#include "../precomp.hpp"
|
||||
#include "logtagmanager.hpp"
|
||||
#include "logtagconfigparser.hpp"
|
||||
|
||||
namespace cv {
|
||||
namespace utils {
|
||||
namespace logging {
|
||||
|
||||
LogTagManager::LogTagManager(LogLevel defaultUnconfiguredGlobalLevel)
|
||||
: m_mutex()
|
||||
, m_globalLogTag(new LogTag(m_globalName, defaultUnconfiguredGlobalLevel))
|
||||
, m_config(std::make_shared<LogTagConfigParser>())
|
||||
{
|
||||
assign(m_globalName, m_globalLogTag.get());
|
||||
}
|
||||
|
||||
LogTagManager::~LogTagManager()
|
||||
{
|
||||
}
|
||||
|
||||
void LogTagManager::setConfigString(const std::string& configString, bool apply /*true*/)
|
||||
{
|
||||
m_config->parse(configString);
|
||||
if (m_config->hasMalformed())
|
||||
{
|
||||
return;
|
||||
}
|
||||
if (!apply)
|
||||
{
|
||||
return;
|
||||
}
|
||||
// The following code is arranged with "priority by overwriting",
|
||||
// where when the same log tag has multiple matches, the last code
|
||||
// block has highest priority by literally overwriting the effects
|
||||
// from the earlier code blocks.
|
||||
//
|
||||
// Matching by full name has highest priority.
|
||||
// Matching by any name part has moderate priority.
|
||||
// Matching by first name part (prefix) has lowest priority.
|
||||
//
|
||||
const auto& globalConfig = m_config->getGlobalConfig();
|
||||
m_globalLogTag->level = globalConfig.level;
|
||||
for (const auto& config : m_config->getFirstPartConfigs())
|
||||
{
|
||||
setLevelByFirstPart(config.namePart, config.level);
|
||||
}
|
||||
for (const auto& config : m_config->getAnyPartConfigs())
|
||||
{
|
||||
setLevelByAnyPart(config.namePart, config.level);
|
||||
}
|
||||
for (const auto& config : m_config->getFullNameConfigs())
|
||||
{
|
||||
setLevelByFullName(config.namePart, config.level);
|
||||
}
|
||||
}
|
||||
|
||||
LogTagConfigParser& LogTagManager::getConfigParser() const
|
||||
{
|
||||
return *m_config;
|
||||
}
|
||||
|
||||
void LogTagManager::assign(const std::string& fullName, LogTag* ptr)
|
||||
{
|
||||
CV_TRACE_FUNCTION();
|
||||
LockType lock(m_mutex);
|
||||
FullNameLookupResult result(fullName);
|
||||
result.m_findCrossReferences = true;
|
||||
m_nameTable.addOrLookupFullName(result);
|
||||
FullNameInfo& fullNameInfo = *result.m_fullNameInfoPtr;
|
||||
const bool isPtrChanged = (fullNameInfo.logTagPtr != ptr);
|
||||
if (!isPtrChanged)
|
||||
{
|
||||
return;
|
||||
}
|
||||
fullNameInfo.logTagPtr = ptr;
|
||||
if (!ptr)
|
||||
{
|
||||
return;
|
||||
}
|
||||
const bool hasAppliedFullNameConfig = internal_applyFullNameConfigToTag(fullNameInfo);
|
||||
if (hasAppliedFullNameConfig)
|
||||
{
|
||||
return;
|
||||
}
|
||||
internal_applyNamePartConfigToSpecificTag(result);
|
||||
}
|
||||
|
||||
void LogTagManager::unassign(const std::string& fullName)
|
||||
{
|
||||
// Lock is inside assign() method.
|
||||
assign(fullName, nullptr);
|
||||
}
|
||||
|
||||
LogTag* LogTagManager::get(const std::string& fullName)
|
||||
{
|
||||
CV_TRACE_FUNCTION();
|
||||
LockType lock(m_mutex);
|
||||
FullNameInfo* fullNameInfoPtr = m_nameTable.getFullNameInfo(fullName);
|
||||
if (fullNameInfoPtr && fullNameInfoPtr->logTagPtr)
|
||||
{
|
||||
return fullNameInfoPtr->logTagPtr;
|
||||
}
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
void LogTagManager::setLevelByFullName(const std::string& fullName, LogLevel level)
|
||||
{
|
||||
CV_TRACE_FUNCTION();
|
||||
LockType lock(m_mutex);
|
||||
FullNameLookupResult result(fullName);
|
||||
result.m_findCrossReferences = false;
|
||||
m_nameTable.addOrLookupFullName(result);
|
||||
FullNameInfo& fullNameInfo = *result.m_fullNameInfoPtr;
|
||||
if (fullNameInfo.parsedLevel.scope == MatchingScope::Full &&
|
||||
fullNameInfo.parsedLevel.level == level)
|
||||
{
|
||||
// skip additional processing if nothing changes.
|
||||
return;
|
||||
}
|
||||
// update the cached configured value.
|
||||
fullNameInfo.parsedLevel.scope = MatchingScope::Full;
|
||||
fullNameInfo.parsedLevel.level = level;
|
||||
// update the actual tag, if already registered.
|
||||
LogTag* logTagPtr = fullNameInfo.logTagPtr;
|
||||
if (logTagPtr)
|
||||
{
|
||||
logTagPtr->level = level;
|
||||
}
|
||||
}
|
||||
|
||||
void LogTagManager::setLevelByFirstPart(const std::string& firstPart, LogLevel level)
|
||||
{
|
||||
// Lock is inside setLevelByNamePart() method.
|
||||
setLevelByNamePart(firstPart, level, MatchingScope::FirstNamePart);
|
||||
}
|
||||
|
||||
void LogTagManager::setLevelByAnyPart(const std::string& anyPart, LogLevel level)
|
||||
{
|
||||
// Lock is inside setLevelByNamePart() method.
|
||||
setLevelByNamePart(anyPart, level, MatchingScope::AnyNamePart);
|
||||
}
|
||||
|
||||
void LogTagManager::setLevelByNamePart(const std::string& namePart, LogLevel level, MatchingScope scope)
|
||||
{
|
||||
CV_TRACE_FUNCTION();
|
||||
LockType lock(m_mutex);
|
||||
NamePartLookupResult result(namePart);
|
||||
result.m_findCrossReferences = true;
|
||||
m_nameTable.addOrLookupNamePart(result);
|
||||
NamePartInfo& namePartInfo = *result.m_namePartInfoPtr;
|
||||
if (namePartInfo.parsedLevel.scope == scope &&
|
||||
namePartInfo.parsedLevel.level == level)
|
||||
{
|
||||
// skip additional processing if nothing changes.
|
||||
return;
|
||||
}
|
||||
namePartInfo.parsedLevel.scope = scope;
|
||||
namePartInfo.parsedLevel.level = level;
|
||||
internal_applyNamePartConfigToMatchingTags(result);
|
||||
}
|
||||
|
||||
std::vector<std::string> LogTagManager::splitNameParts(const std::string& fullName)
|
||||
{
|
||||
const size_t npos = std::string::npos;
|
||||
const size_t len = fullName.length();
|
||||
std::vector<std::string> nameParts;
|
||||
size_t start = 0u;
|
||||
while (start < len)
|
||||
{
|
||||
size_t nextPeriod = fullName.find('.', start);
|
||||
if (nextPeriod == npos)
|
||||
{
|
||||
nextPeriod = len;
|
||||
}
|
||||
if (nextPeriod >= start + 1u)
|
||||
{
|
||||
nameParts.emplace_back(fullName.substr(start, nextPeriod - start));
|
||||
}
|
||||
start = nextPeriod + 1u;
|
||||
}
|
||||
return nameParts;
|
||||
}
|
||||
|
||||
bool LogTagManager::internal_isNamePartMatch(MatchingScope scope, size_t matchingPos)
|
||||
{
|
||||
switch (scope)
|
||||
{
|
||||
case MatchingScope::FirstNamePart:
|
||||
return (matchingPos == 0u);
|
||||
case MatchingScope::AnyNamePart:
|
||||
return true;
|
||||
case MatchingScope::None:
|
||||
case MatchingScope::Full:
|
||||
default:
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
bool LogTagManager::internal_applyFullNameConfigToTag(FullNameInfo& fullNameInfo)
|
||||
{
|
||||
if (!fullNameInfo.logTagPtr)
|
||||
{
|
||||
return false;
|
||||
}
|
||||
if (fullNameInfo.parsedLevel.scope == MatchingScope::Full)
|
||||
{
|
||||
fullNameInfo.logTagPtr->level = fullNameInfo.parsedLevel.level;
|
||||
return true;
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
bool LogTagManager::internal_applyNamePartConfigToSpecificTag(FullNameLookupResult& fullNameResult)
|
||||
{
|
||||
const FullNameInfo& fullNameInfo = *fullNameResult.m_fullNameInfoPtr;
|
||||
LogTag* const logTag = fullNameInfo.logTagPtr;
|
||||
if (!logTag)
|
||||
{
|
||||
return false;
|
||||
}
|
||||
CV_Assert(fullNameResult.m_findCrossReferences);
|
||||
const auto& crossReferences = fullNameResult.m_crossReferences;
|
||||
const size_t matchingNamePartCount = crossReferences.size();
|
||||
for (size_t k = 0u; k < matchingNamePartCount; ++k)
|
||||
{
|
||||
const auto& match = crossReferences.at(k);
|
||||
const auto& namePartInfo = *match.m_namePartInfo;
|
||||
const auto& parsedLevel = namePartInfo.parsedLevel;
|
||||
const auto scope = parsedLevel.scope;
|
||||
const LogLevel level = parsedLevel.level;
|
||||
const size_t matchingPos = match.m_matchingPos;
|
||||
const bool isMatch = internal_isNamePartMatch(scope, matchingPos);
|
||||
if (isMatch)
|
||||
{
|
||||
logTag->level = level;
|
||||
return true;
|
||||
}
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
void LogTagManager::internal_applyNamePartConfigToMatchingTags(NamePartLookupResult& namePartResult)
|
||||
{
|
||||
CV_Assert(namePartResult.m_findCrossReferences);
|
||||
const auto& crossReferences = namePartResult.m_crossReferences;
|
||||
const size_t matchingFullNameCount = crossReferences.size();
|
||||
NamePartInfo& namePartInfo = *namePartResult.m_namePartInfoPtr;
|
||||
const MatchingScope scope = namePartInfo.parsedLevel.scope;
|
||||
CV_Assert(scope != MatchingScope::Full);
|
||||
if (scope == MatchingScope::None)
|
||||
{
|
||||
return;
|
||||
}
|
||||
const LogLevel level = namePartInfo.parsedLevel.level;
|
||||
for (size_t k = 0u; k < matchingFullNameCount; ++k)
|
||||
{
|
||||
const auto& match = crossReferences.at(k);
|
||||
FullNameInfo& fullNameInfo = *match.m_fullNameInfo;
|
||||
LogTag* logTagPtr = fullNameInfo.logTagPtr;
|
||||
if (!logTagPtr)
|
||||
{
|
||||
continue;
|
||||
}
|
||||
if (fullNameInfo.parsedLevel.scope == MatchingScope::Full)
|
||||
{
|
||||
// If the full name already has valid config, that full name config
|
||||
// has precedence over name part config.
|
||||
continue;
|
||||
}
|
||||
const size_t matchingPos = match.m_matchingPos;
|
||||
const bool isMatch = internal_isNamePartMatch(scope, matchingPos);
|
||||
if (!isMatch)
|
||||
{
|
||||
continue;
|
||||
}
|
||||
logTagPtr->level = level;
|
||||
}
|
||||
}
|
||||
|
||||
void LogTagManager::NameTable::addOrLookupFullName(FullNameLookupResult& result)
|
||||
{
|
||||
const auto fullNameIdAndFlag = internal_addOrLookupFullName(result.m_fullName);
|
||||
result.m_fullNameId = fullNameIdAndFlag.first;
|
||||
result.m_nameParts = LogTagManager::splitNameParts(result.m_fullName);
|
||||
internal_addOrLookupNameParts(result.m_nameParts, result.m_namePartIds);
|
||||
const bool isNew = fullNameIdAndFlag.second;
|
||||
if (isNew)
|
||||
{
|
||||
internal_addCrossReference(result.m_fullNameId, result.m_namePartIds);
|
||||
}
|
||||
// ====== IMPORTANT ====== Critical order-of-operation ======
|
||||
// The gathering of the pointers of FullNameInfo and NamePartInfo are performed
|
||||
// as the last step of the operation, so that these pointer are not invalidated
|
||||
// by the vector append operations earlier in this function.
|
||||
// ======
|
||||
result.m_fullNameInfoPtr = internal_getFullNameInfo(result.m_fullNameId);
|
||||
if (result.m_findCrossReferences)
|
||||
{
|
||||
internal_findMatchingNamePartsForFullName(result);
|
||||
}
|
||||
}
|
||||
|
||||
void LogTagManager::NameTable::addOrLookupNamePart(NamePartLookupResult& result)
|
||||
{
|
||||
result.m_namePartId = internal_addOrLookupNamePart(result.m_namePart);
|
||||
result.m_namePartInfoPtr = internal_getNamePartInfo(result.m_namePartId);
|
||||
if (result.m_findCrossReferences)
|
||||
{
|
||||
internal_findMatchingFullNamesForNamePart(result);
|
||||
}
|
||||
}
|
||||
|
||||
std::pair<size_t, bool> LogTagManager::NameTable::internal_addOrLookupFullName(const std::string& fullName)
|
||||
{
|
||||
const auto fullNameIdIter = m_fullNameIds.find(fullName);
|
||||
if (fullNameIdIter != m_fullNameIds.end())
|
||||
{
|
||||
return std::make_pair(fullNameIdIter->second, false);
|
||||
}
|
||||
const size_t fullNameId = m_fullNameInfos.size();
|
||||
m_fullNameInfos.emplace_back(FullNameInfo{});
|
||||
m_fullNameIds.emplace(fullName, fullNameId);
|
||||
return std::make_pair(fullNameId, true);
|
||||
}
|
||||
|
||||
void LogTagManager::NameTable::internal_addOrLookupNameParts(const std::vector<std::string>& nameParts,
|
||||
std::vector<size_t>& namePartIds)
|
||||
{
|
||||
const size_t namePartCount = nameParts.size();
|
||||
namePartIds.resize(namePartCount, ~(size_t)0u);
|
||||
for (size_t namePartIndex = 0u; namePartIndex < namePartCount; ++namePartIndex)
|
||||
{
|
||||
const std::string& namePart = nameParts.at(namePartIndex);
|
||||
const size_t namePartId = internal_addOrLookupNamePart(namePart);
|
||||
namePartIds.at(namePartIndex) = namePartId;
|
||||
}
|
||||
}
|
||||
|
||||
size_t LogTagManager::NameTable::internal_addOrLookupNamePart(const std::string& namePart)
|
||||
{
|
||||
const auto namePartIter = m_namePartIds.find(namePart);
|
||||
if (namePartIter != m_namePartIds.end())
|
||||
{
|
||||
return namePartIter->second;
|
||||
}
|
||||
const size_t namePartId = m_namePartInfos.size();
|
||||
m_namePartInfos.emplace_back(NamePartInfo{});
|
||||
m_namePartIds.emplace(namePart, namePartId);
|
||||
return namePartId;
|
||||
}
|
||||
|
||||
void LogTagManager::NameTable::internal_addCrossReference(size_t fullNameId, const std::vector<size_t>& namePartIds)
|
||||
{
|
||||
const size_t namePartCount = namePartIds.size();
|
||||
for (size_t namePartPos = 0u; namePartPos < namePartCount; ++namePartPos)
|
||||
{
|
||||
const size_t namePartId = namePartIds.at(namePartPos);
|
||||
m_fullNameToNamePartIds.emplace(fullNameId, std::make_pair(namePartId, namePartPos));
|
||||
m_namePartToFullNameIds.emplace(namePartId, std::make_pair(fullNameId, namePartPos));
|
||||
}
|
||||
}
|
||||
|
||||
LogTagManager::FullNameInfo* LogTagManager::NameTable::getFullNameInfo(const std::string& fullName)
|
||||
{
|
||||
const auto fullNameIdIter = m_fullNameIds.find(fullName);
|
||||
if (fullNameIdIter == m_fullNameIds.end())
|
||||
{
|
||||
return nullptr;
|
||||
}
|
||||
const size_t fullNameId = fullNameIdIter->second;
|
||||
return internal_getFullNameInfo(fullNameId);
|
||||
}
|
||||
|
||||
LogTagManager::FullNameInfo* LogTagManager::NameTable::internal_getFullNameInfo(size_t fullNameId)
|
||||
{
|
||||
return std::addressof(m_fullNameInfos.at(fullNameId));
|
||||
}
|
||||
|
||||
LogTagManager::NamePartInfo* LogTagManager::NameTable::internal_getNamePartInfo(size_t namePartId)
|
||||
{
|
||||
return std::addressof(m_namePartInfos.at(namePartId));
|
||||
}
|
||||
|
||||
void LogTagManager::NameTable::internal_findMatchingNamePartsForFullName(FullNameLookupResult& fullNameResult)
|
||||
{
|
||||
const size_t fullNameId = fullNameResult.m_fullNameId;
|
||||
FullNameInfo* fullNameInfo = fullNameResult.m_fullNameInfoPtr;
|
||||
const auto& namePartIds = fullNameResult.m_namePartIds;
|
||||
const size_t namePartCount = namePartIds.size();
|
||||
auto& crossReferences = fullNameResult.m_crossReferences;
|
||||
crossReferences.clear();
|
||||
crossReferences.reserve(namePartCount);
|
||||
for (size_t matchingPos = 0u; matchingPos < namePartCount; ++matchingPos)
|
||||
{
|
||||
const size_t namePartId = namePartIds.at(matchingPos);
|
||||
NamePartInfo* namePartInfo = internal_getNamePartInfo(namePartId);
|
||||
crossReferences.emplace_back(CrossReference(fullNameId, namePartId, matchingPos, fullNameInfo, namePartInfo));
|
||||
}
|
||||
}
|
||||
|
||||
void LogTagManager::NameTable::internal_findMatchingFullNamesForNamePart(NamePartLookupResult& result)
|
||||
{
|
||||
const size_t namePartId = result.m_namePartId;
|
||||
NamePartInfo* namePartInfo = result.m_namePartInfoPtr;
|
||||
const size_t matchingFullNameCount = m_namePartToFullNameIds.count(namePartId);
|
||||
std::vector<CrossReference>& crossReferences = result.m_crossReferences;
|
||||
crossReferences.clear();
|
||||
crossReferences.reserve(matchingFullNameCount);
|
||||
const auto namePartToFullNameIterPair = m_namePartToFullNameIds.equal_range(result.m_namePartId);
|
||||
const auto iterBegin = namePartToFullNameIterPair.first;
|
||||
const auto iterEnd = namePartToFullNameIterPair.second;
|
||||
for (auto iter = iterBegin; iter != iterEnd; ++iter)
|
||||
{
|
||||
const size_t fullNameId = iter->second.first;
|
||||
const size_t matchingPos = iter->second.second;
|
||||
FullNameInfo* fullNameInfo = internal_getFullNameInfo(fullNameId);
|
||||
crossReferences.emplace_back(CrossReference(fullNameId, namePartId, matchingPos, fullNameInfo, namePartInfo));
|
||||
}
|
||||
}
|
||||
|
||||
}}} //namespace
|
284
modules/core/src/utils/logtagmanager.hpp
Normal file
284
modules/core/src/utils/logtagmanager.hpp
Normal file
@ -0,0 +1,284 @@
|
||||
// This file is part of OpenCV project.
|
||||
// It is subject to the license terms in the LICENSE file found in the top-level directory
|
||||
// of this distribution and at http://opencv.org/license.html.
|
||||
|
||||
#ifndef OPENCV_CORE_LOGTAGMANAGER_HPP
|
||||
#define OPENCV_CORE_LOGTAGMANAGER_HPP
|
||||
|
||||
#if 1 // if not already in precompiled headers
|
||||
#include <string>
|
||||
#include <algorithm>
|
||||
#include <functional>
|
||||
#include <unordered_map>
|
||||
#include <mutex>
|
||||
#include <memory>
|
||||
#endif
|
||||
|
||||
#include <opencv2/core/utils/logtag.hpp>
|
||||
#include "logtagconfig.hpp"
|
||||
|
||||
namespace cv {
|
||||
namespace utils {
|
||||
namespace logging {
|
||||
|
||||
// forward declaration
|
||||
class LogTagConfigParser;
|
||||
|
||||
// A lookup table of LogTags using full name, first part of name, and any part of name.
|
||||
// The name parts of a LogTag is delimited by period.
|
||||
//
|
||||
// This class does not handle wildcard characters. The name-matching method can only be
|
||||
// selected by calling the appropriate function.
|
||||
//
|
||||
class LogTagManager
|
||||
{
|
||||
private:
|
||||
// Current implementation does not seem to require recursive mutex;
|
||||
// also, extensible functions (accepting user-provided callback) are not allowed
|
||||
// to call LogTagManger (to prevent iterator invalidation), which needs enforced
|
||||
// with a non-recursive mutex.
|
||||
using MutexType = std::mutex;
|
||||
using LockType = std::lock_guard<MutexType>;
|
||||
|
||||
enum class MatchingScope
|
||||
{
|
||||
None,
|
||||
Full,
|
||||
FirstNamePart,
|
||||
AnyNamePart
|
||||
};
|
||||
|
||||
struct ParsedLevel
|
||||
{
|
||||
LogLevel level;
|
||||
MatchingScope scope;
|
||||
|
||||
ParsedLevel()
|
||||
: level()
|
||||
, scope(MatchingScope::None)
|
||||
{
|
||||
}
|
||||
};
|
||||
|
||||
struct FullNameInfo
|
||||
{
|
||||
LogTag* logTagPtr;
|
||||
ParsedLevel parsedLevel;
|
||||
};
|
||||
|
||||
struct NamePartInfo
|
||||
{
|
||||
ParsedLevel parsedLevel;
|
||||
};
|
||||
|
||||
struct CrossReference
|
||||
{
|
||||
size_t m_fullNameId;
|
||||
size_t m_namePartId;
|
||||
size_t m_matchingPos;
|
||||
FullNameInfo* m_fullNameInfo;
|
||||
NamePartInfo* m_namePartInfo;
|
||||
|
||||
explicit CrossReference(size_t fullNameId, size_t namePartId, size_t matchingPos,
|
||||
FullNameInfo* fullNameInfo, NamePartInfo* namePartInfo)
|
||||
: m_fullNameId(fullNameId)
|
||||
, m_namePartId(namePartId)
|
||||
, m_matchingPos(matchingPos)
|
||||
, m_fullNameInfo(fullNameInfo)
|
||||
, m_namePartInfo(namePartInfo)
|
||||
{
|
||||
}
|
||||
};
|
||||
|
||||
struct FullNameLookupResult
|
||||
{
|
||||
// The full name being looked up
|
||||
std::string m_fullName;
|
||||
|
||||
// The full name being broken down into name parts
|
||||
std::vector<std::string> m_nameParts;
|
||||
|
||||
// The full name ID that is added or looked up from the table
|
||||
size_t m_fullNameId;
|
||||
|
||||
// The name part IDs that are added to or looked up from the table
|
||||
// listed in the same order as m_nameParts
|
||||
std::vector<size_t> m_namePartIds;
|
||||
|
||||
// The information struct for the full name
|
||||
FullNameInfo* m_fullNameInfoPtr;
|
||||
|
||||
// Specifies whether cross references (full names that match the name part)
|
||||
// should be computed.
|
||||
bool m_findCrossReferences;
|
||||
|
||||
// List of all full names that match the given name part.
|
||||
// This field is computed only if m_findCrossReferences is true.
|
||||
std::vector<CrossReference> m_crossReferences;
|
||||
|
||||
explicit FullNameLookupResult(const std::string& fullName)
|
||||
: m_fullName(fullName)
|
||||
, m_nameParts()
|
||||
, m_fullNameId()
|
||||
, m_namePartIds()
|
||||
, m_fullNameInfoPtr()
|
||||
, m_findCrossReferences()
|
||||
, m_crossReferences()
|
||||
{
|
||||
}
|
||||
};
|
||||
|
||||
struct NamePartLookupResult
|
||||
{
|
||||
// The name part being looked up
|
||||
std::string m_namePart;
|
||||
|
||||
// The name part ID that is added or looked up from the table
|
||||
size_t m_namePartId;
|
||||
|
||||
// Information struct ptr for the name part
|
||||
NamePartInfo* m_namePartInfoPtr;
|
||||
|
||||
// Specifies whether cross references (full names that match the name part) should be computed.
|
||||
bool m_findCrossReferences;
|
||||
|
||||
// List of all full names that match the given name part.
|
||||
// This field is computed only if m_findCrossReferences is true.
|
||||
std::vector<CrossReference> m_crossReferences;
|
||||
|
||||
explicit NamePartLookupResult(const std::string& namePart)
|
||||
: m_namePart(namePart)
|
||||
, m_namePartId()
|
||||
, m_namePartInfoPtr()
|
||||
, m_findCrossReferences()
|
||||
, m_crossReferences()
|
||||
{
|
||||
}
|
||||
};
|
||||
|
||||
struct NameTable
|
||||
{
|
||||
// All data structures in this class are append-only. The item count
|
||||
// is being used as an incrementing integer key.
|
||||
|
||||
public:
|
||||
// Full name information struct.
|
||||
std::vector<FullNameInfo> m_fullNameInfos;
|
||||
|
||||
// Name part information struct.
|
||||
std::vector<NamePartInfo> m_namePartInfos;
|
||||
|
||||
// key: full name (string)
|
||||
// value: full name ID
|
||||
// .... (index into the vector of m_fullNameInfos)
|
||||
// .... (key into m_fullNameToNamePartIds)
|
||||
// .... (value.second in m_namePartToFullNameIds)
|
||||
std::unordered_map<std::string, size_t> m_fullNameIds;
|
||||
|
||||
// key: name part (string)
|
||||
// value: name part ID
|
||||
// .... (index into the vector of m_namePartInfos)
|
||||
// .... (key into m_namePartToFullNameIds)
|
||||
// .... (value.second in m_fullNameToNamePartIds)
|
||||
std::unordered_map<std::string, size_t> m_namePartIds;
|
||||
|
||||
// key: full name ID
|
||||
// value.first: name part ID
|
||||
// value.second: occurrence position of name part in the full name
|
||||
std::unordered_multimap<size_t, std::pair<size_t, size_t>> m_fullNameToNamePartIds;
|
||||
|
||||
// key: name part ID
|
||||
// value.first: full name ID
|
||||
// value.second: occurrence position of name part in the full name
|
||||
std::unordered_multimap<size_t, std::pair<size_t, size_t>> m_namePartToFullNameIds;
|
||||
|
||||
public:
|
||||
void addOrLookupFullName(FullNameLookupResult& result);
|
||||
void addOrLookupNamePart(NamePartLookupResult& result);
|
||||
FullNameInfo* getFullNameInfo(const std::string& fullName);
|
||||
|
||||
private:
|
||||
// Add or get full name. Does not compute name parts or access them in the table.
|
||||
// Returns the full name ID, and a bool indicating if the full name is new.
|
||||
std::pair<size_t, bool> internal_addOrLookupFullName(const std::string& fullName);
|
||||
|
||||
// Add or get multiple name parts. Saves name part IDs into a vector.
|
||||
void internal_addOrLookupNameParts(const std::vector<std::string>& nameParts, std::vector<size_t>& namePartIds);
|
||||
|
||||
// Add or get name part. Returns namePartId.
|
||||
size_t internal_addOrLookupNamePart(const std::string& namePart);
|
||||
|
||||
// For each name part ID, insert the tuples (full name, name part ID, name part position)
|
||||
// into the cross reference table
|
||||
void internal_addCrossReference(size_t fullNameId, const std::vector<size_t>& namePartIds);
|
||||
|
||||
// Gather pointer for full name info struct.
|
||||
// Note: The pointer is interior to the table vector. The pointers are invalidated
|
||||
// if the table is modified.
|
||||
FullNameInfo* internal_getFullNameInfo(size_t fullNameId);
|
||||
|
||||
// Gather pointers for name part info struct.
|
||||
// Note: The pointers are interior to the table vector. The pointers are invalidated
|
||||
// if the table is modified.
|
||||
NamePartInfo* internal_getNamePartInfo(size_t namePartId);
|
||||
|
||||
void internal_findMatchingNamePartsForFullName(FullNameLookupResult& fullNameResult);
|
||||
void internal_findMatchingFullNamesForNamePart(NamePartLookupResult& result);
|
||||
};
|
||||
|
||||
public:
|
||||
LogTagManager(LogLevel defaultUnconfiguredGlobalLevel);
|
||||
~LogTagManager();
|
||||
|
||||
public:
|
||||
// Parse and apply the config string.
|
||||
void setConfigString(const std::string& configString, bool apply = true);
|
||||
|
||||
// Gets the config parser. This is necessary to retrieve the list of malformed strings.
|
||||
LogTagConfigParser& getConfigParser() const;
|
||||
|
||||
// Add (register) the log tag.
|
||||
// Note, passing in nullptr as value is equivalent to unassigning.
|
||||
void assign(const std::string& fullName, LogTag* ptr);
|
||||
|
||||
// Unassign the log tag. This is equivalent to calling assign with nullptr value.
|
||||
void unassign(const std::string& fullName);
|
||||
|
||||
// Retrieve the log tag by exact name.
|
||||
LogTag* get(const std::string& fullName);
|
||||
|
||||
// Changes the log level of the tag having the exact full name.
|
||||
void setLevelByFullName(const std::string& fullName, LogLevel level);
|
||||
|
||||
// Changes the log level of the tags matching the first part of the name.
|
||||
void setLevelByFirstPart(const std::string& firstPart, LogLevel level);
|
||||
|
||||
// Changes the log level of the tags matching any part of the name.
|
||||
void setLevelByAnyPart(const std::string& anyPart, LogLevel level);
|
||||
|
||||
// Changes the log level of the tags with matching name part according
|
||||
// to the specified scope.
|
||||
void setLevelByNamePart(const std::string& namePart, LogLevel level, MatchingScope scope);
|
||||
|
||||
private:
|
||||
bool internal_applyFullNameConfigToTag(FullNameInfo& fullNameInfo);
|
||||
bool internal_applyNamePartConfigToSpecificTag(FullNameLookupResult& fullNameResult);
|
||||
void internal_applyNamePartConfigToMatchingTags(NamePartLookupResult& namePartResult);
|
||||
|
||||
private:
|
||||
static std::vector<std::string> splitNameParts(const std::string& fullName);
|
||||
static bool internal_isNamePartMatch(MatchingScope scope, size_t matchingPos);
|
||||
|
||||
private:
|
||||
static constexpr const char* m_globalName = "global";
|
||||
|
||||
private:
|
||||
mutable MutexType m_mutex;
|
||||
std::unique_ptr<LogTag> m_globalLogTag;
|
||||
NameTable m_nameTable;
|
||||
std::shared_ptr<LogTagConfigParser> m_config;
|
||||
};
|
||||
|
||||
}}} //namespace
|
||||
|
||||
#endif //OPENCV_CORE_LOGTAGMANAGER_HPP
|
281
modules/core/test/test_logtagconfigparser.cpp
Normal file
281
modules/core/test/test_logtagconfigparser.cpp
Normal file
@ -0,0 +1,281 @@
|
||||
// This file is part of OpenCV project.
|
||||
// It is subject to the license terms in the LICENSE file found in the top-level directory
|
||||
// of this distribution and at http://opencv.org/license.html.
|
||||
|
||||
#include "test_precomp.hpp"
|
||||
#include <opencv2/core/utils/logger.hpp>
|
||||
#include "../src/utils/logtagmanager.hpp"
|
||||
#include "../src/utils/logtagconfigparser.hpp"
|
||||
|
||||
// Because "LogTagConfigParser" isn't exported from "opencv_core", the only way
|
||||
// to perform test is to compile the source code into "opencv_test_core".
|
||||
// This workaround may cause step debugger breakpoints to work unreliably.
|
||||
#if 1
|
||||
#include "../src/utils/logtagconfigparser.cpp"
|
||||
#endif
|
||||
|
||||
using cv::utils::logging::LogTagConfigParser;
|
||||
|
||||
namespace opencv_test {
|
||||
namespace {
|
||||
|
||||
typedef testing::TestWithParam<tuple<std::string, cv::utils::logging::LogLevel>> GlobalShouldSucceedTests;
|
||||
|
||||
TEST_P(GlobalShouldSucceedTests, globalCases)
|
||||
{
|
||||
const std::string input = get<0>(GetParam());
|
||||
const cv::utils::logging::LogLevel expectedLevel = get<1>(GetParam());
|
||||
LogTagConfigParser parser;
|
||||
parser.parse(input);
|
||||
EXPECT_FALSE(parser.hasMalformed());
|
||||
EXPECT_EQ(parser.getMalformed().size(), 0u) << "Malformed list should be empty";
|
||||
EXPECT_TRUE(parser.getGlobalConfig().isGlobal);
|
||||
EXPECT_EQ(parser.getGlobalConfig().level, expectedLevel);
|
||||
EXPECT_EQ(parser.getFullNameConfigs().size(), 0u) << "Specifying global log level should not emit full names";
|
||||
EXPECT_EQ(parser.getFirstPartConfigs().size(), 0u) << "Specifying global log level should not emit first name part result";
|
||||
EXPECT_EQ(parser.getAnyPartConfigs().size(), 0u) << "Specifying global log level should not emit any name part result";
|
||||
}
|
||||
|
||||
INSTANTIATE_TEST_CASE_P(Core_LogTagConfigParser, GlobalShouldSucceedTests,
|
||||
testing::Values(
|
||||
// Following test cases omit the name part
|
||||
std::make_tuple("S", cv::utils::logging::LOG_LEVEL_SILENT),
|
||||
std::make_tuple("SILENT", cv::utils::logging::LOG_LEVEL_SILENT),
|
||||
std::make_tuple("F", cv::utils::logging::LOG_LEVEL_FATAL),
|
||||
std::make_tuple("FATAL", cv::utils::logging::LOG_LEVEL_FATAL),
|
||||
std::make_tuple("E", cv::utils::logging::LOG_LEVEL_ERROR),
|
||||
std::make_tuple("ERROR", cv::utils::logging::LOG_LEVEL_ERROR),
|
||||
std::make_tuple("W", cv::utils::logging::LOG_LEVEL_WARNING),
|
||||
std::make_tuple("WARN", cv::utils::logging::LOG_LEVEL_WARNING),
|
||||
std::make_tuple("WARNING", cv::utils::logging::LOG_LEVEL_WARNING),
|
||||
std::make_tuple("I", cv::utils::logging::LOG_LEVEL_INFO),
|
||||
std::make_tuple("INFO", cv::utils::logging::LOG_LEVEL_INFO),
|
||||
std::make_tuple("D", cv::utils::logging::LOG_LEVEL_DEBUG),
|
||||
std::make_tuple("DEBUG", cv::utils::logging::LOG_LEVEL_DEBUG),
|
||||
std::make_tuple("V", cv::utils::logging::LOG_LEVEL_VERBOSE),
|
||||
std::make_tuple("VERBOSE", cv::utils::logging::LOG_LEVEL_VERBOSE),
|
||||
// Following test cases uses a single asterisk as name
|
||||
std::make_tuple("*:S", cv::utils::logging::LOG_LEVEL_SILENT),
|
||||
std::make_tuple("*:SILENT", cv::utils::logging::LOG_LEVEL_SILENT),
|
||||
std::make_tuple("*:V", cv::utils::logging::LOG_LEVEL_VERBOSE),
|
||||
std::make_tuple("*:VERBOSE", cv::utils::logging::LOG_LEVEL_VERBOSE)
|
||||
)
|
||||
);
|
||||
|
||||
// GlobalShouldSucceedPairedTests, globalNameHandling
|
||||
//
|
||||
// The following tests use a strategy of performing two tests as a pair, and require the pair
|
||||
// to succeed, in order to avoid false negatives due to default settings.
|
||||
// The first input string is supposed to set global to SILENT, the second input string VERBOSE.
|
||||
|
||||
typedef testing::TestWithParam<tuple<std::string, std::string>> GlobalShouldSucceedPairedTests;
|
||||
|
||||
TEST_P(GlobalShouldSucceedPairedTests, globalNameHandling)
|
||||
{
|
||||
const auto firstExpected = cv::utils::logging::LOG_LEVEL_SILENT;
|
||||
const auto secondExpected = cv::utils::logging::LOG_LEVEL_VERBOSE;
|
||||
//
|
||||
const std::string firstInput = get<0>(GetParam());
|
||||
LogTagConfigParser firstParser;
|
||||
firstParser.parse(firstInput);
|
||||
ASSERT_FALSE(firstParser.hasMalformed());
|
||||
ASSERT_EQ(firstParser.getMalformed().size(), 0u) << "Malformed list should be empty";
|
||||
ASSERT_TRUE(firstParser.getGlobalConfig().isGlobal);
|
||||
ASSERT_EQ(firstParser.getFullNameConfigs().size(), 0u) << "Specifying global log level should not emit full names";
|
||||
ASSERT_EQ(firstParser.getFirstPartConfigs().size(), 0u) << "Specifying global log level should not emit first name part result";
|
||||
ASSERT_EQ(firstParser.getAnyPartConfigs().size(), 0u) << "Specifying global log level should not emit any name part result";
|
||||
const cv::utils::logging::LogLevel firstActual = firstParser.getGlobalConfig().level;
|
||||
//
|
||||
const std::string secondInput = get<1>(GetParam());
|
||||
LogTagConfigParser secondParser;
|
||||
secondParser.parse(secondInput);
|
||||
ASSERT_FALSE(secondParser.hasMalformed());
|
||||
ASSERT_EQ(secondParser.getMalformed().size(), 0u) << "Malformed list should be empty";
|
||||
ASSERT_TRUE(secondParser.getGlobalConfig().isGlobal);
|
||||
ASSERT_EQ(secondParser.getFullNameConfigs().size(), 0u) << "Specifying global log level should not emit full names";
|
||||
ASSERT_EQ(secondParser.getFirstPartConfigs().size(), 0u) << "Specifying global log level should not emit first name part result";
|
||||
ASSERT_EQ(secondParser.getAnyPartConfigs().size(), 0u) << "Specifying global log level should not emit any name part result";
|
||||
const cv::utils::logging::LogLevel secondActual = secondParser.getGlobalConfig().level;
|
||||
//
|
||||
EXPECT_EQ(firstActual, firstExpected);
|
||||
EXPECT_EQ(secondActual, secondExpected);
|
||||
}
|
||||
|
||||
// Following test cases uses lowercase "global" as name
|
||||
INSTANTIATE_TEST_CASE_P(Core_LogTagConfigParser, GlobalShouldSucceedPairedTests,
|
||||
testing::Values(
|
||||
std::make_tuple("global:S", "global:V"),
|
||||
std::make_tuple("global:SILENT", "global:VERBOSE")
|
||||
)
|
||||
);
|
||||
|
||||
// In the next few smoke tests, the use of EXPECT versus ASSERT is as follows.
|
||||
// Each test will try to read the first element from one of the vector results.
|
||||
// Prior to that, the vector need to be ASSERT'ed to have at least one element.
|
||||
// All remaining assertions in the test body would use EXPECT instead.
|
||||
|
||||
TEST(Core_LogTagConfigParser, FullNameSmokeTest)
|
||||
{
|
||||
LogTagConfigParser parser;
|
||||
parser.parse("something:S");
|
||||
EXPECT_FALSE(parser.hasMalformed());
|
||||
EXPECT_EQ(parser.getMalformed().size(), 0u);
|
||||
ASSERT_EQ(parser.getFullNameConfigs().size(), 1u);
|
||||
EXPECT_EQ(parser.getFirstPartConfigs().size(), 0u);
|
||||
EXPECT_EQ(parser.getAnyPartConfigs().size(), 0u);
|
||||
EXPECT_STREQ(parser.getFullNameConfigs().at(0u).namePart.c_str(), "something");
|
||||
}
|
||||
|
||||
TEST(Core_LogTagConfigParser, FirstPartSmokeTest_NoPeriod)
|
||||
{
|
||||
LogTagConfigParser parser;
|
||||
parser.parse("something*:S");
|
||||
EXPECT_FALSE(parser.hasMalformed());
|
||||
EXPECT_EQ(parser.getMalformed().size(), 0u);
|
||||
EXPECT_EQ(parser.getFullNameConfigs().size(), 0u);
|
||||
ASSERT_EQ(parser.getFirstPartConfigs().size(), 1u);
|
||||
EXPECT_EQ(parser.getAnyPartConfigs().size(), 0u);
|
||||
EXPECT_STREQ(parser.getFirstPartConfigs().at(0u).namePart.c_str(), "something");
|
||||
}
|
||||
|
||||
TEST(Core_LogTagConfigParser, FirstPartSmokeTest_WithPeriod)
|
||||
{
|
||||
LogTagConfigParser parser;
|
||||
parser.parse("something.*:S");
|
||||
EXPECT_FALSE(parser.hasMalformed());
|
||||
EXPECT_EQ(parser.getMalformed().size(), 0u);
|
||||
EXPECT_EQ(parser.getFullNameConfigs().size(), 0u);
|
||||
ASSERT_EQ(parser.getFirstPartConfigs().size(), 1u);
|
||||
EXPECT_EQ(parser.getAnyPartConfigs().size(), 0u);
|
||||
EXPECT_STREQ(parser.getFirstPartConfigs().at(0u).namePart.c_str(), "something");
|
||||
}
|
||||
|
||||
TEST(Core_LogTagConfigParser, AnyPartSmokeTest_PrecedeAsterisk_NoPeriod)
|
||||
{
|
||||
LogTagConfigParser parser;
|
||||
parser.parse("*something:S");
|
||||
EXPECT_FALSE(parser.hasMalformed());
|
||||
EXPECT_EQ(parser.getMalformed().size(), 0u);
|
||||
EXPECT_EQ(parser.getFullNameConfigs().size(), 0u);
|
||||
EXPECT_EQ(parser.getFirstPartConfigs().size(), 0u);
|
||||
ASSERT_EQ(parser.getAnyPartConfigs().size(), 1u);
|
||||
EXPECT_STREQ(parser.getAnyPartConfigs().at(0u).namePart.c_str(), "something");
|
||||
}
|
||||
|
||||
TEST(Core_LogTagConfigParser, AnyPartSmokeTest_PrecedeAsterisk_WithPeriod)
|
||||
{
|
||||
LogTagConfigParser parser;
|
||||
parser.parse("*.something:S");
|
||||
EXPECT_FALSE(parser.hasMalformed());
|
||||
EXPECT_EQ(parser.getMalformed().size(), 0u);
|
||||
EXPECT_EQ(parser.getFullNameConfigs().size(), 0u);
|
||||
EXPECT_EQ(parser.getFirstPartConfigs().size(), 0u);
|
||||
ASSERT_EQ(parser.getAnyPartConfigs().size(), 1u);
|
||||
EXPECT_STREQ(parser.getAnyPartConfigs().at(0u).namePart.c_str(), "something");
|
||||
}
|
||||
|
||||
TEST(Core_LogTagConfigParser, AnyPartSmokeTest_PrecedeFollowAsterisks_NoPeriod)
|
||||
{
|
||||
LogTagConfigParser parser;
|
||||
parser.parse("*something*:S");
|
||||
EXPECT_FALSE(parser.hasMalformed());
|
||||
EXPECT_EQ(parser.getMalformed().size(), 0u);
|
||||
EXPECT_EQ(parser.getFullNameConfigs().size(), 0u);
|
||||
EXPECT_EQ(parser.getFirstPartConfigs().size(), 0u);
|
||||
ASSERT_EQ(parser.getAnyPartConfigs().size(), 1u);
|
||||
EXPECT_STREQ(parser.getAnyPartConfigs().at(0u).namePart.c_str(), "something");
|
||||
}
|
||||
|
||||
TEST(Core_LogTagConfigParser, AnyPartSmokeTest_PrecedeFollowAsterisks_WithPeriod)
|
||||
{
|
||||
LogTagConfigParser parser;
|
||||
parser.parse("*.something.*:S");
|
||||
EXPECT_FALSE(parser.hasMalformed());
|
||||
EXPECT_EQ(parser.getMalformed().size(), 0u);
|
||||
EXPECT_EQ(parser.getFullNameConfigs().size(), 0u);
|
||||
EXPECT_EQ(parser.getFirstPartConfigs().size(), 0u);
|
||||
ASSERT_EQ(parser.getAnyPartConfigs().size(), 1u);
|
||||
EXPECT_STREQ(parser.getAnyPartConfigs().at(0u).namePart.c_str(), "something");
|
||||
}
|
||||
|
||||
TEST(Core_LogTagConfigParser, FullName_EqualSign_ShouldSucceed)
|
||||
{
|
||||
LogTagConfigParser parser;
|
||||
parser.parse("something=S");
|
||||
EXPECT_FALSE(parser.hasMalformed());
|
||||
EXPECT_EQ(parser.getMalformed().size(), 0u);
|
||||
ASSERT_EQ(parser.getFullNameConfigs().size(), 1u);
|
||||
EXPECT_EQ(parser.getFirstPartConfigs().size(), 0u);
|
||||
EXPECT_EQ(parser.getAnyPartConfigs().size(), 0u);
|
||||
EXPECT_STREQ(parser.getFullNameConfigs().at(0u).namePart.c_str(), "something");
|
||||
}
|
||||
|
||||
TEST(Core_LogTagConfigParser, FirstPart_EqualSign_ShouldSucceed)
|
||||
{
|
||||
LogTagConfigParser parser;
|
||||
parser.parse("something*=S");
|
||||
EXPECT_FALSE(parser.hasMalformed());
|
||||
EXPECT_EQ(parser.getMalformed().size(), 0u);
|
||||
EXPECT_EQ(parser.getFullNameConfigs().size(), 0u);
|
||||
ASSERT_EQ(parser.getFirstPartConfigs().size(), 1u);
|
||||
EXPECT_EQ(parser.getAnyPartConfigs().size(), 0u);
|
||||
EXPECT_STREQ(parser.getFirstPartConfigs().at(0u).namePart.c_str(), "something");
|
||||
}
|
||||
|
||||
TEST(Core_LogTagConfigParser, AnyPart_EqualSign_ShouldSucceed)
|
||||
{
|
||||
LogTagConfigParser parser;
|
||||
parser.parse("*something*=S");
|
||||
EXPECT_FALSE(parser.hasMalformed());
|
||||
EXPECT_EQ(parser.getMalformed().size(), 0u);
|
||||
EXPECT_EQ(parser.getFullNameConfigs().size(), 0u);
|
||||
EXPECT_EQ(parser.getFirstPartConfigs().size(), 0u);
|
||||
ASSERT_EQ(parser.getAnyPartConfigs().size(), 1u);
|
||||
EXPECT_STREQ(parser.getAnyPartConfigs().at(0u).namePart.c_str(), "something");
|
||||
}
|
||||
|
||||
TEST(Core_LogTagConfigParser, DuplicateColon_ShouldFail)
|
||||
{
|
||||
LogTagConfigParser parser;
|
||||
parser.parse("something::S");
|
||||
EXPECT_TRUE(parser.hasMalformed());
|
||||
EXPECT_EQ(parser.getMalformed().size(), 1u);
|
||||
EXPECT_EQ(parser.getFullNameConfigs().size(), 0u);
|
||||
EXPECT_EQ(parser.getFirstPartConfigs().size(), 0u);
|
||||
EXPECT_EQ(parser.getAnyPartConfigs().size(), 0u);
|
||||
}
|
||||
|
||||
TEST(Core_LogTagConfigParser, DuplicateEqual_ShouldFail)
|
||||
{
|
||||
LogTagConfigParser parser;
|
||||
parser.parse("something==S");
|
||||
EXPECT_TRUE(parser.hasMalformed());
|
||||
EXPECT_EQ(parser.getMalformed().size(), 1u);
|
||||
EXPECT_EQ(parser.getFullNameConfigs().size(), 0u);
|
||||
EXPECT_EQ(parser.getFirstPartConfigs().size(), 0u);
|
||||
EXPECT_EQ(parser.getAnyPartConfigs().size(), 0u);
|
||||
}
|
||||
|
||||
TEST(Core_LogTagConfigParser, DuplicateColonAndEqual_ShouldFail)
|
||||
{
|
||||
LogTagConfigParser parser;
|
||||
parser.parse("something:=S");
|
||||
EXPECT_TRUE(parser.hasMalformed());
|
||||
EXPECT_EQ(parser.getMalformed().size(), 1u);
|
||||
EXPECT_EQ(parser.getFullNameConfigs().size(), 0u);
|
||||
EXPECT_EQ(parser.getFirstPartConfigs().size(), 0u);
|
||||
EXPECT_EQ(parser.getAnyPartConfigs().size(), 0u);
|
||||
}
|
||||
|
||||
TEST(Core_LogTagConfigParser, DuplicateEqualAndColon_ShouldFail)
|
||||
{
|
||||
LogTagConfigParser parser;
|
||||
parser.parse("something=:S");
|
||||
EXPECT_TRUE(parser.hasMalformed());
|
||||
EXPECT_EQ(parser.getMalformed().size(), 1u);
|
||||
EXPECT_EQ(parser.getFullNameConfigs().size(), 0u);
|
||||
EXPECT_EQ(parser.getFirstPartConfigs().size(), 0u);
|
||||
EXPECT_EQ(parser.getAnyPartConfigs().size(), 0u);
|
||||
}
|
||||
|
||||
}} // namespace
|
645
modules/core/test/test_logtagmanager.cpp
Normal file
645
modules/core/test/test_logtagmanager.cpp
Normal file
@ -0,0 +1,645 @@
|
||||
// This file is part of OpenCV project.
|
||||
// It is subject to the license terms in the LICENSE file found in the top-level directory
|
||||
// of this distribution and at http://opencv.org/license.html.
|
||||
|
||||
#include "test_precomp.hpp"
|
||||
#include <opencv2/core/utils/logger.hpp>
|
||||
|
||||
#include "../src/utils/logtagmanager.hpp"
|
||||
#include "../src/utils/logtagconfigparser.hpp"
|
||||
|
||||
// Because "LogTagManager" isn't exported from "opencv_core", the only way
|
||||
// to perform test is to compile the source code into "opencv_test_core".
|
||||
// This workaround may cause step debugger breakpoints to work unreliably.
|
||||
#if 1
|
||||
#include "../src/utils/logtagmanager.cpp"
|
||||
#endif
|
||||
|
||||
namespace opencv_test {
|
||||
namespace {
|
||||
|
||||
using LogLevel = cv::utils::logging::LogLevel;
|
||||
using LogTag = cv::utils::logging::LogTag;
|
||||
using LogTagManager = cv::utils::logging::LogTagManager;
|
||||
|
||||
// Value to initialize log tag constructors
|
||||
static constexpr const LogLevel constTestLevelBegin = cv::utils::logging::LOG_LEVEL_SILENT;
|
||||
|
||||
// Value to be set as part of test (to simulate runtime changes)
|
||||
static constexpr const LogLevel constTestLevelChanged = cv::utils::logging::LOG_LEVEL_VERBOSE;
|
||||
|
||||
// An alternate value to initialize log tag constructors,
|
||||
// for test cases where two distinct initialization values are needed.
|
||||
static constexpr const LogLevel constTestLevelAltBegin = cv::utils::logging::LOG_LEVEL_FATAL;
|
||||
|
||||
// An alternate value to be set as part of test (to simulate runtime changes),
|
||||
// for test cases where two distinct runtime set values are needed.
|
||||
static constexpr const LogLevel constTestLevelAltChanged = cv::utils::logging::LOG_LEVEL_DEBUG;
|
||||
|
||||
// Enums for specifying which LogTagManager method to call.
|
||||
// Used in parameterized tests.
|
||||
enum class ByWhat
|
||||
{
|
||||
ByFullName = 0,
|
||||
ByFirstPart = 1,
|
||||
ByAnyPart = 2
|
||||
};
|
||||
|
||||
std::ostream& operator<< (std::ostream& strm, ByWhat byWhat)
|
||||
{
|
||||
switch (byWhat)
|
||||
{
|
||||
case ByWhat::ByFullName:
|
||||
strm << "ByFullName";
|
||||
break;
|
||||
case ByWhat::ByFirstPart:
|
||||
strm << "ByFirstPart";
|
||||
break;
|
||||
case ByWhat::ByAnyPart:
|
||||
strm << "ByAnyPart";
|
||||
break;
|
||||
default:
|
||||
strm << "(invalid ByWhat enum" << (int)byWhat << ")";
|
||||
break;
|
||||
}
|
||||
return strm;
|
||||
}
|
||||
|
||||
// Enums for describing relative timing.
|
||||
// Used in parameterized tests.
|
||||
enum class Timing
|
||||
{
|
||||
Never = 0,
|
||||
Before = 1,
|
||||
After = 2
|
||||
};
|
||||
|
||||
std::ostream& operator<< (std::ostream& strm, Timing timing)
|
||||
{
|
||||
switch (timing)
|
||||
{
|
||||
case Timing::Never:
|
||||
strm << "Never";
|
||||
break;
|
||||
case Timing::Before:
|
||||
strm << "Before";
|
||||
break;
|
||||
case Timing::After:
|
||||
strm << "After";
|
||||
break;
|
||||
default:
|
||||
strm << "(invalid Timing enum" << (int)timing << ")";
|
||||
break;
|
||||
}
|
||||
return strm;
|
||||
}
|
||||
|
||||
// Enums for selecting the substrings used in substring confusion tests.
|
||||
enum class SubstringType
|
||||
{
|
||||
Prefix = 0,
|
||||
Midstring = 1,
|
||||
Suffix = 2,
|
||||
Straddle = 3
|
||||
};
|
||||
|
||||
std::ostream& operator<< (std::ostream& strm, SubstringType substringType)
|
||||
{
|
||||
switch (substringType)
|
||||
{
|
||||
case SubstringType::Prefix:
|
||||
strm << "Prefix";
|
||||
break;
|
||||
case SubstringType::Midstring:
|
||||
strm << "Midstring";
|
||||
break;
|
||||
case SubstringType::Suffix:
|
||||
strm << "Suffix";
|
||||
break;
|
||||
case SubstringType::Straddle:
|
||||
strm << "Straddle";
|
||||
break;
|
||||
default:
|
||||
strm << "(invalid SubstringType enum: " << (int)substringType << ")";
|
||||
break;
|
||||
}
|
||||
return strm;
|
||||
}
|
||||
|
||||
// A base fixture consisting of the LogTagManager.
|
||||
// Note that an instance of LogTagManager contains its own instance of "global" log tag.
|
||||
class LogTagManagerTestFixture
|
||||
: public ::testing::Test
|
||||
{
|
||||
protected:
|
||||
LogTagManager m_logTagManager;
|
||||
|
||||
public:
|
||||
LogTagManagerTestFixture(LogLevel initGlobalLogLevel)
|
||||
: m_logTagManager(initGlobalLogLevel)
|
||||
{
|
||||
}
|
||||
|
||||
~LogTagManagerTestFixture()
|
||||
{
|
||||
}
|
||||
};
|
||||
|
||||
// LogTagManagerGlobalSmokeTest verifies that the "global" log tag works as intended.
|
||||
class LogTagManagerGlobalSmokeTest
|
||||
: public LogTagManagerTestFixture
|
||||
{
|
||||
protected:
|
||||
LogTag* m_actualGlobalLogTag;
|
||||
|
||||
protected:
|
||||
static constexpr const char* m_globalTagName = "global";
|
||||
|
||||
public:
|
||||
LogTagManagerGlobalSmokeTest()
|
||||
: LogTagManagerTestFixture(constTestLevelBegin)
|
||||
, m_actualGlobalLogTag(nullptr)
|
||||
{
|
||||
}
|
||||
};
|
||||
|
||||
TEST_F(LogTagManagerGlobalSmokeTest, AfterCtorCanGetGlobalWithoutAssign)
|
||||
{
|
||||
EXPECT_NE(m_logTagManager.get(m_globalTagName), nullptr);
|
||||
}
|
||||
|
||||
TEST_F(LogTagManagerGlobalSmokeTest, AfterCtorGetGlobalHasDefaultLevel)
|
||||
{
|
||||
auto globalLogTag = m_logTagManager.get(m_globalTagName);
|
||||
ASSERT_NE(globalLogTag, nullptr);
|
||||
EXPECT_EQ(globalLogTag->level, constTestLevelBegin);
|
||||
}
|
||||
|
||||
TEST_F(LogTagManagerGlobalSmokeTest, AfterCtorCanSetGlobalByFullName)
|
||||
{
|
||||
m_logTagManager.setLevelByFullName(m_globalTagName, constTestLevelChanged);
|
||||
auto globalLogTag = m_logTagManager.get(m_globalTagName);
|
||||
ASSERT_NE(globalLogTag, nullptr);
|
||||
EXPECT_EQ(globalLogTag->level, constTestLevelChanged);
|
||||
}
|
||||
|
||||
#if 0
|
||||
// "global" level is not supposed to be settable by name-parts.
|
||||
// Therefore this test code is supposed to fail.
|
||||
TEST_F(LogTagManagerGlobalSmokeTest, DISABLED_AfterCtorCanSetGlobalByFirstPart)
|
||||
{
|
||||
m_logTagManager.setLevelByFirstPart(m_globalTagName, constTestLevelChanged);
|
||||
auto globalLogTag = m_logTagManager.get(m_globalTagName);
|
||||
ASSERT_NE(globalLogTag, nullptr);
|
||||
EXPECT_EQ(globalLogTag->level, constTestLevelChanged);
|
||||
}
|
||||
#endif
|
||||
|
||||
#if 0
|
||||
// "global" level is not supposed to be settable by name-parts.
|
||||
// Therefore this test code is supposed to fail.
|
||||
TEST_F(LogTagManagerGlobalSmokeTest, DISABLED_AfterCtorCanSetGlobalByAnyPart)
|
||||
{
|
||||
m_logTagManager.setLevelByAnyPart(m_globalTagName, constTestLevelChanged);
|
||||
auto globalLogTag = m_logTagManager.get(m_globalTagName);
|
||||
ASSERT_NE(globalLogTag, nullptr);
|
||||
EXPECT_EQ(globalLogTag->level, constTestLevelChanged);
|
||||
}
|
||||
#endif
|
||||
|
||||
// LogTagManagerNonGlobalSmokeTest performs basic smoke tests to verify that
|
||||
// a log tag (that is not the "global" log tag) can be assigned, and its
|
||||
// log level can be configured.
|
||||
class LogTagManagerNonGlobalSmokeTest
|
||||
: public LogTagManagerTestFixture
|
||||
{
|
||||
protected:
|
||||
LogTag m_something;
|
||||
|
||||
protected:
|
||||
static constexpr const char* m_somethingTagName = "something";
|
||||
|
||||
public:
|
||||
LogTagManagerNonGlobalSmokeTest()
|
||||
: LogTagManagerTestFixture(constTestLevelAltBegin)
|
||||
, m_something(m_somethingTagName, constTestLevelBegin)
|
||||
{
|
||||
}
|
||||
};
|
||||
|
||||
TEST_F(LogTagManagerNonGlobalSmokeTest, CanAssignTagAndThenGet)
|
||||
{
|
||||
m_logTagManager.assign(m_something.name, &m_something);
|
||||
ASSERT_EQ(m_logTagManager.get(m_somethingTagName), &m_something);
|
||||
}
|
||||
|
||||
TEST_F(LogTagManagerNonGlobalSmokeTest, CanAssignTagAndThenGetAndShouldHaveDefaultLevel)
|
||||
{
|
||||
m_logTagManager.assign(m_something.name, &m_something);
|
||||
ASSERT_EQ(m_logTagManager.get(m_somethingTagName), &m_something);
|
||||
EXPECT_EQ(m_logTagManager.get(m_somethingTagName)->level, constTestLevelBegin);
|
||||
}
|
||||
|
||||
TEST_F(LogTagManagerNonGlobalSmokeTest, CanSetLevelByFullNameBeforeAssignTag)
|
||||
{
|
||||
m_logTagManager.setLevelByFullName(m_somethingTagName, constTestLevelChanged);
|
||||
m_logTagManager.assign(m_something.name, &m_something);
|
||||
ASSERT_EQ(m_logTagManager.get(m_somethingTagName), &m_something);
|
||||
EXPECT_EQ(m_logTagManager.get(m_somethingTagName)->level, constTestLevelChanged);
|
||||
EXPECT_NE(m_logTagManager.get(m_somethingTagName)->level, constTestLevelBegin) << "Should not be left unchanged (default value)";
|
||||
}
|
||||
|
||||
TEST_F(LogTagManagerNonGlobalSmokeTest, CanSetLevelByFirstPartBeforeAssignTag)
|
||||
{
|
||||
m_logTagManager.setLevelByFirstPart(m_somethingTagName, constTestLevelChanged);
|
||||
m_logTagManager.assign(m_something.name, &m_something);
|
||||
ASSERT_EQ(m_logTagManager.get(m_somethingTagName), &m_something);
|
||||
EXPECT_EQ(m_logTagManager.get(m_somethingTagName)->level, constTestLevelChanged);
|
||||
EXPECT_NE(m_logTagManager.get(m_somethingTagName)->level, constTestLevelBegin) << "Should not be left unchanged (default value)";
|
||||
}
|
||||
|
||||
TEST_F(LogTagManagerNonGlobalSmokeTest, CanSetLevelByAnyPartBeforeAssignTag)
|
||||
{
|
||||
m_logTagManager.setLevelByAnyPart(m_somethingTagName, constTestLevelChanged);
|
||||
m_logTagManager.assign(m_something.name, &m_something);
|
||||
ASSERT_EQ(m_logTagManager.get(m_somethingTagName), &m_something);
|
||||
EXPECT_EQ(m_logTagManager.get(m_somethingTagName)->level, constTestLevelChanged);
|
||||
EXPECT_NE(m_logTagManager.get(m_somethingTagName)->level, constTestLevelBegin) << "Should not be left unchanged (default value)";
|
||||
}
|
||||
|
||||
TEST_F(LogTagManagerNonGlobalSmokeTest, CanSetLevelByFullNameAfterAssignTag)
|
||||
{
|
||||
m_logTagManager.assign(m_something.name, &m_something);
|
||||
m_logTagManager.setLevelByFullName(m_somethingTagName, constTestLevelChanged);
|
||||
ASSERT_EQ(m_logTagManager.get(m_somethingTagName), &m_something);
|
||||
EXPECT_EQ(m_logTagManager.get(m_somethingTagName)->level, constTestLevelChanged);
|
||||
EXPECT_NE(m_logTagManager.get(m_somethingTagName)->level, constTestLevelBegin) << "Should not be left unchanged (default value)";
|
||||
}
|
||||
|
||||
TEST_F(LogTagManagerNonGlobalSmokeTest, CanSetLevelByFirstPartAfterAssignTag)
|
||||
{
|
||||
m_logTagManager.assign(m_something.name, &m_something);
|
||||
m_logTagManager.setLevelByFirstPart(m_somethingTagName, constTestLevelChanged);
|
||||
ASSERT_EQ(m_logTagManager.get(m_somethingTagName), &m_something);
|
||||
EXPECT_EQ(m_logTagManager.get(m_somethingTagName)->level, constTestLevelChanged);
|
||||
EXPECT_NE(m_logTagManager.get(m_somethingTagName)->level, constTestLevelBegin) << "Should not be left unchanged (default value)";
|
||||
}
|
||||
|
||||
TEST_F(LogTagManagerNonGlobalSmokeTest, CanSetLevelByAnyPartAfterAssignTag)
|
||||
{
|
||||
m_logTagManager.assign(m_something.name, &m_something);
|
||||
m_logTagManager.setLevelByAnyPart(m_somethingTagName, constTestLevelChanged);
|
||||
ASSERT_EQ(m_logTagManager.get(m_somethingTagName), &m_something);
|
||||
EXPECT_EQ(m_logTagManager.get(m_somethingTagName)->level, constTestLevelChanged);
|
||||
EXPECT_NE(m_logTagManager.get(m_somethingTagName)->level, constTestLevelBegin) << "Should not be left unchanged (default value)";
|
||||
}
|
||||
|
||||
// Non-confusion tests use two or more (non-global) log tags with chosen names to verify
|
||||
// that LogTagManager does not accidentally apply log levels to log tags other than the
|
||||
// ones intended.
|
||||
|
||||
|
||||
// LogTagManagerSubstrNonConfusionSmokeTest are non-confusion tests that focus on
|
||||
// substrings. Keep in mind string matching in LogTagManager must be aligned to
|
||||
// the period delimiter; substrings are not considered for matching.
|
||||
class LogTagManagerSubstrNonConfusionFixture
|
||||
: public LogTagManagerTestFixture
|
||||
, public ::testing::WithParamInterface<std::tuple<SubstringType, bool, Timing, ByWhat>>
|
||||
{
|
||||
public:
|
||||
struct MyTestParam
|
||||
{
|
||||
const SubstringType detractorNameSelector;
|
||||
const bool initTargetTagWithCall;
|
||||
const Timing whenToAssignDetractorTag;
|
||||
const ByWhat howToSetDetractorLevel;
|
||||
MyTestParam(const std::tuple<SubstringType, bool, Timing, ByWhat>& args)
|
||||
: detractorNameSelector(std::get<0u>(args))
|
||||
, initTargetTagWithCall(std::get<1u>(args))
|
||||
, whenToAssignDetractorTag(std::get<2u>(args))
|
||||
, howToSetDetractorLevel(std::get<3u>(args))
|
||||
{
|
||||
}
|
||||
};
|
||||
|
||||
// The following substrings are all derived from "soursop".
|
||||
// In particular, "ours" is a substring of "soursop".
|
||||
protected:
|
||||
LogTag tagSour;
|
||||
LogTag tagSop;
|
||||
LogTag tagSoursop;
|
||||
LogTag tagSourDotSop;
|
||||
LogTag tagOurs;
|
||||
|
||||
protected:
|
||||
static constexpr const char* strSour = "sour";
|
||||
static constexpr const char* strSop = "sop";
|
||||
static constexpr const char* strSoursop = "soursop";
|
||||
static constexpr const char* strSourDotSop = "sour.sop";
|
||||
static constexpr const char* strOurs = "ours";
|
||||
|
||||
public:
|
||||
LogTagManagerSubstrNonConfusionFixture()
|
||||
: LogTagManagerTestFixture(constTestLevelAltBegin)
|
||||
, tagSour(strSour, constTestLevelBegin)
|
||||
, tagSop(strSop, constTestLevelBegin)
|
||||
, tagSoursop(strSoursop, constTestLevelBegin)
|
||||
, tagSourDotSop(strSourDotSop, constTestLevelBegin)
|
||||
, tagOurs(strOurs, constTestLevelBegin)
|
||||
{
|
||||
}
|
||||
|
||||
const char* selectDetractorName(const MyTestParam& myTestParam)
|
||||
{
|
||||
switch (myTestParam.detractorNameSelector)
|
||||
{
|
||||
case SubstringType::Prefix:
|
||||
return strSour;
|
||||
case SubstringType::Midstring:
|
||||
return strOurs;
|
||||
case SubstringType::Suffix:
|
||||
return strSop;
|
||||
case SubstringType::Straddle:
|
||||
return strSourDotSop;
|
||||
default:
|
||||
throw std::logic_error("LogTagManagerSubstrNonConfusionTest::selectDetractorName");
|
||||
}
|
||||
}
|
||||
|
||||
LogTag& selectDetractorTag(const MyTestParam& myTestParam)
|
||||
{
|
||||
switch (myTestParam.detractorNameSelector)
|
||||
{
|
||||
case SubstringType::Prefix:
|
||||
return tagSour;
|
||||
case SubstringType::Midstring:
|
||||
return tagOurs;
|
||||
case SubstringType::Suffix:
|
||||
return tagSop;
|
||||
case SubstringType::Straddle:
|
||||
return tagSourDotSop;
|
||||
default:
|
||||
throw std::logic_error("LogTagManagerSubstrNonConfusionTest::selectDetractorName");
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
INSTANTIATE_TEST_CASE_P(
|
||||
LogTagManagerSubstrNonConfusionTest,
|
||||
LogTagManagerSubstrNonConfusionFixture,
|
||||
::testing::Combine(
|
||||
::testing::Values(SubstringType::Prefix, SubstringType::Midstring, SubstringType::Suffix, SubstringType::Straddle),
|
||||
::testing::Values(false, true),
|
||||
::testing::Values(Timing::Never, Timing::Before, Timing::After),
|
||||
::testing::Values(ByWhat::ByFullName, ByWhat::ByFirstPart, ByWhat::ByAnyPart)
|
||||
)
|
||||
);
|
||||
|
||||
TEST_P(LogTagManagerSubstrNonConfusionFixture, ParameterizedTestFunc)
|
||||
{
|
||||
const auto myTestParam = MyTestParam(GetParam());
|
||||
const char* detractorName = selectDetractorName(myTestParam);
|
||||
LogTag& detractorTag = selectDetractorTag(myTestParam);
|
||||
// Target tag is assigned
|
||||
m_logTagManager.assign(tagSoursop.name, &tagSoursop);
|
||||
// If detractor tag is to be assigned "before"
|
||||
if (myTestParam.whenToAssignDetractorTag == Timing::Before)
|
||||
{
|
||||
m_logTagManager.assign(detractorName, &detractorTag);
|
||||
}
|
||||
// Initialize target tag to constTestLevelChanged
|
||||
if (myTestParam.initTargetTagWithCall)
|
||||
{
|
||||
m_logTagManager.setLevelByFullName(strSoursop, constTestLevelChanged);
|
||||
}
|
||||
else
|
||||
{
|
||||
tagSoursop.level = constTestLevelChanged;
|
||||
}
|
||||
// If detractor tag is to be assigned "after"
|
||||
if (myTestParam.whenToAssignDetractorTag == Timing::After)
|
||||
{
|
||||
m_logTagManager.assign(detractorName, &detractorTag);
|
||||
}
|
||||
// Set the log level using the detractor name
|
||||
switch (myTestParam.howToSetDetractorLevel)
|
||||
{
|
||||
case ByWhat::ByFullName:
|
||||
m_logTagManager.setLevelByFullName(detractorName, constTestLevelAltChanged);
|
||||
break;
|
||||
case ByWhat::ByFirstPart:
|
||||
m_logTagManager.setLevelByFirstPart(detractorName, constTestLevelAltChanged);
|
||||
break;
|
||||
case ByWhat::ByAnyPart:
|
||||
m_logTagManager.setLevelByAnyPart(detractorName, constTestLevelAltChanged);
|
||||
break;
|
||||
default:
|
||||
FAIL() << "Invalid parameterized test value, check test case.";
|
||||
}
|
||||
// Verifies that the target tag is not disturbed by changes made using detractor name
|
||||
ASSERT_EQ(m_logTagManager.get(strSoursop), &tagSoursop);
|
||||
EXPECT_EQ(tagSoursop.level, constTestLevelChanged);
|
||||
EXPECT_NE(tagSoursop.level, constTestLevelAltChanged) << "Should not be changed unless confusion bug exists";
|
||||
}
|
||||
|
||||
// LogTagManagerNamePartNonConfusionFixture are non-confusion tests that assumes
|
||||
// no substring confusions are happening, and proceed to test matching by name parts.
|
||||
// In particular, setLevelByFirstPart() and setLevelByAnyPart() are the focus of these tests.
|
||||
class LogTagManagerNamePartNonConfusionFixture
|
||||
: public LogTagManagerTestFixture
|
||||
, public ::testing::WithParamInterface<std::tuple<int, ByWhat, int, Timing, Timing>>
|
||||
{
|
||||
public:
|
||||
struct MyTestParam
|
||||
{
|
||||
// Config tag name can only specify either full name or exactly one name part.
|
||||
// When specifying exactly one name part, there is a choice of matching the
|
||||
// first name part of a tag, or matching any name part that appears in a tag
|
||||
// name regardless of its position.
|
||||
const int configTagIndex;
|
||||
const ByWhat configMatchBy;
|
||||
const int targetTagIndex;
|
||||
const Timing whenToAssignTargetTag;
|
||||
const Timing whenToAssignConfigTag;
|
||||
MyTestParam(const std::tuple<int, ByWhat, int, Timing, Timing>& args)
|
||||
: configTagIndex(std::get<0u>(args))
|
||||
, configMatchBy(std::get<1u>(args))
|
||||
, targetTagIndex(std::get<2u>(args))
|
||||
, whenToAssignTargetTag(std::get<3u>(args))
|
||||
, whenToAssignConfigTag(std::get<4u>(args))
|
||||
{
|
||||
}
|
||||
};
|
||||
|
||||
protected:
|
||||
LogTag m_apple;
|
||||
LogTag m_banana;
|
||||
LogTag m_coconut;
|
||||
LogTag m_orange;
|
||||
LogTag m_pineapple;
|
||||
LogTag m_bananaDotOrange;
|
||||
LogTag m_bananaDotPineapple;
|
||||
LogTag m_coconutDotPineapple;
|
||||
|
||||
protected:
|
||||
static constexpr const char* strApple = "apple";
|
||||
static constexpr const char* strBanana = "banana";
|
||||
static constexpr const char* strCoconut = "coconut";
|
||||
static constexpr const char* strOrange = "orange";
|
||||
static constexpr const char* strPineapple = "pineapple";
|
||||
static constexpr const char* strBananaDotOrange = "banana.orange";
|
||||
static constexpr const char* strBananaDotPineapple = "banana.pineapple";
|
||||
static constexpr const char* strCoconutDotPineapple = "coconut.pineapple";
|
||||
|
||||
public:
|
||||
LogTagManagerNamePartNonConfusionFixture()
|
||||
: LogTagManagerTestFixture(constTestLevelAltBegin)
|
||||
, m_apple(strApple, constTestLevelBegin)
|
||||
, m_banana(strBanana, constTestLevelBegin)
|
||||
, m_coconut(strCoconut, constTestLevelBegin)
|
||||
, m_orange(strOrange, constTestLevelBegin)
|
||||
, m_pineapple(strPineapple, constTestLevelBegin)
|
||||
, m_bananaDotOrange(strBananaDotOrange, constTestLevelBegin)
|
||||
, m_bananaDotPineapple(strBananaDotPineapple, constTestLevelBegin)
|
||||
, m_coconutDotPineapple(strCoconutDotPineapple, constTestLevelBegin)
|
||||
{
|
||||
}
|
||||
|
||||
protected:
|
||||
LogTag* getLogTagByIndex(int index)
|
||||
{
|
||||
switch (index)
|
||||
{
|
||||
case 0:
|
||||
return &m_apple;
|
||||
case 1:
|
||||
return &m_banana;
|
||||
case 2:
|
||||
return &m_coconut;
|
||||
case 3:
|
||||
return &m_orange;
|
||||
case 4:
|
||||
return &m_pineapple;
|
||||
case 5:
|
||||
return &m_bananaDotOrange;
|
||||
case 6:
|
||||
return &m_bananaDotPineapple;
|
||||
case 7:
|
||||
return &m_coconutDotPineapple;
|
||||
default:
|
||||
ADD_FAILURE() << "Invalid parameterized test value, check test case. "
|
||||
<< "Function LogTagManagerNamePartNonConfusionFixture::getLogTagByIndex.";
|
||||
return nullptr;
|
||||
}
|
||||
}
|
||||
|
||||
// findTabulatedExpectedResult returns the hard-coded expected results for parameterized
|
||||
// test cases. The tables need updated if the index, name, or ordering of test tags are
|
||||
// changed.
|
||||
bool findTabulatedExpectedResult(const MyTestParam& myTestParam) const
|
||||
{
|
||||
// expectedResultUsingFirstPart:
|
||||
// Each row ("config") specifies the tag name specifier used to call setLevelByFirstPart().
|
||||
// Each column ("target") specifies whether an actual tag with the "target"
|
||||
// name would have its log level changed because of the call to setLevelByFirstPart().
|
||||
static constexpr const bool expectedResultUsingFirstPart[5][8] =
|
||||
{
|
||||
/*byFirstPart(apple)*/ { true, false, false, false, false, false, false, false },
|
||||
/*byFirstPart(banana)*/ { false, true, false, false, false, true, true, false },
|
||||
/*byFirstPart(coconut)*/ { false, false, true, false, false, false, false, true },
|
||||
/*byFirstPart(orange)*/ { false, false, false, true, false, false, false, false },
|
||||
/*byFirstPart(pineapple)*/ { false, false, false, false, true, false, false, false },
|
||||
};
|
||||
|
||||
// expectedResultUsingAnyPart:
|
||||
// Each row ("config") specifies the tag name specifier used to call setLevelByAnyPart().
|
||||
// Each column ("target") specifies whether an actual tag with the "target"
|
||||
// name would have its log level changed because of the call to setLevelByAnyPart().
|
||||
static constexpr const bool expectedResultUsingAnyPart[5][8] =
|
||||
{
|
||||
/*byAnyPart(apple)*/ { true, false, false, false, false, false, false, false },
|
||||
/*byAnyPart(banana)*/ { false, true, false, false, false, true, true, false },
|
||||
/*byAnyPart(coconut)*/ { false, false, true, false, false, false, false, true },
|
||||
/*byAnyPart(orange)*/ { false, false, false, true, false, true, false, false },
|
||||
/*byAnyPart(pineapple)*/ { false, false, false, false, true, false, true, true },
|
||||
};
|
||||
|
||||
switch (myTestParam.configMatchBy)
|
||||
{
|
||||
case ByWhat::ByFirstPart:
|
||||
return expectedResultUsingFirstPart[myTestParam.configTagIndex][myTestParam.targetTagIndex];
|
||||
case ByWhat::ByAnyPart:
|
||||
return expectedResultUsingAnyPart[myTestParam.configTagIndex][myTestParam.targetTagIndex];
|
||||
default:
|
||||
ADD_FAILURE() << "Invalid parameterized test value, check test case. "
|
||||
<< "Function LogTagManagerNamePartNonConfusionFixture::getLogTagByIndex.";
|
||||
return false;
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
INSTANTIATE_TEST_CASE_P(
|
||||
LogTagManagerNamePartNonConfusionTest,
|
||||
LogTagManagerNamePartNonConfusionFixture,
|
||||
::testing::Combine(
|
||||
::testing::Values(0, 1, 2, 3, 4),
|
||||
::testing::Values(ByWhat::ByFirstPart, ByWhat::ByAnyPart),
|
||||
::testing::Values(0, 1, 2, 3, 4, 5, 6, 7),
|
||||
::testing::Values(Timing::Before, Timing::After),
|
||||
::testing::Values(Timing::Before, Timing::After, Timing::Never)
|
||||
)
|
||||
);
|
||||
|
||||
TEST_P(LogTagManagerNamePartNonConfusionFixture, NamePartTestFunc)
|
||||
{
|
||||
const auto myTestParam = MyTestParam(GetParam());
|
||||
LogTag* configTag = getLogTagByIndex(myTestParam.configTagIndex);
|
||||
LogTag* targetTag = getLogTagByIndex(myTestParam.targetTagIndex);
|
||||
ASSERT_NE(configTag, nullptr) << "Invalid parameterized test value, check value of myTestParam.configTagIndex.";
|
||||
ASSERT_NE(targetTag, nullptr) << "Invalid parameterized test value, check value of myTestParam.targetTagIndex.";
|
||||
if (myTestParam.whenToAssignConfigTag == Timing::Before)
|
||||
{
|
||||
m_logTagManager.assign(configTag->name, configTag);
|
||||
}
|
||||
if (myTestParam.whenToAssignTargetTag == Timing::Before)
|
||||
{
|
||||
m_logTagManager.assign(targetTag->name, targetTag);
|
||||
}
|
||||
switch (myTestParam.configMatchBy)
|
||||
{
|
||||
case ByWhat::ByFirstPart:
|
||||
m_logTagManager.setLevelByFirstPart(configTag->name, constTestLevelChanged);
|
||||
break;
|
||||
case ByWhat::ByAnyPart:
|
||||
m_logTagManager.setLevelByAnyPart(configTag->name, constTestLevelChanged);
|
||||
break;
|
||||
default:
|
||||
FAIL() << "Invalid parameterized test value, check test case. "
|
||||
<< "Fixture LogTagManagerNamePartNonConfusionFixture, Case NamePartTestFunc.";
|
||||
}
|
||||
if (myTestParam.whenToAssignConfigTag == Timing::After)
|
||||
{
|
||||
m_logTagManager.assign(configTag->name, configTag);
|
||||
}
|
||||
if (myTestParam.whenToAssignTargetTag == Timing::After)
|
||||
{
|
||||
m_logTagManager.assign(targetTag->name, targetTag);
|
||||
}
|
||||
// Verifies the registration of the log tag pointer. If fail, cannot proceed
|
||||
// because it is not certain whether the returned pointer is valid to dereference
|
||||
ASSERT_EQ(m_logTagManager.get(targetTag->name), targetTag);
|
||||
// Verifies the log level of target tag
|
||||
const bool isChangeExpected = findTabulatedExpectedResult(myTestParam);
|
||||
if (isChangeExpected)
|
||||
{
|
||||
EXPECT_EQ(targetTag->level, constTestLevelChanged);
|
||||
EXPECT_NE(targetTag->level, constTestLevelBegin);
|
||||
}
|
||||
else
|
||||
{
|
||||
EXPECT_EQ(targetTag->level, constTestLevelBegin);
|
||||
EXPECT_NE(targetTag->level, constTestLevelChanged);
|
||||
}
|
||||
}
|
||||
|
||||
}} // namespace
|
Loading…
Reference in New Issue
Block a user