Files
aria2-rust-pro/crates/aria2-rust-pro-cli/src/tests/runtime_execution/segment_planning.rs
T

421 lines
16 KiB
Rust

use super::*;
#[expect(
clippy::too_many_lines,
reason = "segment-plan regression fixture keeps response bodies, ranges, and request assertions adjacent"
)]
#[test]
fn execute_runtime_uses_segment_plan_after_bootstrap_partial_response() {
let downloader = SequencedHttpDownloader::new(vec![
Ok(HttpResponseModel {
status: 206,
reason: "Partial Content".to_owned(),
version: HttpVersion::Http11,
headers: HttpResponseHeaders {
headers: vec![
aria2_rust_pro_protocol::HttpHeader {
name: "content-length".to_owned(),
value: "4".to_owned(),
kind: aria2_rust_pro_protocol::HeaderKind::Response,
},
aria2_rust_pro_protocol::HttpHeader {
name: "content-range".to_owned(),
value: "bytes 0-3/10".to_owned(),
kind: aria2_rust_pro_protocol::HeaderKind::Response,
},
],
},
body: ResponseBody::Inline(b"1234".to_vec()),
content_range: Some(aria2_rust_pro_protocol::ContentRangeSpec {
unit: aria2_rust_pro_protocol::RangeUnit::Bytes,
start: 0,
end_inclusive: 3,
total_size: Some(10),
unsatisfied: false,
}),
partial_content: true,
checksum: None,
redirected_from: None,
}),
Ok(HttpResponseModel {
status: 206,
reason: "Partial Content".to_owned(),
version: HttpVersion::Http11,
headers: HttpResponseHeaders {
headers: vec![
aria2_rust_pro_protocol::HttpHeader {
name: "content-length".to_owned(),
value: "4".to_owned(),
kind: aria2_rust_pro_protocol::HeaderKind::Response,
},
aria2_rust_pro_protocol::HttpHeader {
name: "content-range".to_owned(),
value: "bytes 4-7/10".to_owned(),
kind: aria2_rust_pro_protocol::HeaderKind::Response,
},
],
},
body: ResponseBody::Inline(b"5678".to_vec()),
content_range: Some(aria2_rust_pro_protocol::ContentRangeSpec {
unit: aria2_rust_pro_protocol::RangeUnit::Bytes,
start: 4,
end_inclusive: 7,
total_size: Some(10),
unsatisfied: false,
}),
partial_content: true,
checksum: None,
redirected_from: None,
}),
Ok(HttpResponseModel {
status: 206,
reason: "Partial Content".to_owned(),
version: HttpVersion::Http11,
headers: HttpResponseHeaders {
headers: vec![
aria2_rust_pro_protocol::HttpHeader {
name: "content-length".to_owned(),
value: "2".to_owned(),
kind: aria2_rust_pro_protocol::HeaderKind::Response,
},
aria2_rust_pro_protocol::HttpHeader {
name: "content-range".to_owned(),
value: "bytes 8-9/10".to_owned(),
kind: aria2_rust_pro_protocol::HeaderKind::Response,
},
],
},
body: ResponseBody::Inline(b"90".to_vec()),
content_range: Some(aria2_rust_pro_protocol::ContentRangeSpec {
unit: aria2_rust_pro_protocol::RangeUnit::Bytes,
start: 8,
end_inclusive: 9,
total_size: Some(10),
unsatisfied: false,
}),
partial_content: true,
checksum: None,
redirected_from: None,
}),
]);
let temp_dir = std::env::temp_dir().join("aria2-rust-pro-cli-explicit-segments-test");
let _ = fs::create_dir_all(&temp_dir);
let config_path = temp_dir.join("aria2.conf");
fs::write(
&config_path,
"max-tries=3\nsplit=3\nmin-split-size=4\npiece-length=4\n",
)
.expect("config should write");
let report = execute_runtime_with_downloader(
Invocation::Run {
config_path: Some(config_path.clone()),
uris: vec!["http://example.com/segment-plan.bin".to_owned()],
},
&downloader,
)
.expect("runtime should execute via explicit segments");
assert_eq!(report.completed_download_count, 1);
assert_eq!(report.first_status.as_deref(), Some("complete"));
assert_eq!(report.first_completed_length, Some(10));
let recorded = downloader.recorded_requests();
let [bootstrap, first_followup, second_followup] = recorded.as_slice() else {
panic!("expected exactly three recorded requests");
};
assert_eq!(bootstrap.request.range, None);
let mut followup_ranges = [first_followup, second_followup]
.iter()
.map(|task| task.request.range)
.collect::<Vec<_>>();
followup_ranges.sort_by_key(|range| range.as_ref().map(|range| range.start));
assert_eq!(
followup_ranges,
vec![
Some(aria2_rust_pro_protocol::RangeSpec {
start: 4,
end_inclusive: Some(9),
unit: aria2_rust_pro_protocol::RangeUnit::Bytes,
}),
Some(aria2_rust_pro_protocol::RangeSpec {
start: 8,
end_inclusive: Some(9),
unit: aria2_rust_pro_protocol::RangeUnit::Bytes,
}),
]
);
let _ = fs::remove_file(config_path);
}
#[expect(
clippy::too_many_lines,
reason = "initial range-probe regression keeps bootstrap and follow-up range fixtures adjacent"
)]
#[test]
fn execute_runtime_uses_initial_range_probe_when_split_budget_allows_parallel_segments() {
let downloader = SequencedHttpDownloader::new(vec![
Ok(HttpResponseModel {
status: 206,
reason: "Partial Content".to_owned(),
version: HttpVersion::Http11,
headers: HttpResponseHeaders {
headers: vec![
aria2_rust_pro_protocol::HttpHeader {
name: "content-length".to_owned(),
value: "4".to_owned(),
kind: aria2_rust_pro_protocol::HeaderKind::Response,
},
aria2_rust_pro_protocol::HttpHeader {
name: "content-range".to_owned(),
value: "bytes 0-3/10".to_owned(),
kind: aria2_rust_pro_protocol::HeaderKind::Response,
},
],
},
body: ResponseBody::Inline(b"1234".to_vec()),
content_range: Some(aria2_rust_pro_protocol::ContentRangeSpec {
unit: aria2_rust_pro_protocol::RangeUnit::Bytes,
start: 0,
end_inclusive: 3,
total_size: Some(10),
unsatisfied: false,
}),
partial_content: true,
checksum: None,
redirected_from: None,
}),
Ok(HttpResponseModel {
status: 206,
reason: "Partial Content".to_owned(),
version: HttpVersion::Http11,
headers: HttpResponseHeaders {
headers: vec![
aria2_rust_pro_protocol::HttpHeader {
name: "content-length".to_owned(),
value: "4".to_owned(),
kind: aria2_rust_pro_protocol::HeaderKind::Response,
},
aria2_rust_pro_protocol::HttpHeader {
name: "content-range".to_owned(),
value: "bytes 4-7/10".to_owned(),
kind: aria2_rust_pro_protocol::HeaderKind::Response,
},
],
},
body: ResponseBody::Inline(b"5678".to_vec()),
content_range: Some(aria2_rust_pro_protocol::ContentRangeSpec {
unit: aria2_rust_pro_protocol::RangeUnit::Bytes,
start: 4,
end_inclusive: 7,
total_size: Some(10),
unsatisfied: false,
}),
partial_content: true,
checksum: None,
redirected_from: None,
}),
Ok(HttpResponseModel {
status: 206,
reason: "Partial Content".to_owned(),
version: HttpVersion::Http11,
headers: HttpResponseHeaders {
headers: vec![
aria2_rust_pro_protocol::HttpHeader {
name: "content-length".to_owned(),
value: "2".to_owned(),
kind: aria2_rust_pro_protocol::HeaderKind::Response,
},
aria2_rust_pro_protocol::HttpHeader {
name: "content-range".to_owned(),
value: "bytes 8-9/10".to_owned(),
kind: aria2_rust_pro_protocol::HeaderKind::Response,
},
],
},
body: ResponseBody::Inline(b"90".to_vec()),
content_range: Some(aria2_rust_pro_protocol::ContentRangeSpec {
unit: aria2_rust_pro_protocol::RangeUnit::Bytes,
start: 8,
end_inclusive: 9,
total_size: Some(10),
unsatisfied: false,
}),
partial_content: true,
checksum: None,
redirected_from: None,
}),
]);
let temp_dir = std::env::temp_dir().join("aria2-rust-pro-cli-segment-probe-test");
let _ = fs::create_dir_all(&temp_dir);
let config_path = temp_dir.join("aria2.conf");
fs::write(
&config_path,
"split=3\nmax-connection-per-server=3\nmin-split-size=4\npiece-length=4\n",
)
.expect("config should write");
let report = execute_runtime_with_downloader(
Invocation::Run {
config_path: Some(config_path.clone()),
uris: vec!["http://example.com/probe-plan.bin".to_owned()],
},
&downloader,
)
.expect("runtime should execute via initial range probe");
assert_eq!(report.completed_download_count, 1);
assert_eq!(report.first_status.as_deref(), Some("complete"));
assert_eq!(report.first_completed_length, Some(10));
let recorded = downloader.recorded_requests();
let [probe, first_followup, second_followup] = recorded.as_slice() else {
panic!("expected exactly three recorded requests");
};
assert_eq!(
probe.request.range,
Some(aria2_rust_pro_protocol::RangeSpec {
start: 0,
end_inclusive: Some(3),
unit: aria2_rust_pro_protocol::RangeUnit::Bytes,
})
);
let mut followup_ranges = [first_followup, second_followup]
.iter()
.map(|task| task.request.range)
.collect::<Vec<_>>();
followup_ranges.sort_by_key(|range| range.as_ref().map(|range| range.start));
assert_eq!(
followup_ranges,
vec![
Some(aria2_rust_pro_protocol::RangeSpec {
start: 4,
end_inclusive: Some(7),
unit: aria2_rust_pro_protocol::RangeUnit::Bytes,
}),
Some(aria2_rust_pro_protocol::RangeSpec {
start: 8,
end_inclusive: Some(9),
unit: aria2_rust_pro_protocol::RangeUnit::Bytes,
}),
]
);
let _ = fs::remove_file(config_path);
}
#[expect(
clippy::too_many_lines,
reason = "tiny-tail regression keeps the coalesced probe fixture next to the expected request ranges"
)]
#[test]
fn execute_runtime_coalesces_tiny_followup_tail_after_initial_probe() {
let downloader = SequencedHttpDownloader::new(vec![
Ok(HttpResponseModel {
status: 206,
reason: "Partial Content".to_owned(),
version: HttpVersion::Http11,
headers: HttpResponseHeaders {
headers: vec![
aria2_rust_pro_protocol::HttpHeader {
name: "content-length".to_owned(),
value: "32".to_owned(),
kind: aria2_rust_pro_protocol::HeaderKind::Response,
},
aria2_rust_pro_protocol::HttpHeader {
name: "content-range".to_owned(),
value: "bytes 0-31/36".to_owned(),
kind: aria2_rust_pro_protocol::HeaderKind::Response,
},
],
},
body: ResponseBody::Inline(b"1234567890abcdefghijklmnopqrstuv".to_vec()),
content_range: Some(aria2_rust_pro_protocol::ContentRangeSpec {
unit: aria2_rust_pro_protocol::RangeUnit::Bytes,
start: 0,
end_inclusive: 31,
total_size: Some(36),
unsatisfied: false,
}),
partial_content: true,
checksum: None,
redirected_from: None,
}),
Ok(HttpResponseModel {
status: 206,
reason: "Partial Content".to_owned(),
version: HttpVersion::Http11,
headers: HttpResponseHeaders {
headers: vec![
aria2_rust_pro_protocol::HttpHeader {
name: "content-length".to_owned(),
value: "4".to_owned(),
kind: aria2_rust_pro_protocol::HeaderKind::Response,
},
aria2_rust_pro_protocol::HttpHeader {
name: "content-range".to_owned(),
value: "bytes 32-35/36".to_owned(),
kind: aria2_rust_pro_protocol::HeaderKind::Response,
},
],
},
body: ResponseBody::Inline(b"ghij".to_vec()),
content_range: Some(aria2_rust_pro_protocol::ContentRangeSpec {
unit: aria2_rust_pro_protocol::RangeUnit::Bytes,
start: 32,
end_inclusive: 35,
total_size: Some(36),
unsatisfied: false,
}),
partial_content: true,
checksum: None,
redirected_from: None,
}),
]);
let temp_dir = std::env::temp_dir().join("aria2-rust-pro-cli-tiny-tail-coalesce-test");
let _ = fs::create_dir_all(&temp_dir);
let config_path = temp_dir.join("aria2.conf");
fs::write(
&config_path,
"split=4\nmax-connection-per-server=4\nmin-split-size=4\npiece-length=4\n",
)
.expect("config should write");
let report = execute_runtime_with_downloader(
Invocation::Run {
config_path: Some(config_path.clone()),
uris: vec!["http://example.com/coalesced-tail.bin".to_owned()],
},
&downloader,
)
.expect("runtime should coalesce tiny follow-up tail after probe");
assert_eq!(report.completed_download_count, 1);
assert_eq!(report.first_status.as_deref(), Some("complete"));
assert_eq!(report.first_completed_length, Some(36));
let recorded = downloader.recorded_requests();
let [probe, first_followup] = recorded.as_slice() else {
panic!("expected exactly two recorded requests");
};
assert_eq!(
probe.request.range,
Some(aria2_rust_pro_protocol::RangeSpec {
start: 0,
end_inclusive: Some(31),
unit: aria2_rust_pro_protocol::RangeUnit::Bytes,
})
);
assert_eq!(
first_followup.request.range,
Some(aria2_rust_pro_protocol::RangeSpec {
start: 32,
end_inclusive: Some(35),
unit: aria2_rust_pro_protocol::RangeUnit::Bytes,
})
);
let _ = fs::remove_file(config_path);
}