|
| 1 | +"""Test suite for pub (peer) > router > sub (client) scenarios.""" |
| 2 | +import os |
| 3 | +from signal import SIGINT |
| 4 | +import subprocess |
| 5 | +import sys |
| 6 | +import time |
| 7 | +import re |
| 8 | + |
| 9 | +# Specify the directory for the binaries |
| 10 | +DIR_EXAMPLES = "build/examples" |
| 11 | + |
| 12 | + |
| 13 | +def pub_and_sub(pub_args, sub_args, pub_first=True): |
| 14 | + """ |
| 15 | + Run a publisher and subscriber test. |
| 16 | +
|
| 17 | + Parameters: |
| 18 | + pub_args (str): Arguments passed to the publisher binary. |
| 19 | + sub_args (str): Arguments passed to the subscriber binary. |
| 20 | + pub_first (bool): If True, start publisher first; otherwise start subscriber first. |
| 21 | +
|
| 22 | + Returns: |
| 23 | + int: 0 if the test passes, 1 if it fails. |
| 24 | + """ |
| 25 | + test_status = 0 |
| 26 | + |
| 27 | + # Expected z_pub status |
| 28 | + z_pub_expected_status = 0 |
| 29 | + |
| 30 | + # Expected z_sub output & status |
| 31 | + z_sub_expected_status = 0 |
| 32 | + z_sub_expected_pattern = re.compile( |
| 33 | + r">> \[Subscriber\] Received \('demo/example/zenoh-pico-pub': '\[.*?\] Pub from Pico!'\)" |
| 34 | + ) |
| 35 | + |
| 36 | + z_pub_command = f"stdbuf -oL -eL ./{DIR_EXAMPLES}/z_pub {pub_args} -n 10" |
| 37 | + z_sub_command = f"stdbuf -oL -eL ./{DIR_EXAMPLES}/z_sub {sub_args} -n 1" |
| 38 | + |
| 39 | + print("PUB CMD:", z_pub_command) |
| 40 | + print("SUB CMD:", z_sub_command) |
| 41 | + |
| 42 | + if pub_first: |
| 43 | + print("Start publisher") |
| 44 | + # Start z_pub |
| 45 | + z_pub_process = subprocess.Popen( |
| 46 | + z_pub_command, |
| 47 | + shell=True, |
| 48 | + stdin=subprocess.PIPE, |
| 49 | + stdout=subprocess.PIPE, |
| 50 | + stderr=subprocess.PIPE, |
| 51 | + text=True, |
| 52 | + ) # pylint: disable=consider-using-with |
| 53 | + |
| 54 | + # Give publisher time to start |
| 55 | + time.sleep(2) |
| 56 | + |
| 57 | + print("Start subscriber") |
| 58 | + # Start z_sub |
| 59 | + z_sub_process = subprocess.Popen( |
| 60 | + z_sub_command, |
| 61 | + shell=True, |
| 62 | + stdin=subprocess.PIPE, |
| 63 | + stdout=subprocess.PIPE, |
| 64 | + stderr=subprocess.PIPE, |
| 65 | + text=True, |
| 66 | + start_new_session=True, |
| 67 | + ) # pylint: disable=consider-using-with |
| 68 | + else: |
| 69 | + print("Start subscriber") |
| 70 | + # Start z_sub first |
| 71 | + z_sub_process = subprocess.Popen( |
| 72 | + z_sub_command, |
| 73 | + shell=True, |
| 74 | + stdin=subprocess.PIPE, |
| 75 | + stdout=subprocess.PIPE, |
| 76 | + stderr=subprocess.PIPE, |
| 77 | + text=True, |
| 78 | + start_new_session=True, |
| 79 | + ) # pylint: disable=consider-using-with |
| 80 | + |
| 81 | + # Give subscriber time to start |
| 82 | + time.sleep(2) |
| 83 | + |
| 84 | + print("Start publisher") |
| 85 | + # Then start z_pub |
| 86 | + z_pub_process = subprocess.Popen( |
| 87 | + z_pub_command, |
| 88 | + shell=True, |
| 89 | + stdin=subprocess.PIPE, |
| 90 | + stdout=subprocess.PIPE, |
| 91 | + stderr=subprocess.PIPE, |
| 92 | + text=True, |
| 93 | + ) |
| 94 | + |
| 95 | + # Wait for z_pub to finish |
| 96 | + z_pub_process.wait() |
| 97 | + |
| 98 | + print("Stop subscriber") |
| 99 | + if z_sub_process.poll() is None: |
| 100 | + # send SIGINT to group (safe because of start_new_session=True) |
| 101 | + z_sub_process_gid = os.getpgid(z_sub_process.pid) |
| 102 | + os.killpg(z_sub_process_gid, SIGINT) |
| 103 | + |
| 104 | + print("Check publisher status") |
| 105 | + # Check the exit status of z_pub |
| 106 | + z_pub_status = z_pub_process.returncode |
| 107 | + if z_pub_status == z_pub_expected_status: |
| 108 | + print("z_pub status valid") |
| 109 | + else: |
| 110 | + print(f"z_pub status invalid, expected: {z_pub_expected_status}, received: {z_pub_status}") |
| 111 | + test_status = 1 |
| 112 | + |
| 113 | + print("Check subscriber status & output") |
| 114 | + # Check the exit status of z_sub |
| 115 | + z_sub_status = z_sub_process.returncode |
| 116 | + if z_sub_status == z_sub_expected_status: |
| 117 | + print("z_sub status valid") |
| 118 | + else: |
| 119 | + print(f"z_sub status invalid, expected: {z_sub_expected_status}, received: {z_sub_status}") |
| 120 | + test_status = 1 |
| 121 | + |
| 122 | + # Check the output of z_sub |
| 123 | + if z_sub_expected_pattern.search(z_sub_process.stdout.read()): |
| 124 | + print("z_sub output valid") |
| 125 | + else: |
| 126 | + print("z_sub output invalid:") |
| 127 | + test_status = 1 |
| 128 | + |
| 129 | + # Return value |
| 130 | + return test_status |
| 131 | + |
| 132 | +def test_tcp_unicast(locator, pub_first=True): |
| 133 | + """Run TCP unicast pub/sub test.""" |
| 134 | + print(f"*** TCP Unicast Test (pub_first={pub_first}) ***") |
| 135 | + pub_args = f"-m peer -e {locator}" |
| 136 | + sub_args = f"-m client -e {locator}" |
| 137 | + |
| 138 | + return pub_and_sub(pub_args, sub_args, pub_first) |
| 139 | + |
| 140 | +def test_udp_multicast(tcp_locator, udp_locator, pub_first=True): |
| 141 | + """Run UDP multicast pub/sub test.""" |
| 142 | + print(f"*** UDP Multicast Test (pub_first={pub_first}) ***") |
| 143 | + pub_args = f"-m peer -l {udp_locator}" |
| 144 | + sub_args = f"-m client -e {tcp_locator}" |
| 145 | + |
| 146 | + return pub_and_sub(pub_args, sub_args, pub_first) |
| 147 | + |
| 148 | +if __name__ == "__main__": |
| 149 | + EXIT_STATUS = 0 |
| 150 | + |
| 151 | + _tcp_unicast = None |
| 152 | + _udp_multicast = None |
| 153 | + |
| 154 | + args = sys.argv[1:] |
| 155 | + |
| 156 | + for arg in args: |
| 157 | + if arg.startswith("tcp/"): |
| 158 | + _tcp_unicast = arg |
| 159 | + elif arg.startswith("udp/"): |
| 160 | + _udp_multicast = arg |
| 161 | + |
| 162 | + print("TCP unicast locator:", _tcp_unicast) |
| 163 | + print("UDP multicast locator:", _udp_multicast) |
| 164 | + |
| 165 | + if _tcp_unicast is not None: |
| 166 | + test_tcp_unicast(_tcp_unicast, True) |
| 167 | + test_tcp_unicast(_tcp_unicast, False) |
| 168 | + else: |
| 169 | + print("No TCP unicast locator provided, skipping test_tcp_unicast.") |
| 170 | + |
| 171 | + if _tcp_unicast is not None and _udp_multicast is not None: |
| 172 | + test_udp_multicast(_tcp_unicast, _udp_multicast, True) |
| 173 | + test_udp_multicast(_tcp_unicast, _udp_multicast, False) |
| 174 | + else: |
| 175 | + print("No TCP unicast or UDP multicast locators provided, skipping test_udp_multicast.") |
| 176 | + |
| 177 | + sys.exit(EXIT_STATUS) |
0 commit comments