Improve GPU compatibility, RDNA2 kernels and startup diagnostics

- Match render/compute adapters and gate RR models by GPU capabilities
- Add RDNA2 native texture kernels and fix FP16 rounding
- Harden model builds, cache validation and packaging
- Add startup tracing, timeouts, documentation and regression tests
This commit is contained in:
ayo committed 2026-10-19 11:36:40 -04:00
1 parent ca93d0fbf7
commit 2558fcbd39
32 files changed
+1131 -81

No files matched your search

+149
View File
@@ -0,0 +1,149 @@
"""GPU identity and model capability decisions without a GPU or Wine process."""
from pathlib import Path
import subprocess
import tempfile
import unittest
ROOT = Path(__file__).resolve().parents[1]
class GpuCompatibilityTests(unittest.TestCase):
def compile_run(self, source, cxx=False):
with tempfile.TemporaryDirectory() as t:
executable = Path(t) / 'check'
result = subprocess.run(['c++' if cxx else 'cc', '-x', 'c++' if cxx else 'c',
'-std=c++17' if cxx else '-std=gnu11', '-Wall', '-Wextra', '-Werror',
'-pthread',
'-I', str(ROOT / 'tools'), '-o', str(executable), '-'], input=source,
capture_output=True, text=True)
self.assertEqual(result.returncode, 0, result.stderr)
subprocess.run([str(executable)], check=True, timeout=5)
def test_pci_identity_is_not_a_vendor_id_or_gpu_size_guess(self):
self.compile_run(r'''
#include "d4r_gpu_identity.h"
#include <assert.h>
int main(void) {
assert(d4r_same_pci("0000:03:00.0", "00000000:03:00.0"));
assert(!d4r_same_pci("0000:03:00.0", "0000:04:00.0"));
assert(!d4r_same_pci("0000:03:00.0", "0001:03:00.0"));
assert(!d4r_same_pci("0000:03:00.0garbage", "0000:03:00.0"));
assert(d4r_pci_matches_kfd("0000:03:00.0", 0, 0x300));
assert(!d4r_pci_matches_kfd("0000:03:00.0", 0, 0x400));
assert(!d4r_pci_matches_kfd("0000:03:00.0", 1, 0x300));
}
''')
def test_bridge_maps_nonzero_compute_ordinal_and_only_its_luid(self):
source = (ROOT / 'tools/wine_nvcuda_bridge.c').read_text()
selection = source[source.index('static CUresult selected_cuda_device('):
source.index('static void create_default_zluda_context(')]
luid = source[source.index('CUresult WINAPI cuDeviceGetLuid('):
source.index('CUresult WINAPI cuDeviceGetAttribute(')]
self.compile_run(r'''
#include "d4r_gpu_identity.h"
#include <assert.h>
#include <stdlib.h>
#include <string.h>
#define WINAPI
typedef int CUresult;
typedef int CUdevice;
typedef int (*CUDEVICEGETCOUNT_FN)(int*);
typedef int (*CUDEVICEGET_FN)(int*, int);
typedef int (*CUDEVICEGETLUID_FN)(char*, unsigned*, int);
enum { CUDA_SUCCESS=0, CUDA_ERROR_NOT_INITIALIZED=3, CUDA_ERROR_NOT_SUPPORTED=801 };
static const char* render = "0000:03:00.0";
static int devices = 2;
static unsigned GetEnvironmentVariableA(const char* key, char* out, unsigned size) {
(void)key; snprintf(out, size, "%s", render); return (unsigned)strlen(render);
}
static void tracef(const char* fmt, ...) { (void)fmt; }
static int count(int* n) { *n=devices; return 0; }
static int get(int* d,int n) { *d=100+n; return 0; }
static int pci(char* out,int size,int device) {
snprintf(out, (size_t)size, "%s", device==101 ? "00000000:03:00.0" : "0000:04:00.0"); return 0;
}
static int original_luid(char* out,unsigned* mask,int device) { (void)device; memset(out,0,8);*mask=0;return 0; }
static void* find_zluda_symbol(const char* name) {
if (!strcmp(name,"cuDeviceGetCount")) return (void*)count;
if (!strcmp(name,"cuDeviceGet")) return (void*)get;
if (!strcmp(name,"cuDeviceGetPCIBusId")) return (void*)pci;
return (void*)original_luid;
}
static int missing(const char* name) { (void)name; return 999; }
static unsigned get_process_u32(const char* name,unsigned fallback) {
(void)fallback;
return strstr(name,"LOW") ? 0x3f2 : strstr(name,"HIGH") ? 0 : 1;
}
''' + selection + luid + r'''
int main(void) {
assert(d4r_pci_matches_kfd("0000:03:00.0",0,0x300));
int selected=-1; assert(selected_cuda_device(&selected)==0 && selected==101);
char id[8]; unsigned mask=9;
assert(cuDeviceGetLuid(id,&mask,100)==0 && mask==0);
assert(cuDeviceGetLuid(id,&mask,101)==0 && mask==1);
unsigned low=0;memcpy(&low,id,4);assert(low==0x3f2);
render="0000:05:00.0";assert(selected_cuda_device(&selected)==801);
render="";assert(selected_cuda_device(&selected)==801);
devices=1;assert(selected_cuda_device(&selected)==0 && selected==100);
}
''')
def test_rr_model_cannot_reach_unsupported_fp8_pipelines(self):
self.compile_run(r'''
#include "d4r_engine_compat.h"
#include <cassert>
int main() {
const char* fp8 = "D4RR0001\npreset 4\nstage enc0 x.spv\n";
const char* wide = "D4RR0001\npreset 4\narithmetic gfx11\nstage enc0 x.spv\n";
assert(!d4r_rr_model_compatible(fp8, true, false));
assert(!d4r_rr_model_compatible(wide, false, false));
assert(d4r_rr_model_compatible(wide, true, false));
assert(d4r_rr_model_compatible(fp8, true, true));
assert(!d4r_rr_model_compatible("D4RR0002\npreset 6\n", true, false));
assert(!d4r_rr_model_compatible("D4RR0001\narithmetic unknown\n", true, true));
VkCooperativeMatrixPropertiesKHR p{};
p.MSize=p.NSize=p.KSize=16; p.scope=VK_SCOPE_SUBGROUP_KHR;
p.AType=p.BType=VK_COMPONENT_TYPE_FLOAT16_KHR;
p.CType=p.ResultType=VK_COMPONENT_TYPE_FLOAT32_KHR;
assert(d4r_matrix_tuple(p, VK_COMPONENT_TYPE_FLOAT16_KHR));
p.KSize=32; assert(!d4r_matrix_tuple(p, VK_COMPONENT_TYPE_FLOAT16_KHR));
}
''', cxx=True)
def test_existing_context_is_preserved_and_bootstrap_binds_each_thread(self):
text = (ROOT / 'tools/wine_nvcuda_bridge.c').read_text()
ensure = text[text.index('static void ensure_context(void)'):text.index('/* D4R_NGX_STREAM_PRIORITY')]
self.compile_run(r'''
#include <assert.h>
#include <pthread.h>
#include <stdint.h>
#include <string.h>
typedef int CUresult;
typedef void* CUcontext;
typedef CUresult (*CUCTX_CURRENT_FN)(CUcontext);
enum { CUDA_SUCCESS=0 };
static pthread_once_t context_once=PTHREAD_ONCE_INIT;
static int context_bootstrap_result=3, bootstrap_calls=0;
static _Thread_local int context_setup_result=3;
static _Thread_local void* active;
static void* cuda_context;
static int current(void** out) { *out=active;return 0; }
static int set(void* context) { active=context;return 0; }
static void* find_zluda_symbol(const char* key) {
return !strcmp(key,"cuCtxGetCurrent") ? (void*)current : (void*)set;
}
static void create_default_zluda_context(void) {
++bootstrap_calls;cuda_context=(void*)(uintptr_t)0x456;context_bootstrap_result=0;
}
''' + ensure + r'''
static void* worker(void* ignored) {
(void)ignored;ensure_context();assert(active==cuda_context && context_setup_result==0);return NULL;
}
int main(void) {
active=(void*)(uintptr_t)0x123;ensure_context();assert(active==(void*)(uintptr_t)0x123 && bootstrap_calls==0);
pthread_t a;assert(!pthread_create(&a,NULL,worker,NULL));pthread_join(a,NULL);
assert(bootstrap_calls==1);ensure_context();assert(active==(void*)(uintptr_t)0x123);
assert(!pthread_create(&a,NULL,worker,NULL));pthread_join(a,NULL);assert(bootstrap_calls==1);
}
''')
+96
View File
@@ -0,0 +1,96 @@
"""Model builds must time out, retain failures, and reject incomplete/corrupt artifacts."""
import contextlib
import importlib.util
import io
import json
import os
from pathlib import Path
import subprocess
import sys
import tempfile
import unittest
ROOT = Path(__file__).resolve().parents[1]
spec = importlib.util.spec_from_file_location('model_tools', ROOT / 'packaging/model_tools.py')
model_tools = importlib.util.module_from_spec(spec)
spec.loader.exec_module(model_tools)
class ModelBuildTests(unittest.TestCase):
def test_installer_key_tracks_model_arithmetic_options(self):
text = (ROOT / 'install.sh').read_text()
key = text[text.index('key() {'):text.index('have() {')]
with tempfile.TemporaryDirectory() as t:
root = Path(t)
(root / 'tools').mkdir()
(root / 'tools/compiler.py').write_text('compiler fixture')
for name in ('nvngx_dlss.dll', 'nvngx_dlssd.dll'): (root / name).write_bytes(b'DLL fixture')
env = {k: v for k, v in os.environ.items() if not k.startswith(('D4R_RR_', 'K_', 'M_'))}
command = ['bash', '-c', 'RUNTIME=$1\n' + key + '\nkey 0', '_', t]
baseline = subprocess.check_output(command, env=env)
for name in ('K_DEFS_ENC0', 'M_EXACT', 'D4R_RR_MIX'):
self.assertNotEqual(baseline, subprocess.check_output(command, env=dict(env, **{name: '1'})))
def test_child_exit_status_and_timeout_are_preserved(self):
with tempfile.TemporaryDirectory() as t, contextlib.redirect_stdout(io.StringIO()), contextlib.redirect_stderr(io.StringIO()):
log = Path(t) / 'capture.log'
self.assertEqual(model_tools.run(log, 5, [sys.executable, '-c', 'print("failure detail");raise SystemExit(37)']), 37)
self.assertIn('failure detail', log.read_text())
self.assertEqual(model_tools.run(log, .15, [sys.executable, '-c', 'import time;time.sleep(60)']), 124)
self.assertTrue(log.exists())
def test_manifest_rejects_missing_modified_and_extra_files(self):
with tempfile.TemporaryDirectory() as t:
directory = Path(t)
(directory / 'model.json').write_text('{}')
weights = directory / 'weights.bin'
weights.write_bytes(b'weights')
model_tools.manifest(directory, write=True)
model_tools.manifest(directory)
weights.write_bytes(b'corrupt')
with self.assertRaises(ValueError): model_tools.manifest(directory)
weights.unlink()
with self.assertRaises(ValueError): model_tools.manifest(directory)
weights.write_bytes(b'weights')
(directory / 'unexpected.spv').write_bytes(b'extra')
with self.assertRaises(ValueError): model_tools.manifest(directory)
def test_capture_requires_completed_frame_and_all_layers(self):
import numpy as np
with tempfile.TemporaryDirectory() as t:
directory = Path(t)
pixels = np.ones((720, 1280, 4), dtype=np.float16)
pixels.tofile(directory / 'out.raw')
replay = directory / 'replay'
replay.mkdir()
for layer in ('enc0', 'enc1', 'enc2', 'enc3', 'enc4', 'dec5', 'dec4', 'dec3', 'dec2', 'dec1', 'dec0'):
kernel = 'dltss_pwin_' + layer + '_layer'
d = replay / ('replay-000001-' + kernel)
d.mkdir()
(d / 'manifest.txt').write_text(f'kernel {kernel}\nargs 8\nalloc 0 0x1000 4\npointer 0 0 0\n')
(d / 'args.bin').write_bytes(bytes(8))
(d / 'alloc-0.bin').write_bytes(bytes(4))
model_tools.capture(directory, 'k')
(d / 'alloc-0.bin').write_bytes(b'')
with self.assertRaises(ValueError): model_tools.capture(directory, 'k')
(d / 'alloc-0.bin').write_bytes(bytes(4))
pixels[0, 0, 0] = np.nan
pixels.tofile(directory / 'out.raw')
with self.assertRaises(ValueError): model_tools.capture(directory, 'k')
def test_dump_does_not_accept_a_failed_child_with_a_replay_marker(self):
text = (ROOT / 'packaging/build_engine_models.sh').read_text()
function = text[text.index(' dump() {'):text.index(" echo 'Building the Super Resolution")]
with tempfile.TemporaryDirectory() as t:
root = Path(t)
(root / 'proton').mkdir(); (root / 'app').mkdir()
proton = root / 'proton/proton'
proton.write_text('#!/bin/sh\nmkdir -p "$D4R_CUDA_REPLAY_DUMP_DIR/replay-000001-dltss_pwin_dec0_layer"\nexit 37\n')
proton.chmod(0o755)
prefix = 'set -euo pipefail\nWORK=$1; APP=$WORK/app; PROTON=$WORK/proton; STEAM_ROOT=$WORK; RUNTIME=$WORK\n'
prefix += 'MODEL_TOOLS=$2; winpath() { printf "%s" "$1"; }; fail() { echo "$*" >&2; exit 1; }\n'
result = subprocess.run(['bash', '-c', prefix + function + '\ndump k 11 ignored\n', '_', t,
str(ROOT / 'packaging/model_tools.py')], capture_output=True, text=True, timeout=10)
self.assertNotEqual(result.returncode, 0)
self.assertIn('capture process failed', result.stderr)
self.assertTrue((root / 'k/run.log').exists())
+193
View File
@@ -0,0 +1,193 @@
"""RDNA2 (gfx103x) native hiluma kernels: build gating, packaging allowlist and the f16 rounding contract.
Run: python3 -m unittest tests.test_rdna2_native_output -v
The compile checks need ROCm's clang (D4R_ROCM_DIR, default /opt/rocm) and are skipped without it. The full
texture build additionally needs the extracted DLSS 310.7 PTX (kernels/extracted/ptx) and
D4R_RUN_KERNEL_BUILD_TESTS=1, since it compiles all twelve kernels.
Arithmetic equality with ZLUDA's compile is checked in the harness (docs/native-kernels.md), not here.
"""
import os
from pathlib import Path
import re
import shutil
import subprocess
import tempfile
import unittest
ROOT = Path(__file__).resolve().parents[1]
ROCM = Path(os.environ.get("D4R_ROCM_DIR", "/opt/rocm"))
CLANG = ROCM / "lib/llvm/bin/clang++"
PTX_DIR = ROOT / "kernels/extracted/ptx"
TOOLS = [shutil.which(t) for t in ("llvm-readelf", "llvm-objdump")]
OUTPUTS = {f"hiluma_engine_output_{d}_{mv}_hdr_max_v2_rel" for d in ("depthinv", "depthreg") for mv in ("mvhi", "mvlo")}
INPUTS = {f"hiluma_engine_input_{d}_{mv}_{r}_v2_rel"
for d in ("depthinv", "depthreg") for mv in ("mvhi", "mvlo") for r in ("hdr", "ldr")}
# cvt.rn.f16.f32 of an fma (and the packed form of a product) of widened f16 values: LLVM would fold the
# conversion into v_fma_mixlo/mixhi_f16, which rounds once instead of twice
TINY_PTX = """.version 8.0
.target sm_80
.address_size 64
.visible .entry tiny(
.param .align 8 .b8 tiny_param_0[16]
)
{
.reg .b16 %rs<6>;
.reg .f32 %f<8>;
.reg .b32 %r<3>;
.reg .b64 %rd<3>;
ld.param.u64 %rd1, [tiny_param_0];
cvta.to.global.u64 %rd2, %rd1;
ld.global.u16 %rs2, [%rd2+8];
ld.global.u16 %rs3, [%rd2+10];
ld.global.u16 %rs4, [%rd2+12];
cvt.f32.f16 %f3, %rs2;
cvt.f32.f16 %f4, %rs3;
cvt.f32.f16 %f5, %rs4;
fma.rn.f32 %f2, %f3, %f4, %f5;
cvt.rn.f16.f32 %rs1, %f2;
mul.rn.f32 %f6, %f3, %f4;
cvt.rn.f16x2.f32 %r1, %f6, %f2;
st.global.u16 [%rd2], %rs1;
st.global.u32 [%rd2+4], %r1;
ret;
}
"""
def run(*args, **kwargs):
return subprocess.run([str(a) for a in args], text=True, capture_output=True, **kwargs)
class Rdna2BuildGating(unittest.TestCase):
def setUp(self):
self.temp = tempfile.TemporaryDirectory()
self.addCleanup(self.temp.cleanup)
self.root = Path(self.temp.name)
# a compiler that fails if anything reaches it: the refusals must come first
compiler = self.root / "rocm/lib/llvm/bin/clang++"
compiler.parent.mkdir(parents=True)
compiler.write_text("#!/bin/sh\nexit 99\n")
compiler.chmod(0o755)
def test_wmma_families_are_refused_on_every_rdna2_target(self):
for arch in ("gfx1030", "gfx1032", "gfx1036"):
for family in ("m", "l", "rr", "rrswin"):
env = dict(os.environ, D4R_ROCM_DIR=str(self.root / "rocm"), D4R_GPU_ARCH=arch)
result = run("bash", ROOT / "kernels/build.sh", family, self.root / "out", env=env)
self.assertEqual(result.returncode, 2, (arch, family, result.stderr))
self.assertIn("need WMMA", result.stderr)
def test_rdna2_texture_build_does_not_need_zluda(self):
env = dict(os.environ, D4R_ROCM_DIR=str(self.root / "rocm"), D4R_GPU_ARCH="gfx1032",
D4R_DLSS_DLL=str(self.root / "missing.dll"), D4R_DLSS_PTX_DIR=str(self.root / "no-ptx"))
env.pop("D4R_ZLUDA_EMIT", None)
result = run("bash", ROOT / "kernels/build.sh", "tex", self.root / "out", env=env)
self.assertNotIn("D4R_ZLUDA_EMIT", result.stderr)
self.assertIn("missing extracted PTX directory", result.stderr)
def test_rdna3_texture_build_still_needs_zluda(self):
env = dict(os.environ, D4R_ROCM_DIR=str(self.root / "rocm"), D4R_GPU_ARCH="gfx1101",
D4R_DLSS_DLL=str(self.root / "missing.dll"))
env.pop("D4R_ZLUDA_EMIT", None)
result = run("bash", ROOT / "kernels/build.sh", "tex", self.root / "out", env=env)
self.assertNotEqual(result.returncode, 0)
self.assertIn("D4R_ZLUDA_EMIT", result.stderr)
class Rdna2Packaging(unittest.TestCase):
def rdna2_branch(self, path):
text = (ROOT / path).read_text()
start = text.index('if [[ "$arch" == gfx103* ]]; then')
return text[start:text.index("\n fi\n", start)]
def test_package_copies_only_the_validated_hiluma_kernels(self):
branch = self.rdna2_branch("scripts/package_release.sh")
copies = re.findall(r'"\$tex_dir"/([^ ;]+\.hsaco)', branch)
self.assertEqual(sorted(copies), ["hiluma_engine_input_*_v2_rel.hsaco",
"hiluma_engine_output_*_hdr_max_v2_rel.hsaco"])
self.assertIn('"$VARIANT" != clean', branch) # the no-NVIDIA zip ships no PTX-derived code
self.assertNotIn("build.sh\" m", branch)
# the accuracy set gets K's layers only (ZLUDA keeps compiling hiluma there)
self.assertNotRegex(branch, r'PREFER_ACCURACY=1[^\n]*\n?[^\n]*build.sh" tex')
self.assertIn("kernel_manifest.py", branch)
def test_release_builder_builds_rdna2_fast_textures_only(self):
text = (ROOT / "scripts/build_release_glibc241.sh").read_text()
start = text.index('if [[ "$arch" == gfx103* ]]; then')
branch = text[start:text.index("fi\n", start)]
self.assertIn("D4R_PREFER_ACCURACY=0", branch)
self.assertNotIn("D4R_PREFER_ACCURACY=1", branch)
self.assertIn('kernels/build.sh" tex', branch)
self.assertIn("return 0", branch)
class F16RoundingContract(unittest.TestCase):
def translate(self, directory):
ptx = directory / "tiny.ptx"
ptx.write_text(TINY_PTX)
result = run("python3", ROOT / "kernels/native/ptx2hip.py", ptx, "tiny", check=True)
return result.stdout
def test_translator_routes_f16_conversions_through_f2h_rn(self):
with tempfile.TemporaryDirectory() as temp:
source = self.translate(Path(temp))
self.assertEqual(source.count("f2h_rn("), 3)
self.assertNotRegex(source, r"\(h1\)f\d")
@unittest.skipUnless(CLANG.is_file(), "ROCm clang not found (set D4R_ROCM_DIR)")
def test_no_single_rounding_mix_instructions(self):
with tempfile.TemporaryDirectory() as temp:
temp = Path(temp)
(temp / "tiny.hip").write_text(self.translate(temp))
for arch, wave in (("gfx1032", "32"), ("gfx1101", "64")):
env = dict(os.environ, D4R_ROCM_DIR=str(ROCM), D4R_GPU_ARCH=arch, WAVE=wave)
run("bash", ROOT / "kernels/native/build_native.sh", temp / "tiny.hip", temp / f"{arch}.hsaco",
env=env, check=True)
asm = (temp / f"{arch}.s").read_text()
self.assertNotRegex(asm, r"v_fma_mix(lo|hi)_f16", arch)
# canary: a plain cast in the same kernel is fused, so the check above can fail
(temp / "cast.hip").write_text((temp / "tiny.hip").read_text().replace("f2h_rn(", "(h1)("))
env = dict(os.environ, D4R_ROCM_DIR=str(ROCM), D4R_GPU_ARCH="gfx1032", WAVE="32")
run("bash", ROOT / "kernels/native/build_native.sh", temp / "cast.hip", temp / "cast.hsaco",
env=env, check=True)
self.assertRegex((temp / "cast.s").read_text(), r"v_fma_mix(lo|hi)_f16")
@unittest.skipUnless(os.environ.get("D4R_RUN_KERNEL_BUILD_TESTS") == "1", "set D4R_RUN_KERNEL_BUILD_TESTS=1")
@unittest.skipUnless(CLANG.is_file() and PTX_DIR.is_dir() and all(TOOLS), "needs ROCm clang, LLVM tools and PTX")
class Rdna2TextureBuild(unittest.TestCase):
@classmethod
def setUpClass(cls):
cls.temp = tempfile.TemporaryDirectory()
cls.out = Path(cls.temp.name) / "gfx1032"
env = dict(os.environ, D4R_ROCM_DIR=str(ROCM), D4R_GPU_ARCH="gfx1032", D4R_DLSS_DLL="unused",
D4R_DLSS_PTX_DIR=str(PTX_DIR))
run("bash", ROOT / "kernels/build.sh", "tex", cls.out, env=env, check=True)
@classmethod
def tearDownClass(cls):
cls.temp.cleanup()
def notes(self, name):
return run(TOOLS[0], "--notes", "-h", self.out / f"{name}.hsaco", check=True).stdout
def test_exactly_the_validated_kernels(self):
self.assertEqual({p.stem for p in self.out.glob("*.hsaco")}, OUTPUTS | INPUTS)
def test_code_objects_are_wave32_gfx1032_without_scratch_or_wmma(self):
for name in OUTPUTS | INPUTS:
notes = self.notes(name)
self.assertIn("gfx1032", notes, name)
self.assertRegex(notes, r"\.wavefront_size:\s+32", name)
self.assertRegex(notes, r"\.private_segment_fixed_size:\s+0\b", name)
code = run(TOOLS[1], "-d", "--mcpu=gfx1032", self.out / f"{name}.hsaco", check=True).stdout
self.assertNotRegex(code, r"v_wmma|v_fma_mix(lo|hi)_f16|v_dot\d", name)
def test_output_kernels_use_wgp_mode(self):
for name in OUTPUTS:
asm = (self.out / f"{name}.s").read_text()
self.assertIn(".amdhsa_workgroup_processor_mode 1", asm, name)
if __name__ == "__main__":
unittest.main()
+14
View File
@@ -60,6 +60,20 @@ class ShimConfigTests(unittest.TestCase):
result = subprocess.run(cmd, env=env, text=True, capture_output=True)
self.assertEqual(result.returncode, 2)
def test_auto_engine_detects_models_without_overriding_environment(self):
with tempfile.TemporaryDirectory() as t:
root = Path(t)
ini = root / 'd4r.ini'
ini.write_text('[Engine]\nEnabled = auto\n')
(root / 'engine/rr-d-gfx11').mkdir(parents=True)
env = {k: v for k, v in os.environ.items() if not k.startswith('D4R_')}
cmd = ['python3', str(ROOT / 'scripts/d4r_config.py'), '--config', str(ini)]
output = subprocess.check_output(cmd, env=env, text=True)
self.assertIn('export D4R_ENGINE=1\n', output)
self.assertIn('export D4R_ENGINE_K=0\n', output)
output = subprocess.check_output(cmd, env=dict(env, D4R_ENGINE='0'), text=True)
self.assertNotIn('export D4R_ENGINE=', output)
def test_launcher_marker_poll_defaults_overrides_and_validation(self):
with tempfile.TemporaryDirectory() as temporary:
ini = Path(temporary) / 'd4r.ini'