Newer
Older
NewLang / tests / parse / templates.py
# SPDX-License-Identifier: LGPL-2.1-only
# Copyright 2022 Jookia <contact@jookia.org>

from hypothesis import given
from hypothesis.strategies import composite, integers

from src.parse import ParseErrorException
from src.token import TokenStream
from tests.parse.test_error import static_parse_context
from tests.test_token import static_token_by_value


# Inserts an element randomly between the first and last token of a list
def insert_random_within(draw, list, data):
    pos = draw(integers(min_value=1, max_value=(len(list) - 1)))
    new_data = list[0:pos] + [data] + list[pos:]
    return new_data


# Tests that something parses correctly
# We expect the following behaviour:
# - The parse function generates the expected output
# - The parse function doesn't consume extra tokens
def template_test_valid(parser, tokens, expected):
    canary = static_token_by_value("CANARY")
    stream = TokenStream(tokens + [canary])
    parsed = parser(stream, None)
    if expected is None:
        assert parsed is None
    else:
        assert parsed is not None
        assert parsed == expected
    assert stream.pop() == canary
    assert stream.pop() is None


# Test that something parses incorrectly
# We expect the following behaviour:
# - The parse function generates the expected error
# - The parse function uses the parse context as a parent
def template_test_invalid(parser, context, tokens, expected):
    stream = TokenStream(tokens.copy())
    error = None
    try:
        parsed = parser(stream, context)
        raise AssertionError("Parsed invalid data: %s" % (parsed))
    except ParseErrorException as e:
        error = e
    assert error == expected


# Deprecated: Do not use
# Tests that something parses correctly
# We expect the following behaviour:
# - The decoration supplies a generator for test data and expected output
# - The decorated function is unused
# - Only the supplied tokens are parsed
# - The supplied tokens parse to the expected value
# - The Token's value is the expected value
# - The Token's location is the first token's location
def template_parse_valid(parser, draw):
    @given(draw)
    def do(test_data):
        (tokens, expected) = test_data
        return template_test_valid(parser, tokens, expected)

    return lambda func: do


# Deprecated: Do not use
# Tests that something parses correctly with a custom parser
# We expect the following behaviour:
# - The decorated function supplies a parser, test data and expected data
# - Only the supplied tokens are parsed
# - The supplied tokens parse to the expected value
# - The Token's value is the expected value
# - The Token's location is the first token's location
def template_parse_valid_composite(func):
    @given(composite(func)())
    def do(test_data):
        (parser, tokens, expected) = test_data
        return template_test_valid(parser, tokens, expected)

    return do


# Deprecated: Do not use
# Test that something parses incorrectly
# We expect the following behaviour:
# - The decoration supplies a parser function
# - The decorated function takes a parse context
# - The decorated function generates input tokens and an error
# - Parsing causes an error
# - The parse error is as expected
def template_parse_invalid(parser):
    # Wrapper to add parse_context to our test_data
    @composite
    def wrapper(draw, func):
        context = static_parse_context()
        (tokens, error) = draw(composite(func)(context))
        return (tokens, error, context)

    # test_data is the output of wrapper
    def do(test_data):
        (tokens, error, context) = test_data
        return template_test_invalid(parser, context, tokens, error)

    return lambda func: given(wrapper(func))(do)


# Deprecated: Do not use
# Test that something parses incorrectly with a custom parser
# We expect the following behaviour:
# - The decorated function takes a parse context
# - The decorated function generates a parser, input tokens and an error
# - Parsing causes an error
# - The parse error is as expected
def template_parse_invalid_composite(func):
    # Wrapper to add parse_context to our test_data
    @composite
    def wrapper(draw):
        context = static_parse_context()
        (parser, tokens, error) = draw(composite(func)(context))
        return (parser, tokens, error, context)

    # test_data is the output of wrapper
    @given(wrapper())
    def do(test_data):
        (parser, tokens, error, context) = test_data
        return template_test_invalid(parser, context, tokens, error)

    return do