diff --git a/tests/autotests/cfpq_concrete_cases.py b/tests/autotests/cfpq_concrete_cases.py new file mode 100644 index 000000000..7d86a0f4b --- /dev/null +++ b/tests/autotests/cfpq_concrete_cases.py @@ -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)}, + ), +] diff --git a/tests/autotests/graphs.py b/tests/autotests/graphs.py new file mode 100644 index 000000000..9265d04cd --- /dev/null +++ b/tests/autotests/graphs.py @@ -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"})] +) diff --git a/tests/autotests/rpq_concrete_cases.py b/tests/autotests/rpq_concrete_cases.py new file mode 100644 index 000000000..2027891f6 --- /dev/null +++ b/tests/autotests/rpq_concrete_cases.py @@ -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)}, + ), +] diff --git a/tests/autotests/test_task03.py b/tests/autotests/test_task03.py index e970e4d2a..321d73494 100644 --- a/tests/autotests/test_task03.py +++ b/tests/autotests/test_task03.py @@ -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!") @@ -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) diff --git a/tests/autotests/test_task04.py b/tests/autotests/test_task04.py index 2b73fabde..645133461 100644 --- a/tests/autotests/test_task04.py +++ b/tests/autotests/test_task04.py @@ -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: @@ -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( diff --git a/tests/autotests/test_task06.py b/tests/autotests/test_task06.py index 8558273de..01b5339f5 100644 --- a/tests/autotests/test_task06.py +++ b/tests/autotests/test_task06.py @@ -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: @@ -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) diff --git a/tests/autotests/test_task07.py b/tests/autotests/test_task07.py index 72aef324d..513ca5fe7 100644 --- a/tests/autotests/test_task07.py +++ b/tests/autotests/test_task07.py @@ -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: @@ -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) diff --git a/tests/autotests/test_task08.py b/tests/autotests/test_task08.py index decfb479e..2ca5a31a1 100644 --- a/tests/autotests/test_task08.py +++ b/tests/autotests/test_task08.py @@ -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: @@ -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] diff --git a/tests/autotests/test_task09.py b/tests/autotests/test_task09.py index 895574fef..6a5703c12 100644 --- a/tests/autotests/test_task09.py +++ b/tests/autotests/test_task09.py @@ -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: @@ -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]