Skip to content

Commit 2b182f5

Browse files
committed
Implement reporting unit test timings in CI
1 parent bcded2d commit 2b182f5

File tree

2 files changed

+167
-4
lines changed

2 files changed

+167
-4
lines changed

.circleci/config.yml

Lines changed: 166 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -149,14 +149,166 @@ commands:
149149
- checkout
150150
- setup_environment:
151151
cache_key: v4.2.0-rust-1.88.0-<< parameters.workspace_member >><< parameters.cache_key_suffix >>-cache
152+
153+
- run:
154+
name: Install cargo-nextest
155+
command: |
156+
cargo install cargo-nextest --locked || true
157+
152158
- run:
153159
name: "Run Tests"
154160
timeout: << parameters.timeout >>
155161
no_output_timeout: << parameters.no_output_timeout >>
156-
command: RUST_MIN_STACK=67108864 cargo test --package=<< parameters.workspace_member >> << parameters.flags >>
162+
command: |
163+
set -euo pipefail
164+
export CARGO_TERM_COLOR=never
165+
166+
# Create artifact + nextest output directories
167+
mkdir -p artifacts target/nextest/ci
168+
169+
# Create artifact + nextest output directories
170+
OUT="artifacts/<< parameters.workspace_member >>-nextest.txt"
171+
CSV="artifacts/<< parameters.workspace_member >>-test-times.csv"
172+
TOP="artifacts/<< parameters.workspace_member >>-slow-tests-top30.csv"
173+
JUNIT="target/nextest/ci/junit.xml"
174+
175+
# Extract and trim flags from parameters
176+
FLAGS_FULL="<< parameters.flags >>"
177+
FLAGS_TRIM="$(printf '%s' "$FLAGS_FULL" | sed -e 's/^[[:space:]]*//' -e 's/[[:space:]]*$//')"
178+
179+
# Split flags into cargo build flags (before --) and libtest args (after --)
180+
RIGHT_OF_SEP=""
181+
if printf '%s' "$FLAGS_TRIM" | grep -q ' -- '; then
182+
BUILD_FLAGS="${FLAGS_TRIM%% -- *}"
183+
RIGHT_OF_SEP="${FLAGS_TRIM#* -- }"
184+
elif printf '%s' "$FLAGS_TRIM" | grep -Eq '^--[[:space:]]'; then
185+
BUILD_FLAGS=""
186+
RIGHT_OF_SEP="${FLAGS_TRIM#-- }"
187+
else
188+
BUILD_FLAGS="$FLAGS_TRIM"
189+
fi
190+
191+
# Remove any --test target flags,they cause nextest build errors
192+
BUILD_FLAGS_SANITIZED="$(printf '%s' "$BUILD_FLAGS" \
193+
| sed -E "s/(^|[[:space:]])--test[[:space:]]+'[^']*'([[:space:]]|$)/ /g" \
194+
| sed -E 's/(^|[[:space:]])--test[[:space:]]+\"[^\"]*\"([[:space:]]|$)/ /g' \
195+
| sed -E 's/(^|[[:space:]])--test[[:space:]]+[^[:space:]]+([[:space:]]|$)/ /g' \
196+
| tr -s ' ' | sed -E 's/^[[:space:]]+//; s/[[:space:]]+$//' \
197+
)"
198+
199+
# Build nextest filter expression from any --skip <pattern> arguments
200+
FILTER_EXPR=""
201+
if [ -n "$RIGHT_OF_SEP" ]; then
202+
SKIPS="$(printf '%s\n' "$RIGHT_OF_SEP" \
203+
| sed -E 's/[[:space:]]+/ /g' \
204+
| grep -oE -- '--skip[[:space:]]+(\"[^\"]+\"|'\''[^'\'']+'\''|[^[:space:]]+)' || true)"
205+
if [ -n "$SKIPS" ]; then
206+
EXPRS=""
207+
tmpf="$(mktemp)"
208+
printf '%s\n' "$SKIPS" > "$tmpf"
209+
while IFS= read -r line; do
210+
pat="${line#--skip }"
211+
pat="${pat%\"}"; pat="${pat#\"}"
212+
pat="${pat%\'}"; pat="${pat#\'}"
213+
pat_rx="${pat//\//\\/}" # escape forward slashes for regex
214+
if [ -z "$EXPRS" ]; then
215+
EXPRS="test(/$pat_rx/)"
216+
else
217+
EXPRS="$EXPRS or test(/$pat_rx/)"
218+
fi
219+
done < "$tmpf"
220+
rm -f "$tmpf"
221+
if [ -n "$EXPRS" ]; then
222+
FILTER_EXPR="not ($EXPRS)"
223+
fi
224+
fi
225+
fi
226+
227+
# Detect and extract --test-threads value (used to set JOBS)
228+
TEST_THREADS=""
229+
if [ -n "$RIGHT_OF_SEP" ]; then
230+
# form: --test-threads=8
231+
TEST_THREADS="$(printf '%s\n' "$RIGHT_OF_SEP" | sed -nE 's/.*--test-threads=([0-9]+).*/\1/p' | head -n1)"
232+
if [ -z "$TEST_THREADS" ]; then
233+
# form: --test-threads 8
234+
TEST_THREADS="$(printf '%s\n' "$RIGHT_OF_SEP" | sed -nE 's/.*--test-threads[[:space:]]+([0-9]+).*/\1/p' | head -n1)"
235+
fi
236+
fi
237+
238+
# Default JOBS, overridden by TEST_THREADS if set
239+
JOBS="${NEXTEST_JOBS:-2}"
240+
if [ -n "$TEST_THREADS" ]; then
241+
JOBS="$TEST_THREADS"
242+
fi
243+
244+
# Create nextest config (disable fail-fast, add slow-timeout + JUnit output)
245+
SLOW_PERIOD="${NEXTEST_SLOW_PERIOD:-60s}"
246+
CFG_ARGS=()
247+
TMPD="$(mktemp -d)"
248+
if [ -f ".config/nextest.toml" ]; then
249+
CFG_ARGS+=(--config-file ".config/nextest.toml")
250+
fi
251+
{
252+
printf '%s\n' '[profile.ci]'
253+
printf '%s\n' 'fail-fast = false'
254+
printf '%s\n' "slow-timeout = \"${SLOW_PERIOD}\""
255+
printf '%s\n' ''
256+
printf '%s\n' '[profile.ci.junit]'
257+
printf '%s\n' 'path = "junit.xml"'
258+
} > "$TMPD/nextest-override.toml"
259+
CFG_ARGS+=(--config-file "$TMPD/nextest-override.toml")
260+
261+
# Print configuration summary for debugging
262+
echo "==> nextest package: << parameters.workspace_member >>"
263+
echo "==> build flags (sanitized): '${BUILD_FLAGS_SANITIZED}'"
264+
echo "==> filter-expr: ${FILTER_EXPR:-<none>} (right-of-sep: ${RIGHT_OF_SEP:-<none>})"
265+
echo "==> test-threads override: ${TEST_THREADS:-<none>}"
266+
echo "==> jobs: ${JOBS}"
267+
268+
# Use plain cargo test if --no-run was requested
269+
# Otherwise, run tests with cargo nextest
270+
if printf '%s' " $BUILD_FLAGS_SANITIZED " | grep -q ' --no-run '; then
271+
cargo test --package="<< parameters.workspace_member >>" $BUILD_FLAGS_SANITIZED | tee "$OUT"
272+
: > "$CSV"; : > "$TOP"
273+
else
274+
cargo nextest run \
275+
-p "<< parameters.workspace_member >>" $BUILD_FLAGS_SANITIZED \
276+
--profile ci -j "${JOBS}" "${CFG_ARGS[@]}" \
277+
--status-level fail --hide-progress-bar \
278+
--no-tests=pass \
279+
${FILTER_EXPR:+--filter-expr "$FILTER_EXPR"} \
280+
2>&1 | tee "$OUT"
281+
282+
# Parse junit.xml to extract test timings
283+
if [ -f "$JUNIT" ]; then
284+
awk -v RS='<testcase ' '
285+
NR>1 {
286+
name=""; classname=""; time="";
287+
start=index($0,"classname=\""); if(start>0){ start+=11; rest=substr($0,start); end=index(rest,"\""); if(end>0) classname=substr(rest,1,end-1) }
288+
start=index($0,"name=\""); if(start>0){ start+=6; rest=substr($0,start); end=index(rest,"\""); if(end>0) name=substr(rest,1,end-1) }
289+
start=index($0,"time=\""); if(start>0){ start+=6; rest=substr($0,start); end=index(rest,"\""); if(end>0) time=substr(rest,1,end-1) }
290+
if(name!="" && time!=""){ full=(classname!=""?classname"::"name:name); printf "%s,%s\n", full, time }
291+
}
292+
' "$JUNIT" > "$CSV" || true
293+
294+
# Sort and store top 30 slowest tests
295+
sort -t, -k2,2nr "$CSV" | head -n 30 > "$TOP" || true
296+
else
297+
echo "WARN: $JUNIT not found; no timings extracted." >&2
298+
: > "$CSV"; : > "$TOP"
299+
fi
300+
fi
301+
302+
- store_artifacts:
303+
path: artifacts
304+
destination: test-timings/<< parameters.workspace_member >>
305+
- store_test_results:
306+
path: target/nextest/ci
307+
157308
- clear_environment:
158309
cache_key: v4.2.0-rust-1.88.0-<< parameters.workspace_member >><< parameters.cache_key_suffix >>-cache
159310

