diff --git a/tests/parse/test_file.py b/tests/parse/test_file.py new file mode 100644 index 0000000..c7ce368 --- /dev/null +++ b/tests/parse/test_file.py @@ -0,0 +1,116 @@ +# SPDX-License-Identifier: LGPL-2.1-only +# Copyright 2022 Jookia + +# File syntax consists of the following: +# - One or more directives +# +# Parsing gives the following: +# A list of directives - All directives in the file +# +# The following error contexts are used: +# PARSE_FILE - Used when parsing the file +# +# No parse errors are generated. +# +# The following parsers are used and have their errors +# and data structures propagated: +# parse_directive - Used to parse a directive + +import enum + +from hypothesis import assume, given +from hypothesis.strategies import composite, data, integers, just, lists + +from src.token import TokenStream +from src.parse import ( + ParseContext, + ParseError, + ParseErrorException, + ParseTask, + Parser, + read_token, +) +from tests.parse.templates import ( + template_test_invalid, +) +from tests.test_token import ( + draw_token_random, + static_token_by_value, +) +from tests.parse.test_parse import static_parse_context + +# +# Helper functions +# + + +# Values used by the mocked parser +class MockDirective(enum.Enum): + MockDirective = enum.auto() + + +# Mocks and tests the parse_directive parser +# Instead of parsing directives, return a mock value +# it instead returns a mock value +class MockParser(Parser): + def parse_directive(self, stream, parent_context): + read_token(stream, "MockDirective", parent_context) + return MockDirective.MockDirective + + +# A valid directive +def static_directive(): + return ([static_token_by_value("MockDirective")], MockDirective.MockDirective) + + +# A valid file +@composite +def draw_file_valid(draw): + directives = draw(lists(just(static_directive()))) + all_tokens = [] + all_expected = [] + for (tokens, expected) in directives: + all_tokens += tokens + all_expected.append(expected) + return (all_tokens, all_expected) + + +# +# Test functions +# + +# Tests parsing a valid file +# We expect the following behaviour: +# - All directives are parsed +# - No tokens are left after parsing +@given(draw_file_valid()) +def test_parse_file_valid(test_data): + (tokens, expected) = test_data + stream = TokenStream(tokens.copy()) + parsed = MockParser().parse_file(stream, None) + assert parsed == expected + assert stream.pop() is None + + +# Tests parsing a invalid file +# We expect the following behaviour: +# - The error context is PARSE_FILE +# - A wrong directive error is propagated +@given(draw_file_valid(), data()) +def test_parse_file_invalid(test_data, data): + (tokens, _) = test_data + assume(len(tokens) > 0) + new_token = data.draw(draw_token_random(), "error token") + assume(new_token.value != "MockDirective") + max_chosen = len(tokens) - 1 + chosen = data.draw( + integers(min_value=0, max_value=max_chosen), "error token position" + ) + new_tokens = tokens[:chosen] + [new_token] + tokens[chosen + 1 :] + parent_context = static_parse_context() + context = ParseContext(ParseTask.PARSE_FILE, new_tokens[0], parent_context) + error = ParseErrorException( + ParseError.WRONG_TOKEN, new_token, "MockDirective", context + ) + parser = MockParser().parse_file + template_test_invalid(parser, parent_context, new_tokens, error)