Make the first 3 nearest/pick.feature scenarios pass

This commit is contained in:
Dennis 2024-05-31 21:07:10 +02:00
parent 59cbb08c0e
commit 5cf37a0c7c
No known key found for this signature in database
GPG Key ID: 6937EAEA33A3FA5D
9 changed files with 122 additions and 29 deletions

8
Cargo.lock generated
View File

@ -548,6 +548,12 @@ version = "0.5.0"
source = "registry+https://github.com/rust-lang/crates.io-index" source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "2304e00983f87ffb38b55b444b5e3b60a884b5d30c0fca7d82fe33449bbe55ea" checksum = "2304e00983f87ffb38b55b444b5e3b60a884b5d30c0fca7d82fe33449bbe55ea"
[[package]]
name = "help"
version = "0.0.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "b5d2c6714f78261bbacbd07338eb21943fa5a25c67760287aecb6aefbec89062"
[[package]] [[package]]
name = "humantime" name = "humantime"
version = "2.1.0" version = "2.1.0"
@ -740,6 +746,8 @@ dependencies = [
"cucumber", "cucumber",
"futures", "futures",
"geo-types", "geo-types",
"help",
"serde",
"serde_json", "serde_json",
"ureq", "ureq",
"xml-builder", "xml-builder",

View File

@ -10,6 +10,8 @@ clap = "4.5.4"
cucumber = "0.21.0" cucumber = "0.21.0"
futures = "0.3.30" futures = "0.3.30"
geo-types = "0.7.13" geo-types = "0.7.13"
help = "0.0.0"
serde = { version = "1.0.203", features = ["serde_derive"] }
serde_json = "1.0.117" serde_json = "1.0.117"
ureq = "2.9.7" ureq = "2.9.7"
xml-builder = "0.5.2" xml-builder = "0.5.2"

View File

@ -7,6 +7,7 @@
#include "util/json_container.hpp" #include "util/json_container.hpp"
#include <boost/format.hpp> #include <boost/format.hpp>
#include <iostream>
namespace osrm::server::service namespace osrm::server::service
{ {
@ -30,11 +31,11 @@ std::string getWrongOptionHelp(const engine::api::NearestParameters &parameters)
return help; return help;
} }
} // namespace } // namespace
engine::Status NearestService::RunQuery(std::size_t prefix_length, engine::Status NearestService::RunQuery(std::size_t prefix_length,
std::string &query, std::string &query,
osrm::engine::api::ResultT &result) osrm::engine::api::ResultT &result)
{ {
std::cout << "running query: " << query << "\n";
result = util::json::Object(); result = util::json::Object();
auto &json_result = std::get<util::json::Object>(result); auto &json_result = std::get<util::json::Object>(result);

View File

@ -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 // TODO: port into toolbox-rs
pub struct LexicographicFileWalker { pub struct LexicographicFileWalker {

View File

@ -1,4 +1,5 @@
pub mod lexicographic_file_walker; pub mod lexicographic_file_walker;
pub mod nearest_response;
pub mod osm; pub mod osm;
pub mod osm_db; pub mod osm_db;
pub mod osrm_world; pub mod osrm_world;

View File

@ -0,0 +1,23 @@
use geo_types::{point, Point};
use serde::Deserialize;
#[derive(Deserialize, Debug)]
pub struct Waypoint {
hint: String,
nodes: Vec<u64>,
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<Waypoint>,
}

View File

@ -1,5 +1,5 @@
use xml_builder::{XMLBuilder, XMLElement, XMLVersion};
use super::osm::{OSMNode, OSMWay}; use super::osm::{OSMNode, OSMWay};
use xml_builder::{XMLBuilder, XMLElement, XMLVersion};
// TODO: better error handling in XML creation // TODO: better error handling in XML creation
#[derive(Debug, Default)] #[derive(Debug, Default)]

View File

@ -1,6 +1,6 @@
use std::{collections::HashMap, fs::File, path::PathBuf};
use crate::Point; use crate::Point;
use cucumber::World; use cucumber::World;
use std::{collections::HashMap, fs::File, path::PathBuf};
use super::{osm::OSMNode, osm_db::OSMDb}; use super::{osm::OSMNode, osm_db::OSMDb};
@ -58,10 +58,17 @@ impl OSRMWorld {
pub fn get_location(&self, name: char) -> Point { pub fn get_location(&self, name: char) -> Point {
match name { match name {
// TODO: move lookup to world // TODO: move lookup to world
'0'..='9' => self.known_locations.get(&name).expect("test case specifies unknown location: {name}"), '0'..='9' => self
'a'..='z' => self.known_osm_nodes.get(&name).expect("test case specifies unknown osm node: {name}"), .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]"), _ => unreachable!("nodes have to be name in [0-9][a-z]"),
}.clone() }
.clone()
} }
pub fn add_location(&mut self, name: char, location: Point) { pub fn add_location(&mut self, name: char, location: Point) {

View File

@ -6,20 +6,20 @@ use crate::common::osrm_world::OSRMWorld;
use cheap_ruler::CheapRuler; use cheap_ruler::CheapRuler;
use clap::Parser; use clap::Parser;
use common::lexicographic_file_walker::LexicographicFileWalker; use common::lexicographic_file_walker::LexicographicFileWalker;
use common::nearest_response::NearestResponse;
use common::osm::OSMWay; use common::osm::OSMWay;
use serde_json::Value;
use ureq::Agent;
use core::panic; use core::panic;
use std::time::Duration;
use cucumber::{self, gherkin::Step, given, when, World}; use cucumber::{self, gherkin::Step, given, when, World};
use futures::{future, FutureExt}; use futures::{future, FutureExt};
use geo_types::{point, Point}; use geo_types::{point, Point};
use std::fmt::Display; use std::fmt::{format, Display};
use std::fs::{create_dir_all, File}; 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::path::PathBuf;
use std::process::Command; use std::process::{Command, Stdio};
use std::time::Duration;
use std::{env, fs}; use std::{env, fs};
use ureq::Agent;
const DEFAULT_ORIGIN: [f64; 2] = [1., 1.]; // TODO: move to world? const DEFAULT_ORIGIN: [f64; 2] = [1., 1.]; // TODO: move to world?
const DEFAULT_GRID_SIZE: f64 = 100.; // 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"); let data_path = cache_path.join(world.scenario_id.to_owned() + ".osrm");
println!("{routed_path:?} {}", data_path.to_str().unwrap()); 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()) .arg(data_path.to_str().unwrap())
.stdout(Stdio::piped())
.spawn(); .spawn();
if let Err(e) = handle { let child = match &mut handle {
panic!("{e}"); 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() let agent: Agent = ureq::AgentBuilder::new()
.timeout_read(Duration::from_secs(5)) .timeout_read(Duration::from_secs(5))
.timeout_write(Duration::from_secs(5)) .timeout_write(Duration::from_secs(5))
.build(); .build();
// parse and run test cases // parse and run test cases
for (query, expected) in 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:?}"); println!("{query_location:?} => {expected_location:?}");
// run queries // run queries
// "http://localhost:5000/nearest/v1/testbot/1.0008984512067491,1.0" // "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 url = format!(
let body: String = agent.get(&url) "http://localhost:5000/nearest/v1/{}/{},{}",
.call().unwrap() world.profile,
.into_string().unwrap(); query_location.x(),
query_location.y()
);
let call = agent.get(&url).call();
let v: Value = serde_json::from_str(&body).unwrap(); let body = match call {
let result_location = point!{ x: v["location"][0].as_f64().unwrap(), y: v["location"][1].as_f64().unwrap()}; Ok(response) => response.into_string().expect("response not parseable"),
assert_eq!(result_location, expected_location) 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}"); // println!("{body}");
// check results // check results
} }
if let Err(e) = handle.expect("osrm-routed died unexpectedly").kill() { if let Err(e) = child.kill() {
panic!("shutdown failed: {e}"); 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 // TODO: move to different file
fn get_file_as_byte_vec(path: &PathBuf) -> Vec<u8> { fn get_file_as_byte_vec(path: &PathBuf) -> Vec<u8> {
println!("opening {path:?}"); println!("opening {path:?}");