311+
160312
install_rust_nightly:
161313
description: "Install Rust nightly toolchain"
162314
steps:
@@ -446,7 +598,8 @@ jobs:
446598
- run_test:
447599
workspace_member: snarkvm-ledger
448600
flags: --release --features=rocks -- --test-threads=2
449-
timeout: 20m
601+
no_output_timeout: 20m
602+
timeout: 30m
450603

451604
ledger-with-valid-solutions:
452605
executor: rust-docker
@@ -470,6 +623,8 @@ jobs:
470623
steps:
471624
- run_test:
472625
workspace_member: snarkvm-ledger-block
626+
no_output_timeout: 20m
627+
timeout: 30m
473628

474629
ledger-committee:
475630
executor: rust-docker
@@ -593,6 +748,8 @@ jobs:
593748
workspace_member: snarkvm-parameters
594749
flags: -- --test-threads=2 --ignored test_load_bytes_mini
595750
cache_key_suffix: -v-{{ epoch }}
751+
no_output_timeout: 20m
752+
timeout: 30m
596753

597754
synthesizer:
598755
executor: rust-docker
@@ -601,11 +758,14 @@ jobs:
601758
- run_test:
602759
workspace_member: snarkvm-synthesizer
603760
flags: --lib --bins
604-
timeout: 20m
761+
no_output_timeout: 20m
762+
timeout: 30m
605763

