Fix up CommandParser operation and add unit tests
This commit is contained in:
parent
0da4108c64
commit
210a7b82c5
@ -18,7 +18,7 @@ bool CommandParser::parse(const EncodedString & command)
|
|||||||
auto collect_arg = [this, ¤t_arg, &arg_content] {
|
auto collect_arg = [this, ¤t_arg, &arg_content] {
|
||||||
if (arg_content)
|
if (arg_content)
|
||||||
{
|
{
|
||||||
m_args.push_back(std::make_shared<EncodedString>(¤t_arg[0], current_arg.size()));
|
m_args.push_back(EncodedString(¤t_arg[0], current_arg.size()));
|
||||||
current_arg.clear();
|
current_arg.clear();
|
||||||
arg_content = false;
|
arg_content = false;
|
||||||
}
|
}
|
||||||
@ -30,16 +30,13 @@ bool CommandParser::parse(const EncodedString & command)
|
|||||||
if ((quote != 0u) && (cp == quote))
|
if ((quote != 0u) && (cp == quote))
|
||||||
{
|
{
|
||||||
quote = 0u;
|
quote = 0u;
|
||||||
++it;
|
|
||||||
continue;
|
|
||||||
}
|
}
|
||||||
if ((quote == 0u) && ((cp == '\'') || (cp == '"')))
|
else if ((quote == 0u) && ((cp == '\'') || (cp == '"')))
|
||||||
{
|
{
|
||||||
quote = cp;
|
quote = cp;
|
||||||
++it;
|
arg_content = true;
|
||||||
continue;
|
|
||||||
}
|
}
|
||||||
if (cp == '\\')
|
else if (cp == '\\')
|
||||||
{
|
{
|
||||||
if (++it == end)
|
if (++it == end)
|
||||||
{
|
{
|
||||||
@ -47,19 +44,16 @@ bool CommandParser::parse(const EncodedString & command)
|
|||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
append_char();
|
append_char();
|
||||||
++it;
|
|
||||||
continue;
|
|
||||||
}
|
}
|
||||||
else if (cp == ' ')
|
else if ((cp == ' ') && (quote == 0u))
|
||||||
{
|
{
|
||||||
collect_arg();
|
collect_arg();
|
||||||
}
|
}
|
||||||
else
|
else
|
||||||
{
|
{
|
||||||
append_char();
|
append_char();
|
||||||
++it;
|
|
||||||
continue;
|
|
||||||
}
|
}
|
||||||
|
++it;
|
||||||
}
|
}
|
||||||
|
|
||||||
if (quote != 0u)
|
if (quote != 0u)
|
||||||
|
@ -9,7 +9,7 @@ class CommandParser
|
|||||||
{
|
{
|
||||||
public:
|
public:
|
||||||
bool parse(const EncodedString & command);
|
bool parse(const EncodedString & command);
|
||||||
std::shared_ptr<EncodedString> operator[](size_t index) const
|
const EncodedString & operator[](size_t index) const
|
||||||
{
|
{
|
||||||
return m_args[index];
|
return m_args[index];
|
||||||
}
|
}
|
||||||
@ -21,9 +21,13 @@ public:
|
|||||||
{
|
{
|
||||||
return m_parse_error;
|
return m_parse_error;
|
||||||
}
|
}
|
||||||
|
const std::vector<EncodedString> args() const
|
||||||
|
{
|
||||||
|
return m_args;
|
||||||
|
}
|
||||||
|
|
||||||
protected:
|
protected:
|
||||||
std::vector<std::shared_ptr<EncodedString>> m_args;
|
std::vector<EncodedString> m_args;
|
||||||
std::string m_parse_error;
|
std::string m_parse_error;
|
||||||
};
|
};
|
||||||
|
|
||||||
|
@ -46,6 +46,10 @@ public:
|
|||||||
};
|
};
|
||||||
|
|
||||||
EncodedString(const uint8_t * data, size_t size, Encoding::Type encoding = Encoding::UTF_8);
|
EncodedString(const uint8_t * data, size_t size, Encoding::Type encoding = Encoding::UTF_8);
|
||||||
|
EncodedString(const std::string & s)
|
||||||
|
: EncodedString((const uint8_t *)&s[0], s.size())
|
||||||
|
{
|
||||||
|
}
|
||||||
|
|
||||||
iterator begin() const;
|
iterator begin() const;
|
||||||
iterator end() const;
|
iterator end() const;
|
||||||
@ -61,6 +65,16 @@ public:
|
|||||||
{
|
{
|
||||||
return (*m_data)[index];
|
return (*m_data)[index];
|
||||||
}
|
}
|
||||||
|
bool operator==(const EncodedString & other) const
|
||||||
|
{
|
||||||
|
return (m_encoding == other.m_encoding) &&
|
||||||
|
(m_size == other.m_size) &&
|
||||||
|
(memcmp(&(*m_data)[0], &(*other.m_data)[0], m_size) == 0);
|
||||||
|
}
|
||||||
|
bool operator!=(const EncodedString & other) const
|
||||||
|
{
|
||||||
|
return !(*this == other);
|
||||||
|
}
|
||||||
bool operator==(const std::string & s) const
|
bool operator==(const std::string & s) const
|
||||||
{
|
{
|
||||||
return (m_size == s.size()) && (memcmp(&(*m_data)[0], &s[0], m_size) == 0);
|
return (m_size == s.size()) && (memcmp(&(*m_data)[0], &s[0], m_size) == 0);
|
||||||
|
90
test/src/test_CommandParser.cc
Normal file
90
test/src/test_CommandParser.cc
Normal file
@ -0,0 +1,90 @@
|
|||||||
|
#include "gtest/gtest.h"
|
||||||
|
#include "CommandParser.h"
|
||||||
|
#include <iostream>
|
||||||
|
|
||||||
|
typedef std::string ss;
|
||||||
|
|
||||||
|
void dump(const std::vector<EncodedString> & v)
|
||||||
|
{
|
||||||
|
for (auto s : v)
|
||||||
|
{
|
||||||
|
std::cerr << " \"" << ss((const char *)&s[0], s.size()) << "\"" << std::endl;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
#define compare_parsed(expected, results) \
|
||||||
|
compare_parsed_(expected, results, __LINE__)
|
||||||
|
|
||||||
|
void compare_parsed_(const std::vector<EncodedString> & expected,
|
||||||
|
const std::vector<EncodedString> & results,
|
||||||
|
int line)
|
||||||
|
{
|
||||||
|
if (expected != results)
|
||||||
|
{
|
||||||
|
std::cerr << "Line " << line << " unexpected parsed result:" << std::endl;
|
||||||
|
std::cerr << "Expected:" << std::endl;
|
||||||
|
dump(expected);
|
||||||
|
std::cerr << "Actual:" << std::endl;
|
||||||
|
dump(results);
|
||||||
|
}
|
||||||
|
EXPECT_EQ(expected, results);
|
||||||
|
}
|
||||||
|
|
||||||
|
TEST(CommandParser_parse, parses_a_basic_command)
|
||||||
|
{
|
||||||
|
EncodedString command("w /home/josh/file");
|
||||||
|
CommandParser cp;
|
||||||
|
EXPECT_TRUE(cp.parse(command));
|
||||||
|
compare_parsed(std::vector<EncodedString>({
|
||||||
|
ss("w"),
|
||||||
|
ss("/home/josh/file"),
|
||||||
|
}), cp.args());
|
||||||
|
}
|
||||||
|
|
||||||
|
TEST(CommandParser_parse, parses_a_command_with_quoted_arguments)
|
||||||
|
{
|
||||||
|
EncodedString command("echo 'Hello There'\" World!\" a'b'c d\"e\"f ' ' \"\" 1 2 3");
|
||||||
|
CommandParser cp;
|
||||||
|
EXPECT_TRUE(cp.parse(command));
|
||||||
|
compare_parsed(std::vector<EncodedString>({
|
||||||
|
ss("echo"),
|
||||||
|
ss("Hello There World!"),
|
||||||
|
ss("abc"),
|
||||||
|
ss("def"),
|
||||||
|
ss(" "),
|
||||||
|
ss(""),
|
||||||
|
ss("1"),
|
||||||
|
ss("2"),
|
||||||
|
ss("3"),
|
||||||
|
}), cp.args());
|
||||||
|
}
|
||||||
|
|
||||||
|
TEST(CommandParser_parse, parses_a_command_with_escaped_characters)
|
||||||
|
{
|
||||||
|
EncodedString command(" echo \\ ' \\' a ' \"b \\\" c\" \\\\too");
|
||||||
|
CommandParser cp;
|
||||||
|
EXPECT_TRUE(cp.parse(command));
|
||||||
|
compare_parsed(std::vector<EncodedString>({
|
||||||
|
ss("echo"),
|
||||||
|
ss(" "),
|
||||||
|
ss(" ' a "),
|
||||||
|
ss("b \" c"),
|
||||||
|
ss("\\too"),
|
||||||
|
}), cp.args());
|
||||||
|
}
|
||||||
|
|
||||||
|
TEST(CommandParser_parse, fails_to_parse_with_an_unterminated_escape_sequence)
|
||||||
|
{
|
||||||
|
EncodedString command("echo hi\\");
|
||||||
|
CommandParser cp;
|
||||||
|
EXPECT_FALSE(cp.parse(command));
|
||||||
|
EXPECT_EQ("Unterminated escape sequence", cp.parse_error());
|
||||||
|
}
|
||||||
|
|
||||||
|
TEST(CommandParser_parse, fails_to_parse_with_an_unterminated_quoted_region)
|
||||||
|
{
|
||||||
|
EncodedString command("echo Bob's Golf Bag");
|
||||||
|
CommandParser cp;
|
||||||
|
EXPECT_FALSE(cp.parse(command));
|
||||||
|
EXPECT_EQ("Unterminated quoted region", cp.parse_error());
|
||||||
|
}
|
Loading…
x
Reference in New Issue
Block a user