use crate::bandwidth_loop::run_concurrent_streams;
use crate::endpoints::ServerEndpoints;
use crate::error::Error;
use crate::progress::Tracker;
use crate::test_config::TestConfig;
use crate::types::Server;
use reqwest::Client;
use std::sync::Arc;
#[must_use]
pub fn extract_base_url(url: &str) -> String {
ServerEndpoints::from_server_url(url).base().to_string()
}
#[must_use]
pub fn build_test_url(server_url: &str, file_index: usize) -> String {
let sizes = ["2000x2000", "3000x3000", "3500x3500", "4000x4000"];
let size = sizes[file_index % sizes.len()];
ServerEndpoints::from_server_url(server_url).download_asset(&format!("random{size}.jpg"))
}
use futures_util::StreamExt;
pub async fn run(
client: &Client,
server: &Server,
single: bool,
progress: Arc<Tracker>,
) -> Result<(f64, f64, u64, Vec<f64>), Error> {
let config = TestConfig::default();
let stream_count = TestConfig::stream_count_for(single);
let result = run_concurrent_streams(
config.estimated_download_bytes,
stream_count,
progress,
"download",
|_, state, sample_interval| {
let client = client.clone();
let server_url = Arc::new(server.url.clone());
tokio::spawn(async move {
for j in 0..config.download_rounds {
let test_url = build_test_url(&server_url, j);
let response = client
.get(&test_url)
.send()
.await
.map_err(Error::DownloadTest)?;
if !response.status().is_success() {
return Err(Error::DownloadFailure(format!(
"server returned {} for {test_url}",
response.status()
)));
}
let mut stream = response.bytes_stream();
while let Some(item) = stream.next().await {
let chunk = item.map_err(Error::DownloadTest)?;
let len = u64::try_from(chunk.len()).unwrap_or(u64::MAX);
if len > 0 {
state.record_bytes(len, sample_interval);
}
}
}
Ok(())
})
},
)
.await?;
Ok((
result.avg_bps,
result.peak_bps,
result.total_bytes,
result.speed_samples,
))
}
#[cfg(test)]
mod tests {
use crate::common;
use crate::test_config::TestConfig;
use super::*;
#[test]
fn test_download_bandwidth_calculation() {
let result = common::calculate_bandwidth(10_000_000, 2.0);
assert!((result - 40_000_000.0).abs() < f64::EPSILON);
}
#[test]
fn test_download_bandwidth_zero_elapsed() {
let result = common::calculate_bandwidth(10_000_000, 0.0);
assert!(result.abs() < f64::EPSILON);
}
#[test]
fn test_download_concurrent_streams_single() {
assert_eq!(TestConfig::stream_count_for(true), 1);
}
#[test]
fn test_download_concurrent_streams_multiple() {
assert_eq!(TestConfig::stream_count_for(false), 4);
}
#[test]
fn test_download_url_generation() {
let server_url = "http://server.example.com/speedtest/upload.php";
let test_url = build_test_url(server_url, 0);
assert_eq!(
test_url,
"http://server.example.com/speedtest/random2000x2000.jpg"
);
}
#[test]
fn test_download_url_generation_cycles() {
let server_url = "http://server.example.com/speedtest/upload.php";
let url_0 = build_test_url(server_url, 0);
let url_4 = build_test_url(server_url, 4);
assert_eq!(url_0, url_4);
}
#[test]
fn test_download_url_generation_all_sizes() {
let server_url = "http://server.example.com/speedtest/upload.php";
let expected = [
"http://server.example.com/speedtest/random2000x2000.jpg",
"http://server.example.com/speedtest/random3000x3000.jpg",
"http://server.example.com/speedtest/random3500x3500.jpg",
"http://server.example.com/speedtest/random4000x4000.jpg",
];
for (i, expected_url) in expected.iter().enumerate() {
assert_eq!(build_test_url(server_url, i), *expected_url);
}
}
#[test]
fn test_extract_base_url() {
let url = "http://server.example.com:8080/speedtest/upload.php";
assert_eq!(
extract_base_url(url),
"http://server.example.com:8080/speedtest"
);
}
#[test]
fn test_extract_base_url_no_suffix() {
let url = "http://server.example.com/speedtest";
assert_eq!(extract_base_url(url), "http://server.example.com/speedtest");
}
#[test]
fn test_extract_base_url_different_path() {
let url = "https://cdn.speedtest.net/upload.php";
assert_eq!(extract_base_url(url), "https://cdn.speedtest.net");
}
#[test]
fn test_estimated_download_bytes_from_config() {
let config = TestConfig::default();
assert!(config.estimated_download_bytes > 10_000_000);
assert!(config.estimated_download_bytes < 20_000_000);
}
#[test]
fn test_sample_interval_constant() {
const _: () = assert!(crate::bandwidth_loop::SAMPLE_INTERVAL_MS == 50);
}
}