From aee0b7c337d7eed28b2f9bce2e232f27d005f700 Mon Sep 17 00:00:00 2001 From: Eberhard Graether Date: Sun, 31 Aug 2014 11:36:08 +0200 Subject: [PATCH] test: added QueryTree tests checking correct query syntax --- src/lib/data/query/QueryCommand.cpp | 5 - src/lib/data/query/QueryNode.cpp | 11 + src/lib/data/query/QueryNode.h | 3 + src/lib/data/query/QueryOperator.cpp | 2 +- src/lib/data/query/QueryTree.cpp | 26 +- src/lib/utility/utilityString.h | 7 +- src/test/CMakeLists.txt | 1 + src/test/QueryTreeTestSuite.h | 351 +++++++++++++++++++++++++++ 8 files changed, 392 insertions(+), 14 deletions(-) create mode 100644 src/test/QueryTreeTestSuite.h diff --git a/src/lib/data/query/QueryCommand.cpp b/src/lib/data/query/QueryCommand.cpp index b7a530ad..e4a48d72 100644 --- a/src/lib/data/query/QueryCommand.cpp +++ b/src/lib/data/query/QueryCommand.cpp @@ -40,11 +40,6 @@ bool QueryCommand::isComplete() const void QueryCommand::print(std::ostream& ostream) const { ostream << m_name; - - if (!isComplete()) - { - ostream << " INVALID"; - } } QueryCommand::CommandType QueryCommand::getType() const diff --git a/src/lib/data/query/QueryNode.cpp b/src/lib/data/query/QueryNode.cpp index 9546de01..7359779e 100644 --- a/src/lib/data/query/QueryNode.cpp +++ b/src/lib/data/query/QueryNode.cpp @@ -2,6 +2,7 @@ QueryNode::QueryNode() : m_isGroup(false) + , m_isComplete(true) { } @@ -28,6 +29,11 @@ void QueryNode::print(std::ostream& ostream, int n) const ostream << ')'; } + if (!m_isComplete || !isComplete()) + { + ostream << " INVALID"; + } + ostream << '\n'; } @@ -40,3 +46,8 @@ void QueryNode::setIsGroup(bool isGroup) { m_isGroup = isGroup; } + +void QueryNode::setIsComplete(bool isComplete) +{ + m_isComplete = isComplete; +} diff --git a/src/lib/data/query/QueryNode.h b/src/lib/data/query/QueryNode.h index c0344039..170e0ec4 100644 --- a/src/lib/data/query/QueryNode.h +++ b/src/lib/data/query/QueryNode.h @@ -21,8 +21,11 @@ public: bool isGroup() const; void setIsGroup(bool isGroup); + void setIsComplete(bool isComplete); + private: bool m_isGroup; + bool m_isComplete; }; #endif // QUERY_NODE_H diff --git a/src/lib/data/query/QueryOperator.cpp b/src/lib/data/query/QueryOperator.cpp index 67e74cae..ef6d17d2 100644 --- a/src/lib/data/query/QueryOperator.cpp +++ b/src/lib/data/query/QueryOperator.cpp @@ -86,7 +86,7 @@ bool QueryOperator::isComplete() const void QueryOperator::print(std::ostream& ostream) const { - ostream << m_type; + ostream << getOperator(m_type); } void QueryOperator::print(std::ostream& ostream, int n) const diff --git a/src/lib/data/query/QueryTree.cpp b/src/lib/data/query/QueryTree.cpp index caafc29f..ca58fec5 100644 --- a/src/lib/data/query/QueryTree.cpp +++ b/src/lib/data/query/QueryTree.cpp @@ -45,7 +45,7 @@ void QueryTree::print(std::ostream& ostream) const if (!m_valid) { - ostream << " INVALID"; + ostream << "INVALID"; } ostream << '\n'; @@ -61,6 +61,12 @@ std::shared_ptr QueryTree::buildTree(std::deque& tokens, std::shared_ptr node = getNextNode(tokens); std::shared_ptr operatorNode = std::dynamic_pointer_cast(node); + if (!node) + { + m_valid = false; + return nullptr; + } + if (frontNode) { if (node->isComplete()) @@ -109,7 +115,7 @@ std::shared_ptr QueryTree::buildTree(std::deque& tokens, } } - if (!node->isComplete()) + if (!node || !node->isComplete()) { m_valid = false; } @@ -122,6 +128,8 @@ std::shared_ptr QueryTree::buildGroup(std::deque& tokens std::deque group; std::string name; char delimiter = QueryOperator::getOperator(closeType); + bool valid = true; + while (tokens.front() != std::string(1, delimiter) && tokens.size()) { group.push_back(tokens.front()); @@ -135,6 +143,7 @@ std::shared_ptr QueryTree::buildGroup(std::deque& tokens } else { + valid = false; m_valid = false; } @@ -143,14 +152,18 @@ std::shared_ptr QueryTree::buildGroup(std::deque& tokens return nullptr; } + std::shared_ptr groupNode; if (closeType == QueryOperator::OPERATOR_NAME) { - return std::make_shared(name); + groupNode = std::make_shared(name); + } + else + { + groupNode = buildTree(group, nullptr); + groupNode->setIsGroup(true); } - std::shared_ptr groupNode = buildTree(group, nullptr); - groupNode->setIsGroup(true); - + groupNode->setIsComplete(valid); return groupNode; } @@ -195,6 +208,7 @@ std::shared_ptr QueryTree::createCommand(std::string name) if (node->getType() == QueryCommand::COMMAND_INVALID) { m_valid = false; + return nullptr; } return node; diff --git a/src/lib/utility/utilityString.h b/src/lib/utility/utilityString.h index ace4c972..99a2819e 100644 --- a/src/lib/utility/utilityString.h +++ b/src/lib/utility/utilityString.h @@ -100,8 +100,11 @@ ContainerType utility::tokenize(const ContainerType& list, const std::string& de for (std::string str : list) { - ContainerType c2 = tokenize(str, delimiter); - c.insert(c.end(), c2.begin(), c2.end()); + if (str.size()) + { + ContainerType c2 = tokenize(str, delimiter); + c.insert(c.end(), c2.begin(), c2.end()); + } } return c; diff --git a/src/test/CMakeLists.txt b/src/test/CMakeLists.txt index 96267776..dcec1d40 100644 --- a/src/test/CMakeLists.txt +++ b/src/test/CMakeLists.txt @@ -11,6 +11,7 @@ add_files( GraphTestSuite.h LogManagerTestSuite.h MessageQueueTestSuite.h + QueryTreeTestSuite.h SettingsTestSuite.h StorageTestSuite.h TextAccessTestSuite.h diff --git a/src/test/QueryTreeTestSuite.h b/src/test/QueryTreeTestSuite.h new file mode 100644 index 00000000..97a7e9b6 --- /dev/null +++ b/src/test/QueryTreeTestSuite.h @@ -0,0 +1,351 @@ +#include "cxxtest/TestSuite.h" + +#include "data/query/QueryTree.h" + +class QueryTreeTestSuite : public CxxTest::TestSuite +{ +public: + void test_empty_query() + { + TS_ASSERT_EQUALS( + printedQueryTree(""), + + "INVALID\n" + ); + } + + void test_invalid_query() + { + TS_ASSERT_EQUALS( + printedQueryTree(" -"), + + "- INVALID\n" + ); + } + + void test_command_query() + { + TS_ASSERT_EQUALS( + printedQueryTree("class"), + + "class \n" + "class\n" + ); + } + + void test_invalid_command_query() + { + TS_ASSERT_EQUALS( + printedQueryTree("banana"), + + "banana INVALID\n" + ); + } + + void test_token_query() + { + TS_ASSERT_EQUALS( + printedQueryTree("\"A\""), + + "\" A \" \n" + "\"A\"\n" + ); + } + + void test_invalid_token_query() + { + TS_ASSERT_EQUALS( + printedQueryTree("\"A"), + + "\" A INVALID\n" + "\"A\" INVALID\n" + ); + + TS_ASSERT_EQUALS( + printedQueryTree("\"\""), + + "\" \" INVALID\n" + ); + + TS_ASSERT_EQUALS( + printedQueryTree("A\""), + + "A \" INVALID\n" + ); + } + + void test_operator_not_query() + { + TS_ASSERT_EQUALS( + printedQueryTree("!field"), + + "! field \n" + "!\n" + " field\n" + ); + + TS_ASSERT_EQUALS( + printedQueryTree("!!field"), + + "! ! field \n" + "!\n" + " !\n" + " field\n" + ); + } + + void test_invalid_operator_not_query() + { + TS_ASSERT_EQUALS( + printedQueryTree("!"), + + "! INVALID\n" + "! INVALID\n" + ); + + TS_ASSERT_EQUALS( + printedQueryTree("field!"), + + "field ! INVALID\n" + " field\n" + "! INVALID\n" + ); + } + + void test_operator_sub_query() + { + TS_ASSERT_EQUALS( + printedQueryTree("\"A\".\"B\""), + + "\" A \" . \" B \" \n" + " \"A\"\n" + ".\n" + " \"B\"\n" + ); + } + + void test_invalid_operator_sub_query() + { + TS_ASSERT_EQUALS( + printedQueryTree("\"A\"."), + + "\" A \" . INVALID\n" + " \"A\"\n" + ". INVALID\n" + ); + + TS_ASSERT_EQUALS( + printedQueryTree("."), + + ". INVALID\n" + ". INVALID\n" + ); + + TS_ASSERT_EQUALS( + printedQueryTree(".\"A\""), + + ". \" A \" INVALID\n" + ". INVALID\n" + " \"A\"\n" + ); + + TS_ASSERT_EQUALS( + printedQueryTree("\"A\"..\"B\""), + + "\" A \" . . \" B \" INVALID\n" + " \"A\"\n" + ".\n" + " . INVALID\n" + " \"B\"\n" + ); + } + + void test_operator_has_query() + { + TS_ASSERT_EQUALS( + printedQueryTree("\"A\":\"B\""), + + "\" A \" : \" B \" \n" + " \"A\"\n" + ":\n" + " \"B\"\n" + ); + } + + void test_operator_and_query() + { + TS_ASSERT_EQUALS( + printedQueryTree("\"A\"&\"B\""), + + "\" A \" & \" B \" \n" + " \"A\"\n" + "&\n" + " \"B\"\n" + ); + } + + void test_operator_or_query() + { + TS_ASSERT_EQUALS( + printedQueryTree("\"A\"|\"B\""), + + "\" A \" | \" B \" \n" + " \"A\"\n" + "|\n" + " \"B\"\n" + ); + } + + void test_operator_group_query() + { + TS_ASSERT_EQUALS( + printedQueryTree("(\"A\")"), + + "( \" A \" ) \n" + "(\"A\")\n" + ); + + TS_ASSERT_EQUALS( + printedQueryTree("(\"A\"|\"B\")"), + + "( \" A \" | \" B \" ) \n" + " \"A\"\n" + "(|)\n" + " \"B\"\n" + ); + } + + void test_invalid_operator_group_query() + { + TS_ASSERT_EQUALS( + printedQueryTree("()"), + + "( ) INVALID\n" + ); + + TS_ASSERT_EQUALS( + printedQueryTree("(\"A\""), + + "( \" A \" INVALID\n" + "(\"A\") INVALID\n" + ); + + TS_ASSERT_EQUALS( + printedQueryTree("\"A\")"), + + "\" A \" ) INVALID\n" + ); + + TS_ASSERT_EQUALS( + printedQueryTree(")("), + + ") ( INVALID\n" + ); + } + + void test_implicit_operator_sub_query() + { + TS_ASSERT_EQUALS( + printedQueryTree("\"A\"(\"B\")"), + + "\" A \" ( \" B \" ) \n" + " \"A\"\n" + ".\n" + " (\"B\")\n" + ); + } + + void test_operator_precedence_not_before_sub() + { + TS_ASSERT_EQUALS( + printedQueryTree("!method.!const"), + + "! method . ! const \n" + " !\n" + " method\n" + ".\n" + " !\n" + " const\n" + ); + } + + void test_operator_precedence_sub_before_has() + { + TS_ASSERT_EQUALS( + printedQueryTree("namespace.class:method"), + + "namespace . class : method \n" + " namespace\n" + " .\n" + " class\n" + ":\n" + " method\n" + ); + } + + void test_operator_precedence_has_before_or() + { + TS_ASSERT_EQUALS( + printedQueryTree("class:method|field"), + + "class : method | field \n" + " class\n" + " :\n" + " method\n" + "|\n" + " field\n" + ); + } + + void test_operator_precedence_respects_groups() + { + TS_ASSERT_EQUALS( + printedQueryTree("namespace.(class:method)"), + + "namespace . ( class : method ) \n" + " namespace\n" + ".\n" + " class\n" + " (:)\n" + " method\n" + ); + + TS_ASSERT_EQUALS( + printedQueryTree("class:(method|field)"), + + "class : ( method | field ) \n" + " class\n" + ":\n" + " method\n" + " (|)\n" + " field\n" + ); + } + + void test_spaces_get_stripped_out_of_query() + { + TS_ASSERT_EQUALS( + printedQueryTree(" \"Field \":(method | field) .const | public "), + + "\" Field \" : ( method | field ) . const | public \n" + " \"Field\"\n" + " :\n" + " method\n" + " (|)\n" + " field\n" + " .\n" + " const\n" + "|\n" + " public\n" + ); + } + +private: + std::string printedQueryTree(std::string query) const + { + QueryTree tree(query); + std::stringstream ss; + tree.print(ss); + return ss.str(); + } +};