# SPDX-License-Identifier: LGPL-2.1-only # Copyright 2022 Jookia <contact@jookia.org> from hypothesis import assume, given from hypothesis.strategies import composite, lists from src.parse import ( NoteSkipper, ParseContext, ParseError, ParseErrorException, ParseTask, ) from src.syntax import SyntaxStream from tests.parse.templates import template_parse_invalid from tests.test_syntax import draw_token_by_value, draw_syntax_token # Dummy parse_note implementation for testing note clearing # This redefines skip_note to skip the StartNote and not do anything else def clear_notes_skip_note_valid(stream, parent_context): stream.pop() return None # Creates a NoteSkipper with clear_notes_skip_note_valid def dummy_skipper_valid(): skipper = NoteSkipper() skipper.skip_note = clear_notes_skip_note_valid return skipper # Dummy parse_note implementation for testing error propgation # This redefines skip_note to always throw an error def clear_notes_skip_note_error(stream, parent_context): s = stream.peek() raise ParseErrorException(ParseError.TEST_ERROR, s, None, parent_context) # Creates a NoteSkipper with clear_notes_skip_note_error def dummy_skipper_invalid(): skipper = NoteSkipper() skipper.skip_note = clear_notes_skip_note_error return skipper # Draws a random token suitable for note clearing testing @composite def draw_clear_notes_value_token(draw): token = draw(draw_syntax_token()) assume(token.value not in ["EndNote"]) return token # Draws tokens to make a valid soup to clear notes @composite def draw_syntax_clear_notes_valid(draw): tokens = draw(lists(draw_clear_notes_value_token())) output = [] for token in tokens: if token.value != "StartNote": output.append(token) return (tokens, output) # Tests clear_notes works correctly # We expect the following behaviour: # - When StartNote is encountered skip_note is called to skip the note # - Other tokens are passed through @given(draw_syntax_clear_notes_valid()) def test_parse_clear_notes_valid(test_data): (tokens, result) = test_data stream = SyntaxStream(tokens) cleared = dummy_skipper_valid().clear_notes(stream, None) assert cleared == result # Error when finding a specific token in a stream of notes def error_on_token(draw, parent_context, value, error): tokens = draw(lists(draw_clear_notes_value_token())) # Ensure we have a value somewhere start = draw(draw_token_by_value(value)) new_tokens = tokens + [start] context = ParseContext(ParseTask.CLEAR_NOTES, new_tokens[0], parent_context) for token in new_tokens: if token.value == value: error = ParseErrorException(error, token, None, context) return (new_tokens, error) # Tests clear_notes passes through skip_note errors # We expect the following behaviour: # - When StartNote is encountered skip_note is called to skip the note # - Any error skip_note gives is propagated through clear_notes @template_parse_invalid(dummy_skipper_invalid().clear_notes) def test_parse_clear_notes_startnote_propagation(draw, parent_context): return error_on_token(draw, parent_context, "StartNote", ParseError.TEST_ERROR) # Tests clear_notes errors when finding an EndNote # We expect the following behaviour: # - When EndNote is encountered a FOUND_ENDNOTE error is raised @template_parse_invalid(dummy_skipper_valid().clear_notes) def test_parse_clear_notes_invalid_endnote(draw, parent_context): return error_on_token(draw, parent_context, "EndNote", ParseError.FOUND_ENDNOTE)