diff --git a/libs/attribute/src/ecflow/attribute/GenericAttr.cpp b/libs/attribute/src/ecflow/attribute/GenericAttr.cpp index 5a85d7ae9..ddbb75252 100644 --- a/libs/attribute/src/ecflow/attribute/GenericAttr.cpp +++ b/libs/attribute/src/ecflow/attribute/GenericAttr.cpp @@ -26,7 +26,7 @@ GenericAttr::GenericAttr(const std::string& name, const std::vector : name_(name), values_(values) { std::string msg; - if (!Str::valid_name(name, msg)) { + if (!ecf::algorithm::is_valid_name(name, msg)) { throw std::runtime_error("GenericAttr::GenericAttr : Invalid generic name : " + msg); } } @@ -34,7 +34,7 @@ GenericAttr::GenericAttr(const std::string& name, const std::vector GenericAttr::GenericAttr(const std::string& name) : name_(name) { std::string msg; - if (!Str::valid_name(name, msg)) { + if (!ecf::algorithm::is_valid_name(name, msg)) { throw std::runtime_error("GenericAttr::GenericAttr : Invalid generic name : " + msg); } } diff --git a/libs/attribute/src/ecflow/attribute/NodeAttr.cpp b/libs/attribute/src/ecflow/attribute/NodeAttr.cpp index 6fc070ad9..c7745698e 100644 --- a/libs/attribute/src/ecflow/attribute/NodeAttr.cpp +++ b/libs/attribute/src/ecflow/attribute/NodeAttr.cpp @@ -51,7 +51,7 @@ Event::Event(int number, const std::string& eventName, bool iv, bool check_name) iv_(iv) { if (!eventName.empty() && check_name) { std::string msg; - if (!Str::valid_name(eventName, msg)) { + if (!ecf::algorithm::is_valid_name(eventName, msg)) { throw std::runtime_error("Event::Event: Invalid event name : " + msg); } } @@ -87,7 +87,7 @@ Event::Event(const std::string& eventName, bool iv) } std::string msg; - if (!Str::valid_name(eventName, msg)) { + if (!ecf::algorithm::is_valid_name(eventName, msg)) { throw std::runtime_error("Event::Event: Invalid event name : " + msg); } } @@ -214,7 +214,7 @@ Meter::Meter(const std::string& name, int min, int max, int colorChange, int val cc_(colorChange), n_(name) { if (check) { - if (!Str::valid_name(name)) { + if (!ecf::algorithm::is_valid_name(name)) { throw std::runtime_error("Meter::Meter: Invalid Meter name: " + name); } } @@ -323,7 +323,7 @@ Label::Label(const std::string& name, const std::string& value, const std::strin : n_(name), v_(value), new_v_(new_value) { - if (check_name && !Str::valid_name(n_)) { + if (check_name && !ecf::algorithm::is_valid_name(n_)) { throw std::runtime_error(MESSAGE("Label::Label: Invalid Label name :" << n_)); } } diff --git a/libs/attribute/src/ecflow/attribute/QueueAttr.cpp b/libs/attribute/src/ecflow/attribute/QueueAttr.cpp index 0e78f53c9..60684a8b8 100644 --- a/libs/attribute/src/ecflow/attribute/QueueAttr.cpp +++ b/libs/attribute/src/ecflow/attribute/QueueAttr.cpp @@ -35,7 +35,7 @@ QueueAttr::QueueAttr(const std::string& name, const std::vector& th : theQueue_(theQueue), name_(name) { std::string msg; - if (!Str::valid_name(name, msg)) { + if (!ecf::algorithm::is_valid_name(name, msg)) { throw std::runtime_error("QueueAttr::QueueAttr: Invalid queue name : " + msg); } if (theQueue.empty()) { @@ -273,7 +273,7 @@ void QueueAttr::set_state_vec(const std::vector& state_vec) { void QueueAttr::set_name(const std::string& name) { std::string msg; - if (!Str::valid_name(name, msg)) { + if (!ecf::algorithm::is_valid_name(name, msg)) { throw std::runtime_error("QueueAttr::set_name: Invalid queue name : " + msg); } name_ = name; diff --git a/libs/attribute/src/ecflow/attribute/RepeatAttr.cpp b/libs/attribute/src/ecflow/attribute/RepeatAttr.cpp index d589a38c0..ddc8aefce 100644 --- a/libs/attribute/src/ecflow/attribute/RepeatAttr.cpp +++ b/libs/attribute/src/ecflow/attribute/RepeatAttr.cpp @@ -156,7 +156,7 @@ RepeatDate::RepeatDate(const std::string& variable, int start, int end, int delt end_(end), delta_(delta), value_(start) { - if (!Str::valid_name(variable)) { + if (!ecf::algorithm::is_valid_name(variable)) { throw std::runtime_error("RepeatDate::RepeatDate: Invalid name: " + variable); } @@ -500,7 +500,7 @@ RepeatDateTime::RepeatDateTime(const std::string& variable, Instant start, Insta end_(end), delta_(delta), value_(start) { - if (!Str::valid_name(variable)) { + if (!ecf::algorithm::is_valid_name(variable)) { throw std::runtime_error("RepeatDateTime::RepeatDateTime: Invalid name: " + variable); } @@ -806,7 +806,7 @@ void RepeatDateTime::set_value(long the_new_date) { RepeatDateList::RepeatDateList(const std::string& variable, const std::vector& l) : RepeatBase(variable), list_(l) { - if (!Str::valid_name(variable)) { + if (!ecf::algorithm::is_valid_name(variable)) { throw std::runtime_error("RepeatDateList: Invalid name: " + variable); } if (list_.empty()) { @@ -1131,7 +1131,7 @@ RepeatInteger::RepeatInteger(const std::string& variable, int start, int end, in delta_(delta), value_(start) { // cout << toString() << "\n"; - if (!Str::valid_name(variable)) { + if (!ecf::algorithm::is_valid_name(variable)) { throw std::runtime_error("RepeatInteger: Invalid name: " + variable); } } @@ -1325,7 +1325,7 @@ std::string RepeatInteger::prev_value_as_string() const { RepeatEnumerated::RepeatEnumerated(const std::string& variable, const std::vector& theEnums) : RepeatBase(variable), theEnums_(theEnums) { - if (!Str::valid_name(variable)) { + if (!ecf::algorithm::is_valid_name(variable)) { throw std::runtime_error("RepeatEnumerated: Invalid name: " + variable); } if (theEnums.empty()) { @@ -1543,7 +1543,7 @@ bool RepeatEnumerated::operator==(const RepeatEnumerated& rhs) const { RepeatString::RepeatString(const std::string& variable, const std::vector& theEnums) : RepeatBase(variable), theStrings_(theEnums) { - if (!Str::valid_name(variable)) { + if (!ecf::algorithm::is_valid_name(variable)) { throw std::runtime_error("RepeatString:: Invalid name: " + variable); } if (theEnums.empty()) { diff --git a/libs/attribute/src/ecflow/attribute/Variable.cpp b/libs/attribute/src/ecflow/attribute/Variable.cpp index ccabc98ac..efba46626 100644 --- a/libs/attribute/src/ecflow/attribute/Variable.cpp +++ b/libs/attribute/src/ecflow/attribute/Variable.cpp @@ -31,14 +31,14 @@ Variable::Variable(const std::string& name, const std::string& value) : n_(name), v_(value) { std::string msg; - if (!Str::valid_name(name, msg)) { + if (!ecf::algorithm::is_valid_name(name, msg)) { throw std::runtime_error("Variable::Variable: Invalid Variable name: " + msg); } } void Variable::set_name(const std::string& v) { std::string msg; - if (!Str::valid_name(v, msg)) { + if (!ecf::algorithm::is_valid_name(v, msg)) { throw std::runtime_error("Variable::set_name: Invalid Variable name: " + msg); } n_ = v; diff --git a/libs/base/src/ecflow/base/cts/task/QueueCmd.cpp b/libs/base/src/ecflow/base/cts/task/QueueCmd.cpp index 3eafd67d0..ec5674adc 100644 --- a/libs/base/src/ecflow/base/cts/task/QueueCmd.cpp +++ b/libs/base/src/ecflow/base/cts/task/QueueCmd.cpp @@ -277,7 +277,7 @@ void QueueCmd::create(Cmd_ptr& cmd, boost::program_options::variables_map& vm, A } std::string msg; - if (!Str::valid_name(queue_name, msg)) { + if (!ecf::algorithm::is_valid_name(queue_name, msg)) { throw std::runtime_error("QueueCmd: Invalid queue name : " + msg); } diff --git a/libs/core/CMakeLists.txt b/libs/core/CMakeLists.txt index 4d9e5f887..db61c07ca 100644 --- a/libs/core/CMakeLists.txt +++ b/libs/core/CMakeLists.txt @@ -47,6 +47,7 @@ set(test_srcs test/TestLog.cpp test/TestMessage.cpp test/TestMigration.cpp + test/TestNodeNameValidity.cpp test/TestNodePath.cpp test/TestPasswdFile.cpp test/TestPasswordEncryption.cpp diff --git a/libs/core/src/ecflow/core/Str.cpp b/libs/core/src/ecflow/core/Str.cpp index be6050ad7..5c223a8fe 100644 --- a/libs/core/src/ecflow/core/Str.cpp +++ b/libs/core/src/ecflow/core/Str.cpp @@ -14,7 +14,46 @@ namespace ecf { -const char* VALID_NODE_CHARS = "abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ0123456789_."; +namespace algorithm { + +inline bool is_valid_node_name_first_character(char c) { + return std::isalnum(c) || c == '_'; +} + +inline bool is_valid_node_name_following_character(char c) { + return std::isalnum(c) || c == '_' || c == '.'; +} + +bool is_valid_name(const std::string& name, std::string& error) { + + if (name.empty()) { + error = "Invalid name '': empty string"; + return false; + } + + if (!is_valid_node_name_first_character(name.front())) { + error = "Invalid name '"; + error += name; + error += "': only alphanumeric characters or underscore are accepted as first character"; + return false; + } + + if (!std::all_of(name.begin() + 1, name.end(), is_valid_node_name_following_character)) { + error = "Invalid name '"; + error += name; + error += "': only alphanumeric characters, underscore or dot are accepted"; + return false; + } + + return true; +} + +bool is_valid_name(const std::string& name) { + return !name.empty() && is_valid_node_name_first_character(name.front()) && + std::all_of(name.begin() + 1, name.end(), is_valid_node_name_following_character); +} + +} // namespace algorithm void Str::removeQuotes(std::string& s) { if (!s.empty()) { @@ -323,67 +362,6 @@ bool Str::caseInsGreater(const std::string& a, const std::string& b) { }); } -bool Str::valid_name(const std::string& name, std::string& msg) { - // valid names are alphabetic (alphanumeric | underscore | .) - // however we can't have a leading '.' as that can interfere with trigger expressions - - // verify that the string is not empty - if (name.empty()) { - msg = "Invalid name. Empty string."; - return false; - } - - // verify that the first character is alphanumeric or is an underscore - bool result = ecf::string_constants::alphanumeric_underscore_chars.find(name[0], 0) != std::string::npos; - if (!result) { - msg = "Valid names can only consist of alphanumeric characters, " - "underscores and dots (The first character cannot be a dot). " - "The first character is not valid (only alphanumeric or an underscore is allowed): "; - msg += name; - return false; - } - - // verify that any other characters are alphanumeric or underscore - if (name.size() > 1) { - result = name.find_first_not_of(VALID_NODE_CHARS, 1) == std::string::npos; - if (!result) { - msg = "Valid names can only consist of alphanumeric characters, " - "underscores and dots (The first character cannot be a dot). "; - if (name.find('\r') != std::string::npos) { - msg += "Windows line ending ? "; - } - msg += "'"; - msg += name; - msg += "'"; // use '' to show if PC format, i.e. carriage return - } - } - - return result; -} - -bool Str::valid_name(const std::string& name) { - // valid names are alphabetic (alphanumeric | underscore | .) - // however we can't have a leading '.' as that can interfere with trigger expressions - - // verify that the string is not empty - if (name.empty()) { - return false; - } - - // verify that the first character is alphabetic or has underscore - bool result = ecf::string_constants::alphanumeric_underscore_chars.find(name[0], 0) != std::string::npos; - if (!result) { - return false; - } - - // verify that any other characters are alphanumeric or underscore - if (name.size() > 1) { - result = name.find_first_not_of(VALID_NODE_CHARS, 1) == std::string::npos; - } - - return result; -} - int Str::to_int(const std::string& the_str, int error_return) { if (the_str.find_first_of(ecf::string_constants::numeric_chars, 0) != std::string::npos) { try { diff --git a/libs/core/src/ecflow/core/Str.hpp b/libs/core/src/ecflow/core/Str.hpp index 78a419acc..b1f733a00 100644 --- a/libs/core/src/ecflow/core/Str.hpp +++ b/libs/core/src/ecflow/core/Str.hpp @@ -101,6 +101,30 @@ inline std::string tolower(std::string s) { return s; } +/// +/// @brief Check if the \param name is valid according to the rules for node/attributes names in ecFlow. +/// +/// This is used to verify the validity of nodes and attributes (Variable, Label, Event, ...) names in ecFlow. +/// +/// If the name is valid, the error buffer will not be updated (e.g. cleared), otherwise the error buffer will contain a +/// description of the validation failure. +/// +/// @param name The name to be validated. +/// @param error A buffer to hold the error description, if validation fails (i.e. when function returns false). +/// @return true if the name is valid, false otherwise. +/// +bool is_valid_name(const std::string& name, std::string& error); + +/// +/// @brief Check if the \param name is valid according to the rules for node names in ecFlow. +/// +/// This is used to verify the validity of nodes and attributes (Variable, Label, Event, ...) names in ecFlow. +/// +/// @param name The name to be validated. +/// @return true if the name is valid, false otherwise. +/// +bool is_valid_name(const std::string& name); + } // namespace algorithm class Str { @@ -188,10 +212,6 @@ class Str { /// case-insensitive Greater static bool caseInsGreater(const std::string&, const std::string&); - /// Used for checking node names - static bool valid_name(const std::string& name, std::string& msg); - static bool valid_name(const std::string& name); - /** * Convert a given string to an integer. * diff --git a/libs/core/test/TestNodeNameValidity.cpp b/libs/core/test/TestNodeNameValidity.cpp new file mode 100644 index 000000000..1524e658e --- /dev/null +++ b/libs/core/test/TestNodeNameValidity.cpp @@ -0,0 +1,332 @@ +/* + * Copyright 2009- ECMWF. + * + * This software is licensed under the terms of the Apache Licence version 2.0 + * which can be obtained at http://www.apache.org/licenses/LICENSE-2.0. + * In applying this licence, ECMWF does not waive the privileges and immunities + * granted to it by virtue of its status as an intergovernmental organisation + * nor does it submit to any jurisdiction. + */ + +#include // std::ostream_iterator +#include +#include + +#include + +#include "ecflow/core/NodePath.hpp" +#include "ecflow/core/Stl.hpp" +#include "ecflow/core/Str.hpp" +#include "ecflow/core/Timer.hpp" +#include "ecflow/test/scaffold/Naming.hpp" + +// +// Important: +// +// The following implementation was the one used by ecFlow to check if a node name is valid. +// It is now replaced by the implementation in Str.hpp, but it was kept in the scope of this test +// to check that the new implementation behaves as expected. +// + +namespace ecf { +namespace prototype { + +const char* VALID_NODE_CHARS = "abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ0123456789_."; + +bool Str_valid_name(const std::string& name, std::string& msg) { + // valid names are alphabetic (alphanumeric | underscore | .) + // however we can't have a leading '.' as that can interfere with trigger expressions + + // verify that the string is not empty + if (name.empty()) { + msg = "Invalid name. Empty string."; + return false; + } + + // verify that the first character is alphanumeric or is an underscore + bool result = ecf::string_constants::alphanumeric_underscore_chars.find(name[0], 0) != std::string::npos; + if (!result) { + msg = "Valid names can only consist of alphanumeric characters, " + "underscores and dots (The first character cannot be a dot). " + "The first character is not valid (only alphanumeric or an underscore is allowed): "; + msg += name; + return false; + } + + // verify that any other characters are alphanumeric or underscore + if (name.size() > 1) { + result = name.find_first_not_of(VALID_NODE_CHARS, 1) == std::string::npos; + if (!result) { + msg = "Valid names can only consist of alphanumeric characters, " + "underscores and dots (The first character cannot be a dot). "; + if (name.find('\r') != std::string::npos) { + msg += "Windows line ending ? "; + } + msg += "'"; + msg += name; + msg += "'"; // use '' to show if PC format, i.e. carriage return + } + } + + return result; +} + +bool Str_valid_name(const std::string& name) { + // valid names are alphabetic (alphanumeric | underscore | .) + // however we can't have a leading '.' as that can interfere with trigger expressions + + // verify that the string is not empty + if (name.empty()) { + return false; + } + + // verify that the first character is alphabetic or has underscore + bool result = ecf::string_constants::alphanumeric_underscore_chars.find(name[0], 0) != std::string::npos; + if (!result) { + return false; + } + + // verify that any other characters are alphanumeric or underscore + if (name.size() > 1) { + result = name.find_first_not_of(VALID_NODE_CHARS, 1) == std::string::npos; + } + + return result; +} + +} // namespace prototype +} // namespace ecf + +BOOST_AUTO_TEST_SUITE(U_Core) + +BOOST_AUTO_TEST_SUITE(T_NodeNameValidity) + +BOOST_AUTO_TEST_CASE(test_node_name_validity) { + ECF_NAME_THIS_TEST(); + + struct tc + { + std::string name; + bool expected; + }; + + std::vector test_cases = { + {"", false}, + {".", false}, + {".a.", false}, + {".A", false}, + {".1", false}, + {"_", true}, + {"_a.", true}, + {"_A", true}, + {"_1", true}, + {"a.", true}, + {"A", true}, + {"1", true}, + {"aa.", true}, + {"AA", true}, + {"11", true}, + {"a.", true}, + {"ab.", true}, + {"a.b", true}, + {"a.b.", true}, + {"a._", true}, + {"ab._", true}, + {"a.b_", true}, + {"a.b._", true}, + {"a1", true}, + {"1a", true}, + {"a1.", true}, + {"1a.", true}, + {"a1_", true}, + {"1a_", true}, + {"a", true}, + {"a122345", true}, + {"_a122345", true}, + {"0", true}, + {"1", true}, + {"2", true}, + {"3", true}, + {"4", true}, + {"5", true}, + {"6", true}, + {"7", true}, + {"8", true}, + {"9", true}, + {"11", true}, + {"111", true}, + {"abcdefghABCDEFGH", true}, + {"1234567890abcdefghABCDEFGH", true}, + {"abcdefgh1234567890ABCDEFGH", true}, + {"abcdefghABCDEFGH1234567890", true}, + {"a.b.c.d.e.f.g.h.i.j.k.l.m.n.o.p.q.r.s.t.u.v.w.x.y.z", true}, + {"a.b.c.d.e.f.g.h.i.j.k.l.m.n.o.p.q.r.s.t.u.v.w.x.y.z.", true}, + {"abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ0123456789_.", true}, + {"_abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ0123456789_.", true}, + {".abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ0123456789_.", false}, + {"abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ0123456789_.\r", false}, + {"abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ0123456789_.\n", false}, + {"abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ0123456789_.\t", false}, + {"abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ0123456789_. ", false}, + {"\t", false}, + {"\n", false}, + {"\r\n", false}, + {"?", false}, + {"!", false}, + {"\"", false}, + {"$", false}, + {"%", false}, + {"^", false}, + {"*", false}, + {"(", false}, + {")", false}, + {"-", false}, + {"+", false}, + {":", false}, + {";", false}, + {"@", false}, + {"~", false}, + {"<", false}, + {">", false}, + {"!", false}, + {" invalid", false}, + {"\tinvalid", false}, + {"\ninvalid", false}, + {"invalid space", false}, + {"invalid ", false}, + {"inva lid", false}, + {"inva\tlid", false}, + {"inva\nlid", false}, + {"inva\rlid", false}, + }; + + for (const auto& tc : test_cases) { + + { + bool actual = ecf::algorithm::is_valid_name(tc.name); + bool original = ecf::prototype::Str_valid_name(tc.name); + + BOOST_CHECK_MESSAGE(actual == tc.expected, + "For name '" << tc.name << "' expected " << tc.expected << ", found " << actual); + BOOST_CHECK_MESSAGE(actual == original, + "For name '" << tc.name << "' both original and new algorithms provide same result"); + } + { + std::string actual_error; + bool actual = ecf::algorithm::is_valid_name(tc.name, actual_error); + + std::string original_error; + bool original = ecf::prototype::Str_valid_name(tc.name, original_error); + + BOOST_CHECK_MESSAGE(actual == tc.expected, + "For name '" << tc.name << "' expected " << tc.expected << ", found " << actual); + BOOST_CHECK_MESSAGE(actual == original, + "For name '" << tc.name << "' both original and new algorithms provide same result"); + + if (!tc.expected) { + auto expected_prefix = "Invalid name '" + tc.name + "': "; + auto actual_prefix = actual_error.substr(0, expected_prefix.size()); + + BOOST_CHECK_MESSAGE(actual_prefix == expected_prefix, + "The error message for '" << tc.name << "' expected prefix <" << expected_prefix + << ">, found <" << actual_prefix << ">"); + } + } + } +} + +BOOST_AUTO_TEST_CASE(test_node_name_validity_random) { + ECF_NAME_THIS_TEST(); + + struct timer + { + timer() = default; + ~timer() = default; + + void begin() { start = std::chrono::steady_clock::now(); } + void end() { finish = std::chrono::steady_clock::now(); } + + std::chrono::nanoseconds duration() const { + return std::chrono::duration_cast(finish - start); + } + + std::chrono::time_point start; + std::chrono::time_point finish; + std::string_view name; + std::string_view type; + }; + + auto generate_random_valid_name = []() { + std::string_view valid = "abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ0123456789_."; + + static std::random_device rd; + static std::mt19937_64 gen(rd()); + std::uniform_int_distribution length_dist(1, 20); + + std::uniform_int_distribution first_chars_dist(0, 62); // abc...ABC...0123456789_ (63 chars, no '.'!) + std::uniform_int_distribution following_chars_dist(0, 63); // abc...ABC...0123456789_. (64 chars) + + int length = length_dist(gen); + std::string name; + name.reserve(length); + for (int i = 0; i < length; ++i) { + if (i == 0) { + name.push_back(valid[first_chars_dist(gen)]); + } + else { + name.push_back(valid[following_chars_dist(gen)]); + } + } + return name; + }; + + double n_time = 0.; + double o_time = 0.; + + int iterations = 1000; + + for (int i = 0; i < iterations; ++i) { + + auto name = generate_random_valid_name(); + + bool original; + { + timer t; + + t.begin(); + original = ecf::prototype::Str_valid_name(name); + t.end(); + + auto ns = t.duration().count(); + o_time += ns; + ECF_TEST_DBG(<< "Original approach, name: '" << name << "', duration: " << ns << " ns"); + } + + bool actual; + { + timer t; + + t.begin(); + actual = ecf::algorithm::is_valid_name(name); + t.end(); + + auto ns = t.duration().count(); + n_time += ns; + ECF_TEST_DBG(<< " New approach, name: " << name << ", duration: " << ns << " ns"); + } + + BOOST_CHECK_MESSAGE(actual == original, + "For name '" << name << "' both original and new approaches provide same result"); + } + + double old_time_per_iteration = o_time / iterations; + ECF_TEST_DBG(<< "Original approach time: " << old_time_per_iteration << " ns"); + double new_time_per_iteration = n_time / iterations; + ECF_TEST_DBG(<< " New approach time: " << new_time_per_iteration << " ns"); + + BOOST_CHECK_MESSAGE(new_time_per_iteration < old_time_per_iteration, "New algorithm is faster than original"); +} + +BOOST_AUTO_TEST_SUITE_END() + +BOOST_AUTO_TEST_SUITE_END() diff --git a/libs/core/test/TestStr.cpp b/libs/core/test/TestStr.cpp index 89fd3072e..5b090786d 100644 --- a/libs/core/test/TestStr.cpp +++ b/libs/core/test/TestStr.cpp @@ -885,64 +885,6 @@ BOOST_AUTO_TEST_CASE(test_int_to_str_perf, *boost::unit_test::disabled()) { } } -BOOST_AUTO_TEST_CASE(test_str_valid_name) { - ECF_NAME_THIS_TEST(); - - std::vector valid; - valid.emplace_back("a"); - valid.emplace_back("a122345"); - valid.emplace_back("_a122345"); - valid.emplace_back("_"); - valid.emplace_back("0"); - valid.emplace_back("1"); - valid.emplace_back("2"); - valid.emplace_back("3"); - valid.emplace_back("4"); - valid.emplace_back("5"); - valid.emplace_back("6"); - valid.emplace_back("7"); - valid.emplace_back("8"); - valid.emplace_back("9"); - valid.emplace_back("11"); - valid.emplace_back("111"); - for (const auto& i : valid) { - std::string msg; - BOOST_CHECK_MESSAGE(Str::valid_name(i, msg), "Expected " << i << " to be valid"); - BOOST_CHECK_MESSAGE(Str::valid_name(i), "Expected " << i << " to be valid"); - } - - BOOST_CHECK_MESSAGE(!Str::valid_name(""), "Expected empty string to be in-valid"); - BOOST_CHECK_MESSAGE(!Str::valid_name("."), "Expected '.' string to be in-valid"); - std::vector invalid; - invalid.emplace_back("?"); - invalid.emplace_back("!"); - invalid.emplace_back("\""); - invalid.emplace_back("$"); - invalid.emplace_back("%"); - invalid.emplace_back("^"); - invalid.emplace_back("*"); - invalid.emplace_back("("); - invalid.emplace_back(")"); - invalid.emplace_back("-"); - invalid.emplace_back("+"); - invalid.emplace_back(":"); - invalid.emplace_back(";"); - invalid.emplace_back("@"); - invalid.emplace_back("~"); - invalid.emplace_back("<"); - invalid.emplace_back(">"); - invalid.emplace_back("!"); - for (const auto& i : invalid) { - std::string msg; - BOOST_CHECK_MESSAGE(!Str::valid_name(i, msg), "Expected " << i << " to be in-valid"); - BOOST_CHECK_MESSAGE(!Str::valid_name(i), "Expected " << i << " to be in-valid"); - - std::string s = "a" + i; - BOOST_CHECK_MESSAGE(!Str::valid_name(s, msg), "Expected " << s << " to be in-valid"); - BOOST_CHECK_MESSAGE(!Str::valid_name(s), "Expected " << s << " to be in-valid"); - } -} - BOOST_AUTO_TEST_SUITE_END() BOOST_AUTO_TEST_SUITE_END() diff --git a/libs/node/src/ecflow/node/AvisoAttr.cpp b/libs/node/src/ecflow/node/AvisoAttr.cpp index 9cadff605..00acb673c 100644 --- a/libs/node/src/ecflow/node/AvisoAttr.cpp +++ b/libs/node/src/ecflow/node/AvisoAttr.cpp @@ -36,7 +36,7 @@ std::string ensure_single_quotes(const AvisoAttr::listener_t listener) { } // namespace implementation bool AvisoAttr::is_valid_name(const std::string& name) { - return ecf::Str::valid_name(name); + return ecf::algorithm::is_valid_name(name); } AvisoAttr::AvisoAttr(Node* parent, @@ -59,7 +59,7 @@ AvisoAttr::AvisoAttr(Node* parent, reason_{implementation::ensure_single_quotes(reason)}, revision_{revision}, controller_{nullptr} { - if (!ecf::Str::valid_name(name_)) { + if (!ecf::algorithm::is_valid_name(name_)) { THROW_EXCEPTION(ecf::InvalidArgument, "Invalid AvisoAttr name :" << name_); } } diff --git a/libs/node/src/ecflow/node/InLimit.cpp b/libs/node/src/ecflow/node/InLimit.cpp index 4e4c9ac2f..bad4a9800 100644 --- a/libs/node/src/ecflow/node/InLimit.cpp +++ b/libs/node/src/ecflow/node/InLimit.cpp @@ -35,7 +35,7 @@ InLimit::InLimit(const std::string& name, tokens_(tokens), limit_this_node_only_(limit_this_node_only), limit_submission_(limit_submission) { - if (check && !Str::valid_name(name)) { + if (check && !ecf::algorithm::is_valid_name(name)) { throw std::runtime_error("InLimit::InLimit: Invalid InLimit name: " + name); } if (limit_this_node_only_ && limit_submission_) { diff --git a/libs/node/src/ecflow/node/Limit.cpp b/libs/node/src/ecflow/node/Limit.cpp index 918aba1c2..6a22cb671 100644 --- a/libs/node/src/ecflow/node/Limit.cpp +++ b/libs/node/src/ecflow/node/Limit.cpp @@ -21,7 +21,7 @@ Limit::Limit(const std::string& name, int limit) : n_(name), lim_(limit) { - if (!ecf::Str::valid_name(name)) { + if (!ecf::algorithm::is_valid_name(name)) { throw std::runtime_error("Limit::Limit: Invalid Limit name: " + name); } } @@ -31,7 +31,7 @@ Limit::Limit(const std::string& name, int limit, int value, const std::set& vec, const py::list& lis std::string part_expr; if (py::extract(list[i]).check()) { part_expr = py::extract(list[i]); - if (ecf::Str::valid_name(part_expr)) { + if (ecf::algorithm::is_valid_name(part_expr)) { part_expr += " == complete"; } }