# 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, ParseError, ParseErrorException, ) from src.syntax import SyntaxStream 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): stream.pop() return None # Dummy parse_note implementation for testing error propgation # This redefines skip_note to always throw an error def clear_notes_skip_note_error(stream): s = stream.peek() raise ParseErrorException(ParseError.TEST_ERROR, s, None) # 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) skipper = NoteSkipper() skipper.skip_note = clear_notes_skip_note_valid cleared = skipper.clear_notes(stream) assert cleared == result # Draws tokens to test clear_notes error propagation @composite def draw_syntax_clear_notes_startnote_propagation(draw): tokens = draw(lists(draw_clear_notes_value_token())) # Ensure we have a StartNote somewhere start = draw(draw_token_by_value("StartNote")) new_tokens = tokens + [start] for token in new_tokens: if token.value == "StartNote": error = ParseErrorException(ParseError.TEST_ERROR, token, None) return (new_tokens, error) raise AssertionError("Unable to find StartNote?") # 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 @given(draw_syntax_clear_notes_startnote_propagation()) def test_parse_clear_notes(test_data): (tokens, error) = test_data stream = SyntaxStream(tokens) skipper = NoteSkipper() skipper.skip_note = clear_notes_skip_note_error try: parsed = skipper.clear_notes(stream) raise AssertionError("Parsed invalid data: %s" % (parsed)) except ParseErrorException as e: assert e == error # Draws tokens to test clear_notes EndNote invalid error @composite def draw_syntax_clear_notes_invalid_endnote(draw): tokens = draw(lists(draw_clear_notes_value_token())) # Ensure we have an EndNote somewhere start = draw(draw_token_by_value("EndNote")) new_tokens = tokens + [start] for token in new_tokens: if token.value == "EndNote": error = ParseErrorException(ParseError.FOUND_ENDNOTE, token, None) return (new_tokens, error) raise AssertionError("Unable to find EndNote?") # Tests clear_notes errors when finding an EndNote # We expect the following behaviour: # - When EndNote is encountered a FOUND_ENDNOTE error is raised @given(draw_syntax_clear_notes_invalid_endnote()) def test_parse_clear_notes_invalid_endnote(test_data): (tokens, error) = test_data stream = SyntaxStream(tokens) skipper = NoteSkipper() skipper.skip_note = clear_notes_skip_note_valid try: parsed = skipper.clear_notes(stream) raise AssertionError("Parsed invalid data: %s" % (parsed)) except ParseErrorException as e: assert e == error