diff options
Diffstat (limited to 'tests')
-rw-r--r-- | tests/arguments.rs | 158 | ||||
-rw-r--r-- | tests/common/errorfunc.rs | 66 | ||||
-rw-r--r-- | tests/common/errorpanic.rs | 21 |
3 files changed, 241 insertions, 4 deletions
diff --git a/tests/arguments.rs b/tests/arguments.rs new file mode 100644 index 0000000..4b5a2a0 --- /dev/null +++ b/tests/arguments.rs @@ -0,0 +1,158 @@ +// Copyright (c) 2021 Soni L. +// Copyright (c) Microsoft Corporation. All rights reserved. +// Licensed under the MIT license. +// Documentation and comments licensed under CC BY-SA 4.0. + +// because we wanna use double underscore (__) for test names +#![allow(non_snake_case)] + +use ::std::io::Cursor; +use ::std::marker::PhantomData; + +use ::iosonism::args::ArgumentType; +use ::iosonism::args::BoolArgumentType; +use ::iosonism::args::FloatArgumentType; +use ::iosonism::args::IntegerArgumentType; +use ::iosonism::strcursor::StringReader; + +mod common; + +use self::common::ErrorCall; +use self::common::ErrorFunc; +use self::common::ErrorPanic; +use self::common::ErrorType; + +#[test] +fn test_bool__parse() { + assert_eq!( + ArgumentType::<(), ErrorPanic>::parse( + &BoolArgumentType, + &mut Cursor::new("true"), + ), + Ok(true), + ); + assert_eq!( + ArgumentType::<(), ErrorPanic>::parse( + &BoolArgumentType, + &mut Cursor::new("false"), + ), + Ok(false), + ); +} + +#[test] +fn test_i32__parse() { + let mut reader = Cursor::new("15"); + assert_eq!( + ArgumentType::<(), ErrorPanic>::parse( + &IntegerArgumentType { range: .., _ty: PhantomData::<i32> }, + &mut reader, + ), + Ok(15), + ); + assert!(!reader.can_read()); +} + +#[test] +fn test_i32__parse__range() { + let mut reader = Cursor::new("-5"); + assert!(ArgumentType::<(), ErrorCall<ErrFn>>::parse( + &IntegerArgumentType { range: 0..=100, _ty: PhantomData::<i32> }, + &mut reader, + ).is_err()); + struct ErrFn; + impl<'a> ErrorFunc<'a, Cursor<&'a str>> for ErrFn { + fn call(context: &Cursor<&'a str>, ty: ErrorType) { + assert!(matches!(ty, ErrorType::RangeErrori32(..))); + assert_eq!(context.position(), 0); + } + } +} + +#[test] +fn test_i64__parse() { + let mut reader = Cursor::new("15"); + assert_eq!( + ArgumentType::<(), ErrorPanic>::parse( + &IntegerArgumentType { range: .., _ty: PhantomData::<i64> }, + &mut reader, + ), + Ok(15), + ); + assert!(!reader.can_read()); +} + +#[test] +fn test_i64__parse__range() { + let mut reader = Cursor::new("-5"); + assert!(ArgumentType::<(), ErrorCall<ErrFn>>::parse( + &IntegerArgumentType { range: 0..=100, _ty: PhantomData::<i64> }, + &mut reader, + ).is_err()); + struct ErrFn; + impl<'a> ErrorFunc<'a, Cursor<&'a str>> for ErrFn { + fn call(context: &Cursor<&'a str>, ty: ErrorType) { + assert!(matches!(ty, ErrorType::RangeErrori64(..))); + assert_eq!(context.position(), 0); + } + } +} + +#[test] +fn test_f32__parse() { + let mut reader = Cursor::new("15"); + assert_eq!( + ArgumentType::<(), ErrorPanic>::parse( + &FloatArgumentType { range: .., _ty: PhantomData::<f32> }, + &mut reader, + ), + Ok(15.0), + ); + assert!(!reader.can_read()); +} + +#[test] +fn test_f32__parse__range() { + let mut reader = Cursor::new("-5"); + assert!(ArgumentType::<(), ErrorCall<ErrFn>>::parse( + &FloatArgumentType { range: 0.0..=100.0, _ty: PhantomData::<f32> }, + &mut reader, + ).is_err()); + struct ErrFn; + impl<'a> ErrorFunc<'a, Cursor<&'a str>> for ErrFn { + fn call(context: &Cursor<&'a str>, ty: ErrorType) { + assert!(matches!(ty, ErrorType::RangeErrorf32(..))); + assert_eq!(context.position(), 0); + } + } +} + +#[test] +fn test_f64__parse() { + let mut reader = Cursor::new("15"); + assert_eq!( + ArgumentType::<(), ErrorPanic>::parse( + &FloatArgumentType { range: .., _ty: PhantomData::<f64> }, + &mut reader, + ), + Ok(15.0), + ); + assert!(!reader.can_read()); +} + +#[test] +fn test_f64__parse__range() { + let mut reader = Cursor::new("-5"); + assert!(ArgumentType::<(), ErrorCall<ErrFn>>::parse( + &FloatArgumentType { range: 0.0..=100.0, _ty: PhantomData::<f64> }, + &mut reader, + ).is_err()); + struct ErrFn; + impl<'a> ErrorFunc<'a, Cursor<&'a str>> for ErrFn { + fn call(context: &Cursor<&'a str>, ty: ErrorType) { + assert!(matches!(ty, ErrorType::RangeErrorf64(..))); + assert_eq!(context.position(), 0); + } + } +} + diff --git a/tests/common/errorfunc.rs b/tests/common/errorfunc.rs index 926c94c..5fb5fb6 100644 --- a/tests/common/errorfunc.rs +++ b/tests/common/errorfunc.rs @@ -4,8 +4,11 @@ // Documentation and comments licensed under CC BY-SA 4.0. use ::std::marker::PhantomData; +use ::std::ops::Bound; +use ::std::ops::RangeBounds; -use ::iosonism::strcursor::ReadError; +use ::iosonism::error::RangeError; +use ::iosonism::error::ReadError; use ::iosonism::strcursor::StringReader; /// An error callback. @@ -17,7 +20,7 @@ pub trait ErrorFunc<'a, C: StringReader<'a>> { pub struct ErrorCall<T>(PhantomData<T>); #[non_exhaustive] -#[derive(PartialEq, Eq, Debug)] +#[derive(PartialEq, Debug)] pub enum ErrorType<'a> { InvalidInteger(&'a str), ExpectedInteger, @@ -29,6 +32,17 @@ pub enum ErrorType<'a> { ExpectedEndOfQuote, InvalidEscape(&'a str), ExpectedSymbol(&'a str), + RangeErrori32(RangeErrorT<i32>), + RangeErrori64(RangeErrorT<i64>), + RangeErrorf32(RangeErrorT<f32>), + RangeErrorf64(RangeErrorT<f64>), +} + +#[derive(PartialEq, Debug)] +pub struct RangeErrorT<T> { + pub value: T, + pub start: Bound<T>, + pub end: Bound<T>, } impl<T> ::std::fmt::Display for ErrorCall<T> { @@ -46,6 +60,54 @@ impl<T> ::std::fmt::Debug for ErrorCall<T> { impl<T> ::std::error::Error for ErrorCall<T> { } +impl<'a, C, R, T> RangeError<'a, C, i32, R> for ErrorCall<T> +where C: StringReader<'a>, R: RangeBounds<i32>, T: ErrorFunc<'a, C> { + fn value_not_in_range(context: &C, from: &i32, range: &R) -> Self { + T::call(context, ErrorType::RangeErrori32(RangeErrorT { + value: *from, + start: range.start_bound().cloned(), + end: range.end_bound().cloned(), + })); + Self(PhantomData) + } +} + +impl<'a, C, R, T> RangeError<'a, C, i64, R> for ErrorCall<T> +where C: StringReader<'a>, R: RangeBounds<i64>, T: ErrorFunc<'a, C> { + fn value_not_in_range(context: &C, from: &i64, range: &R) -> Self { + T::call(context, ErrorType::RangeErrori64(RangeErrorT { + value: *from, + start: range.start_bound().cloned(), + end: range.end_bound().cloned(), + })); + Self(PhantomData) + } +} + +impl<'a, C, R, T> RangeError<'a, C, f32, R> for ErrorCall<T> +where C: StringReader<'a>, R: RangeBounds<f32>, T: ErrorFunc<'a, C> { + fn value_not_in_range(context: &C, from: &f32, range: &R) -> Self { + T::call(context, ErrorType::RangeErrorf32(RangeErrorT { + value: *from, + start: range.start_bound().cloned(), + end: range.end_bound().cloned(), + })); + Self(PhantomData) + } +} + +impl<'a, C, R, T> RangeError<'a, C, f64, R> for ErrorCall<T> +where C: StringReader<'a>, R: RangeBounds<f64>, T: ErrorFunc<'a, C> { + fn value_not_in_range(context: &C, from: &f64, range: &R) -> Self { + T::call(context, ErrorType::RangeErrorf64(RangeErrorT { + value: *from, + start: range.start_bound().cloned(), + end: range.end_bound().cloned(), + })); + Self(PhantomData) + } +} + impl<'a, C: StringReader<'a>, T> ReadError<'a, C> for ErrorCall<T> where T: ErrorFunc<'a, C> { fn invalid_integer(context: &C, from: &str) -> Self { diff --git a/tests/common/errorpanic.rs b/tests/common/errorpanic.rs index faef603..6b4254b 100644 --- a/tests/common/errorpanic.rs +++ b/tests/common/errorpanic.rs @@ -3,11 +3,12 @@ // Licensed under the MIT license. // Documentation and comments licensed under CC BY-SA 4.0. -use ::iosonism::strcursor::ReadError; +use ::iosonism::error::RangeError; +use ::iosonism::error::ReadError; use ::iosonism::strcursor::StringReader; /// An implementation of various Iosonism errors that just panics. -#[derive(Debug)] +#[derive(Debug, PartialEq, Eq, PartialOrd, Ord, Hash)] pub enum ErrorPanic { // uninhabitable! } @@ -22,6 +23,22 @@ impl ::std::fmt::Display for ErrorPanic { impl ::std::error::Error for ErrorPanic { } +impl<'a, C, T, R> RangeError<'a, C, T, R> for ErrorPanic +where C: StringReader<'a>, T: ::std::fmt::Display, R: ::std::fmt::Debug { + fn value_not_in_range(context: &C, from: &T, range: &R) -> Self { + if !context.get_remaining().is_empty() { + panic!( + "value ({}) not in range: {:?} at ...{}", + from, + range, + context.get_remaining(), + ); + } else { + panic!("value ({}) not in range: {:?}", from, range); + } + } +} + impl<'a, C: StringReader<'a>> ReadError<'a, C> for ErrorPanic { fn invalid_integer(context: &C, from: &str) -> Self { if !context.get_remaining().is_empty() { |