Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Zero Examples of impl Arbitrary ? #445

Open
bionicles opened this issue Apr 9, 2024 · 3 comments
Open

Zero Examples of impl Arbitrary ? #445

bionicles opened this issue Apr 9, 2024 · 3 comments
Labels
documentation issues around documentation (doc comments, book, best-practices, etc)

Comments

@bionicles
Copy link

bionicles commented Apr 9, 2024

Hey, I admit I'm a noob, but i just wanted to make a simple toy example of proptest but it looks like everything is either macros or derive, functions accepting ranges as arguments, what's the simple straightforward way to create a BoxedStrategy from a prop_map? Should i be using Arbitrary from the arbitrary crate instead of from the proptest crate?

use chrono::TimeZone;
use proptest::prelude::*;
use proptest::strategy::{BoxedStrategy, Just, Strategy};
// suppose this is a separate crate and you can't implement our traits on it
// how do you add a new plugin custom datatype easily
mod coordinate_library {
    use chrono::{DateTime, FixedOffset};
    use uom::si::angle::degree;
    use uom::si::f64::{Angle, Length};
    use uom::si::length::meter;

    #[derive(Debug)]
    pub struct SpaceTimeCoordinate {
        pub latitude: Angle,
        pub longitude: Angle,
        pub elevation: Length,
        pub datetime: DateTime<FixedOffset>,
    }

    impl SpaceTimeCoordinate {
        pub fn new(lat: f64, lng: f64, elev: f64, dt: DateTime<FixedOffset>) -> Self {
            SpaceTimeCoordinate {
                latitude: Angle::new::<degree>(lat),
                longitude: Angle::new::<degree>(lng),
                elevation: Length::new::<meter>(elev),
                datetime: dt,
            }
        }
    }
}

use chrono::{DateTime, FixedOffset, Utc};
// Import the external library type.
use coordinate_library::SpaceTimeCoordinate;

use uom::si::angle::degree;
use uom::si::f64::{Angle, Length};
use uom::si::length::meter;

// compile failure due to orphan rules prevents direct implementation
// impl CustomDType for SpaceTimeCoordinate {} // error

#[derive(Debug)]
// Define a newtype wrapper for the external library type.
pub struct Coordinate(pub SpaceTimeCoordinate);

impl Arbitrary for Coordinate {
    type Parameters = ();
    type Strategy = BoxedStrategy<Self>;

    fn arbitrary_with(_args: Self::Parameters) -> Self::Strategy {
        let timestamp = any::<i64>().prop_map(|seconds| {
            let tz = FixedOffset::east_opt(0).unwrap();
            tz.timestamp_opt(seconds, 0).unwrap()
        });
        let latitude = any::<f64>().prop_map(|value| Angle::new::<degree>(value));
        let longitude = any::<f64>().prop_map(|value| Angle::new::<degree>(value));
        let elevation = any::<f64>().prop_map(|value| Length::new::<meter>(value));

        (latitude, longitude, elevation, timestamp)
            .prop_map(|(latitude, longitude, elevation, datetime)| {
                Box::new(Coordinate(SpaceTimeCoordinate {
                    latitude,
                    longitude,
                    elevation,
                    datetime,
                }))
            })
            .into_boxed_strategy()
    }
}

you could improve the docs a ton by putting complete example of how to impl Arbitrary somewhere, even the reference docs for the trait itself do not have a code block example of how to implement that, and it seems pretty specific about how we ought to code this, anyway, thanks for all the fuzz testing, i feel dumb right now

@bionicles
Copy link
Author

bionicles commented Apr 9, 2024

(tried without the box, tried Ok(box), etc etc) , edited to add some context but tried not to put too much. Anyway, just pestering about docs / examples, user error, carry on

@bionicles
Copy link
Author

bionicles commented Apr 10, 2024

Strategy::boxed worked, here is an example impl proptest::Arbitrary which compiles and works

