110 lines
3.3 KiB
Python
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())
|