# SPDX-License-Identifier: LGPL-2.1-only # Copyright 2022 Jookia <contact@jookia.org> from hypothesis import assume from hypothesis.strategies import ( composite, lists, ) from src.ast_types import Text from src.parse import ( ParseContext, ParseError, ParseErrorException, ParseTask, Parser, ) from src.syntax import SyntaxType from tests.parse.templates import ( insert_random_within, template_parse_invalid, template_parse_valid, ) from tests.test_syntax import ( draw_token_by_value, draw_syntax_random, draw_syntax_not_token, draw_syntax_token, ) # Draws a random token suitable for text building @composite def draw_text_value_token(draw): token = draw(draw_syntax_token()) assume(token.value not in ["StartText", "EndText"]) return token # Draws a random syntax that isn't StartText token @composite def draw_syntax_not_starttext(draw): token = draw(draw_syntax_random()) assume(not (token.type == SyntaxType.TOKEN and token.value == "StartText")) return token # Draws tokens to make a valid text string and its value @composite def draw_syntax_text_valid(draw): tokens = draw(lists(draw_text_value_token())) buffer = "" for token in tokens: buffer += token.value + " " value = buffer[:-1] # Drop trailing space start = draw(draw_token_by_value("StartText")) end = draw(draw_token_by_value("EndText")) all_tokens = [start] + tokens + [end] return (all_tokens, Text(value)) # Tests parse_text works correctly # We expect the following behaviour: # - The resulting text is the value of tokens between StartText and EndText # - The value of the tokens is joined by U+0020 SPACE code points # - The Syntax's value is the resulting text # - The Syntax's type is SyntaxType.TEXT # template_parse_valid provides general parsing properties @template_parse_valid(Parser().parse_text, draw_syntax_text_valid()) def test_parse_text_valid(): pass # Test parsing text without StartText # We expect the following behaviour: # - Error if StartText is not a SyntaxType.TOKEN # - Error if StartText's token value is not "StartText" @template_parse_invalid(Parser().parse_text) def test_parse_text_invalid_nostarttext(draw, parent_context): (tokens, _) = draw(draw_syntax_text_valid()) token = draw(draw_syntax_not_starttext()) new_tokens = [token] + tokens[1:0] context = ParseContext(ParseTask.PARSE_TEXT, new_tokens[0], parent_context) if token.type == SyntaxType.TOKEN: error = ParseErrorException(ParseError.WRONG_TOKEN, token, "StartText", context) else: error = ParseErrorException(ParseError.NOT_TOKEN, token, None, context) return (new_tokens, error) # Tests parsing empty text # We expect the following behaviour: # - Error if there is no StartText token at all @template_parse_invalid(Parser().parse_text) def test_parse_text_invalid_empty(draw, parent_context): context = ParseContext(ParseTask.PARSE_TEXT, None, parent_context) error = ParseErrorException(ParseError.NO_TOKEN, None, None, context) return ([], error) # Tests parsing text with invalid content tokens # We expect the following behaviour: # - Error if a content token is not a SyntaxType.TOKEN @template_parse_invalid(Parser().parse_text) def test_parse_text_invalid_invalidcontent(draw, parent_context): (tokens, _) = draw(draw_syntax_text_valid()) token = draw(draw_syntax_not_token()) new_tokens = insert_random_within(draw, tokens, token) context = ParseContext(ParseTask.PARSE_TEXT, new_tokens[0], parent_context) error = ParseErrorException(ParseError.NOT_TOKEN, token, None, context) return (new_tokens, error) # Tests parsing text with a StartText token in it # We expect the following behaviour: # - Error if a StartText token is in the text content @template_parse_invalid(Parser().parse_text) def test_parse_text_invalid_extrastarttext(draw, parent_context): (tokens, _) = draw(draw_syntax_text_valid()) start = draw(draw_token_by_value("StartText")) new_tokens = insert_random_within(draw, tokens, start) context = ParseContext(ParseTask.PARSE_TEXT, new_tokens[0], parent_context) error = ParseErrorException(ParseError.FOUND_STARTTEXT, start, None, context) return (new_tokens, error) # Tests parsing text without an EndText token # We expect the following behaviour: # - Error if there is no EndText token at all @template_parse_invalid(Parser().parse_text) def test_parse_text_invalid_noendtext(draw, parent_context): (tokens, _) = draw(draw_syntax_text_valid()) context = ParseContext(ParseTask.PARSE_TEXT, tokens[0], parent_context) error = ParseErrorException(ParseError.NO_TOKEN, None, None, context) return (tokens[0:-1], error)