"""Real short-duration process tests; these never stand in for an eight-hour run.""" import importlib.util import json import os from pathlib import Path import subprocess import sys import tempfile import time import unittest ROOT = Path(__file__).resolve().parents[1] TOOL = ROOT / 'tools/native_soak.py' class VerdictTests(unittest.TestCase): def test_eight_hour_liveness_is_not_full_dr_acceptance(self): # Classification logic only: this test supplies a number, not duration evidence. spec = importlib.util.spec_from_file_location('native_soak', TOOL) module = importlib.util.module_from_spec(spec) spec.loader.exec_module(module) report = module.verdict(28800, True) self.assertTrue(report['duration_liveness_pass']) self.assertFalse(report['full_dr_acceptance']) self.assertTrue(report['resource_growth_review_required']) self.assertFalse(report['no_deadlock_claim']) for seconds, progress, gap in [(28799, True, False), (28800, False, False), (28800, True, True)]: self.assertFalse(module.verdict(seconds, progress, sampling_gap=gap)['eight_hour_pass']) @unittest.skipUnless(sys.platform.startswith('linux'), 'Linux /proc required') class NativeSoakTests(unittest.TestCase): def run_monitor(self, directory, *args): output = Path(directory) / 'evidence' process = subprocess.run([sys.executable, str(TOOL), '--output', str(output), '--duration-seconds', '0.45', '--sample-interval-seconds', '0.05', *args], capture_output=True, text=True, timeout=8) self.assertTrue((output / 'summary.json').exists(), process.stderr) return process, json.loads((output / 'summary.json').read_text()), output def test_late_increment_cannot_erase_elapsed_progress_deadline(self): # Fault-injection unit test only; this clock is never duration evidence. from unittest.mock import patch spec = importlib.util.spec_from_file_location('native_soak_deadline', TOOL) module = importlib.util.module_from_spec(spec) spec.loader.exec_module(module) with tempfile.TemporaryDirectory() as directory: progress = Path(directory) / 'counter.json' progress.write_text('{"completed":0}') output = Path(directory) / 'evidence' clock = [0.0] def advance(seconds): clock[0] += seconds # The new completion is only visible after the .12-second deadline. if clock[0] >= .16: progress.write_text('{"completed":1}') with patch.object(module.time, 'monotonic', side_effect=lambda: clock[0]), \ patch.object(module.time, 'sleep', side_effect=advance), patch('builtins.print'): code = module.main(['--pid', str(os.getpid()), '--output', str(output), '--duration-seconds', '.16', '--sample-interval-seconds', '.08', '--max-progress-gap-seconds', '.12', '--progress-file', str(progress)]) report = json.loads((output / 'summary.json').read_text()) self.assertEqual(code, 1) self.assertEqual(report['status'], 'failed') self.assertIn('progress deadline', report['reason']) def test_real_short_workload_is_incomplete_and_one_process(self): with tempfile.TemporaryDirectory() as directory: progress = Path(directory) / 'progress.json' script = ('import json,os,time,sys; from pathlib import Path; p=Path(sys.argv[1]); n=0\n' 'while True:\n n+=1; q=p.with_suffix(".tmp"); q.write_text(json.dumps({"completed":n})); os.replace(q,p); time.sleep(.025)\n') process, report, output = self.run_monitor(directory, '--progress-file', str(progress), '--max-progress-gap-seconds', '0.25', '--command', sys.executable, '-u', '-c', script, str(progress)) self.assertEqual(process.returncode, 2, process.stderr) self.assertEqual(report['status'], 'qualification_incomplete') self.assertFalse(report['eight_hour_pass']) self.assertGreaterEqual(report['elapsed_seconds'], .45) self.assertTrue(report['progress_observed']) samples = [json.loads(line) for line in (output / 'samples.jsonl').read_text().splitlines()] self.assertGreater(len(samples), 3) self.assertEqual(len({(s['pid'], s['start_ticks']) for s in samples}), 1) self.assertGreater(samples[-1]['rss_bytes'], 0) self.assertIn('cpu_seconds', samples[-1]) self.assertIn('fd_count', samples[-1]) self.assertFalse(report['no_deadlock_claim']) def test_attached_process_is_left_alive_and_no_metric_not_pass(self): with tempfile.TemporaryDirectory() as directory: child = subprocess.Popen([sys.executable, '-c', 'import time; time.sleep(10)']) try: process, report, _ = self.run_monitor(directory, '--pid', str(child.pid)) self.assertEqual(process.returncode, 2, process.stderr) self.assertIsNone(child.poll()) self.assertFalse(report['progress_observed']) self.assertFalse(report['no_deadlock_claim']) finally: child.terminate() child.wait(timeout=3) def test_early_exit_fails(self): with tempfile.TemporaryDirectory() as directory: process, report, _ = self.run_monitor(directory, '--command', sys.executable, '-c', 'pass') self.assertEqual(process.returncode, 1) self.assertEqual(report['status'], 'failed') self.assertIn('process', report['reason']) def test_stalled_metric_fails_even_if_process_alive(self): with tempfile.TemporaryDirectory() as directory: progress = Path(directory) / 'progress.json' progress.write_text('{"completed":1}') process, report, _ = self.run_monitor(directory, '--progress-file', str(progress), '--max-progress-gap-seconds', '.15', '--command', sys.executable, '-c', 'import time; time.sleep(10)') self.assertEqual(process.returncode, 1) self.assertIn('progress', report['reason']) def test_journal_size_is_observed(self): with tempfile.TemporaryDirectory() as directory: journal = Path(directory) / 'goals.log' journal.write_bytes(b'goal\n') process, report, output = self.run_monitor(directory, '--journal', str(journal), '--command', sys.executable, '-c', 'import time; time.sleep(10)') self.assertEqual(process.returncode, 2) sample = json.loads((output / 'samples.jsonl').read_text().splitlines()[-1]) self.assertEqual(sample['journal_bytes'][str(journal.resolve())], 5) if __name__ == '__main__': unittest.main()