aboutsummaryrefslogtreecommitdiffstatshomepage
path: root/test/unit_tests
diff options
context:
space:
mode:
authorEgor Tensin <Egor.Tensin@gmail.com>2019-12-07 03:36:21 +0300
committerEgor Tensin <Egor.Tensin@gmail.com>2019-12-07 03:36:21 +0300
commit00863566ec4601c65c435b74e575d49546a1c707 (patch)
tree479a0a6e96aba8191c7a65ea9bee2f4d5e3a4aba /test/unit_tests
parentadd stress_test.py (diff)
downloadmath-server-00863566ec4601c65c435b74e575d49546a1c707.tar.gz
math-server-00863566ec4601c65c435b74e575d49546a1c707.zip
split server into multiple components
In a vague attempt to make header files more readable, split server/ into a number of components. Also, refactor the unit tests to use the "Data-driven test cases" of Boost.Test.
Diffstat (limited to 'test/unit_tests')
-rw-r--r--test/unit_tests/CMakeLists.txt6
-rw-r--r--test/unit_tests/lexer.cpp209
-rw-r--r--test/unit_tests/parser.cpp125
3 files changed, 221 insertions, 119 deletions
diff --git a/test/unit_tests/CMakeLists.txt b/test/unit_tests/CMakeLists.txt
index d320974..ebdfbf1 100644
--- a/test/unit_tests/CMakeLists.txt
+++ b/test/unit_tests/CMakeLists.txt
@@ -1,6 +1,8 @@
find_package(Boost REQUIRED)
add_executable(unit_tests main.cpp lexer.cpp parser.cpp)
-target_include_directories(unit_tests SYSTEM PRIVATE ${Boost_INCLUDE_DIRS})
-
+target_link_libraries(unit_tests PRIVATE lexer parser)
target_include_directories(unit_tests PRIVATE ../..)
+
+target_include_directories(unit_tests SYSTEM PRIVATE ${Boost_INCLUDE_DIRS})
+target_link_libraries(unit_tests PRIVATE ${Boost_LIBRARIES})
diff --git a/test/unit_tests/lexer.cpp b/test/unit_tests/lexer.cpp
index 7b513e8..fdc93e1 100644
--- a/test/unit_tests/lexer.cpp
+++ b/test/unit_tests/lexer.cpp
@@ -1,12 +1,27 @@
-#include <server/lexer.hpp>
+#include <server/lexer/error.hpp>
+#include <server/lexer/lexer.hpp>
+#include <server/lexer/token.hpp>
+#include <server/lexer/token_type.hpp>
+#include <boost/test/data/test_case.hpp>
+#include <boost/test/data/monomorphic.hpp>
#include <boost/test/unit_test.hpp>
+#include <ostream>
+#include <string>
+#include <string_view>
#include <vector>
-BOOST_AUTO_TEST_CASE(test_lexer_parse_number) {
- using namespace math::server::lexer;
+BOOST_AUTO_TEST_SUITE(lexer_tests)
+namespace bdata = boost::unit_test::data;
+using math::server::Lexer;
+using math::server::LexerError;
+using math::server::lexer::Token;
+using math::server::lexer::token::Type;
+namespace details = math::server::lexer::details;
+
+BOOST_AUTO_TEST_CASE(test_parse_number) {
// These are valid numbers:
BOOST_TEST(details::parse_number("0").value() == 0);
BOOST_TEST(details::parse_number("1.").value() == 1.);
@@ -25,85 +40,129 @@ BOOST_AUTO_TEST_CASE(test_lexer_parse_number) {
BOOST_TEST(!details::parse_number("e12").has_value());
// This is definitely a number, but a malformed one (an exponent must be
// followed by some digits).
- BOOST_CHECK_THROW(details::parse_number("12e"), Error);
+ BOOST_CHECK_THROW(details::parse_number("12e"), LexerError);
}
-BOOST_AUTO_TEST_CASE(test_lexer_parse_const_token) {
- using namespace math::server::lexer;
-
- // TODO: No time to implement the required string conversions, hence the
- // extra parentheses.
- BOOST_TEST((details::parse_const_token("+").value() == Token::Type::PLUS));
+BOOST_AUTO_TEST_CASE(test_parse_const_token) {
+ BOOST_TEST(details::parse_const_token("+").value() == Type::PLUS);
// parse_* functions only consume a single token:
- BOOST_TEST((details::parse_const_token("+++").value() == Token::Type::PLUS));
- BOOST_TEST((details::parse_const_token("-").value() == Token::Type::MINUS));
+ BOOST_TEST(details::parse_const_token("+/*").value() == Type::PLUS);
+ BOOST_TEST(details::parse_const_token("-").value() == Type::MINUS);
BOOST_TEST(!details::parse_const_token("&+").has_value());
}
-BOOST_AUTO_TEST_CASE(test_lexer_get_tokens) {
- using namespace math::server;
- using namespace math::server::lexer;
+namespace {
+namespace get_tokens::valid {
- // TODO: No time to implement the required string conversions, hence the
- // extra parentheses.
- {
- Lexer lexer{""};
- BOOST_TEST((lexer.get_tokens() == std::vector<Token>{}));
- }
- {
- Lexer lexer{" + - "};
- BOOST_TEST((lexer.get_tokens() == std::vector<Token>{
- Token{Token::Type::PLUS},
- Token{Token::Type::MINUS},
- }));
- }
- {
- Lexer lexer{"&"};
- BOOST_CHECK_THROW((lexer.get_tokens()), lexer::Error);
- }
- {
- Lexer lexer{" 1 + 123 & 456"};
- BOOST_CHECK_THROW((lexer.get_tokens()), lexer::Error);
- }
- {
- Lexer lexer{"1+2"};
- BOOST_TEST((lexer.get_tokens() == std::vector<Token>{
- Token{1},
- Token{Token::Type::PLUS},
- Token{2},
- }));
- }
- {
- Lexer lexer{"1+2 * (3- 4e-2)"};
- BOOST_TEST((lexer.get_tokens() == std::vector<Token>{
- Token{1},
- Token{Token::Type::PLUS},
- Token{2},
- Token{Token::Type::ASTERISK},
- Token{Token::Type::LEFT_PAREN},
- Token{3},
- Token{Token::Type::MINUS},
- Token{4e-2},
- Token{Token::Type::RIGHT_PAREN},
- }));
+const std::vector<std::string_view> input{
+ "",
+ " + - ",
+ "1+2",
+ "1+2 * (3- 4e-2)",
+ " 2 * (1 + 3 * (1 - -3)) ",
+};
+
+// Some black magic-fuckery to resolve operator<< for std::vector<Token>.
+// See https://stackoverflow.com/a/18817428/514684.
+
+struct Expected {
+ std::vector<Token> m_tokens;
+};
+
+std::ostream& operator<<(std::ostream& os, const Expected& expected) {
+ for (const auto& token : expected.m_tokens) {
+ os << token;
}
- {
- Lexer lexer{" 2 * (1 + 3 * (1 - -3)) "};
- BOOST_TEST((lexer.get_tokens() == std::vector<Token>{
- Token{2},
- Token{Token::Type::ASTERISK},
- Token{Token::Type::LEFT_PAREN},
- Token{1},
- Token{Token::Type::PLUS},
- Token{3},
- Token{Token::Type::ASTERISK},
- Token{Token::Type::LEFT_PAREN},
- Token{1},
- Token{Token::Type::MINUS},
- Token{Token::Type::MINUS},
- Token{3},
- Token{Token::Type::RIGHT_PAREN},
- Token{Token::Type::RIGHT_PAREN},
- }));
+ return os;
+}
+
+const std::vector<Expected> expected{
+ {{}},
+ {{
+ Token{Type::PLUS},
+ Token{Type::MINUS},
+ }},
+ {{
+ Token{1},
+ Token{Type::PLUS},
+ Token{2},
+ }},
+ {{
+ Token{1},
+ Token{Type::PLUS},
+ Token{2},
+ Token{Type::ASTERISK},
+ Token{Type::LEFT_PAREN},
+ Token{3},
+ Token{Type::MINUS},
+ Token{4e-2},
+ Token{Type::RIGHT_PAREN},
+ }},
+ {{
+ Token{2},
+ Token{Type::ASTERISK},
+ Token{Type::LEFT_PAREN},
+ Token{1},
+ Token{Type::PLUS},
+ Token{3},
+ Token{Type::ASTERISK},
+ Token{Type::LEFT_PAREN},
+ Token{1},
+ Token{Type::MINUS},
+ Token{Type::MINUS},
+ Token{3},
+ Token{Type::RIGHT_PAREN},
+ Token{Type::RIGHT_PAREN},
+ }},
+};
+
+}
+
+namespace get_tokens::invalid {
+
+const std::vector<std::string_view> input{
+ "&",
+ " 1 + 123 & 456",
+};
+
+const std::vector<std::string> error_msg{
+ "server error: lexer error: invalid input at: &",
+ "server error: lexer error: invalid input at: & 456",
+};
+
+}
+}
+
+BOOST_DATA_TEST_CASE(
+ test_get_tokens_valid,
+ bdata::make(get_tokens::valid::input) ^ get_tokens::valid::expected,
+ input,
+ expected) {
+
+ Lexer lexer{input};
+ const auto actual = lexer.get_tokens();
+ BOOST_CHECK_EQUAL_COLLECTIONS(actual.cbegin(), actual.cend(),
+ expected.m_tokens.cbegin(),
+ expected.m_tokens.cend());
+}
+
+BOOST_DATA_TEST_CASE(
+ test_get_tokens_invalid,
+ bdata::make(get_tokens::invalid::input) ^ get_tokens::invalid::error_msg,
+ input,
+ error_msg) {
+
+ BOOST_REQUIRE_THROW(do {
+ Lexer lexer{input};
+ lexer.get_tokens();
+ } while (0), LexerError);
+
+ try {
+ Lexer lexer{input};
+ lexer.get_tokens();
+ } catch (const LexerError& e) {
+ BOOST_TEST(error_msg == e.what());
}
}
+
+BOOST_AUTO_TEST_SUITE_END()
diff --git a/test/unit_tests/parser.cpp b/test/unit_tests/parser.cpp
index 11f48d3..bf31223 100644
--- a/test/unit_tests/parser.cpp
+++ b/test/unit_tests/parser.cpp
@@ -1,48 +1,89 @@
-#include <server/parser.hpp>
+#include <server/parser/error.hpp>
+#include <server/parser/parser.hpp>
+#include <boost/test/data/test_case.hpp>
+#include <boost/test/data/monomorphic.hpp>
#include <boost/test/unit_test.hpp>
-BOOST_AUTO_TEST_CASE(test_parser_exec) {
- using namespace math::server;
+#include <string>
+#include <string_view>
+#include <vector>
- {
- Parser parser{""};
- BOOST_CHECK_THROW(parser.exec(), parser::Error);
- }
- {
- Parser parser{"1"};
- BOOST_TEST(parser.exec() == 1);
- }
- {
- Parser parser{" 1 + "};
- BOOST_CHECK_THROW(parser.exec(), parser::Error);
- }
- {
- Parser parser{" 1 + 2 "};
- BOOST_TEST(parser.exec() == 3);
- }
- {
- Parser parser{" 2 * 1 + 3 "};
- BOOST_TEST(parser.exec() == 5);
- }
- {
- Parser parser{" 2 * (1 + 3) "};
- BOOST_TEST(parser.exec() == 8);
- }
- {
- Parser parser{" 2 * (1 + 3 "};
- BOOST_CHECK_THROW(parser.exec(), parser::Error);
- }
- {
- Parser parser{" 2 * (1 + 3) )"};
- BOOST_CHECK_THROW(parser.exec(), parser::Error);
- }
- {
- Parser parser{" 2 * (1 + 3 * (1 - -3)) "};
- BOOST_TEST(parser.exec() == 26);
- }
- {
- Parser parser{" -2 * ---- (3 + -100e-1) "}; // Looks weird, but also works in e.g. Python
- BOOST_TEST(parser.exec() == 14);
+BOOST_AUTO_TEST_SUITE(parser_tests)
+
+namespace bdata = boost::unit_test::data;
+using math::server::Parser;
+using math::server::ParserError;
+
+namespace {
+namespace exec::valid {
+
+const std::vector<std::string_view> input{
+ "1",
+ " 1 + 2 ",
+ " 2 * 1 + 3 ",
+ " 2 * (1 + 3) ",
+ " 2 * (1 + 3 * (1 - -3)) ",
+ " -2 * ---- (3 + -100e-1) ", // Looks weird, but also works in e.g. Python
+};
+
+const std::vector<double> expected{
+ 1,
+ 3,
+ 5,
+ 8,
+ 26,
+ 14,
+};
+
+}
+
+namespace exec::invalid {
+
+const std::vector<std::string_view> input{
+ "",
+ " 1 + ",
+ " 2 * (1 + 3 ",
+ " 2 * (1 + 3) )",
+};
+
+const std::vector<std::string> error_msg{
+ "server error: parser error: expected '-', '(' or a number",
+ "server error: parser error: expected '-', '(' or a number",
+ "server error: parser error: missing closing ')'",
+ "server error: parser error: expected a binary operator",
+};
+
+}
+}
+
+BOOST_DATA_TEST_CASE(
+ test_exec_valid,
+ bdata::make(exec::valid::input) ^ exec::valid::expected,
+ input,
+ expected) {
+
+ Parser parser{input};
+ BOOST_TEST(parser.exec() == expected);
+}
+
+BOOST_DATA_TEST_CASE(
+ test_exec_invalid,
+ bdata::make(exec::invalid::input) ^ exec::invalid::error_msg,
+ input,
+ error_msg) {
+
+ BOOST_REQUIRE_THROW(do {
+ Parser parser{input};
+ parser.exec();
+ } while (0), ParserError);
+
+ try {
+ Parser parser{input};
+ parser.exec();
+ } catch (const ParserError& e) {
+ BOOST_TEST(error_msg == e.what());
}
}
+
+BOOST_AUTO_TEST_SUITE_END()