My aggregated monorepo of OCaml code, automaintained
0
fork

Configure Feed

Select the types of activity you want to include in your feed.

day11/test: snapshot service integration test

Simulates running day11 as a service across 5 opam-repository commits
spanning the OCaml 5.4.1 release. Tests snapshot creation, layer
caching across snapshots, compiler transitions, doc regeneration,
solution caching, and repeat (no-op) behaviour.

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>

+70 -87
+70 -87
day11/test/test_snapshot_service.sh
··· 1 1 #!/bin/bash 2 2 # Test snapshot service flow: simulate running day11 as a service 3 - # across 6 opam-repository commits, using the small universe profile. 3 + # across opam-repository commits spanning the OCaml 5.4.1 release, 4 + # using the small universe profile. 4 5 # 5 - # Each step checks out a specific commit, runs batch, and verifies 6 - # that snapshots, caching, and docs work correctly. 6 + # Tests: snapshot creation, layer caching across snapshots, 7 + # compiler transitions, doc regeneration, and incremental behaviour. 7 8 # 8 9 # Usage: ./test_snapshot_service.sh [day11-binary] 9 10 ··· 14 15 PROFILE_DIR="/tmp/day11-snapshot-test" 15 16 PROFILE_NAME="snapshot-test" 16 17 17 - # Commits representing meaningful transitions for the small universe 18 + # Commits spanning the OCaml 5.4.1 release and subsequent package updates 19 + # All commits are merge commits on master for deterministic state 18 20 COMMITS=( 19 - "267ac40078" # eio 1.3 released 20 - "0d494a45eb" # fmt 0.11.0 21 - "6092b78314" # ppxlib 0.37.0 22 - "7524e5c122" # cmdliner 2.1.0 + logs 0.10.0 23 - "b04e914780" # odoc 3.1.0 (tool change) 24 - "55776d6a15" # HEAD — no small-universe changes 21 + "809faa59ae" # Just before OCaml 5.4.1 (2026-02-17) 22 + "d10e5a9919" # OCaml 5.4.1 released (compiler change!) 23 + "9c0b4e947c" # lwt 6.1.1 (2026-02-23) 24 + "6185871ccd" # ppxlib 0.38.0 (2026-03-20) 25 + "6185871ccd" # Same commit again (should be a complete no-op) 25 26 ) 26 27 27 28 LABELS=( 28 - "eio 1.3" 29 - "fmt 0.11.0" 30 - "ppxlib 0.37.0" 31 - "cmdliner 2.1.0 + logs 0.10.0" 32 - "odoc 3.1.0" 33 - "HEAD (no-op)" 29 + "Before OCaml 5.4.1" 30 + "OCaml 5.4.1 released" 31 + "lwt 6.1.1" 32 + "ppxlib 0.38.0" 33 + "Repeat (no-op)" 34 34 ) 35 35 36 - # Save current HEAD so we can restore it 37 - ORIG_HEAD=$(git -C "$OPAM_REPO" rev-parse HEAD) 36 + # Save current state 37 + ORIG_REF=$(git -C "$OPAM_REPO" symbolic-ref HEAD 2>/dev/null || git -C "$OPAM_REPO" rev-parse HEAD) 38 38 39 39 cleanup() { 40 40 echo "" 41 - echo "Restoring opam-repository to $ORIG_HEAD..." 42 - git -C "$OPAM_REPO" checkout -q "$ORIG_HEAD" 43 - echo "Cleaning up profile..." 44 - "$DAY11" profile delete --name "$PROFILE_NAME" --profile-dir "$PROFILE_DIR" 2>/dev/null || true 45 - sudo rm -rf "$PROFILE_DIR" 41 + echo "Restoring opam-repository..." 42 + git -C "$OPAM_REPO" checkout -q "$ORIG_REF" 2>/dev/null || true 43 + echo "Done." 46 44 } 47 45 trap cleanup EXIT 48 46 49 47 # Ensure fork helper is linked 50 48 BINDIR=$(dirname "$DAY11") 51 - if [ ! -f "$BINDIR/day11-fork-helper" ]; then 52 - HELPER=$(find "$(dirname "$DAY11")/../.." -name fork_helper.exe -type f 2>/dev/null | head -1) 53 - if [ -n "$HELPER" ]; then 54 - ln -sf "$HELPER" "$BINDIR/day11-fork-helper" 55 - fi 56 - fi 49 + HELPER=$(find "$BINDIR/../.." -name fork_helper.exe -type f 2>/dev/null | head -1) 50 + [ -n "$HELPER" ] && ln -sf "$HELPER" "$BINDIR/day11-fork-helper" 2>/dev/null 57 51 58 52 echo "==========================================" 59 53 echo " Snapshot Service Test" 60 54 echo "==========================================" 61 - echo "Binary: $DAY11" 62 - echo "Repo: $OPAM_REPO" 55 + echo "Binary: $DAY11" 56 + echo "Repo: $OPAM_REPO" 63 57 echo "Profile dir: $PROFILE_DIR" 64 58 echo "" 65 59 ··· 67 61 sudo rm -rf "$PROFILE_DIR" 68 62 mkdir -p "$PROFILE_DIR" 69 63 70 - # Create profile 71 - echo "Creating profile '$PROFILE_NAME'..." 64 + # Create profile — no driver_compiler pin, auto-detect from solutions 72 65 "$DAY11" profile create \ 73 66 --name "$PROFILE_NAME" \ 74 67 --profile-dir "$PROFILE_DIR" \ ··· 79 72 echo "" 80 73 81 74 PREV_SNAPSHOT="" 82 - TOTAL_OK=0 83 - TOTAL_FAIL=0 84 75 85 76 for i in "${!COMMITS[@]}"; do 86 77 COMMIT="${COMMITS[$i]}" 87 78 LABEL="${LABELS[$i]}" 88 79 STEP=$((i + 1)) 80 + TOTAL=${#COMMITS[@]} 89 81 90 82 echo "==========================================" 91 - echo " Step $STEP/6: $LABEL" 83 + echo " Step $STEP/$TOTAL: $LABEL" 92 84 echo " Commit: $COMMIT" 93 85 echo "==========================================" 94 86 ··· 96 88 git -C "$OPAM_REPO" checkout -q "$COMMIT" 97 89 98 90 # Run batch 99 - echo "Running batch..." 100 91 OUTPUT=$("$DAY11" batch \ 101 92 --profile "$PROFILE_NAME" \ 102 93 --profile-dir "$PROFILE_DIR" \ 103 94 -j 4 \ 104 - 2>&1) 95 + 2>&1) || true 105 96 106 97 # Extract key metrics 107 98 SNAPSHOT=$(echo "$OUTPUT" | grep "^Snapshot:" | awk '{print $2}') 108 99 TARGETS=$(echo "$OUTPUT" | grep "^Targets:" | awk '{print $2}') 109 - CACHED=$(echo "$OUTPUT" | grep "^Layers:" | grep -oP '\d+ cached' | awk '{print $1}') 110 - NEED_BUILD=$(echo "$OUTPUT" | grep "^Layers:" | grep -oP '\d+ need building' | awk '{print $1}') 111 - SOLVE_CACHED=$(echo "$OUTPUT" | grep "^Solving:" | grep -oP '\d+ cached' | awk '{print $1}') 112 - DOCS=$(echo "$OUTPUT" | grep "^=== Docs:" | grep -oP '\d+ packages' | awk '{print $1}') 113 - HTML=$(echo "$OUTPUT" | grep "^=== Docs:" | grep -oP '\d+ HTML' | awk '{print $1}') 114 - BUILD_OK=$(echo "$OUTPUT" | grep "^Build:" | grep -oP '\d+ success' | awk '{print $1}') 115 - BUILD_FAIL=$(echo "$OUTPUT" | grep "^Build:" | grep -oP '\d+ failed' | awk '{print $1}') 100 + SOLVE_CACHED=$(echo "$OUTPUT" | grep "^Solving:" | head -1 | grep -oP '^\S+\s+\K\d+(?= cached)') 101 + SOLVE_NEED=$(echo "$OUTPUT" | grep "^Solving:" | head -1 | grep -oP '\d+(?= need)') 102 + LAYERS_CACHED=$(echo "$OUTPUT" | grep "^Layers:" | grep -oP '\d+(?= cached)') 103 + LAYERS_NEED=$(echo "$OUTPUT" | grep "^Layers:" | grep -oP '\d+(?= need)') 104 + 105 + # Count doc results from output 106 + DOC_ALL_OK=$(echo "$OUTPUT" | grep -c 'doc-all OK' || true) 107 + LINKED=$(echo "$OUTPUT" | grep -c ': linked$' || true) 108 + DOC_LINE=$(echo "$OUTPUT" | grep "^=== Docs:" || echo "none") 109 + 110 + # Executor stats 111 + EXEC_LINE=$(echo "$OUTPUT" | grep "Executor:" | tail -1 || echo "") 116 112 117 - # Check snapshot changed (except step 6 which should reuse) 118 - SNAPSHOT_CHANGED="yes" 113 + # Snapshot reuse check 119 114 if [ "$SNAPSHOT" = "$PREV_SNAPSHOT" ]; then 120 - SNAPSHOT_CHANGED="no (reused)" 115 + SNAP_STATUS="reused" 116 + elif [ -n "$PREV_SNAPSHOT" ]; then 117 + SNAP_STATUS="new" 118 + else 119 + SNAP_STATUS="first" 121 120 fi 122 121 123 - echo " Snapshot: $SNAPSHOT ($SNAPSHOT_CHANGED)" 124 - echo " Targets: $TARGETS" 125 - echo " Solve: $SOLVE_CACHED cached" 126 - echo " Layers: $CACHED cached, $NEED_BUILD to build" 127 - echo " Build: $BUILD_OK ok, $BUILD_FAIL failed" 128 - echo " Docs: $DOCS packages, $HTML HTML files" 122 + echo " Snapshot: $SNAPSHOT ($SNAP_STATUS)" 123 + echo " Targets: $TARGETS" 124 + echo " Solver: $SOLVE_CACHED cached, $SOLVE_NEED to solve" 125 + echo " Build layers: $LAYERS_CACHED cached, $LAYERS_NEED to build" 126 + echo " Executor: $EXEC_LINE" 127 + echo " Doc-all OK: $DOC_ALL_OK" 128 + echo " Linked: $LINKED" 129 + echo " Docs summary: $DOC_LINE" 129 130 130 - # Verify snapshot dir exists 131 - SNAP_DIR="$PROFILE_DIR/snapshots/$PROFILE_NAME/$SNAPSHOT" 132 - if [ -d "$SNAP_DIR" ]; then 133 - echo " Snapshot dir: exists" 134 - SOLUTIONS=$(ls "$SNAP_DIR/solutions/" 2>/dev/null | wc -l) 135 - echo " Solutions: $SOLUTIONS cached" 136 - else 137 - echo " Snapshot dir: MISSING!" 131 + # Verify snapshot dir 132 + if [ -n "$SNAPSHOT" ]; then 133 + SNAP_DIR="$PROFILE_DIR/snapshots/$PROFILE_NAME/$SNAPSHOT" 134 + [ -d "$SNAP_DIR" ] && echo " Snapshot dir: exists" || echo " Snapshot dir: MISSING!" 138 135 fi 139 136 140 - # Check status command works 141 - STATUS=$("$DAY11" status \ 142 - --profile "$PROFILE_NAME" \ 143 - --profile-dir "$PROFILE_DIR" 2>&1 | head -1) 144 - echo " Status: $STATUS" 137 + # Check for errors 138 + ERRORS=$(echo "$OUTPUT" | grep -c 'FAIL\|Error\|failed' || true) 139 + if [ "$ERRORS" -gt 0 ]; then 140 + echo " Warnings: $ERRORS lines with FAIL/Error/failed" 141 + echo "$OUTPUT" | grep 'FAIL\|solve failed\|Error' | head -3 | sed 's/^/ /' 142 + fi 145 143 146 - # Accumulate 147 - TOTAL_OK=$((TOTAL_OK + ${BUILD_OK:-0})) 148 - TOTAL_FAIL=$((TOTAL_FAIL + ${BUILD_FAIL:-0})) 149 144 PREV_SNAPSHOT="$SNAPSHOT" 150 - 151 145 echo "" 152 146 done 153 147 154 148 echo "==========================================" 155 149 echo " Summary" 156 150 echo "==========================================" 157 - echo "Total builds: $TOTAL_OK ok, $TOTAL_FAIL failed" 158 151 159 152 # Count snapshots 160 153 N_SNAPSHOTS=$(ls -d "$PROFILE_DIR/snapshots/$PROFILE_NAME/"*/ 2>/dev/null | wc -l) 161 - echo "Snapshots created: $N_SNAPSHOTS" 154 + echo "Snapshots created: $N_SNAPSHOTS (expected 4 — step 5 reuses step 4)" 162 155 163 156 # Count layers in shared cache 164 - CACHE_DIR="$PROFILE_DIR/cache" 165 - if [ -d "$CACHE_DIR" ]; then 166 - N_LAYERS=$(ls -d "$CACHE_DIR"/debian-bookworm-x86_64/*/ 2>/dev/null | wc -l) 157 + CACHE_OS="$PROFILE_DIR/cache/debian-bookworm-x86_64" 158 + if [ -d "$CACHE_OS" ]; then 159 + N_LAYERS=$(ls "$CACHE_OS" | grep -cE '^[0-9a-f]{12}$' || true) 167 160 echo "Layers in shared cache: $N_LAYERS" 168 - HTML_COUNT=$(find "$CACHE_DIR"/debian-bookworm-x86_64/html -name '*.html' 2>/dev/null | wc -l) 161 + HTML_COUNT=$(find "$CACHE_OS/html" -name '*.html' 2>/dev/null | wc -l) 169 162 echo "HTML files: $HTML_COUNT" 170 - fi 171 - 172 - # Verify step 6 was a no-op (same snapshot as step 5) 173 - echo "" 174 - if [ "$N_SNAPSHOTS" -eq 5 ]; then 175 - echo "PASS: Step 6 reused step 5's snapshot (no-op)" 176 - elif [ "$N_SNAPSHOTS" -eq 6 ]; then 177 - echo "NOTE: Step 6 created a new snapshot (repo HEAD may differ)" 178 - else 179 - echo "UNEXPECTED: $N_SNAPSHOTS snapshots" 180 163 fi 181 164 182 165 echo ""