Make the base escape mission independent and add native objectives
This commit is contained in:
+11
-1
@@ -5,7 +5,7 @@ import json,re,subprocess,sys
|
||||
ROOT=Path(__file__).resolve().parents[1]
|
||||
BASE='58D0FB3206B6F859'
|
||||
def check():
|
||||
ids={};graph={};resources={};paths={}
|
||||
ids={};graph={};resources={};paths={};owners={}
|
||||
for p in sorted((ROOT/'addons').glob('*/addon.gproj')):
|
||||
text=p.read_text();g=re.search(r'GUID "([A-F0-9]{16})"',text)[1]
|
||||
assert g not in ids,f'Duplicate addon GUID: {p}'
|
||||
@@ -25,6 +25,13 @@ def check():
|
||||
assert p.with_suffix('')==ROOT/'addons'/addon/path, f'Resource path mismatch: {p}'
|
||||
assert p.with_suffix('').is_file(),f'Missing resource for {p}'
|
||||
resources[g]=path;paths[path]=g
|
||||
owners[g]=addon
|
||||
def visible(g):
|
||||
result={ids[g]}
|
||||
for dep in graph[g]:
|
||||
if dep!=BASE:result.update(visible(dep))
|
||||
return result
|
||||
visibility={ids[g]:visible(g) for g in ids}
|
||||
vanilla=json.loads((ROOT/'docs/vanilla-resources.json').read_text())
|
||||
for p in (ROOT/'addons').rglob('*'):
|
||||
if not p.is_file() or p.suffix not in ['.c','.et','.conf','.ent','.layer','.layout','.emat']:continue
|
||||
@@ -32,6 +39,9 @@ def check():
|
||||
assert not re.search(r'\b(?:ESCT\w*|SCRTC_\w+|SCRT_\w+|EAI_\w+)\b',s),f'Original ecosystem dependency: {p}'
|
||||
for g,path in re.findall(r'\{([A-F0-9]{16})\}([^"\n]+)',s):
|
||||
assert resources.get(g)==path or vanilla.get(path)==g,f'Unresolved/mismatched resource in {p}: {g} {path}'
|
||||
if g in owners:
|
||||
addon=p.relative_to(ROOT/'addons').parts[0]
|
||||
assert owners[g] in visibility[addon],f'Resource outside declared dependency graph in {p}: {g} {path}'
|
||||
if p.suffix in ['.et','.conf','.ent','.layout','.emat']:
|
||||
assert p.with_name(p.name+'.meta').exists(),f'Missing metadata: {p}'
|
||||
print(f'PASS: {len(ids)} isolated addons, acyclic dependencies, {len(resources)} unique resources, exact vanilla references')
|
||||
|
||||
@@ -13,6 +13,7 @@ p.add_argument('--profile', type=Path, required=True, help='Separate disposable
|
||||
p.add_argument('--smoke', choices=['victory', 'lost-helicopter', 'player-death', 'radio-network', 'hq-response', 'mortar-inventory'], default='victory')
|
||||
p.add_argument('--rounds', type=int, default=2)
|
||||
p.add_argument('--timeout', type=int, default=360)
|
||||
p.add_argument('--project', choices=['core', 'chat'], default='chat', help='Core-only base or full companion integration')
|
||||
a = p.parse_args()
|
||||
steam, compat, profile = a.steam.resolve(), a.compat.resolve(), a.profile.resolve()
|
||||
if not 1 <= a.rounds <= 10 or not 60 <= a.timeout <= 1800:
|
||||
@@ -46,7 +47,11 @@ for proc in Path('/proc').iterdir():
|
||||
if profile_arg.encode() in args and any(x.endswith(b'ArmaReforgerWorkbenchSteamDiag.exe') for x in args):
|
||||
p.error('Another Workbench already uses this test profile')
|
||||
except (FileNotFoundError, PermissionError, ProcessLookupError): pass
|
||||
command = ['gamescope', '--backend', 'headless', '-W', '1280', '-H', '720', '-w', '1280', '-h', '720', '-r', '30', '--', str(proton), 'run', str(wb/'ArmaReforgerWorkbenchSteamDiag.exe'), '-gproj', 'Z:'+str(ROOT/'addons/chat/addon.gproj'), '-addonsDir', 'Z:'+str(ROOT/'addons'), '-profile', profile_arg, '-logconsole', '-wbModule=WorldEditor', '-load', 'Worlds/ERR_Everon.ent', '-plugin=ERR_ValidateWorldPlugin', '-errPlay', '1', '-errSmoke', '1', '-errSmokeOutcome', a.smoke, '-noThrow']
|
||||
command = ['gamescope', '--backend', 'headless', '-W', '1280', '-H', '720', '-w', '1280', '-h', '720', '-r', '30', '--', str(proton), 'run', str(wb/'ArmaReforgerWorkbenchSteamDiag.exe'), '-gproj', 'Z:'+str(ROOT/'addons'/a.project/'addon.gproj'), '-addonsDir', 'Z:'+str(ROOT/'addons'), '-profile', profile_arg, '-logconsole', '-wbModule=WorldEditor', '-load', 'Worlds/ERR_Everon.ent', '-plugin=ERR_ValidateWorldPlugin', '-errPlay', '1', '-errSmoke', '1', '-errSmokeOutcome', a.smoke, '-noThrow']
|
||||
if a.project == 'core':
|
||||
if a.smoke != 'victory': p.error('Core-only fixture currently supports victory')
|
||||
command[command.index('-plugin=ERR_ValidateWorldPlugin')] = '-plugin=ERR_ValidateBaseWorldPlugin'
|
||||
command[command.index('-errSmoke')] = '-errBaseSmoke'
|
||||
env = os.environ.copy()
|
||||
env.update(STEAM_COMPAT_DATA_PATH=str(compat), STEAM_COMPAT_CLIENT_INSTALL_PATH=str(steam), WINEPREFIX=str(compat/'pfx'))
|
||||
# A test-only sink prevents startup sounds; a stream check also catches a saved
|
||||
@@ -115,7 +120,7 @@ with (profile/'headless-launch.log').open('w') as output:
|
||||
# Gamescope's reaper can outlive its parent and ignores SIGTERM.
|
||||
try: os.killpg(child.pid, signal.SIGKILL)
|
||||
except ProcessLookupError: pass
|
||||
summary = {'result':result, 'session':str(session) if session else None, 'rounds_required':a.rounds, 'display':'headless Gamescope 1280x720/30Hz', 'dedicated_server':False, 'audio':'silent sink and per-test-stream mute'}
|
||||
summary = {'result':result, 'session':str(session) if session else None, 'rounds_required':a.rounds, 'project':a.project, 'display':'headless Gamescope 1280x720/30Hz', 'dedicated_server':False, 'audio':'silent sink and per-test-stream mute'}
|
||||
if session:
|
||||
(session/'runner.json').write_text(json.dumps(summary, indent=2)+'\n')
|
||||
shutil.copyfile(profile/'headless-launch.log', session/'headless-launch.log')
|
||||
|
||||
@@ -10,17 +10,22 @@ p.add_argument('--profile',required=True,type=Path)
|
||||
p.add_argument('--smoke',choices=['victory','lost-helicopter','player-death', 'radio-network', 'hq-response', 'mortar-inventory'],help='Opt-in Workbench test; use a disposable profile')
|
||||
p.add_argument('--execute',action='store_true',help='Deliberately execute the command; Workbench opens a window')
|
||||
p.add_argument('--visible-output',help='User-approved spare display; required when executing Workbench')
|
||||
p.add_argument('--project',choices=['core','chat'],default='chat')
|
||||
a=p.parse_args()
|
||||
if a.smoke and a.mode!='workbench':p.error('Scripted smoke is compiled only for Workbench')
|
||||
if a.execute and a.mode=='workbench' and not a.visible_output:
|
||||
p.error('Workbench opens windows. Select/configure a spare display and supply --visible-output first.')
|
||||
command=[str(a.exe),'-addonsDir',str(ROOT/'addons'),'-profile',str(a.profile),'-logconsole']
|
||||
if a.mode=='workbench':
|
||||
command+=['-gproj',str(ROOT/'addons/chat/addon.gproj'),'-wbModule=WorldEditor','-load','Worlds/ERR_Everon.ent']
|
||||
command+=['-gproj',str(ROOT/'addons'/a.project/'addon.gproj'),'-wbModule=WorldEditor','-load','Worlds/ERR_Everon.ent']
|
||||
if a.smoke:command+=['-plugin','ERR_ValidateWorldPlugin','-errPlay','1','-errSmoke','1','-errSmokeOutcome',a.smoke]
|
||||
if a.project=='core' and a.smoke:
|
||||
if a.smoke!='victory':p.error('Core-only fixture currently supports victory')
|
||||
command[command.index('ERR_ValidateWorldPlugin')]='ERR_ValidateBaseWorldPlugin'
|
||||
command[command.index('-errSmoke')]='-errBaseSmoke'
|
||||
else:
|
||||
if 'server' not in a.exe.name.lower():p.error('Use an actual dedicated server binary for background tests.')
|
||||
command+=['-addons','ERR_Chat','-server','Worlds/ERR_Everon.ent','-maxFPS','60','-logStats','1000']
|
||||
command+=['-addons',{'core':'ERR_Core','chat':'ERR_Chat'}[a.project],'-server','Worlds/ERR_Everon.ent','-maxFPS','60','-logStats','1000']
|
||||
print(json.dumps(command,indent=2))
|
||||
if a.execute:
|
||||
if not a.exe.is_file():p.error('Executable not found')
|
||||
|
||||
Reference in New Issue
Block a user