#!/usr/bin/env python3 """Тест задачи 07: поднимает сервер, проверяет эхо на нескольких соединениях и SIGTERM.""" import os import signal import socket import subprocess import sys import time HERE = os.path.dirname(os.path.abspath(__file__)) BIN = os.path.join(HERE, "solution") failures = 0 def check(cond, name): global failures if cond: print(f"ok {name}") else: print(f"FAIL {name}") failures += 1 def free_port(): s = socket.socket() s.bind(("127.0.0.1", 0)) p = s.getsockname()[1] s.close() return p def main(): port = free_port() proc = subprocess.Popen([BIN, str(port)], stdout=subprocess.PIPE, stderr=subprocess.PIPE) up = False for _ in range(100): # до 5 секунд на старт if proc.poll() is not None: break try: s = socket.create_connection(("127.0.0.1", port), timeout=0.3) s.close() up = True break except OSError: time.sleep(0.05) check(up, "сервер принял соединение и слушает порт") if up: # три одновременных соединения, большие и разорванные сообщения socks = [] for i in range(3): s = socket.create_connection(("127.0.0.1", port), timeout=2) s.settimeout(5) socks.append(s) payloads = [b"hello\n", b"x" * 70000, b"chunk-" + bytes(range(32, 127))] for s, p in zip(socks, payloads): s.sendall(p) ok = True for i, (s, p) in enumerate(zip(socks, payloads)): got = b"" while len(got) < len(p): chunk = s.recv(65536) if not chunk: break got += chunk if got != p: ok = False print(f" соединение {i}: получено {len(got)} из {len(p)} байт") check(ok, "эхо на трёх одновременных соединениях (включая 70 КБ)") # эхо в двух шагах по одному соединению s = socks[0] s.sendall(b"part1") first = s.recv(5) s.sendall(b"part2") second = b"" while len(second) < 5: chunk = s.recv(5 - len(second)) if not chunk: break second += chunk check(first == b"part1" and second == b"part2", "эхо по частям (несколько циклов read/write)") # клиент закрывается — сервер не падает for s in socks: s.close() time.sleep(0.3) check(proc.poll() is None, "сервер жив после закрытия всех клиентов") # новое соединение после закрытия старых try: s = socket.create_connection(("127.0.0.1", port), timeout=2) s.settimeout(3) s.sendall(b"again") check(s.recv(5) == b"again", "новое соединение после отключения старых") s.close() except OSError as e: check(False, f"новое соединение после отключения старых ({e})") proc.send_signal(signal.SIGTERM) try: rc = proc.wait(timeout=5) check(rc == 0, f"аккуратное завершение по SIGTERM (exit={rc})") except subprocess.TimeoutExpired: proc.kill() check(False, "аккуратное завершение по SIGTERM (не завершился за 5 с)") if failures: err = proc.stderr.read().decode(errors="replace")[:2000] if err: print("--- stderr сервера ---") print(err) print(f"\n{'FAILURES: %d' % failures if failures else 'ALL PASS'}") sys.exit(1 if failures else 0) if __name__ == "__main__": main()