From 86da25a3f9e170835088c666274bd2e42e92d0e0 Mon Sep 17 00:00:00 2001 From: Tomek Mrugalski Date: Tue, 9 Jun 2015 15:13:45 +0200 Subject: [PATCH] [3796] Changes after review: - a lot of dead code removed from command_interpreter.cc|h|tests - unit-tests an el() function now have comments - comments improved (added for constants, cleaned up for functions) - copyright years updated - unnecessary includes removed --- src/bin/dhcp4/tests/config_parser_unittest.cc | 1 + src/bin/dhcp6/tests/config_parser_unittest.cc | 1 + src/lib/config/command_interpreter.cc | 275 +----------------- src/lib/config/command_interpreter.h | 161 +++------- .../tests/command_interpreter_unittests.cc | 85 ++---- 5 files changed, 66 insertions(+), 457 deletions(-) diff --git a/src/bin/dhcp4/tests/config_parser_unittest.cc b/src/bin/dhcp4/tests/config_parser_unittest.cc index 67a1e30432..26ecc7d98e 100644 --- a/src/bin/dhcp4/tests/config_parser_unittest.cc +++ b/src/bin/dhcp4/tests/config_parser_unittest.cc @@ -18,6 +18,7 @@ #include #include +#include #include #include #include diff --git a/src/bin/dhcp6/tests/config_parser_unittest.cc b/src/bin/dhcp6/tests/config_parser_unittest.cc index a0c624c761..e117e364f2 100644 --- a/src/bin/dhcp6/tests/config_parser_unittest.cc +++ b/src/bin/dhcp6/tests/config_parser_unittest.cc @@ -15,6 +15,7 @@ #include #include +#include #include #include #include diff --git a/src/lib/config/command_interpreter.cc b/src/lib/config/command_interpreter.cc index 7a87ad0927..cba3a8eb38 100644 --- a/src/lib/config/command_interpreter.cc +++ b/src/lib/config/command_interpreter.cc @@ -1,4 +1,4 @@ -// Copyright (C) 2009 Internet Systems Consortium, Inc. ("ISC") +// Copyright (C) 2009,2015 Internet Systems Consortium, Inc. ("ISC") // // Permission to use, copy, modify, and/or distribute this software for any // purpose with or without fee is hereby granted, provided that the above @@ -14,34 +14,10 @@ #include -#include -#include -#include -#include -#include - -#include -#include -#include -#include -#include -#include -#include - -#include -#include - -#include -#include #include - -#include #include - -#include -#include -#include -#include +#include +#include using namespace std; @@ -53,11 +29,10 @@ using isc::data::JSONError; namespace isc { namespace config { -const char *CONTROL_COMMAND="command"; - -const char *CONTROL_RESULT="result"; -const char *CONTROL_TEXT="text"; -const char *CONTROL_ARGUMENTS="arguments"; +const char *CONTROL_COMMAND = "command"; +const char *CONTROL_RESULT = "result"; +const char *CONTROL_TEXT = "text"; +const char *CONTROL_ARGUMENTS = "arguments"; // Full version, with status, text and arguments ConstElementPtr @@ -167,241 +142,5 @@ parseCommand(ConstElementPtr& arg, ConstElementPtr command) { return (cmd->stringValue()); } -/// @todo: The code below should be reviewed whether it's still in use. If it is, -/// it should be moved to a separate file. - -namespace { -// Temporary workaround functions for missing functionality in -// getValue() (main problem described in ticket #993) -// This returns either the value set for the given relative id, -// or its default value -// (intentionally defined here so this interface does not get -// included in ConfigData as it is) -ConstElementPtr getValueOrDefault(ConstElementPtr config_part, - const std::string& relative_id, - const ConfigData& config_data, - const std::string& full_id) { - if (config_part->contains(relative_id)) { - return config_part->get(relative_id); - } else { - return config_data.getDefaultValue(full_id); - } -} - -/// @brief Prefix name with "kea-". -/// -/// In BIND 10, modules had names taken from the .spec file, which are typically -/// names starting with a capital letter (e.g. "Resolver", "Auth" etc.). The -/// names of the associated binaries are derived from the module names, being -/// prefixed "b10-" and having the first letter of the module name lower-cased -/// (e.g. "b10-resolver", "b10-auth"). (It is a required convention that there -/// be this relationship between the names.) -/// -/// In Kea we're not using module names, but we do still keep some capability to -/// run Kea servers in Bundy framework. For that reason the whole discussion here -/// applies only to case when Kea is compiled with Bundy configuration backend. -/// -/// Within the binaries the root loggers are named after the binaries themselves. -/// (The reason for this is that the name of the logger is included in the -/// message logged, so making it clear which message comes from which Kea -/// process.) As logging is configured using module names, the configuration code -/// has to match these with the corresponding logger names. This function -/// converts a module name to a root logger name by lowercasing the first letter -/// of the module name and prepending "kea-". -/// -/// \param instring String to convert. (This may be empty, in which case -/// "kea-" will be returned.) -/// -/// \return Converted string. -std::string -keaPrefix(const std::string& instring) { - std::string result = instring; - if (!result.empty()) { - result[0] = tolower(result[0]); - } - return (std::string("kea-") + result); -} - -// Reads a output_option subelement of a logger configuration, -// and sets the values thereing to the given OutputOption struct, -// or defaults values if they are not provided (from config_data). -void -readOutputOptionConf(isc::log::OutputOption& output_option, - ConstElementPtr output_option_el, - const ConfigData& config_data) -{ - ConstElementPtr destination_el = getValueOrDefault(output_option_el, - "destination", config_data, - "loggers/output_options/destination"); - output_option.destination = isc::log::getDestination(destination_el->stringValue()); - ConstElementPtr output_el = getValueOrDefault(output_option_el, - "output", config_data, - "loggers/output_options/output"); - if (output_option.destination == isc::log::OutputOption::DEST_CONSOLE) { - output_option.stream = isc::log::getStream(output_el->stringValue()); - } else if (output_option.destination == isc::log::OutputOption::DEST_FILE) { - output_option.filename = output_el->stringValue(); - } else if (output_option.destination == isc::log::OutputOption::DEST_SYSLOG) { - output_option.facility = output_el->stringValue(); - } - output_option.flush = getValueOrDefault(output_option_el, - "flush", config_data, - "loggers/output_options/flush")->boolValue(); - output_option.maxsize = getValueOrDefault(output_option_el, - "maxsize", config_data, - "loggers/output_options/maxsize")->intValue(); - output_option.maxver = getValueOrDefault(output_option_el, - "maxver", config_data, - "loggers/output_options/maxver")->intValue(); -} - -// Reads a full 'loggers' configuration, and adds the loggers therein -// to the given vector, fills in blanks with defaults from config_data -void -readLoggersConf(std::vector& specs, - ConstElementPtr logger, - const ConfigData& config_data) -{ - // Read name, adding prefix as required. - std::string lname = logger->get("name")->stringValue(); - - ConstElementPtr severity_el = getValueOrDefault(logger, - "severity", config_data, - "loggers/severity"); - isc::log::Severity severity = isc::log::getSeverity( - severity_el->stringValue()); - int dbg_level = getValueOrDefault(logger, "debuglevel", - config_data, - "loggers/debuglevel")->intValue(); - bool additive = getValueOrDefault(logger, "additive", config_data, - "loggers/additive")->boolValue(); - - isc::log::LoggerSpecification logger_spec( - lname, severity, dbg_level, additive - ); - - if (logger->contains("output_options")) { - BOOST_FOREACH(ConstElementPtr output_option_el, - logger->get("output_options")->listValue()) { - // create outputoptions - isc::log::OutputOption output_option; - readOutputOptionConf(output_option, - output_option_el, - config_data); - logger_spec.addOutputOption(output_option); - } - } - - specs.push_back(logger_spec); -} - -// Copies the map for a logger, changing the name of the logger in the process. -// This is used because the map being copied is "const", so in order to -// change the name we need to create a new one. -// -// \param cur_logger Logger being copied. -// \param new_name New value of the "name" element at the top level. -// -// \return Pointer to the map with the updated element. -ConstElementPtr -copyLogger(ConstElementPtr& cur_logger, const std::string& new_name) { - - // Since we'll only be updating one first-level element and subsequent - // use won't change the contents of the map, a shallow map copy is enough. - ElementPtr new_logger(Element::createMap()); - new_logger->setValue(cur_logger->mapValue()); - new_logger->set("name", Element::create(new_name)); - - return (new_logger); -} - - -} // end anonymous namespace - -ConstElementPtr -getRelatedLoggers(ConstElementPtr loggers) { - // Keep a list of names for easier lookup later - std::set our_names; - const std::string& root_name = isc::log::getRootLoggerName(); - - ElementPtr result = isc::data::Element::createList(); - - BOOST_FOREACH(ConstElementPtr cur_logger, loggers->listValue()) { - // Need to add the kea- prefix to names ready from the spec file. - const std::string cur_name = cur_logger->get("name")->stringValue(); - const std::string mod_name = keaPrefix(cur_name); - if (mod_name == root_name || mod_name.find(root_name + ".") == 0) { - - // Note this name so that we don't add a wildcard that matches it. - our_names.insert(mod_name); - - // We want to store the logger with the modified name (i.e. with - // the kea- prefix). As we are dealing with const loggers, we - // store a modified copy of the data. - result->add(copyLogger(cur_logger, mod_name)); - LOG_DEBUG(config_logger, DBG_CONFIG_PROCESS, CONFIG_LOG_EXPLICIT) - .arg(cur_name); - - } else if (!cur_name.empty() && (cur_name[0] != '*')) { - // Not a wildcard logger and we are ignoring it. - LOG_DEBUG(config_logger, DBG_CONFIG_PROCESS, - CONFIG_LOG_IGNORE_EXPLICIT).arg(cur_name); - } - } - - // Now find the wildcard names (the one that start with "*"). - BOOST_FOREACH(ConstElementPtr cur_logger, loggers->listValue()) { - const std::string cur_name = cur_logger->get("name")->stringValue(); - // If name is '*', or starts with '*.', replace * with root - // logger name. - if (cur_name == "*" || (cur_name.length() > 1 && - cur_name[0] == '*' && cur_name[1] == '.')) { - - // Substitute the "*" with the root name - std::string mod_name = cur_name; - mod_name.replace(0, 1, root_name); - - // Now add it to the result list, but only if a logger with - // that name was not configured explicitly. - if (our_names.find(mod_name) == our_names.end()) { - - // We substitute the name here, but as we are dealing with - // consts, we need to copy the data. - result->add(copyLogger(cur_logger, mod_name)); - LOG_DEBUG(config_logger, DBG_CONFIG_PROCESS, - CONFIG_LOG_WILD_MATCH).arg(cur_name); - - } else if (!cur_name.empty() && (cur_name[0] == '*')) { - // Is a wildcard and we are ignoring it (because the wildcard - // expands to a specification that we already encountered when - // processing explicit names). - LOG_DEBUG(config_logger, DBG_CONFIG_PROCESS, - CONFIG_LOG_IGNORE_WILD).arg(cur_name); - } - } - } - return (result); -} - -void -default_logconfig_handler(const std::string& module_name, - ConstElementPtr new_config, - const ConfigData& config_data) { - config_data.getModuleSpec().validateConfig(new_config, true); - - std::vector specs; - - if (new_config->contains("loggers")) { - ConstElementPtr loggers = getRelatedLoggers(new_config->get("loggers")); - BOOST_FOREACH(ConstElementPtr logger, - loggers->listValue()) { - readLoggersConf(specs, logger, config_data); - } - } - - isc::log::LoggerManager logger_manager; - logger_manager.process(specs.begin(), specs.end()); -} - } } diff --git a/src/lib/config/command_interpreter.h b/src/lib/config/command_interpreter.h index 802db92a66..5168c8c029 100644 --- a/src/lib/config/command_interpreter.h +++ b/src/lib/config/command_interpreter.h @@ -1,4 +1,4 @@ -// Copyright (C) 2009 Internet Systems Consortium, Inc. ("ISC") +// Copyright (C) 2009,2015 Internet Systems Consortium, Inc. ("ISC") // // Permission to use, copy, modify, and/or distribute this software for any // purpose with or without fee is hereby granted, provided that the above @@ -12,30 +12,48 @@ // OR OTHER TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR // PERFORMANCE OF THIS SOFTWARE. -#ifndef CCSESSION_H -#define CCSESSION_H 1 - -#include -#include +#ifndef COMMAND_INTERPRETER_H +#define COMMAND_INTERPRETER_H #include - #include -#include -#include + +/// @file command_interpreter.h +/// +/// This file contains several functions and constants that are used for +/// handling commands and responses sent over control channel. The design +/// is described here: http://kea.isc.org/wiki/StatsDesign, but also +/// in @ref ctrlSocket section in the Developer's Guide. namespace isc { namespace config { +/// @brief String used for commands ("command") extern const char *CONTROL_COMMAND; +/// @brief String used for result, i.e. integer status ("result") extern const char *CONTROL_RESULT; + +/// @brief String used for storing textual description ("text") extern const char *CONTROL_TEXT; + +/// @brief String used for arguments map ("arguments") extern const char *CONTROL_ARGUMENTS; +/// @brief Status code indicating a successful operation const int CONTROL_RESULT_SUCCESS = 0; + +/// @brief Status code indicating a general failure const int CONTROL_RESULT_ERROR = 1; +/// @brief A standard control channel exception that is thrown if a function +/// is there is a problem with one of the messages +class CtrlChannelError : public isc::Exception { +public: + CtrlChannelError(const char* file, size_t line, const char* what) : + isc::Exception(file, line, what) {} +}; + /// @brief Creates a standard config/command level success answer message /// (i.e. of the form { "result": 0 } /// @return Standard command/config success answer message @@ -54,19 +72,16 @@ isc::data::ConstElementPtr createAnswer(const int status_code, /// (i.e. of the form { "result": status_code, "arguments": arg } /// /// @param status_code The return code (0 for success) -/// @param status_text A string to put into the "text" argument +/// @param arg argument (any data to be passed in the response, may be null) /// @return Standard command/config answer message isc::data::ConstElementPtr createAnswer(const int status_code, const isc::data::ConstElementPtr& arg); /// @brief Creates a standard config/command level answer message -/// (i.e. of the form { "result": X, "[ rcode, arg ] } -/// If rcode != 0, arg must be a StringElement /// /// @param status_code The return code (0 for success) -/// @param arg For status_code == 0, this is an optional argument of any -/// Element type. For status_code == 1, this argument is mandatory, -/// and may be any type of ElementPtr. +/// @param status textual represenation of the status (used mostly for errors) +/// @param arg argument (any data to be passed in the response, may be null) /// @return Standard command/config answer message isc::data::ConstElementPtr createAnswer(const int status_code, const std::string& status, @@ -77,14 +92,12 @@ isc::data::ConstElementPtr createAnswer(const int status_code, /// @param status_code This value will be set to the return code contained in /// the message /// @param msg The message to parse -/// @return The optional argument in the message, or an empty ElementPtr -/// if there was no argument. If rcode != 0, this contains a -/// StringElement with the error description. +/// @return The optional argument in the message. isc::data::ConstElementPtr parseAnswer(int &status_code, const isc::data::ConstElementPtr& msg); /// @brief Creates a standard config/command command message with no -/// argument (of the form { "command": "my_command" } +/// argument (of the form { "command": "my_command" }) /// /// @param command The command string /// @return The created message @@ -109,117 +122,11 @@ isc::data::ConstElementPtr createCommand(const std::string& command, /// the argument, or to an empty Map (ElementPtr) if there was none. /// @param command The command message containing the command (as made /// by createCommand() -/// \return The command name +/// @return The command name std::string parseCommand(isc::data::ConstElementPtr& arg, isc::data::ConstElementPtr command); -/// @brief A standard control channel exception that is thrown if a function -/// is there is a problem with one of the messages -class CtrlChannelError : public isc::Exception { -public: - CtrlChannelError(const char* file, size_t line, const char* what) : - isc::Exception(file, line, what) {} -}; - - /** - * Set a command handler; the function that is passed takes an - * ElementPtr, pointing to a list element, containing - * [ module_name, command_name, arg1, arg2, ... ] - * The returned ElementPtr should look like - * { "result": [ return_value, result_value ] } - * result value here is optional and depends on the command - * - * This protocol is very likely to change. - */ - void setCommandHandler(isc::data::ConstElementPtr(*command_handler)( - const std::string& command, - isc::data::ConstElementPtr args)); - - /** - * Gives access to the configuration values of a different module - * Once this function has been called with the name of the specification - * file or the module you want the configuration of, you can use - * \c getRemoteConfigValue() to get a specific setting. - * Changes are automatically updated, and you can specify handlers - * for those changes. This function will subscribe to the relevant module - * channel. - * - * This method must be called before calling the \c start() method on the - * ModuleCCSession (it also implies the ModuleCCSession must have been - * constructed with start_immediately being false). - * - * \param spec_name This specifies the module to add. It is either a - * filename of the spec file to use or a name of module - * (in case it's a module name, the spec data is - * downloaded from the configuration manager, therefore - * the configuration manager must know it). If - * spec_is_filename is true (the default), then a - * filename is assumed, otherwise a module name. - * \param handler The handler functor called whenever there's a change. - * Called once initially from this function. May be NULL - * if you don't want any handler to be called and you're - * fine with requesting the data through - * getRemoteConfigValue() each time. - * - * The handler should not throw, or it'll fall through and - * the exception will get into strange places, probably - * aborting the application. - * \param spec_is_filename Says if spec_name is filename or module name. - * \return The name of the module specified in the given specification - * file - */ - typedef boost::function RemoteHandler; - - /// \brief Called when a notification comes - /// - /// The callback should be exception-free. If it raises an exception, - /// it'll leak through the event loop up and probably terminate the - /// application. - /// - /// \param event_name The identification of event type. - /// \param params The parameters of the event. This may be NULL - /// pointer in case no parameters were sent with the event. - typedef boost::function - NotificationCallback; - - /// \brief Multiple notification callbacks for the same notification - typedef std::list NotificationCallbacks; - - /// \brief Returns the loggers related to this module - /// - /// This function does two things; - /// - it drops the configuration parts for loggers for other modules. - /// - it replaces the '*' in the name of the loggers by the name of - /// this module, but *only* if the expanded name is not configured - /// explicitly. - /// - /// Examples: if this is the module b10-resolver, - /// For the config names ['*', 'b10-auth'] - /// The '*' is replaced with 'b10-resolver', and this logger is used. - /// 'b10-auth' is ignored (of course, it will not be in the b10-auth - /// module). - /// - /// For ['*', 'b10-resolver'] - /// The '*' is ignored, and only 'b10-resolver' is used. - /// - /// For ['*.reslib', 'b10-resolver'] - /// Or ['b10-resolver.reslib', '*'] - /// Both are used, where the * will be expanded to b10-resolver - /// - /// \note This is a public function at this time, but mostly for - /// the purposes of testing. Once we can directly test what loggers - /// are running, this function may be moved to the unnamed namespace - /// - /// \param loggers the original 'loggers' config list - /// \return ListElement containing only loggers relevant for this - /// module, where * is replaced by the root logger name - isc::data::ConstElementPtr - getRelatedLoggers(isc::data::ConstElementPtr loggers); - }; // end of namespace isc::config }; // end of namespace isc -#endif // CCSESSION_H +#endif // COMMAND_INTERPRETER_H diff --git a/src/lib/config/tests/command_interpreter_unittests.cc b/src/lib/config/tests/command_interpreter_unittests.cc index 6b06607b38..bfb287ad3d 100644 --- a/src/lib/config/tests/command_interpreter_unittests.cc +++ b/src/lib/config/tests/command_interpreter_unittests.cc @@ -1,4 +1,4 @@ -// Copyright (C) 2009 Internet Systems Consortium, Inc. ("ISC") +// Copyright (C) 2009,2015 Internet Systems Consortium, Inc. ("ISC") // // Permission to use, copy, modify, and/or distribute this software for any // purpose with or without fee is hereby granted, provided that the above @@ -28,27 +28,43 @@ using namespace std; namespace { + +/// @brief Shortcut method for creating elements from JSON string +/// +/// @param str string to be converted +/// @return Element structure ElementPtr el(const std::string& str) { return (Element::fromJSON(str)); } +// This test verifies that that createAnswer method is able to generate +// various answers. TEST(CommandInterpreterTest, createAnswer) { ConstElementPtr answer; + + // By default the answer is a successful one. answer = createAnswer(); EXPECT_EQ("{ \"result\": 0 }", answer->str()); + + // Let's check if we can generate an error. answer = createAnswer(1, "error"); EXPECT_EQ("{ \"result\": 1, \"text\": \"error\" }", answer->str()); + // This is expected to throw. When status code is non-zero (indicating error), + // textual explanation is mandatory. EXPECT_THROW(createAnswer(1, ElementPtr()), CtrlChannelError); EXPECT_THROW(createAnswer(1, Element::create(1)), CtrlChannelError); + // Let's check if answer can be generate with some data in it. ConstElementPtr arg = el("[ \"just\", \"some\", \"data\" ]"); answer = createAnswer(0, arg); EXPECT_EQ("{ \"arguments\": [ \"just\", \"some\", \"data\" ], \"result\": 0 }", answer->str()); } +// This test checks whether parseAnswer is able to handle well and malformed +// answers. TEST(CommandInterpreterTest, parseAnswer) { ConstElementPtr answer; ConstElementPtr arg; @@ -79,6 +95,8 @@ TEST(CommandInterpreterTest, parseAnswer) { EXPECT_EQ("[ \"just\", \"some\", \"data\" ]", arg->str()); } +// This test checks whether createCommand function is able to create commands +// with and without parameters. TEST(CommandInterpreterTest, createCommand) { ConstElementPtr command; ConstElementPtr arg; @@ -102,6 +120,8 @@ TEST(CommandInterpreterTest, createCommand) { command->str()); } +// This test checks whether parseCommand function is able to parse various valid +// and malformed commands. TEST(CommandInterpreterTest, parseCommand) { ConstElementPtr arg; std::string cmd; @@ -124,71 +144,12 @@ TEST(CommandInterpreterTest, parseCommand) { EXPECT_EQ("my_command", cmd); EXPECT_EQ("1", arg->str()); - parseCommand(arg, el("{ \"command\": \"my_command\", \"arguments\": [ \"some\", \"argument\", \"list\" ] }")); + parseCommand(arg, el("{ \"command\": \"my_command\", \"arguments\": " + "[ \"some\", \"argument\", \"list\" ] }")); EXPECT_EQ("my_command", cmd); ASSERT_TRUE(arg); EXPECT_EQ("[ \"some\", \"argument\", \"list\" ]", arg->str()); } -void doRelatedLoggersTest(const char* input, const char* expected) { - ConstElementPtr all_conf = isc::data::Element::fromJSON(input); - ConstElementPtr expected_conf = isc::data::Element::fromJSON(expected); - EXPECT_EQ(*expected_conf, *isc::config::getRelatedLoggers(all_conf)); -} - -TEST(LogConfigTest, relatedLoggersTest) { - // make sure logger configs for 'other' programs are ignored, - // and that * is substituted correctly - // We'll use a root logger name of "kea-test". - isc::log::setRootLoggerName("kea-test"); - - doRelatedLoggersTest("[{ \"name\": \"other_module\" }]", - "[]"); - doRelatedLoggersTest("[{ \"name\": \"other_module.somelib\" }]", - "[]"); - doRelatedLoggersTest("[{ \"name\": \"test_other\" }]", - "[]"); - doRelatedLoggersTest("[{ \"name\": \"test_other.somelib\" }]", - "[]"); - doRelatedLoggersTest("[ { \"name\": \"other_module\" }," - " { \"name\": \"test\" }]", - "[ { \"name\": \"kea-test\" } ]"); - doRelatedLoggersTest("[ { \"name\": \"test\" }]", - "[ { \"name\": \"kea-test\" } ]"); - doRelatedLoggersTest("[ { \"name\": \"test.somelib\" }]", - "[ { \"name\": \"kea-test.somelib\" } ]"); - doRelatedLoggersTest("[ { \"name\": \"other_module.somelib\" }," - " { \"name\": \"test.somelib\" }]", - "[ { \"name\": \"kea-test.somelib\" } ]"); - doRelatedLoggersTest("[ { \"name\": \"other_module.somelib\" }," - " { \"name\": \"test\" }," - " { \"name\": \"test.somelib\" }]", - "[ { \"name\": \"kea-test\" }," - " { \"name\": \"kea-test.somelib\" } ]"); - doRelatedLoggersTest("[ { \"name\": \"*\" }]", - "[ { \"name\": \"kea-test\" } ]"); - doRelatedLoggersTest("[ { \"name\": \"*.somelib\" }]", - "[ { \"name\": \"kea-test.somelib\" } ]"); - doRelatedLoggersTest("[ { \"name\": \"*\", \"severity\": \"DEBUG\" }," - " { \"name\": \"test\", \"severity\": \"WARN\"}]", - "[ { \"name\": \"kea-test\", \"severity\": \"WARN\"} ]"); - doRelatedLoggersTest("[ { \"name\": \"*\", \"severity\": \"DEBUG\" }," - " { \"name\": \"some_module\", \"severity\": \"WARN\"}]", - "[ { \"name\": \"kea-test\", \"severity\": \"DEBUG\"} ]"); - doRelatedLoggersTest("[ { \"name\": \"kea-test\" }]", - "[]"); - // make sure 'bad' things like '*foo.x' or '*lib' are ignored - // (cfgmgr should have already caught it in the logconfig plugin - // check, and is responsible for reporting the error) - doRelatedLoggersTest("[ { \"name\": \"*foo\" }]", - "[ ]"); - doRelatedLoggersTest("[ { \"name\": \"*foo.bar\" }]", - "[ ]"); - doRelatedLoggersTest("[ { \"name\": \"*foo\" }," - " { \"name\": \"*foo.lib\" }," - " { \"name\": \"test\" } ]", - "[ { \"name\": \"kea-test\" } ]"); -} - } -- 2.47.3