aim-web2.1.0rc9
This commit is contained in:
@@ -0,0 +1,53 @@
|
||||
"""Run release tests in bounded file groups using a disposable test environment.
|
||||
|
||||
Set AIM_TEST_CORE_ROOT to a pristine Core source extraction, not a live controller.
|
||||
Artifacts go to --out; this does not install dependencies or contact real hosts.
|
||||
"""
|
||||
from __future__ import annotations
|
||||
import argparse
|
||||
import concurrent.futures
|
||||
import json
|
||||
import os
|
||||
from pathlib import Path
|
||||
import re
|
||||
import subprocess
|
||||
import sys
|
||||
import time
|
||||
|
||||
|
||||
def main() -> int:
|
||||
parser=argparse.ArgumentParser(description=__doc__)
|
||||
parser.add_argument('--out',type=Path,required=True)
|
||||
parser.add_argument('--workers',type=int,default=3,choices=range(1,5))
|
||||
parser.add_argument('--timeout',type=int,default=180)
|
||||
args=parser.parse_args()
|
||||
if args.timeout<30:parser.error('timeout must be at least 30 seconds')
|
||||
core=os.environ.get('AIM_TEST_CORE_ROOT','')
|
||||
if not core or not (Path(core)/'scripts/aimctl.py').is_file():
|
||||
parser.error('Set AIM_TEST_CORE_ROOT to the pristine supplied Core source extraction.')
|
||||
root=Path(__file__).resolve().parents[1];args.out.mkdir(parents=True,exist_ok=True)
|
||||
env={**os.environ,'PYTEST_DISABLE_PLUGIN_AUTOLOAD':'1'}
|
||||
def run(path: Path) -> dict:
|
||||
started=time.monotonic()
|
||||
try:
|
||||
p=subprocess.run([sys.executable,'-m','pytest',str(path),'-q','-p','no:cacheprovider'],
|
||||
cwd=root,env=env,capture_output=True,text=True,timeout=args.timeout)
|
||||
output=p.stdout+p.stderr;code=p.returncode
|
||||
except subprocess.TimeoutExpired as exc:
|
||||
output=(exc.stdout or b'').decode(errors='replace') if isinstance(exc.stdout,bytes) else (exc.stdout or '')
|
||||
output+='\nRelease test-file timeout; not a pass.\n';code=124
|
||||
(args.out/(path.stem+'.log')).write_text(output)
|
||||
summary=next((line for line in reversed(output.splitlines()) if re.search(r'\d+ (passed|failed|skipped|error)',line)),'No test summary')
|
||||
counts={k:int(n) for n,k in re.findall(r'(\d+) (passed|failed|skipped|errors?)',summary)}
|
||||
return {'file':path.name,'returncode':code,'seconds':round(time.monotonic()-started,3),'summary':summary,'counts':counts}
|
||||
with concurrent.futures.ThreadPoolExecutor(max_workers=args.workers) as pool:
|
||||
results=list(pool.map(run,sorted((root/'tests').glob('test_*.py'))))
|
||||
totals={k:sum(x['counts'].get(k,0) for x in results) for k in ('passed','failed','skipped','error','errors')}
|
||||
value={'mode':'independent bounded test-file groups','files':results,'totals':totals,
|
||||
'all_passed':all(x['returncode']==0 for x in results),'native_managed_hosts_tested':False}
|
||||
(args.out/'results.json').write_text(json.dumps(value,indent=2)+'\n')
|
||||
for result in results:print(result['file']+': '+result['summary'],flush=True)
|
||||
print(json.dumps(totals),flush=True)
|
||||
return 0 if value['all_passed'] else 1
|
||||
|
||||
if __name__=='__main__':raise SystemExit(main())
|
||||
Reference in New Issue
Block a user