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

from hypothesis import assume
from hypothesis.strategies import (
    booleans,
    composite,
    lists,
)

from src.parse import (
    NoteSkipper,
    ParseContext,
    ParseError,
    ParseErrorException,
    ParseTask,
)
from src.syntax import SyntaxType
from tests.parse.templates import (
    insert_random,
    template_parse_invalid,
    template_parse_valid,
)
from tests.parse.test_parse import draw_parse_context
from tests.test_syntax import (
    draw_token_by_value,
    draw_syntax_random,
    draw_syntax_token,
)


# Draws a random token suitable for note building
@composite
def draw_note_value_token(draw):
    token = draw(draw_syntax_token())
    assume(token.value not in ["StartNote", "EndNote"])
    return token


# Draws tokens to make a valid note
@composite
def draw_syntax_note_valid(draw):
    tokens = draw(lists(draw_note_value_token()))
    start = draw(draw_token_by_value("StartNote"))
    end = draw(draw_token_by_value("EndNote"))
    all_tokens = [start] + tokens + [end]
    return (all_tokens, None)


# Tests skip_note works correctly
# We expect the following behaviour:
# - No value is returned
# template_parse_valid provides general parsing properties
@template_parse_valid(NoteSkipper().skip_note, draw_syntax_note_valid())
def test_parse_note_valid():
    pass


# Generate note without StartNote
# We expect the following behaviour:
# - Error if there is no StartNote node at all
# - Error if StartNote is not a SyntaxType.TOKEN
# - Error if StartNote's token value is not "StartNote"
@template_parse_invalid(NoteSkipper().skip_note)
def test_parse_note_invalid_nostartnote(draw):
    (tokens, _) = draw(draw_syntax_note_valid())
    parent_context = draw(draw_parse_context())
    if draw(booleans()):
        token = draw(draw_syntax_random())
        assume(not (token.type == SyntaxType.TOKEN and token.value == "StartNote"))
        new_tokens = [token] + tokens[1:0]
        context = ParseContext(ParseTask.PARSE_NOTE, new_tokens[0], parent_context)
        if token.type == SyntaxType.TOKEN:
            error = ParseErrorException(
                ParseError.WRONG_TOKEN, token, "StartNote", context
            )
        else:
            error = ParseErrorException(ParseError.NOT_TOKEN, token, None, context)
        return (new_tokens, error, parent_context)
    else:
        context = ParseContext(ParseTask.PARSE_NOTE, None, parent_context)
        error = ParseErrorException(ParseError.NO_TOKEN, None, None, context)
        return ([], error, parent_context)


# Generate note with a StartNote token in it
# We expect the following behaviour:
# - Error if a StartNote token is in the note content
@template_parse_invalid(NoteSkipper().skip_note)
def test_parse_note_invalid_extrastartnote(draw):
    (tokens, _) = draw(draw_syntax_note_valid())
    start = draw(draw_token_by_value("StartNote"))
    new_tokens = insert_random(draw, tokens, start)
    parent_context = draw(draw_parse_context())
    context = ParseContext(ParseTask.PARSE_NOTE, new_tokens[0], parent_context)
    error = ParseErrorException(ParseError.FOUND_STARTNOTE, start, None, context)
    return (new_tokens, error, parent_context)


# Generate note without EndNote
# We expect the following behaviour:
# - Error if there is no EndNote node at all
@template_parse_invalid(NoteSkipper().skip_note)
def test_parse_note_invalid_noendnote(draw):
    (tokens, _) = draw(draw_syntax_note_valid())
    parent_context = draw(draw_parse_context())
    context = ParseContext(ParseTask.PARSE_NOTE, tokens[0], parent_context)
    error = ParseErrorException(ParseError.NO_TOKEN, None, None, context)
    return (tokens[0:-1], error, parent_context)