From 869202e7450105936ae1c25d455d938b21b37625 Mon Sep 17 00:00:00 2001 From: Adriano Marto Reis Date: Fri, 15 Mar 2024 14:12:21 +1000 Subject: [PATCH 1/2] Adding topic inheritance test --- setup.py | 1 + tests/test_topic_inheritance.py | 111 ++++++++++++++++++++++++++++++++ 2 files changed, 112 insertions(+) create mode 100755 tests/test_topic_inheritance.py diff --git a/setup.py b/setup.py index f4e95b08..a2c8280b 100644 --- a/setup.py +++ b/setup.py @@ -147,6 +147,7 @@ extras_require={ "dev": [ "pytest>=6.2", + "pytest-asyncio", "pytest-cov", "pytest-mock", "flake8", diff --git a/tests/test_topic_inheritance.py b/tests/test_topic_inheritance.py new file mode 100755 index 00000000..da82388d --- /dev/null +++ b/tests/test_topic_inheritance.py @@ -0,0 +1,111 @@ +import asyncio +import pytest + +import cyclonedds.idl as idl +import cyclonedds.idl.annotations as annotate + +from dataclasses import dataclass +from cyclonedds.domain import DomainParticipant +from cyclonedds.topic import Topic +from cyclonedds.pub import DataWriter +from cyclonedds.sub import DataReader +from cyclonedds.util import duration + + +@dataclass +@annotate.mutable +@annotate.autoid("sequential") +class Base(idl.IdlStruct, typename="Hierarchy.Base"): + fieldA: str + + +@dataclass +@annotate.mutable +@annotate.autoid("sequential") +class Derived(Base, typename="Hierarchy.Derived"): + fieldB: str + + +@pytest.mark.asyncio +async def test_base_topic(): + ''' + Creates a publisher and a subscriber of the Base topic and checks if the + update sent by the publisher is received by the subscriber. + ''' + base = Base(fieldA='Lorem') + tasks = [ + _subscriber(Base), + _publisher(Base, base), + ] + results = await asyncio.gather(*tasks) + assert results[0] == results[1] + + +@pytest.mark.asyncio +async def test_derived_topic(): + ''' + Creates a publisher and a subscriber of the Derived topic and checks if the + update sent by the publisher is received by the subscriber. + ''' + derived = Derived( + fieldA='Ipsum', + fieldB='Dolor') + + tasks = [ + _subscriber(Derived), + _publisher(Derived, derived), + ] + + results = await asyncio.gather(*tasks) + assert results[0] == results[1] + + +@pytest.mark.asyncio +async def test_base_and_derived_topics(): + ''' + Creates publishers and a subscribers of the Base and Derived topics and + checks if the updates sent by the publishers are received by the + subscribers. + ''' + base = Base(fieldA='Lorem') + derived = Derived( + fieldA='Ipsum', + fieldB='Dolor') + + tasks = [ + # This should not be necessary. I suspect there is a bug in CycloneDDS + _subscriber(Base), + _publisher(Base, base), + + # This is the part of the test that really matters + _subscriber(Derived), + _publisher(Derived, derived), + ] + + results = await asyncio.gather(*tasks) + assert results[0] == results[1] + assert results[2] == results[3] + + +async def _publisher(topicClass, value, timeout=2): + ''' + Publishes an update with a given value. + ''' + participant = DomainParticipant(0) + topic = Topic(participant, topicClass.__name__, topicClass) + writer = DataWriter(participant, topic) + writer.write(value) + await asyncio.sleep(timeout) + return value + + +async def _subscriber(topicClass, timeout=2): + ''' + Receives an update. Raises an exception if no update is received within a + given timeout. + ''' + participant = DomainParticipant(0) + topic = Topic(participant, topicClass.__name__, topicClass) + reader = DataReader(participant, topic) + async for update in reader.read_aiter(timeout=duration(seconds=timeout)): + return update From 7494072f4746fb00634cc26ea56bda4994ea9725 Mon Sep 17 00:00:00 2001 From: Adriano Marto Reis Date: Tue, 2 Apr 2024 09:52:33 +1000 Subject: [PATCH 2/2] Testing "cyclonedds typeof" with derived topic --- tests/test_topic_inheritance.py | 47 ++++++++++++++++++++++++++++++--- 1 file changed, 43 insertions(+), 4 deletions(-) diff --git a/tests/test_topic_inheritance.py b/tests/test_topic_inheritance.py index da82388d..52f30ce0 100755 --- a/tests/test_topic_inheritance.py +++ b/tests/test_topic_inheritance.py @@ -11,7 +11,7 @@ from cyclonedds.sub import DataReader from cyclonedds.util import duration - +BASE_IDL = 'module Hierarchy { @mutable struct Base { string fieldA; }; };' @dataclass @annotate.mutable @annotate.autoid("sequential") @@ -19,6 +19,7 @@ class Base(idl.IdlStruct, typename="Hierarchy.Base"): fieldA: str +DERIVED_IDL = 'module Hierarchy { struct Derived : Base { string fieldB; }; };' @dataclass @annotate.mutable @annotate.autoid("sequential") @@ -73,11 +74,8 @@ async def test_base_and_derived_topics(): fieldB='Dolor') tasks = [ - # This should not be necessary. I suspect there is a bug in CycloneDDS _subscriber(Base), _publisher(Base, base), - - # This is the part of the test that really matters _subscriber(Derived), _publisher(Derived, derived), ] @@ -87,6 +85,21 @@ async def test_base_and_derived_topics(): assert results[2] == results[3] +@pytest.mark.asyncio +async def test_cyclonedds_typeof_command(): + ''' + Executes the command "cyclonedds typeof" and compares the results with the + expected IDL. + ''' + tasks = [ + _subscriber(Base), + _subscriber(Derived), + _type_checker(Base, BASE_IDL), + _type_checker(Derived, DERIVED_IDL), + ] + await asyncio.gather(*tasks) + + async def _publisher(topicClass, value, timeout=2): ''' Publishes an update with a given value. @@ -109,3 +122,29 @@ async def _subscriber(topicClass, timeout=2): reader = DataReader(participant, topic) async for update in reader.read_aiter(timeout=duration(seconds=timeout)): return update + + +async def _type_checker(topicClass, expectedIdl): + ''' + Executes the command "cyclonedds typeof" and compares the result with the + expected IDL. + ''' + def _normalise(text): + text = text.replace('\n', '') + text = ' '.join(text.split()) + return text + + import subprocess + result = subprocess.run( + ['cyclonedds', 'typeof', topicClass.__name__, '--suppress-progress-bar'], + stdout = subprocess.PIPE, + check = True) + outputLines = result.stdout.decode().splitlines() + + # Skips the first lines of the output (As defined in participant...) + firstIdlLine = 0 + while firstIdlLine < len(outputLines) and not outputLines[firstIdlLine].startswith('module'): + firstIdlLine += 1 + actualIdl = '\n'.join(outputLines[firstIdlLine:]) + + assert _normalise(actualIdl) == _normalise(expectedIdl)