Source code

Revision control

Copy as Markdown

Other Tools

//! Implementations of the [`quickcheck::Arbitrary`](quickcheck::Arbitrary) trait.
//!
//! This enables users to write tests such as this, and have test values provided automatically:
//!
//! ```
//! # #![allow(dead_code)]
//! use quickcheck::quickcheck;
//! use time::Date;
//!
//! struct DateRange {
//! from: Date,
//! to: Date,
//! }
//!
//! impl DateRange {
//! fn new(from: Date, to: Date) -> Result<Self, ()> {
//! Ok(DateRange { from, to })
//! }
//! }
//!
//! quickcheck! {
//! fn date_range_is_well_defined(from: Date, to: Date) -> bool {
//! let r = DateRange::new(from, to);
//! if from <= to {
//! r.is_ok()
//! } else {
//! r.is_err()
//! }
//! }
//! }
//! ```
//!
//! An implementation for `Instant` is intentionally omitted since its values are only meaningful in
//! relation to a [`Duration`], and obtaining an `Instant` from a [`Duration`] is very simple
//! anyway.
use alloc::boxed::Box;
use quickcheck::{empty_shrinker, single_shrinker, Arbitrary, Gen};
use crate::convert::*;
use crate::date_time::{DateTime, MaybeOffset};
use crate::{Date, Duration, Month, OffsetDateTime, PrimitiveDateTime, Time, UtcOffset, Weekday};
/// Obtain an arbitrary value between the minimum and maximum inclusive.
macro_rules! arbitrary_between {
($type:ty; $gen:expr, $min:expr, $max:expr) => {{
let min = $min;
let max = $max;
let range = max - min;
<$type>::arbitrary($gen).rem_euclid(range + 1) + min
}};
}
impl Arbitrary for Date {
fn arbitrary(g: &mut Gen) -> Self {
Self::from_julian_day_unchecked(arbitrary_between!(
i32;
g,
Self::MIN.to_julian_day(),
Self::MAX.to_julian_day()
))
}
fn shrink(&self) -> Box<dyn Iterator<Item = Self>> {
Box::new(
self.to_ordinal_date()
.shrink()
.flat_map(|(year, ordinal)| Self::from_ordinal_date(year, ordinal)),
)
}
}
impl Arbitrary for Duration {
fn arbitrary(g: &mut Gen) -> Self {
Self::nanoseconds_i128(arbitrary_between!(
i128;
g,
Self::MIN.whole_nanoseconds(),
Self::MAX.whole_nanoseconds()
))
}
fn shrink(&self) -> Box<dyn Iterator<Item = Self>> {
Box::new(
(self.subsec_nanoseconds(), self.whole_seconds())
.shrink()
.map(|(mut nanoseconds, seconds)| {
// Coerce the sign if necessary.
if (seconds > 0 && nanoseconds < 0) || (seconds < 0 && nanoseconds > 0) {
nanoseconds *= -1;
}
Self::new_unchecked(seconds, nanoseconds)
}),
)
}
}
impl Arbitrary for Time {
fn arbitrary(g: &mut Gen) -> Self {
Self::__from_hms_nanos_unchecked(
arbitrary_between!(u8; g, 0, Hour.per(Day) - 1),
arbitrary_between!(u8; g, 0, Minute.per(Hour) - 1),
arbitrary_between!(u8; g, 0, Second.per(Minute) - 1),
arbitrary_between!(u32; g, 0, Nanosecond.per(Second) - 1),
)
}
fn shrink(&self) -> Box<dyn Iterator<Item = Self>> {
Box::new(
self.as_hms_nano()
.shrink()
.map(|(hour, minute, second, nanosecond)| {
Self::__from_hms_nanos_unchecked(hour, minute, second, nanosecond)
}),
)
}
}
impl Arbitrary for PrimitiveDateTime {
fn arbitrary(g: &mut Gen) -> Self {
Self(<_>::arbitrary(g))
}
fn shrink(&self) -> Box<dyn Iterator<Item = Self>> {
Box::new(self.0.shrink().map(Self))
}
}
impl Arbitrary for UtcOffset {
fn arbitrary(g: &mut Gen) -> Self {
let seconds =
arbitrary_between!(i32; g, -(Second.per(Day) as i32 - 1), Second.per(Day) as i32 - 1);
Self::__from_hms_unchecked(
(seconds / Second.per(Hour) as i32) as _,
((seconds % Second.per(Hour) as i32) / Minute.per(Hour) as i32) as _,
(seconds % Second.per(Minute) as i32) as _,
)
}
fn shrink(&self) -> Box<dyn Iterator<Item = Self>> {
Box::new(
self.as_hms().shrink().map(|(hours, minutes, seconds)| {
Self::__from_hms_unchecked(hours, minutes, seconds)
}),
)
}
}
impl Arbitrary for OffsetDateTime {
fn arbitrary(g: &mut Gen) -> Self {
Self(<_>::arbitrary(g))
}
fn shrink(&self) -> Box<dyn Iterator<Item = Self>> {
Box::new(self.0.shrink().map(Self))
}
}
impl<O: MaybeOffset + 'static> Arbitrary for DateTime<O> {
fn arbitrary(g: &mut Gen) -> Self {
Self {
date: <_>::arbitrary(g),
time: <_>::arbitrary(g),
offset: <_>::arbitrary(g),
}
}
fn shrink(&self) -> Box<dyn Iterator<Item = Self>> {
Box::new(
(self.date, self.time, self.offset)
.shrink()
.map(|(date, time, offset)| Self { date, time, offset }),
)
}
}
impl Arbitrary for Weekday {
fn arbitrary(g: &mut Gen) -> Self {
use Weekday::*;
match arbitrary_between!(u8; g, 0, 6) {
0 => Monday,
1 => Tuesday,
2 => Wednesday,
3 => Thursday,
4 => Friday,
5 => Saturday,
val => {
debug_assert!(val == 6);
Sunday
}
}
}
fn shrink(&self) -> Box<dyn Iterator<Item = Self>> {
match self {
Self::Monday => empty_shrinker(),
_ => single_shrinker(self.previous()),
}
}
}
impl Arbitrary for Month {
fn arbitrary(g: &mut Gen) -> Self {
use Month::*;
match arbitrary_between!(u8; g, 1, 12) {
1 => January,
2 => February,
3 => March,
4 => April,
5 => May,
6 => June,
7 => July,
8 => August,
9 => September,
10 => October,
11 => November,
val => {
debug_assert!(val == 12);
December
}
}
}
fn shrink(&self) -> Box<dyn Iterator<Item = Self>> {
match self {
Self::January => empty_shrinker(),
_ => single_shrinker(self.previous()),
}
}
}