Test Pipeline
The test command probes each selected config across one or more stages (ICMP,
TCP, real-delay, download, upload) and persists a per-config connection_tests
row plus a connection_test_runs row that groups the batch.
High-Level Flow
flowchart TD
classDef cli fill:#1a2744,stroke:#4a9eff,color:#e6edf3
classDef app fill:#2a1a3a,stroke:#b070df,color:#e6edf3
classDef engine fill:#2e1a1a,stroke:#df6060,color:#e6edf3
classDef probe fill:#2a2a1a,stroke:#c0df5b,color:#e6edf3
classDef store fill:#1a2e2e,stroke:#5bcfdf,color:#e6edf3
START["CLI: xrat test"]:::cli
RESOLVE["resolve_test_settings()\nsettings/resolve.rs"]:::app
LOAD["load target configs\ndb/repository/configs/"]:::store
EXEC["run for each config\nbulk/single.rs"]:::app
PROBE["generate probe config\nspawn Xray process"]:::engine
STAGES["run enabled stages\nsequentially"]:::probe
KILL["kill probe process"]:::engine
SAVE["persist results\ndb/repository/connection_tests/"]:::store
OUT["format & print\noutput/print.rs"]:::app
START --> RESOLVE --> LOAD --> EXEC --> PROBE --> STAGES --> KILL --> SAVE --> OUT
Probers
src/prober/ is the leaf-level measurement crate. Each prober is a small async
function that returns a *Result struct plus a FailureKind on failure. The
combined TestResult is the only thing the rest of the command touches.
#![allow(unused)]
fn main() {
pub use download::{DownloadResult, download_speed_check};
pub use icmp::{IcmpResult, icmp_ping};
pub use real_delay::{RealDelayResult, real_delay_check};
pub use tcp::{TcpResult, tcp_check};
pub use upload::{UploadResult, upload_speed_check};
pub struct TestResult {
pub icmp_ok: bool,
pub icmp_ms: Option<u32>,
pub tcp_ok: bool,
pub tcp_ms: Option<u32>,
pub real_delay_ok: bool,
pub real_delay_ms: Option<u32>,
pub download_ok: bool,
pub download_mbps: Option<f64>,
pub upload_ok: bool,
pub upload_mbps: Option<f64>,
pub ttfb_ms: Option<u32>,
pub http_status: Option<u16>,
pub endpoint_ip: Option<String>,
pub endpoint_location: Option<String>,
pub endpoint_country: Option<String>,
pub endpoint_asn: Option<String>,
pub failure_kind: Option<FailureKind>,
pub failure_reason: Option<String>,
}
}
Prober Source Layout
src/prober/
βββ icmp/
β βββ mod.rs β icmp_ping, ping_with_system_command
β βββ parsing.rs β parse_ping_latency, classify_ping_failure
βββ tcp/
β βββ check.rs β tcp_check
β βββ classify.rs β classify_dns_error, classify_tcp_error
β βββ model.rs β TcpResult
β βββ mod.rs
β βββ errors.rs
β βββ tests.rs
βββ real_delay/
β βββ check/
β β βββ execute.rs β proxied HTTP latency through Xray
β β βββ model.rs β RealDelayResult
β β βββ mod.rs
β β βββ port.rs β inbound port detection
β β βββ request.rs β request execution
β βββ classify.rs
β βββ mod.rs
βββ download/
β βββ check/
β β βββ proxied.rs β proxied download
β β βββ result.rs β throughput calculation
β β βββ mod.rs
β βββ classify.rs
β βββ mod.rs
βββ upload/
βββ classify.rs β classify_request_error, classify_xray_error
βββ mod.rs β upload_speed_check
βββ request.rs β make_proxied_upload
ICMP, TCP, and upload keep their logic in a single module + flat
classify/parsing files; real_delay and download push the proxied network
code one level deeper into check/.
Settings Resolution
The persisted TestingSettings (in app/config/testing/types.rs) is the
βconfig file + defaultsβ form. settings/resolve.rs::resolve_test_settings
merges that with the CLI flags into a ResolvedTestSettings value that the
executor consumes.
#![allow(unused)]
fn main() {
pub struct TestingSettings {
pub concurrency: i32,
pub order: Vec<ConnectionTestStage>,
pub failure_policy: TestFailurePolicy,
pub real_delay: RealDelayTestSettings,
pub icmp: IcmpTestSettings,
pub download: DownloadTestSettings,
pub tcp: TcpTestSettings,
pub geoip: GeoIpTestSettings,
}
pub enum ConnectionTestStage {
Icmp,
RealDelay,
Download,
}
pub enum TestFailurePolicy {
Continue,
SkipRemaining,
MarkFailed,
}
}
TestFailurePolicy::halts_after_failure() returns true for SkipRemaining
and MarkFailed; the executor uses this to decide whether to keep going after a
stage failure.
Resolved Settings
#![allow(unused)]
fn main() {
pub(crate) struct ResolvedTestSettings {
pub(crate) stage_order: Vec<ConnectionTestStage>,
pub(crate) failure_policy: TestFailurePolicy,
pub(crate) real_delay_url: String,
pub(crate) download_url: String,
pub(crate) upload_url: Option<String>, // upload only runs if Some
pub(crate) xray_binary_path: PathBuf,
pub(crate) icmp_timeout: Duration,
pub(crate) tcp_timeout: Duration,
pub(crate) xray_startup_timeout: Duration,
pub(crate) real_delay_timeout: Duration,
pub(crate) download_timeout: Duration,
pub(crate) upload_timeout: Duration,
pub(crate) upload_payload_bytes: usize,
pub(crate) run_icmp: bool,
pub(crate) run_tcp: bool,
pub(crate) run_real_delay: bool,
pub(crate) run_download: bool,
pub(crate) run_upload: bool,
pub(crate) concurrency: i32,
pub(crate) geoip_enabled: bool,
pub(crate) geoip_country_path: PathBuf,
pub(crate) geoip_city_path: PathBuf,
pub(crate) geoip_asn_path: PathBuf,
}
}
Note: upload_url is Option<String> and the upload stage is skipped when it
is None β ConnectionTestStage itself has no Upload variant.
Failure Classification
#![allow(unused)]
fn main() {
pub enum FailureKind {
Dns,
Timeout,
Refused,
Unreachable,
PermissionDenied,
Tls,
Auth,
Process,
Proxy,
Unknown,
}
}
as_str() is the canonical snake-case form stored in
connection_tests.failure_kind. Classifiers live next to each prober
(icmp/parsing.rs::classify_ping_failure,
tcp/classify.rs::classify_dns_error/classify_tcp_error,
upload/classify.rs::classify_request_error/classify_xray_error).
flowchart LR
classDef input fill:#1a2744,stroke:#4a9eff,color:#e6edf3
classDef icmp fill:#2e2a1a,stroke:#dfba5b,color:#e6edf3
classDef tcp fill:#2a1a3a,stroke:#b070df,color:#e6edf3
classDef http fill:#2e1a1a,stroke:#df6060,color:#e6edf3
FAIL["test failure"]:::input
CAT{"prober"}
ICMP["dns Β· timeout Β· permission_denied\nunreachable Β· unknown"]:::icmp
TCP["dns Β· timeout Β· refused\nunreachable Β· permission_denied Β· unknown"]:::tcp
RD["process Β· timeout Β· tls\nauth Β· proxy Β· unknown"]:::http
TH["process Β· timeout Β· tls\nauth Β· proxy Β· unknown"]:::http
FAIL --> CAT
CAT -- "ICMP" --> ICMP
CAT -- "TCP" --> TCP
CAT -- "RealDelay" --> RD
CAT -- "Download/Upload" --> TH
Stage Execution
Per config, the executor walks stage_order and runs the matching run_*_stage
function (stages/throughput.rs etc.) into a single TestResult. ICMP and TCP
run without spinning up Xray; real-delay, download, and upload each generate a
probe config and spawn a short-lived Xray process via
xray::XrayProcess::spawn_with_binary.
flowchart LR
classDef direct fill:#2e2a1a,stroke:#dfba5b,color:#e6edf3
classDef proxy fill:#2e1a1a,stroke:#df6060,color:#e6edf3
classDef opt fill:#1a2c3a,stroke:#5b8def,color:#e6edf3
ICMP["ICMP\n(direct)"]:::direct
TCP["TCP\n(direct)"]:::direct
REAL["Real Delay\n(via Xray)"]:::proxy
DL["Download\n(via Xray)"]:::proxy
UL["Upload\n(via Xray, optional)"]:::opt
ICMP --> TCP --> REAL --> DL -.-> UL
Stages marked (direct) probe the remote endpoint without a proxy process.
Stages marked (via Xray) spawn a short-lived Xray probe instance on a random
local port. Upload runs only when upload_url is configured.
Persistence
db/repository/connection_tests/ writes one row per config with the fields in
ConnectionTestRecord, and connection_test_runs records the batch metadata
(kind, created_at). The bulk executor groups results into a single run id so
callers can paginate by batch.
Output Formatting
output/print.rs and output/format.rs produce the per-row and tabular output
for the CLI. The TUI reuses the same in-process executors through
app::commands::test::bulk::* and does not shell out to a child xrat test
process.