1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144 145 146 147 148 149 150 151 152 153 154 155 156 157 158 159 160 161 162 163 164 165 166 167 168 169 170 171 172 173 174 175 176 177 178 179 180 181 182 183 184 185 186 187 188 189 190 191 192 193 194 195 196 197 198 199 200 201 202 203 204 205 206 207 208 209 210 211 212 213 214 215 216 217 218 219 220 221 222 223 224 225 226 227 228 229 230 231 232 233 234 235 236 237 238 239 240 241 242 243 244 245 246 247 248 249 250 251 252 253 254 255 256 257 258 259 260 261 262 263 264 265 266 267 268 269 270 271 272 273 274 275 276 277 278 279 280 281 282 283 284 285 286 287 288 289 290 291 292 293 294 295 296 297 298 299 300 301 302 303 304 305 306 307 308 309 310 311 312 313 314 315 316 317 318 319 320 321 322 323 324 325 326 327 328 329 330 331 332 333 334 335 336 337 338 339 340 341 342 343 344 345 346 347 348 349 350 351 352 353 354 355 356 357 358 359 360 361 362 363 364 365 366 367 368
//- // Copyright 2017 Mazdak Farrokhzad // // Licensed under the Apache License, Version 2.0 <LICENSE-APACHE or // http://www.apache.org/licenses/LICENSE-2.0> or the MIT license // <LICENSE-MIT or http://opensource.org/licenses/MIT>, at your // option. This file may not be copied, modified, or distributed // except according to those terms. //! [`proptest`] is a property testing framework (i.e., the [`QuickCheck`] family) //! inspired by the [Hypothesis](http://hypothesis.works/) framework for //! Python. //! //! [`quickcheck`] is another property testing framework for Rust which uses //! a shrinking logic similar to Haskell's [`QuickCheck`]. //! //! **This crate provides interoperability between quickcheck and proptest.** //! Currently, this is one way - if you've implemented quickcheck's //! [`Arbitrary`] trait as well as [`Debug`] (a temporary requirement) //! then you may get back the equivalent [`Strategy`] in proptest. //! //! ## Status of this crate //! //! This crate is unlikely to see major changes. Any breaking changes //! are only likely to come as a result of changes in the dependencies used by //! this crate, in particular proptest or quickcheck. Since neither of those //! crates have reached `1.0.0` neither has this crate. Once that happen, this //! crate will go `1.0.0` as well. //! //! See the [changelog] for a full list of substantial historical changes, //! breaking and otherwise. //! //! ## Using the interoperability layer //! //! Assuming that you already have a `Cargo.toml` file in your project with, //! among other things, the following: //! //! ```toml //! [dependencies] //! //! quickcheck = "0.5.0" //! proptest = "0.3.2" //! ``` //! //! Now add this crate to your dependencies: //! //! ```toml //! [dependencies] //! //! quickcheck = "0.5.0" //! proptest = "0.3.2" //! proptest_quickcheck_interop = "0.1.0" //! ``` //! //! Let's now assume that `usize` is a complex type for which you have //! implemented `quickcheck::Arbitrary`. You wish you reuse this in proptest //! or if you simply prefer the implementation provided by quickcheck. //! To do so, you can use [`from_qc`]: //! //! ```rust //! // Import crates: //! #[macro_use] extern crate proptest; //! extern crate proptest_quickcheck_interop as pqci; //! //! // And what we need into our scope: //! use proptest::strategy::Strategy; //! use pqci::from_qc; //! //! /// Given a usize returns the nearest usize that is also even. //! fn make_even(x: usize) -> usize { //! if x % 2 == 1 { x - 1 } else { x } //! } //! //! proptest! { //! /// A property asserting that make_even always produces an even usize. //! fn always_even(ref x in from_qc::<usize>().prop_map(make_even)) { //! prop_assert!(x % 2 == 0); //! } //! } //! //! fn main() { //! always_even(); //! } //! ``` //! //! If you want to control the `size` of the input generated by quickcheck //! you may instead use [`from_qc_sized(size)`][`from_qc_sized`]. If you use, //! [`from_qc`], then the default size used by quickcheck is used. //! //! [`from_qc`]: https://docs.rs/proptest/0.1.0/proptest-quickcheck-interop/fn.from_qc.html //! [`from_qc_sized`]: https://docs.rs/proptest/0.1.0/proptest-quickcheck-interop/fn.from_qc_sized.html //! //! [changelog]: //! https://github.com/Centril/proptest-quickcheck-interop/blob/master/CHANGELOG.md //! //! [`Debug`]: https://doc.rust-lang.org/nightly/std/fmt/trait.Debug.html //! //! [`Arbitrary`]: https://docs.rs/quickcheck/0.5.0/quickcheck/trait.Arbitrary.html //! //! [`proptest`]: https://crates.io/crates/proptest //! //! [`quickcheck`]: https://crates.io/crates/quickcheck //! //! [`Strategy`]: https://docs.rs/proptest/0.3.2/proptest/strategy/trait.Strategy.html #![deny(missing_docs)] //============================================================================== // Imports: //============================================================================== extern crate rand; extern crate proptest; extern crate quickcheck as quickcheck_crate; use std::mem; use std::marker::PhantomData; use std::fmt::{Debug, Formatter, Result as FResult}; use quickcheck_crate::{Arbitrary, Gen, Rng}; use rand::XorShiftRng; use proptest::strategy::*; use proptest::test_runner::TestRunner; //============================================================================== // Convenience API: //============================================================================== /// Constructs a new [`Strategy`] for any type that implements [`quickcheck`]'s /// [`Arbitrary`] trait as well as [`Debug`] (a temporary requirement). /// You may use this to gain interoperability by reusing implementations of /// [`Arbitrary`] that you've already defined for some type. /// /// Using this version, the size parameter controlling the size of inputs will /// be the default one used by `quickcheck`. This parameter is passed to the /// implementation of `Arbitrary`. If you want to provide some other value, /// you may instead use [`from_qc_sized`]. /// /// [`Arbitrary`]: https://docs.rs/quickcheck/0.5.0/quickcheck/trait.Arbitrary.html /// [`from_qc_sized`]: function.from_qc_sized /// [`quickcheck`]: https://crates.io/crates/quickcheck /// [`Strategy`]: https://docs.rs/proptest/0.3.2/proptest/strategy/trait.Strategy.html pub fn from_qc<A: Arbitrary + Debug>() -> QCStrategy<A> { QCStrategy::new(qc_gen_size()) } /// Copied from: /// https://docs.rs/quickcheck/0.5.0/src/quickcheck/tester.rs.html#35-41 /// TODO: Remove if @burntsushi makes this pub. fn qc_gen_size() -> usize { use std::env; let default = 100; match env::var("QUICKCHECK_GENERATOR_SIZE") { Ok(val) => val.parse().unwrap_or(default), Err(_) => default, } } /// Constructs a new [`Strategy`] for any type that implements [`quickcheck`]'s /// [`Arbitrary`] trait as well as [`Debug`] (a temporary requirement). /// You may use this to gain interoperability by reusing implementations of /// [`Arbitrary`] that you've already defined for some type. /// /// Using this version, you may provide a size parameter controlling the size /// of inputs. This parameter is passed to the implementation of `Arbitrary`. /// /// [`Arbitrary`]: https://docs.rs/quickcheck/0.5.0/quickcheck/trait.Arbitrary.html /// [`quickcheck`]: https://crates.io/crates/quickcheck /// [`Strategy`]: https://docs.rs/proptest/0.3.2/proptest/strategy/trait.Strategy.html pub fn from_qc_sized<A: Arbitrary + Debug>(size: usize) -> QCStrategy<A> { QCStrategy::new(size) } //============================================================================== // The strategy //============================================================================== /// `QCStrategy` is a [`Strategy`] that provides interoperability with /// [`quickcheck`]'s [`Arbitrary`] trait. If you have any type implementing /// [`Arbitrary`] and [`Debug`] (a temporary requirement) then you may get /// back the equivalent Strategy in proptest. /// /// [`Debug`]: https://doc.rust-lang.org/nightly/std/fmt/trait.Debug.html /// [`Arbitrary`]: https://docs.rs/quickcheck/0.5.0/quickcheck/trait.Arbitrary.html /// [`quickcheck`]: https://crates.io/crates/quickcheck /// [`Strategy`]: https://docs.rs/proptest/0.3.2/proptest/strategy/trait.Strategy.html #[derive(Copy, Clone, PartialEq, Eq, PartialOrd, Ord, Hash, Debug)] pub struct QCStrategy<A: Arbitrary + Debug> { __ph: PhantomData<A>, size: usize, } /// The [`ValueTree`] implementation for [`QCStrategy`]. /// /// [`QCStrategy`]: struct.QCStrategy /// [`ValueTree`]: https://docs.rs/proptest/0.3.2/proptest/strategy/trait.ValueTree.html pub struct QCValueTree<A: Arbitrary + Debug> { curr: A, prev: Option<A>, shrinker: Box<Iterator<Item = A>> } impl<A: Arbitrary + Debug> QCStrategy<A> { /// Constructs a new `QCStrategy` given a `size` parameter that: /// > controls the size of random values generated. For example, it /// > specifies the maximum length of a randomly generated vector and also /// > will specify the maximum magnitude of a randomly generated number. /// as defined by [`quickcheck`] /// /// [`quickcheck`]: https://crates.io/crates/quickcheck pub fn new(size: usize) -> Self { Self { __ph: PhantomData, size } } } impl<A: Arbitrary + Debug> QCValueTree<A> { /// Constructs a new `QCValueTree`, that is the [`ValueTree`] for /// [`QCStrategy`] given: /// + the current value, /// + the iterator producing increasingly shrunken versions of the current /// value given. /// /// [`QCStrategy`]: struct.QCStrategy /// [`ValueTree`]: trait.ValueTree fn new(curr: A, shrinker: Box<Iterator<Item = A>>) -> Self { Self { prev: None, curr, shrinker } } } type NewTree<S> = Result<<S as Strategy>::Value, String>; impl<A: Arbitrary + Debug> Strategy for QCStrategy<A> { type Value = QCValueTree<A>; fn new_value(&self, runner: &mut TestRunner) -> NewTree<Self> { let mut gen = XorShiftGen::new(runner.rng(), self.size); let curr = A::arbitrary(&mut gen); let shrinker = curr.shrink(); Ok(QCValueTree::new(curr, shrinker)) } } impl<A: Arbitrary + Debug> ValueTree for QCValueTree<A> { type Value = A; fn current(&self) -> Self::Value { self.curr.clone() } fn complicate(&mut self) -> bool { // We can only complicate if we previously simplified. // Complicating twice in a row without interleaved simplification // is guaranteed to always yield false for the second call. if let Some(prev) = self.prev.take() { // Throw away the current value! self.curr = prev; true } else { false } } fn simplify(&mut self) -> bool { if let Some(simpler) = self.shrinker.next() { // Throw away the previous value and set the current value as prev. // Advance the iterator and set the current value to the next one. self.prev = Some(mem::replace(&mut self.curr, simpler)); true } else { // Successive calls to .next() are now assumed to yield None, // Can't shrink anymore. false } } } impl<A: Arbitrary + Debug> Debug for QCValueTree<A> { fn fmt(&self, fmt: &mut Formatter) -> FResult { fmt.debug_struct("QCValueTree") .field("curr", &self.curr) .field("prev", &self.prev) // We could change shrinker to be : Vec<A> instead, but that seems // not worth the cost of maintaining all the shrinked-to elements // in the ValueTree. If the iterator is side-effecting the behaviour // may also be quite strange. .field("shrinker", &"<iterator>") .finish() } } //============================================================================== // XorShiftGen wrapper //============================================================================== /// A wrapper around a mutable reference of [`XorShiftRng`] and a `size` that /// controls the size of random values generated makes for an implementation /// of a [`Gen`]. /// /// [`XorShiftRng`]: https://docs.rs/rand/0.3.18/rand/struct.XorShiftRng.html /// [`Gen`]: https://docs.rs/quickcheck/0.5.0/quickcheck/trait.Gen.html #[derive(Debug)] struct XorShiftGen<'a> { rng: &'a mut XorShiftRng, size: usize, } impl<'a> XorShiftGen<'a> { /// Construct a new generator given the backing RNG and the /// size controlling the size of random values generated. pub fn new(rng: &'a mut XorShiftRng, size: usize) -> Self { Self { rng, size } } } impl<'a> Rng for XorShiftGen<'a> { fn next_u32(&mut self) -> u32 { self.rng.next_u32() } } impl<'a> Gen for XorShiftGen<'a> { fn size(&self) -> usize { self.size } } //============================================================================== // Tests //============================================================================== impl Clone for QCValueTree<Vec<u32>> { /// Only implemented so that the implementation can be tested. Do **not** /// consider this a public API, it may be removed at any time. fn clone(&self) -> Self { // We assume that: // // let x = A::arbitrary(gen); // let s1 = x.clone().shrink(); // let s2 = x.shrink(); // assert_eq(s1.collect::<Vec<A>>(), s2.collect::<Vec<A>>()); // // always holds. In other words, that shrinking is deterministic. // We also assume that: // // A::arbitrary(gen); // let s = x.shrink(); // let y = x.next()?; // assert_eq!(x.next(), y.shrink().next()); // // always holds. // // It may not actually hold for all A, but we don't care, as we only // provide Clone for testing purposes and we pick A = u32 for which // it always holds. QCValueTree { prev: self.prev.clone(), curr: self.curr.clone(), shrinker: self.curr.shrink() } } } #[cfg(test)] mod tests { use super::*; #[test] fn contract_followed_by_qc_strategy() { check_strategy_sanity(from_qc_sized::<Vec<u32>>(10), None); } }