use std::{
collections::{BTreeMap, HashMap},
path::PathBuf,
time::Duration,
};
use hisui::{
layout::{self, AggregatedSourceInfo, AssignedSource, Layout, Resolution},
layout_region::{ReuseKind, assign_sources, decide_grid_dimensions, decide_required_cells},
metadata::{SourceId, SourceInfo},
};
use orfail::OrFail;
#[test]
fn valid_resolutions() -> orfail::Result<()> {
let valid_jsons = [r#""16x16""#, r#""3840x3840""#];
for json in valid_jsons {
json.parse::<nojson::Json<Resolution>>().or_fail()?;
}
for i in 0..2 {
let json = format!(r#""{}x{}""#, 32 + i, 32 + i);
let v = json.parse::<nojson::Json<Resolution>>().or_fail()?;
assert_eq!(v.0.width().get(), 32);
assert_eq!(v.0.height().get(), 32);
}
Ok(())
}
#[test]
fn decide_grid_dimensions_works() {
assert_eq!(decide_grid_dimensions(0, 0, 1), (1, 1));
assert_eq!(decide_grid_dimensions(0, 0, 2), (1, 2));
assert_eq!(decide_grid_dimensions(0, 0, 3), (2, 2));
assert_eq!(decide_grid_dimensions(0, 0, 4), (2, 2));
assert_eq!(decide_grid_dimensions(0, 0, 5), (2, 3));
assert_eq!(decide_grid_dimensions(0, 0, 6), (2, 3));
assert_eq!(decide_grid_dimensions(0, 0, 7), (3, 3));
assert_eq!(decide_grid_dimensions(0, 0, 9), (3, 3));
assert_eq!(decide_grid_dimensions(0, 0, 10), (3, 4));
assert_eq!(decide_grid_dimensions(0, 0, 12), (3, 4));
assert_eq!(decide_grid_dimensions(0, 0, 17), (4, 5));
assert_eq!(decide_grid_dimensions(0, 0, 20), (4, 5));
assert_eq!(decide_grid_dimensions(1, 0, 1), (1, 1));
assert_eq!(decide_grid_dimensions(0, 1, 1), (1, 1));
assert_eq!(decide_grid_dimensions(1, 0, 2), (1, 2));
assert_eq!(decide_grid_dimensions(0, 1, 2), (2, 1));
assert_eq!(decide_grid_dimensions(1, 0, 3), (1, 3));
assert_eq!(decide_grid_dimensions(0, 1, 3), (3, 1));
assert_eq!(decide_grid_dimensions(2, 0, 4), (2, 2));
assert_eq!(decide_grid_dimensions(0, 2, 4), (2, 2));
assert_eq!(decide_grid_dimensions(2, 0, 5), (2, 3));
assert_eq!(decide_grid_dimensions(0, 2, 5), (3, 2));
assert_eq!(decide_grid_dimensions(2, 0, 6), (2, 3));
assert_eq!(decide_grid_dimensions(0, 2, 6), (3, 2));
assert_eq!(decide_grid_dimensions(2, 0, 7), (2, 4));
assert_eq!(decide_grid_dimensions(0, 2, 7), (4, 2));
assert_eq!(decide_grid_dimensions(2, 0, 9), (2, 5));
assert_eq!(decide_grid_dimensions(0, 2, 9), (5, 2));
assert_eq!(decide_grid_dimensions(2, 0, 12), (2, 6));
assert_eq!(decide_grid_dimensions(0, 2, 12), (6, 2));
assert_eq!(decide_grid_dimensions(0, 4, 5), (2, 4));
assert_eq!(decide_grid_dimensions(4, 0, 5), (4, 2));
assert_eq!(decide_grid_dimensions(1, 1, 1), (1, 1));
assert_eq!(decide_grid_dimensions(1, 2, 1), (1, 1));
assert_eq!(decide_grid_dimensions(2, 2, 1), (1, 1));
assert_eq!(decide_grid_dimensions(1, 1, 2), (1, 1));
assert_eq!(decide_grid_dimensions(1, 2, 2), (1, 2));
assert_eq!(decide_grid_dimensions(2, 2, 2), (1, 2));
assert_eq!(decide_grid_dimensions(1, 1, 3), (1, 1));
assert_eq!(decide_grid_dimensions(1, 2, 3), (1, 2));
assert_eq!(decide_grid_dimensions(2, 2, 3), (2, 2));
assert_eq!(decide_grid_dimensions(1, 1, 4), (1, 1));
assert_eq!(decide_grid_dimensions(1, 2, 4), (1, 2));
assert_eq!(decide_grid_dimensions(2, 2, 4), (2, 2));
assert_eq!(decide_grid_dimensions(1, 1, 5), (1, 1));
assert_eq!(decide_grid_dimensions(1, 2, 5), (1, 2));
assert_eq!(decide_grid_dimensions(2, 2, 5), (2, 2));
assert_eq!(decide_grid_dimensions(1, 7, 9), (1, 7));
assert_eq!(decide_grid_dimensions(2, 7, 9), (2, 5));
assert_eq!(decide_grid_dimensions(3, 7, 9), (3, 3));
assert_eq!(decide_grid_dimensions(10, 10, 5), (1, 5));
assert_eq!(decide_grid_dimensions(10, 9, 5), (1, 5));
assert_eq!(decide_grid_dimensions(9, 10, 5), (5, 1));
}
#[test]
fn decide_required_cells_works() {
let source0 = source(0, 2);
let source1 = source(1, 1);
let source2 = source(4, 6);
let source3 = source(5, 7);
let source4 = source(6, 8);
let sources = [source0, source1, source2, source3, source4]
.into_iter()
.map(|s| {
(
s.id.clone(),
AggregatedSourceInfo {
id: s.id,
start_timestamp: s.start_timestamp,
stop_timestamp: s.stop_timestamp,
audio: true,
video: true,
format: Default::default(),
media_paths: Default::default(),
},
)
})
.collect();
let kind = ReuseKind::ShowOldest;
let cells_excluded = [];
assert_eq!(decide_required_cells(&sources, kind, &cells_excluded), 3);
assert_eq!(
decide_required_cells(
&sources.clone().into_iter().take(2).collect(),
kind,
&cells_excluded
),
2
);
let cells_excluded = [1, 3];
assert_eq!(decide_required_cells(&sources, kind, &cells_excluded), 5);
let cells_excluded = [2];
assert_eq!(decide_required_cells(&sources, kind, &cells_excluded), 4);
let cells_excluded = [3];
assert_eq!(decide_required_cells(&sources, kind, &cells_excluded), 3);
let kind = ReuseKind::None;
let cells_excluded = [];
assert_eq!(decide_required_cells(&sources, kind, &cells_excluded), 5); assert_eq!(
decide_required_cells(
&sources.clone().into_iter().take(2).collect(),
kind,
&cells_excluded
),
2
);
let cells_excluded = [1, 3];
assert_eq!(decide_required_cells(&sources, kind, &cells_excluded), 7);
let cells_excluded = [2];
assert_eq!(decide_required_cells(&sources, kind, &cells_excluded), 6);
let cells_excluded = [5, 10];
assert_eq!(decide_required_cells(&sources, kind, &cells_excluded), 5);
let empty_sources = BTreeMap::new();
assert_eq!(decide_required_cells(&empty_sources, kind, &[]), 0);
assert_eq!(decide_required_cells(&empty_sources, kind, &[1, 2]), 0); }
#[test]
fn assign_sources_works() {
let source0 = source(0, 2);
let source1 = source(1, 1);
let source2 = source(4, 6);
let source3 = source(5, 7);
let source4 = source(6, 8);
let sources = [source0, source1, source2, source3, source4]
.into_iter()
.map(|s| {
(
s.id.clone(),
AggregatedSourceInfo {
id: s.id.clone(),
start_timestamp: s.start_timestamp,
stop_timestamp: s.stop_timestamp,
audio: true,
video: true,
format: Default::default(),
media_paths: Default::default(),
},
)
})
.collect::<BTreeMap<_, _>>();
let cells_excluded = [];
fn get_assigned_source(
assigned: &HashMap<SourceId, AssignedSource>,
sources: &BTreeMap<SourceId, AggregatedSourceInfo>,
timestamp: u64,
cell_index: usize,
) -> Option<usize> {
let t = Duration::from_secs(timestamp);
sources
.values()
.enumerate()
.filter(|(_i, s)| (s.start_timestamp..=s.stop_timestamp).contains(&t))
.filter_map(|(i, s)| assigned.get(&s.id).map(|v| (i, v)))
.filter(|(_i, s)| s.cell_index == cell_index)
.map(|(i, s)| (s.priority, i))
.min()
.map(|(_priority, i)| i)
}
let assigned = assign_sources(
ReuseKind::None,
sources.values().cloned().collect(),
1,
&cells_excluded,
);
assert_eq!(get_assigned_source(&assigned, &sources, 0, 0), Some(0));
assert_eq!(get_assigned_source(&assigned, &sources, 1, 0), Some(0));
assert_eq!(get_assigned_source(&assigned, &sources, 2, 0), Some(0));
assert_eq!(get_assigned_source(&assigned, &sources, 3, 0), None);
assert_eq!(get_assigned_source(&assigned, &sources, 4, 0), None);
assert_eq!(get_assigned_source(&assigned, &sources, 5, 0), None);
assert_eq!(get_assigned_source(&assigned, &sources, 6, 0), None);
assert_eq!(get_assigned_source(&assigned, &sources, 7, 0), None);
assert_eq!(get_assigned_source(&assigned, &sources, 8, 0), None);
let assigned = assign_sources(
ReuseKind::ShowOldest,
sources.values().cloned().collect(),
1,
&cells_excluded,
);
assert_eq!(get_assigned_source(&assigned, &sources, 0, 0), Some(0));
assert_eq!(get_assigned_source(&assigned, &sources, 1, 0), Some(0));
assert_eq!(get_assigned_source(&assigned, &sources, 2, 0), Some(0));
assert_eq!(get_assigned_source(&assigned, &sources, 3, 0), None);
assert_eq!(get_assigned_source(&assigned, &sources, 4, 0), Some(2));
assert_eq!(get_assigned_source(&assigned, &sources, 5, 0), Some(2));
assert_eq!(get_assigned_source(&assigned, &sources, 6, 0), Some(2));
assert_eq!(get_assigned_source(&assigned, &sources, 7, 0), Some(3));
assert_eq!(get_assigned_source(&assigned, &sources, 8, 0), Some(4));
let assigned = assign_sources(
ReuseKind::ShowNewest,
sources.values().cloned().collect(),
1,
&cells_excluded,
);
assert_eq!(get_assigned_source(&assigned, &sources, 0, 0), Some(0));
assert_eq!(get_assigned_source(&assigned, &sources, 1, 0), Some(1));
assert_eq!(get_assigned_source(&assigned, &sources, 2, 0), Some(0));
assert_eq!(get_assigned_source(&assigned, &sources, 3, 0), None);
assert_eq!(get_assigned_source(&assigned, &sources, 4, 0), Some(2));
assert_eq!(get_assigned_source(&assigned, &sources, 5, 0), Some(3));
assert_eq!(get_assigned_source(&assigned, &sources, 6, 0), Some(4));
assert_eq!(get_assigned_source(&assigned, &sources, 7, 0), Some(4));
assert_eq!(get_assigned_source(&assigned, &sources, 8, 0), Some(4));
let assigned = assign_sources(
ReuseKind::None,
sources.values().cloned().collect(),
2,
&cells_excluded,
);
assert_eq!(get_assigned_source(&assigned, &sources, 0, 0), Some(0));
assert_eq!(get_assigned_source(&assigned, &sources, 0, 1), None);
assert_eq!(get_assigned_source(&assigned, &sources, 1, 0), Some(0));
assert_eq!(get_assigned_source(&assigned, &sources, 1, 1), Some(1));
assert_eq!(get_assigned_source(&assigned, &sources, 2, 0), Some(0));
assert_eq!(get_assigned_source(&assigned, &sources, 2, 1), None);
assert_eq!(get_assigned_source(&assigned, &sources, 3, 0), None);
assert_eq!(get_assigned_source(&assigned, &sources, 3, 1), None);
assert_eq!(get_assigned_source(&assigned, &sources, 4, 0), None);
assert_eq!(get_assigned_source(&assigned, &sources, 4, 1), None);
assert_eq!(get_assigned_source(&assigned, &sources, 5, 0), None);
assert_eq!(get_assigned_source(&assigned, &sources, 5, 1), None);
assert_eq!(get_assigned_source(&assigned, &sources, 6, 0), None);
assert_eq!(get_assigned_source(&assigned, &sources, 6, 1), None);
assert_eq!(get_assigned_source(&assigned, &sources, 7, 0), None);
assert_eq!(get_assigned_source(&assigned, &sources, 7, 1), None);
assert_eq!(get_assigned_source(&assigned, &sources, 8, 0), None);
assert_eq!(get_assigned_source(&assigned, &sources, 8, 1), None);
let assigned = assign_sources(
ReuseKind::ShowOldest,
sources.values().cloned().collect(),
2,
&cells_excluded,
);
assert_eq!(get_assigned_source(&assigned, &sources, 0, 0), Some(0));
assert_eq!(get_assigned_source(&assigned, &sources, 0, 1), None);
assert_eq!(get_assigned_source(&assigned, &sources, 1, 0), Some(0));
assert_eq!(get_assigned_source(&assigned, &sources, 1, 1), Some(1));
assert_eq!(get_assigned_source(&assigned, &sources, 2, 0), Some(0));
assert_eq!(get_assigned_source(&assigned, &sources, 2, 1), None);
assert_eq!(get_assigned_source(&assigned, &sources, 3, 0), None);
assert_eq!(get_assigned_source(&assigned, &sources, 3, 1), None);
assert_eq!(get_assigned_source(&assigned, &sources, 4, 0), Some(2));
assert_eq!(get_assigned_source(&assigned, &sources, 4, 1), None);
assert_eq!(get_assigned_source(&assigned, &sources, 5, 0), Some(2));
assert_eq!(get_assigned_source(&assigned, &sources, 5, 1), Some(3));
assert_eq!(get_assigned_source(&assigned, &sources, 6, 0), Some(2));
assert_eq!(get_assigned_source(&assigned, &sources, 6, 1), Some(3));
assert_eq!(get_assigned_source(&assigned, &sources, 7, 0), Some(4));
assert_eq!(get_assigned_source(&assigned, &sources, 7, 1), Some(3));
assert_eq!(get_assigned_source(&assigned, &sources, 8, 0), Some(4));
assert_eq!(get_assigned_source(&assigned, &sources, 8, 1), None);
let assigned = assign_sources(
ReuseKind::ShowNewest,
sources.values().cloned().collect(),
2,
&cells_excluded,
);
assert_eq!(get_assigned_source(&assigned, &sources, 0, 0), Some(0));
assert_eq!(get_assigned_source(&assigned, &sources, 0, 1), None);
assert_eq!(get_assigned_source(&assigned, &sources, 1, 0), Some(0));
assert_eq!(get_assigned_source(&assigned, &sources, 1, 1), Some(1));
assert_eq!(get_assigned_source(&assigned, &sources, 2, 0), Some(0));
assert_eq!(get_assigned_source(&assigned, &sources, 2, 1), None);
assert_eq!(get_assigned_source(&assigned, &sources, 3, 0), None);
assert_eq!(get_assigned_source(&assigned, &sources, 3, 1), None);
assert_eq!(get_assigned_source(&assigned, &sources, 4, 0), Some(2));
assert_eq!(get_assigned_source(&assigned, &sources, 4, 1), None);
assert_eq!(get_assigned_source(&assigned, &sources, 5, 0), Some(2));
assert_eq!(get_assigned_source(&assigned, &sources, 5, 1), Some(3));
assert_eq!(get_assigned_source(&assigned, &sources, 6, 0), Some(4));
assert_eq!(get_assigned_source(&assigned, &sources, 6, 1), Some(3));
assert_eq!(get_assigned_source(&assigned, &sources, 7, 0), Some(4));
assert_eq!(get_assigned_source(&assigned, &sources, 7, 1), Some(3));
assert_eq!(get_assigned_source(&assigned, &sources, 8, 0), Some(4));
assert_eq!(get_assigned_source(&assigned, &sources, 8, 1), None);
let assigned = assign_sources(
ReuseKind::None,
sources.values().cloned().collect(),
2 * 3,
&cells_excluded,
);
assert_eq!(get_assigned_source(&assigned, &sources, 0, 0), Some(0));
assert_eq!(get_assigned_source(&assigned, &sources, 0, 1), None);
assert_eq!(get_assigned_source(&assigned, &sources, 0, 2), None);
assert_eq!(get_assigned_source(&assigned, &sources, 0, 3), None);
assert_eq!(get_assigned_source(&assigned, &sources, 0, 4), None);
assert_eq!(get_assigned_source(&assigned, &sources, 0, 5), None);
assert_eq!(get_assigned_source(&assigned, &sources, 1, 0), Some(0));
assert_eq!(get_assigned_source(&assigned, &sources, 1, 1), Some(1));
assert_eq!(get_assigned_source(&assigned, &sources, 1, 2), None);
assert_eq!(get_assigned_source(&assigned, &sources, 1, 3), None);
assert_eq!(get_assigned_source(&assigned, &sources, 1, 4), None);
assert_eq!(get_assigned_source(&assigned, &sources, 1, 5), None);
assert_eq!(get_assigned_source(&assigned, &sources, 2, 0), Some(0));
assert_eq!(get_assigned_source(&assigned, &sources, 2, 1), None);
assert_eq!(get_assigned_source(&assigned, &sources, 2, 2), None);
assert_eq!(get_assigned_source(&assigned, &sources, 2, 3), None);
assert_eq!(get_assigned_source(&assigned, &sources, 2, 4), None);
assert_eq!(get_assigned_source(&assigned, &sources, 2, 5), None);
assert_eq!(get_assigned_source(&assigned, &sources, 3, 0), None);
assert_eq!(get_assigned_source(&assigned, &sources, 3, 1), None);
assert_eq!(get_assigned_source(&assigned, &sources, 3, 2), None);
assert_eq!(get_assigned_source(&assigned, &sources, 3, 3), None);
assert_eq!(get_assigned_source(&assigned, &sources, 3, 4), None);
assert_eq!(get_assigned_source(&assigned, &sources, 3, 5), None);
assert_eq!(get_assigned_source(&assigned, &sources, 4, 0), None);
assert_eq!(get_assigned_source(&assigned, &sources, 4, 1), None);
assert_eq!(get_assigned_source(&assigned, &sources, 4, 2), Some(2));
assert_eq!(get_assigned_source(&assigned, &sources, 4, 3), None);
assert_eq!(get_assigned_source(&assigned, &sources, 4, 4), None);
assert_eq!(get_assigned_source(&assigned, &sources, 4, 5), None);
assert_eq!(get_assigned_source(&assigned, &sources, 5, 0), None);
assert_eq!(get_assigned_source(&assigned, &sources, 5, 1), None);
assert_eq!(get_assigned_source(&assigned, &sources, 5, 2), Some(2));
assert_eq!(get_assigned_source(&assigned, &sources, 5, 3), Some(3));
assert_eq!(get_assigned_source(&assigned, &sources, 5, 4), None);
assert_eq!(get_assigned_source(&assigned, &sources, 5, 5), None);
assert_eq!(get_assigned_source(&assigned, &sources, 6, 0), None);
assert_eq!(get_assigned_source(&assigned, &sources, 6, 1), None);
assert_eq!(get_assigned_source(&assigned, &sources, 6, 2), Some(2));
assert_eq!(get_assigned_source(&assigned, &sources, 6, 3), Some(3));
assert_eq!(get_assigned_source(&assigned, &sources, 6, 4), Some(4));
assert_eq!(get_assigned_source(&assigned, &sources, 6, 5), None);
assert_eq!(get_assigned_source(&assigned, &sources, 7, 0), None);
assert_eq!(get_assigned_source(&assigned, &sources, 7, 1), None);
assert_eq!(get_assigned_source(&assigned, &sources, 7, 2), None);
assert_eq!(get_assigned_source(&assigned, &sources, 7, 3), Some(3));
assert_eq!(get_assigned_source(&assigned, &sources, 7, 4), Some(4));
assert_eq!(get_assigned_source(&assigned, &sources, 7, 5), None);
assert_eq!(get_assigned_source(&assigned, &sources, 8, 0), None);
assert_eq!(get_assigned_source(&assigned, &sources, 8, 1), None);
assert_eq!(get_assigned_source(&assigned, &sources, 8, 2), None);
assert_eq!(get_assigned_source(&assigned, &sources, 8, 3), None);
assert_eq!(get_assigned_source(&assigned, &sources, 8, 4), Some(4));
assert_eq!(get_assigned_source(&assigned, &sources, 8, 5), None);
let assigned = assign_sources(
ReuseKind::ShowOldest,
sources.values().cloned().collect(),
2 * 2,
&cells_excluded,
);
assert_eq!(get_assigned_source(&assigned, &sources, 0, 0), Some(0));
assert_eq!(get_assigned_source(&assigned, &sources, 0, 1), None);
assert_eq!(get_assigned_source(&assigned, &sources, 0, 2), None);
assert_eq!(get_assigned_source(&assigned, &sources, 0, 3), None);
assert_eq!(get_assigned_source(&assigned, &sources, 1, 0), Some(0));
assert_eq!(get_assigned_source(&assigned, &sources, 1, 1), Some(1));
assert_eq!(get_assigned_source(&assigned, &sources, 1, 2), None);
assert_eq!(get_assigned_source(&assigned, &sources, 1, 3), None);
assert_eq!(get_assigned_source(&assigned, &sources, 2, 0), Some(0));
assert_eq!(get_assigned_source(&assigned, &sources, 2, 1), None);
assert_eq!(get_assigned_source(&assigned, &sources, 2, 2), None);
assert_eq!(get_assigned_source(&assigned, &sources, 2, 3), None);
assert_eq!(get_assigned_source(&assigned, &sources, 3, 0), None);
assert_eq!(get_assigned_source(&assigned, &sources, 3, 1), None);
assert_eq!(get_assigned_source(&assigned, &sources, 3, 2), None);
assert_eq!(get_assigned_source(&assigned, &sources, 3, 3), None);
assert_eq!(get_assigned_source(&assigned, &sources, 4, 0), Some(2));
assert_eq!(get_assigned_source(&assigned, &sources, 4, 1), None);
assert_eq!(get_assigned_source(&assigned, &sources, 4, 2), None);
assert_eq!(get_assigned_source(&assigned, &sources, 4, 3), None);
assert_eq!(get_assigned_source(&assigned, &sources, 5, 0), Some(2));
assert_eq!(get_assigned_source(&assigned, &sources, 5, 1), Some(3));
assert_eq!(get_assigned_source(&assigned, &sources, 5, 2), None);
assert_eq!(get_assigned_source(&assigned, &sources, 5, 3), None);
assert_eq!(get_assigned_source(&assigned, &sources, 6, 0), Some(2));
assert_eq!(get_assigned_source(&assigned, &sources, 6, 1), Some(3));
assert_eq!(get_assigned_source(&assigned, &sources, 6, 2), Some(4));
assert_eq!(get_assigned_source(&assigned, &sources, 6, 3), None);
assert_eq!(get_assigned_source(&assigned, &sources, 7, 0), None);
assert_eq!(get_assigned_source(&assigned, &sources, 7, 1), Some(3));
assert_eq!(get_assigned_source(&assigned, &sources, 7, 2), Some(4));
assert_eq!(get_assigned_source(&assigned, &sources, 7, 3), None);
assert_eq!(get_assigned_source(&assigned, &sources, 8, 0), None);
assert_eq!(get_assigned_source(&assigned, &sources, 8, 1), None);
assert_eq!(get_assigned_source(&assigned, &sources, 8, 2), Some(4));
assert_eq!(get_assigned_source(&assigned, &sources, 8, 3), None);
let assigned = assign_sources(
ReuseKind::ShowNewest,
sources.values().cloned().collect(),
2 * 2,
&cells_excluded,
);
assert_eq!(get_assigned_source(&assigned, &sources, 0, 0), Some(0));
assert_eq!(get_assigned_source(&assigned, &sources, 0, 1), None);
assert_eq!(get_assigned_source(&assigned, &sources, 0, 2), None);
assert_eq!(get_assigned_source(&assigned, &sources, 0, 3), None);
assert_eq!(get_assigned_source(&assigned, &sources, 1, 0), Some(0));
assert_eq!(get_assigned_source(&assigned, &sources, 1, 1), Some(1));
assert_eq!(get_assigned_source(&assigned, &sources, 1, 2), None);
assert_eq!(get_assigned_source(&assigned, &sources, 1, 3), None);
assert_eq!(get_assigned_source(&assigned, &sources, 2, 0), Some(0));
assert_eq!(get_assigned_source(&assigned, &sources, 2, 1), None);
assert_eq!(get_assigned_source(&assigned, &sources, 2, 2), None);
assert_eq!(get_assigned_source(&assigned, &sources, 2, 3), None);
assert_eq!(get_assigned_source(&assigned, &sources, 3, 0), None);
assert_eq!(get_assigned_source(&assigned, &sources, 3, 1), None);
assert_eq!(get_assigned_source(&assigned, &sources, 3, 2), None);
assert_eq!(get_assigned_source(&assigned, &sources, 3, 3), None);
assert_eq!(get_assigned_source(&assigned, &sources, 4, 0), Some(2));
assert_eq!(get_assigned_source(&assigned, &sources, 4, 1), None);
assert_eq!(get_assigned_source(&assigned, &sources, 4, 2), None);
assert_eq!(get_assigned_source(&assigned, &sources, 4, 3), None);
assert_eq!(get_assigned_source(&assigned, &sources, 5, 0), Some(2));
assert_eq!(get_assigned_source(&assigned, &sources, 5, 1), Some(3));
assert_eq!(get_assigned_source(&assigned, &sources, 5, 2), None);
assert_eq!(get_assigned_source(&assigned, &sources, 5, 3), None);
assert_eq!(get_assigned_source(&assigned, &sources, 6, 0), Some(2));
assert_eq!(get_assigned_source(&assigned, &sources, 6, 1), Some(3));
assert_eq!(get_assigned_source(&assigned, &sources, 6, 2), Some(4));
assert_eq!(get_assigned_source(&assigned, &sources, 6, 3), None);
assert_eq!(get_assigned_source(&assigned, &sources, 7, 0), None);
assert_eq!(get_assigned_source(&assigned, &sources, 7, 1), Some(3));
assert_eq!(get_assigned_source(&assigned, &sources, 7, 2), Some(4));
assert_eq!(get_assigned_source(&assigned, &sources, 7, 3), None);
assert_eq!(get_assigned_source(&assigned, &sources, 8, 0), None);
assert_eq!(get_assigned_source(&assigned, &sources, 8, 1), None);
assert_eq!(get_assigned_source(&assigned, &sources, 8, 2), Some(4));
assert_eq!(get_assigned_source(&assigned, &sources, 8, 3), None);
}
#[test]
fn invalid_resolutions() -> orfail::Result<()> {
let invalid_jsons = [
r#""15x20""#,
r#""3841x20""#,
r#""100x15""#,
r#""30x3841""#,
r#""x100""#,
r#""100x""#,
r#""100.0x100""#,
r#""100x100.0""#,
];
for json in invalid_jsons {
assert!(json.parse::<nojson::Json<Resolution>>().is_err());
}
Ok(())
}
#[test]
fn layout_without_resolution_specified() -> orfail::Result<()> {
let base_path = PathBuf::from(".");
let layout_json = r#"
{
"video_layout": {
"main": {
"video_sources": [],
"width": 640,
"height": 480,
"x_pos": 100,
"y_pos": 50
},
"sub": {
"video_sources": [],
"width": 200,
"height": 150,
"x_pos": 800,
"y_pos": 300
}
}
}"#;
let layout = Layout::from_layout_json_str(base_path.clone(), layout_json)?;
assert_eq!(layout.resolution.width.get(), 1000);
assert_eq!(layout.resolution.height.get(), 530);
let audio_only_json = r#"
{
"audio_sources": ["testdata/source_timestamps/archive-*.json"]
}"#;
let layout = Layout::from_layout_json_str(base_path.clone(), audio_only_json)?;
assert_eq!(layout.resolution.width.get(), 16);
assert_eq!(layout.resolution.height.get(), 16);
let invalid_json = r#"
{
"video_layout": {
"main": {
"video_sources": []
}
}
}"#;
let result = Layout::from_layout_json_str(base_path, invalid_json);
assert!(result.is_err());
assert!(
result
.unwrap_err()
.message
.contains("Region width must be specified")
);
Ok(())
}
#[test]
fn cell_width_and_cell_height_handling() -> orfail::Result<()> {
let base_path = PathBuf::from(".");
let layout_json = r#"
{
"resolution": "1920x1080",
"video_layout": {
"main": {
"video_sources": ["testdata/source_timestamps/archive-*.json"],
"cell_width": 320,
"cell_height": 240,
"max_columns": 2,
"max_rows": 2
}
}
}"#;
let layout = Layout::from_layout_json_str(base_path.clone(), layout_json)?;
let region = &layout.video_regions[0];
assert_eq!(region.grid.cell_width.get(), 320);
assert_eq!(region.grid.cell_height.get(), 240);
assert_eq!(region.width.get(), 646);
assert_eq!(region.height.get(), 486);
let invalid_layout_json = r#"
{
"resolution": "1920x1080",
"video_layout": {
"main": {
"video_sources": [],
"width": 640,
"cell_width": 320,
"max_columns": 2,
"max_rows": 2
}
}
}"#;
let result = Layout::from_layout_json_str(base_path.clone(), invalid_layout_json);
assert!(result.is_err());
assert!(
result
.unwrap_err()
.message
.contains("cannot specify both 'width' and 'cell_width'")
);
let invalid_layout_json2 = r#"
{
"resolution": "1920x1080",
"video_layout": {
"main": {
"video_sources": [],
"height": 480,
"cell_height": 240,
"max_columns": 2,
"max_rows": 2
}
}
}"#;
let result = Layout::from_layout_json_str(base_path.clone(), invalid_layout_json2);
assert!(result.is_err());
assert!(
result
.unwrap_err()
.message
.contains("cannot specify both 'height' and 'cell_height'")
);
let layout_json_odd = r#"
{
"resolution": "1920x1080",
"video_layout": {
"main": {
"video_sources": [],
"cell_width": 321,
"cell_height": 241,
"max_columns": 1,
"max_rows": 1
}
}
}"#;
let layout = Layout::from_layout_json_str(base_path, layout_json_odd)?;
let region = &layout.video_regions[0];
assert_eq!(region.grid.cell_width.get(), 320); assert_eq!(region.grid.cell_height.get(), 240);
Ok(())
}
fn source(start: u64, end: u64) -> SourceInfo {
SourceInfo {
id: SourceId::new(&format!("{start}_{end}")),
start_timestamp: Duration::from_secs(start),
stop_timestamp: Duration::from_secs(end),
audio: true,
video: true,
format: Default::default(),
}
}
#[test]
fn source_wildcard() -> orfail::Result<()> {
let base_path = PathBuf::from("testdata/files/").canonicalize().or_fail()?;
let to_absolute = |path| std::path::absolute(base_path.join(path)).or_fail();
let resolved = layout::resolve_source_paths(
&base_path,
&[PathBuf::from("bar-0.json"), PathBuf::from("foo-1.json")],
&[],
)
.or_fail()?;
assert_eq!(
resolved,
&[to_absolute("bar-0.json")?, to_absolute("foo-1.json")?]
);
let resolved = layout::resolve_source_paths(
&base_path,
&[PathBuf::from("bar-0.json"), PathBuf::from("foo-1.json")],
&[
PathBuf::from("foo-1.json"),
PathBuf::from("foo-2.json"), ],
)
.or_fail()?;
assert_eq!(resolved, &[to_absolute("bar-0.json")?]);
let resolved =
layout::resolve_source_paths(&base_path, &[PathBuf::from("foo-*.json")], &[]).or_fail()?;
assert_eq!(
resolved,
&[
to_absolute("foo-0.json")?,
to_absolute("foo-1.json")?,
to_absolute("foo-2.json")?
]
);
let resolved = layout::resolve_source_paths(
&base_path,
&[PathBuf::from("*.json")],
&[PathBuf::from("*-1.json")],
)
.or_fail()?;
assert_eq!(
resolved,
&[
to_absolute("bar-0.json")?,
to_absolute("bar-2.json")?,
to_absolute("baz-0.json")?,
to_absolute("baz-2.json")?,
to_absolute("foo-0.json")?,
to_absolute("foo-2.json")?
]
);
let resolved = layout::resolve_source_paths(
&base_path,
&[
PathBuf::from("foo-2.json"),
PathBuf::from("bar-*.json"),
PathBuf::from("baz-0.json"),
],
&[PathBuf::from("bar-2.json"), PathBuf::from("baz-*.json")],
)
.or_fail()?;
assert_eq!(
resolved,
&[
to_absolute("foo-2.json")?,
to_absolute("bar-0.json")?,
to_absolute("bar-1.json")?,
]
);
Ok(())
}
#[test]
fn source_path_outside_base_dir_error() -> orfail::Result<()> {
let base_path = PathBuf::from("testdata/files/").canonicalize().or_fail()?;
let result =
layout::resolve_source_paths(&base_path, &[PathBuf::from("../files2/foo-0.json")], &[]);
assert!(result.is_err());
assert!(
result
.unwrap_err()
.to_string()
.contains("outside the base dir")
);
Ok(())
}
#[test]
fn wildcard_excludes_sources_without_media_files() -> orfail::Result<()> {
let base_path = PathBuf::from("testdata/files2/").canonicalize().or_fail()?;
let to_absolute = |path| std::path::absolute(base_path.join(path)).or_fail();
let resolved =
layout::resolve_source_paths(&base_path, &[PathBuf::from("*.json")], &[]).or_fail()?;
assert_eq!(resolved, &[to_absolute("foo-0.json")?]);
let result = layout::resolve_source_paths(
&base_path,
&[PathBuf::from("source-without-media.json")],
&[],
);
assert!(result.is_err());
assert!(
result
.unwrap_err()
.to_string()
.contains("no media file for the source")
);
Ok(())
}
#[test]
fn invalid_layouts() {
let base_path = PathBuf::from(".");
let invalid_json = r#"
{
"resolution": "720x480",
"video_layout": {
"main": {
"video_sources": [],
"width": 700,
"height": 400,
"x_pos": 50
}
}
}"#;
let result = Layout::from_layout_json_str(base_path.clone(), invalid_json);
assert!(result.is_err());
let error_message = result.unwrap_err().message;
assert!(error_message.contains("x_pos + width"));
assert!(error_message.contains("exceeds resolution width"));
let valid_json = invalid_json.replace(r#"x_pos": 50"#, r#"x_pos": 20"#);
let result = Layout::from_layout_json_str(base_path.clone(), &valid_json);
assert!(result.is_ok());
let invalid_json = r#"
{
"resolution": "720x480",
"video_layout": {
"main": {
"video_sources": [],
"width": 700,
"height": 400,
"y_pos": 100
}
}
}"#;
let result = Layout::from_layout_json_str(base_path.clone(), invalid_json);
assert!(result.is_err());
let error_message = result.unwrap_err().message;
assert!(error_message.contains("y_pos + height"));
assert!(error_message.contains("exceeds resolution height"));
let valid_json = invalid_json.replace(r#"y_pos": 100"#, r#"y_pos": 80"#);
let result = Layout::from_layout_json_str(base_path.clone(), &valid_json);
assert!(result.is_ok());
}
#[test]
fn source_timestamps() -> orfail::Result<()> {
let base_path = PathBuf::from("./testdata/source_timestamps/");
let layout_path = base_path.join("layout.json");
let layout = Layout::from_layout_json_file(base_path.clone(), &layout_path).or_fail()?;
assert_eq!(layout.sources.len(), 4);
let id = SourceId::new("0TFGTG90RS5J55VGR0D7Z5QKJC");
let source = layout.sources.get(&id).or_fail()?;
assert_eq!(source.start_timestamp.as_secs(), 0);
assert_eq!(source.stop_timestamp.as_secs(), 31);
let id = SourceId::new("EVYCT2Q86H6ZDCD08JV3YTXCDR");
let source = layout.sources.get(&id).or_fail()?;
assert_eq!(source.start_timestamp.as_secs(), 22);
assert_eq!(source.stop_timestamp.as_secs(), 61);
let id = SourceId::new("SBRZKAAYF526HCRFGPB5VXCX78");
let source = layout.sources.get(&id).or_fail()?;
assert_eq!(source.start_timestamp.as_secs(), 41);
assert_eq!(source.stop_timestamp.as_secs(), 73);
let id = SourceId::new("Z2EDY12WX13QK8X8JDZK81MM0G");
let source = layout.sources.get(&id).or_fail()?;
assert_eq!(source.start_timestamp.as_secs(), 11);
assert_eq!(source.stop_timestamp.as_secs(), 51);
let layout_path = base_path.join("split-layout.json");
let layout = Layout::from_layout_json_file(base_path, &layout_path).or_fail()?;
assert_eq!(layout.sources.len(), 4);
let id = SourceId::new("0TFGTG90RS5J55VGR0D7Z5QKJC");
let source = layout.sources.get(&id).or_fail()?;
assert_eq!(source.start_timestamp.as_secs(), 0);
assert_eq!(source.stop_timestamp.as_secs(), 31);
let id = SourceId::new("EVYCT2Q86H6ZDCD08JV3YTXCDR");
let source = layout.sources.get(&id).or_fail()?;
assert_eq!(source.start_timestamp.as_secs(), 22);
assert_eq!(source.stop_timestamp.as_secs(), 61);
let id = SourceId::new("SBRZKAAYF526HCRFGPB5VXCX78");
let source = layout.sources.get(&id).or_fail()?;
assert_eq!(source.start_timestamp.as_secs(), 41);
assert_eq!(source.stop_timestamp.as_secs(), 73);
let id = SourceId::new("Z2EDY12WX13QK8X8JDZK81MM0G");
let source = layout.sources.get(&id).or_fail()?;
assert_eq!(source.start_timestamp.as_secs(), 11);
assert_eq!(source.stop_timestamp.as_secs(), 51);
Ok(())
}
#[test]
fn trim() -> orfail::Result<()> {
let base_path = PathBuf::from("./testdata/trim/");
let layout_path = base_path.join("layout-trim-true.json");
let layout = Layout::from_layout_json_file(base_path.clone(), &layout_path).or_fail()?;
assert_eq!(layout.sources.len(), 3);
for (start, stop) in [(5, 15), (10, 20), (25, 30)] {
let id = SourceId::new(&format!("{start}-{stop}"));
let source = layout.sources.get(&id).or_fail()?;
assert_eq!(source.start_timestamp.as_secs(), start);
assert_eq!(source.stop_timestamp.as_secs(), stop);
}
for (time, should_trim) in [
(0, true),
(5, false),
(10, false),
(15, false),
(20, true),
(25, false),
] {
assert_eq!(
layout.trim_spans.contains(Duration::from_secs(time)),
should_trim,
"{time}: {should_trim}"
);
}
let layout_path = base_path.join("layout-trim-false.json");
let layout = Layout::from_layout_json_file(base_path.clone(), &layout_path).or_fail()?;
assert_eq!(layout.sources.len(), 3);
for (time, should_trim) in [
(0, true),
(5, false),
(10, false),
(15, false),
(20, false), (25, false),
] {
assert_eq!(
layout.trim_spans.contains(Duration::from_secs(time)),
should_trim,
"{time}: {should_trim}"
);
}
Ok(())
}