2
0
Fork 0
textout/tests/test_parser.py

196 lines
5.7 KiB
Python

#!/usr/bin/env python
# *****************************************************************************
# Copyright (C) 2023 Thomas Touhey <thomas@touhey.fr>
# This file is part of the textoutpc project, which is MIT-licensed.
# *****************************************************************************
"""Parser tests for textoutpc."""
from __future__ import annotations
from itertools import chain
from typing import Iterable, Sequence
from docutils.nodes import Element, Node, Text, container, emphasis, strong
from docutils.utils import new_document
import pytest
from textoutpc.nodes import progress, spoiler
from textoutpc.parser import TextoutParser
def represent_node(node: Node) -> str:
"""Get the representation string for a node.
:param node: The node to get the representation for.
:return: The string representation.
"""
base = repr(node)
if isinstance(node, Element):
base += (
" ["
+ ", ".join(
f"{key}={value!r}" for key, value in node.attributes.items()
)
+ "]"
)
return base
def compare_nodes(
first_node_iterable: Iterable[Node],
second_node_iterable: Iterable[Node],
/,
*,
path: str = "",
) -> bool:
"""Compare node sequences.
:param first_node_iterable: The first node iterable.
:param second_node_iterable: The second node iterable.
:param path: The path.
:return: Whether the comparison works or not.
"""
first_node_iterator = iter(first_node_iterable)
second_node_iterator = iter(second_node_iterable)
result = True
for i, (first_node, second_node) in enumerate(
zip(first_node_iterator, second_node_iterator),
):
similar_nodes = True
if type(first_node) is not type(second_node):
similar_nodes = False
if isinstance(first_node, Text) and isinstance(second_node, Text):
similar_nodes = str(first_node) == str(second_node) and result
elif isinstance(first_node, Element) and isinstance(
second_node,
Element,
):
if isinstance(first_node, progress) and isinstance(
second_node,
progress,
):
similar_nodes = (
first_node.value == second_node.value and similar_nodes
)
if first_node.attributes != second_node.attributes:
similar_nodes = False
if not similar_nodes:
result = False
result = (
compare_nodes(first_node, second_node, path=path + f"[{i}]")
and result
)
if not similar_nodes:
print(f"Different nodes at {path or 'root'}:")
print(f" in input: {represent_node(first_node)}")
print(f" in output: {represent_node(second_node)}")
result = False
try:
first_node = next(first_node_iterator)
except StopIteration:
pass
else:
result = False
print(f"Additional nodes in the input at {path or 'root'}:")
for node in chain((first_node,), first_node_iterator):
print(f" {represent_node(node)}")
try:
second_node = next(second_node_iterator)
except StopIteration:
pass
else:
result = False
print(f"Additional nodes in the output at {path or 'root'}:")
for node in chain((second_node,), second_node_iterator):
print(f" {represent_node(node)}")
return result
@pytest.mark.parametrize(
"inputstring,expected",
(
("hello\nworld", [Text("hello\nworld")]),
(
"[b][i]hello[/i]",
[strong("", emphasis("", Text("hello")))],
),
(
"[b][i]hello[/b]omg",
[strong("", emphasis("", Text("hello"))), Text("omg")],
),
(
"[c=#abc;font-size: 12pt]hello",
[
container(
"",
Text("hello"),
style="color: #AABBCC; font-size: 12pt",
),
],
),
(
"[b=unexpected]wow",
[Text("[b=unexpected]wow")],
),
(
"[center]hello",
[container("", Text("hello"), **{"class": "align-center"})],
),
(
"[progress=55]My super progress bar",
[progress("", Text("My super progress bar"), value=55)],
),
(
"[hello]world[/hello]",
[Text("[hello]world[/hello]")],
),
(
"[noeval][hello]world[/hello][/noeval]",
[Text("[hello]world[/hello]")],
),
(
"the message is: [rot=13]uryyb[/rot] - the - [rot13]jbeyq",
[Text("the message is: hello - the - world")],
),
(
"[spoiler=should open|should close]spoiler [b]content[/b]!",
[
spoiler(
"",
Text("spoiler "),
strong("", Text("content")),
Text("!"),
closed_title="should open",
opened_title="should close",
),
],
),
(
"[code=c]int main() { return 0; }",
[
container(
"",
Text("int main() { return 0; }"),
**{"class": "code"},
),
],
),
),
)
def test_parser(inputstring: str, expected: Sequence[Node]) -> None:
"""Test that the parser works correctly."""
doc = new_document("/tmp/fake-source.bbcode") # noqa: S108
parser = TextoutParser()
parser.parse(inputstring, doc)
assert compare_nodes(doc, expected)