initial commit
This commit is contained in:
86
src/archipelago_tester/pipeline/batch/runner.py
Normal file
86
src/archipelago_tester/pipeline/batch/runner.py
Normal file
@@ -0,0 +1,86 @@
|
||||
"""Running the tests the cache could not answer."""
|
||||
|
||||
import threading
|
||||
from concurrent.futures import ThreadPoolExecutor, as_completed
|
||||
|
||||
from archipelago_tester.pipeline.batch.record import TestRecord
|
||||
from archipelago_tester.pipeline.generation.tester import WorldTester
|
||||
|
||||
|
||||
class Runner:
|
||||
"""Tests each outstanding world and persists what it did."""
|
||||
|
||||
def __init__(self, config, paths, store, options, state, tests, tag,
|
||||
image, roms_fingerprint):
|
||||
self.config = config
|
||||
self.paths = paths
|
||||
self.store = store
|
||||
self.options = options
|
||||
self.state = state
|
||||
self.tests = tests
|
||||
self.tag = tag
|
||||
self.tester = WorldTester(config, paths, image)
|
||||
self.roms_fingerprint = roms_fingerprint
|
||||
self.lock = threading.Lock()
|
||||
|
||||
@staticmethod
|
||||
def promotion_name(report, game_name):
|
||||
"""The name Archipelago itself registered this world under.
|
||||
|
||||
Recorded for promotion's collision check, while the report's
|
||||
own "game" becomes the sheet folder name for display. A world
|
||||
that never loaded has no real name, so it falls back to a
|
||||
marked sheet name - otherwise every such entry would collide
|
||||
under one None key and none would promote.
|
||||
"""
|
||||
return report.get("game") or f"unverified:{game_name}"
|
||||
|
||||
def store_record(self, key, record):
|
||||
"""Persist after every test, not just at the end, so a killed
|
||||
run does not lose the tests it already completed.
|
||||
"""
|
||||
with self.lock:
|
||||
self.tests[key] = record
|
||||
self.store.save(self.state)
|
||||
|
||||
def test_one(self, game_name, apworld_path, key, current_hash):
|
||||
options = self.options
|
||||
report = self.tester.test(
|
||||
apworld_path=apworld_path,
|
||||
game=None if apworld_path else game_name,
|
||||
output_dir=options["output_directory"],
|
||||
timeout=options["timeout"],
|
||||
companion_range=options["companion_range"],
|
||||
spoiler=options["spoiler"],
|
||||
repeats=options["repeats"],
|
||||
companion_pool=options["pool"],
|
||||
random_repeats=options["random_repeats"],
|
||||
)
|
||||
record = TestRecord(game_name, key,
|
||||
is_core=apworld_path is None)
|
||||
self.store_record(key, record.from_report(
|
||||
report=report,
|
||||
current_hash=current_hash,
|
||||
tag=self.tag,
|
||||
internal_game=self.promotion_name(report, game_name),
|
||||
roms_fingerprint=self.roms_fingerprint,
|
||||
))
|
||||
report["game"] = game_name
|
||||
report["apworld"] = key
|
||||
report["skipped"] = False
|
||||
return report
|
||||
|
||||
def run(self, to_test, total, completed, on_progress):
|
||||
"""Run the outstanding tests, reporting as they land."""
|
||||
results = []
|
||||
reporting = threading.Lock()
|
||||
with ThreadPoolExecutor(max_workers=self.options["jobs"]) as pool:
|
||||
futures = [pool.submit(self.test_one, *args) for args in to_test]
|
||||
for future in as_completed(futures):
|
||||
report = future.result()
|
||||
results.append(report)
|
||||
with reporting:
|
||||
completed += 1
|
||||
if on_progress:
|
||||
on_progress(completed, total, report)
|
||||
return results
|
||||
Reference in New Issue
Block a user