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, given
from hypothesis.strategies import (
    composite,
    lists,
)

from src.parse import (
    skip_note,
    ParseContext,
    ParseError,
    ParseErrorException,
    ParseTask,
)
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,
)


# Static tokens representing a note
def static_note_tokens():
    return [static_token_by_value("StartNote"), static_token_by_value("EndNote")]


# An invalid note token
def static_note_invalid():
    return [static_token_by_value("StartNote")]


# An invalid note token error
def static_note_invalid_error(parent_context):
    token = static_note_invalid()[0]
    note_context = ParseContext(ParseTask.PARSE_NOTE, token, parent_context)
    return ParseErrorException(ParseError.NO_TOKEN, None, None, note_context)


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


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


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


# Tests skip_note works correctly
# We expect the following behaviour:
# - No value is returned
# template_test provides general parsing properties
@given(draw_token_note_valid())
def test_parse_note_valid(tokens):
    template_test_valid(skip_note, tokens, None)


# Tests parsing notes without StartNote
# We expect the following behaviour:
# - Error if StartNote's token value is not "StartNote"
# - Have ParseError.WRONG_TOKEN as the exception code
# - Have ParseTask.PARSE_NOTE as the context's parse task
@given(draw_token_note_valid(), draw_token_not_startnote())
def test_parse_note_invalid_nostartnote(tokens, token):
    new_tokens = [token] + tokens[1:0]
    parent_context = static_parse_context()
    context = ParseContext(ParseTask.PARSE_NOTE, new_tokens[0], parent_context)
    error = ParseErrorException(ParseError.WRONG_TOKEN, token, "StartNote", context)
    template_test_invalid(skip_note, parent_context, new_tokens, error)


# Tests parsing empty notes
# We expect the following behaviour:
# - Error if there is no StartNote token at all
# - Have ParseError.NO_TOKEN as the exception code
# - Have ParseTask.PARSE_NOTE as the context's parse task
def test_parse_note_invalid_empty():
    parent_context = static_parse_context()
    context = ParseContext(ParseTask.PARSE_NOTE, None, parent_context)
    error = ParseErrorException(ParseError.NO_TOKEN, None, None, context)
    template_test_invalid(skip_note, parent_context, [], error)


# Tests parsing a note with a StartNote token in it
# We expect the following behaviour:
# - Error if a StartNote token is in the note content
# - Have ParseTask.PARSE_NOTE as the context's parse task
@given(draw_random_within(draw_token_note_valid(), "StartNote"))
def test_parse_note_invalid_extrastartnote(within):
    (tokens, start) = within
    parent_context = static_parse_context()
    context = ParseContext(ParseTask.PARSE_NOTE, tokens[0], parent_context)
    error = ParseErrorException(ParseError.FOUND_STARTNOTE, start, None, context)
    template_test_invalid(skip_note, parent_context, tokens, error)


# Tests parsing a note without an EndNote token
# We expect the following behaviour:
# - Error if there is no EndNote token at all
# - Have ParseError.NO_TOKEN as the exception code
# - Have ParseTask.PARSE_NOTE as the context's parse task
@given(draw_token_note_valid())
def test_parse_note_invalid_noendnote(tokens):
    new_tokens = tokens[0:-1]
    parent_context = static_parse_context()
    context = ParseContext(ParseTask.PARSE_NOTE, tokens[0], parent_context)
    error = ParseErrorException(ParseError.NO_TOKEN, None, None, context)
    template_test_invalid(skip_note, parent_context, new_tokens, error)