Files
hse-2026/homework/02-grpc-messenger/tests/main.py
T
2026-09-17 16:34:16 +03:00

110 lines
3.3 KiB
Python

import argparse
import os
import pathlib
import signal
import subprocess
import sys
import pytest
SCRIPT_DIR = pathlib.Path(__file__).parent.resolve()
SUITE_TIMEOUT_S = 120
class PassedCounter:
def __init__(self):
self.passed = 0
def pytest_report_teststatus(self, report, config):
if report.when == 'call' and report.passed:
self.passed += 1
def suite_score(passed, expected, maximum, exit_code=pytest.ExitCode.OK):
return maximum if exit_code == pytest.ExitCode.OK and passed == expected else 0
def run_suite(filename, expected, maximum):
# Each component has its own deadline so a timeout cannot discard other scores.
process = subprocess.Popen(
[sys.executable, '-u', str(pathlib.Path(__file__).resolve()),
'--suite', str(SCRIPT_DIR / filename), str(expected)],
start_new_session=os.name == 'posix',
)
try:
exit_code = process.wait(timeout=SUITE_TIMEOUT_S)
except subprocess.TimeoutExpired:
if os.name == 'posix':
# Include grpcurl and other subprocesses started by this test suite.
try:
os.killpg(process.pid, signal.SIGKILL)
except ProcessLookupError:
pass # The suite may have exited just after wait() timed out.
else:
process.kill()
process.wait()
print(f'{filename} exceeded {SUITE_TIMEOUT_S} seconds; component score is zero.', flush=True)
return 0
return maximum if exit_code == 0 else 0
def suite_exit_code(filename, expected):
counter = PassedCounter()
exit_code = pytest.main(['-vs', filename], plugins=[counter])
return 0 if suite_score(counter.passed, expected, 1, exit_code) else 1
def component_enabled(environment_name):
return os.environ.get(environment_name, '1') == '1'
def parse_args():
parser = argparse.ArgumentParser()
parser.add_argument(
'--component',
choices=('all', 'proto', 'server', 'client'),
default='all',
)
parser.add_argument('--suite', nargs=2, metavar=('FILE', 'EXPECTED'), help=argparse.SUPPRESS)
return parser.parse_args()
def main():
args = parse_args()
if args.suite is not None:
return suite_exit_code(args.suite[0], int(args.suite[1]))
component = args.component
score = 0
if component in ('all', 'proto'):
proto_score = run_suite('test_proto.py', expected=1, maximum=2)
score += proto_score
print(f'Proto: {proto_score}/2')
print()
if component in ('all', 'server'):
if component_enabled('MESSENGER_SERVER_TESTS_ENABLED'):
server_score = run_suite('test_server.py', expected=4, maximum=4)
else:
server_score = 0
print('Server tests were not run because the server image did not build.')
score += server_score
print(f'Server: {server_score}/4')
print()
if component in ('all', 'client'):
if component_enabled('MESSENGER_CLIENT_TESTS_ENABLED'):
client_score = run_suite('test_client.py', expected=3, maximum=4)
else:
client_score = 0
print('Client tests were not run because the client image did not build.')
score += client_score
print(f'Client: {client_score}/4')
print(f'\nSCORE: {score}')
if __name__ == '__main__':
sys.exit(main())