mirror of
https://github.com/polybar/polybar.git
synced 2026-02-06 08:05:50 +00:00
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);
|
|
}
|
|
// }}}
|