Standalone mock distribution server and forward proxy for testing rustup, together with the integration tests that use them.
rustup-mock-server— serves a mock distribution tree over HTTP, optionally requiring Basic authentication on every request (Authorization). When started without a--directory, it generates the mock tree itself.rustup-mock-proxy— a forward proxy that requiresProxy-Authorizationcredentials and strips them before forwarding the request to the real target.tests/— integration tests for therustup-initbinary, therustupprogram, and therustup-init.shscript: installing from the mock server and through the mock proxy, with and without Basic authentication.
rustup has no test environment of its own that covers authenticated
downloads (rust-lang/rustup#1343).
An attempt to add mock programs to the rustup repository was
proposed and later
submitted as a pull request;
this crate keeps the mock programs and their tests out of the rustup source
tree so that the rustup pull request only contains the feature they test (the
RUSTUP_AUTHORIZATION_HEADER and RUSTUP_PROXY_AUTHORIZATION_HEADER
environment variables).
The crate does not link against rustup: the rustup directory is a git
submodule that is a build input only — build.rs builds
rustup-init from it and records the path in RUSTUP_INIT_BIN, and the
tests run that binary. src/dist_tree.rs vendors the small amount of mock
distribution tree generation that the tests need (attributed in its header),
so the crate works without a rustup library dependency.
rustup/ git submodule: the rustup checkout that is
built to provide `rustup-init` and `rustup-init.sh`
build.rs builds the submodule's rustup-init and the mock
toolchain binary; emits RUSTUP_INIT_BIN,
RUSTUP_INIT_SH, MOCK_BIN
src/lib.rs crate root (mock_data, dist_tree, test_support)
src/mock_data.rs the program data file (readiness + address)
src/dist_tree.rs mock distribution tree generation (vendored)
src/bin/rustup-mock-server.rs
src/bin/rustup-mock-proxy.rs
tests/proxy.rs binary/program/server tests
tests/init_sh.rs rustup-init.sh tests (Unix only)
git clone --recursive <this repository>
cd rustup-mocks
cargo testcargo test builds everything that is needed: build.rs first compiles
rustup-init from the rustup submodule (into rustup/target/), then
compiles the mock toolchain binary that the mock distribution tree ships.
The tests/init_sh.rs tests run only on Unix and require sh, curl, and
wget in PATH (they force the script onto a specific downloader by running
it with a restricted PATH).
The rustup submodule pins the revision the tests exercise. To test a PR
head or any other revision:
git -C rustup fetch <remote> <ref>
git -C rustup checkout <commit>
cd ..
git add rustup
git commit -m "rustup: test against <commit>"For CI, the rustup input of the rustup-mocks-ci workflow fetches and
checks out a revision in the submodule without committing it.
The mock server is also a standalone developer tool: it lets you run a rustup
build against a local distribution server, without network access and with
controllable authentication. The mock tree it generates has one
rust package (stable channel, version 1.30.0) whose tarball contains a
precompiled mock rustup binary, so toolchain installs succeed offline.
# Build rustup (in this crate or a separate checkout) and the mock server
cargo build --bin rustup-mock-server
# Create a scratch RUSTUP_HOME/CARGO_HOME
export RUSTUP_HOME="$(mktemp -d)"
export CARGO_HOME="$RUSTUP_HOME"
# Start the mock server in the background. It picks a free port and records
# it in its data file ($HOME/.local/share/rustup-mock-server.data by
# default; %LOCALAPPDATA%\rustup-mock-server.data on Windows).
./target/debug/rustup-mock-server &
MOCK_PID=$!
# Wait for the server to start (its data file appears once it is listening)
DATA_FILE="$HOME/.local/share/rustup-mock-server.data"
while ! grep -q '^port=' "$DATA_FILE" 2>/dev/null; do sleep 1; done
PORT=$(grep '^port=' "$DATA_FILE" | cut -d= -f2)
# Point rustup at the mock server
export RUSTUP_DIST_SERVER="http://127.0.0.1:${PORT}"
export RUSTUP_UPDATE_ROOT="${RUSTUP_DIST_SERVER}/rustup"
# Run a rustup build against it, for example:
RUSTUP_HOME="$RUSTUP_HOME" CARGO_HOME="$CARGO_HOME" \
./rustup/target/debug/rustup-init --no-modify-path
RUSTUP_HOME="$RUSTUP_HOME" CARGO_HOME="$CARGO_HOME" \
RUSTUP_FORCE_ARG0=rustup ./rustup/target/debug/rustup-init --default stable
# Clean up (the server removes its data file on exit)
kill $MOCK_PID
rm -rf "$RUSTUP_HOME"For authenticated downloads, start the server with a credential and present
it through RUSTUP_AUTHORIZATION_HEADER:
./target/debug/rustup-mock-server --basic-test-credential "testuser:testpass" &
export RUSTUP_AUTHORIZATION_HEADER="Basic $(echo -n 'testuser:testpass' | base64)"To route the downloads through an authenticating proxy, also start
rustup-mock-proxy with a credential, point http_proxy/https_proxy at
it, and set RUSTUP_PROXY_AUTHORIZATION_HEADER:
./target/debug/rustup-mock-proxy --basic-test-credential "proxyuser:proxypass" &
PROXY_PORT=$(grep '^port=' "$HOME/.local/share/rustup-mock-proxy.data" | cut -d= -f2)
export http_proxy="http://127.0.0.1:${PROXY_PORT}"
export https_proxy="http://127.0.0.1:${PROXY_PORT}"
export RUSTUP_PROXY_AUTHORIZATION_HEADER="Basic $(echo -n 'proxyuser:proxypass' | base64)"See the command-line help of each program for all options (--addr,
--port, --directory, --basic-test-credential, --data-file, and
--arch for the server).
Both programs write a data file once their listener is bound, and remove it
when they exit (after a normal exit or SIGINT/SIGTERM). The presence of the
file is therefore a readiness signal, and it tells the client the address and
port the program is listening on (important when the port is OS-assigned,
which is the default). The file contains one key=value pair per line:
| Key | Description |
|---|---|
addr |
The address the program is listening on |
port |
The port the program is listening on |
pid |
The process id of the program |
credential |
The basic test credential in use (only when --basic-test-credential was given) |
directory |
The directory being served (rustup-mock-server only) |
.github/workflows/ci.yaml runs cargo build and cargo test on Ubuntu.
The workflow can also be started manually with a rustup input to test
against a specific rustup revision (for example the head of a pull request).