"""Tests for the Windows worker port (win/). Runs on Linux; no GPU, no Windows host. Covers the three pieces the wave doc says must hold before first hardware contact: 1. ffmpeg-qsv.py argument rewriting matches the validated Linux adapter's contract and fails closed on unreviewed commands (exit 78). 2. the capability-query passthrough list matches what the shipped runner actually generates (verified in the 0.2.0 bundle). 3. pt-worker-win.py Meter/Admission semantics are byte-identical to the Linux controller's for every decision input (states, hysteresis, floors). """ import importlib.util import json import subprocess import sys import tempfile import time import unittest from pathlib import Path WIN = Path(__file__).resolve().parents[2] / 'win' ROOT = Path(__file__).resolve().parents[2] def load(name, path): spec = importlib.util.spec_from_file_location(name, path) module = importlib.util.module_from_spec(spec) spec.loader.exec_module(module) return module qsv = load('qsv_win', WIN / 'ffmpeg-qsv.py') win = load('worker_win', WIN / 'pt-worker-win.py') linux = load('worker_linux', ROOT / 'pt-worker.py') PROBE = {'streams': [{'codec_type': 'video', 'codec_name': 'h264', 'pix_fmt': 'yuv420p', 'width': 1920, 'height': 1080}], 'format': {'duration': '10.0'}} class RewriteTests(unittest.TestCase): def setUp(self): self.old_state, self.old_probe = qsv.STATE, qsv.probe self.tmp = tempfile.TemporaryDirectory() qsv.STATE = Path(self.tmp.name) def stub_probe(path): # no real ffprobe.exe on this Linux host name = str(path).lower() if name.endswith(('.png', '.mjpeg', '.webp')): return {'streams': [{**PROBE['streams'][0], 'codec_name': 'png', 'pix_fmt': 'rgba'}], 'format': {'duration': '0'}} return PROBE qsv.probe = stub_probe def tearDown(self): qsv.STATE, qsv.probe = self.old_state, self.old_probe self.tmp.cleanup() def rewritten(self, args, data=PROBE): out, _ = qsv.rewrite(args, input_probe=data) return out def test_h264_web_video_becomes_qsv_pipeline(self): out = self.rewritten(['-i', 'in.mp4', '-vf', 'scale=w=-2:h=720', '-c:v', 'libx264', '-preset', 'veryfast', '-crf', '18', '-maxrate', '1500K', '-bufsize', '3000K', '-g', '96', '-x264-params', 'nofast-pskip=1', '-movflags', '+faststart', 'out.mp4']) text = ' '.join(out) self.assertIn('-hwaccel qsv', text) self.assertIn('-hwaccel_output_format qsv', text) self.assertIn('-c:v h264_qsv', text) self.assertIn('scale_qsv=w=1280:h=720', text) self.assertIn('-preset medium', text) self.assertIn('-b:v 1500K', text) # runner's rate cap kept, CRF dropped self.assertIn('-g 96', text) # runner's GOP kept self.assertNotIn('libx264', text) self.assertNotIn('-crf', text) def test_hevc_source_uses_hevc_qsv_decode(self): data = {**PROBE, 'streams': [{**PROBE['streams'][0], 'codec_name': 'hevc', 'pix_fmt': 'yuv420p10le'}]} out = self.rewritten(['-i', 'in.mkv', '-c:v', 'libx264', 'out.mp4'], data) self.assertIn('-c:v hevc_qsv', ' '.join(out)) def test_no_vf_gets_default_scale_qsv(self): out = self.rewritten(['-i', 'in.mp4', '-c:v', 'libx264', 'out.mp4']) self.assertIn('scale_qsv=w=1920:h=1080', ' '.join(out)) def test_audio_only_passthrough(self): out, info = qsv.rewrite(['-i', 'in.mp4', '-vn', '-c:a', 'aac', 'out.m4a']) self.assertEqual(info, None) def test_capability_queries_passthrough(self): for args in (['-version'], ['-encoders'], ['-hwaccels']): out, info = qsv.rewrite(args) self.assertEqual((out, info), (args, None)) def test_fail_closed_cpu_or_unknown(self): # Note: -vf contents are validated after the (stubbed) probe, matching # the Linux adapter, which needs the probe to resolve -2 dimensions. for args in (['-i', 'in.mp4', '-c:v', 'mpeg4', 'out.mp4'], ['-i', 'in.mp4', '-c:v', 'libx264', '-filter_complex', 'x', 'out.mp4'], ['-i', 'in.mp4', '-c:v', 'libx264', '-vf', 'scale=w=-2:h=720,hue=s=0', 'out.mp4'], ['-i', 'in.png', '-c:v', 'libx264', '-vf', 'scale=w=-2:h=720', 'out.mp4'], ['-i', 'in.mp4', '-c:v', 'libx264', '-vf', 'scale=w=0:h=720', 'out.mp4']): with self.assertRaises(ValueError): qsv.rewrite(args) def test_refusal_process_exit_78(self): import os env = {**os.environ, 'PT_STATE_DIR': str(self.tmp.name), 'PT_FFPROBE_EXE': 'ffprobe.exe'} p = subprocess.run([sys.executable, str(WIN / 'ffmpeg-qsv.py'), '-i', 'x.mp4', '-c:v', 'libx264', 'o.mp4'], capture_output=True, text=True, env=env) self.assertEqual(p.returncode, 78) def test_events_have_fixed_reasons_only(self): import os env = {**os.environ, 'PT_STATE_DIR': str(self.tmp.name), 'PT_FFPROBE_EXE': 'ffprobe.exe'} p = subprocess.run([sys.executable, str(WIN / 'ffmpeg-qsv.py'), '-i', 'x.mp4', '-c:v', 'libx264', 'o.mp4'], capture_output=True, text=True, env=env) events = [json.loads(line) for line in (Path(self.tmp.name) / 'events.jsonl').read_text().splitlines()] self.assertEqual(events[0]['kind'], 'gpu_failure') self.assertEqual(events[0]['reason'], 'ValueError') self.assertNotIn('x.mp4', p.stderr) class RunnerContractTests(unittest.TestCase): """Verified directly in the shipped runner 0.2.0 bundle this session.""" def test_queries_actually_issued_by_runner(self): bundle = Path('/tmp/peertube-worker-inspect/runtime/node_modules/@peertube/peertube-runner' '/dist/peertube-runner.js') if not bundle.exists(): self.skipTest('pinned runner bundle not present') text = bundle.read_text(errors='replace') # Exactly the queries fluent-ffmpeg's capability probe issues in 0.2.0 # (-hwaccels is NOT issued; the shim passes it through anyway, harmlessly). for query in ('-version', '-formats', '-encoders', '-filters', '-codecs'): self.assertIn(query, text) self.assertNotIn('-hwaccels', text) self.assertIn('encodersToTry = this.availableEncoders.encodersToTry', text) self.assertIn('"libx264"', text) def test_spawn_without_shell_and_ffprobe_sibling(self): bundle = Path('/tmp/peertube-worker-inspect/runtime/node_modules/@peertube/peertube-runner' '/dist/peertube-runner.js') if not bundle.exists(): self.skipTest('pinned runner bundle not present') text = bundle.read_text(errors='replace') self.assertNotIn('shell: true', text) # EINVAL on .cmd/.bat shims self.assertIn('utils.isWindows ? "ffprobe.exe" : "ffprobe"', text) self.assertIn('!utils.isWindows', text) # `nice` skipped on win32 class AdmissionParityTests(unittest.TestCase): """The Windows controller must decide identically to the Linux controller.""" def setUp(self): self.tmp = tempfile.TemporaryDirectory() self.old_win, self.old_lin = win.STATE, linux.STATE win.STATE = linux.STATE = Path(self.tmp.name) self.now = time.time() def tearDown(self): win.STATE, linux.STATE = self.old_win, self.old_lin self.tmp.cleanup() def meter_with_transfers(self, module, mbps): meter = module.Meter() for direction in ('download', 'upload'): meter.consume({'kind': 'transfer', 'time': self.now, 'ok': True, 'direction': direction, 'bytes': int(mbps * 1e6 / 8), 'seconds': 1}) return meter def sample(self, **overrides): base = {'tailscale_path': 'direct', 'latency_ms': 5, 'network_ok': True, 'registered': True, 'gpu_ready': True, 'gpu_tested_at': self.now, 'gpu_accessible': True, 'on_ac': True, 'disk_free_bytes': 100 * 1024 ** 3, 'temperature_c': 50, 'throttle_counts': {}, 'active_jobs': 0} base.update(overrides) return base def parity(self, overrides, control=None, decisions=1): control = control or {'paused': False} outputs = [] for module in (linux, win): policy = module.Admission({}) meter = self.meter_with_transfers(module, 50) for _ in range(decisions): outputs.append(policy.decide(self.sample(**overrides), meter, control, self.now)) self.assertEqual(outputs[0], outputs[1], f'Linux and Windows controllers diverge on {overrides}: {outputs}') return outputs[0] def test_happy_path(self): self.parity({}, decisions=4) def test_power_loss_drains_identically(self): self.assertEqual(self.parity({'on_ac': False}), 'DRAINING') def test_unknown_power_drains_identically(self): self.assertEqual(self.parity({'on_ac': None}), 'DRAINING') def test_missing_gpu_proof_blocks_identically(self): self.assertEqual(self.parity({'gpu_ready': False, 'gpu_accessible': False}), 'GPU_UNAVAILABLE') def test_gpu_failure_event_after_proof_blocks_identically(self): outputs = [] for module in (linux, win): policy = module.Admission({}) meter = self.meter_with_transfers(module, 50) meter.consume({'kind': 'gpu_failure', 'time': self.now + 1}) outputs.append(policy.decide(self.sample(gpu_tested_at=self.now), meter, {'paused': False}, self.now + 2)) self.assertEqual(outputs[0], 'GPU_UNAVAILABLE') self.assertEqual(outputs[0], outputs[1]) def test_low_disk_and_thermal(self): self.assertEqual(self.parity({'disk_free_bytes': 10 * 1024 ** 3}), 'LOW_DISK') self.assertEqual(self.parity({'temperature_c': 90}), 'THERMAL_THROTTLED') def test_derp_disallowed_and_latency_ceiling(self): self.assertEqual(self.parity({'tailscale_path': 'DERP'}), 'NETWORK_DEGRADED') self.assertEqual(self.parity({'latency_ms': 500}), 'NETWORK_DEGRADED') def test_bandwidth_floor_hysteresis(self): # 5 Mbps < 10 Mbps floor: every sample is bad, both controllers degrade. for module in (linux, win): policy = module.Admission({}) meter = self.meter_with_transfers(module, 5) outputs = [policy.decide(self.sample(), meter, {'paused': False}, self.now) for _ in range(3)] self.assertEqual(outputs, ['NETWORK_DEGRADED'] * 3, module.__name__) def test_recovery_factor(self): outputs = [] for module in (linux, win): policy = module.Admission({}) meter = self.meter_with_transfers(module, 11) outputs.append(policy.decide(self.sample(), meter, {'paused': False}, self.now)) outputs.append(policy.decide(self.sample(), meter, {'paused': False}, self.now)) self.assertEqual(outputs[0], 'NETWORK_DEGRADED') self.assertEqual(outputs[0], outputs[1]) if __name__ == '__main__': unittest.main(verbosity=1)