From fbf1a9214b8f2ac0f81ce236d04e21332cffe847 Mon Sep 17 00:00:00 2001 From: Patrick Marsee Date: Mon, 20 Jan 2025 13:50:07 -0500 Subject: [PATCH] Added better error checking and tests for appropriate failures. --- src/lib.rs | 2 +- src/rail_graph.rs | 267 ++++++++++++++++++++++++++++++------- src/rail_segment.rs | 23 ++-- src/segment_endpoint.rs | 13 -- src/simple_segment.rs | 282 ++++++++++++++++++++++++++++++---------- 5 files changed, 444 insertions(+), 143 deletions(-) delete mode 100644 src/segment_endpoint.rs diff --git a/src/lib.rs b/src/lib.rs index b585191..0712c68 100644 --- a/src/lib.rs +++ b/src/lib.rs @@ -1,8 +1,8 @@ +//mod graph_coord; mod rail_graph; mod rail_segment; mod rail_connector; mod samplers; -mod segment_endpoint; mod simple_segment; mod prelude { diff --git a/src/rail_graph.rs b/src/rail_graph.rs index e690cb5..1d95c85 100644 --- a/src/rail_graph.rs +++ b/src/rail_graph.rs @@ -1,9 +1,30 @@ use crate::prelude::*; +use crate::rail_segment; use crate::rail_segment::RailSegment; use crate::rail_connector::RailConnector; +//use crate::graph_coord::GraphCoord; //use std::collections::HashMap; use std::vec::Vec; //use std::string::String; +use std::error; +use std::fmt; + +#[derive(Debug)] +pub enum Error { + IndexError(usize), + SegmentError(rail_segment::Error), +} + +impl fmt::Display for Error { + fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { + match self { + Self::IndexError(index) => write!(f, "Nonexistent segment index: {index}."), + Self::SegmentError(err) => write!(f, "Segment error: {err}"), + } + } +} + +impl error::Error for Error {} pub struct RailGraph { segments: Vec>>, @@ -21,10 +42,12 @@ impl RailGraph { } } - pub fn get_segment(&self, index: usize) -> Option<&Box> { + // definition + + pub fn get_segment(&self, index: usize) -> Result<&Box, Error> { match &self.segments.get(index) { - Some(ret) => ret.as_ref(), - None => None, + Some(Some(ret)) => Ok(ret), + _ => Err(Error::IndexError(index)), } } @@ -40,58 +63,110 @@ impl RailGraph { ret } - pub fn remove_segment(&mut self, index: usize) { + pub fn remove_segment(&mut self, index: usize) -> Result { + if index >= self.segments.len() { + return Err(Error::IndexError(index)); + } + self.isolate_segment(index); self.segments[index] = Option::None; if index < self.min_available_index { self.min_available_index = index; } + Ok(true) } - pub fn connect_endpoints(&mut self, index1: usize, endpoint1: usize, index2: usize, endpoint2: usize) { + pub fn connect_endpoints(&mut self, index1: usize, endpoint1: usize, index2: usize, endpoint2: usize) -> Result { + // validate input first + if !self.validate_index(index1) { + return Err(Error::IndexError(index1)); + } else if !self.validate_index(index2) { + return Err(Error::IndexError(index2)); + } else if !self.validate_endpoint(index1, endpoint1) { + return Err(Error::SegmentError(rail_segment::Error::EndpointError(endpoint1))); + } else if !self.validate_endpoint(index2, endpoint2) { + return Err(Error::SegmentError(rail_segment::Error::EndpointError(endpoint2))); + } + // Needs improvement: silently fails with invalid endpoints. let connector1 = RailConnector::new(index2, endpoint2); let connector2 = RailConnector::new(index1, endpoint1); // Before we continue, make sure that anything that these endpoints are already connected to are disconnected. - if let Some(segment1) = &self.segments[index1] { - if let Ok(old_connection) = segment1.borrow_connection(endpoint1) { - let old_index = old_connection.segment_index; - let old_endpoint = old_connection.endpoint_index; - if let Some(old_segment) = &mut self.segments[old_index] { - let _ = old_segment.remove_connection(old_endpoint); + let old_connection1 = self.get_connection_from_index(index1, endpoint1); + let old_connection2 = self.get_connection_from_index(index2, endpoint2); + if let Ok(old_connection) = old_connection1 { + match &mut self.segments[old_connection.segment_index] { + Some(old_segment) => { + if let Err(e) = old_segment.remove_connection(old_connection.endpoint_index) { + return Err(Error::SegmentError(e)); + } + }, + None => { + return Err(Error::IndexError(old_connection.segment_index)); } } } - if let Some(segment2) = &self.segments[index2] { - if let Ok(old_connection) = segment2.borrow_connection(endpoint2) { - let old_index = old_connection.segment_index; - let old_endpoint = old_connection.endpoint_index; - if let Some(old_segment) = &mut self.segments[old_index] { - let _ = old_segment.remove_connection(old_endpoint); + if let Ok(old_connection) = old_connection2 { + match &mut self.segments[old_connection.segment_index] { + Some(old_segment) => { + if let Err(e) = old_segment.remove_connection(old_connection.endpoint_index) { + return Err(Error::SegmentError(e)); + } + }, + None => { + return Err(Error::IndexError(old_connection.segment_index)); } } } // Connect them now - if let Some(segment1) = &mut self.segments[index1] { - let _ = segment1.add_connection(endpoint1, connector1); - } - if let Some(segment2) = &mut self.segments[index2] { - let _ = segment2.add_connection(endpoint2, connector2); - } + let segment1 = &mut self.segments[index1].as_mut().unwrap(); + let _ = segment1.add_connection(endpoint1, connector1); + let segment2 = &mut self.segments[index2].as_mut().unwrap(); + let _ = segment2.add_connection(endpoint2, connector2); + + //if let Some(segment1) = &mut self.segments[index1] { + // if let Err(e) = segment1.add_connection(endpoint1, connector1) { + // return Err(Error::SegmentError(e)); + // } + //} + //if let Some(segment2) = &mut self.segments[index2] { + // if let Err(e) = segment2.add_connection(endpoint2, connector2) { + // return Err(Error::SegmentError(e)); + // } + //} + Ok(true) } - pub fn disconnect_endpoint(&mut self, index: usize, endpoint: usize) { - if let Some(segment) = &self.segments[index] { - if let Ok(old_connection) = segment.borrow_connection(endpoint) { - let old_index = old_connection.segment_index; - let old_endpoint = old_connection.endpoint_index; - if let Some(old_segment) = &mut self.segments[old_index] { - let _ = old_segment.remove_connection(old_endpoint); - } - } + pub fn disconnect_endpoint(&mut self, index: usize, endpoint: usize) -> Result { + if index >= self.segments.len() { + return Err(Error::IndexError(index)); } + + match self.get_connection_from_index(index, endpoint) { + Ok(old_connection) => { + match &mut self.segments[old_connection.segment_index] { + Some(old_segment) => { + if let Err(e) = old_segment.remove_connection(old_connection.endpoint_index) { + return Err(Error::SegmentError(e)); + } + }, + None => { + return Err(Error::IndexError(old_connection.segment_index)); + }, + } + }, + Err(e) => { + return Err(e); + }, + } + if let Some(segment) = &mut self.segments[index] { - let _ = segment.remove_connection(endpoint); + match segment.remove_connection(endpoint) { + Ok(_) => Ok(true), + Err(e) => Err(Error::SegmentError(e)), + } + } else { + Err(Error::IndexError(index)) // should never happen since this is already tested in the big match block. } } @@ -99,9 +174,50 @@ impl RailGraph { new_segment: Box, new_segment_endpoint: usize, onto_segment: usize, - ont_segment_endpoint: usize) { + ont_segment_endpoint: usize) -> Result { let index = self.add_segment(new_segment); - self.connect_endpoints(index, new_segment_endpoint, onto_segment, ont_segment_endpoint); + self.connect_endpoints(index, new_segment_endpoint, onto_segment, ont_segment_endpoint) + } + + // querying and traversal + + //pub fn transform_of_coord(&self, coord: &GraphCoord) -> Transform3D { + // if let Some(segment) = self. + //} + + // private + + fn validate_index(&self, index: usize) -> bool { + match &self.segments.get(index) { + Some(Some(_)) => true, + _ => false, + } + } + + fn validate_endpoint(&self, index: usize, endpoint: usize) -> bool { + match self.get_segment(index) { + Ok(segment) => { + match segment.check_connection(endpoint) { + Err(rail_segment::Error::EndpointError(endpoint)) => false, + _ => true, + } + }, + Err(_) => false, + } + } + + fn get_connection_from_segment(segment: &Box, endpoint: usize) -> Result { + match segment.get_connection(endpoint) { + Ok(connection) => Ok(connection), + Err(e) => Err(Error::SegmentError(e)), + } + } + + fn get_connection_from_index(&self, index: usize, endpoint: usize) -> Result { + match self.get_segment(index) { + Ok(segment) => Self::get_connection_from_segment(segment, endpoint), + Err(e) => Err(e), + } } fn isolate_segment(&mut self, index: usize) { @@ -165,22 +281,29 @@ mod tests { let mut test_graph = RailGraph::new(); test_graph.add_segment(test_segment1); - test_graph.append_segment(test_segment2, 0, 0, 1); - test_graph.append_segment(test_segment3, 0, 1, 1); + let _ = test_graph.append_segment(test_segment2, 0, 0, 1); + let _ = test_graph.append_segment(test_segment3, 0, 1, 1); return test_graph; } #[test] fn test_get_segment() { - let test_graph = create_test_graph(); - assert!(match test_graph.get_segment(0) { Option::Some(_)=>true, _=>false }, + let mut test_graph = create_test_graph(); + + // test successes + assert!(match test_graph.get_segment(0) { Ok(_)=>true, Err(_)=>false, }, "Segment 0 could not be retrieved."); - assert!(match test_graph.get_segment(1) { Option::Some(_)=>true, _=>false }, + assert!(match test_graph.get_segment(1) { Ok(_)=>true, _=>false, }, "Segment 1 could not be retrieved."); - assert!(match test_graph.get_segment(2) { Option::Some(_)=>true, _=>false }, + assert!(match test_graph.get_segment(2) { Ok(_)=>true, _=>false, }, "Segment 2 could not be retrieved."); - assert!(match test_graph.get_segment(3) { Option::None=>true, _=>false }, - "Segment that didn't exist was retrieved."); + + // test failures + assert!(match test_graph.get_segment(3) { Err(Error::IndexError(3)) =>true, _=>false, }, + "Segment was retrieved: index out of bounds."); + _ = test_graph.remove_segment(1); + assert!(match test_graph.get_segment(1) { Err(Error::IndexError(1)) =>true, _=>false, }, + "Segment was retrieved: deleted."); } #[test] @@ -198,21 +321,57 @@ mod tests { #[test] fn test_remove_segment() { let mut test_graph = create_test_graph(); - test_graph.remove_segment(1); + + // test failures + assert!(match test_graph.remove_segment(3) { Err(Error::IndexError(3))=>true, _=>false, }, + "Non-existent segment was removed."); + + // test successes + assert!(match test_graph.remove_segment(1) { Ok(true)=>true, _=>false, }, + "Segment was not removed."); assert_eq!(test_graph.segments.len(), 3, "Graph has incorrect number of segments."); assert_eq!(test_graph.min_available_index, 1, "Incorrect min_available_index."); let first_segment = test_graph.get_segment(0).unwrap(); assert!(!first_segment.check_connection(1).unwrap(), "Connection 1 wasn't broken."); let second_segment = test_graph.get_segment(2).unwrap(); assert!(!second_segment.check_connection(0).unwrap(), "Connection 2 wasn't broken."); - assert!(match test_graph.get_segment(1) { Option::None=>true, _=>false }, + assert!(match test_graph.get_segment(1) { Err(Error::IndexError(1))=>true, _=>false, }, "Segment that didn't exist was retrieved."); } #[test] fn test_connect_endpoints() { let mut test_graph = create_test_graph(); - test_graph.connect_endpoints(0, 0, 2, 1); + + // test failures + assert!(match test_graph.connect_endpoints(3, 0, 2, 1) { Err(Error::IndexError(3))=>true, _=> false, }, + "Connection(3, 0) to (2, 1) was made."); + assert!(match test_graph.get_segment(2).unwrap().borrow_connection(1) { Err(rail_segment::Error::ConnectionError(1))=>true, _=>false, }, + "Invalid connection at (2, 1) #1."); + assert!(match test_graph.connect_endpoints(0, 2, 2, 1) { Err(Error::SegmentError(rail_segment::Error::EndpointError(2)))=>true, _=> false, }, + "Connection(0, 2) to (2, 1) was made."); + assert!(match test_graph.get_segment(2).unwrap().borrow_connection(1) { Err(rail_segment::Error::ConnectionError(1))=>true, _=>false, }, + "Invalid connection at (2, 1) #2."); + assert!(match test_graph.connect_endpoints(0, 0, 3, 1) { Err(Error::IndexError(3))=>true, _=> false, }, + "Connection(0, 0) to (3, 1) was made."); + assert!(match test_graph.get_segment(0).unwrap().borrow_connection(0) { Err(rail_segment::Error::ConnectionError(0))=>true, _=>false, }, + "Invalid connection at (0, 0) #1."); + assert!(match test_graph.connect_endpoints(0, 0, 2, 2) { Err(Error::SegmentError(rail_segment::Error::EndpointError(2)))=>true, _=> false, }, + "Connection(0, 0) to (2, 2) was made."); + assert!(match test_graph.get_segment(0).unwrap().borrow_connection(0) { Err(rail_segment::Error::ConnectionError(0))=>true, _=>false, }, + "Invalid connection at (0, 0) #2."); + assert!(match test_graph.connect_endpoints(0, 1, 3, 1) { Err(Error::IndexError(3))=>true, _=> false, }, + "Connection(0, 1) to (3, 1) was made."); + assert!(match test_graph.get_segment(0).unwrap().borrow_connection(1) { Ok(_)=>true, _=>false, }, + "Removed connection at (0, 1) #1."); + assert!(match test_graph.connect_endpoints(0, 1, 2, 2) { Err(Error::SegmentError(rail_segment::Error::EndpointError(2)))=>true, _=> false, }, + "Connection(0, 1) to (2, 2) was made."); + assert!(match test_graph.get_segment(0).unwrap().borrow_connection(1) { Ok(_)=>true, _=>false, }, + "Removed connection at (0, 1) #2."); + + // test successes + assert!(match test_graph.connect_endpoints(0, 0, 2, 1) { Ok(true)=>true, _=> false, }, + "Connection(0, 0) to (2, 1) was not made."); let first_segment = test_graph.get_segment(0).unwrap(); let first_connection = first_segment.borrow_connection(0).unwrap(); assert_eq!(first_connection.segment_index, 2, "Connection(0, 0) had the incorrect index."); @@ -221,7 +380,8 @@ mod tests { let second_connection = second_segment.borrow_connection(1).unwrap(); assert_eq!(second_connection.segment_index, 0, "Connection(2, 1) had the incorrect index."); assert_eq!(second_connection.endpoint_index, 0, "Connection(2, 1) had the incorrect endpoint."); - test_graph.connect_endpoints(0, 1, 2, 0); + assert!(match test_graph.connect_endpoints(0, 1, 2, 0) { Ok(true)=>true, _=> false, }, + "Connection(0, 1) to (2, 0) was not made."); let first_segment = test_graph.get_segment(0).unwrap(); let first_connection = first_segment.borrow_connection(1).unwrap(); assert_eq!(first_connection.segment_index, 2, "Connection(0, 1) had the incorrect index."); @@ -242,7 +402,18 @@ mod tests { #[test] fn test_disconnect_endpoint() { let mut test_graph = create_test_graph(); - test_graph.disconnect_endpoint(0, 1); + + // test failures + assert!(match test_graph.disconnect_endpoint(3, 1) { Err(Error::IndexError(3))=>true, _=>false }, + "Invalid segment accessed."); + assert!(match test_graph.disconnect_endpoint(1, 2) { Err(Error::SegmentError(rail_segment::Error::EndpointError(2)))=>true, _=>false }, + "Invalid endpoint disconnected."); + assert!(match test_graph.disconnect_endpoint(0, 0) { Err(Error::SegmentError(rail_segment::Error::ConnectionError(0)))=>true, _=>false }, + "Unconnected endpoint disconnected."); + + // test successes + assert!(match test_graph.disconnect_endpoint(0, 1) { Ok(true)=>true, _=>false }, + "Endpoint failed to disconnect."); let first_segment = test_graph.get_segment(0).unwrap(); let first_connection = first_segment.borrow_connection(1); assert!(match first_connection { Result::Err(_)=>true, _=>false }, diff --git a/src/rail_segment.rs b/src/rail_segment.rs index e083bc8..bd793e7 100644 --- a/src/rail_segment.rs +++ b/src/rail_segment.rs @@ -6,19 +6,21 @@ use std::iter::Iterator; #[derive(Debug)] pub enum Error { - IndexError, - EndpointError, - OffsetError, - NeighborError, + IndexError(usize), + EndpointError(usize), + OffsetError(f32), + ConnectionError(usize), + StateError(f32), } impl fmt::Display for Error { fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { match self { - Self::IndexError => write!(f, "Nonexistent internal curve index"), - Self::EndpointError => write!(f, "Nonexistent endpoint index"), - Self::OffsetError => write!(f, "Invalid offset"), - Self::NeighborError => write!(f, "Invalid Neighbor"), + Self::IndexError(index) => write!(f, "Nonexistent sub-segment index: {index}."), + Self::EndpointError(endpoint) => write!(f, "Nonexistent endpoint index: {endpoint}."), + Self::OffsetError(offset) => write!(f, "Invalid offset: {offset}."), + Self::ConnectionError(endpoint) => write!(f, "Endpoint {endpoint} is not connected."), + Self::StateError(state) => write!(f, "Invalid state: {state}."), } } } @@ -106,6 +108,9 @@ pub trait RailSegment { /** Borrow the connector associated with the given endpoint. */ fn borrow_connection(&self, endpoint: usize) -> Result<&RailConnector, Error>; + /** Copy the connector associated with the given endpoint. */ + fn get_connection(&self, endpoint: usize) -> Result; + /** Check if a connection is established on the given endpoint. */ fn check_connection(&self, endpoint: usize) -> Result; @@ -132,5 +137,5 @@ pub trait RailSegment { fn get_state(&self) -> f32; /** Set the internal state float value. */ - fn set_state(&mut self, value: f32); + fn set_state(&mut self, value: f32) -> Result; } diff --git a/src/segment_endpoint.rs b/src/segment_endpoint.rs deleted file mode 100644 index 7077a6f..0000000 --- a/src/segment_endpoint.rs +++ /dev/null @@ -1,13 +0,0 @@ -pub struct SegmentEndpoint { - internal_segment_index: usize, - is_upper: bool, -} - -impl SegmentEndpoint { - fn new(internal_segment_index: usize, is_upper: bool) -> Self { - Self { - internal_segment_index, - is_upper, - } - } -} diff --git a/src/simple_segment.rs b/src/simple_segment.rs index d4cf2bb..e3777a8 100644 --- a/src/simple_segment.rs +++ b/src/simple_segment.rs @@ -14,6 +14,12 @@ fn new_looking_at(direction: Vector3, up: Vector3, _positive_z_forward: bool) -> Basis::from_cols(x, y, z) } +/** + * A simple segment with one alignment sampler and one elevation sampler. + * This necessarily has exactly 2 endpoints and 1 sub-segment. + * Despite the sub-segment being unambiguous, it shall still fail if the incorrect index is used. + * This is also true of its float state. + */ pub struct SimpleSegment { alignment: A, elevation: E, @@ -25,11 +31,12 @@ pub struct SimpleSegment { // cache some stuff //endpoint0: Option, //endpoint1: Option, - //length: Option, + length: f32, } impl SimpleSegment { pub fn new(alignment: A, elevation: E) -> Self { + let length = elevation.length(); Self { alignment, elevation, @@ -37,27 +44,25 @@ impl SimpleSegment { upper_connection: Option::None, //endpoint0: None, //endpoint1: None, - //length: None, + length, } } } impl RailSegment for SimpleSegment { - fn get_length(&self, _index: usize) -> Result { - /*match self.length { - Some(length) => Ok(length), - None => { - let result = self.elevation.length(); - self.length = Some(result); - Ok(result) - }, - }*/ - Ok(self.elevation.length()) + fn get_length(&self, index: usize) -> Result { + if index == 0 { + Ok(self.length) + } else { + Err(rail_segment::Error::IndexError(index)) + } } - fn sample(&self, _index: usize, offset: f32) -> Result { - if offset < 0.0 || offset > self.get_length(1).unwrap() { - return Err(rail_segment::Error::OffsetError); + fn sample(&self, index: usize, offset: f32) -> Result { + if index != 0 { + return Err(rail_segment::Error::IndexError(index)); + } else if offset < 0.0 || offset > self.length { + return Err(rail_segment::Error::OffsetError(offset)); } let top_down_t = self.elevation.to_top_down_normalized(offset / self.elevation.length()); let xz = self.alignment.sample_normalized(top_down_t); @@ -65,9 +70,11 @@ impl RailSegment for SimpleSegment Result { - if offset < 0.0 || offset > self.get_length(1).unwrap() { - return Err(rail_segment::Error::OffsetError); + fn sample_with_rotation(&self, index: usize, offset: f32) -> Result { + if index != 0 { + return Err(rail_segment::Error::IndexError(index)); + } else if offset < 0.0 || offset > self.length { + return Err(rail_segment::Error::OffsetError(offset)); } let top_down_t = self.elevation.to_top_down_normalized(offset / self.elevation.length()); let direction_xz = self.alignment.tangent_normalized(top_down_t); @@ -79,9 +86,11 @@ impl RailSegment for SimpleSegment Result { - if offset < 0.0 || offset > self.get_length(1).unwrap() { - return Err(rail_segment::Error::OffsetError); + fn sample_with_rotation_reverse(&self, index: usize, offset: f32) -> Result { + if index != 0 { + return Err(rail_segment::Error::IndexError(index)); + } else if offset < 0.0 || offset > self.length { + return Err(rail_segment::Error::OffsetError(offset)); } let top_down_t = self.elevation.to_top_down_normalized(offset / self.elevation.length()); let direction_zx = self.alignment.tangent_normalized(top_down_t); @@ -93,9 +102,11 @@ impl RailSegment for SimpleSegment Result { - if offset < 0.0 || offset > self.get_length(1).unwrap() { - return Err(rail_segment::Error::OffsetError); + fn sample_up_vector(&self, index: usize, offset: f32) -> Result { + if index != 0 { + return Err(rail_segment::Error::IndexError(index)); + } else if offset < 0.0 || offset > self.length { + return Err(rail_segment::Error::OffsetError(offset)); } let top_down_t = self.elevation.to_top_down_normalized(offset / self.elevation.length()); let direction_xz = self.alignment.tangent_normalized(top_down_t); @@ -105,35 +116,19 @@ impl RailSegment for SimpleSegment Result { - Ok(1) + fn segment_from_endpoint(&self, endpoint: usize) -> Result { + if endpoint > 1 { + return Err(rail_segment::Error::EndpointError(endpoint)); + } else { + Ok(0) + } } fn get_endpoint_transform(&self, endpoint: usize) -> Result { match endpoint { - /*0 => { - match self.endpoint0 { - Some(result) => Ok(result), - None => { - let result = self.sample_with_rotation_reverse(0, 0.0).unwrap(); - self.endpoint0 = Some(result); - Ok(result) - }, - } - }, - 1 => { - match self.endpoint1 { - Some(result) => Ok(result), - None => { - let result = self.sample_with_rotation(0, self.get_length(1).unwrap()).unwrap(); - self.endpoint1 = Some(result); - Ok(result) - }, - } - },*/ 0 => self.sample_with_rotation_reverse(0, 0.0), 1 => self.sample_with_rotation(0, self.get_length(1).unwrap()), - _ => Err(rail_segment::Error::EndpointError), + _ => Err(rail_segment::Error::EndpointError(endpoint)), } } @@ -141,7 +136,7 @@ impl RailSegment for SimpleSegment Ok(false), 1 => Ok(true), - _ => Err(rail_segment::Error::EndpointError), + _ => Err(rail_segment::Error::EndpointError(endpoint)), } } @@ -155,17 +150,37 @@ impl RailSegment for SimpleSegment { if let Some(ret) = &self.upper_connection { Ok(ret) } else { - Err(rail_segment::Error::NeighborError) + Err(rail_segment::Error::ConnectionError(endpoint)) } }, - _ => Err(rail_segment::Error::EndpointError), + _ => Err(rail_segment::Error::EndpointError(endpoint)), + } + } + + fn get_connection(&self, endpoint: usize) -> Result { + match endpoint { + 0 => { + if let Some(ret) = self.lower_connection { + Ok(ret) + } else { + Err(rail_segment::Error::ConnectionError(endpoint)) + } + }, + 1 => { + if let Some(ret) = self.upper_connection { + Ok(ret) + } else { + Err(rail_segment::Error::ConnectionError(endpoint)) + } + }, + _ => Err(rail_segment::Error::EndpointError(endpoint)), } } @@ -183,7 +198,7 @@ impl RailSegment for SimpleSegment Ok(false), } }, - _ => Err(rail_segment::Error::EndpointError), + _ => Err(rail_segment::Error::EndpointError(endpoint)), } } @@ -201,7 +216,7 @@ impl RailSegment for SimpleSegment Err(rail_segment::Error::EndpointError), + _ => Err(rail_segment::Error::EndpointError(endpoint)), } } @@ -215,7 +230,7 @@ impl RailSegment for SimpleSegment Err(rail_segment::Error::EndpointError), + _ => Err(rail_segment::Error::EndpointError(endpoint)), } } @@ -228,8 +243,8 @@ impl RailSegment for SimpleSegment Result { + Err(rail_segment::Error::StateError(state)) } } @@ -264,8 +279,14 @@ mod tests { let alignment = TangentAlignmentSampler::new(Vector2::new(1.0, 1.0), Vector2::new(10.0, 13.0)); let elevation = TangentElevationSampler::new(1.0, 9.0, 15.0); let test_segment = SimpleSegment::new(alignment, elevation); - assert_eq!(test_segment.get_length(1).unwrap(), 17.0, - "Segment length was incorrect."); + + // test success + assert!(match test_segment.get_length(0) { Ok(17.0)=>true, _=>false, }, + "Segment length was incorrect."); + + // test failure + assert!(match test_segment.get_length(1) { Err(rail_segment::Error::IndexError(1))=>true, _=>false, }, + "Got length of non-existant sub-segment."); } #[test] @@ -273,12 +294,22 @@ mod tests { let alignment = TangentAlignmentSampler::new(Vector2::new(1.0, 1.0), Vector2::new(10.0, 13.0)); let elevation = TangentElevationSampler::new(1.0, 9.0, 15.0); let test_segment = SimpleSegment::new(alignment, elevation); - assert_eq!(test_segment.sample(1, 0.0).unwrap(), Vector3::new(1.0, 1.0, 1.0), + + // test successes + assert_eq!(test_segment.sample(0, 0.0).unwrap(), Vector3::new(1.0, 1.0, 1.0), "Segment begins at the wrong location."); - assert_eq!(test_segment.sample(1, 17.0).unwrap(), Vector3::new(10.0, 9.0, 13.0), + assert_eq!(test_segment.sample(0, 17.0).unwrap(), Vector3::new(10.0, 9.0, 13.0), "Segment ends at the wrong location."); - assert_eq!(test_segment.sample(1, 8.5).unwrap(), Vector3::new(5.5, 5.0, 7.0), + assert_eq!(test_segment.sample(0, 8.5).unwrap(), Vector3::new(5.5, 5.0, 7.0), "Segment passes through the wrong location."); + + // test failures + assert!(match test_segment.sample(1, 0.0) { Err(rail_segment::Error::IndexError(1))=>true, _=>false, }, + "Sampled non-existant sub-segment."); + assert!(match test_segment.sample(0, -1.0) { Err(rail_segment::Error::OffsetError(-1.0))=>true, _=>false, }, + "Sampled segment with negative offset."); + assert!(match test_segment.sample(0, 18.0) { Err(rail_segment::Error::OffsetError(18.0))=>true, _=>false, }, + "Sampled segment with excess offset."); } #[test] @@ -289,8 +320,10 @@ mod tests { let direction_forward = Vector3::new(-9.0 / 17.0, -8.0 / 17.0, -12.0 / 17.0); let direction_right = Vector3::new(-0.8, 0.0, 0.6); let direction_up = direction_forward.cross(direction_right); - let transform1 = test_segment.sample_with_rotation(1, 0.0).unwrap(); + let transform1 = test_segment.sample_with_rotation(0, 0.0).unwrap(); let epsilon = 1e-6; + + // test successes assert_eq!(transform1.origin, Vector3::new(1.0, 1.0, 1.0), "Segment begins at the wrong location."); compare_vectors(transform1.basis.col_c(), direction_forward, epsilon, @@ -299,16 +332,16 @@ mod tests { "Segment begins with the wrong right direction."); compare_vectors(transform1.basis.col_b(), direction_up, epsilon, "Segment begins with the wrong up direction."); - let transform2 = test_segment.sample_with_rotation(1, 17.0).unwrap(); + let transform2 = test_segment.sample_with_rotation(0, 17.0).unwrap(); assert_eq!(transform2.origin, Vector3::new(10.0, 9.0, 13.0), "Segment ends at the wrong location."); compare_vectors(transform2.basis.col_c(), direction_forward, epsilon, "Segment ends facing the wrong direction."); compare_vectors(transform2.basis.col_a(), direction_right, epsilon, - "Segment ends with the wrong righ direction."); + "Segment ends with the wrong right direction."); compare_vectors(transform2.basis.col_b(), direction_up, epsilon, "Segment ends with the wrong up direction."); - let transform3 = test_segment.sample_with_rotation(1, 8.5).unwrap(); + let transform3 = test_segment.sample_with_rotation(0, 8.5).unwrap(); assert_eq!(transform3.origin, Vector3::new(5.5, 5.0, 7.0), "Segment passes through the wrong location."); compare_vectors(transform3.basis.col_c(), direction_forward, epsilon, @@ -317,6 +350,14 @@ mod tests { "Segment passes through with the wrong righ direction."); compare_vectors(transform3.basis.col_b(), direction_up, epsilon, "Segment passes through with the wrong up direction."); + + // test failures + assert!(match test_segment.sample_with_rotation(1, 0.0) { Err(rail_segment::Error::IndexError(1))=>true, _=>false, }, + "Sampled non-existant sub-segment."); + assert!(match test_segment.sample_with_rotation(0, -1.0) { Err(rail_segment::Error::OffsetError(-1.0))=>true, _=>false, }, + "Sampled segment with negative offset."); + assert!(match test_segment.sample_with_rotation(0, 18.0) { Err(rail_segment::Error::OffsetError(18.0))=>true, _=>false, }, + "Sampled segment with excess offset."); } #[test] @@ -327,8 +368,10 @@ mod tests { let direction_forward = Vector3::new(9.0 / 17.0, 8.0 / 17.0, 12.0 / 17.0); let direction_right = Vector3::new(0.8, 0.0, -0.6); let direction_up = direction_forward.cross(direction_right); - let transform1 = test_segment.sample_with_rotation_reverse(1, 0.0).unwrap(); + let transform1 = test_segment.sample_with_rotation_reverse(0, 0.0).unwrap(); let epsilon = 1e-6; + + // test successes assert_eq!(transform1.origin, Vector3::new(1.0, 1.0, 1.0), "Segment begins at the wrong location."); compare_vectors(transform1.basis.col_c(), direction_forward, epsilon, @@ -337,16 +380,16 @@ mod tests { "Segment begins with the wrong right direction."); compare_vectors(transform1.basis.col_b(), direction_up, epsilon, "Segment begins with the wrong up direction."); - let transform2 = test_segment.sample_with_rotation_reverse(1, 17.0).unwrap(); + let transform2 = test_segment.sample_with_rotation_reverse(0, 17.0).unwrap(); assert_eq!(transform2.origin, Vector3::new(10.0, 9.0, 13.0), "Segment ends at the wrong location."); compare_vectors(transform2.basis.col_c(), direction_forward, epsilon, "Segment ends facing the wrong direction."); compare_vectors(transform2.basis.col_a(), direction_right, epsilon, - "Segment ends with the wrong righ direction."); + "Segment ends with the wrong right direction."); compare_vectors(transform2.basis.col_b(), direction_up, epsilon, "Segment ends with the wrong up direction."); - let transform3 = test_segment.sample_with_rotation_reverse(1, 8.5).unwrap(); + let transform3 = test_segment.sample_with_rotation_reverse(0, 8.5).unwrap(); assert_eq!(transform3.origin, Vector3::new(5.5, 5.0, 7.0), "Segment passes through the wrong location."); compare_vectors(transform3.basis.col_c(), direction_forward, epsilon, @@ -355,6 +398,14 @@ mod tests { "Segment passes through with the wrong righ direction."); compare_vectors(transform3.basis.col_b(), direction_up, epsilon, "Segment passes through with the wrong up direction."); + + // test failures + assert!(match test_segment.sample_with_rotation_reverse(1, 0.0) { Err(rail_segment::Error::IndexError(1))=>true, _=>false, }, + "Sampled non-existant sub-segment."); + assert!(match test_segment.sample_with_rotation_reverse(0, -1.0) { Err(rail_segment::Error::OffsetError(-1.0))=>true, _=>false, }, + "Sampled segment with negative offset."); + assert!(match test_segment.sample_with_rotation_reverse(0, 18.0) { Err(rail_segment::Error::OffsetError(18.0))=>true, _=>false, }, + "Sampled segment with excess offset."); } #[test] @@ -365,15 +416,102 @@ mod tests { let direction_forward = Vector3::new(-9.0 / 17.0, -8.0 / 17.0, -12.0 / 17.0); let direction_right = Vector3::new(-0.8, 0.0, 0.6); let direction_up = direction_forward.cross(direction_right); - let up1 = test_segment.sample_up_vector(1, 0.0).unwrap(); + let up1 = test_segment.sample_up_vector(0, 0.0).unwrap(); let epsilon = 1e-6; + + // test successes compare_vectors(up1, direction_up, epsilon, "Segment begins with the wrong up direction."); - let up2 = test_segment.sample_up_vector(1, 17.0).unwrap(); + let up2 = test_segment.sample_up_vector(0, 17.0).unwrap(); compare_vectors(up2, direction_up, epsilon, "Segment ends with the wrong up direction."); - let up3 = test_segment.sample_up_vector(1, 8.5).unwrap(); + let up3 = test_segment.sample_up_vector(0, 8.5).unwrap(); compare_vectors(up3, direction_up, epsilon, "Segment passes through with the wrong up direction."); + + // test failures + assert!(match test_segment.sample_up_vector(1, 0.0) { Err(rail_segment::Error::IndexError(1))=>true, _=>false, }, + "Sampled non-existant sub-segment."); + assert!(match test_segment.sample_up_vector(0, -1.0) { Err(rail_segment::Error::OffsetError(-1.0))=>true, _=>false, }, + "Sampled segment with negative offset."); + assert!(match test_segment.sample_up_vector(0, 18.0) { Err(rail_segment::Error::OffsetError(18.0))=>true, _=>false, }, + "Sampled segment with excess offset."); } + + #[test] + fn test_segment_from_endpoint() { + let alignment = TangentAlignmentSampler::new(Vector2::new(1.0, 1.0), Vector2::new(10.0, 13.0)); + let elevation = TangentElevationSampler::new(1.0, 9.0, 15.0); + let test_segment = SimpleSegment::new(alignment, elevation); + + // test successes + assert_eq!(test_segment.segment_from_endpoint(0).unwrap(), 0, "Endpoint 0 points to incorrect internal segment."); + assert_eq!(test_segment.segment_from_endpoint(1).unwrap(), 0, "Endpoint 1 points to incorrect internal segment."); + + // test failures + assert!(match test_segment.segment_from_endpoint(2) { Err(rail_segment::Error::EndpointError(2))=>true, _=>false, }, + "Endpoint 2 points to an internal segment."); + } + + #[test] + fn test_get_endpoint_transform() { + let alignment = TangentAlignmentSampler::new(Vector2::new(1.0, 1.0), Vector2::new(10.0, 13.0)); + let elevation = TangentElevationSampler::new(1.0, 9.0, 15.0); + let test_segment = SimpleSegment::new(alignment, elevation); + let direction_forward = Vector3::new(9.0 / 17.0, 8.0 / 17.0, 12.0 / 17.0); + let direction_right = Vector3::new(0.8, 0.0, -0.6); + let direction_up = direction_forward.cross(direction_right); + + // test successes + let transform1 = test_segment.get_endpoint_transform(0).unwrap(); + let epsilon = 1e-6; + assert_eq!(transform1.origin, Vector3::new(1.0, 1.0, 1.0), + "Segment begins at the wrong location."); + compare_vectors(transform1.basis.col_c(), direction_forward, epsilon, + "Segment begins facing the wrong direction."); + compare_vectors(transform1.basis.col_a(), direction_right, epsilon, + "Segment begins with the wrong right direction."); + compare_vectors(transform1.basis.col_b(), direction_up, epsilon, + "Segment begins with the wrong up direction."); + + let transform2 = test_segment.sample_with_rotation(0, 17.0).unwrap(); + assert_eq!(transform2.origin, Vector3::new(10.0, 9.0, 13.0), + "Segment ends at the wrong location."); + compare_vectors(transform2.basis.col_c(), -direction_forward, epsilon, + "Segment ends facing the wrong direction."); + compare_vectors(transform2.basis.col_a(), -direction_right, epsilon, + "Segment ends with the wrong right direction."); + compare_vectors(transform2.basis.col_b(), direction_up, epsilon, + "Segment ends with the wrong up direction."); + + // test failures + assert!(match test_segment.get_endpoint_transform(2) { Err(rail_segment::Error::EndpointError(2))=>true, _=>false, }, + "Endpoint 2 has a transform."); + } + + #[test] + fn test_is_endpoint_upper() { + let alignment = TangentAlignmentSampler::new(Vector2::new(1.0, 1.0), Vector2::new(10.0, 13.0)); + let elevation = TangentElevationSampler::new(1.0, 9.0, 15.0); + let test_segment = SimpleSegment::new(alignment, elevation); + + // test successes + assert_eq!(test_segment.is_endpoint_upper(0).unwrap(), false, "Endpoint 0 is upper."); + assert_eq!(test_segment.is_endpoint_upper(1).unwrap(), true, "Endpoint 1 is lower."); + + // test failures + assert!(match test_segment.is_endpoint_upper(2) { Err(rail_segment::Error::EndpointError(2))=>true, _=>false, }, + "Endpoint 2 should not be upper or lower."); + } + + #[test] + fn test_num_endpoints() { + // By far the most critical and intricate test. + let alignment = TangentAlignmentSampler::new(Vector2::new(1.0, 1.0), Vector2::new(10.0, 13.0)); + let elevation = TangentElevationSampler::new(1.0, 9.0, 15.0); + let test_segment = SimpleSegment::new(alignment, elevation); + assert_eq!(test_segment.num_endpoints(), 2, "Incorrect number of endpoints."); + } + + // For now, I'm not going to write tests for most of the connection methods since they're basically all getters and setters. }