aboutsummaryrefslogtreecommitdiff
path: root/tests/src/v2script
diff options
context:
space:
mode:
author Spartan322 <Megacake1234@gmail.com>2024-06-15 15:40:31 +0200
committer Spartan322 <Megacake1234@gmail.com>2024-06-22 13:57:49 +0200
commit1a694a8b26a441b12547057d6e0be61a111cced3 (patch)
tree51ca6d5948e92be37b9ee6674cb96801d2cd03f8 /tests/src/v2script
parent8b623bf4087aa360842ad31145d4ab6946cee9aa (diff)
Add unit testsadd/unit-testing
Make github action tests run explicit Fix dropping annotation list for Errors Fix potential empty get_errors crashes Fix incorrect csv error behavior Add use_sep for `LineObject` and `std::vector<LineObject>` Remove constexpr of load_from_buffer and load_from_string for parsers Add snitch-org/snitch@d6632123cc8d13bdbc5cd60fd6741b9e0f635e82 Make versioned submodules ignore dirty Add tests/bin/* to gitignore
Diffstat (limited to 'tests/src/v2script')
-rw-r--r--tests/src/v2script/AbstractSyntaxTree.cpp172
-rw-r--r--tests/src/v2script/Parser.cpp437
2 files changed, 609 insertions, 0 deletions
diff --git a/tests/src/v2script/AbstractSyntaxTree.cpp b/tests/src/v2script/AbstractSyntaxTree.cpp
new file mode 100644
index 0000000..c06da08
--- /dev/null
+++ b/tests/src/v2script/AbstractSyntaxTree.cpp
@@ -0,0 +1,172 @@
+#include <string_view>
+#include <type_traits>
+
+#include <openvic-dataloader/NodeLocation.hpp>
+#include <openvic-dataloader/detail/SymbolIntern.hpp>
+#include <openvic-dataloader/v2script/AbstractSyntaxTree.hpp>
+
+#include <dryad/node_map.hpp>
+#include <dryad/tree.hpp>
+
+#include "Helper.hpp"
+#include <range/v3/iterator/operations.hpp>
+#include <range/v3/view/enumerate.hpp>
+#include <range/v3/view/zip.hpp>
+#include <snitch/snitch.hpp>
+
+using namespace ovdl;
+using namespace ovdl::v2script::ast;
+using namespace std::string_view_literals;
+
+struct Ast : SymbolIntern {
+ dryad::tree<FileTree> tree;
+ dryad::node_map<const Node, NodeLocation> map;
+ symbol_interner_type symbol_interner;
+
+ template<typename T, typename... Args>
+ T* create(Args&&... args) {
+ auto node = tree.template create<T>(DRYAD_FWD(args)...);
+ return node;
+ }
+
+ template<typename T, typename... Args>
+ T* create_with_intern(std::string_view view, Args&&... args) {
+ auto intern = symbol_interner.intern(view.data(), view.size());
+ auto node = tree.template create<T>(intern, DRYAD_FWD(args)...);
+ return node;
+ }
+
+ template<typename T, typename... Args>
+ T* create_with_loc(NodeLocation loc, Args&&... args) {
+ auto node = tree.template create<T>(DRYAD_FWD(args)...);
+ set_location(node, loc);
+ return node;
+ }
+
+ template<typename T, typename... Args>
+ T* create_with_loc_and_intern(NodeLocation loc, std::string_view view, Args&&... args) {
+ auto intern = symbol_interner.intern(view.data(), view.size());
+ auto node = tree.template create<T>(intern, DRYAD_FWD(args)...);
+ set_location(node, loc);
+ return node;
+ }
+
+ void set_location(const Node* n, NodeLocation loc) {
+ map.insert(n, loc);
+ }
+
+ NodeLocation location_of(const Node* n) const {
+ auto result = map.lookup(n);
+ if (result == nullptr)
+ return {};
+ return *result;
+ }
+};
+
+TEST_CASE("V2Script Nodes", "[v2script-nodes]") {
+ Ast ast;
+
+ auto* id = ast.create_with_intern<IdentifierValue>("id");
+ CHECK_IF(id) {
+ CHECK(id->kind() == NodeKind::IdentifierValue);
+ CHECK(id->value(ast.symbol_interner) == "id"sv);
+ }
+
+ auto* str = ast.create_with_intern<StringValue>("str");
+ CHECK_IF(str) {
+ CHECK(str->kind() == NodeKind::StringValue);
+ CHECK(str->value(ast.symbol_interner) == "str"sv);
+ }
+
+ auto* list = ast.create<ListValue>();
+ CHECK_IF(list) {
+ CHECK(list->kind() == NodeKind::ListValue);
+ }
+
+ auto* null = ast.create<NullValue>();
+ CHECK_IF(null) {
+ CHECK(null->kind() == NodeKind::NullValue);
+ }
+
+ auto* event = ast.create<EventStatement>(false, list);
+ CHECK_IF(event) {
+ CHECK(event->kind() == NodeKind::EventStatement);
+ CHECK(!event->is_province_event());
+ CHECK(event->right() == list);
+ }
+
+ auto* assign = ast.create<AssignStatement>(id, str);
+ CHECK_IF(assign) {
+ CHECK(assign->kind() == NodeKind::AssignStatement);
+ CHECK(assign->left() == id);
+ CHECK(assign->right() == str);
+ }
+
+ auto* value_statement = ast.create<ValueStatement>(null);
+ CHECK_IF(value_statement) {
+ CHECK(value_statement->kind() == NodeKind::ValueStatement);
+ CHECK(value_statement->value() == null);
+ }
+
+ if (!assign || !value_statement || !event || !null || !list || !str || !id) {
+ return;
+ }
+
+ StatementList statement_list {};
+ statement_list.push_back(assign);
+ statement_list.push_back(value_statement);
+ statement_list.push_back(event);
+ CHECK_FALSE(statement_list.empty());
+ CHECK(ranges::distance(statement_list) == 3);
+
+ for (const auto [statement_list_index, statement] : statement_list | ranges::views::enumerate) {
+ CAPTURE(statement_list_index);
+ CHECK_OR_CONTINUE(statement);
+ switch (statement_list_index) {
+ case 0: CHECK_OR_CONTINUE(statement == assign); break;
+ case 1: CHECK_OR_CONTINUE(statement == value_statement); break;
+ case 2: CHECK_OR_CONTINUE(statement == event); break;
+ default: CHECK_OR_CONTINUE(false); break;
+ }
+ }
+
+ auto* file_tree = ast.create<FileTree>(statement_list);
+ CHECK_IF(file_tree) {
+ CHECK(file_tree->kind() == NodeKind::FileTree);
+
+ const auto statements = file_tree->statements();
+ CHECK_FALSE(statements.empty());
+ CHECK(ranges::distance(statements) == 3);
+
+ for (const auto [statement, list_statement] : ranges::views::zip(statements, statement_list)) {
+ CHECK_OR_CONTINUE(list_statement);
+ CHECK_OR_CONTINUE(statement);
+ CHECK_OR_CONTINUE(statement == list_statement);
+ }
+ }
+
+ ast.tree.set_root(file_tree);
+ CHECK(ast.tree.has_root());
+ CHECK(ast.tree.root() == file_tree);
+
+ ast.tree.clear();
+ CHECK_FALSE(ast.tree.has_root());
+ CHECK(ast.tree.root() != file_tree);
+}
+
+TEST_CASE("V2Script Nodes Location", "[v2script-nodes-location]") {
+ Ast ast;
+
+ constexpr auto fake_buffer = "id"sv;
+
+ auto* id = ast.create_with_loc_and_intern<IdentifierValue>(NodeLocation::make_from(&fake_buffer[0], &fake_buffer[1]), "id");
+
+ CHECK_IF(id) {
+ CHECK(id->value(ast.symbol_interner) == "id"sv);
+
+ auto location = ast.location_of(id);
+ CHECK_FALSE(location.is_synthesized());
+ CHECK(location.begin() == &fake_buffer[0]);
+ CHECK(location.end() - 1 == &fake_buffer[1]);
+ }
+} \ No newline at end of file
diff --git a/tests/src/v2script/Parser.cpp b/tests/src/v2script/Parser.cpp
new file mode 100644
index 0000000..5ddc49d
--- /dev/null
+++ b/tests/src/v2script/Parser.cpp
@@ -0,0 +1,437 @@
+#include <filesystem>
+#include <fstream>
+#include <string_view>
+
+#include <openvic-dataloader/v2script/AbstractSyntaxTree.hpp>
+#include <openvic-dataloader/v2script/Parser.hpp>
+
+#include <dryad/node.hpp>
+
+#include "Helper.hpp"
+#include <detail/NullBuff.hpp>
+#include <range/v3/iterator/operations.hpp>
+#include <range/v3/view/enumerate.hpp>
+#include <snitch/snitch.hpp>
+
+using namespace ovdl;
+using namespace v2script;
+using namespace std::string_view_literals;
+
+static constexpr auto simple_buffer = "a = b"sv;
+static constexpr auto simple_path = "file.txt"sv;
+
+static void SetupFile(std::string_view path) {
+ std::ofstream stream(path.data());
+ stream << simple_buffer << std::flush;
+}
+
+#define CHECK_PARSE(...) \
+ CHECK_OR_RETURN(parser.get_errors().empty()); \
+ CHECK_OR_RETURN(parser.simple_parse(__VA_ARGS__)); \
+ CHECK_OR_RETURN(parser.get_errors().empty())
+
+TEST_CASE("V2Script Memory Buffer (data, size) Simple Parse", "[v2script-memory-simple-parse][buffer][data-size]") {
+ Parser parser(ovdl::detail::cnull);
+
+ parser.load_from_buffer(simple_buffer.data(), simple_buffer.size());
+
+ CHECK_PARSE();
+}
+
+TEST_CASE("V2Script Memory Buffer (begin, end) Simple Parse", "[v2script-memory-simple-parse][buffer][begin-end]") {
+ Parser parser(ovdl::detail::cnull);
+
+ parser.load_from_buffer(simple_buffer.data(), simple_buffer.data() + simple_buffer.size());
+
+ CHECK_PARSE();
+}
+
+TEST_CASE("V2Script Memory Buffer String Simple Parse", "[v2script-memory-simple-parse][buffer][string]") {
+ Parser parser(ovdl::detail::cnull);
+
+ parser.load_from_string(simple_buffer);
+
+ CHECK_PARSE();
+}
+
+TEST_CASE("V2Script File (const char*) Simple Parse", "[v2script-file-simple-parse][char-ptr]") {
+ SetupFile(simple_path);
+
+ Parser parser(ovdl::detail::cnull);
+
+ parser.load_from_file(simple_path.data());
+
+ std::filesystem::remove(simple_path);
+
+ CHECK_PARSE();
+}
+
+TEST_CASE("V2Script File (filesystem::path) Simple Parse", "[v2script-file-simple-parse][filesystem-path]") {
+ SetupFile(simple_path);
+
+ Parser parser(ovdl::detail::cnull);
+
+ parser.load_from_file(std::filesystem::path(simple_path));
+
+ std::filesystem::remove(simple_path);
+
+ CHECK_PARSE();
+}
+
+TEST_CASE("V2Script File (HasCstr) Simple Parse", "[v2script-file-simple-parse][has-cstr]") {
+ SetupFile(simple_path);
+
+ Parser parser(ovdl::detail::cnull);
+
+ parser.load_from_file(std::string { simple_path });
+
+ std::filesystem::remove(simple_path);
+
+ CHECK_PARSE();
+}
+
+TEST_CASE("V2Script Identifier Simple Parse", "[v2script-id-simple-parse]") {
+ Parser parser(ovdl::detail::cnull);
+
+ SECTION("a = b") {
+ static constexpr auto buffer = "a = b"sv;
+ parser.load_from_string(buffer);
+
+ CHECK_PARSE();
+
+ const ast::FileTree* file_tree = parser.get_file_node();
+ CHECK(file_tree);
+
+ const auto statements = file_tree->statements();
+ CHECK_FALSE(statements.empty());
+ CHECK(ranges::distance(statements) == 1);
+
+ const ast::Statement* statement = statements.front();
+ CHECK(statement);
+
+ const auto* assign = dryad::node_try_cast<ast::AssignStatement>(statement);
+ CHECK(assign);
+ CHECK(assign->left());
+ CHECK(assign->right());
+
+ const auto* left = dryad::node_try_cast<ast::IdentifierValue>(assign->left());
+ CHECK_IF(left) {
+ CHECK(parser.value(left) == "a"sv);
+ }
+
+ const auto* right = dryad::node_try_cast<ast::IdentifierValue>(assign->right());
+ CHECK_IF(right) {
+ CHECK(parser.value(right) == "b"sv);
+ }
+ }
+
+ SECTION("a b c d") {
+ static constexpr auto buffer = "a b c d"sv;
+ parser.load_from_string(buffer);
+
+ CHECK_PARSE();
+
+ const ast::FileTree* file_tree = parser.get_file_node();
+ CHECK(file_tree);
+
+ const auto statements = file_tree->statements();
+ CHECK_FALSE(statements.empty());
+ CHECK(ranges::distance(statements) == 4);
+
+ for (const auto [statement_index, statement] : statements | ranges::views::enumerate) {
+ CHECK_OR_CONTINUE(statement);
+
+ const auto* value_statement = dryad::node_try_cast<ast::ValueStatement>(statement);
+ CHECK_OR_CONTINUE(value_statement);
+
+ const auto* value = dryad::node_try_cast<ast::IdentifierValue>(value_statement->value());
+ CHECK_OR_CONTINUE(value);
+ switch (statement_index) {
+ case 0: CHECK_OR_CONTINUE(parser.value(value) == "a"sv); break;
+ case 1: CHECK_OR_CONTINUE(parser.value(value) == "b"sv); break;
+ case 2: CHECK_OR_CONTINUE(parser.value(value) == "c"sv); break;
+ case 3: CHECK_OR_CONTINUE(parser.value(value) == "d"sv); break;
+ default: CHECK_OR_CONTINUE(false); break;
+ }
+ }
+ }
+
+ SECTION("a = { a = b }") {
+ static constexpr auto buffer = "a = { a = b }"sv;
+ parser.load_from_string(buffer);
+
+ CHECK_PARSE();
+
+ const ast::FileTree* file_tree = parser.get_file_node();
+ CHECK(file_tree);
+
+ const auto statements = file_tree->statements();
+ CHECK_FALSE(statements.empty());
+ CHECK(ranges::distance(statements) == 1);
+
+ const ast::Statement* statement = statements.front();
+ CHECK(statement);
+
+ const auto* assign = dryad::node_try_cast<ast::AssignStatement>(statement);
+ CHECK(assign);
+ CHECK(assign->left());
+ CHECK(assign->right());
+
+ const auto* left = dryad::node_try_cast<ast::IdentifierValue>(assign->left());
+ CHECK_IF(left) {
+ CHECK(parser.value(left) == "a"sv);
+ }
+
+ const auto* right = dryad::node_try_cast<ast::ListValue>(assign->right());
+ CHECK_IF(right) {
+ const auto inner_statements = right->statements();
+ CHECK_FALSE(inner_statements.empty());
+ CHECK(ranges::distance(inner_statements) == 1);
+
+ const ast::Statement* inner_statement = inner_statements.front();
+ CHECK(inner_statement);
+
+ const auto* inner_assign = dryad::node_try_cast<ast::AssignStatement>(inner_statement);
+ CHECK(inner_assign);
+ CHECK(inner_assign->left());
+ CHECK(inner_assign->right());
+
+ const auto* inner_left = dryad::node_try_cast<ast::IdentifierValue>(inner_assign->left());
+ CHECK_IF(inner_left) {
+ CHECK(parser.value(inner_left) == "a"sv);
+ }
+
+ const auto* inner_right = dryad::node_try_cast<ast::IdentifierValue>(inner_assign->right());
+ CHECK_IF(inner_right) {
+ CHECK(parser.value(inner_right) == "b"sv);
+ }
+ }
+ }
+
+ SECTION("a = { { a } }") {
+ static constexpr auto buffer = "a = { { a } }"sv;
+ parser.load_from_string(buffer);
+
+ CHECK_PARSE();
+
+ const ast::FileTree* file_tree = parser.get_file_node();
+ CHECK(file_tree);
+
+ const auto statements = file_tree->statements();
+ CHECK_FALSE(statements.empty());
+ CHECK(ranges::distance(statements) == 1);
+
+ const ast::Statement* statement = statements.front();
+ CHECK(statement);
+
+ const auto* assign = dryad::node_try_cast<ast::AssignStatement>(statement);
+ CHECK(assign);
+ CHECK(assign->left());
+ CHECK(assign->right());
+
+ const auto* left = dryad::node_try_cast<ast::IdentifierValue>(assign->left());
+ CHECK_IF(left) {
+ CHECK(parser.value(left) == "a"sv);
+ }
+
+ const auto* right = dryad::node_try_cast<ast::ListValue>(assign->right());
+ CHECK_IF(right) {
+ const auto inner_statements = right->statements();
+ CHECK_FALSE(inner_statements.empty());
+ CHECK(ranges::distance(inner_statements) == 1);
+
+ const ast::Statement* inner_statement = inner_statements.front();
+ CHECK(inner_statement);
+
+ const auto* value_statement = dryad::node_try_cast<ast::ValueStatement>(inner_statement);
+ CHECK(value_statement);
+
+ const auto* list_value = dryad::node_try_cast<ast::ListValue>(value_statement->value());
+ CHECK(list_value);
+
+ const auto list_statements = list_value->statements();
+ CHECK_FALSE(list_statements.empty());
+ CHECK(ranges::distance(list_statements) == 1);
+
+ const auto* inner_value_statement = dryad::node_try_cast<ast::ValueStatement>(list_statements.front());
+ CHECK(inner_value_statement);
+
+ const auto* id_value = dryad::node_try_cast<ast::IdentifierValue>(inner_value_statement->value());
+ CHECK(id_value);
+ CHECK(parser.value(id_value) == "a"sv);
+ }
+ }
+}
+
+TEST_CASE("V2Script String Simple Parse", "[v2script-id-simple-parse]") {
+ Parser parser(ovdl::detail::cnull);
+
+ SECTION("a = \"b\"") {
+ static constexpr auto buffer = "a = \"b\""sv;
+ parser.load_from_string(buffer);
+
+ CHECK_PARSE();
+
+ const ast::FileTree* file_tree = parser.get_file_node();
+ CHECK(file_tree);
+
+ const auto statements = file_tree->statements();
+ CHECK_FALSE(statements.empty());
+ CHECK(ranges::distance(statements) == 1);
+
+ const ast::Statement* statement = statements.front();
+ CHECK(statement);
+
+ const auto* assign = dryad::node_try_cast<ast::AssignStatement>(statement);
+ CHECK(assign);
+ CHECK(assign->left());
+ CHECK(assign->right());
+
+ const auto* left = dryad::node_try_cast<ast::IdentifierValue>(assign->left());
+ CHECK_IF(left) {
+ CHECK(parser.value(left) == "a"sv);
+ }
+
+ const auto* right = dryad::node_try_cast<ast::StringValue>(assign->right());
+ CHECK_IF(right) {
+ CHECK(parser.value(right) == "b"sv);
+ }
+ }
+
+ SECTION("\"a\" \"b\" \"c\" \"d\"") {
+ static constexpr auto buffer = "\"a\" \"b\" \"c\" \"d\""sv;
+ parser.load_from_string(buffer);
+
+ CHECK_PARSE();
+
+ const ast::FileTree* file_tree = parser.get_file_node();
+ CHECK(file_tree);
+
+ const auto statements = file_tree->statements();
+ CHECK_FALSE(statements.empty());
+ CHECK(ranges::distance(statements) == 4);
+
+ for (const auto [statement_index, statement] : statements | ranges::views::enumerate) {
+ CHECK_OR_CONTINUE(statement);
+
+ const auto* value_statement = dryad::node_try_cast<ast::ValueStatement>(statement);
+ CHECK_OR_CONTINUE(value_statement);
+
+ const auto* value = dryad::node_try_cast<ast::StringValue>(value_statement->value());
+ CHECK_OR_CONTINUE(value);
+ switch (statement_index) {
+ case 0: CHECK_OR_CONTINUE(parser.value(value) == "a"sv); break;
+ case 1: CHECK_OR_CONTINUE(parser.value(value) == "b"sv); break;
+ case 2: CHECK_OR_CONTINUE(parser.value(value) == "c"sv); break;
+ case 3: CHECK_OR_CONTINUE(parser.value(value) == "d"sv); break;
+ default: CHECK_OR_CONTINUE(false); break;
+ }
+ }
+ }
+
+ SECTION("a = { a = \"b\" }") {
+ static constexpr auto buffer = "a = { a = \"b\" }"sv;
+ parser.load_from_string(buffer);
+
+ CHECK_PARSE();
+
+ const ast::FileTree* file_tree = parser.get_file_node();
+ CHECK(file_tree);
+
+ const auto statements = file_tree->statements();
+ CHECK_FALSE(statements.empty());
+ CHECK(ranges::distance(statements) == 1);
+
+ const ast::Statement* statement = file_tree->statements().front();
+ CHECK(statement);
+
+ const auto* assign = dryad::node_try_cast<ast::AssignStatement>(statement);
+ CHECK(assign);
+ CHECK(assign->left());
+ CHECK(assign->right());
+
+ const auto* left = dryad::node_try_cast<ast::IdentifierValue>(assign->left());
+ CHECK_IF(left) {
+ CHECK(parser.value(left) == "a"sv);
+ }
+
+ const auto* right = dryad::node_try_cast<ast::ListValue>(assign->right());
+ CHECK_IF(right) {
+ const auto inner_statements = right->statements();
+ CHECK_FALSE(inner_statements.empty());
+ CHECK(ranges::distance(inner_statements) == 1);
+
+ const ast::Statement* inner_statement = inner_statements.front();
+ CHECK(inner_statement);
+
+ const auto* inner_assign = dryad::node_try_cast<ast::AssignStatement>(inner_statement);
+ CHECK(inner_assign);
+ CHECK(inner_assign->left());
+ CHECK(inner_assign->right());
+
+ const auto* inner_left = dryad::node_try_cast<ast::IdentifierValue>(inner_assign->left());
+ CHECK_IF(inner_left) {
+ CHECK(parser.value(inner_left) == "a"sv);
+ }
+
+ const auto* inner_right = dryad::node_try_cast<ast::StringValue>(inner_assign->right());
+ CHECK_IF(inner_right) {
+ CHECK(parser.value(inner_right) == "b"sv);
+ }
+ }
+ }
+
+ SECTION("a = { { \"a\" } }") {
+ static constexpr auto buffer = "a = { { \"a\" } }"sv;
+ parser.load_from_string(buffer);
+
+ CHECK_PARSE();
+
+ const ast::FileTree* file_tree = parser.get_file_node();
+ CHECK(file_tree);
+
+ const auto statements = file_tree->statements();
+ CHECK_FALSE(statements.empty());
+ CHECK(ranges::distance(statements) == 1);
+
+ const ast::Statement* statement = statements.front();
+ CHECK(statement);
+
+ const auto* assign = dryad::node_try_cast<ast::AssignStatement>(statement);
+ CHECK(assign);
+ CHECK(assign->left());
+ CHECK(assign->right());
+
+ const auto* left = dryad::node_try_cast<ast::IdentifierValue>(assign->left());
+ CHECK_IF(left) {
+ CHECK(parser.value(left) == "a"sv);
+ }
+
+ const auto* right = dryad::node_try_cast<ast::ListValue>(assign->right());
+ CHECK_IF(right) {
+ const auto inner_statements = right->statements();
+ CHECK_FALSE(inner_statements.empty());
+ CHECK(ranges::distance(inner_statements) == 1);
+
+ const ast::Statement* inner_statement = inner_statements.front();
+ CHECK(inner_statement);
+
+ const auto* value_statement = dryad::node_try_cast<ast::ValueStatement>(inner_statement);
+ CHECK(value_statement);
+
+ const auto* list_value = dryad::node_try_cast<ast::ListValue>(value_statement->value());
+ CHECK(list_value);
+
+ const auto list_statements = list_value->statements();
+ CHECK_FALSE(list_statements.empty());
+ CHECK(ranges::distance(list_statements) == 1);
+
+ const auto* inner_value_statement = dryad::node_try_cast<ast::ValueStatement>(list_statements.front());
+ CHECK_OR_RETURN(inner_value_statement);
+
+ const auto* str_value = dryad::node_try_cast<ast::StringValue>(inner_value_statement->value());
+ CHECK_OR_RETURN(str_value);
+ CHECK(parser.value(str_value) == "a"sv);
+ }
+ }
+} \ No newline at end of file