Skip to content

Commit af04b2a

Browse files
committed
fix: keep macos release gate awake
1 parent 5f3e1b0 commit af04b2a

5 files changed

Lines changed: 68 additions & 4 deletions

File tree

CHANGELOG.md

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -7,6 +7,11 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0
77

88
## [Unreleased]
99

10+
### Fixed
11+
- Kept unattended macOS candidate gates awake, ran the release benchmark once,
12+
and preserved service/VM logs when exception teardown precedes pytest's
13+
failure report.
14+
1015
## [1.5.1784663414] - 2026-07-21
1116

1217
### Fixed

justfile

Lines changed: 9 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -656,6 +656,14 @@ _bootstrap:
656656
test:
657657
#!/bin/bash
658658
set -euo pipefail
659+
if [ "$(uname -s)" = "Darwin" ] && [ -z "${CAPSEM_TEST_CAFFEINATED:-}" ]; then
660+
command -v caffeinate >/dev/null || {
661+
echo "macOS just test requires caffeinate to prevent an unattended release gate from sleeping" >&2
662+
exit 1
663+
}
664+
echo "=== Holding macOS awake for the complete candidate gate ==="
665+
exec caffeinate -dimsu env CAPSEM_TEST_CAFFEINATED=1 just test
666+
fi
659667
if [ -n "$(git status --porcelain --untracked-files=all)" ]; then
660668
echo "just test requires a clean working tree; commit the complete candidate first." >&2
661669
git status --short >&2
@@ -874,7 +882,7 @@ _test-candidate: _bound-docker-test-storage _bootstrap _install-tools _clean-sta
874882
CAPSEM_REQUIRE_ARTIFACTS=1 uv run python -m pytest \
875883
tests/capsem-serial/ \
876884
tests/ironbank/test_route_health.py \
877-
-v --tb=short -m serial
885+
-v --tb=short -m serial -k 'not test_capsem_bench_baseline'
878886

879887
echo "=== Python: Build chain and release tests (serial) ==="
880888
CAPSEM_REQUIRE_ARTIFACTS=1 uv run python -m pytest tests/capsem-build-chain/ tests/capsem-release/ -v --tb=short

tests/helpers/service.py

Lines changed: 10 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -97,7 +97,7 @@ def materialize_test_profiles(tmp_dir: Path) -> Path:
9797
return profiles_dir
9898

9999

100-
def preserve_tmp_dir_on_failure(tmp_dir):
100+
def preserve_tmp_dir_on_failure(tmp_dir, *, force: bool = False):
101101
"""Copy tmp_dir to test-artifacts/ when this worker saw any failure.
102102
103103
Called by integration-test fixture teardowns BEFORE they rmtree the
@@ -132,7 +132,7 @@ def preserve_tmp_dir_on_failure(tmp_dir):
132132
# tmp_dir regardless of that worker's own failure state. Used during
133133
# concurrency investigations where a failure on worker B needs to be
134134
# correlated against what worker A was doing at the same time.
135-
force = os.environ.get("CAPSEM_TEST_PRESERVE_ALWAYS")
135+
force = force or bool(os.environ.get("CAPSEM_TEST_PRESERVE_ALWAYS"))
136136
if not force and not FAILED_NODEIDS:
137137
return
138138
import stat as statmod
@@ -346,7 +346,14 @@ def stop(self, *, cleanup: bool = True):
346346
if not cleanup:
347347
return
348348

349-
preserve_tmp_dir_on_failure(self.home_dir)
349+
# Tests commonly stop the service from a ``finally`` block. That
350+
# happens before pytest's makereport hook records FAILED_NODEIDS, so
351+
# use the actively-propagating exception as authoritative failure
352+
# evidence instead of deleting the only service/process logs.
353+
if sys.exc_info()[0] is not None:
354+
preserve_tmp_dir_on_failure(self.home_dir, force=True)
355+
else:
356+
preserve_tmp_dir_on_failure(self.home_dir)
350357

351358
if self.home_dir.exists():
352359
shutil.rmtree(self.home_dir, ignore_errors=True)

tests/test_release_gate_integrity.py

Lines changed: 19 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -38,6 +38,25 @@ def test_just_test_binds_clean_tree_to_one_commit_without_archiving_benchmarks()
3838
assert "benchmarks/**/data_*.json" in _read(".gitignore")
3939

4040

41+
def test_full_gate_runs_capsem_bench_baseline_exactly_once() -> None:
42+
justfile = _read("justfile")
43+
candidate = justfile.split("\n_test-candidate:", maxsplit=1)[1].split(
44+
"\nbuild-host-image:", maxsplit=1
45+
)[0]
46+
47+
assert candidate.count("tests/capsem-serial/test_capsem_bench_baseline.py") == 1
48+
49+
50+
def test_macos_full_gate_holds_a_system_sleep_assertion() -> None:
51+
justfile = _read("justfile")
52+
wrapper = justfile.split("\ntest:", maxsplit=1)[1].split(
53+
"\n_test-candidate:", maxsplit=1
54+
)[0]
55+
56+
assert "caffeinate" in wrapper
57+
assert "CAPSEM_TEST_CAFFEINATED" in wrapper
58+
59+
4160
def test_toolchain_and_workflow_inputs_are_immutable_and_consistent() -> None:
4261
toolchain = tomllib.loads(_read("rust-toolchain.toml"))
4362
assert toolchain["toolchain"]["channel"] == PINNED_RUST

tests/test_service_profile_helper.py

Lines changed: 25 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -83,3 +83,28 @@ def test_service_instance_can_keep_shutdown_flushed_state_for_assertions(
8383

8484
service.stop()
8585
assert not home.exists()
86+
87+
88+
def test_service_instance_preserves_artifacts_during_exception_teardown(
89+
tmp_path: Path, monkeypatch: pytest.MonkeyPatch
90+
) -> None:
91+
"""A failure active inside ``finally`` must survive pre-report teardown."""
92+
home = tmp_path / "capsem-home"
93+
home.mkdir()
94+
monkeypatch.setattr(service_helper, "make_capsem_tmp_dir", lambda _prefix: home)
95+
preserved: list[tuple[Path, bool]] = []
96+
97+
def record_preserve(path: Path, *, force: bool = False) -> None:
98+
preserved.append((Path(path), force))
99+
100+
monkeypatch.setattr(service_helper, "preserve_tmp_dir_on_failure", record_preserve)
101+
service = service_helper.ServiceInstance()
102+
103+
with pytest.raises(RuntimeError, match="benchmark failed"):
104+
try:
105+
raise RuntimeError("benchmark failed")
106+
finally:
107+
service.stop()
108+
109+
assert preserved == [(home, True)]
110+
assert not home.exists()

0 commit comments

Comments
 (0)