// In your library's module (e.g., mod.rs)
// suppose you want to enable a new data type in your sql tables, like GIS / coordinates
use chrono::{DateTime, FixedOffset, TimeZone};
use serde::{Deserialize, Deserializer, Serialize, Serializer};
use uom::si::angle::degree;
use uom::si::f64::{Angle, Length};
use uom::si::length::meter;

// suppose this is a separate crate and you can't implement our traits on it
// how do you add a new plugin custom datatype easily
mod coordinate_library {
    use chrono::{DateTime, FixedOffset};
    use uom::si::angle::degree;
    use uom::si::f64::{Angle, Length};
    use uom::si::length::meter;

    #[derive(Debug)]
    pub struct SpaceTimeCoordinate {
        pub latitude: Angle,
        pub longitude: Angle,
        pub elevation: Length,
        pub datetime: DateTime<FixedOffset>,
    }

    impl SpaceTimeCoordinate {
        pub fn new(lat: f64, lng: f64, elev: f64, dt: DateTime<FixedOffset>) -> Self {
            SpaceTimeCoordinate {
                latitude: Angle::new::<degree>(lat),
                longitude: Angle::new::<degree>(lng),
                elevation: Length::new::<meter>(elev),
                datetime: dt,
            }
        }
    }
}

// Import the external library type.
use coordinate_library::SpaceTimeCoordinate;

// compile failure due to orphan rules prevents direct implementation
// impl CustomDType for SpaceTimeCoordinate {} // error

#[derive(Debug)]
// Define a newtype wrapper for the external library type.
pub struct Coordinate(pub SpaceTimeCoordinate);

use proptest::prelude::*;
use proptest::test_runner::TestRunner;
// use proptest::strategy::{BoxedStrategy, Just, Strategy}; // redundant with "prelude"

const HOUR: i32 = 3600;

impl Arbitrary for Coordinate {
    type Parameters = ();
    type Strategy = BoxedStrategy<Self>;

    fn arbitrary_with(_args: Self::Parameters) -> Self::Strategy {
        let timestamp = any::<i32>().prop_map(|seconds| {
            let seconds_i64 = seconds as i64;
            let tz = FixedOffset::east_opt(5 * HOUR).unwrap();
            println!("seconds={seconds:#?}");
            let dt: DateTime<FixedOffset> = tz.timestamp_opt(seconds_i64, 0).unwrap();
            dt
        });
        let latitude = any::<f32>().prop_map(|x| Angle::new::<degree>(x as f64));
        let longitude = any::<f32>().prop_map(|x| Angle::new::<degree>(x as f64));
        let elevation = any::<f32>().prop_map(|x| Length::new::<meter>(x as f64));

        let pipeline = (latitude, longitude, elevation, timestamp).prop_map(
            |(latitude, longitude, elevation, datetime)| {
                Coordinate(SpaceTimeCoordinate {
                    latitude,
                    longitude,
                    elevation,
                    datetime,
                })
            },
        );
        Strategy::boxed(pipeline)
    }
}

const MAX_LOOPS: usize = 12;
fn main() {
    let config = Default::default();
    let mut runner = TestRunner::new(config);
    let strat = Coordinate::arbitrary_with(());
    for iteration in 0..MAX_LOOPS {
        match strat.new_tree(&mut runner) {
            Ok(tree) => {
                let value = tree.current();
                println!("{iteration:#?} {value:#?}")
            }
            Err(reason) => {
                eprintln!("{reason:#?}")
            }
        }
    }
}

image

gonna keep this open since it's a docs issue
edited to use f32 for more satisfying diversity of timestamps and less huge angles

@matthew-russo matthew-russo added the documentation issues around documentation (doc comments, book, best-practices, etc) label Apr 10, 2024
@matthew-russo
Copy link
Member

Hi sorry for the delay -- glad you ended up figuring it out. Will see what we can do to improve the docs/guides

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
documentation issues around documentation (doc comments, book, best-practices, etc)
Projects
None yet
Development

No branches or pull requests

2 participants