Newer
Older
NewLang / tests / parse / test_text.py
# 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.ast_types import Text
from src.parse import (
    ParseContext,
    ParseError,
    ParseErrorException,
    ParseTask,
    Parser,
)
from tests.parse.templates import (
    draw_random_within,
    template_test_invalid,
    template_test_valid,
)
from tests.parse.test_error import static_parse_context
from tests.test_token import (
    static_token_by_value,
    draw_token_random,
)


# Draws a random token suitable for text building
@composite
def draw_text_value_token(draw):
    token = draw(draw_token_random())
    assume(token.value not in ["StartText", "EndText"])
    return token


# Draws a random token that isn't StartText token
@composite
def draw_token_not_starttext(draw):
    token = draw(draw_token_random())
    assume(token.value != "StartText")
    return token


# Draws tokens to make a valid text string and its value
@composite
def draw_token_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 = static_token_by_value("StartText")
    end = static_token_by_value("EndText")
    all_tokens = [start] + tokens + [end]
    return (all_tokens, Text(value))


# Draws just the tokens of a valid text string
@composite
def draw_token_text_valid_tokens(draw):
    (tokens, _) = draw(draw_token_text_valid())
    return tokens


# 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 Token's value is the resulting text
# template_test_valid provides general parsing properties
@given(draw_token_text_valid())
def test_parse_text_valid(valid_data):
    (tokens, expected) = valid_data
    template_test_valid(Parser().parse_text, tokens, expected)


# Test parsing text without StartText
# We expect the following behaviour:
# - Error if StartText's token value is not "StartText"
# - Have ParseError.PARSE_TEXT as the exception code
# - Have ParseTask.PARSE_TEXT as the context's parse task
@given(draw_token_text_valid_tokens(), draw_token_not_starttext())
def test_parse_text_invalid_nostarttext(tokens, not_starttext):
    new_tokens = [not_starttext] + tokens[1:0]
    parent_context = static_parse_context()
    context = ParseContext(ParseTask.PARSE_TEXT, new_tokens[0], parent_context)
    error = ParseErrorException(
        ParseError.WRONG_TOKEN, not_starttext, "StartText", context
    )
    template_test_invalid(Parser().parse_text, parent_context, new_tokens, error)


# Tests parsing empty text
# We expect the following behaviour:
# - Error if there is no StartText token at all
# - Have ParseError.NO_TOKEN as the exception code
# - Have ParseTask.PARSE_TEXT as the context's parse task
def test_parse_text_invalid_empty():
    parent_context = static_parse_context()
    context = ParseContext(ParseTask.PARSE_TEXT, None, parent_context)
    error = ParseErrorException(ParseError.NO_TOKEN, None, None, context)
    template_test_invalid(Parser().parse_text, parent_context, [], 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
# - Have ParseError.FOUND_STARTTEXT as the exception code
# - Have ParseTask.PARSE_TEXT as the context's parse task
@given(draw_random_within(draw_token_text_valid_tokens(), "StartText"))
def test_parse_text_invalid_extrastarttext(within):
    (tokens, start) = within
    parent_context = static_parse_context()
    context = ParseContext(ParseTask.PARSE_TEXT, tokens[0], parent_context)
    error = ParseErrorException(ParseError.FOUND_STARTTEXT, start, None, context)
    template_test_invalid(Parser().parse_text, parent_context, tokens, error)


# Tests parsing text without an EndText token
# We expect the following behaviour:
# - Error if there is no EndText token at all
# - Have ParseError.NO_TOKEN as the exception code
# - Have ParseTask.PARSE_TEXT as the context's parse task
@given(draw_token_text_valid_tokens())
def test_parse_text_invalid_noendtext(tokens):
    new_tokens = tokens[0:-1]
    parent_context = static_parse_context()
    context = ParseContext(ParseTask.PARSE_TEXT, tokens[0], parent_context)
    error = ParseErrorException(ParseError.NO_TOKEN, None, None, context)
    template_test_invalid(Parser().parse_text, parent_context, new_tokens, error)