Skip to content

Commit

Permalink
Ручные тесты (#164)
Browse files Browse the repository at this point in the history
* Add concrete tests for cfpq algorithms

* Add concrete tests for rpq algorithms

Also rename TestCaseCFPQ -> CaseCFPQ and fix docs

* Fix linter errors
  • Loading branch information
KubEF authored Sep 1, 2024
1 parent d4308b5 commit f0d0b99
Show file tree
Hide file tree
Showing 9 changed files with 256 additions and 8 deletions.
106 changes: 106 additions & 0 deletions tests/autotests/cfpq_concrete_cases.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,106 @@
from typing import Callable
from pyformlang.cfg import CFG
from copy import copy
from pyformlang.rsa import RecursiveAutomaton
from networkx import MultiDiGraph
import graphs


class CaseCFPQ:
"""
class that contains all information about test case for cfpq algorithms
"""

def __init__(
self,
graph: MultiDiGraph,
query: CFG,
actual_answer: set[tuple[int, int]],
start_nodes: set[int] = None,
final_nodes: set[int] = None,
):
self.graph = copy(graph)
self.query = copy(query)
self.expected_answer = copy(actual_answer)
self.start_nodes = copy(start_nodes) if start_nodes else graph.nodes
self.final_nodes = copy(final_nodes) if final_nodes else graph.nodes

def check_answer_cfg(
self,
function: Callable[
[CFG, MultiDiGraph, set[int], set[int]],
set[tuple[int, int]],
],
):
"""
assertion function for algorithms with cfg
:param function: the function under test (*hellings_based_cfpq* or *matrix_based_cfpq*)
:return: assertion
"""
actual_res = function(
self.query, self.graph, self.start_nodes, self.final_nodes
)
assert actual_res == self.expected_answer

def check_answer_rsm(
self,
function: Callable[
[RecursiveAutomaton, MultiDiGraph, set[int], set[int]], set[tuple[int, int]]
],
cfg_to_rsm: Callable[[CFG], RecursiveAutomaton],
):
"""
assertion function for algorithms with rsm
:param function: the function under test (*tensor_based_cfpq* or *gll_based_cfpq*)
:param cfg_to_rsm: function that convert CFG to RecursiveAutomaton
:return: assertion
"""
actual_res = function(
cfg_to_rsm(self.query), self.graph, self.start_nodes, self.final_nodes
)
assert actual_res == self.expected_answer

def __str__(self):
return (
f"expected result: {self.expected_answer}\n"
+ f"query: {self.query.to_text()}"
+ f"graph: {self.graph.edges(data=True)}"
)


CASES_CFPQ = [
CaseCFPQ(graphs.point_graph, CFG.from_text("S -> a"), set()),
CaseCFPQ(graphs.point_graph, CFG.from_text("S -> S a | $"), {(1, 1)}),
CaseCFPQ(
graphs.set_of_vertices_without_edges,
CFG.from_text("S -> S a | $"),
{(0, 0), (2, 2), (1, 1)},
),
CaseCFPQ(graphs.b_graph, CFG.from_text("S -> a"), set()),
CaseCFPQ(graphs.b_graph, CFG.from_text("S -> b"), {(0, 1)}),
CaseCFPQ(graphs.b_graph, CFG.from_text("S -> S b | $"), {(0, 1), (0, 0), (1, 1)}),
CaseCFPQ(
graphs.bbb_graph,
CFG.from_text("S -> S b b | $"),
{(0, 1), (0, 0), (0, 2), (1, 1), (1, 2), (1, 0), (2, 2), (2, 1), (2, 0)},
),
CaseCFPQ(
graphs.bab_graph,
CFG.from_text("S -> S a | S b | $"),
{(0, 1), (0, 0), (0, 2), (1, 1), (1, 2), (1, 0), (2, 2), (2, 1), (2, 0)},
),
CaseCFPQ(
graphs.baa_graph, CFG.from_text("S -> a S b | $"), {(0, 0), (1, 1), (1, 0)}
),
CaseCFPQ(graphs.baa_graph, CFG.from_text("S -> a S b | a b"), {(0, 0), (1, 0)}),
CaseCFPQ(
graphs.set_of_vertices_without_edges,
CFG.from_text("S -> $"),
{(0, 0), (2, 2), (1, 1)},
),
CaseCFPQ(
graphs.aaa_graph,
CFG.from_text("S -> a | S S"),
{(0, 1), (0, 0), (0, 2), (1, 1), (1, 2), (1, 0), (2, 2), (2, 1), (2, 0)},
),
]
31 changes: 31 additions & 0 deletions tests/autotests/graphs.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,31 @@
from networkx import MultiDiGraph
from constants import LABEL

point_graph = MultiDiGraph()
point_graph.add_node(1)

set_of_vertices_without_edges = MultiDiGraph()
set_of_vertices_without_edges.add_nodes_from([0, 1, 2])

b_graph = MultiDiGraph()
b_graph.add_edges_from([(0, 1, {LABEL: "b"})])

bbb_graph = MultiDiGraph()
bbb_graph.add_edges_from(
[(0, 1, {LABEL: "b"}), (1, 2, {LABEL: "b"}), (2, 0, {LABEL: "b"})]
)

bab_graph = MultiDiGraph()
bab_graph.add_edges_from(
[(0, 1, {LABEL: "b"}), (1, 2, {LABEL: "a"}), (2, 0, {LABEL: "b"})]
)

baa_graph = MultiDiGraph()
baa_graph.add_edges_from(
[(0, 0, {LABEL: "b"}), (0, 1, {LABEL: "a"}), (1, 0, {LABEL: "a"})]
)

aaa_graph = MultiDiGraph()
aaa_graph.add_edges_from(
[(0, 1, {LABEL: "a"}), (1, 2, {LABEL: "a"}), (2, 0, {LABEL: "a"})]
)
84 changes: 84 additions & 0 deletions tests/autotests/rpq_concrete_cases.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,84 @@
from typing import Callable

import graphs
from copy import copy
from helper import rpq_dict_to_set
from networkx import MultiDiGraph


class CaseRPQ:
"""
class that contains all information about test case for rpq algorithms
"""

def __init__(
self,
graph: MultiDiGraph,
regex: str,
actual_answer: set[tuple[int, int]],
start_nodes: set[int] = None,
final_nodes: set[int] = None,
):
self.graph = copy(graph)
self.regex = copy(regex)
self.expected_answer = copy(actual_answer)
self.start_nodes = copy(start_nodes) if start_nodes else graph.nodes
self.final_nodes = copy(final_nodes) if final_nodes else graph.nodes

def check_answer_regex(
self,
function: Callable[
[MultiDiGraph, set[int], set[int], str], list[tuple[int, int]]
],
):
"""
assertion function for algorithms with regex
:param function: the function under test (*tensor_based_rpq*)
:return: assertion
"""
assert (
set(function(self.graph, self.start_nodes, self.final_nodes, self.regex))
== self.expected_answer
)

def check_answer_automata(
self,
function,
fa,
constraints_fa,
):
"""
assertion function for algorithms with automata
:param function: the function under test (*ms_bfs_based_rpq*)
:param fa: automata by graph
:param constraints_fa: automata by regex
:return: assertion
"""
assert rpq_dict_to_set(function(fa, constraints_fa)) == self.expected_answer

def __str__(self):
return (
f"expected result: {self.expected_answer}\n"
+ f"regex: {self.regex}"
+ f"graph: {self.graph.edges(data=True)}"
)


CASES_RPQ = [
CaseRPQ(graphs.point_graph, "a", set()),
CaseRPQ(graphs.point_graph, "a*", {(1, 1)}),
CaseRPQ(graphs.set_of_vertices_without_edges, "a*", {(0, 0), (2, 2), (1, 1)}),
CaseRPQ(graphs.b_graph, "a", set()),
CaseRPQ(graphs.b_graph, "b", {(0, 1)}),
CaseRPQ(graphs.b_graph, "b*", {(0, 1), (0, 0), (1, 1)}),
CaseRPQ(
graphs.bbb_graph,
"(b b)*",
{(0, 1), (0, 0), (0, 2), (1, 1), (1, 2), (1, 0), (2, 2), (2, 1), (2, 0)},
),
CaseRPQ(
graphs.bab_graph,
"(a | b)*",
{(0, 1), (0, 0), (0, 2), (1, 1), (1, 2), (1, 0), (2, 2), (2, 1), (2, 0)},
),
]
14 changes: 6 additions & 8 deletions tests/autotests/test_task03.py
Original file line number Diff line number Diff line change
Expand Up @@ -8,11 +8,12 @@
import random
import itertools
from grammars_constants import REGEXES
from rpq_concrete_cases import CASES_RPQ, CaseRPQ

# Fix import statements in try block to run tests
try:
from project.task2 import regex_to_dfa
from project.task3 import intersect_automata, AdjacencyMatrixFA
from project.task3 import intersect_automata, AdjacencyMatrixFA, tensor_based_rpq
except ImportError:
pytestmark = pytest.mark.skip("Task 3 is not ready to test!")

Expand Down Expand Up @@ -46,10 +47,7 @@ def test(self, regex_str1: str, regex_str2: str) -> None:
assert intersect_fa.accepts(word)


def test_tensor_based_rpq_exists():
try:
import project.task3

assert "tensor_based_rpq" in dir(project.task3)
except NameError:
assert False
class TestTensorBasedRPQ:
@pytest.mark.parametrize("case", CASES_RPQ)
def test_concrete_cases(self, case: CaseRPQ):
case.check_answer_regex(tensor_based_rpq)
9 changes: 9 additions & 0 deletions tests/autotests/test_task04.py
Original file line number Diff line number Diff line change
Expand Up @@ -7,6 +7,7 @@
import pytest
from grammars_constants import REGEXES
from helper import generate_rnd_start_and_final, rpq_dict_to_set
from rpq_concrete_cases import CASES_RPQ, CaseRPQ

# Fix import statements in try block to run tests
try:
Expand All @@ -23,6 +24,14 @@ def query(request) -> str:


class TestRPQ:
@pytest.mark.parametrize("case", CASES_RPQ)
def test_concrete_cases(self, case: CaseRPQ):
fa = AdjacencyMatrixFA(
graph_to_nfa(case.graph, case.start_nodes, case.final_nodes)
)
constraint_fa = AdjacencyMatrixFA(regex_to_dfa(case.regex))
case.check_answer_automata(ms_bfs_based_rpq, fa, constraint_fa)

def test(self, graph, query) -> None:
start_nodes, final_nodes = generate_rnd_start_and_final(graph.copy())
fa = AdjacencyMatrixFA(
Expand Down
5 changes: 5 additions & 0 deletions tests/autotests/test_task06.py
Original file line number Diff line number Diff line change
Expand Up @@ -5,6 +5,7 @@
import pytest
from grammars_constants import REGEXP_CFG, GRAMMARS
from rpq_template_test import rpq_cfpq_test, different_grammars_test
from cfpq_concrete_cases import CaseCFPQ, CASES_CFPQ

# Fix import statements in try block to run tests
try:
Expand All @@ -14,6 +15,10 @@


class TestHellingBasedCFPQ:
@pytest.mark.parametrize("case", CASES_CFPQ)
def test_concrete_cases(self, case: CaseCFPQ):
case.check_answer_cfg(hellings_based_cfpq)

@pytest.mark.parametrize("regex_str, cfg_list", REGEXP_CFG)
def test_rpq_cfpq_hellings(self, graph, regex_str, cfg_list):
rpq_cfpq_test(graph, regex_str, cfg_list, hellings_based_cfpq)
Expand Down
5 changes: 5 additions & 0 deletions tests/autotests/test_task07.py
Original file line number Diff line number Diff line change
Expand Up @@ -7,6 +7,7 @@
from grammars_constants import REGEXP_CFG, GRAMMARS, GRAMMARS_DIFFERENT
from helper import generate_rnd_start_and_final
from rpq_template_test import rpq_cfpq_test, different_grammars_test
from cfpq_concrete_cases import CASES_CFPQ, CaseCFPQ

# Fix import statements in try block to run tests
try:
Expand All @@ -17,6 +18,10 @@


class TestMatrixBasedCFPQ:
@pytest.mark.parametrize("case", CASES_CFPQ)
def test_concrete_cases(self, case: CaseCFPQ):
case.check_answer_cfg(matrix_based_cfpq)

@pytest.mark.parametrize("regex_str, cfg_list", REGEXP_CFG)
def test_rpq_cfpq_matrix(self, graph, regex_str, cfg_list) -> None:
rpq_cfpq_test(graph, regex_str, cfg_list, matrix_based_cfpq)
Expand Down
5 changes: 5 additions & 0 deletions tests/autotests/test_task08.py
Original file line number Diff line number Diff line change
Expand Up @@ -11,6 +11,7 @@
different_grammars_test,
cfpq_algorithm_test,
)
from cfpq_concrete_cases import CASES_CFPQ, CaseCFPQ

# Fix import statements in try block to run tests
try:
Expand All @@ -22,6 +23,10 @@


class TestTensorBasedCFPQ:
@pytest.mark.parametrize("case", CASES_CFPQ)
def test_concrete_cases(self, case: CaseCFPQ):
case.check_answer_rsm(tensor_based_cfpq, cfg_to_rsm)

@pytest.mark.parametrize("regex_str, cfg_list", REGEXP_CFG)
def test_rpq_cfpq_tensor(self, graph, regex_str, cfg_list) -> None:
cfg_list_rsm = [cfg_to_rsm(grammar) for grammar in cfg_list]
Expand Down
5 changes: 5 additions & 0 deletions tests/autotests/test_task09.py
Original file line number Diff line number Diff line change
Expand Up @@ -11,6 +11,7 @@
different_grammars_test,
cfpq_algorithm_test,
)
from cfpq_concrete_cases import CaseCFPQ, CASES_CFPQ

# Fix import statements in try block to run tests
try:
Expand All @@ -23,6 +24,10 @@


class TestGLLBasedCFPQ:
@pytest.mark.parametrize("case", CASES_CFPQ)
def test_concrete_cases(self, case: CaseCFPQ):
case.check_answer_rsm(gll_based_cfpq, cfg_to_rsm)

@pytest.mark.parametrize("regex_str, cfg_list", REGEXP_CFG)
def test_rpq_cfpq_gll(self, graph, regex_str, cfg_list) -> None:
rsm_list = [cfg_to_rsm(grammar) for grammar in cfg_list]
Expand Down

0 comments on commit f0d0b99

Please sign in to comment.