Files
shoes/.github/workflows/test.yml
cfal a72cc7f0bb Expand test coverage and fix the protocol bugs it exposed (#212)
Broadens unit and integration coverage of WebSocket framing, ShadowTLS and REALITY ClientHello parsing, VLESS fallback, Hysteria2 and TUIC authentication, DNS, and connection budgets across reloads, and fixes the defects it exposed: WebSocket streams now accept control frames up to RFC 6455's 125-byte limit, no longer report EOF into partly filled read buffers, and flush buffered frames before shutting down; the shared ClientHello parser reassembles hellos split across TLS records, as Go TLS stacks do; and message-stream shutdown after EOF is bounded like the stream relay's. It also builds the binary on the library crate instead of compiling every module twice, removes the unused UdpMessageStream, moves ShadowTLS interop tests to local handshake servers, makes filtered CI test steps fail when they select no tests, consolidates the WebSocket payload-reading code, and documents how to measure coverage along with the remaining gaps, including why WebSocket Close frames stay unanswered.
2026-10-23 06:18:10 -04:00

410 lines
15 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
MIHOMO_VERSION: 1.19.31
MIHOMO_SHA256: 04cf9f09671704f839ddbee2e93069dc831a4123a75281e725d1d96ab9ac1afc
XRAY_VERSION: 26.10.21
XRAY_SHA256: 0eec3ed4163c273d8c8bf821f7d219e257921cf584aa3fb970715e458e631978
XRAY_COMMIT: 1b1647cd9ddf0abc34b981d4611fc03bcac1ca74
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.2
- 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: 1b1647cd9ddf0abc34b981d4611fc03bcac1ca74
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: 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 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: '24'
package-manager-cache: false
- name: Test benchmark harness and manifests
run: node --test benches/*.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 --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
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 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 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: 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 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