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 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, } 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) { // get graph-components dependent on own direction 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, metric_indices: DimVec, alternative_nodes: Vec>, } impl TestPath { pub fn from_alternatives( src: TestNode, dst: TestNode, costs: DimVec, metric_indices: DimVec, alternative_nodes: Vec>, ) -> 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, ); // flatten shortcuts 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 { // build path from own path let mut own_proto_path = Vec::new(); // build own path 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(), ); } // check path 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; } // check path-cost 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; } } // check if one correct alternative has been equal // if not, print error // ATTENTION: order is important since path is set above before cost 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 ); } } }