diff --git a/common/Cargo.lock b/common/Cargo.lock index 07233c97..4a41fb60 100644 --- a/common/Cargo.lock +++ b/common/Cargo.lock @@ -4,9 +4,9 @@ version = 3 [[package]] name = "anstream" -version = "0.5.0" +version = "0.6.4" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "b1f58811cfac344940f1a400b6e6231ce35171f614f26439e80f8c1465c5cc0c" +checksum = "2ab91ebe16eb252986481c5b62f6098f3b698a45e34b5b98200cf20dd2484a44" dependencies = [ "anstyle", "anstyle-parse", @@ -18,15 +18,15 @@ dependencies = [ [[package]] name = "anstyle" -version = "1.0.3" +version = "1.0.4" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "b84bf0a05bbb2a83e5eb6fa36bb6e87baa08193c35ff52bbf6b38d8af2890e46" +checksum = "7079075b41f533b8c61d2a4d073c4676e1f8b249ff94a393b0595db304e0dd87" [[package]] name = "anstyle-parse" -version = "0.2.1" +version = "0.2.2" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "938874ff5980b03a87c5524b3ae5b59cf99b1d6bc836848df7bc5ada9643c333" +checksum = "317b9a89c1868f5ea6ff1d9539a69f45dffc21ce321ac1fd1160dfa48c8e2140" dependencies = [ "utf8parse", ] @@ -42,9 +42,9 @@ dependencies = [ [[package]] name = "anstyle-wincon" -version = "2.1.0" +version = "3.0.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "58f54d10c6dfa51283a066ceab3ec1ab78d13fae00aa49243a45e4571fb79dfd" +checksum = "f0699d10d2f4d628a98ee7b57b289abbc98ff3bad977cb3152709d4bf2330628" dependencies = [ "anstyle", "windows-sys", @@ -67,9 +67,9 @@ dependencies = [ [[package]] name = "askama" -version = "0.12.0" +version = "0.12.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "47cbc3cf73fa8d9833727bbee4835ba5c421a0d65b72daf9a7b5d0e0f9cfb57e" +checksum = "b79091df18a97caea757e28cd2d5fda49c6cd4bd01ddffd7ff01ace0c0ad2c28" dependencies = [ "askama_derive", "askama_escape", @@ -77,14 +77,14 @@ dependencies = [ [[package]] name = "askama_derive" -version = "0.12.1" +version = "0.12.2" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "c22fbe0413545c098358e56966ff22cdd039e10215ae213cfbd65032b119fc94" +checksum = "9a0fc7dcf8bd4ead96b1d36b41df47c14beedf7b0301fc543d8f2384e66a2ec0" dependencies = [ + "askama_parser", "basic-toml", "mime", "mime_guess", - "nom", "proc-macro2", "quote", "serde", @@ -97,6 +97,15 @@ version = "0.10.3" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "619743e34b5ba4e9703bba34deac3427c72507c7159f5fd030aea8cac0cfe341" +[[package]] +name = "askama_parser" +version = "0.1.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "c268a96e01a4c47c8c5c2472aaa570707e006a875ea63e819f75474ceedaf7b4" +dependencies = [ + "nom", +] + [[package]] name = "assert-json-diff" version = "2.0.2" @@ -169,9 +178,9 @@ checksum = "b4682ae6287fcf752ecaabbfcc7b6f9b72aa33933dc23a554d853aea8eea8635" [[package]] name = "byteorder" -version = "1.4.3" +version = "1.5.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "14c189c53d098945499cdfa7ecc63567cf3886b3332b312a5b4585d8d3a6a610" +checksum = "1fd0f2584146f6f2ef48085050886acf353beff7305ebd1ae69500e27c67f64b" [[package]] name = "bytes" @@ -190,9 +199,9 @@ dependencies = [ [[package]] name = "cargo-platform" -version = "0.1.3" +version = "0.1.4" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "2cfa25e60aea747ec7e1124f238816749faa93759c6ff5b31f1ccdda137f4479" +checksum = "12024c4645c97566567129c204f65d5815a8c9aecf30fcbe682b2fe034996d36" dependencies = [ "serde", ] @@ -211,15 +220,6 @@ dependencies = [ "thiserror", ] -[[package]] -name = "cc" -version = "1.0.83" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "f1174fb0b6ec23863f8b971027804a42614e347eafb0a95bf0b12cdae21fc4d0" -dependencies = [ - "libc", -] - [[package]] name = "cfg-if" version = "1.0.0" @@ -228,9 +228,9 @@ checksum = "baf1de4339761588bc0619e3cbc0120ee582ebb74b53b4efbf79117bd2da40fd" [[package]] name = "clap" -version = "4.4.4" +version = "4.4.6" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "b1d7b8d5ec32af0fadc644bf1fd509a688c2103b185644bb1e29d164e0703136" +checksum = "d04704f56c2cde07f43e8e2c154b43f216dc5c92fc98ada720177362f953b956" dependencies = [ "clap_builder", "clap_derive", @@ -238,9 +238,9 @@ dependencies = [ [[package]] name = "clap_builder" -version = "4.4.4" +version = "4.4.6" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "5179bb514e4d7c2051749d8fcefa2ed6d06a9f4e6d69faf3805f5d80b8cf8d56" +checksum = "0e231faeaca65ebd1ea3c737966bf858971cd38c3849107aa3ea7de90a804e45" dependencies = [ "anstream", "anstyle", @@ -296,25 +296,14 @@ checksum = "a26ae43d7bcc3b814de94796a5e736d4029efb0ee900c12e2d54c993ad1a1e07" [[package]] name = "errno" -version = "0.3.4" +version = "0.3.5" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "add4f07d43996f76ef320709726a556a9d4f965d9410d8d0271132d2f8293480" +checksum = "ac3e13f66a2f95e32a39eaa81f6b95d42878ca0e1db0c7543723dfe12557e860" dependencies = [ - "errno-dragonfly", "libc", "windows-sys", ] -[[package]] -name = "errno-dragonfly" -version = "0.1.2" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "aa68f1b12764fab894d2755d2518754e71b4fd80ecfb822714a1206c2aab39bf" -dependencies = [ - "cc", - "libc", -] - [[package]] name = "fastrand" version = "2.0.1" @@ -469,21 +458,21 @@ checksum = "e2abad23fbc42b3700f2f279844dc832adb2b2eb069b2df918f455c4e18cc646" [[package]] name = "libc" -version = "0.2.148" +version = "0.2.149" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "9cdc71e17332e86d2e1d38c1f99edcb6288ee11b815fb1a4b049eaa2114d369b" +checksum = "a08173bc88b7955d1b3145aa561539096c421ac8debde8cbc3612ec635fee29b" [[package]] name = "libm" -version = "0.2.7" +version = "0.2.8" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "f7012b1bbb0719e1097c47611d3898568c546d597c2e74d66f6087edd5233ff4" +checksum = "4ec2a862134d2a7d32d7983ddcdd1c4923530833c9f2ea1a44fc5fa473989058" [[package]] name = "linux-raw-sys" -version = "0.4.8" +version = "0.4.10" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "3852614a3bd9ca9804678ba6be5e3b8ce76dfc902cae004e3e0c44051b6e88db" +checksum = "da2479e8c062e40bf0066ffa0bc823de0a9368974af99c9f6df941d2c231e03f" [[package]] name = "lock_api" @@ -503,9 +492,9 @@ checksum = "b5e6163cb8c49088c2c36f57875e58ccd8c87c7427f7fbd50ea6710b2f3f2e8f" [[package]] name = "memchr" -version = "2.6.3" +version = "2.6.4" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "8f232d6ef707e1956a43342693d2a31e72989554d58299d7a88738cc95b0d35c" +checksum = "f665ee40bc4a3c5590afb1e9677db74a508659dfd71e126420da8274909a0167" [[package]] name = "mime" @@ -541,9 +530,9 @@ dependencies = [ [[package]] name = "num-traits" -version = "0.2.16" +version = "0.2.17" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "f30b0abd723be7e2ffca1272140fac1a2f084c77ec3e123c192b66af1ee9e6c2" +checksum = "39e3200413f237f41ab11ad6d161bc7239c84dcb631773ccd7de3dfe4b5c267c" dependencies = [ "autocfg", "libm", @@ -584,9 +573,9 @@ checksum = "5b40af805b3121feab8a3c29f04d8ad262fa8e0561883e7653e024ae4479e6de" [[package]] name = "proc-macro2" -version = "1.0.67" +version = "1.0.69" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "3d433d9f1a3e8c1263d9456598b16fec66f4acc9a74dacffd35c7bb09b3a1328" +checksum = "134c189feb4956b20f6f547d2cf727d4c0fe06722b20a0eec87ed445a97f92da" dependencies = [ "unicode-ident", ] @@ -765,9 +754,9 @@ dependencies = [ [[package]] name = "semver" -version = "1.0.18" +version = "1.0.19" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "b0293b4b29daaf487284529cc2f5675b8e57c61f70167ba415a463651fd6a918" +checksum = "ad977052201c6de01a8ef2aa3378c4bd23217a056337d1d6da40468d267a4fb0" dependencies = [ "serde", ] @@ -811,9 +800,9 @@ checksum = "38b58827f4464d87d377d175e90bf58eb00fd8716ff0a62f80356b5e61555d0d" [[package]] name = "smallvec" -version = "1.11.0" +version = "1.11.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "62bb4feee49fdd9f707ef802e22365a35de4b7b299de4763d44bfea899442ff9" +checksum = "942b4a808e05215192e39f4ab80813e599068285906cc91aa64f923db842bd5a" [[package]] name = "spin" @@ -844,9 +833,9 @@ checksum = "73473c0e59e6d5812c5dfe2a064a6444949f089e20eec9a2e5506596494e4623" [[package]] name = "syn" -version = "2.0.37" +version = "2.0.38" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "7303ef2c05cd654186cb250d29049a24840ca25d2747c25c0381c8d9e2f582e8" +checksum = "e96b79aaa137db8f61e26363a0c9b47d8b4ec75da28b7d1d614c2303e232408b" dependencies = [ "proc-macro2", "quote", @@ -868,18 +857,18 @@ dependencies = [ [[package]] name = "thiserror" -version = "1.0.48" +version = "1.0.49" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "9d6d7a740b8a666a7e828dd00da9c0dc290dff53154ea77ac109281de90589b7" +checksum = "1177e8c6d7ede7afde3585fd2513e611227efd6481bd78d2e82ba1ce16557ed4" dependencies = [ "thiserror-impl", ] [[package]] name = "thiserror-impl" -version = "1.0.48" +version = "1.0.49" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "49922ecae66cc8a249b77e68d1d0623c1b2c514f0060c27cdc68bd62a1219d35" +checksum = "10712f02019e9288794769fba95cd6847df9874d49d871d062172f9dd41bc4cc" dependencies = [ "proc-macro2", "quote", diff --git a/common/ferrostar-core/Cargo.toml b/common/ferrostar-core/Cargo.toml index b6829038..df3f5c9f 100644 --- a/common/ferrostar-core/Cargo.toml +++ b/common/ferrostar-core/Cargo.toml @@ -29,4 +29,4 @@ assert-json-diff = "2.0.2" proptest = "1.3.1" [lib] -crate-type = ["cdylib", "staticlib"] +crate-type = ["cdylib", "staticlib", "lib"] diff --git a/common/ferrostar-core/src/models.rs b/common/ferrostar-core/src/models.rs index ae0e30e8..40fad2d9 100644 --- a/common/ferrostar-core/src/models.rs +++ b/common/ferrostar-core/src/models.rs @@ -82,7 +82,7 @@ pub struct Route { /// NOTE: OSRM specifies this rather precisely as "travel along a single way to the subsequent step" /// but we will intentionally define this somewhat looser unless/until it becomes clear something /// -#[derive(Clone, Debug)] +#[derive(Clone, Debug, PartialEq)] pub struct RouteStep { /// The starting location of the step (start of the maneuver). pub start_location: GeographicCoordinates, @@ -96,6 +96,7 @@ pub struct RouteStep { // TODO: trigger_at doesn't really have to live in the public interface; figure out if we want to have a separate FFI vs internal type +#[derive(Debug, PartialEq)] pub struct SpokenInstruction { /// Plain-text instruction which can be synthesized with a TTS engine. pub text: String, @@ -104,6 +105,7 @@ pub struct SpokenInstruction { pub trigger_at: GeographicCoordinates, } +#[derive(Debug, PartialEq)] pub struct VisualInstructions { pub primary_content: VisualInstructionContent, pub secondary_content: Option, @@ -144,6 +146,7 @@ pub enum ManeuverModifier { SharpLeft, } +#[derive(Debug, Eq, PartialEq)] pub struct VisualInstructionContent { pub text: String, pub maneuver_type: Option, diff --git a/common/ferrostar-core/src/navigation_controller/models.rs b/common/ferrostar-core/src/navigation_controller/models.rs index 5494fe02..d5ad7496 100644 --- a/common/ferrostar-core/src/navigation_controller/models.rs +++ b/common/ferrostar-core/src/navigation_controller/models.rs @@ -23,6 +23,7 @@ pub(super) enum TripState { } /// Public updates pushed up to the direct user of the NavigationController. +#[derive(Debug, PartialEq)] pub enum NavigationStateUpdate { Navigating { snapped_user_location: UserLocation, diff --git a/common/ferrostar-core/src/navigation_controller/utils.rs b/common/ferrostar-core/src/navigation_controller/utils.rs index 01aa89fc..c2a746dc 100644 --- a/common/ferrostar-core/src/navigation_controller/utils.rs +++ b/common/ferrostar-core/src/navigation_controller/utils.rs @@ -1,5 +1,5 @@ use crate::models::{GeographicCoordinates, RouteStep, UserLocation}; -use geo::{Closest, HaversineClosestPoint, HaversineDistance, LineString, Point}; +use geo::{Closest, ClosestPoint, HaversineDistance, LineString, Point}; #[cfg(test)] use proptest::prelude::*; @@ -13,7 +13,7 @@ use std::time::SystemTime; pub fn snap_to_line(location: &UserLocation, line: &LineString) -> UserLocation { let original_point = Point::new(location.coordinates.lng, location.coordinates.lat); - match line.haversine_closest_point(&original_point) { + match line.closest_point(&original_point) { Closest::Intersection(snapped) | Closest::SinglePoint(snapped) => UserLocation { coordinates: GeographicCoordinates { lng: snapped.x(), @@ -92,10 +92,10 @@ pub fn do_advance_to_next_step( #[cfg(test)] proptest! { #[test] - fn test_should_advance_exact_position(x1 in -180f64..180f64, y1 in -90f64..90f64, - x2 in -180f64..180f64, y2 in -90f64..90f64, - distance: u16, minimum_horizontal_accuracy: u16, - excess_inaccuracy in 0f64..65535f64) { + fn should_advance_exact_position(x1 in -180f64..180f64, y1 in -90f64..90f64, + x2 in -180f64..180f64, y2 in -90f64..90f64, + distance: u16, minimum_horizontal_accuracy: u16, + excess_inaccuracy in 0f64..65535f64) { let route_step = RouteStep { start_location: GeographicCoordinates { lng: x1, lat: y1 }, end_location: GeographicCoordinates { lng: x2, lat: y2 }, diff --git a/common/ferrostar-core/src/routing_adapters/osrm/mod.rs b/common/ferrostar-core/src/routing_adapters/osrm/mod.rs index 85712e40..cd233787 100644 --- a/common/ferrostar-core/src/routing_adapters/osrm/mod.rs +++ b/common/ferrostar-core/src/routing_adapters/osrm/mod.rs @@ -101,7 +101,7 @@ mod tests { const VALHALLA_OSRM_RESPONSE: &str = r#"{"routes":[{"weight_name":"auto","weight":56.002,"duration":11.488,"distance":284,"legs":[{"via_waypoints":[],"annotation":{"maxspeed":[{"speed":89,"unit":"km/h"},{"speed":89,"unit":"km/h"},{"speed":89,"unit":"km/h"},{"speed":89,"unit":"km/h"},{"speed":89,"unit":"km/h"},{"speed":89,"unit":"km/h"},{"speed":89,"unit":"km/h"},{"speed":89,"unit":"km/h"},{"speed":89,"unit":"km/h"}],"speed":[24.7,24.7,24.7,24.7,24.7,24.7,24.7,24.7,24.7],"distance":[23.6,14.9,9.6,13.2,25,28.1,38.1,41.6,90],"duration":[0.956,0.603,0.387,0.535,1.011,1.135,1.539,1.683,3.641]},"admins":[{"iso_3166_1_alpha3":"USA","iso_3166_1":"US"}],"weight":56.002,"duration":11.488,"steps":[{"intersections":[{"bearings":[288],"entry":[true],"admin_index":0,"out":0,"geometry_index":0,"location":[-149.543469,60.534716]}],"speedLimitUnit":"mph","maneuver":{"type":"depart","instruction":"Drive west on AK 1/Seward Highway.","bearing_after":288,"bearing_before":0,"location":[-149.543469,60.534716]},"speedLimitSign":"mutcd","name":"Seward Highway","duration":11.488,"distance":284,"driving_side":"right","weight":56.002,"mode":"driving","ref":"AK 1","geometry":"wzvmrBxalf|GcCrX}A|Nu@jI}@pMkBtZ{@x^_Afj@Inn@`@veB"},{"intersections":[{"bearings":[89],"entry":[true],"in":0,"admin_index":0,"geometry_index":9,"location":[-149.548581,60.534991]}],"speedLimitUnit":"mph","maneuver":{"type":"arrive","instruction":"You have arrived at your destination.","bearing_after":0,"bearing_before":269,"location":[-149.548581,60.534991]},"speedLimitSign":"mutcd","name":"Seward Highway","duration":0,"distance":0,"driving_side":"right","weight":0,"mode":"driving","ref":"AK 1","geometry":"}kwmrBhavf|G??"}],"distance":284,"summary":"AK 1"}],"geometry":"wzvmrBxalf|GcCrX}A|Nu@jI}@pMkBtZ{@x^_Afj@Inn@`@veB"}],"waypoints":[{"distance":0,"name":"AK 1","location":[-149.543469,60.534715]},{"distance":0,"name":"AK 1","location":[-149.548581,60.534991]}],"code":"Ok"}"#; #[test] - fn test_parse_standard_osrm() { + fn parse_standard_osrm() { let parser = OsrmResponseParser::new(6); let response = parser .parse_response(STANDARD_OSRM_POLYLINE6_RESPONSE.into()) @@ -187,7 +187,7 @@ mod tests { } #[test] - fn test_parse_valhalla_osrm() { + fn parse_valhalla_osrm() { let parser = OsrmResponseParser::new(6); let response = parser .parse_response(VALHALLA_OSRM_RESPONSE.into()) diff --git a/common/ferrostar-core/src/routing_adapters/osrm/models.rs b/common/ferrostar-core/src/routing_adapters/osrm/models.rs index 259e2fe3..5a51e8bb 100644 --- a/common/ferrostar-core/src/routing_adapters/osrm/models.rs +++ b/common/ferrostar-core/src/routing_adapters/osrm/models.rs @@ -264,7 +264,7 @@ mod tests { // TODO: RouteLeg #[test] - fn test_deserialize_annotation() { + fn deserialize_annotation() { // Example from Mapbox's public docs, which include several annotations not supported at // the time of this writing. let data = r#"{ @@ -371,7 +371,7 @@ mod tests { } #[test] - fn test_deserialize_banner_instruction() { + fn deserialize_banner_instruction() { // Example from Mapbox's public docs let data = r#" { diff --git a/common/ferrostar-core/src/routing_adapters/valhalla.rs b/common/ferrostar-core/src/routing_adapters/valhalla.rs index 24167087..2e0a155a 100644 --- a/common/ferrostar-core/src/routing_adapters/valhalla.rs +++ b/common/ferrostar-core/src/routing_adapters/valhalla.rs @@ -115,7 +115,7 @@ mod tests { ]; #[test] - fn test_not_enough_locations() { + fn not_enough_locations() { let generator = ValhallaHttpRequestGenerator::new(ENDPOINT_URL.to_string(), COSTING.to_string()); @@ -127,7 +127,7 @@ mod tests { } #[test] - fn test_request_body_without_course() { + fn request_body_without_course() { let generator = ValhallaHttpRequestGenerator::new(ENDPOINT_URL.to_string(), COSTING.to_string()); @@ -168,7 +168,7 @@ mod tests { } #[test] - fn test_request_body_with_course() { + fn request_body_with_course() { let generator = ValhallaHttpRequestGenerator::new(ENDPOINT_URL.to_string(), COSTING.to_string()); @@ -211,7 +211,7 @@ mod tests { } #[test] - fn test_request_body_with_invalid_horizontal_accuracy() { + fn request_body_with_invalid_horizontal_accuracy() { let generator = ValhallaHttpRequestGenerator::new(ENDPOINT_URL.to_string(), COSTING.to_string()); let location = UserLocation { diff --git a/common/ferrostar-core/tests/navigation_controller.rs b/common/ferrostar-core/tests/navigation_controller.rs new file mode 100644 index 00000000..3a0083a6 --- /dev/null +++ b/common/ferrostar-core/tests/navigation_controller.rs @@ -0,0 +1,147 @@ +extern crate ferrostar_core; + +use ferrostar_core::routing_adapters::osrm::OsrmResponseParser; +use ferrostar_core::{ + NavigationController, NavigationControllerConfig, NavigationStateUpdate, Route, + RouteResponseParser, StepAdvanceMode, UserLocation, +}; +use std::time::SystemTime; + +const TWO_STEP_RESPONSE: &str = r#"{"routes":[{"weight_name":"auto","weight":56.002,"duration":11.488,"distance":284,"legs":[{"via_waypoints":[],"annotation":{"maxspeed":[{"speed":89,"unit":"km/h"},{"speed":89,"unit":"km/h"},{"speed":89,"unit":"km/h"},{"speed":89,"unit":"km/h"},{"speed":89,"unit":"km/h"},{"speed":89,"unit":"km/h"},{"speed":89,"unit":"km/h"},{"speed":89,"unit":"km/h"},{"speed":89,"unit":"km/h"}],"speed":[24.7,24.7,24.7,24.7,24.7,24.7,24.7,24.7,24.7],"distance":[23.6,14.9,9.6,13.2,25,28.1,38.1,41.6,90],"duration":[0.956,0.603,0.387,0.535,1.011,1.135,1.539,1.683,3.641]},"admins":[{"iso_3166_1_alpha3":"USA","iso_3166_1":"US"}],"weight":56.002,"duration":11.488,"steps":[{"intersections":[{"bearings":[288],"entry":[true],"admin_index":0,"out":0,"geometry_index":0,"location":[-149.543469,60.534716]}],"speedLimitUnit":"mph","maneuver":{"type":"depart","instruction":"Drive west on AK 1/Seward Highway.","bearing_after":288,"bearing_before":0,"location":[-149.543469,60.534716]},"speedLimitSign":"mutcd","name":"Seward Highway","duration":11.488,"distance":284,"driving_side":"right","weight":56.002,"mode":"driving","ref":"AK 1","geometry":"wzvmrBxalf|GcCrX}A|Nu@jI}@pMkBtZ{@x^_Afj@Inn@`@veB"},{"intersections":[{"bearings":[89],"entry":[true],"in":0,"admin_index":0,"geometry_index":9,"location":[-149.548581,60.534991]}],"speedLimitUnit":"mph","maneuver":{"type":"arrive","instruction":"You have arrived at your destination.","bearing_after":0,"bearing_before":269,"location":[-149.548581,60.534991]},"speedLimitSign":"mutcd","name":"Seward Highway","duration":0,"distance":0,"driving_side":"right","weight":0,"mode":"driving","ref":"AK 1","geometry":"}kwmrBhavf|G??"}],"distance":284,"summary":"AK 1"}],"geometry":"wzvmrBxalf|GcCrX}A|Nu@jI}@pMkBtZ{@x^_Afj@Inn@`@veB"}],"waypoints":[{"distance":0,"name":"AK 1","location":[-149.543469,60.534715]},{"distance":0,"name":"AK 1","location":[-149.548581,60.534991]}],"code":"Ok"}"#; + +/// Gets a route with two steps. +/// +/// The accuracy of each parser is tested separately in the routing_adapters module; +/// this function simply intends to return a route with two steps. +fn get_route_with_two_steps() -> Route { + let parser = OsrmResponseParser::new(6); + parser + .parse_response(TWO_STEP_RESPONSE.into()) + .expect("Unable to parse OSRM response") + .pop() + .expect("Expected a route") +} + +#[test] +fn simple_route_state_machine_manual_advance() { + let route = get_route_with_two_steps(); + let initial_user_location = UserLocation { + coordinates: route.steps[0].start_location, + horizontal_accuracy: 0.0, + course_over_ground: None, + timestamp: SystemTime::now(), + }; + let user_location_end_of_first_step = UserLocation { + coordinates: route.steps[0].end_location, + horizontal_accuracy: 0.0, + course_over_ground: None, + timestamp: SystemTime::now(), + }; + + let controller = NavigationController::new( + initial_user_location, + route, + NavigationControllerConfig { + step_advance: StepAdvanceMode::Manual, + }, + ); + + // The first update is meaningless in this test, except to get the state + let initial_state = controller.update_user_location(initial_user_location); + let NavigationStateUpdate::Navigating { + snapped_user_location, + current_step: first_step, + .. + } = controller.update_user_location(initial_user_location) + else { + panic!("Expected state to be navigating"); + }; + assert_eq!(initial_user_location, snapped_user_location); + + // Nothing should happen if given the exact same user location update + assert_eq!( + controller.update_user_location(initial_user_location), + initial_state + ); + + // The current step should not advance until we specifically trigger an advance + assert!(matches!( + controller.update_user_location(user_location_end_of_first_step), + NavigationStateUpdate::Navigating { + current_step: first_step, + .. + } + )); + + // Jump to the next step + let NavigationStateUpdate::Navigating { + current_step: second_step, + .. + } = controller.advance_to_next_step() + else { + panic!("Expected state to be navigating"); + }; + + assert_ne!(first_step, second_step); + + // There are only two steps, so advancing to the next step should put us in the "arrived" state + assert!(matches!( + controller.advance_to_next_step(), + NavigationStateUpdate::Arrived { .. } + )); +} + +#[test] +fn simple_route_state_machine_advances_with_location_change() { + let route = get_route_with_two_steps(); + let initial_user_location = UserLocation { + coordinates: route.steps[0].start_location, + horizontal_accuracy: 0.0, + course_over_ground: None, + timestamp: SystemTime::now(), + }; + let user_location_end_of_first_step = UserLocation { + coordinates: route.steps[0].end_location, + horizontal_accuracy: 0.0, + course_over_ground: None, + timestamp: SystemTime::now(), + }; + + let controller = NavigationController::new( + initial_user_location, + route, + NavigationControllerConfig { + // NOTE: We will use an exact location to trigger the update; + // this is not testing the thresholds. + step_advance: StepAdvanceMode::DistanceToLastWaypoint { distance: 0, minimum_horizontal_accuracy: 0 }, + }, + ); + + // The first update is meaningless in this test, except to get the state + let initial_state = controller.update_user_location(initial_user_location); + let NavigationStateUpdate::Navigating { + current_step: first_step, + .. + } = controller.update_user_location(initial_user_location) + else { + panic!("Expected state to be navigating"); + }; + + // Nothing should happen if given the exact same user location update + assert_eq!( + controller.update_user_location(initial_user_location), + initial_state + ); + + // The current step should change when we jump to the end location + let NavigationStateUpdate::Navigating { + current_step: second_step, + .. + } = controller.update_user_location(user_location_end_of_first_step) + else { + panic!("Expected state to be navigating"); + }; + + assert_ne!(first_step, second_step); +} +