Files
cfal dd2f370a9f Expand whole-path routing regression coverage (#266)
Expand regression coverage for whole-path routing across forwarding, DNS, rule downloads, probes, and session reuse to guard against route changes affecting established flows or shared state. Add native Caddy and WireGuard cases plus dual-stack kernel-TUN coverage, strengthen routing boundaries and controller assertions, enable routing and consumer suites on macOS, and make test fixtures portable across platforms.
2026-11-02 03:09:03 +08:00

549 lines
22 KiB
YAML

name: Test
on:
push:
branches: [ master ]
pull_request:
branches: [ master ]
env:
CARGO_TERM_COLOR: always
RUST_BACKTRACE: 1
SING_BOX_VERSION: 1.14.1
SING_BOX_SHA256: 12cb2816b52febb356f6a885b740cc8758c3f30b8ae0ca8edba80f0d2d35343f
ANYTLS_SING_BOX_VERSION: 1.15.0-alpha.13
ANYTLS_SING_BOX_SHA256: bfc39f643a5ecc1b702c5723ff662e61dde813f12b3af0d0cfce4f629ff74047
MIHOMO_VERSION: 1.19.31
MIHOMO_SHA256: 04cf9f09671704f839ddbee2e93069dc831a4123a75281e725d1d96ab9ac1afc
XRAY_VERSION: 26.10.31
XRAY_SHA256: e4bd9119f50ae7c41658b1abd0679ae06f98bd864a2907ff33f2f2cf7081b214
SSSERVER_VERSION: 1.25.0
SSSERVER_SHA256: 874f817fcf3e6d7681ec715a1c13c686c6eaae936524d102639b38364f3966ae
XRAY_COMMIT: a4a3dff5b20c2faae567b742c172c7b95e75c083
MIHOMO_ALPHA_COMMIT: 94b13aed49b8dc41973e4bb3f721c5fa2c5c5c1f
NAIVE_CADDY_VERSION: 2.11.7-naive
NAIVE_CADDY_SHA256: ac97057c925bb098eb575e7b0cb8bd547d4c5f845b09a3dedfbe34444aa7ae88
jobs:
dependency-notices:
name: Dependency notices
runs-on: ubuntu-latest
timeout-minutes: 15
steps:
- uses: actions/checkout@v7
- uses: dtolnay/rust-toolchain@stable
- uses: taiki-e/install-action@v2
with:
tool: cargo-about@0.9.3
- name: Verify generated dependency licenses and artifact checks
run: |
python3 scripts/generate-release-notices.py
git diff --exit-code -- licenses/dependencies.txt
cargo fetch --locked
python3 scripts/test-release-notices.py
wireguard-interop:
name: WireGuard interoperability
runs-on: ubuntu-latest
timeout-minutes: 60
env:
GOMAXPROCS: 1
WG_SING_BOX_COMMIT: 7054cac5124465ffdb145870d7c01885566711b1
WG_XRAY_COMMIT: a4a3dff5b20c2faae567b742c172c7b95e75c083
steps:
- uses: actions/checkout@v7
- uses: dtolnay/rust-toolchain@stable
- name: Restore pinned WireGuard peers
id: wireguard-peers
uses: actions/cache/restore@v6
with:
path: ~/.cache/shoes-wireguard
key: ${{ runner.os }}-${{ runner.arch }}-wireguard-peers-go1.27.3-${{ env.WG_SING_BOX_COMMIT }}-${{ env.WG_XRAY_COMMIT }}
- uses: actions/setup-go@v7
if: steps.wireguard-peers.outputs.cache-hit != 'true'
with:
go-version: '1.27.3'
cache: false
- uses: actions/cache@v6
with:
path: |
~/.cargo/registry
~/.cargo/git
target
~/go/pkg/mod
~/.cache/go-build
key: ${{ runner.os }}-wireguard-${{ hashFiles('**/Cargo.lock') }}-${{ env.WG_SING_BOX_COMMIT }}-${{ env.WG_XRAY_COMMIT }}
- name: Build pinned independent WireGuard peers
if: steps.wireguard-peers.outputs.cache-hit != 'true'
run: |
mkdir -p "$HOME/.cache/shoes-wireguard"
git clone --filter=blob:none --no-checkout https://github.com/SagerNet/sing-box.git "$HOME/wg-sing-box"
git -C "$HOME/wg-sing-box" checkout --detach "$WG_SING_BOX_COMMIT"
test "$(git -C "$HOME/wg-sing-box" rev-parse HEAD)" = "$WG_SING_BOX_COMMIT"
git clone --filter=blob:none --no-checkout https://github.com/XTLS/Xray-core.git "$HOME/wg-xray"
git -C "$HOME/wg-xray" checkout --detach "$WG_XRAY_COMMIT"
test "$(git -C "$HOME/wg-xray" rev-parse HEAD)" = "$WG_XRAY_COMMIT"
cd "$HOME/wg-sing-box"
go build -mod=readonly -p 1 -tags with_gvisor,with_wireguard -o "$HOME/.cache/shoes-wireguard/sing-box" ./cmd/sing-box
cd "$HOME/wg-xray"
go build -mod=readonly -p 1 -o "$HOME/.cache/shoes-wireguard/xray" ./main
- name: Save pinned WireGuard peers
if: steps.wireguard-peers.outputs.cache-hit != 'true'
uses: actions/cache/save@v6
with:
path: ~/.cache/shoes-wireguard
key: ${{ steps.wireguard-peers.outputs.cache-primary-key }}
- name: Select WireGuard peers
run: |
echo "SHOES_TEST_SING_BOX_BIN=$HOME/.cache/shoes-wireguard/sing-box" >> "$GITHUB_ENV"
echo "SHOES_TEST_XRAY_BIN=$HOME/.cache/shoes-wireguard/xray" >> "$GITHUB_ENV"
"$HOME/.cache/shoes-wireguard/sing-box" version
"$HOME/.cache/shoes-wireguard/xray" version
- name: Test both roles, inner address families, and real rekey
env:
RUST_LOG: warn
run: >-
.github/require-tests.sh cargo test -p shoes --locked --features optional-peer-tests
--test optional_peer wireguard -- --ignored --test-threads=1
unit-tests:
name: Unit tests
runs-on: ubuntu-latest
timeout-minutes: 25
steps:
- uses: actions/checkout@v7
- name: Install Rust
uses: dtolnay/rust-toolchain@stable
- name: Cache cargo registry
uses: actions/cache@v6
with:
path: ~/.cargo/registry
key: ${{ runner.os }}-cargo-registry-${{ hashFiles('**/Cargo.lock') }}
- name: Cache cargo index
uses: actions/cache@v6
with:
path: ~/.cargo/git
key: ${{ runner.os }}-cargo-index-${{ hashFiles('**/Cargo.lock') }}
- name: Cache cargo build
uses: actions/cache@v6
with:
path: target
key: ${{ runner.os }}-cargo-build-target-${{ hashFiles('**/Cargo.lock') }}
- name: Build
run: cargo build --workspace --locked --verbose
- name: Run local tests
run: cargo test --workspace --locked --lib --bins --verbose
- name: Check resolved configuration type contracts
run: .github/require-tests.sh cargo test -p shoes --locked --doc config::resolved
- uses: actions/setup-go@v7
with:
go-version: '1.27.3'
cache: false
- name: Build pinned AnyTLS reference peer
working-directory: tests/fixtures/anytls-peer
run: GOMAXPROCS=1 go build -mod=readonly -p 1 -o "$RUNNER_TEMP/shoes-anytls-peer" .
- name: Test AnyTLS pooling against the reference peer
env:
ANYTLS_REFERENCE_PEER: ${{ runner.temp }}/shoes-anytls-peer
CARGO_BUILD_JOBS: 1
run: >-
.github/require-tests.sh cargo test -p shoes --locked --lib
reference_peer_tcp_uot_and_concurrent_session_reuse -- --ignored
- name: Run process contract tests
run: cargo test -p shoes --locked --test process_contract --verbose -- --test-threads=1
- name: Run rule compilation allocation regression
run: cargo test -p shoes --locked --test rule_allocation --verbose
- name: Run retention allocation and matcher invariants
run: cargo test -p shoes --locked --test retention_allocation --test matcher_invariants -- --nocapture
- name: Run bounded rule property corpus
env:
CARGO_BUILD_JOBS: 1
CARGO_TARGET_X86_64_UNKNOWN_LINUX_GNU_RUNNER: timeout --kill-after=5s 30s
run: >-
.github/require-tests.sh timeout --kill-after=10s 10m cargo test -p shoes --locked
--test rule_properties -- --test-threads=1
- name: Run host FFI tests
run: cargo test -p shoes --locked --lib --features ffi -- --test-threads=1
benchmark-tooling:
name: Benchmark tooling
runs-on: ubuntu-latest
timeout-minutes: 5
steps:
- uses: actions/checkout@v7
- uses: actions/setup-node@v7
with:
node-version: '26'
package-manager-cache: false
- name: Test benchmark harness and manifests
run: node --test benches/*.test.mjs scripts/coverage-lines.test.mjs scripts/require-tests.test.mjs
tun-macos:
name: Rootless TUN (macOS)
runs-on: macos-latest
timeout-minutes: 30
steps:
- uses: actions/checkout@v7
- name: Install Rust
uses: dtolnay/rust-toolchain@stable
- name: Cache Rust build
uses: actions/cache@v6
with:
path: |
~/.cargo/registry
~/.cargo/git
target
key: ${{ runner.os }}-tun-cargo-${{ hashFiles('**/Cargo.lock') }}
- name: Run raw-FD and packet tests without a TUN device
run: >-
.github/require-tests.sh cargo test -p shoes --locked --lib --features ffi tun:: -- --test-threads=1
- name: Run rootless WireGuard, FFI, and process lifecycle tests
run: |
.github/require-tests.sh cargo test -p shoes --locked --lib --features ffi wireguard -- --test-threads=1
.github/require-tests.sh cargo test -p shoes --locked --test process_contract --features ffi wireguard -- --test-threads=1
- name: Run FFI rule-source startup and packet tests
run: >-
.github/require-tests.sh cargo test -p shoes --locked --lib --features ffi ffi::common::rule_source_tests -- --test-threads=1
- name: Run rule-source routing and filesystem reload tests
run: >-
.github/require-tests.sh cargo test -p shoes --locked --test process_contract --features ffi rule_sets:: -- --test-threads=1
- name: Run process filesystem watcher unit tests
run: >-
.github/require-tests.sh cargo test -p shoes --locked --lib process:: -- --test-threads=1
- name: Run native filesystem watcher regressions
run: >-
.github/require-tests.sh cargo test -p shoes --locked --lib file_watcher:: -- --test-threads=1
- name: Run remote-rule installer tests
run: >-
.github/require-tests.sh cargo test -p shoes --locked --lib --features ffi
rule_updater:: -- --test-threads=1
- name: Run whole-path routing and consumer tests
env:
CARGO_BUILD_JOBS: 1
run: |
for filter in client_proxy_chain:: config::composition_tests:: config::resolved:: control:: dns:: naiveproxy::; do
.github/require-tests.sh cargo test -p shoes --locked --lib --features ffi "$filter" -- --test-threads=1
done
.github/require-tests.sh cargo test -p shoes --locked --test process_contract --features ffi clash_api:: -- --test-threads=1
- name: Run remote-rule process tests
run: >-
.github/require-tests.sh cargo test -p shoes --locked --test process_contract --features ffi
remote_rules:: -- --test-threads=1
- name: Run WebSocket unit and configuration tests
env:
CARGO_BUILD_JOBS: 1
run: |
.github/require-tests.sh cargo test -p shoes --locked --lib --features ffi websocket:: -- --test-threads=1
.github/require-tests.sh cargo test -p shoes --locked --lib --features ffi config::validate::tests::websocket_early_data_ -- --test-threads=1
- name: Run local WebSocket early-data interoperability tests
env:
CARGO_BUILD_JOBS: 1
RUST_LOG: warn
run: >-
.github/require-tests.sh cargo test -p shoes --locked --features ffi,integration-tests
--test ws_early_data -- --test-threads=1
integration-tests:
name: Integration (${{ matrix.name }})
needs: unit-tests
runs-on: ubuntu-latest
timeout-minutes: 30
strategy:
fail-fast: false
matrix:
include:
- name: local protocols
features: integration-tests
target: integration
filters: ""
peer: sing-box
- name: optional peer interoperability
features: optional-peer-tests
target: optional_peer
filters: ""
peer: mihomo
- name: XHTTP interoperability
features: integration-tests
target: xhttp_interop
filters: ""
peer: xhttp
- name: external DNS
features: external-network-tests
target: external_network
filters: dns resolver vless_doh vless_dot
peer: sing-box
- name: external proxies
features: external-network-tests
target: external_network
filters: portforward shadowtls
peer: sing-box
- name: external REALITY
features: external-network-tests
target: external_network
filters: reality
peer: sing-box
- name: kernel WireGuard
features: privileged-tests
target: privileged
filters: wireguard_kernel
peer: kernel-wireguard
steps:
- uses: actions/checkout@v7
- name: Install Rust
uses: dtolnay/rust-toolchain@stable
- name: Cache cargo registry
uses: actions/cache@v6
with:
path: ~/.cargo/registry
key: ${{ runner.os }}-cargo-registry-${{ hashFiles('**/Cargo.lock') }}
- name: Cache cargo index
uses: actions/cache@v6
with:
path: ~/.cargo/git
key: ${{ runner.os }}-cargo-index-${{ hashFiles('**/Cargo.lock') }}
- name: Cache cargo build
uses: actions/cache@v6
with:
path: target
key: ${{ runner.os }}-cargo-build-target-${{ hashFiles('**/Cargo.lock') }}
- name: Install kernel WireGuard test tools
if: matrix.peer == 'kernel-wireguard'
run: |
sudo apt-get update
sudo apt-get install --yes --no-install-recommends wireguard-tools iproute2
uname -r
wg --version
- name: Install sing-box test peer
if: matrix.peer == 'sing-box' || matrix.target == 'optional_peer'
run: |
archive="$RUNNER_TEMP/sing-box.tar.gz"
curl --fail --location --retry 3 \
--output "$archive" \
"https://github.com/SagerNet/sing-box/releases/download/v${SING_BOX_VERSION}/sing-box-${SING_BOX_VERSION}-linux-amd64.tar.gz"
echo "${SING_BOX_SHA256} ${archive}" | sha256sum --check --strict
tar --extract --gzip --file "$archive" --directory "$RUNNER_TEMP"
binary="$RUNNER_TEMP/sing-box-${SING_BOX_VERSION}-linux-amd64/sing-box"
chmod +x "$binary"
echo "SHOES_TEST_SING_BOX_BIN=$binary" >> "$GITHUB_ENV"
- name: Install sing-box AnyTLS padding test peer
if: matrix.target == 'optional_peer'
run: |
archive="$RUNNER_TEMP/sing-box-anytls.tar.gz"
curl --fail --location --retry 3 \
--output "$archive" \
"https://github.com/SagerNet/sing-box/releases/download/v${ANYTLS_SING_BOX_VERSION}/sing-box-${ANYTLS_SING_BOX_VERSION}-linux-amd64.tar.gz"
echo "${ANYTLS_SING_BOX_SHA256} ${archive}" | sha256sum --check --strict
tar --extract --gzip --file "$archive" --directory "$RUNNER_TEMP"
binary="$RUNNER_TEMP/sing-box-${ANYTLS_SING_BOX_VERSION}-linux-amd64/sing-box"
chmod +x "$binary"
echo "SHOES_TEST_ANYTLS_SING_BOX_BIN=$binary" >> "$GITHUB_ENV"
- name: Install Mihomo test peer
if: matrix.peer == 'mihomo'
run: |
archive="$RUNNER_TEMP/mihomo.gz"
binary="$RUNNER_TEMP/mihomo"
curl --fail --location --retry 3 \
--output "$archive" \
"https://github.com/MetaCubeX/mihomo/releases/download/v${MIHOMO_VERSION}/mihomo-linux-amd64-compatible-v${MIHOMO_VERSION}.gz"
echo "${MIHOMO_SHA256} ${archive}" | sha256sum --check --strict
gzip --decompress --stdout "$archive" > "$binary"
chmod +x "$binary"
echo "SHOES_TEST_MIHOMO_BIN=$binary" >> "$GITHUB_ENV"
- name: Install Xray test peer
if: matrix.target == 'optional_peer'
run: |
archive="$RUNNER_TEMP/xray.zip"
curl --fail --location --retry 3 \
--output "$archive" \
"https://github.com/XTLS/Xray-core/releases/download/v${XRAY_VERSION}/Xray-linux-64.zip"
echo "${XRAY_SHA256} ${archive}" | sha256sum --check --strict
unzip "$archive" xray -d "$RUNNER_TEMP/xray-peer"
binary="$RUNNER_TEMP/xray-peer/xray"
chmod +x "$binary"
echo "SHOES_TEST_XRAY_BIN=$binary" >> "$GITHUB_ENV"
- name: Install shadowsocks-rust test peer
if: matrix.target == 'optional_peer'
run: |
archive="$RUNNER_TEMP/shadowsocks-rust.tar.xz"
curl --fail --location --retry 3 \
--output "$archive" \
"https://github.com/shadowsocks/shadowsocks-rust/releases/download/v${SSSERVER_VERSION}/shadowsocks-v${SSSERVER_VERSION}.x86_64-unknown-linux-gnu.tar.xz"
echo "${SSSERVER_SHA256} ${archive}" | sha256sum --check --strict
tar --extract --xz --file "$archive" --directory "$RUNNER_TEMP" ssserver
binary="$RUNNER_TEMP/ssserver"
chmod +x "$binary"
echo "SHOES_TEST_SSSERVER_BIN=$binary" >> "$GITHUB_ENV"
- name: Install native Naive Caddy test peer
if: matrix.target == 'optional_peer'
run: |
archive="$RUNNER_TEMP/caddy-forwardproxy-naive.tar.xz"
curl --fail --location --retry 3 \
--output "$archive" \
"https://github.com/klzgrad/forwardproxy/releases/download/v${NAIVE_CADDY_VERSION}/caddy-forwardproxy-naive.tar.xz"
echo "${NAIVE_CADDY_SHA256} ${archive}" | sha256sum --check --strict
tar --extract --xz --file "$archive" --directory "$RUNNER_TEMP"
binary="$RUNNER_TEMP/caddy-forwardproxy-naive/caddy"
chmod +x "$binary"
echo "SHOES_TEST_NAIVE_CADDY_BIN=$binary" >> "$GITHUB_ENV"
- name: Run sing-box mux lifecycle tests
if: matrix.name == 'local protocols'
run: >-
.github/require-tests.sh cargo test -p shoes --locked --lib --features integration-tests
h2mux::h2mux_server_session::singbox_interop_tests:: -- --test-threads=1
- name: Run bounded XUDP payload integrity regression
if: matrix.name == 'local protocols'
run: >-
.github/require-tests.sh cargo test -p shoes --locked --features stress-tests
--test stress vless_xudp_long_lived::test_content_integrity_varying_sizes
-- --exact --test-threads=1
- name: Run native QUIC client interoperability tests
if: matrix.target == 'optional_peer'
run: >-
.github/require-tests.sh cargo test -p shoes --locked --lib --features optional-peer-tests
quic_client::interop:: -- --test-threads=1
- name: Restore pinned XHTTP peers
if: matrix.target == 'xhttp_interop'
id: xhttp-peers
uses: actions/cache/restore@v6
with:
path: |
~/.cache/shoes-xray/xray
~/.cache/shoes-mihomo/mihomo
key: ${{ runner.os }}-xhttp-peers-go1.27.3-${{ env.XRAY_COMMIT }}-${{ env.MIHOMO_ALPHA_COMMIT }}
- name: Install Go for XHTTP peers
if: matrix.target == 'xhttp_interop' && steps.xhttp-peers.outputs.cache-hit != 'true'
uses: actions/setup-go@v7
with:
go-version: '1.27.3'
cache: false
- name: Build pinned Xray test peer
if: matrix.target == 'xhttp_interop' && steps.xhttp-peers.outputs.cache-hit != 'true'
run: |
source="$HOME/.cache/shoes-xray"
git clone --filter=blob:none --no-checkout https://github.com/XTLS/Xray-core.git "$source"
git -C "$source" checkout --detach "$XRAY_COMMIT"
test "$(git -C "$source" rev-parse HEAD)" = "$XRAY_COMMIT"
cd "$source"
GOMAXPROCS=1 go build -p 1 -o "$source/xray" ./main
- name: Build pinned Mihomo Alpha test peer
if: matrix.target == 'xhttp_interop' && steps.xhttp-peers.outputs.cache-hit != 'true'
run: |
source="$HOME/.cache/shoes-mihomo"
git clone --filter=blob:none --no-checkout https://github.com/MetaCubeX/mihomo.git "$source"
git -C "$source" checkout --detach "$MIHOMO_ALPHA_COMMIT"
test "$(git -C "$source" rev-parse HEAD)" = "$MIHOMO_ALPHA_COMMIT"
cd "$source"
GOMAXPROCS=1 CGO_ENABLED=0 go build -p 1 \
-ldflags "-X github.com/metacubex/mihomo/constant.Version=alpha-${MIHOMO_ALPHA_COMMIT:0:8}" \
-o "$source/mihomo" .
- name: Save pinned XHTTP peers
if: matrix.target == 'xhttp_interop' && steps.xhttp-peers.outputs.cache-hit != 'true'
uses: actions/cache/save@v6
with:
path: |
~/.cache/shoes-xray/xray
~/.cache/shoes-mihomo/mihomo
key: ${{ steps.xhttp-peers.outputs.cache-primary-key }}
- name: Select XHTTP peers
if: matrix.target == 'xhttp_interop'
run: |
echo "SHOES_TEST_XRAY_BIN=$HOME/.cache/shoes-xray/xray" >> "$GITHUB_ENV"
echo "SHOES_TEST_MIHOMO_BIN=$HOME/.cache/shoes-mihomo/mihomo" >> "$GITHUB_ENV"
"$HOME/.cache/shoes-xray/xray" version
"$HOME/.cache/shoes-mihomo/mihomo" -v
- name: Run WebSocket early-data interoperability tests
if: matrix.target == 'optional_peer'
env:
CARGO_BUILD_JOBS: 1
RUST_LOG: warn
WS_SING_BOX_VERSION: 1.14.3
WS_SING_BOX_SHA256: e2bdf179c15a3652955dc44867e33ca0965c9c9b91b34267dcc5a5d639a5feee
run: |
# Older sing-box clients discard response bytes read ahead during the upgrade.
archive="$RUNNER_TEMP/ws-sing-box.tar.gz"
curl --fail --location --retry 3 \
--output "$archive" \
"https://github.com/SagerNet/sing-box/releases/download/v${WS_SING_BOX_VERSION}/sing-box-${WS_SING_BOX_VERSION}-linux-amd64.tar.gz"
echo "${WS_SING_BOX_SHA256} ${archive}" | sha256sum --check --strict
tar --extract --gzip --file "$archive" --directory "$RUNNER_TEMP"
export SHOES_TEST_SING_BOX_BIN="$RUNNER_TEMP/sing-box-${WS_SING_BOX_VERSION}-linux-amd64/sing-box"
"$SHOES_TEST_SING_BOX_BIN" version
.github/require-tests.sh cargo test -p shoes --locked --features optional-peer-tests \
--test ws_early_data -- --test-threads=1
- name: Run integration tests
env:
TEST_FILTERS: ${{ matrix.filters }}
RUST_LOG: warn
run: |
if [ -z "$TEST_FILTERS" ]; then
cargo test -p shoes --locked \
--features "${{ matrix.features }}" \
--test "${{ matrix.target }}" \
--verbose -- --test-threads=1
else
for filter in $TEST_FILTERS; do
.github/require-tests.sh cargo test -p shoes --locked \
--features "${{ matrix.features }}" \
--test "${{ matrix.target }}" \
--verbose "$filter::" -- --test-threads=1
done
fi
- name: Run public HTTPS rule delivery
if: matrix.name == 'external proxies'
run: >-
.github/require-tests.sh cargo test -p shoes --locked --features external-network-tests
--test process_contract remote_rules::public_https -- --test-threads=1