4ded401aab
Add a config parser method which, for now, deals only with escaping the literal backslash character and logs an error message to inform the user of the coming change. The error message includes a properly escaped value for the user. As a result of introducing an escape character('\'): - Warn the user of any unescaped backslashes, as they will not be treated as a literal character in the future - For now, still treat a single backslash as a literal character - Treat two consecutive backslashes as a single properly escaped literal backslash Also: - Add documentation about the escape character to polybar(5) manpage - Add info about the escape character to changelog - Add testcases for ParseLineKeyTest - Add new test ParseEscapedValueTest Resolves: First step in #2354 Improve value parsing - Take value arg in as an rvalue reference and move parsed value back - Remove unnecessary if statement - Rename function - Improve error message - Improve function description - Format Add escape character documentation to manpages Add information about the escape character to the polybar(5) manpage. Add info about the esacape character to changelog Add test cases for ParseLineKeyTest Fix ParseLineKeyTest test cases Also make config parser method parse_escaped_value private. Add tests for escaped_value_parser method Also remove unsued include statement Simplify parse_escaped_value in config_parser Remove unnecessary escaped value generation, so we do not have to keep track of index differences. Fix ParseEscapedValueTest test cases Fix parse_escaped_value Add more test cases for ParseLineKeyTest Update CHANGELOG.md Co-authored-by: Patrick Ziegler <p.ziegler96@gmail.com> Adress review - Adjust documentation - Small code changes Improve parse_escaped_value Add initial support for an escape character Add a config parser method which, for now, deals only with escaping the literal backslash character and logs an error message to inform the user of the coming change. The error message includes a properly escaped value for the user. As a result of introducing an escape character('\'): - Warn the user of any unescaped backslashes, as they will not be treated as a literal character in the future - For now, still treat a single backslash as a literal character - Treat two consecutive backslashes as a single properly escaped literal backslash Resolves: First step in #2354 Improve value parsing - Take value arg in as an rvalue reference and move parsed value back - Remove unnecessary if statement - Rename function - Improve error message - Improve function description - Format Add info about the esacape character to changelog Add test cases for ParseLineKeyTest Fix ParseLineKeyTest test cases Also make config parser method parse_escaped_value private. Add tests for escaped_value_parser method Also remove unsued include statement Simplify parse_escaped_value in config_parser Remove unnecessary escaped value generation, so we do not have to keep track of index differences. Fix ParseEscapedValueTest test cases Add more test cases for ParseLineKeyTest Adress review - Adjust documentation - Small code changes Remove duplicate testcase from ParseLineKeyTest Add initial support for an escape character Add a config parser method which, for now, deals only with escaping the literal backslash character and logs an error message to inform the user of the coming change. The error message includes a properly escaped value for the user. As a result of introducing an escape character('\'): - Warn the user of any unescaped backslashes, as they will not be treated as a literal character in the future - For now, still treat a single backslash as a literal character - Treat two consecutive backslashes as a single properly escaped literal backslash Resolves: First step in #2354 Improve value parsing - Take value arg in as an rvalue reference and move parsed value back - Remove unnecessary if statement - Rename function - Improve error message - Improve function description - Format Fix ParseLineKeyTest test cases Also make config parser method parse_escaped_value private. Remove duplicate testcase from ParseLineKeyTest
279 lines
8.5 KiB
C++
279 lines
8.5 KiB
C++
#include "components/config_parser.hpp"
|
|
|
|
#include "common/test.hpp"
|
|
#include "components/logger.hpp"
|
|
|
|
using namespace polybar;
|
|
using namespace std;
|
|
|
|
/**
|
|
* \brief Testing-only subclass of config_parser to change access level
|
|
*/
|
|
class TestableConfigParser : public config_parser {
|
|
using config_parser::config_parser;
|
|
|
|
public:
|
|
using config_parser::get_line_type;
|
|
|
|
public:
|
|
using config_parser::parse_key;
|
|
|
|
public:
|
|
using config_parser::parse_header;
|
|
|
|
public:
|
|
using config_parser::parse_line;
|
|
|
|
public:
|
|
using config_parser::parse_escaped_value;
|
|
|
|
public:
|
|
using config_parser::m_files;
|
|
};
|
|
|
|
/**
|
|
* \brief Fixture class
|
|
*/
|
|
class ConfigParser : public ::testing::Test {
|
|
protected:
|
|
const logger l = logger(loglevel::NONE);
|
|
unique_ptr<TestableConfigParser> parser = make_unique<TestableConfigParser>(l, "/dev/zero", "TEST");
|
|
};
|
|
|
|
// ParseLineTest {{{
|
|
class ParseLineInValidTest : public ConfigParser, public ::testing::WithParamInterface<string> {};
|
|
|
|
class ParseLineHeaderTest : public ConfigParser, public ::testing::WithParamInterface<pair<string, string>> {};
|
|
|
|
class ParseLineKeyTest : public ConfigParser,
|
|
public ::testing::WithParamInterface<pair<pair<string, string>, string>> {};
|
|
|
|
vector<string> parse_line_invalid_list = {
|
|
" # comment",
|
|
"; comment",
|
|
"\t#",
|
|
"",
|
|
" ",
|
|
"\t ",
|
|
};
|
|
|
|
vector<pair<string, string>> parse_line_header_list = {
|
|
{"section", "\t[section]"},
|
|
{"section", "\t[section] "},
|
|
{"bar/name", "\t[bar/name] "},
|
|
};
|
|
|
|
vector<pair<pair<string, string>, string>> parse_line_key_list = {
|
|
{{"key", "value"}, " key = value"},
|
|
{{"key", ""}, " key\t = \"\""},
|
|
{{"key", "\""}, " key\t = \"\"\""},
|
|
{{"key", "\\"}, " key = \\"},
|
|
{{"key", "\\"}, " key = \\\\"},
|
|
{{"key", "\\val\\ue\\"}, " key = \\val\\ue\\"},
|
|
{{"key", "\\val\\ue\\"}, " key = \\\\val\\\\ue\\\\"},
|
|
{{"key", "\\val\\ue\\"}, " key = \"\\val\\ue\\\""},
|
|
{{"key", "\\val\\ue\\"}, " key = \"\\\\val\\\\ue\\\\\""},
|
|
};
|
|
|
|
INSTANTIATE_TEST_SUITE_P(Inst, ParseLineInValidTest, ::testing::ValuesIn(parse_line_invalid_list));
|
|
|
|
TEST_P(ParseLineInValidTest, correctness) {
|
|
line_t line = parser->parse_line(GetParam());
|
|
|
|
EXPECT_FALSE(line.useful);
|
|
}
|
|
|
|
INSTANTIATE_TEST_SUITE_P(Inst, ParseLineHeaderTest, ::testing::ValuesIn(parse_line_header_list));
|
|
|
|
TEST_P(ParseLineHeaderTest, correctness) {
|
|
line_t line = parser->parse_line(GetParam().second);
|
|
|
|
EXPECT_TRUE(line.useful);
|
|
|
|
EXPECT_TRUE(line.is_header);
|
|
EXPECT_EQ(GetParam().first, line.header);
|
|
}
|
|
|
|
INSTANTIATE_TEST_SUITE_P(Inst, ParseLineKeyTest, ::testing::ValuesIn(parse_line_key_list));
|
|
|
|
TEST_P(ParseLineKeyTest, correctness) {
|
|
line_t line = parser->parse_line(GetParam().second);
|
|
|
|
EXPECT_TRUE(line.useful);
|
|
|
|
EXPECT_FALSE(line.is_header);
|
|
EXPECT_EQ(GetParam().first.first, line.key);
|
|
EXPECT_EQ(GetParam().first.second, line.value);
|
|
}
|
|
|
|
TEST_F(ParseLineInValidTest, throwsSyntaxError) {
|
|
EXPECT_THROW(parser->parse_line("unknown"), syntax_error);
|
|
EXPECT_THROW(parser->parse_line("\ufeff"), syntax_error);
|
|
}
|
|
// }}}
|
|
|
|
// GetLineTypeTest {{{
|
|
|
|
/**
|
|
* \brief Class for parameterized tests on get_line_type
|
|
*
|
|
* Parameters are pairs of the expected line type and a string that should be
|
|
* detected as that line type
|
|
*/
|
|
class GetLineTypeTest : public ConfigParser, public ::testing::WithParamInterface<pair<line_type, string>> {};
|
|
|
|
/**
|
|
* \brief Helper function generate GetLineTypeTest parameter values
|
|
*/
|
|
vector<pair<line_type, string>> line_type_transform(vector<string>&& in, line_type type) {
|
|
vector<pair<line_type, string>> out;
|
|
|
|
out.reserve(in.size());
|
|
for (const auto& i : in) {
|
|
out.emplace_back(type, i);
|
|
}
|
|
|
|
return out;
|
|
}
|
|
|
|
/**
|
|
* \brief Parameter values for GetLineTypeTest
|
|
*/
|
|
auto line_type_key = line_type_transform({"a = b", " a =b", " a\t =\t \t b", "a = "}, line_type::KEY);
|
|
auto line_type_header = line_type_transform({"[section]", "[section]", "[section/sub]"}, line_type::HEADER);
|
|
auto line_type_comment = line_type_transform({";abc", "#abc", ";", "#"}, line_type::COMMENT);
|
|
auto line_type_empty = line_type_transform({""}, line_type::EMPTY);
|
|
auto line_type_unknown = line_type_transform({"|a", " |a", "a"}, line_type::UNKNOWN);
|
|
|
|
/**
|
|
* Instantiate GetLineTypeTest for the different line types
|
|
*/
|
|
INSTANTIATE_TEST_SUITE_P(LineTypeKey, GetLineTypeTest, ::testing::ValuesIn(line_type_key));
|
|
INSTANTIATE_TEST_SUITE_P(LineTypeHeader, GetLineTypeTest, ::testing::ValuesIn(line_type_header));
|
|
INSTANTIATE_TEST_SUITE_P(LineTypeComment, GetLineTypeTest, ::testing::ValuesIn(line_type_comment));
|
|
INSTANTIATE_TEST_SUITE_P(LineTypeEmpty, GetLineTypeTest, ::testing::ValuesIn(line_type_empty));
|
|
INSTANTIATE_TEST_SUITE_P(LineTypeUnknown, GetLineTypeTest, ::testing::ValuesIn(line_type_unknown));
|
|
|
|
/**
|
|
* \brief Parameterized test for get_line_type
|
|
*/
|
|
TEST_P(GetLineTypeTest, correctness) {
|
|
EXPECT_EQ(GetParam().first, parser->get_line_type(GetParam().second));
|
|
}
|
|
|
|
// }}}
|
|
|
|
// ParseKeyTest {{{
|
|
|
|
/**
|
|
* \brief Class for parameterized tests on parse_key
|
|
*
|
|
* The first element of the pair is the expected key-value pair and the second
|
|
* element is the string to be parsed, has to be trimmed and valid.
|
|
*/
|
|
class ParseKeyTest : public ConfigParser, public ::testing::WithParamInterface<pair<pair<string, string>, string>> {};
|
|
|
|
vector<pair<pair<string, string>, string>> parse_key_list = {
|
|
{{"key", "value"}, "key = value"},
|
|
{{"key", "value"}, "key=value"},
|
|
{{"key", "value"}, "key =\"value\""},
|
|
{{"key", "value"}, "key\t=\t \"value\""},
|
|
{{"key", "\"value"}, "key = \"value"},
|
|
{{"key", "value\""}, "key = value\""},
|
|
{{"key", "= value"}, "key == value"},
|
|
{{"key", ""}, "key ="},
|
|
{{"key", ""}, R"(key ="")"},
|
|
{{"key", "\"\""}, R"(key ="""")"},
|
|
};
|
|
|
|
INSTANTIATE_TEST_SUITE_P(Inst, ParseKeyTest, ::testing::ValuesIn(parse_key_list));
|
|
|
|
/**
|
|
* Parameterized test for parse_key with valid line
|
|
*/
|
|
TEST_P(ParseKeyTest, correctness) {
|
|
EXPECT_EQ(GetParam().first, parser->parse_key(GetParam().second));
|
|
}
|
|
|
|
/**
|
|
* Tests if exception is thrown for invalid key line
|
|
*/
|
|
TEST_F(ParseKeyTest, throwsSyntaxError) {
|
|
EXPECT_THROW(parser->parse_key("= empty name"), syntax_error);
|
|
EXPECT_THROW(parser->parse_key("forbidden char = value"), syntax_error);
|
|
EXPECT_THROW(parser->parse_key("forbidden\tchar = value"), syntax_error);
|
|
}
|
|
// }}}
|
|
|
|
// ParseHeaderTest {{{
|
|
|
|
/**
|
|
* \brief Class for parameterized tests on parse_key
|
|
*
|
|
* The first element of the pair is the expected key-value pair and the second
|
|
* element is the string to be parsed, has to be trimmed and valid
|
|
*/
|
|
class ParseHeaderTest : public ConfigParser, public ::testing::WithParamInterface<pair<string, string>> {};
|
|
|
|
vector<pair<string, string>> parse_header_list = {
|
|
{"section", "[section]"},
|
|
{"bar/name", "[bar/name]"},
|
|
{"with_underscore", "[with_underscore]"},
|
|
};
|
|
|
|
INSTANTIATE_TEST_SUITE_P(Inst, ParseHeaderTest, ::testing::ValuesIn(parse_header_list));
|
|
|
|
/**
|
|
* Parameterized test for parse_header with valid line
|
|
*/
|
|
TEST_P(ParseHeaderTest, correctness) {
|
|
EXPECT_EQ(GetParam().first, parser->parse_header(GetParam().second));
|
|
}
|
|
|
|
/**
|
|
* Tests if exception is thrown for invalid header line
|
|
*/
|
|
TEST_F(ParseHeaderTest, throwsSyntaxError) {
|
|
EXPECT_THROW(parser->parse_header("[]"), syntax_error);
|
|
EXPECT_THROW(parser->parse_header("[no_end"), syntax_error);
|
|
EXPECT_THROW(parser->parse_header("[forbidden char]"), syntax_error);
|
|
EXPECT_THROW(parser->parse_header("[forbidden\tchar]"), syntax_error);
|
|
|
|
// Reserved names
|
|
EXPECT_THROW(parser->parse_header("[self]"), syntax_error);
|
|
EXPECT_THROW(parser->parse_header("[BAR]"), syntax_error);
|
|
EXPECT_THROW(parser->parse_header("[root]"), syntax_error);
|
|
}
|
|
// }}}
|
|
|
|
// ParseEscapedValueTest {{{
|
|
|
|
/**
|
|
* \brief Class for parameterized tests on parse_escaped_value
|
|
*
|
|
* The first element of the pair is the expected value and the second
|
|
* element is the escaped string to be parsed.
|
|
*/
|
|
class ParseEscapedValueTest : public ConfigParser, public ::testing::WithParamInterface<pair<string, string>> {};
|
|
|
|
vector<pair<string, string>> parse_escaped_value_list = {
|
|
{"\\", "\\"},
|
|
{"\\", "\\\\"},
|
|
{"\\val\\ue\\", "\\val\\ue\\"},
|
|
{"\\val\\ue\\", "\\\\val\\\\ue\\\\"},
|
|
{"\"\\val\\ue\\\"", "\"\\val\\ue\\\""},
|
|
{"\"\\val\\ue\\\"", "\"\\\\val\\\\ue\\\\\""},
|
|
};
|
|
|
|
INSTANTIATE_TEST_SUITE_P(Inst, ParseEscapedValueTest, ::testing::ValuesIn(parse_escaped_value_list));
|
|
|
|
/**
|
|
* Parameterized test for parse_escaped_value with valid line
|
|
*/
|
|
TEST_P(ParseEscapedValueTest, correctness) {
|
|
string value = GetParam().second;
|
|
value = parser->parse_escaped_value(move(value), "key");
|
|
EXPECT_EQ(GetParam().first, value);
|
|
}
|
|
// }}}
|