forked from otter-sec/anchor
-
Notifications
You must be signed in to change notification settings - Fork 0
217 lines (198 loc) · 9.49 KB
/
tests.yaml
File metadata and controls
217 lines (198 loc) · 9.49 KB
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
name: Tests
on:
push:
branches:
- anchor-next
paths-ignore:
- "docs/**"
pull_request:
paths-ignore:
- "docs/**"
workflow_dispatch:
env:
SOLANA_VERSION: "3.1.10"
jobs:
test-v2:
name: v2 Tests
runs-on: ubuntu-latest
timeout-minutes: 60
steps:
- uses: actions/checkout@v3
- uses: ./.github/actions/setup/
# tests-v2 runs `cargo build-sbf` on its member programs; the rest of
# the v2 stack doesn't need the Solana toolchain but installing it
# once for the whole job is cheaper than splitting the matrix.
- uses: ./.github/actions/setup-solana/
# Split caches: the cargo home (registry/git/installed binaries)
# only churns on dep additions, whereas ./target/ churns on any
# source change. Keeping them separate means a pure-source PR still
# gets a clean registry restore, and restore-keys prefix fallbacks
# let Cargo.lock-changing PRs pick up a partial target/ from a
# prior run and only rebuild the diff instead of the world.
- uses: actions/cache@v4
name: Cache Cargo home
with:
path: |
~/.cargo/bin/
~/.cargo/registry/index/
~/.cargo/registry/cache/
~/.cargo/git/db/
key: cargo-home-${{ runner.os }}-v2-${{ hashFiles('**/Cargo.lock') }}
restore-keys: |
cargo-home-${{ runner.os }}-v2-
- uses: actions/cache@v4
name: Cache cargo target
with:
path: ./target/
# Per-ref primary key so concurrent branches don't clobber each
# other's incremental state; restore chain falls back first to
# the lockfile-matched cache from any ref, then to any v2 cache.
key: cargo-target-${{ runner.os }}-v2-${{ hashFiles('**/Cargo.lock') }}-${{ github.ref_name }}
restore-keys: |
cargo-target-${{ runner.os }}-v2-${{ hashFiles('**/Cargo.lock') }}-
cargo-target-${{ runner.os }}-v2-
# `bench/` is its own cargo workspace, so its build artifacts live
# in `bench/target/` (separate tree from `./target/` above). The
# `anchor debugger` smoke step below triggers `cargo build-sbf` +
# `cargo test --features profile` for each anchor-v2 program —
# all of which write here. Keyed on `bench/Cargo.lock` since that
# is the lockfile that gates bench-workspace dep resolution.
- uses: actions/cache@v4
name: Cache bench cargo target
with:
path: ./bench/target/
key: cargo-target-bench-${{ runner.os }}-v2-${{ hashFiles('bench/Cargo.lock') }}-${{ github.ref_name }}
restore-keys: |
cargo-target-bench-${{ runner.os }}-v2-${{ hashFiles('bench/Cargo.lock') }}-
cargo-target-bench-${{ runner.os }}-v2-
# Install platform-tools up front so the concurrent `cargo build-sbf`
# invocations fired by parallel test threads don't race on the
# one-time extract-then-rename of `~/.cache/solana/v1.52/platform-tools`
# (surfaces in CI as `Unable to rename: No such file or directory`).
# Version must match `tests-v2/src/lib.rs::build_program`.
- run: cargo build-sbf --tools-version v1.52 --install-only
# Tools for `make coverage-v2`:
# - lcov for merging sbf.lcov + host.lcov and generating the report.
# - cargo-llvm-cov for host-side instrumentation coverage.
#
# The three direct `cargo test -p anchor-{lang,spl}-v2` / `-p tests-v2`
# invocations that used to live here (including `anchor-lang-v2`'s
# `--features testing` flag for the Miri-witnesses scaffold) are
# subsumed by the `make coverage-v2` call below — the Makefile's
# host-coverage step runs the same packages under instrumentation.
- run: sudo apt-get install -y lcov
- name: Install cargo-llvm-cov
run: |
if ! command -v cargo-llvm-cov >/dev/null; then
cargo install cargo-llvm-cov --locked
fi
# `make coverage-v2` runs the same v2 test suites we used to invoke
# directly (`cargo test -p anchor-lang-v2 -p anchor-spl-v2 -p tests-v2`)
# but under llvm-cov instrumentation, and additionally runs SBF
# trace-based coverage via `anchor coverage`. Test pass/fail signal
# is preserved — a failing test exits the make invocation non-zero.
# The combined LCOV is shipped to Codecov in the step below.
- run: make coverage-v2
# Upload to Codecov. The action handles comment posting on PRs via
# the Codecov GitHub App — including fork PRs, where the token
# flows through their bot instead of our workflow's `GITHUB_TOKEN`.
#
# `token` is optional on public repos (Codecov supports tokenless
# uploads for open-source projects) but passing the repo-scoped
# secret when available speeds upload discovery and is required on
# anchor-next pushes. On fork PRs the secret is empty — the action
# falls through to the tokenless flow automatically.
- name: Upload coverage to Codecov
uses: codecov/codecov-action@v5
with:
token: ${{ secrets.CODECOV_TOKEN }}
files: target/coverage/combined.lcov
flags: v2
fail_ci_if_error: false
# Miri (Tree Borrows) — UB + aliasing + provenance checks on the
# same integration tests above. Catches things the default cargo
# test run can't: `AccountView` copy-aliasing, `Slab::header_ptr`
# write provenance, `AccountCursor::next` strict-provenance walks.
# Caches the nightly toolchain and miri component to avoid a
# per-run rustup fetch (~200 MB).
- uses: actions/cache@v4
name: Cache rustup nightly + miri
with:
path: |
~/.rustup/toolchains/
~/.rustup/update-hashes/
~/.rustup/settings.toml
key: rustup-miri-${{ runner.os }}-v2-${{ hashFiles('rust-toolchain*', '**/rust-toolchain*') }}
restore-keys: |
rustup-miri-${{ runner.os }}-v2-
- name: Install nightly + miri
run: |
rustup toolchain install nightly --component miri --profile minimal
cargo +nightly miri setup
- name: Miri (Tree Borrows) for anchor-lang-v2 + anchor-spl-v2
env:
MIRIFLAGS: '-Zmiri-tree-borrows'
run: cargo +nightly miri test -p anchor-lang-v2 -p anchor-spl-v2 --tests --features anchor-lang-v2/testing
# Kani harnesses live in-tree under `#[cfg(kani)]` and are runnable
# locally via `cargo kani -p anchor-lang-v2` / `-p anchor-spl-v2`,
# but are not wired into CI: kani-verifier 0.67.0 has an
# upstream-deterministic `.unwrap()` panic on CBMC `ERROR` status
# lines (kani#4519, fixed on main in #4540, not yet released).
# Re-enable once kani-verifier ≥ 0.68.0 ships on crates.io.
# CLI: unit-test + build sanity only. No anchor-binary install,
# no `anchor build`/`anchor test` flows, no tests/* integration.
- name: Run CLI Tests
run: cargo test -p anchor-cli
# Install the `anchor` bin to `~/.cargo/bin` (already on PATH via
# rustup). Shared `./target/` via `--target-dir` means the build
# reuses artifacts from prior `cargo test -p anchor-cli` instead
# of starting from scratch in a temp tree; `--debug` skips release
# opt (smoke test — runtime speed doesn't matter).
- name: Install CLI
run: cargo install --path cli --locked --debug --force --target-dir ./target
# `anchor debugger` smoke test. These bench crates have no
# Anchor.toml, so the CLI falls into loose mode and runs the full
# pipeline: `cargo build-sbf` → `cargo test --features profile` →
# TUI. We wrap in `script` so crossterm can enter raw mode on a
# pty (GHA stdin/stdout aren't ttys) and `timeout` kills the TUI
# once it's in its event loop.
#
# Failure handling: each `prog` is run in its own subshell so
# `cd` is scoped (no `popd` bookkeeping) and so `|| rc=$?`
# captures the inner pipeline's exit status directly. Build and
# TUI phases are kept distinct so a build break is reported as
# such instead of bleeding into a confusing TUI failure.
#
# Exit-code semantics for the TUI phase:
# 124 → timeout-killed (TUI was still alive — success)
# 0 → clean exit (rare in CI; legal)
# else → real failure; dump the pty log
- name: anchor debugger TUI launches for bench anchor-v2 programs
run: |
set -euo pipefail
fail=0
for prog in bench/programs/*/anchor-v2; do
echo "::group::anchor debugger in $prog"
log=$(mktemp)
# Build phase — surface build breaks as a distinct error.
if ! ( cd "$prog" && anchor coverage ); then
echo "::error::`anchor coverage` failed in $prog"
fail=1
rm -f "$log"
echo "::endgroup::"
continue
fi
# TUI phase — `|| rc=$?` captures the subshell exit cleanly
# without toggling `set +e`/`set -e` around it.
rc=0
( cd "$prog" && timeout --kill-after=30 30 \
script -qec 'anchor debugger --skip-build' "$log" ) || rc=$?
if [ "$rc" -ne 0 ] && [ "$rc" -ne 124 ]; then
echo "::error::anchor debugger failed in $prog (exit $rc)"
cat "$log"
fail=1
fi
rm -f "$log"
echo "::endgroup::"
done
exit "$fail"