test_measurement_contract.mojo (47226B)
1 """H005A measurement contract tests (ADR-0012 D29, ADR-0014 D34, ADR-0019 D39). 2 3 Drives the governed persistent-process measurement tooling against a real 4 build of the existing product entry point and against controlled child 5 processes, proving that the reproduced H005/R56/R57 defects and the period-10 6 counterexamples now fail the measurement instead of reporting success: 7 8 * MR01 — extra, coalesced, split, unterminated and malformed trailing stdout, 9 early EOF and a nonzero child exit; 10 * MR02 — a valid response or exit that arrives after the one work budget, a 11 sampling subprocess that would outlive it, stderr overflow and a 12 deadline-bounded EINTR retry; 13 * MR03 — repeated failing public measurement calls leave no descriptor or 14 child behind, with successful recovery afterward; 15 * MR04 — source/binary identity drift rejection, delayed-startup timing and 16 truthful per-request/instrumentation accounting. 17 18 This module is test-only tooling. It changes no product policy, schema, 19 dependency or lock. 20 """ 21 22 from std.collections import List 23 from std.testing import TestSuite, assert_equal, assert_true 24 25 import std.os 26 from std.pathlib import Path 27 28 from safe_tempdir import SafeTempDir 29 30 from parent_lifecycle import ( 31 CleanupGuard, 32 now_ms, 33 open_fd_count_checked, 34 owned_pid, 35 ) 36 from stdio_process_helper import ( 37 HYF_PATHS_PROFILE_ENV, 38 HYF_PATHS_REPO_LOCAL_ROOT_ENV, 39 ScopedEnvVar, 40 ) 41 from measurement_process_helper import ( 42 MeasurementFaults, 43 MeasurementSession, 44 build_product_binary, 45 build_status_frame, 46 child_process_count, 47 file_sha256, 48 measure_persistent_process, 49 measurement_poll, 50 measurement_poll_retry, 51 measurement_tooling_files, 52 require_clean_source, 53 run_capture, 54 sample_fd_count, 55 sample_rss_kb, 56 sha256_file_set, 57 source_identity, 58 source_manifest_sha256, 59 tooling_manifest_sha256, 60 validate_status_frame, 61 ) 62 from json import Value, loads 63 64 from hyf_core.request_context import default_request_context 65 from hyf_provider.client import post_max_local_chat_completion 66 from hyf_provider.config import MaxLocalProviderConfig 67 from hyf_provider.result import parse_query_analysis_from_chat_completion 68 from hyf_provider.schema import build_query_rewrite_request_body 69 from max_local_process_helper import spawn_max_local_stub 70 71 72 comptime MEASUREMENT_DEADLINE_MS = 120000 73 comptime WARMUP_FRAMES = 20 74 comptime MEASURED_FRAMES = 200 75 76 # One valid sys.status response for request/trace index 0. 77 comptime STATUS0 = ( 78 '{"version":1,"request_id":"meas-status-0","trace_id":"meas-trace-0",' 79 '"ok":true,"output":{"daemon":"hyfd"}}' 80 ) 81 comptime WRONG_REVISION = "0000000000000000000000000000000000000000" 82 comptime WRONG_DIGEST = ( 83 "0000000000000000000000000000000000000000000000000000000000000000" 84 ) 85 86 87 def _one_response() -> String: 88 """A well-behaved responder: answer exactly one request, then consume the 89 rest of stdin until EOF so the child exits cleanly and closes stdout. 90 """ 91 return ( 92 "IFS= read -r line; printf '%s\\n' '" 93 + STATUS0 94 + "'; while IFS= read -r line; do :; done" 95 ) 96 97 98 def _sh(args_text: String) -> List[String]: 99 var args = List[String]() 100 args.append("-c") 101 args.append(args_text) 102 return args^ 103 104 105 def _multi_response() -> String: 106 """A well-behaved responder for any frame count. 107 108 Request ids/trace ids are deterministic (``meas-status-<index>``), so a 109 counter reproduces the exact expected correlation for each frame in order. 110 """ 111 return ( 112 "i=0\n" 113 "while IFS= read -r line; do\n" 114 'printf \'{"version":1,"request_id":"meas-status-%s",' 115 '"trace_id":"meas-trace-%s","ok":true,' 116 '"output":{"daemon":"hyfd"}}\\n\' "$i" "$i"\n' 117 "i=$((i+1))\n" 118 "done" 119 ) 120 121 122 def _run_sh_measurement( 123 args_text: String, 124 warmup: Int, 125 measured: Int, 126 mut guard: CleanupGuard, 127 rss_sampler: String = "ps", 128 fd_sampler: String = "lsof", 129 deadline_ms: Int = MEASUREMENT_DEADLINE_MS, 130 faults: MeasurementFaults = MeasurementFaults(), 131 ) raises -> MeasurementSession: 132 var argv = _sh(args_text) 133 return measure_persistent_process( 134 ".", 135 "/bin/sh", 136 argv^, 137 warmup, 138 measured, 139 deadline_ms, 140 guard, 141 rss_sampler, 142 fd_sampler, 143 "", 144 "", 145 "", 146 faults, 147 ) 148 149 150 def _run_sh_failure( 151 args_text: String, 152 warmup: Int, 153 measured: Int, 154 mut guard: CleanupGuard, 155 rss_sampler: String = "ps", 156 fd_sampler: String = "lsof", 157 deadline_ms: Int = MEASUREMENT_DEADLINE_MS, 158 faults: MeasurementFaults = MeasurementFaults(), 159 ) -> String: 160 try: 161 _ = _run_sh_measurement( 162 args_text, 163 warmup, 164 measured, 165 guard, 166 rss_sampler, 167 fd_sampler, 168 deadline_ms, 169 faults, 170 ) 171 except e: 172 return String(e) 173 return "" 174 175 176 def _failing_hasher(root: String, mut guard: CleanupGuard) raises -> String: 177 """Owned hasher stand-in that fails without touching live tools or host 178 settings (ADR-0021 MP01). The script is created inside the isolated owned 179 temp root and exits 7 with an explicit stage error. 180 """ 181 var path = root + "/failing-hasher.sh" 182 Path(path).write_text( 183 "#!/bin/sh\nprintf 'failing hasher stage\\n' >&2\nexit 7\n" 184 ) 185 var chmod_args = List[String]() 186 chmod_args.append("+x") 187 chmod_args.append(path) 188 var chmodded = run_capture("chmod", chmod_args^, 20000, guard) 189 assert_equal(chmodded.exit_code, 0) 190 return path^ 191 192 193 def _copy_tooling_files( 194 root: String, mut guard: CleanupGuard, start: Int = 0 195 ) raises: 196 """Copy declared tooling inputs into an isolated owned ``tests`` root. 197 198 ``start`` > 0 deliberately omits the leading declared inputs, producing a 199 present-but-partial input set for the missing-one-input control. 200 """ 201 var tests_dir = root + "/tests" 202 _ = std.os.makedirs(tests_dir, exist_ok=True) 203 var sources = measurement_tooling_files(".") 204 assert_true(len(sources) > start + 1) 205 var cp_args = List[String]() 206 for index in range(start, len(sources)): 207 cp_args.append(sources[index]) 208 cp_args.append(tests_dir) 209 var copied = run_capture("cp", cp_args^, 20000, guard) 210 assert_equal(copied.exit_code, 0) 211 212 213 # ── Positive persistent measurement ───────────────────────────────────────── 214 215 216 def test_persistent_measurement_validates_every_frame() raises: 217 # D29/MR01: one process serves warmup and measured frames; every frame has 218 # a parsed envelope, matching correlation and expected outcome, the whole 219 # stream is accounted through EOF, with numeric RSS/FD samples, a checked 220 # child exit and proved cleanup. The recorded environment profile is the 221 # verified live profile of the measured child. 222 var guard = CleanupGuard() 223 with SafeTempDir() as temp_dir: 224 with ScopedEnvVar(HYF_PATHS_PROFILE_ENV, "repo_local"): 225 with ScopedEnvVar(HYF_PATHS_REPO_LOCAL_ROOT_ENV, temp_dir): 226 var built = build_product_binary(".", temp_dir, guard) 227 assert_equal(built.binary_sha256.byte_length(), 64) 228 assert_equal(built.source.dirty_status, "") 229 assert_equal(built.source.manifest_sha256.byte_length(), 64) 230 assert_true(built.build_ms > 0) 231 var argv = List[String]() 232 var session = measure_persistent_process( 233 ".", 234 built.binary_path, 235 argv^, 236 WARMUP_FRAMES, 237 MEASURED_FRAMES, 238 MEASUREMENT_DEADLINE_MS, 239 guard, 240 "ps", 241 "lsof", 242 built.source.revision, 243 built.source.manifest_sha256, 244 built.binary_sha256, 245 ) 246 assert_equal(session.ok_frames, WARMUP_FRAMES + MEASURED_FRAMES) 247 assert_equal(session.failed_frames, 0) 248 assert_equal(session.first_failure, "") 249 # Startup is measured from spawn to the first validated 250 # response; instrumentation is recorded separately. 251 assert_true(session.startup_ms >= 0) 252 assert_true(session.startup_wall_ms >= session.startup_ms) 253 assert_true(session.startup_sampling_ms >= 0) 254 # Measured-phase wall time excludes sampling instrumentation. 255 assert_true(session.measured_ms >= 0) 256 assert_true(session.measured_wall_ms >= session.measured_ms) 257 assert_true(session.measured_sampling_ms >= 0) 258 assert_true(session.request_total_ms > 0) 259 assert_true(session.request_max_ms >= session.request_min_ms) 260 # Numeric sampling with recorded units, method and cadence. 261 assert_true(session.rss_kb_before_warmup > 0) 262 assert_true(session.rss_kb_after_warmup > 0) 263 assert_true(session.rss_kb_after_measured > 0) 264 assert_true(session.rss_kb_peak >= session.rss_kb_after_warmup) 265 assert_true(session.fd_before_warmup > 0) 266 assert_true(session.fd_after_measured > 0) 267 assert_true(session.fd_peak >= session.fd_after_measured) 268 assert_true(session.sampling_method.find("kB") >= 0) 269 assert_true(session.sampling_method.find("-F f") >= 0) 270 assert_true(session.sampling_cadence.find("every") >= 0) 271 # The declared per-process request policy is characterized, not 272 # assumed: the persistent loop does not enforce it. 273 assert_equal(session.declared_max_requests_per_process, 1) 274 assert_true(session.child_exit.find("exited=0") >= 0) 275 assert_true(session.stderr_excerpt == "") 276 # Exact, truthful identity: clean verified source/tree plus a 277 # deterministic content manifest, binary, pixi files, toolchain, 278 # host and the verified environment profile. 279 assert_equal(session.identity.binding, "clean_product_tree") 280 assert_equal( 281 session.identity.binary_sha256, built.binary_sha256 282 ) 283 assert_equal( 284 session.identity.source_revision, built.source.revision 285 ) 286 assert_equal( 287 session.identity.source_manifest_sha256, 288 built.source.manifest_sha256, 289 ) 290 assert_equal(session.identity.source_tree_state, "clean") 291 assert_equal(session.identity.source_tree.byte_length(), 40) 292 assert_equal( 293 session.identity.tooling_manifest_sha256.byte_length(), 64 294 ) 295 assert_equal( 296 session.identity.pixi_lock_sha256.byte_length(), 64 297 ) 298 assert_equal( 299 session.identity.pixi_toml_sha256.byte_length(), 64 300 ) 301 assert_equal(session.identity.source_revision.byte_length(), 40) 302 assert_true(session.identity.cwd.find("oss/hyf") >= 0) 303 assert_true( 304 session.identity.env_profile.find( 305 "HYF_PATHS_PROFILE=repo_local" 306 ) 307 >= 0 308 ) 309 assert_true(session.identity.env_profile.find(temp_dir) >= 0) 310 assert_true(session.identity.toolchain_version != "") 311 assert_true( 312 session.identity.host_platform.find("Darwin") >= 0 313 or session.identity.host_platform.find("Linux") >= 0 314 ) 315 assert_true( 316 session.summary().find( 317 "frames=" + String(WARMUP_FRAMES + MEASURED_FRAMES) 318 ) 319 >= 0 320 ) 321 guard.assert_clean() 322 323 324 def test_measurement_identity_drift_is_rejected() raises: 325 # MR04: a product measurement must reject a binary/source binding that does 326 # not match the observed clean source identity. 327 var guard = CleanupGuard() 328 var message = "" 329 with SafeTempDir() as temp_dir: 330 var built = build_product_binary(".", temp_dir, guard) 331 var wrong_revision = WRONG_REVISION 332 var argv = List[String]() 333 try: 334 _ = measure_persistent_process( 335 ".", 336 built.binary_path, 337 argv^, 338 0, 339 1, 340 MEASUREMENT_DEADLINE_MS, 341 guard, 342 "ps", 343 "lsof", 344 wrong_revision, 345 built.source.manifest_sha256, 346 built.binary_sha256, 347 ) 348 except e: 349 message = String(e) 350 assert_true(message.find("drift") >= 0) 351 guard.assert_clean() 352 353 354 def test_measurement_rejects_wrong_binary_digest() raises: 355 # MR04: the recorded binary digest is enforced, not merely recorded. 356 var guard = CleanupGuard() 357 var message = "" 358 with SafeTempDir() as temp_dir: 359 var built = build_product_binary(".", temp_dir, guard) 360 var argv = List[String]() 361 try: 362 _ = measure_persistent_process( 363 ".", 364 built.binary_path, 365 argv^, 366 0, 367 1, 368 MEASUREMENT_DEADLINE_MS, 369 guard, 370 "ps", 371 "lsof", 372 built.source.revision, 373 built.source.manifest_sha256, 374 WRONG_DIGEST, 375 ) 376 except e: 377 message = String(e) 378 assert_true(message.find("binary drift") >= 0) 379 guard.assert_clean() 380 381 382 def test_measurement_reports_delayed_startup() raises: 383 # MR04: startup timing is spawn-relative and truthful, so a deliberately 384 # delayed child is observed as such rather than as a near-zero value. 385 var guard = CleanupGuard() 386 var session = _run_sh_measurement( 387 "sleep 0.3; " + _one_response(), 0, 1, guard 388 ) 389 assert_equal(session.ok_frames, 1) 390 assert_true(session.startup_ms >= 200) 391 assert_true(session.startup_wall_ms >= session.startup_ms) 392 guard.assert_clean() 393 394 395 def test_measurement_sampling_is_numeric_and_units_are_recorded() raises: 396 # D29: the sampler must return real numeric values with explicit 397 # unavailability, never a placeholder. 398 var guard = CleanupGuard() 399 var self_pid = owned_pid() 400 var rss = sample_rss_kb(self_pid, "ps", guard) 401 assert_true(rss > 0) 402 var fds = sample_fd_count(self_pid, "lsof", guard) 403 assert_true(fds > 0) 404 guard.assert_clean() 405 406 407 comptime ANALYSIS_JSON_TEXT = ( 408 '{"original_text":"eggs near me",' 409 '"normalized_text":"eggs near me",' 410 '"rewritten_text":"eggs",' 411 '"query_terms":["eggs"],' 412 '"normalization_signals":["local_intent_detected"],' 413 '"ranking_hints":["prefer_local_results"],' 414 '"extracted_filters":{' 415 '"local_intent":true,' 416 '"fulfillment":"unspecified",' 417 '"time_window":"unspecified"' 418 "}}" 419 ) 420 421 422 def test_direct_provider_request_and_client_schema_characterization() raises: 423 # D29/MR04: distinguish startup, a deterministic daemon request, a direct 424 # local provider request and connection counts, and characterize 425 # client/schema construction with numeric recorded values and source 426 # evidence. The Morph daemon-assisted path is out of scope here and remains 427 # an explicit H024 obligation. 428 var guard = CleanupGuard() 429 var stub_start = now_ms() 430 with spawn_max_local_stub(0, "count_requests", 1, guard) as stub: 431 var stub_startup_ms = now_ms() - stub_start 432 assert_true(stub_startup_ms >= 0) 433 var config = MaxLocalProviderConfig( 434 base_url="http://127.0.0.1:" + String(stub.port) + "/v1/", 435 health_url="http://127.0.0.1:" + String(stub.port) + "/health", 436 model="max-local-query-rewrite", 437 request_timeout_ms=15000, 438 ) 439 var context = default_request_context() 440 context.return_provenance = True 441 var construct_start = now_ms() 442 var body = build_query_rewrite_request_body( 443 config, "eggs near me", context 444 ) 445 var construct_ms = now_ms() - construct_start 446 # Schema construction is deterministic and source-verifiable, with a 447 # numeric field/message count that is recorded rather than asserted as 448 # a bare non-negative duration. 449 assert_true(construct_ms >= 0) 450 assert_true(body.object_count() > 0) 451 assert_equal(body["messages"].array_count(), 2) 452 assert_equal(body["model"].string_value(), "max-local-query-rewrite") 453 assert_equal(body["messages"][0]["role"].string_value(), "system") 454 assert_equal(body["messages"][1]["role"].string_value(), "user") 455 assert_equal( 456 body["response_format"]["type"].string_value(), "json_schema" 457 ) 458 assert_equal( 459 body["response_format"]["json_schema"]["name"].string_value(), 460 "query_rewrite", 461 ) 462 # One direct provider request over one verified connection, with a 463 # numeric elapsed time that is asserted to be a plausible positive 464 # measurement. 465 var request_start = now_ms() 466 var outcome = post_max_local_chat_completion(config, body) 467 var request_ms = now_ms() - request_start 468 assert_true(not outcome.failure) 469 assert_true(request_ms >= 0) 470 stub.wait() 471 assert_equal(stub.request_count(), 1) 472 assert_equal(stub.connection_count(), 1) 473 # Client-side response parsing is also deterministic. 474 var response = loads("{}") 475 var choices = loads("[]") 476 var choice = loads("{}") 477 var message = loads("{}") 478 message.set("content", Value(ANALYSIS_JSON_TEXT)) 479 choice.set("message", message) 480 choices.append(choice) 481 response.set("choices", choices) 482 var analysis = parse_query_analysis_from_chat_completion(response) 483 assert_equal(analysis.original_text, "eggs near me") 484 assert_equal(analysis.rewritten_text, "eggs") 485 assert_equal(len(analysis.query_terms), 1) 486 guard.assert_clean() 487 488 489 def test_measurement_poll_eintr_is_deadline_bounded() raises: 490 # MR02: a real poll EINTR is retried but can never outlive the budget. 491 var start = now_ms() 492 var pr = measurement_poll_retry(-1, 0, -1, 0, -1, 0, 60, 1) 493 var elapsed = now_ms() - start 494 assert_true(pr.interrupted) 495 assert_true(elapsed >= 50) 496 assert_true(elapsed < 2000) 497 # An ordinary no-readiness poll is not misclassified as interrupted. 498 var quiet = measurement_poll(-1, 0, -1, 0, -1, 0, 0) 499 assert_equal(quiet.count, 0) 500 assert_true(not quiet.interrupted) 501 502 503 # ── R56/R57/R69 counterexamples must fail the measurement ─────────────────── 504 505 506 def test_measurement_rejects_not_json_response() raises: 507 var guard = CleanupGuard() 508 var message = _run_sh_failure( 509 "while IFS= read -r line; do printf 'not-json\\n'; done", 510 0, 511 2, 512 guard, 513 ) 514 assert_true(message.find("not_json") >= 0) 515 guard.assert_clean() 516 517 518 def test_measurement_rejects_wrong_correlation() raises: 519 var guard = CleanupGuard() 520 var message = _run_sh_failure( 521 ( 522 "while IFS= read -r line; do printf '%s\\n' " 523 '\'{"version":1,"request_id":"wrong","trace_id":"wrong",' 524 '"ok":true,"output":{"daemon":"hyfd"}}\'; done' 525 ), 526 0, 527 2, 528 guard, 529 ) 530 assert_true(message.find("correlation_mismatch") >= 0) 531 guard.assert_clean() 532 533 534 def test_measurement_rejects_unterminated_response() raises: 535 var guard = CleanupGuard() 536 var message = _run_sh_failure( 537 ( 538 "IFS= read -r line; printf '%s' " 539 '\'{"version":1,"request_id":"meas-status-0",' 540 '"trace_id":"meas-trace-0","ok":true,' 541 '"output":{"daemon":"hyfd"}}\'; exit 0' 542 ), 543 0, 544 1, 545 guard, 546 ) 547 assert_true(message.find("newline-terminated") >= 0) 548 guard.assert_clean() 549 550 551 def test_measurement_rejects_extra_response_frame() raises: 552 # MR01/R69: an extra unvalidated frame after the expected response must 553 # fail, not be reported as success. 554 var guard = CleanupGuard() 555 var message = _run_sh_failure( 556 ( 557 "IFS= read -r line; printf '%s\\n%s\\n' '" 558 + STATUS0 559 + "' 'EXTRA_UNVALIDATED_FRAME'; while IFS= read -r line; do :; done" 560 ), 561 0, 562 1, 563 guard, 564 ) 565 assert_true(message.find("unexpected trailing stdout") >= 0) 566 guard.assert_clean() 567 568 569 def test_measurement_rejects_coalesced_trailing_frame() raises: 570 # MR01: two frames arriving in the same read chunk are still two frames. 571 var guard = CleanupGuard() 572 var message = _run_sh_failure( 573 ( 574 "IFS= read -r line; printf '%s\\n%s\\n' '" 575 + STATUS0 576 + "' '" 577 + STATUS0 578 + "'; while IFS= read -r line; do :; done" 579 ), 580 0, 581 1, 582 guard, 583 ) 584 assert_true(message.find("unexpected trailing stdout") >= 0) 585 guard.assert_clean() 586 587 588 def test_measurement_rejects_trailing_malformed_bytes() raises: 589 # MR01: trailing bytes without a complete frame are a bounded failure. 590 var guard = CleanupGuard() 591 var message = _run_sh_failure( 592 ( 593 "IFS= read -r line; printf '%s\\n' '" 594 + STATUS0 595 + "'; printf 'garbage'; while IFS= read -r line; do :; done" 596 ), 597 0, 598 1, 599 guard, 600 ) 601 assert_true(message.find("unexpected trailing stdout") >= 0) 602 guard.assert_clean() 603 604 605 def test_measurement_rejects_unterminated_trailing_frame() raises: 606 # MR01: a second frame that never terminates is not silently ignored. 607 var guard = CleanupGuard() 608 var message = _run_sh_failure( 609 ( 610 "IFS= read -r line; printf '%s\\n' '" 611 + STATUS0 612 + "'; printf '{\"partial\":true';" 613 " while IFS= read -r line; do :; done" 614 ), 615 0, 616 1, 617 guard, 618 ) 619 assert_true(message.find("unexpected trailing stdout") >= 0) 620 guard.assert_clean() 621 622 623 def test_measurement_rejects_failed_child() raises: 624 var guard = CleanupGuard() 625 var message = _run_sh_failure( 626 ( 627 "while IFS= read -r line; do printf '%s\\n' " 628 '\'{"version":1,"request_id":"meas-status-0",' 629 '"trace_id":"meas-trace-0","ok":true,' 630 '"output":{"daemon":"hyfd"}}\'; done; exit 17' 631 ), 632 0, 633 1, 634 guard, 635 ) 636 assert_true(message.find("nonzero") >= 0) 637 guard.assert_clean() 638 639 640 def test_measurement_rejects_early_eof_child() raises: 641 # A child that never answers and closes its stream must fail as an early 642 # EOF, not be read as a successful empty response. 643 var guard = CleanupGuard() 644 var message = _run_sh_failure("sleep 1; exit 0", 0, 1, guard) 645 assert_true(message.find("early_eof") >= 0) 646 guard.assert_clean() 647 648 649 # ── MR02 work-budget and pressure controls ────────────────────────────────── 650 651 652 def test_measurement_rejects_late_response_after_budget() raises: 653 # MR02: a valid response that arrives after the one work budget is not 654 # accepted as a late success. 655 var guard = CleanupGuard() 656 var message = _run_sh_failure( 657 "sleep 2; " + _one_response(), 0, 1, guard, "ps", "lsof", 400 658 ) 659 assert_true(message.find("work_deadline_expired") >= 0) 660 guard.assert_clean() 661 662 663 def test_measurement_rejects_late_exit_after_budget() raises: 664 # MR02: complete and valid output does not rescue a child that keeps the 665 # stream open past the work budget. 666 var guard = CleanupGuard() 667 var message = _run_sh_failure( 668 ( 669 "while IFS= read -r line; do printf '%s\\n' '" 670 + STATUS0 671 + "'; done; sleep 2" 672 ), 673 0, 674 1, 675 guard, 676 "ps", 677 "lsof", 678 400, 679 ) 680 assert_true(message.find("work_deadline_expired") >= 0) 681 guard.assert_clean() 682 683 684 def test_measurement_rejects_slow_sampling_past_budget() raises: 685 # MR02: a sampling subprocess consumes the remaining work budget and can 686 # never extend the measured window. 687 var guard = CleanupGuard() 688 with SafeTempDir() as temp_dir: 689 var script = temp_dir + "/slow_sampler.sh" 690 var mk = List[String]() 691 mk.append("-c") 692 mk.append( 693 "printf '#!/bin/sh\\nsleep 4\\necho 17\\n' > '" 694 + script 695 + "'; chmod +x '" 696 + script 697 + "'" 698 ) 699 var made = run_capture("sh", mk^, 10000, guard) 700 assert_equal(made.exit_code, 0) 701 var message = _run_sh_failure( 702 _one_response(), 0, 1, guard, script, "lsof", 1500 703 ) 704 assert_true( 705 message.find("sample_deadline_expired") >= 0 706 or message.find("work_deadline_expired") >= 0 707 ) 708 guard.assert_clean() 709 710 711 def test_measurement_rejects_stderr_overflow() raises: 712 # MR02: stderr pressure past the cap fails explicitly instead of being 713 # silently dropped. 714 var guard = CleanupGuard() 715 var message = _run_sh_failure( 716 ( 717 "IFS= read -r line; printf '%s\\n' '" 718 + STATUS0 719 + "'; head -c 200000 /dev/zero | tr '\\0' 'x' 1>&2;" 720 " while IFS= read -r line; do :; done" 721 ), 722 0, 723 1, 724 guard, 725 ) 726 assert_true(message.find("stderr_overflow") >= 0) 727 guard.assert_clean() 728 729 730 def test_measurement_rejects_stderr_read_error() raises: 731 # MR02: a stderr read error fails explicitly instead of disappearing. 732 var guard = CleanupGuard() 733 var message = _run_sh_failure( 734 ( 735 "IFS= read -r line; printf '%s\\n' '" 736 + STATUS0 737 + "'; printf 'x' 1>&2; while IFS= read -r line; do :; done" 738 ), 739 0, 740 1, 741 guard, 742 "ps", 743 "lsof", 744 MEASUREMENT_DEADLINE_MS, 745 MeasurementFaults(stderr_read_errors=1), 746 ) 747 assert_true(message.find("stderr read_error") >= 0) 748 guard.assert_clean() 749 750 751 def test_measurement_unproved_cleanup_is_retained_and_recovered() raises: 752 # MR03: an unproved cleanup keeps exact retryable ownership and surfaces to 753 # the caller; recovery against the real owned child then succeeds with no 754 # descriptor/child leak. The bounded seam leaves the real child running. 755 var guard = CleanupGuard() 756 var self_pid = owned_pid() 757 var child_before = child_process_count(self_pid, guard) 758 var fd_before = open_fd_count_checked() 759 var message = _run_sh_failure( 760 "while IFS= read -r line; do printf 'not-json\\n'; done", 761 0, 762 1, 763 guard, 764 "ps", 765 "lsof", 766 MEASUREMENT_DEADLINE_MS, 767 MeasurementFaults(cleanup_failures=1), 768 ) 769 assert_true(message.find("not_json") >= 0) 770 assert_true(guard.pending() >= 1) 771 assert_true(guard.retained() >= 1) 772 assert_equal(guard.recover_all(), 0) 773 guard.assert_clean() 774 assert_equal(open_fd_count_checked() - fd_before, 0) 775 assert_equal(child_process_count(self_pid, guard), child_before) 776 777 778 def test_measurement_rejects_unavailable_rss_sampler() raises: 779 var guard = CleanupGuard() 780 var message = _run_sh_failure( 781 "while IFS= read -r line; do printf '%s\\n' ok; done", 782 0, 783 1, 784 guard, 785 "hyf-no-such-rss-sampler", 786 ) 787 assert_true(message.find("rss sampling unavailable") >= 0) 788 guard.assert_clean() 789 790 791 def test_measurement_rejects_unavailable_fd_sampler() raises: 792 var guard = CleanupGuard() 793 var message = _run_sh_failure( 794 "while IFS= read -r line; do printf '%s\\n' ok; done", 795 0, 796 1, 797 guard, 798 "ps", 799 "hyf-no-such-fd-sampler", 800 ) 801 assert_true(message.find("descriptor sampling unavailable") >= 0) 802 guard.assert_clean() 803 804 805 def test_measurement_rejects_invalid_frame_counts() raises: 806 var guard = CleanupGuard() 807 var message = _run_sh_failure("exit 0", 0, 0, guard) 808 assert_true(message.find("invalid warmup/measured") >= 0) 809 guard.assert_clean() 810 811 812 # ── MR03 exact resource ownership ─────────────────────────────────────────── 813 814 815 def test_measurement_repeated_failures_leak_nothing() raises: 816 # MR03: repeated failing public measurement calls must leave no descriptor 817 # or child behind, and a subsequent supported call must still succeed. 818 var guard = CleanupGuard() 819 var self_pid = owned_pid() 820 var child_before = child_process_count(self_pid, guard) 821 var fd_before = open_fd_count_checked() 822 for index in range(4): 823 var message = _run_sh_failure( 824 "while IFS= read -r line; do printf 'not-json\\n'; done", 825 0, 826 1, 827 guard, 828 ) 829 assert_true(message.find("not_json") >= 0) 830 guard.assert_clean() 831 var fd_after = open_fd_count_checked() 832 assert_equal(fd_after - fd_before, 0) 833 assert_equal(child_process_count(self_pid, guard), child_before) 834 var recovered = _run_sh_measurement(_one_response(), 0, 1, guard) 835 assert_equal(recovered.ok_frames, 1) 836 assert_equal(recovered.failed_frames, 0) 837 guard.assert_clean() 838 839 840 # ── Direct correlation validation unit controls ───────────────────────────── 841 842 843 def test_frame_validation_controls() raises: 844 var pair = build_status_frame(7) 845 var frame = pair[0] 846 var request_id = pair[1] 847 var trace_id = pair[2] 848 assert_true(frame.find('"request_id":"meas-status-7"') >= 0) 849 var good = ( 850 '{"version":1,"request_id":"' 851 + request_id 852 + '","trace_id":"' 853 + trace_id 854 + '","ok":true,"output":{"daemon":"hyfd",' 855 + '"limits":{"max_requests_per_process":1}}}' 856 ) 857 var verdict = validate_status_frame(good, request_id, trace_id) 858 assert_true(verdict.ok) 859 assert_equal(verdict.outcome, "sys.status_ok") 860 assert_equal(verdict.declared_max_requests, 1) 861 assert_equal( 862 validate_status_frame("nope", request_id, trace_id).reason, "not_json" 863 ) 864 assert_equal( 865 validate_status_frame( 866 '{"version":2,"request_id":"' 867 + request_id 868 + '","trace_id":"' 869 + trace_id 870 + '","ok":true}', 871 request_id, 872 trace_id, 873 ).reason, 874 "version_mismatch", 875 ) 876 assert_equal( 877 validate_status_frame( 878 '{"version":1,"request_id":"other","trace_id":"' 879 + trace_id 880 + '","ok":true}', 881 request_id, 882 trace_id, 883 ).reason, 884 "correlation_mismatch", 885 ) 886 assert_equal( 887 validate_status_frame( 888 '{"version":1,"request_id":"' 889 + request_id 890 + '","trace_id":"' 891 + trace_id 892 + '","ok":false}', 893 request_id, 894 trace_id, 895 ).reason, 896 "not_ok", 897 ) 898 assert_equal( 899 validate_status_frame( 900 '{"version":1,"request_id":"' 901 + request_id 902 + '","trace_id":"' 903 + trace_id 904 + '","ok":true,"error":{}}', 905 request_id, 906 trace_id, 907 ).reason, 908 "unexpected_error", 909 ) 910 assert_equal( 911 validate_status_frame( 912 '{"version":1,"request_id":"' 913 + request_id 914 + '","trace_id":"' 915 + trace_id 916 + '","ok":true,"output":{"daemon":"other"}}', 917 request_id, 918 trace_id, 919 ).reason, 920 "outcome_mismatch", 921 ) 922 923 924 # ── MC01 corrected timing windows ─────────────────────────────────────────── 925 926 927 def test_measurement_zero_warmup_timing_is_initialized() raises: 928 # ADR-0020 MC01: a zero-warmup measurement must open a bounded measured wall 929 # interval before its first request. The period-11 defect left the start 930 # uninitialized, so an 804 ms run reported 347472213 ms (host uptime). 931 var guard = CleanupGuard() 932 var session = _run_sh_measurement(_one_response(), 0, 1, guard) 933 assert_equal(session.warmup_frames, 0) 934 assert_equal(session.ok_frames, 1) 935 assert_equal(session.warmup_ms, 0) 936 assert_true(session.measured_wall_ms >= 0) 937 assert_true(session.measured_wall_ms <= session.run_wall_ms) 938 assert_true(session.measured_ms >= 0) 939 assert_true(session.measured_sampling_ms >= 0) 940 assert_true(session.measured_ms <= session.measured_wall_ms) 941 assert_equal(session.accounting_error_ms(), 0) 942 assert_true(session.rss_kb_after_warmup > 0) 943 guard.assert_clean() 944 945 946 def test_measurement_positive_warmup_timing_accounting() raises: 947 # ADR-0020 MC01: with a positive warmup the boundary instrumentation is 948 # taken before the measured interval opens and must not be subtracted from 949 # the instrumentation total afterwards (that overstated measured time). 950 var guard = CleanupGuard() 951 var session = _run_sh_measurement(_multi_response(), 3, 2, guard) 952 assert_equal(session.warmup_frames, 3) 953 assert_equal(session.ok_frames, 5) 954 assert_true(session.warmup_ms >= 0) 955 assert_true(session.measured_wall_ms >= 0) 956 assert_true(session.measured_wall_ms <= session.run_wall_ms) 957 assert_true(session.measured_sampling_ms >= 0) 958 assert_true(session.measured_ms >= 0) 959 assert_true(session.measured_ms <= session.measured_wall_ms) 960 assert_equal(session.accounting_error_ms(), 0) 961 guard.assert_clean() 962 963 964 def test_measurement_known_delayed_request_is_observed() raises: 965 # ADR-0020 MC01: a known delayed request is reflected in both the per-request 966 # timing and the measured wall interval, which stay internally consistent. 967 var guard = CleanupGuard() 968 var delayed = ( 969 "IFS= read -r line; sleep 0.15; printf '%s\\n' '" 970 + STATUS0 971 + "'; while IFS= read -r line; do :; done" 972 ) 973 var session = _run_sh_measurement(delayed, 0, 1, guard) 974 assert_equal(session.ok_frames, 1) 975 assert_true(session.request_total_ms >= 100) 976 assert_true(session.request_max_ms >= 100) 977 assert_true(session.measured_wall_ms >= 100) 978 assert_true(session.measured_wall_ms <= session.run_wall_ms) 979 assert_true(session.measured_ms >= 0) 980 assert_equal(session.accounting_error_ms(), 0) 981 guard.assert_clean() 982 983 984 def test_measurement_slow_boundary_sampler_is_excluded_once() raises: 985 # ADR-0020 MC01: a slow after-warmup boundary sampler runs before the 986 # measured wall interval opens. It must be excluded exactly once: the 987 # period-11 code subtracted it from the instrumentation total and reported 988 # more measured time than the interval contained. 989 var guard = CleanupGuard() 990 with SafeTempDir() as temp_dir: 991 var counter = temp_dir + "/calls" 992 var sampler = temp_dir + "/slow_rss.sh" 993 var body = ( 994 '#!/bin/sh\nn=$(cat "' 995 + counter 996 + '" 2>/dev/null || echo 0)\n' 997 + "n=$((n+1))\n" 998 + 'printf \'%s\' "$n" > "' 999 + counter 1000 + '"\n' 1001 + 'if [ "$n" -le 2 ]; then sleep 1.2; fi\n' 1002 + "echo 17000\n" 1003 ) 1004 Path(sampler).write_text(body) 1005 var chmod_args = List[String]() 1006 chmod_args.append("+x") 1007 chmod_args.append(sampler) 1008 var made = run_capture("chmod", chmod_args^, 10000, guard) 1009 assert_equal(made.exit_code, 0) 1010 var session = _run_sh_measurement( 1011 _multi_response(), 1012 1, 1013 1, 1014 guard, 1015 sampler, 1016 "lsof", 1017 MEASUREMENT_DEADLINE_MS, 1018 ) 1019 assert_equal(session.ok_frames, 2) 1020 assert_true(session.measured_sampling_ms >= 0) 1021 assert_true(session.measured_ms >= 0) 1022 assert_true(session.measured_ms <= session.measured_wall_ms) 1023 assert_equal(session.accounting_error_ms(), 0) 1024 # The two 1.2 s boundary samples stayed outside the measured interval. 1025 assert_true(session.measured_wall_ms < 1000) 1026 guard.assert_clean() 1027 1028 1029 # ── MC02 fail-closed provenance ───────────────────────────────────────────── 1030 1031 1032 def test_measurement_tooling_manifest_rejects_missing_inputs() raises: 1033 # ADR-0020 MC02: a missing tooling input must fail, never return the valid 1034 # empty-input digest that the period-11 masked pipeline produced. 1035 var guard = CleanupGuard() 1036 with SafeTempDir() as temp_dir: 1037 var message = "" 1038 try: 1039 _ = tooling_manifest_sha256(temp_dir + "/absent-root", guard) 1040 except e: 1041 message = String(e) 1042 assert_true(message.find("sha256") >= 0) 1043 assert_true(message.find("e3b0c442") < 0) 1044 guard.assert_clean() 1045 1046 1047 def test_measurement_digest_rejects_missing_one_input() raises: 1048 # ADR-0021 MP01: the expressly required missing-one-input control. Seven of 1049 # the eight declared tooling inputs are present valid regular files and one 1050 # declared path is absent, so the checked pipeline must fail instead of 1051 # returning a digest for the partial present set. The all-input-missing and 1052 # failed-Git controls do not cover this case. 1053 var guard = CleanupGuard() 1054 with SafeTempDir() as root: 1055 _copy_tooling_files(root, guard, 1) 1056 var expected = tooling_manifest_sha256(".", guard) 1057 assert_equal(expected.byte_length(), 64) 1058 var message = "" 1059 var reported = "" 1060 try: 1061 reported = tooling_manifest_sha256(root, guard) 1062 except e: 1063 message = String(e) 1064 assert_true(message.find("sha256") >= 0) 1065 assert_true(message.find("e3b0c442") < 0) 1066 # No partial-set digest may be returned as a valid identity. 1067 assert_true(reported != expected) 1068 assert_equal(reported, "") 1069 guard.assert_clean() 1070 1071 1072 def test_measurement_digest_rejects_total_hasher_failure() raises: 1073 # ADR-0021 MP01: the expressly required failed-hasher-command control. All 1074 # declared inputs are present valid regular files and both checked hasher 1075 # stages fail, so the pipeline must reject. A different missing/unreadable 1076 # input would not substitute for this control; the cause text must show the 1077 # real nonzero hasher exit rather than only an unavailable command. 1078 var guard = CleanupGuard() 1079 with SafeTempDir() as root: 1080 _copy_tooling_files(root, guard) 1081 var failing = _failing_hasher(root, guard) 1082 var message = "" 1083 var reported = "" 1084 try: 1085 reported = tooling_manifest_sha256(root, guard, failing, failing) 1086 except e: 1087 message = String(e) 1088 assert_true(message.find("sha256 unavailable") >= 0) 1089 assert_true(message.find("exited=7") >= 0) 1090 assert_true(message.find("e3b0c442") < 0) 1091 assert_equal(reported, "") 1092 guard.assert_clean() 1093 1094 1095 def test_measurement_digest_fallback_hasher_checked_correct() raises: 1096 # ADR-0021 MP01: when the primary hasher stage fails but the supported 1097 # fallback succeeds, the result must be the same checked digest as the 1098 # default path — the fallback may never silently report a partial or empty 1099 # identity. 1100 var guard = CleanupGuard() 1101 with SafeTempDir() as root: 1102 _copy_tooling_files(root, guard) 1103 var failing = _failing_hasher(root, guard) 1104 var expected = tooling_manifest_sha256(root, guard) 1105 assert_equal(expected.byte_length(), 64) 1106 var fallback = tooling_manifest_sha256(root, guard, failing) 1107 assert_equal(fallback.byte_length(), 64) 1108 assert_true(fallback == expected) 1109 guard.assert_clean() 1110 1111 1112 def test_measurement_source_manifest_rejects_missing_repository() raises: 1113 # ADR-0020 MC02: a failed git stage must be an error, not an empty digest. 1114 var guard = CleanupGuard() 1115 with SafeTempDir() as temp_dir: 1116 var message = "" 1117 try: 1118 _ = source_manifest_sha256(temp_dir, guard) 1119 except e: 1120 message = String(e) 1121 assert_true(message.find("source content manifest") >= 0) 1122 assert_true(message.find("e3b0c442") < 0) 1123 guard.assert_clean() 1124 1125 1126 def test_measurement_digest_handles_path_characters() raises: 1127 # ADR-0020 MC02: paths are argv data, so apostrophes and spaces in a path 1128 # must be handled literally (the period-11 tooling pipeline interpolated 1129 # paths into a single-quoted shell string and could mis-hash or fail). 1130 var guard = CleanupGuard() 1131 with SafeTempDir() as base: 1132 var weird = base + "/hyf 'quoted' dir" 1133 _ = std.os.makedirs(weird, exist_ok=True) 1134 var one = weird + "/a 'one'.txt" 1135 var two = weird + "/b two.txt" 1136 Path(one).write_text("alpha") 1137 Path(two).write_text("beta") 1138 var files = List[String]() 1139 files.append(one) 1140 files.append(two) 1141 var digest = sha256_file_set("weird path set", files^, guard) 1142 assert_equal(digest.byte_length(), 64) 1143 Path(two).write_text("gamma") 1144 var files_two = List[String]() 1145 files_two.append(one) 1146 files_two.append(two) 1147 var digest_two = sha256_file_set("weird path set", files_two^, guard) 1148 assert_true(digest != digest_two) 1149 guard.assert_clean() 1150 1151 1152 def test_measurement_tooling_manifest_binds_imported_helpers() raises: 1153 # ADR-0020 MC02: the tooling identity must bind the imported helper closure, 1154 # not only the three top-level tooling files. Mutating an *imported* helper 1155 # in an isolated copy changes the digest. 1156 var guard = CleanupGuard() 1157 with SafeTempDir() as root: 1158 var tests_dir = root + "/tests" 1159 _ = std.os.makedirs(tests_dir, exist_ok=True) 1160 var sources = measurement_tooling_files(".") 1161 var cp_args = List[String]() 1162 for index in range(len(sources)): 1163 cp_args.append(sources[index]) 1164 cp_args.append(tests_dir) 1165 var copied = run_capture("cp", cp_args^, 20000, guard) 1166 assert_equal(copied.exit_code, 0) 1167 var before = tooling_manifest_sha256(root, guard) 1168 assert_equal(before.byte_length(), 64) 1169 Path(tests_dir + "/parent_lifecycle.mojo").write_text("// mutated\n") 1170 var after = tooling_manifest_sha256(root, guard) 1171 assert_true(before != after) 1172 guard.assert_clean() 1173 1174 1175 def test_measurement_rejects_dirty_measured_tree() raises: 1176 # ADR-0020 MC02/MR04: a dirty measured build input is rejected at capture. 1177 # The control uses an isolated owned git repository, never the real 1178 # checkout, and never changes real index or host flags. 1179 var guard = CleanupGuard() 1180 with SafeTempDir() as root: 1181 _ = std.os.makedirs(root + "/src", exist_ok=True) 1182 Path(root + "/src/main.mojo").write_text("fn main():\n pass\n") 1183 Path(root + "/pixi.toml").write_text("[workspace]\n") 1184 Path(root + "/pixi.lock").write_text("version: 4\n") 1185 var init_args = List[String]() 1186 init_args.append("init") 1187 init_args.append("--quiet") 1188 var initialized = run_capture("git", init_args^, 20000, guard, root) 1189 assert_equal(initialized.exit_code, 0) 1190 var add_args = List[String]() 1191 add_args.append("add") 1192 add_args.append("-A") 1193 var added = run_capture("git", add_args^, 20000, guard, root) 1194 assert_equal(added.exit_code, 0) 1195 var commit_args = List[String]() 1196 commit_args.append("-c") 1197 commit_args.append("user.email=hyf-test@invalid") 1198 commit_args.append("-c") 1199 commit_args.append("user.name=hyf test") 1200 commit_args.append("-c") 1201 commit_args.append("commit.gpgsign=false") 1202 commit_args.append("commit") 1203 commit_args.append("--no-verify") 1204 commit_args.append("--quiet") 1205 commit_args.append("-m") 1206 commit_args.append("init") 1207 var committed = run_capture("git", commit_args^, 20000, guard, root) 1208 assert_equal(committed.exit_code, 0) 1209 Path(root + "/src/main.mojo").write_text("fn main():\n return\n") 1210 var identity = source_identity(root, guard) 1211 assert_true(identity.dirty_status != "") 1212 var message = "" 1213 try: 1214 require_clean_source(identity, "test capture") 1215 except e: 1216 message = String(e) 1217 assert_true(message.find("dirty") >= 0) 1218 guard.assert_clean() 1219 1220 1221 # ── MC03 isolated late exit and ownership reuse ───────────────────────────── 1222 1223 1224 def test_measurement_rejects_isolated_late_child_exit() raises: 1225 # ADR-0020 MC03: valid output AND closed stdout/stderr must be proved before 1226 # the late-exit phase. The child answers, closes its own stdout/stderr and 1227 # then stays alive past the budget, so the bounded failure is the child-exit 1228 # wait rather than an output-drain timeout. 1229 var guard = CleanupGuard() 1230 var script = ( 1231 "while IFS= read -r line; do printf '%s\\n' '" 1232 + STATUS0 1233 + "'; done; exec 1>&- 2>&-; sleep 2" 1234 ) 1235 var message = _run_sh_failure(script, 0, 1, guard, "ps", "lsof", 400) 1236 assert_true(message.find("did not exit within its budget") >= 0) 1237 assert_true(message.find("output drain") < 0) 1238 assert_true(message.find("frame invalid") < 0) 1239 guard.assert_clean() 1240 1241 1242 def test_measurement_recovery_then_valid_call_leaks_nothing() raises: 1243 # ADR-0020 MC03/R73: after a retained-and-recovered cleanup, a following 1244 # valid measurement with the same guard must not skip closing a descriptor 1245 # number that was reused. The period-11 probe observed FD delta +1 here. 1246 var guard = CleanupGuard() 1247 var self_pid = owned_pid() 1248 var child_before = child_process_count(self_pid, guard) 1249 var fd_before = open_fd_count_checked() 1250 var message = _run_sh_failure( 1251 "while IFS= read -r line; do printf 'not-json\\n'; done", 1252 0, 1253 1, 1254 guard, 1255 "ps", 1256 "lsof", 1257 MEASUREMENT_DEADLINE_MS, 1258 MeasurementFaults(cleanup_failures=1), 1259 ) 1260 assert_true(message.find("not_json") >= 0) 1261 assert_true(guard.retained() >= 1) 1262 assert_equal(guard.recover_all(), 0) 1263 guard.assert_clean() 1264 var recovered = _run_sh_measurement(_one_response(), 0, 1, guard) 1265 assert_equal(recovered.ok_frames, 1) 1266 assert_equal(recovered.failed_frames, 0) 1267 guard.assert_clean() 1268 assert_equal(open_fd_count_checked() - fd_before, 0) 1269 assert_equal(child_process_count(self_pid, guard), child_before) 1270 1271 1272 def main() raises: 1273 TestSuite.discover_tests[__functions_in_module()]().run()