use kissunits::{distance::Kilometers, geo::Coordinate, speed::KilometersPerHour, time::Hours};
use osmgraphing::{
approximating::Approx,
defaults::capacity::DimVec,
network::{EdgeIdx, Graph, MetricIdx, Node, NodeIdx},
routing::{self},
};
use smallvec::{smallvec, SmallVec};
use std::fmt::{self, Display};
#[derive(Clone, Debug)]
pub struct TestNode {
pub name: String,
pub id: i64,
pub idx: NodeIdx,
pub coord: Coordinate,
pub ch_level: usize,
}
impl From<Node> for TestNode {
fn from(node: Node) -> TestNode {
TestNode {
name: "node-from-graph".to_owned(),
id: node.id(),
idx: node.idx(),
coord: node.coord(),
ch_level: node.ch_level(),
}
}
}
impl Display for TestNode {
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
write!(f, "{} (idx={}, id={})", self.name, self.idx, self.id)
}
}
impl PartialEq for TestNode {
fn eq(&self, other: &Self) -> bool {
self.id == other.id
&& self.idx == other.idx
&& Approx(self.coord) == Approx(other.coord)
&& self.ch_level == other.ch_level
}
}
impl TestNode {
#[allow(dead_code)]
pub fn new(name: &str, id: i64, coord: Coordinate, ch_level: usize, graph: &Graph) -> TestNode {
let idx = graph
.nodes()
.idx_from(id)
.expect(&format!("The node-id {} is not in graph.", id));
TestNode {
name: String::from(name),
id,
idx,
coord,
ch_level,
}
}
}
#[allow(dead_code)]
pub struct TestEdge {
name: String,
edge_idx: EdgeIdx,
is_fwd: bool,
src_idx: NodeIdx,
dst_idx: NodeIdx,
metrics: DimVec<f64>,
}
impl TestEdge {
#[allow(dead_code)]
pub fn new_fwd(
name: Option<&str>,
edge_idx: EdgeIdx,
src: &TestNode,
dst: &TestNode,
distance: Kilometers,
maxspeed: KilometersPerHour,
duration: Hours,
) -> TestEdge {
TestEdge {
name: (name.unwrap_or(&format!("{}->{}", src.name, dst.name))).to_owned(),
edge_idx: edge_idx,
is_fwd: true,
src_idx: src.idx.into(),
dst_idx: dst.idx.into(),
metrics: smallvec![*distance, *maxspeed, *duration],
}
}
#[allow(dead_code)]
pub fn new_bwd(
name: Option<&str>,
edge_idx: EdgeIdx,
src: &TestNode,
dst: &TestNode,
distance: Kilometers,
maxspeed: KilometersPerHour,
duration: Hours,
) -> TestEdge {
TestEdge {
name: (name.unwrap_or(&format!("{}->{}", src.name, dst.name))).to_owned(),
edge_idx: edge_idx,
is_fwd: false,
src_idx: src.idx.into(),
dst_idx: dst.idx.into(),
metrics: smallvec![*distance, *maxspeed, *duration],
}
}
#[allow(dead_code)]
pub fn assert_correct(&self, graph: &Graph) {
let fwd_edges = graph.fwd_edges();
let bwd_edges = graph.bwd_edges();
let edge = {
if self.is_fwd {
fwd_edges
.between(self.src_idx, self.dst_idx)
.expect(&format!(
"Fwd-edge (src_idx, dst_idx): ({}, {}) does not exist.",
self.src_idx, self.dst_idx
))
} else {
bwd_edges
.between(self.src_idx, self.dst_idx)
.expect(&format!(
"Bwd-edge (src_idx, dst_idx): ({}, {}) does not exist.",
self.src_idx, self.dst_idx
))
}
};
let prefix = {
if self.is_fwd {
"fwd-"
} else {
"bwd-"
}
};
assert_eq!(
edge.idx(),
self.edge_idx,
"Wrong {}edge-idx={} for {}",
prefix,
edge.idx(),
self.name
);
assert_eq!(
edge.dst_idx(),
self.dst_idx,
"Wrong dst_idx={} for {}edge {}",
edge.dst_idx(),
prefix,
self.name
);
let expected: DimVec<_> = SmallVec::from_slice(&self.metrics);
assert!(
Approx(edge.metrics()) == Approx(&expected),
"Wrong metrics {:?} for {}edge {}. Expected: {:?}",
edge.metrics(),
prefix,
self.name,
expected
);
}
}
pub struct TestPath {
src: TestNode,
dst: TestNode,
costs: DimVec<f64>,
metric_indices: DimVec<MetricIdx>,
alternative_nodes: Vec<Vec<TestNode>>,
}
impl TestPath {
pub fn from_alternatives(
src: TestNode,
dst: TestNode,
costs: DimVec<f64>,
metric_indices: DimVec<MetricIdx>,
alternative_nodes: Vec<Vec<TestNode>>,
) -> TestPath {
TestPath {
src,
dst,
costs,
metric_indices,
alternative_nodes,
}
}
pub fn assert_correct(&self, actual_path: &routing::paths::Path, graph: &Graph) {
let node = |idx: NodeIdx| -> TestNode { TestNode::from(graph.nodes().create(idx)) };
let path_src = node(actual_path.src_idx());
assert_eq!(
&path_src.idx, &self.src.idx,
"Path has wrong src-idx {} (should be {})",
&path_src.idx, &self.src.idx,
);
let path_dst = node(actual_path.dst_idx());
assert_eq!(
&path_dst.idx, &self.dst.idx,
"Path has wrong dst-idx {} (should be {})",
&path_dst.idx, &self.dst.idx,
);
let flattened_actual_path = actual_path.clone().flatten(graph);
let mut is_path_eq = false;
let mut wrong_path_result = None;
let mut wrong_cost_result = None;
let mut is_cost_eq = false;
for nodes in &self.alternative_nodes {
let mut own_proto_path = Vec::new();
let fwd_edges = graph.fwd_edges();
let mut iter = nodes.windows(2);
while let Some([test_src, test_dst]) = iter.next() {
own_proto_path.push(
fwd_edges
.between(test_src.idx, test_dst.idx)
.expect(&format!(
"Edge expected between idx={} and idx={}. Path is from idx={} to idx={}",
test_src.idx, test_dst.idx, path_src.idx, path_dst.idx
))
.idx(),
);
}
let expected_path = routing::paths::Path::new(
self.src.idx,
self.src.id,
self.dst.idx,
self.dst.id,
own_proto_path,
);
if expected_path != flattened_actual_path {
wrong_path_result = Some((expected_path, &flattened_actual_path));
continue;
} else {
is_path_eq = true;
}
let (expected_cost, actual_cost) = (
&self.costs,
self.metric_indices
.iter()
.map(|&metric_idx| flattened_actual_path.costs()[*metric_idx])
.collect(),
);
if Approx(expected_cost) != Approx(&actual_cost) {
wrong_cost_result = Some((expected_cost, actual_cost));
continue;
} else {
is_cost_eq = true;
}
}
if !is_path_eq {
let (expected_path, flattened_actual_path) =
wrong_path_result.expect("Fix testing path: Bool is set wrongly.");
panic!(
"Graph: {}; Path from src {} to dst {} is not equal. (expected: {}, actual: {})",
graph, self.src, self.dst, expected_path, flattened_actual_path
);
}
if !is_cost_eq {
let (expected_cost, actual_cost) =
wrong_cost_result.expect("Fix testing path-cost: Bool is set wrongly.");
panic!(
"Path-cost {:?} from src {} to dst {} is not correct (expected: {:?}).",
actual_cost, self.src, self.dst, expected_cost
);
}
}
}