Keyboard shortcuts

Press ← or β†’ to navigate between chapters

Press S or / to search in the book

Press ? to show this help

Press Esc to hide this help

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.