#![allow(
clippy::unwrap_used,
clippy::expect_used,
unused_results,
clippy::significant_drop_tightening
)]
mod common;
use std::sync::atomic::{AtomicU32, Ordering};
use std::sync::{Arc, Mutex};
use common::TestServer;
use http_body_util::Full;
use hyper::body::Bytes;
use hyper::Response;
use liburlx::progress::{make_progress_callback, ProgressInfo};
#[tokio::test]
async fn progress_callback_called_during_transfer() {
let server =
TestServer::start(|_req| Response::new(Full::new(Bytes::from("hello world")))).await;
let call_count = Arc::new(AtomicU32::new(0));
let count_clone = call_count.clone();
let mut easy = liburlx::Easy::new();
easy.url(&server.url("/")).unwrap();
easy.progress_callback(make_progress_callback(move |_info: &ProgressInfo| {
count_clone.fetch_add(1, Ordering::SeqCst);
true }));
let resp = easy.perform_async().await.unwrap();
assert_eq!(resp.status(), 200);
assert!(call_count.load(Ordering::SeqCst) > 0, "progress callback should have been called");
}
#[tokio::test]
async fn progress_info_has_download_data() {
let body_size = 1000;
let server =
TestServer::start(move |_req| Response::new(Full::new(Bytes::from(vec![b'x'; body_size]))))
.await;
let last_info =
Arc::new(Mutex::new(ProgressInfo { dl_total: 0, dl_now: 0, ul_total: 0, ul_now: 0 }));
let info_clone = last_info.clone();
let mut easy = liburlx::Easy::new();
easy.url(&server.url("/")).unwrap();
easy.progress_callback(make_progress_callback(move |info: &ProgressInfo| {
let mut last = info_clone.lock().unwrap();
*last = ProgressInfo {
dl_total: info.dl_total,
dl_now: info.dl_now,
ul_total: info.ul_total,
ul_now: info.ul_now,
};
true
}));
let resp = easy.perform_async().await.unwrap();
assert_eq!(resp.size_download(), body_size);
let final_info = last_info.lock().unwrap();
assert!(final_info.dl_now > 0, "dl_now should be > 0, got {}", final_info.dl_now);
}
#[tokio::test]
async fn progress_callback_can_return_true_for_all() {
let server = TestServer::start(|_req| Response::new(Full::new(Bytes::from("data")))).await;
let mut easy = liburlx::Easy::new();
easy.url(&server.url("/")).unwrap();
easy.progress_callback(make_progress_callback(|_: &ProgressInfo| true));
let resp = easy.perform_async().await.unwrap();
assert_eq!(resp.status(), 200);
}
#[test]
fn progress_info_fields_accessible() {
let info = ProgressInfo { dl_total: 1000, dl_now: 500, ul_total: 200, ul_now: 100 };
assert_eq!(info.dl_total, 1000);
assert_eq!(info.dl_now, 500);
assert_eq!(info.ul_total, 200);
assert_eq!(info.ul_now, 100);
}
#[test]
fn make_progress_callback_creates_callable() {
let cb = make_progress_callback(|_: &ProgressInfo| true);
let info = ProgressInfo { dl_total: 0, dl_now: 0, ul_total: 0, ul_now: 0 };
let result = liburlx::progress::call_progress(&cb, &info);
assert!(result, "callback should return true");
}
#[test]
fn callback_returning_false_propagates() {
let cb = make_progress_callback(|_: &ProgressInfo| false);
let info = ProgressInfo { dl_total: 0, dl_now: 0, ul_total: 0, ul_now: 0 };
let result = liburlx::progress::call_progress(&cb, &info);
assert!(!result, "callback should return false");
}
#[tokio::test]
async fn progress_callback_works_across_transfers() {
let server = TestServer::start(|_req| Response::new(Full::new(Bytes::from("data")))).await;
let total_calls = Arc::new(AtomicU32::new(0));
let calls_clone = total_calls.clone();
let mut easy = liburlx::Easy::new();
easy.url(&server.url("/")).unwrap();
easy.progress_callback(make_progress_callback(move |_: &ProgressInfo| {
calls_clone.fetch_add(1, Ordering::SeqCst);
true
}));
let _resp1 = easy.perform_async().await.unwrap();
let calls_after_first = total_calls.load(Ordering::SeqCst);
let _resp2 = easy.perform_async().await.unwrap();
let calls_after_second = total_calls.load(Ordering::SeqCst);
assert!(calls_after_first > 0);
assert!(calls_after_second > calls_after_first);
}