606764
synthesizer-test:
607765
executor: rust-docker
608766
resource_class: << pipeline.parameters.twoxlarge >>
767+
environment:
768+
NEXTEST_JOBS: "4"
609769
steps:
610770
- run_test:
611771
workspace_member: snarkvm-synthesizer
@@ -631,7 +791,7 @@ jobs:
631791
# test_vm_execute_and_finalize can take over 10 minutes in the current setup
632792
no_output_timeout: 20m
633793
workspace_member: snarkvm-synthesizer
634-
flags: --test '*' --features test -- --test-threads=4
794+
flags: --test '*' --features test -- --test-threads=4
635795
cache_key_suffix: -integration
636796

637797
synthesizer-process:
@@ -662,6 +822,8 @@ jobs:
662822
synthesizer-program-integration:
663823
executor: rust-docker
664824
resource_class: << pipeline.parameters.twoxlarge >>
825+
environment:
826+
NEXTEST_JOBS: "4"
665827
steps:
666828
- run_test:
667829
workspace_member: snarkvm-synthesizer-program

.gitignore

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -14,3 +14,4 @@
1414
!**/beta-h.usrs
1515
!**/neg-powers-of-beta.usrs
1616
**/proptest-regressions/
17+
artifacts

0 commit comments

Comments
 (0)