Many imorovments
Build and test / Desktop (Linux) (push) Failing after 1m8s
Build and test / Android (aarch64) (push) Failing after 2s
Build and test / Layer separation (push) Canceled after 23s
Traceability / Requirement traces (push) Failing after 59s

This commit is contained in:
2026-08-12 22:16:15 +02:00
parent fa12afed18
commit 4d78041d1d
24 changed files with 711 additions and 78 deletions
+72 -24
View File
@@ -79,6 +79,13 @@ pub struct ThumbnailReady {
pub width: u32,
pub height: u32,
pub rgba: Vec<u8>,
/// Whether these pixels came off local disk rather than the server.
///
/// The grid paints both identically, so this exists solely for
/// reachability: a store hit is evidence about the *cache*, not the
/// network, and treating one as proof of connectivity clears offline mode
/// before a single request has been attempted.
pub from_cache: bool,
}
/// Capture metadata read from the same header the thumbnail needed.
@@ -330,9 +337,7 @@ pub fn spawn_sidecar_writes(
let (tx, rx) = std::sync::mpsc::channel();
std::thread::spawn(move || {
let rt = match tokio::runtime::Builder::new_current_thread()
.enable_all()
.build()
let rt = match crate::net_runtime::build()
{
Ok(rt) => rt,
Err(e) => {
@@ -560,9 +565,7 @@ fn run_scan(
}
let catalog = Catalog::open(&catalog_path).map_err(ScanFailure::local)?;
let rt = tokio::runtime::Builder::new_current_thread()
.enable_all()
.build()
let rt = crate::net_runtime::build()
.map_err(ScanFailure::local)?;
rt.block_on(async {
@@ -887,9 +890,7 @@ pub fn spawn_pin_fetch(
return;
}
let rt = match tokio::runtime::Builder::new_current_thread()
.enable_all()
.build()
let rt = match crate::net_runtime::build()
{
Ok(rt) => rt,
Err(e) => {
@@ -1055,9 +1056,7 @@ pub fn spawn_full_fetch(
}
}
let rt = match tokio::runtime::Builder::new_current_thread()
.enable_all()
.build()
let rt = match crate::net_runtime::build()
{
Ok(rt) => rt,
Err(e) => {
@@ -1160,6 +1159,7 @@ pub fn spawn_thumbnails(
width,
height,
rgba,
from_cache: true,
});
}
// A corrupt stored blob is a miss, not a failure.
@@ -1202,9 +1202,7 @@ pub fn spawn_thumbnails(
return;
}
let rt = match tokio::runtime::Builder::new_current_thread()
.enable_all()
.build()
let rt = match crate::net_runtime::build()
{
Ok(rt) => rt,
Err(e) => {
@@ -1392,6 +1390,7 @@ async fn fetch_one(
width: preview.width,
height: preview.height,
rgba: preview.rgba,
from_cache: false,
}))
}
@@ -1675,9 +1674,7 @@ pub fn spawn_sweep(
return;
}
let rt = match tokio::runtime::Builder::new_current_thread()
.enable_all()
.build()
let rt = match crate::net_runtime::build()
{
Ok(rt) => rt,
Err(e) => {
@@ -2169,9 +2166,7 @@ mod tests {
// The ordering guarantee is what lets a caller pair results back to
// their inputs; without it a lane's dates could be attributed to the
// wrong images.
let rt = tokio::runtime::Builder::new_current_thread()
.enable_all()
.build()
let rt = crate::net_runtime::build()
.unwrap();
let out = rt.block_on(async {
@@ -2195,9 +2190,7 @@ mod tests {
#[test]
fn join_all_of_nothing_completes() {
let rt = tokio::runtime::Builder::new_current_thread()
.enable_all()
.build()
let rt = crate::net_runtime::build()
.unwrap();
let out: Vec<i32> =
rt.block_on(async { futures_join_all(Vec::<std::future::Ready<i32>>::new()).await });
@@ -2636,6 +2629,61 @@ mod tests {
assert!(out[2] > out[0], "channel order survived the round trip");
}
#[test]
fn a_store_hit_is_not_evidence_the_server_is_reachable() {
// The regression this guards: store hits were delivered as the same
// `Ready` the network path sends, and the UI took any `Ready` as proof
// of connectivity. A mostly-cached window then declared "back online"
// against a server that was down — clearing the banner and kicking off
// a sweep that immediately failed, on every scroll.
let dir = std::env::temp_dir().join(format!("dr-ui-provenance-{}", std::process::id()));
let _ = std::fs::remove_dir_all(&dir);
let rgba: Vec<u8> = std::iter::repeat_n([10u8, 20, 30, 255], 8 * 8)
.flatten()
.collect();
let mut store = ThumbStore::open(&dir).unwrap();
let bytes = dr_thumbs::encode_rgba(8, 8, &rgba).unwrap();
store
.put(99, dr_thumbs::ThumbSize::Grid, &dr_thumbs::Thumbnail {
width: 8,
height: 8,
bytes,
})
.unwrap();
// The split in `spawn_thumbnails`: a hit decodes off local disk and is
// reported with `from_cache` set, which is what the reachability gate
// keys on.
let stored = store.get(99, dr_thumbs::ThumbSize::Grid).unwrap().expect("stored");
let (width, height, rgba) = dr_thumbs::decode_rgba(&stored.bytes).unwrap();
let hit = ThumbnailReady {
row: 0,
width,
height,
rgba,
from_cache: true,
};
assert!(
hit.from_cache,
"a thumbnail read from the store must not be mistaken for a fetch"
);
// And the mechanism it feeds: an offline tracker must survive it.
let mut reach = dr_sync::Reachability::new();
let now = std::time::Instant::now();
reach.mark_unreachable("network error".into(), now);
if !hit.from_cache {
reach.mark_reachable(now);
}
assert!(
reach.is_offline(),
"replaying cached thumbnails must leave offline mode intact"
);
}
#[test]
fn thumbs_live_beside_the_catalog_not_in_the_cache() {
// They sync to the server and are shared with other clients, so a