diff --git a/Cargo.lock b/Cargo.lock index e378284a4..e60a4b6b1 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -548,6 +548,12 @@ version = "0.5.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "2304e00983f87ffb38b55b444b5e3b60a884b5d30c0fca7d82fe33449bbe55ea" +[[package]] +name = "help" +version = "0.0.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "b5d2c6714f78261bbacbd07338eb21943fa5a25c67760287aecb6aefbec89062" + [[package]] name = "humantime" version = "2.1.0" @@ -740,6 +746,8 @@ dependencies = [ "cucumber", "futures", "geo-types", + "help", + "serde", "serde_json", "ureq", "xml-builder", diff --git a/Cargo.toml b/Cargo.toml index 108275ac8..3073382bb 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -10,6 +10,8 @@ clap = "4.5.4" cucumber = "0.21.0" futures = "0.3.30" geo-types = "0.7.13" +help = "0.0.0" +serde = { version = "1.0.203", features = ["serde_derive"] } serde_json = "1.0.117" ureq = "2.9.7" xml-builder = "0.5.2" diff --git a/src/server/service/nearest_service.cpp b/src/server/service/nearest_service.cpp index 8eb0d1ea0..e0e9c0f3f 100644 --- a/src/server/service/nearest_service.cpp +++ b/src/server/service/nearest_service.cpp @@ -7,6 +7,7 @@ #include "util/json_container.hpp" #include +#include namespace osrm::server::service { @@ -30,11 +31,11 @@ std::string getWrongOptionHelp(const engine::api::NearestParameters ¶meters) return help; } } // namespace - engine::Status NearestService::RunQuery(std::size_t prefix_length, std::string &query, osrm::engine::api::ResultT &result) { + std::cout << "running query: " << query << "\n"; result = util::json::Object(); auto &json_result = std::get(result); diff --git a/tests/common/lexicographic_file_walker.rs b/tests/common/lexicographic_file_walker.rs index 973b201ba..46a348a06 100644 --- a/tests/common/lexicographic_file_walker.rs +++ b/tests/common/lexicographic_file_walker.rs @@ -1,4 +1,8 @@ -use std::{collections::VecDeque, fs, path::{Path, PathBuf}}; +use std::{ + collections::VecDeque, + fs, + path::{Path, PathBuf}, +}; // TODO: port into toolbox-rs pub struct LexicographicFileWalker { diff --git a/tests/common/mod.rs b/tests/common/mod.rs index e5cb08593..586d66912 100644 --- a/tests/common/mod.rs +++ b/tests/common/mod.rs @@ -1,4 +1,5 @@ pub mod lexicographic_file_walker; +pub mod nearest_response; pub mod osm; pub mod osm_db; pub mod osrm_world; diff --git a/tests/common/nearest_response.rs b/tests/common/nearest_response.rs new file mode 100644 index 000000000..893d25b91 --- /dev/null +++ b/tests/common/nearest_response.rs @@ -0,0 +1,23 @@ +use geo_types::{point, Point}; +use serde::Deserialize; + +#[derive(Deserialize, Debug)] +pub struct Waypoint { + hint: String, + nodes: Vec, + distance: f64, + name: String, + location: [f64; 2], +} + +impl Waypoint { + pub fn location(&self) -> Point { + point!(self.location) + } +} + +#[derive(Deserialize, Debug)] +pub struct NearestResponse { + code: String, + pub waypoints: Vec, +} diff --git a/tests/common/osm_db.rs b/tests/common/osm_db.rs index c0de48da6..fc8ee1a71 100644 --- a/tests/common/osm_db.rs +++ b/tests/common/osm_db.rs @@ -1,5 +1,5 @@ -use xml_builder::{XMLBuilder, XMLElement, XMLVersion}; use super::osm::{OSMNode, OSMWay}; +use xml_builder::{XMLBuilder, XMLElement, XMLVersion}; // TODO: better error handling in XML creation #[derive(Debug, Default)] @@ -124,4 +124,4 @@ mod tests { println!("{actual}"); assert_eq!(actual, expected); } -} \ No newline at end of file +} diff --git a/tests/common/osrm_world.rs b/tests/common/osrm_world.rs index c2826da2e..e22f2c77f 100644 --- a/tests/common/osrm_world.rs +++ b/tests/common/osrm_world.rs @@ -1,6 +1,6 @@ -use std::{collections::HashMap, fs::File, path::PathBuf}; use crate::Point; use cucumber::World; +use std::{collections::HashMap, fs::File, path::PathBuf}; use super::{osm::OSMNode, osm_db::OSMDb}; @@ -58,10 +58,17 @@ impl OSRMWorld { pub fn get_location(&self, name: char) -> Point { match name { // TODO: move lookup to world - '0'..='9' => self.known_locations.get(&name).expect("test case specifies unknown location: {name}"), - 'a'..='z' => self.known_osm_nodes.get(&name).expect("test case specifies unknown osm node: {name}"), + '0'..='9' => self + .known_locations + .get(&name) + .expect("test case specifies unknown location: {name}"), + 'a'..='z' => self + .known_osm_nodes + .get(&name) + .expect("test case specifies unknown osm node: {name}"), _ => unreachable!("nodes have to be name in [0-9][a-z]"), - }.clone() + } + .clone() } pub fn add_location(&mut self, name: char, location: Point) { @@ -70,4 +77,4 @@ impl OSRMWorld { } self.known_locations.insert(name, location); } -} \ No newline at end of file +} diff --git a/tests/cucumber.rs b/tests/cucumber.rs index 36e41e00d..897291f6d 100644 --- a/tests/cucumber.rs +++ b/tests/cucumber.rs @@ -6,20 +6,20 @@ use crate::common::osrm_world::OSRMWorld; use cheap_ruler::CheapRuler; use clap::Parser; use common::lexicographic_file_walker::LexicographicFileWalker; +use common::nearest_response::NearestResponse; use common::osm::OSMWay; -use serde_json::Value; -use ureq::Agent; use core::panic; -use std::time::Duration; use cucumber::{self, gherkin::Step, given, when, World}; use futures::{future, FutureExt}; use geo_types::{point, Point}; -use std::fmt::Display; +use std::fmt::{format, Display}; use std::fs::{create_dir_all, File}; -use std::io::{Read, Write}; +use std::io::{BufRead, BufReader, Read, Write}; use std::path::PathBuf; -use std::process::Command; +use std::process::{Command, Stdio}; +use std::time::Duration; use std::{env, fs}; +use ureq::Agent; const DEFAULT_ORIGIN: [f64; 2] = [1., 1.]; // TODO: move to world? const DEFAULT_GRID_SIZE: f64 = 100.; // TODO: move to world? @@ -192,18 +192,38 @@ fn request_nearest(world: &mut OSRMWorld, step: &Step) { let data_path = cache_path.join(world.scenario_id.to_owned() + ".osrm"); println!("{routed_path:?} {}", data_path.to_str().unwrap()); - let handle = Command::new(routed_path) + + // TODO: move the child handling into a convenience struct + let mut handle = Command::new(routed_path) .arg(data_path.to_str().unwrap()) + .stdout(Stdio::piped()) .spawn(); - if let Err(e) = handle { - panic!("{e}"); + let child = match &mut handle { + Ok(o) => o, + Err(e) => panic!("cannot access handle: {e}"), + }; + + let mut running = false; + if let Some(output) = &mut child.stdout { + let mut reader = BufReader::new(output); + let mut line = String::new(); + while let Ok(count) = reader.read_line(&mut line) { + println!("count: {count} ->{line}"); + if line.contains("running and waiting for requests") { + running = true; + break; + } + } + } + if !running { + panic! {"routed not started"} } let agent: Agent = ureq::AgentBuilder::new() - .timeout_read(Duration::from_secs(5)) - .timeout_write(Duration::from_secs(5)) - .build(); + .timeout_read(Duration::from_secs(5)) + .timeout_write(Duration::from_secs(5)) + .build(); // parse and run test cases for (query, expected) in test_cases { @@ -213,23 +233,50 @@ fn request_nearest(world: &mut OSRMWorld, step: &Step) { println!("{query_location:?} => {expected_location:?}"); // run queries // "http://localhost:5000/nearest/v1/testbot/1.0008984512067491,1.0" - let url = format!("http://localhost:5000/nearest/v1/{}/{},{}", world.profile, query_location.x(), query_location.y()); - let body: String = agent.get(&url) - .call().unwrap() - .into_string().unwrap(); + let url = format!( + "http://localhost:5000/nearest/v1/{}/{},{}", + world.profile, + query_location.x(), + query_location.y() + ); + let call = agent.get(&url).call(); - let v: Value = serde_json::from_str(&body).unwrap(); - let result_location = point!{ x: v["location"][0].as_f64().unwrap(), y: v["location"][1].as_f64().unwrap()}; - assert_eq!(result_location, expected_location) + let body = match call { + Ok(response) => response.into_string().expect("response not parseable"), + Err(e) => panic!("http error: {e}"), + }; + + println!("body: {body}"); + + let v: NearestResponse = match serde_json::from_str(&body) { + Ok(v) => v, + Err(e) => panic!("parsing error {e}"), + }; + + let result_location = v.waypoints[0].location(); + // assert_eq!(result_location, expected_location) + assert!(approx_equal(result_location.x(), expected_location.x(), 5)); + assert!(approx_equal(result_location.y(), expected_location.y(), 5)); // println!("{body}"); // check results } - if let Err(e) = handle.expect("osrm-routed died unexpectedly").kill() { + if let Err(e) = child.kill() { panic!("shutdown failed: {e}"); } } +// matchLocation (got, want) { +// if (got == null || want == null) return false; +// return this.match(got[0], util.format('%d ~0.0025%', want.lon)) && +// this.match(got[1], util.format('%d ~0.0025%', want.lat)); +// } + +pub fn approx_equal(a: f64, b: f64, dp: u8) -> bool { + let p = 10f64.powi(-(dp as i32)); + (a - b).abs() < p +} + // TODO: move to different file fn get_file_as_byte_vec(path: &PathBuf) -> Vec { println!("opening {path:?}");