diff --git a/swh/graph/tests/conftest.py b/swh/graph/tests/conftest.py new file mode 100644 index 0000000..92ace00 --- /dev/null +++ b/swh/graph/tests/conftest.py @@ -0,0 +1,40 @@ +import multiprocessing +import pytest +from pathlib import Path + +from aiohttp.test_utils import TestServer, TestClient, loop_context + +from swh.graph.client import RemoteGraphClient +from swh.graph.server.backend import Backend +from swh.graph.server.app import make_app + +SWH_GRAPH_ROOT = Path(__file__).parents[3] +TEST_GRAPH_PATH = SWH_GRAPH_ROOT / 'tests/dataset/output/example' + + +class GraphServerProcess(multiprocessing.Process): + def __init__(self, q, *args, **kwargs): + self.q = q + super().__init__(*args, **kwargs) + + def run(self): + backend = Backend(graph_path=str(TEST_GRAPH_PATH)) + with backend: + with loop_context() as loop: + self.loop = loop + app = make_app(backend=backend) + client = TestClient(TestServer(app), loop=loop) + loop.run_until_complete(client.start_server()) + url = client.make_url('/graph/') + self.q.put(url) + loop.run_forever() + + +@pytest.fixture(scope="module") +def graph_client(): + queue = multiprocessing.Queue() + server = GraphServerProcess(queue) + server.start() + url = queue.get() + yield RemoteGraphClient(str(url)) + server.terminate() diff --git a/swh/graph/tests/test_api_client.py b/swh/graph/tests/test_api_client.py index b49ee81..bbf55a2 100644 --- a/swh/graph/tests/test_api_client.py +++ b/swh/graph/tests/test_api_client.py @@ -1,190 +1,104 @@ -from pathlib import Path -from urllib.request import urlopen -import subprocess -import time - -import aiohttp.test_utils -import pytest - -from swh.graph.client import RemoteGraphClient -from swh.graph.tests import SWH_GRAPH_VERSION - - -@pytest.fixture(scope='module') -def graph_client(): - swh_graph_root = Path(__file__).parents[3] - java_dir = swh_graph_root / 'java/server' - - # Compile Java server using maven - pom_path = java_dir / 'pom.xml' - subprocess.run( - ['mvn', '-f', str(pom_path), 'compile', 'assembly:single'], check=True) - - port = aiohttp.test_utils.unused_port() - - # Start Java server - jar_file = 'swh-graph-{}-jar-with-dependencies.jar'.format( - SWH_GRAPH_VERSION) - jar_path = java_dir / 'target' / jar_file - graph_path = java_dir / 'src/test/dataset/output/example' - server = subprocess.Popen([ - 'java', '-cp', str(jar_path), - 'org.softwareheritage.graph.App', str(graph_path), '-p', str(port) - ]) - - # Wait max 5 seconds for server to spawn - localhost = 'http://0.0.0.0:{}'.format(port) - i = 0 - while i < 20: - try: - urlopen(localhost) - except Exception: - i += 1 - time.sleep(0.25) - else: - break - - # Start Python client - client = RemoteGraphClient(localhost) - - yield client - - print('Service teardown') - server.kill() - - -class TestEndpoints: - @pytest.fixture(autouse=True) - def init_graph_client(self, graph_client): - self.client = graph_client - - @staticmethod - def assert_endpoint_output(actual, expected): - assert set(actual.keys()) == {'result', 'meta'} - assert set(actual['result']) == set(expected['result']) - assert actual['meta'] == expected['meta'] - - def test_leaves(self): - actual = self.client.leaves( - 'swh:1:ori:0000000000000000000000000000000000000021' - ) - expected = { - 'result': [ - 'swh:1:cnt:0000000000000000000000000000000000000001', - 'swh:1:cnt:0000000000000000000000000000000000000004', - 'swh:1:cnt:0000000000000000000000000000000000000005', - 'swh:1:cnt:0000000000000000000000000000000000000007' - ], - 'meta': { - 'nb_edges_accessed': 13 - } - } - TestEndpoints.assert_endpoint_output(actual, expected) - - def test_neighbors(self): - actual = self.client.neighbors( +def test_stats(graph_client): + stats = graph_client.stats() + + assert set(stats.keys()) == {'counts', 'ratios', 'indegree', + 'outdegree'} + + assert set(stats['counts'].keys()) == {'nodes', 'edges'} + assert set(stats['ratios'].keys()) == {'compression', 'bits_per_node', + 'bits_per_edge', 'avg_locality'} + assert set(stats['indegree'].keys()) == {'min', 'max', 'avg'} + assert set(stats['outdegree'].keys()) == {'min', 'max', 'avg'} + + assert stats['counts']['nodes'] == 21 + assert stats['counts']['edges'] == 23 + assert isinstance(stats['ratios']['compression'], float) + assert isinstance(stats['ratios']['bits_per_node'], float) + assert isinstance(stats['ratios']['bits_per_edge'], float) + assert isinstance(stats['ratios']['avg_locality'], float) + assert stats['indegree']['min'] == 0 + assert stats['indegree']['max'] == 3 + assert isinstance(stats['indegree']['avg'], float) + assert stats['outdegree']['min'] == 0 + assert stats['outdegree']['max'] == 3 + assert isinstance(stats['outdegree']['avg'], float) + + +def test_leaves(graph_client): + actual = list(graph_client.leaves( + 'swh:1:ori:0000000000000000000000000000000000000021' + )) + expected = [ + 'swh:1:cnt:0000000000000000000000000000000000000001', + 'swh:1:cnt:0000000000000000000000000000000000000004', + 'swh:1:cnt:0000000000000000000000000000000000000005', + 'swh:1:cnt:0000000000000000000000000000000000000007' + ] + assert set(actual) == set(expected) + + +def test_neighbors(graph_client): + actual = list(graph_client.neighbors( + 'swh:1:rev:0000000000000000000000000000000000000009', + direction='backward' + )) + expected = [ + 'swh:1:snp:0000000000000000000000000000000000000020', + 'swh:1:rel:0000000000000000000000000000000000000010', + 'swh:1:rev:0000000000000000000000000000000000000013' + ] + assert set(actual) == set(expected) + + +def test_visit_nodes(graph_client): + actual = list(graph_client.visit_nodes( + 'swh:1:rel:0000000000000000000000000000000000000010', + edges='rel:rev,rev:rev' + )) + expected = [ + 'swh:1:rel:0000000000000000000000000000000000000010', + 'swh:1:rev:0000000000000000000000000000000000000009', + 'swh:1:rev:0000000000000000000000000000000000000003' + ] + assert set(actual) == set(expected) + + +def test_visit_paths(graph_client): + actual = list(graph_client.visit_paths( + 'swh:1:snp:0000000000000000000000000000000000000020', + edges='snp:*,rev:*')) + actual = [tuple(path) for path in actual] + expected = [ + ( + 'swh:1:snp:0000000000000000000000000000000000000020', 'swh:1:rev:0000000000000000000000000000000000000009', - direction='backward' - ) - expected = { - 'result': [ - 'swh:1:snp:0000000000000000000000000000000000000020', - 'swh:1:rel:0000000000000000000000000000000000000010', - 'swh:1:rev:0000000000000000000000000000000000000013' - ], - 'meta': { - 'nb_edges_accessed': 3 - } - } - TestEndpoints.assert_endpoint_output(actual, expected) - - def test_stats(self): - stats = self.client.stats() - - assert set(stats.keys()) == {'counts', 'ratios', 'indegree', - 'outdegree'} - - assert set(stats['counts'].keys()) == {'nodes', 'edges'} - assert set(stats['ratios'].keys()) == {'compression', 'bits_per_node', - 'bits_per_edge', 'avg_locality'} - assert set(stats['indegree'].keys()) == {'min', 'max', 'avg'} - assert set(stats['outdegree'].keys()) == {'min', 'max', 'avg'} - - assert stats['counts']['nodes'] == 21 - assert stats['counts']['edges'] == 23 - assert isinstance(stats['ratios']['compression'], float) - assert isinstance(stats['ratios']['bits_per_node'], float) - assert isinstance(stats['ratios']['bits_per_edge'], float) - assert isinstance(stats['ratios']['avg_locality'], float) - assert stats['indegree']['min'] == 0 - assert stats['indegree']['max'] == 3 - assert isinstance(stats['indegree']['avg'], float) - assert stats['outdegree']['min'] == 0 - assert stats['outdegree']['max'] == 3 - assert isinstance(stats['outdegree']['avg'], float) - - def test_visit_nodes(self): - actual = self.client.visit_nodes( - 'swh:1:rel:0000000000000000000000000000000000000010', - edges='rel:rev,rev:rev' - ) - expected = { - 'result': [ - 'swh:1:rel:0000000000000000000000000000000000000010', - 'swh:1:rev:0000000000000000000000000000000000000009', - 'swh:1:rev:0000000000000000000000000000000000000003' - ], - 'meta': { - 'nb_edges_accessed': 4 - } - } - TestEndpoints.assert_endpoint_output(actual, expected) - - def test_visit_paths(self): - actual = self.client.visit_paths( - 'swh:1:snp:0000000000000000000000000000000000000020', - edges='snp:*,rev:*') - actual['result'] = [tuple(path) for path in actual['result']] - expected = { - 'result': [ - ( - 'swh:1:snp:0000000000000000000000000000000000000020', - 'swh:1:rev:0000000000000000000000000000000000000009', - 'swh:1:rev:0000000000000000000000000000000000000003', - 'swh:1:dir:0000000000000000000000000000000000000002' - ), - ( - 'swh:1:snp:0000000000000000000000000000000000000020', - 'swh:1:rev:0000000000000000000000000000000000000009', - 'swh:1:dir:0000000000000000000000000000000000000008' - ), - ( - 'swh:1:snp:0000000000000000000000000000000000000020', - 'swh:1:rel:0000000000000000000000000000000000000010' - ) - ], - 'meta': { - 'nb_edges_accessed': 10 - } - } - TestEndpoints.assert_endpoint_output(actual, expected) - - def test_walk(self): - actual = self.client.walk( - 'swh:1:dir:0000000000000000000000000000000000000016', 'rel', - edges='dir:dir,dir:rev,rev:*', - direction='backward', - traversal='bfs' + 'swh:1:rev:0000000000000000000000000000000000000003', + 'swh:1:dir:0000000000000000000000000000000000000002' + ), + ( + 'swh:1:snp:0000000000000000000000000000000000000020', + 'swh:1:rev:0000000000000000000000000000000000000009', + 'swh:1:dir:0000000000000000000000000000000000000008' + ), + ( + 'swh:1:snp:0000000000000000000000000000000000000020', + 'swh:1:rel:0000000000000000000000000000000000000010' ) - expected = { - 'result': [ - 'swh:1:dir:0000000000000000000000000000000000000016', - 'swh:1:dir:0000000000000000000000000000000000000017', - 'swh:1:rev:0000000000000000000000000000000000000018', - 'swh:1:rel:0000000000000000000000000000000000000019' - ], - 'meta': { - 'nb_edges_accessed': 3 - } - } - TestEndpoints.assert_endpoint_output(actual, expected) + ] + assert set(actual) == set(expected) + + +def test_walk(graph_client): + actual = list(graph_client.walk( + 'swh:1:dir:0000000000000000000000000000000000000016', 'rel', + edges='dir:dir,dir:rev,rev:*', + direction='backward', + traversal='bfs' + )) + expected = [ + 'swh:1:dir:0000000000000000000000000000000000000016', + 'swh:1:dir:0000000000000000000000000000000000000017', + 'swh:1:rev:0000000000000000000000000000000000000018', + 'swh:1:rel:0000000000000000000000000000000000000019' + ] + assert set(actual) == set(expected)