Giter Site home page Giter Site logo

quickcheck's Introduction

quickcheck

QuickCheck is a way to do property based testing using randomly generated input. This crate comes with the ability to randomly generate and shrink integers, floats, tuples, booleans, lists, strings, options and results. All QuickCheck needs is a property functionโ€”it will then randomly generate inputs to that function and call the property for each set of inputs. If the property fails (whether by a runtime error like index out-of-bounds or by not satisfying your property), the inputs are "shrunk" to find a smaller counter-example.

The shrinking strategies for lists and numbers use a binary search to cover the input space quickly. (It should be the same strategy used in Koen Claessen's QuickCheck for Haskell.)

Build status

Dual-licensed under MIT or the UNLICENSE.

Documentation

The API is fully documented: https://docs.rs/quickcheck.

Simple example

Here's an example that tests a function that reverses a vector:

#[cfg(test)]
#[macro_use]
extern crate quickcheck;

fn reverse<T: Clone>(xs: &[T]) -> Vec<T> {
    let mut rev = vec!();
    for x in xs.iter() {
        rev.insert(0, x.clone())
    }
    rev
}

#[cfg(test)]
mod tests {
  quickcheck! {
      fn prop(xs: Vec<u32>) -> bool {
          xs == reverse(&reverse(&xs))
      }
  }
}

This example uses the quickcheck! macro, which is backwards compatible with old versions of Rust.

The #[quickcheck] attribute

To make it easier to write QuickCheck tests, the #[quickcheck] attribute will convert a property function into a #[test] function.

To use the #[quickcheck] attribute, you must import the quickcheck macro from the quickcheck_macros crate:

#[cfg(test)]
extern crate quickcheck;
#[cfg(test)]
#[macro_use(quickcheck)]
extern crate quickcheck_macros;

#[cfg(test)]
mod tests {
    fn reverse<T: Clone>(xs: &[T]) -> Vec<T> {
        let mut rev = vec!();
        for x in xs {
            rev.insert(0, x.clone())
        }
        rev
    }

    #[quickcheck]
    fn double_reversal_is_identity(xs: Vec<isize>) -> bool {
        xs == reverse(&reverse(&xs))
    }
}

Installation

quickcheck is on crates.io, so you can include it in your project like so:

[dependencies]
quickcheck = "1"

If you're only using quickcheck in your test code, then you can add it as a development dependency instead:

[dev-dependencies]
quickcheck = "1"

If you want to use the #[quickcheck] attribute, then add quickcheck_macros

[dev-dependencies]
quickcheck = "1"
quickcheck_macros = "1"

N.B. When using quickcheck (either directly or via the attributes), RUST_LOG=quickcheck enables info! so that it shows useful output (like the number of tests passed). This is not needed to show witnesses for failures.

Crate features:

  • "use_logging": (Enabled by default.) Enables the log messages governed RUST_LOG.
  • "regex": (Enabled by default.) Enables the use of regexes with env_logger.

Minimum Rust version policy

This crate's minimum supported rustc version is 1.65.0.

The current policy is that the minimum Rust version required to use this crate can be increased in minor version updates. For example, if crate 1.0 requires Rust 1.20.0, then crate 1.0.z for all values of z will also require Rust 1.20.0 or newer. However, crate 1.y for y > 0 may require a newer minimum version of Rust.

In general, this crate will be conservative with respect to the minimum supported version of Rust.

With all of that said, currently, rand is a public dependency of quickcheck. Therefore, the MSRV policy above only applies when it is more aggressive than rand's MSRV policy. Otherwise, quickcheck will defer to rand's MSRV policy.

Compatibility

In general, this crate considers the Arbitrary implementations provided as implementation details. Strategies may or may not change over time, which may cause new test failures, presumably due to the discovery of new bugs due to a new kind of witness being generated. These sorts of changes may happen in semver compatible releases.

Alternative Rust crates for property testing

The proptest crate is inspired by the Hypothesis framework for Python. You can read a comparison between proptest and quickcheck here and here. In particular, proptest improves on the concept of shrinking. So if you've ever had problems/frustration with shrinking in quickcheck, then proptest might be worth a try!

Alternatives for fuzzing

Please see the Rust Fuzz Book and the arbitrary crate.

Discarding test results (or, properties are polymorphic!)

Sometimes you want to test a property that only holds for a subset of the possible inputs, so that when your property is given an input that is outside of that subset, you'd discard it. In particular, the property should neither pass nor fail on inputs outside of the subset you want to test. But properties return boolean valuesโ€”which either indicate pass or fail.

To fix this, we need to take a step back and look at the type of the quickcheck function:

pub fn quickcheck<A: Testable>(f: A) {
    // elided
}

So quickcheck can test any value with a type that satisfies the Testable trait. Great, so what is this Testable business?

pub trait Testable {
    fn result(&self, &mut Gen) -> TestResult;
}

This trait states that a type is testable if it can produce a TestResult given a source of randomness. (A TestResult stores information about the results of a test, like whether it passed, failed or has been discarded.)

Sure enough, bool satisfies the Testable trait:

impl Testable for bool {
    fn result(&self, _: &mut Gen) -> TestResult {
        TestResult::from_bool(*self)
    }
}

But in the example, we gave a function to quickcheck. Yes, functions can satisfy Testable too!

impl<A: Arbitrary + Debug, B: Testable> Testable for fn(A) -> B {
    fn result(&self, g: &mut Gen) -> TestResult {
        // elided
    }
}

Which says that a function satisfies Testable if and only if it has a single parameter type (whose values can be randomly generated and shrunk) and returns any type (that also satisfies Testable). So a function with type fn(usize) -> bool satisfies Testable since usize satisfies Arbitrary and bool satisfies Testable.

So to discard a test, we need to return something other than bool. What if we just returned a TestResult directly? That should work, but we'll need to make sure TestResult satisfies Testable:

impl Testable for TestResult {
    fn result(&self, _: &mut Gen) -> TestResult { self.clone() }
}

Now we can test functions that return a TestResult directly.

As an example, let's test our reverse function to make sure that the reverse of a vector of length 1 is equal to the vector itself.

fn prop(xs: Vec<isize>) -> TestResult {
    if xs.len() != 1 {
        return TestResult::discard()
    }
    TestResult::from_bool(xs == reverse(&xs))
}
quickcheck(prop as fn(Vec<isize>) -> TestResult);

(A full working program for this example is in examples/reverse_single.rs.)

So now our property returns a TestResult, which allows us to encode a bit more information. There are a few more convenience functions defined for the TestResult type. For example, we can't just return a bool, so we convert a bool value to a TestResult.

(The ability to discard tests allows you to get similar functionality as Haskell's ==> combinator.)

N.B. Since discarding a test means it neither passes nor fails, quickcheck will try to replace the discarded test with a fresh one. However, if your condition is seldom met, it's possible that quickcheck will have to settle for running fewer tests than usual. By default, if quickcheck can't find 100 valid tests after trying 10,000 times, then it will give up. These parameters may be changed using QuickCheck::tests and QuickCheck::max_tests, or by setting the QUICKCHECK_TESTS and QUICKCHECK_MAX_TESTS environment variables. There is also QUICKCHECK_MIN_TESTS_PASSED which sets the minimum number of valid tests that need pass (defaults to 0) in order for it to be considered a success.

Shrinking

Shrinking is a crucial part of QuickCheck that simplifies counter-examples for your properties automatically. For example, if you erroneously defined a function for reversing vectors as: (my apologies for the contrived example)

fn reverse<T: Clone>(xs: &[T]) -> Vec<T> {
    let mut rev = vec![];
    for i in 1..xs.len() {
        rev.insert(0, xs[i].clone())
    }
    rev
}

And a property to test that xs == reverse(reverse(xs)):

fn prop(xs: Vec<isize>) -> bool {
    xs == reverse(&reverse(&xs))
}
quickcheck(prop as fn(Vec<isize>) -> bool);

Then without shrinking, you might get a counter-example like:

[quickcheck] TEST FAILED. Arguments: ([-17, 13, -12, 17, -8, -10, 15, -19,
-19, -9, 11, -5, 1, 19, -16, 6])

Which is pretty mysterious. But with shrinking enabled, you're nearly guaranteed to get this counter-example every time:

[quickcheck] TEST FAILED. Arguments: ([0])

Which is going to be much easier to debug.

More Thorough Checking

Quickcheck uses random input to test, so it won't always find bugs that could be uncovered with a particular property. You can improve your odds of finding these latent bugs by spending more CPU cycles asking quickcheck to find them for you. There are a few different ways to do this, and which one you choose is mostly a matter of taste.

If you are finding yourself doing this sort of thing a lot, you might also be interested in trying out cargo fuzz, which runs in a loop by default.

Running in a Loop

One approach is to run your quickcheck properties in a loop that just keeps going until you tell it to stop or it finds a bug. For example, you could use a bash script such as the following one.

#!/usr/bin/bash

while true
do
    cargo test qc_
    if [[ x$? != x0 ]] ; then
        exit $?
    fi
done

One thing to note is that this script passes the qc_ filter to cargo test. This assumes that you've prefixed all your quickcheck properties with qc_. You could leave off the filter, but then you would be running all your deterministic tests as well, which would take time away from quickcheck!

Checking the return code and exiting is also important. Without that test, you won't ever notice when a failure happens.

Cranking the Number of Tests

Another approach is to just ask quickcheck to run properties more times. You can do this either via the tests() method, or via the QUICKCHECK_TESTS environment variable. This will cause quickcheck to run for a much longer time. Unlike, the loop approach this will take a bounded amount of time, which makes it more suitable for something like a release cycle that wants to really hammer your software.

Making Arbitrary Smarter

This approach entails spending more time generating interesting inputs in your implementations of Arbitrary. The idea is to focus on the corner cases. This approach can be tricky because programmers are not usually great at intuiting corner cases, and the whole idea of property checking is to take that burden off the programmer. Despite the theoretical discomfort, this approach can turn out to be practical.

Generating Structs

It is very simple to generate structs in QuickCheck. Consider the following example, where the struct Point is defined:

struct Point {
    x: i32,
    y: i32,
}

In order to generate a random Point instance, you need to implement the trait Arbitrary for the struct Point:

use quickcheck::{Arbitrary, Gen};

impl Arbitrary for Point {
    fn arbitrary(g: &mut Gen) -> Point {
        Point {
            x: i32::arbitrary(g),
            y: i32::arbitrary(g),
        }
    }
}

Case study: The Sieve of Eratosthenes

The Sieve of Eratosthenes is a simple and elegant way to find all primes less than or equal to N. Briefly, the algorithm works by allocating an array with N slots containing booleans. Slots marked with false correspond to prime numbers (or numbers not known to be prime while building the sieve) and slots marked with true are known to not be prime. For each n, all of its multiples in this array are marked as true. When all n have been checked, the numbers marked false are returned as the primes.

As you might imagine, there's a lot of potential for off-by-one errors, which makes it ideal for randomized testing. So let's take a look at my implementation and see if we can spot the bug:

fn sieve(n: usize) -> Vec<usize> {
    if n <= 1 {
        return vec![];
    }

    let mut marked = vec![false; n+1];
    marked[0] = true;
    marked[1] = true;
    marked[2] = true;
    for p in 2..n {
        for i in (2*p..n).filter(|&n| n % p == 0) {
            marked[i] = true;
        }
    }
    marked.iter()
          .enumerate()
          .filter_map(|(i, &m)| if m { None } else { Some(i) })
          .collect()
}

Let's try it on a few inputs by hand:

sieve(3) => [2, 3]
sieve(5) => [2, 3, 5]
sieve(8) => [2, 3, 5, 7, 8] # !!!

Something has gone wrong! But where? The bug is rather subtle, but it's an easy one to make. It's OK if you can't spot it, because we're going to use QuickCheck to help us track it down.

Even before looking at some example outputs, it's good to try and come up with some properties that are always satisfiable by the output of the function. An obvious one for the prime number sieve is to check if all numbers returned are prime. For that, we'll need an is_prime function:

fn is_prime(n: usize) -> bool {
    n != 0 && n != 1 && (2..).take_while(|i| i*i <= n).all(|i| n % i != 0)
}

All this is doing is checking to see if any number in [2, sqrt(n)] divides n with base cases for 0 and 1.

Now we can write our QuickCheck property:

fn prop_all_prime(n: usize) -> bool {
    sieve(n).into_iter().all(is_prime)
}

And finally, we need to invoke quickcheck with our property:

fn main() {
    quickcheck(prop_all_prime as fn(usize) -> bool);
}

A fully working source file with this code is in examples/sieve.rs.

The output of running this program has this message:

[quickcheck] TEST FAILED. Arguments: (4)

Which says that sieve failed the prop_all_prime test when given n = 4. Because of shrinking, it was able to find a (hopefully) minimal counter-example for our property.

With such a short counter-example, it's hopefully a bit easier to narrow down where the bug is. Since 4 is returned, it's likely never marked as being not prime. Since 4 is a multiple of 2, its slot should be marked as true when p = 2 on these lines:

for i in (2*p..n).filter(|&n| n % p == 0) {
    marked[i] = true;
}

Ah! But does the .. (range) operator include n? Nope! This particular operator is a half-open interval.

A 2*p..n range will never yield 4 when n = 4. When we change this to 2*p..n+1, all tests pass.

In addition, if our bug happened to result in an index out-of-bounds error, then quickcheck can handle it just like any other failureโ€”including shrinking on failures caused by runtime errors.

But hold on... we're not done yet. Right now, our property tests that all the numbers returned by sieve are prime but it doesn't test if the list is complete. It does not ensure that all the primes between 0 and n are found.

Here's a property that is more comprehensive:

fn prop_prime_iff_in_the_sieve(n: usize) -> bool {
    sieve(n) == (0..(n + 1)).filter(|&i| is_prime(i)).collect::<Vec<_>>()
}

It tests that for each number between 0 and n, inclusive, the naive primality test yields the same result as the sieve.

Now, if we run it:

fn main() {
    quickcheck(prop_all_prime as fn(usize) -> bool);
    quickcheck(prop_prime_iff_in_the_sieve as fn(usize) -> bool);
}

we see that it fails immediately for value n = 2.

[quickcheck] TEST FAILED. Arguments: (2)

If we inspect sieve() once again, we see that we mistakenly mark 2 as non-prime. Removing the line marked[2] = true; results in both properties passing.

What's not in this port of QuickCheck?

I think I've captured the key features, but there are still things missing:

  • Only functions with 8 or fewer parameters can be quickchecked. This limitation can be lifted to some N, but requires an implementation for each n of the Testable trait.
  • Functions that fail because of a stack overflow are not caught by QuickCheck. Therefore, such failures will not have a witness attached to them. (I'd like to fix this, but I don't know how.)
  • Coarbitrary does not exist in any form in this package. It's unlikely that it ever will.
  • Arbitrary is not implemented for closures. See issue #56 for more details on why.

quickcheck's People

Contributors

andersk avatar apasel422 avatar atouchet avatar badboy avatar blt avatar bluss avatar burntsushi avatar danburkert avatar emallson avatar erickt avatar flying-sheep avatar habnabit avatar hcpl avatar huonw avatar jturner314 avatar lopopolo avatar maxbla avatar milibopp avatar nathantypanski avatar notriddle avatar oherrala avatar pseitz avatar remexre avatar rozbb avatar seanrburton avatar sgrif avatar sobolevn avatar tari avatar tokenrove avatar vi avatar

Stargazers

 avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar

Watchers

 avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar

quickcheck's Issues

Idea: Testing `fail!`ures with `TestResult::must_fail`

Currently, quickcheck doesn't provide a way to test properties that must trigger a task failure to pass. For example, if you want to test that bound checking is working, you would write the following property:

#[quickcheck]
fn out_of_bounds(length: uint, index: uint) -> TestResult {
    let v = Vec::from_elem(length, 0u8);

    if index < length {
        TestResult::discard()
    } else {
        // v[index] ?
    }
}

But you are missing a piece: You need some function that returns TestResult::passed() if v[index] triggers a task failure, or TestResult::failed() if v[index] succeeded.

That's why I propose adding the must_fail function to the library, where must_fail is defined as:

use quickcheck::TestResult;
use std::comm;
use std::io::ChanWriter;
use std::task::TaskBuilder;

// This should be a `TestResult` static method
fn must_fail<T: Send>(f: proc(): Send -> T) -> TestResult {
    // A "/dev/null" sink for the stderr of the child task
    let (tx, _) = comm::channel();
    let dev_null = box ChanWriter::new(tx) as Box<Writer + Send>;

    let failed =
        TaskBuilder::new().
            // Redirect the stderr of the child task to "/dev/null"
            stderr(dev_null).
            try(f).
            is_err();

    TestResult::from_bool(failed)
}

This function provides the missing functionality to complete the out_of_bounds property:

#[quickcheck]
fn out_of_bounds(length: uint, index: uint) -> TestResult {
    let v = Vec::from_elem(length, 0u8);

    if index < length {
        TestResult::discard()
    } else {
        must_fail(proc() v[index])
    }
}

Which now can be tested:

running 1 test
test out_of_bounds ... ok

test result: ok. 1 passed; 0 failed; 0 ignored; 0 measured

And if you wrongly implement the test, for example by reversing the index < length condition, then the test would fail as expected:

running 1 test
test out_of_bounds ... FAILED

failures:

---- out_of_bounds stdout ----
        task 'out_of_bounds' failed at '[quickcheck] TEST FAILED. Arguments: (1, 0)', src/lib.rs:83



failures:
    out_of_bounds

test result: FAILED. 0 passed; 1 failed; 0 ignored; 0 measured

@BurntSushi What do you think? Should this functionality be added to quickcheck?

'Rng.gen_range called with low >= high'

This can happen due to invalid casts from quickcheck::Gen::size of type usize to various other integer types with a value range smaller than usize (cf. quickcheck::arbitrary::unsigned_arbitrary).

Test case:

#[test]
fn test() {
    fn prop(_: u8) -> bool { true }

    QuickCheck::new()
        .gen(StdGen::new(rand::thread_rng(), 1024))
        .quickcheck(prop as fn(u8) -> bool)
}

RUST_LOG has no effect

README.md:

N.B. When using quickcheck (either directly or via the attributes), RUST_LOG=quickcheck enables info! so that it shows useful output (like the number of tests passed). This is not needed to show witnesses for failures.

RUST_LOG no longer controls the output of the log crate itself. The old behavior can be replicated with env_logger, or the logging behavior can be changed (and the documentation be updated).

Implement Wrapping<integer type> for Arbitrary

It would be nice to be able to use quickcheck with (e.g.) Wrapping<u8>. I'm not sure I can fix this in my own code, adding

impl Arbitrary for Wrapping<u8> {
    fn arbitrary<G: Gen>(g: &mut G) -> Wrapping<u8> {
        Wrapping(Arbitrary::arbitrary(g))
    }
}

generates a compile error because neither Arbitrary nor Wrapping were defined by me.

The random selection doesn't appear to be very random

I have a type which wraps an i32, and wrote an Arbitrary impl like so:

trait Arbitrary<PgDate> {
    fn arbitrary<G: Gen>() -> Self {
          PgDate(i32::arbitrary())
    }
}

When I actually output what it's running with, the values are only ranging from 100 to -100, instead of 100 completely random numbers as I'd expect. Additionally, shrink appears to be flawed on these types. My understanding was that quickcheck would test for known problematic values for a given type. For i32 I'd expect that to at minimum be 1, 0, -1, i32::MAX and i32::MIN, but when I add the shrink like so:

fn shrink(&self) -> Box<Iterator<Item=Self>> {
    Box::new(self.0.shrink().map(PgDate))

I just see the same range of -100 to 100. This caused a critical bug that occurs for any values less than -2451545 to go unnoticed.

Compilation flow arrested on ARM using 1.8 dinghy

Compiling quickcheck_macros v0.2.26
     Running `rustc /tmp/.cargo/registry/src/github.com-48ad6e4054423464/quickcheck_macros-0.2.26/src/lib.rs --crate-name quickcheck_macros --crate-type dylib -C prefer-dynamic -C opt-level=3 -C metadata=df62799bae73490e -C extra-filename=-df62799bae73490e --out-dir /tmp/ramp-master/target/release/deps --emit=dep-info,link -L dependency=/tmp/ramp-master/target/release/deps -L dependency=/tmp/ramp-master/target/release/deps --extern quickcheck=/tmp/ramp-master/target/release/deps/libquickcheck-ef66e0544cfbee5c.rlib --cap-lints allow`
/tmp/.cargo/registry/src/github.com-48ad6e4054423464/quickcheck_macros-0.2.26/src/lib.rs:10:1: 10:21 error: found possibly newer version of crate `std` which `syntax` depends on [E0460]
/tmp/.cargo/registry/src/github.com-48ad6e4054423464/quickcheck_macros-0.2.26/src/lib.rs:10 extern crate syntax;
                                                                                            ^~~~~~~~~~~~~~~~~~~~
/tmp/.cargo/registry/src/github.com-48ad6e4054423464/quickcheck_macros-0.2.26/src/lib.rs:10:1: 10:21 note: perhaps this crate needs to be recompiled?
/tmp/.cargo/registry/src/github.com-48ad6e4054423464/quickcheck_macros-0.2.26/src/lib.rs:10 extern crate syntax;
                                                                                            ^~~~~~~~~~~~~~~~~~~~
/tmp/.cargo/registry/src/github.com-48ad6e4054423464/quickcheck_macros-0.2.26/src/lib.rs:10:1: 10:21 note: crate `std` path #1: /home/odroid/rust-1.8.0-system-alloc-LFS/lib/rustlib/arm-unknown-linux-gnueabihf/lib/libstd-db5a760f.rlib
/tmp/.cargo/registry/src/github.com-48ad6e4054423464/quickcheck_macros-0.2.26/src/lib.rs:10:1: 10:21 note: crate `std` path #2: /home/odroid/rust-1.8.0-system-alloc-LFS/lib/rustlib/arm-unknown-linux-gnueabihf/lib/libstd-db5a760f.so
/tmp/.cargo/registry/src/github.com-48ad6e4054423464/quickcheck_macros-0.2.26/src/lib.rs:10:1: 10:21 note: crate `syntax` path #1: /home/odroid/rust-1.8.0-system-alloc-LFS/lib/rustlib/arm-unknown-linux-gnueabihf/lib/libsyntax-db5a760f.so
/tmp/.cargo/registry/src/github.com-48ad6e4054423464/quickcheck_macros-0.2.26/src/lib.rs:10:1: 10:21 note: crate `syntax` path #2: /home/odroid/rust-1.8.0-system-alloc-LFS/lib/rustlib/arm-unknown-linux-gnueabihf/lib/libsyntax-db5a760f.rlib
error: aborting due to previous error
Could not compile `quickcheck_macros`.

Dinghy is almost two nights old.

`#[quickcheck]` doesn't work when `quickcheck` isn't in crate root

The #[quickcheck] macro doesn't work when the quickcheck crate is linked as a path other than ::quickcheck:

#![feature(plugin)]
#![plugin(quickcheck_macros)]

#[cfg(test)]
mod test {
    extern crate quickcheck;

    #[quickcheck]
    fn bar() -> bool { true }
}

Errors:

src/lib.rs:8:5: 8:15 error: failed to resolve. Maybe a missing `extern crate quickcheck`?
src/lib.rs:8     #[quickcheck]
                 ^~~~~~~~~~
src/lib.rs:8:5: 8:18 error: unresolved name `quickcheck::quickcheck`
src/lib.rs:8     #[quickcheck]
                 ^~~~~~~~~~~~~

It's clear why this is happening from the macro's implementation, but it would be nice for it to be supported. One solution would be to pass arguments to the #[quickcheck] macro like

#[quickcheck(self, quickcheck)]
fn bar() -> foo { true }

and turn the sequence of identifiers into a path, defaulting to ::quickcheck when none are given. It should also work when quickcheck is used under a different name:

extern crate "quickcheck" as baz;
#[quickcheck(self, baz)]
fn bar() -> foo { true }

This would involve making use of the third argument (the &ast::MetaItem) passed to expand_meta_quickcheck, which is currently ignored.

How to test a function `fn(&str, &str) -> bool`?

This is a question issue, and I hope that's an acceptable use in this project.

I try to use quickcheck to write tests for function accepting two strings. I haven't been able to adapt the example test to my use case. This is what I have. Is quickcheck able to provide random parameters for strings (or &strs)?

extern crate quickcheck;
// at least difference of the sizes of a and b
#[test]
fn prop_test_() {
    use quickcheck::quickcheck;

    fn at_least_size_difference(a: &str, b: &str) -> bool {
        let size_a = a.chars().count() as i32;
        let size_b = b.chars().count() as i32;
        let diff = (size_a - size_b).abs();

        edit_distance::edit_distance(a, b) > diff;
    }

    quickcheck(at_least_size_difference as fn(a: &str, b: &str) -> bool);
}

Debug trait somehow getting lost (?) making debugging difficult

Expected

Details about why my my data structure failed.

Actual

UNABLE TO SHOW RESULT OF PANIC.

Details

(I am still learning Rust, so bear with me if my code is dumb or I miss something obvious.)

I am trying to define simple, stable-compatible quickcheck tests for a trait, that can be reused to test different data structures that implement it. In this case, I am trying to test a persistent heap.

Data structure + generic test.

Unfortunately, one of my two implementations is broken somewhere, but the only information I get out of quickcheck is Error: "UNABLE TO SHOW RESULT OF PANIC."', <path>/quickcheck-0.2.21/src/tester.rs:113.

Link to broken build.

Link to broken usage location

The data structure in question has #[derive(Debug)] and I have tested that println("{:?}", s) works for this instance of the data structure, so I don't know why I am not getting any other information about the panic, and I don't know what to do to make the situation better.

quickcheck_macros does not work on stable (or beta)

Using stable Rust trying to install quickcheck_macros gives:

Compiling quickcheck_macros v0.2.24
/home/users/russel/.cargo/registry/src/github.com-0a35038f75765ae4/quickcheck_macros-0.2.24/src/lib.rs:8:1: 8:45 error: #[feature] may not be used on the stable release channel
/home/users/russel/.cargo/registry/src/github.com-0a35038f75765ae4/quickcheck_macros-0.2.24/src/lib.rs:8 #![feature(plugin_registrar, rustc_private)]

which more or less makes this potentially wonderful testing tool useless. Or have I missed something?

Build failure with recent rust nightly (rustc 79a5448f4)

When I try to compile quickcheck_macros as a Cargo dependency for something else, I get:

Build failed, waiting for other jobs to finish...
Could not compile `quickcheck_macros`.

--- stderr
src\lib.rs:29:48: 29:70 error: mismatched types: expected `Box<syntax::ext::base
::ItemModifier+'static>`, found `fn(&mut syntax::ext::base::ExtCtxt<'_>, syntax:
:codemap::Span, Gc<syntax::codemap::Spanned<syntax::ast::MetaItem_>>, Gc<syntax:
:ast::Item>) -> Gc<syntax::ast::Item>` (expected box, found extern fn)
src\lib.rs:29                                   ItemModifier(expand_meta_quickch
eck));

rustc version is rustc 0.12.0-pre-nightly (79a5448f4 2014-09-13 20:36:02 +0000), quickcheck_macros version is v0.1.0 (https://github.com/BurntSushi/quickcheck#c5a719a3). Windows 64-bit (hence the unwieldy line breaks, blame the Win32 console!).

Thanks!

quickcheck-macros doesn't build on the latest nightly

When building on the latest nightly, I get the following error message:

$ cargo test
 Downloading rand v0.3.12
 Downloading log v0.3.4
 Downloading quickcheck v0.2.24
 Downloading winapi v0.2.5
 Downloading libc v0.2.2
 Downloading quickcheck_macros v0.2.24
   Compiling winapi v0.2.5
   Compiling libc v0.2.2
   Compiling log v0.3.4
   Compiling advapi32-sys v0.1.2
   Compiling rand v0.3.12
   Compiling quickcheck v0.2.24
   Compiling quickcheck_macros v0.2.24
/Users/hawk/.multirust/toolchains/nightly/cargo/registry/src/github.com-88ac128001ac3a9a/quickcheck_macros-0.2.24/src/lib.rs:23:5: 23:28 error: unresolved import `rustc::plugin::Registry`. Could not find `plugin` in `rustc` [E0432]
/Users/hawk/.multirust/toolchains/nightly/cargo/registry/src/github.com-88ac128001ac3a9a/quickcheck_macros-0.2.24/src/lib.rs:23 use rustc::plugin::Registry;
                                                                                                                                    ^~~~~~~~~~~~~~~~~~~~~~~
/Users/hawk/.multirust/toolchains/nightly/cargo/registry/src/github.com-88ac128001ac3a9a/quickcheck_macros-0.2.24/src/lib.rs:23:5: 23:28 help: run `rustc --explain E0432` to see a detailed explanation
error: aborting due to previous error
Could not compile `quickcheck_macros`.

To learn more, run the command again with --verbose.

On the latest nightly:

$ rustc --version
rustc 1.7.0-nightly (81ae8be71 2015-12-09)
$ cargo --version
cargo 0.7.0-nightly (1af03be 2015-12-08)

Failing build with most recent Rust

Looks like the most recent Rust broke things. Building my repo https://github.com/FranklinChen/type-directed-tdd-rust which uses quickcheck resulted in

src/arbitrary.rs:60:29: 60:40 error: explicit lifetime bound required
src/arbitrary.rs:60 impl<A> Iterator<A> for Box<Shrinker<A>> {
                                                ^~~~~~~~~~~
src/arbitrary.rs:75:35: 75:46 error: explicit lifetime bound required
src/arbitrary.rs:75 pub fn empty_shrinker<A>() -> Box<Shrinker<A>> {
                                                      ^~~~~~~~~~~
src/arbitrary.rs:89:44: 89:55 error: explicit lifetime bound required
src/arbitrary.rs:89 pub fn single_shrinker<A>(value: A) -> Box<Shrinker<A>> {
                                                               ^~~~~~~~~~~
src/arbitrary.rs:372:25: 372:36 error: explicit lifetime bound required
src/arbitrary.rs:372     fn new(x: A) -> Box<Shrinker<A>> {
                                             ^~~~~~~~~~~
src/arbitrary.rs:407:25: 407:36 error: explicit lifetime bound required
src/arbitrary.rs:407     fn new(x: A) -> Box<Shrinker<A>> {
                                             ^~~~~~~~~~~

Does not compile with latest nightly (2015-02-19)

There's more than a few compile errors resulting from using the latest nightly:

$ rustc --version
rustc 1.0.0-nightly (dfc5c0f1e 2015-02-18) (built 2015-02-19)
$ cargo build
   Compiling quickcheck v0.1.39 (file:///home/caspar/src/rust-quickcheck)
src/arbitrary.rs:150:29: 150:44 error: the parameter type `A` may not live long enough [E0310]
src/arbitrary.rs:150                 let chain = single_shrinker(None).chain(x.shrink().map(Some));
                                                 ^~~~~~~~~~~~~~~
src/arbitrary.rs:150:29: 150:44 help: consider adding an explicit lifetime bound `A: 'static`...
src/arbitrary.rs:150                 let chain = single_shrinker(None).chain(x.shrink().map(Some));
                                                 ^~~~~~~~~~~~~~~
src/arbitrary.rs:150:29: 150:44 note: ...so that the type `core::option::Option<A>` will meet its required lifetime bounds
src/arbitrary.rs:150                 let chain = single_shrinker(None).chain(x.shrink().map(Some));

* etc etc for another 85 lifetime-related error messages *

src/tester.rs:189:30: 189:36 error: type `core::result::Result<std::thread::JoinGuard<'_, T>, std::io::error::Error>` does not implement any method in scope named `join`
src/tester.rs:189                             .join()
                                               ^~~~~~
src/tester.rs:446:29: 446:35 error: type `core::result::Result<std::thread::JoinGuard<'_, T>, std::io::error::Error>` does not implement any method in scope named `join`
src/tester.rs:446         match t.scoped(fun).join() {
                                              ^~~~~~
error: aborting due to 88 previous errors
Could not compile `quickcheck`.

I had a shot at fixing it without any success (still getting my head around lifetimes and associated syntax) so I figured the next best thing is reporting it.

Related: maybe it'd be worth fixing up http://www.rust-ci.org/BurntSushi/quickcheck ? Seems like it hasn't kicked off a new build in almost a year; it's not particularly clear on that page but on the main rust-ci.org page it claims it was unable to build and needs the owner to authenticate, or something along those lines.

Feature request: Add auto-derivators for structs and enums

I want quickcheck to have a compiler plugin that automatically generates Arbitrary impl for structs and enums composed of Arbitrary types, like with serialisation.

That should shrink

extern crate quickcheck;

use quickcheck::Arbitrary;

#[derive(Clone,Debug)]
struct Mumu {
    a : u32,
    b : String,
}

#[derive(Clone,Debug)]
enum Qqq {
    Lol,
    Ror,
    Kokoko(Mumu),
    Totot(u32),
}

impl Arbitrary for Mumu {
    fn arbitrary<G : quickcheck::Gen>(g:&mut G) -> Mumu {
        Mumu {a : Arbitrary::arbitrary(g), b : Arbitrary::arbitrary(g) }
    }
    fn shrink(&self) -> Box<Iterator<Item=Self>> {
        Box::new ( (self.a.clone(), self.b.clone()).shrink().map(|(aa,bb)| Mumu { a: aa, b:bb}))
    }
}

impl Arbitrary for Qqq {
    fn arbitrary<G : quickcheck::Gen>(g:&mut G) -> Qqq {
        let y = g.next_u32() % 4;
        match y {
            0 => Qqq::Lol,
            1 => Qqq::Ror,
            2 => Qqq::Kokoko(Arbitrary::arbitrary(g)),
            3 => Qqq::Totot(Arbitrary::arbitrary(g)),
            _ => panic!(),
        }
    }
    fn shrink(&self) -> Box<Iterator<Item=Self>> {
        match self {
            &Qqq::Totot(ref x) => Box::new(x.shrink().map(|s| Qqq::Totot(s))),
            &Qqq::Kokoko(ref x) => Box::new(x.shrink().map(|s| Qqq::Kokoko(s))),
            _ => quickcheck::empty_shrinker(),
        }
    }
}



#[test]
fn it_works() {
    fn qqq(x : Qqq) -> bool {
        if let Qqq::Kokoko(Mumu { a : v, b: _ }) = x {
            return v != 4
        }
        true
    }

    quickcheck::QuickCheck::new().tests(1000_000_000).quickcheck(qqq as fn(Qqq) -> bool);
}

to

extern crate quickcheck;

use quickcheck::Arbitrary;

#[derive(Clone,Debug,Arbitrary)]
struct Mumu {
    a : u32,
    b : String,
}

#[derive(Clone,Debug,Arbitrary)]
enum Qqq {
    Lol,
    Ror,
    Kokoko(Mumu),
    Totot(u32),
}

#[test]
fn it_works() {
    fn qqq(x : Qqq) -> bool {
        if let Qqq::Kokoko(Mumu { a : v, b: _ }) = x {
            return v != 4
        }
        true
    }

    quickcheck::QuickCheck::new().tests(1000_000_000).quickcheck(qqq as fn(Qqq) -> bool);
}

Add a way to export failing test cases

It would be very convenient to add a way to export failing test cases to allow to reproduce them and make a regression test suite.

The failing test should be exported either as:

  • a test function (with the #[test] attribute) you can just copy-paste inside your code or
  • a file containing the test cases with a way to rerun these test case automatically

This feature is more or less presented in the following video:
https://www.youtube.com/watch?v=x7O2Hkq983Y
And is used on:
http://www.quickcheck-ci.com/

Document that String is ASCII alphanumerics-only, maybe have a way of generating more Unicode?

Right now, Strings only have ASCII alphanumeric characters. I tried to use it to generate strings with spaces in it and was surprised when my code was never called ๐Ÿ˜บ

It would be cool if there were more of the Unicode set included. Even awesomer might be if we had some tuning knobs where we could say "more punctuation, less letters", perhaps using the Unicode character classes?

Thanks for the fun-to-use library!

`cargo test` fails

make test succeeds, but cargo test fails to compile with quickcheck macro dependency issues. I spent a few minute poking around, but couldn't figure out how to fix the issues.

$ cargo test
       Fresh quickcheck_macros v0.1.0 (file:///Users/dan/src/rust/quickcheck)
   Compiling quickcheck v0.1.0 (file:///Users/dan/src/rust/quickcheck)
src/lib.rs:23:1: 23:25 error: can't find crate for `quickcheck`
src/lib.rs:23 extern crate quickcheck;
              ^~~~~~~~~~~~~~~~~~~~~~~~

source trait is private (Rng)

Please consider this test case:

extern crate quickcheck;

use quickcheck::{Arbitrary, Gen};

#[derive(Clone)]
pub struct Foo(u8);

impl Arbitrary for Foo {
    fn arbitrary<G: Gen>(g: &mut G) -> Foo {
        Foo(g.gen())
    }
}

Compilation with rustc 1.3.0-nightly (0c052199b 2015-07-11) gives:

src/lib.rs:10:13: 10:20 error: source trait is private
src/lib.rs:10         Foo(g.gen())
                          ^~~~~~~

I think it is necessary to export not only Gen but also it's parent trait Rng:

diff --git a/src/lib.rs b/src/lib.rs
index f13fcdc..ab1698b 100644
--- a/src/lib.rs
+++ b/src/lib.rs
@@ -10,6 +10,7 @@
 #[macro_use] extern crate log;
 extern crate rand;

+pub use rand::Rng;
 pub use arbitrary::{
     Arbitrary, Gen, StdGen,
     empty_shrinker, single_shrinker,

Provide some way to fail the unit test if all the quickcheck tests were discarded

Sometimes I write too many "discard conditions" (along with too many arguments), or sometimes I get those conditions wrong, and quickcheck ends up discarding all the 10k tests, and flagging the unit test as passed. IMO, either case is a bug, because I'm not actually testing anything, and I should fix the discard conditions.

Right now, the only way to detect this issue is to use RUST_LOG=quickcheck and look out for "Passed 0 Quickcheck tests" messages.

I would like to see some way to fail the unit test if too few quickcheck tests passed. Preferably, using a environment variable, say QC_PASSED_TESTS_THRESHOLD=25 cargo test will fail the unit test if less than 25 quickcheck tests passed, if the variable is unset, the threshold will be zero, which is the current behavior.

Thoughts @BurntSushi ?

Write own Arbitrary impls

I wanted to write my own Arbitrary impls but apparently I cannot access the quickcheck::arbitrary module?

Strings generated by default should have more usual characters

Strings typically generted look like this:

๒’ฝน๒ชฌ›๒˜ŒŸ๓ฅซน๑•˜ผ๒Ÿถน๑€๓ฟกฅ๑ฐซฉ๔‚’ก๐ป˜’๒…‡ช๒ธˆ๒‘™๒Ÿช†๓ดฅ๔…ฒด๓ฉ‡‡ี”๐กซ—๓ผบ†๒บฉช๐พบ—๐ต…Ÿ๐ปŽš๑ฆŽ…๑ณœฟ๓”ˆแ˜ค๑œพŠ๑บฅฟ๒”›๓†ฌญ๐™šน๑ƒƒฃ๑ฅŠ†๒‡†—๒‚ค‡๔€‘ฌ๓จ๓‰จซ๒บŒ‘๑ฝ™•๑ท—ฎ๓ป‰๐ฟžŠ๑นฉต๒–ฌŠ๑ฃ‡๓˜…›๓–›‰๐ผ„ ๓„šท๒ฝ•Ž

๑กฐŽ๐ฃ’Œ๓นฃต

๔…˜ค๓€žถ๑ผฎณ๐ฎ•‡๒€•๑—Žฐ๒€‡ฆ๒น–ป๑š‚ซ๓šพช๒ƒข›เตพ๐ฌซค๑˜–ค๔‡พท๒‡ค๑ฎ…ˆๅผ…๒ฅ…ฝ๑จญด๓ถˆก๑ซฏญ๐งšผไ แด๔Œทž๓ปƒƒ๑›‘“๓ตŽ๐ฏญœ๔„ผง๒›ทบ๑š‚›๑Šชง๓ฏฑฉ๑ฉด๐›ญ‘๐จ€’๑กฅ๓›ข˜๑บข๐ต‘Œ็œป๓–™ž๑…‰Š๒›ปจ๑†ทฎ๓ธง–๓ต”ธ๑Žฝ”๒กšฅ๑‚ผฎ๒†ผค๑„“ญ๑ทฌ—๔‚ญก๒ผ €๑ฅ‰‘๓“Šข๒กฝ๔€Ÿž๑ฎ†€๑ฟซ‡๑ฟŒ๒’‹๑œŽ€๑ข’๒‹ฏผ๑นŒˆ๔ƒ”ช๒ˆŸ™๓Ÿฌ ๓€งธ๓€‰–๐ ฅค๑Œกญ๐ง†ผ๑ฆ›ผ๑ถฐ†๑‚ ‘๐ป—›๒™„’

๒‚บ๓ถผ€๐ฐทน๐พ‰๒ฎ˜ญ๒ˆฅ๓“žค๒ซš๓™–˜๓†–™๐ฐปŸ๑‡ซ™๐ด‹…๐ ˆผ๓นŒ€ๅ‡พ๐–™Ÿ๐ “•๐พญญ๐—ข๑ฅ‚ก๑‡ซ˜๒ซฏ๓จƒ–๏จฑ๓ฐฒบ๒˜ณ‘๐›‰๑กดถ๔นŠ๒”Œ‡แ˜ผ๔Žœ…๓‡†ฏ๓ˆคŸ๓ทƒš๔ŽžŽ๑ฆ–„๋ด„๒ฌฉ˜๑Žฉญ๒“ฒœ๓ฅŠ๐ฐ€ˆ๔†”๑ฅ–๒…ˆณ๐ฟด›๔Œ†—๑ฐ๑›ฏš๔กฉ๒ต† ๑ญ†๔˜ฃ๒…•œ๒ž™„๓ฌฆ๑œŸฉ๓ฎ๒Šฝ›๓˜‰๑”€Š๑Ž‡๓ž๑ฅ‡๓•ŽŽ๐‘ฉ๑กจญ๒‹ก ๒ช™ƒ๓†‚ต๐ซŸฏ๑ฉฝ—๐•กซ๒ž ‡๐คทธ๒™บ›๒„Žฉ๒ฎ‚๒’นฃได’๐ป‰œ๓ธ…ฉ๔Žฌ™๐ท†Š๑ฒŽ€๑ชจŒ๒Œ€€๔„ป–๑‡‹’๓นœ…

๑’ขฉ๒ŠŸ—๐ฅฟ๒Œ…ž๑ฌ•ง๐žŽ‚๑ฎ•„๒žฟฎา™๎ฅญ๓ข‰๓ ขœ๑ญˆ‘๓•“ด๓…ธ›๒•Ž…๐ณž‡๑ช‰ฉ๑žขœ๒ญง˜๓›ฆฆ๓Žชฆ๓ฑšํ™€๑‹ง็ฏฌ๓ฆฒซ๒ˆงน๑”ฟ๑šฃช๒›พ๐กš™๓ซฌด๒ฝ‰ซ๑ˆ—๑”ฏ๓น•ฉ๑ฆŸ ๓ผน๐ฆผ„๓น†ฒ๑œ‹‘๑ ‡’๑‡ฝ–๔‹ค‹๑ค”‡๓‚š๑ผ›๔„ผ€๓ฎ€ฏ๐”ฏ€๓ฆƒ‚๑ต’ต๒„ฏŽ๓ฉŽ…๒“Š•๒ฏ Ž๑Œฌ†๋ก‡๑˜บŠ๑ตบพ๐•งจ๑šพ“๒šฌ๓ทˆฎ๓ ขง๒ฆ‚ผ๐นฅž๓ฎพ›๒ผข๔€œ๒ธฌŸ๓ฎƒฃ๒ชพ‚๒ฌท๑ฏ•ž๑‹“๓žญค๓ฐฅ”๑ฎƒ๓ฒซ๓ปžป๒ปฌ’๔„‘Ž๒‰—ญ๒„ซ›๓จถ‡๓”ฝŸ๒น‘น๒ผนจ๒จต–๑ฐŠ…๑ปฃŠ

๑•‚๔Œฅฉ๑ต‰—๑ถ—–๐ฌ‰ž้ญŠ๑žฆน๓™คต๐ฆช€๐’ฎก๓„ฎฝ๐•— ๑ฅฝ•๔‚ฅฆ๔ˆฒน๓™ก๒žžŒ๐ต„๐ณŽฉ๋šฒ๓„ฌฅ๒ถฐ‚๑ททข๒ฌ‰๑žŸ๒ƒนช่…Ž๓‘ญ๒’‹€๒”ฒ™๓‰บฟ๑ฒฅ“๐นฏ๓ฉฌ‘

๑พง๓ซฎ๒Žฟ›็ชฏ๑•“จ๔ˆฒถ๑ฝถ›๒ฉชฃ๒นก๔‚”๒›˜Ž๒˜‡€๐ ทบ๑Ž‚ฆ๑ญญธ๑ฝŽŠ๐ŠŠ๐จบฟ๒†Šบ๐—žฉ๔Œฝ‚๑ธถ่ผฌ๑„ƒธ๑…™œ๑ขณ๑”ง๑ˆ™„๐ณ๐ช‰›

๐ฅน ๐ผžณ๓พข™๐”…ฟ๔Šญท๑พฃ๒Ž•Š๑ญณ–๒•ธŒ๓Ž€›๒ถƒฃ๐œฟ‰๔€ข›๓ถ„ฟ๐ฒŠŠ๑ชŸ›๑ˆ˜‰๑ฒปฆ๑ญฉ‚๓ณ„’๐พบฒ๐ฏ†Š๑ธ†๑Œผ ๑ญณ’๑ทžˆ๒“Ž๔ˆ‹–๐šœธ๓ฟ’ฒ๒”€๐ฐคซ๒ซฅ‘๒žผฒ๐ฅธ๑ฅพฅ๓“ฎฟ๒ณ–ฑ๐—‹•๓ณ‰‹๔‰ จ๐–ณ๓ท•๒˜Š๒ฉฐ๓บฑ๑ฐ‰ๅดณ๓€ˆ๐’ฏป๒ฟฎฅ๐ฏ™‡๑ฅป‚๓“ฟญ๒ซ™Ž๑„ฆฝ๑ ทŽ๐žธ๑ตณณ๓ฟงป๔‚ฏ“๓น—„๒‡ชพ๓ฝ˜๓‰ตท๒—šซ๑ธ„๓˜ณ๑ถŒ๑ฅตพ๓ง’Ÿ๒ฎซฐ๐–ช„๑žผ—๔พข๐ถ•˜๒Šฎด๐—ฐผ๒„Œ‹๑บฟญ๒ฃ˜ธ

๒ต‘†๒ข€๒ฏช‚๑พ๐ง”ฅ๓ผ–ˆ๒ฌฟš๓ฐ–“๔ฏŒ๒†™๑Ž ค๑ฌดž๐’ฒ๓นฉ๒พœฃ๑ฐข๓ญ™ฏ๑ ต›๑น˜ฆ๓’ณผ๑กซ‰๓บฒข๑˜พฅ๑ฟ”ท๓พฃŽๅŸ™๓Šฝ‘๒ผช๒Šฐญ๑ ฎบ็ฆน๓พฃฃ๐ผต๒’น๑“ชŠ๓ซจฌ๒ซ„’๓ฆ‰‚๑‡ซ„๓ฌ†ฟ๒ปƒƒ๑ฏŸถ๓œš“๐ท—š๐ฒฆ‚๓ป”ด๑ฎ‚๒ถฐŽ๑ฅŠข๑ฐตช๑ข”€๒ปขๅ–’๓ขท…๓Ÿฝ„๑šš๑“ผˆ๒ญถธ๔…‡†๓ญ‚๐œฏƒ๐จ–‰๓‘ถง๒ชŒฟ๐จŒฝ๒„”–๐ฑขผ๒ฉฑผ๒ซฎด

๐ปฅป๒–ฟ„๓Žฐ๐”ฑท๐‘’‚๐ผŸŠ๔‡ผ…๐ฒ‚„๔ˆ”œ๓‰„˜๔‹ซข๐–”„๑‹บ†๐ฌŽฝ๒›–ข๎ญ›๒ ฌ”๔ƒธซ๔€ต†๐ฎ˜œ๓›ฅบ๔‚ถ๒ต„ค๐บ…๑€•›๑Ÿฅ ๔ฝ‘๓นซ ๒กง„๓’ธก๒ฐŽœ๒ŠŽฐ๑ฅ‘Œ๑‡‡ฃ๐ฉ”’๒ฐ…ˆ๒ต„ฏ๑“ฃ๓“ข๒‘››๒‰จธ๐พ“Š๐ท…ฃ๐ตผค๐ฆŒ˜๒“€ž๐ฝฃŸ๒žด

๑’šž๑™ถด๓ซƒ”๑€นฎ๑ฃฏ๐ต˜ฃ๓พˆฃ๒œด๓„Œฟ๐ฆถž๓ฏ›ซ๓„Š๑ณฎ๑šถจ๓กทฃ๐ธซ‚๐žณŠ๑œช๓œฎถ๐บพ†ใฎ„๑†ก๓“ถญ๏ฏˆ๓ŠŽŠ๓ฟงบ๔‹”๓ค„๓พ’๒ฃŽฆ๑›ฆ‹๒ฏ –๔“™๑œ ”๒‡บฑ๐“†ฆ๑Ÿฃค๓ขง๒ฑ•๑—ฝณ๓ถฑฎ๓ถŠผ๓ฟ“Š๒พš“๒ตต†๒ผซ๓„ˆฌ๒œ’•๓ฑฆก๑˜—๒ฎ‘๓”ผ‹๒‚„ธ๓บบ˜๐จ›ฝ๓šฒ๑ถ—œ๓–ฟญ๓ฃ ฏ๒†ฑฉ๑ฐŠซๆ†Ž๑ณฅ’๑ผทต๐ —†๑ป ๓ฝ–พ๐ฟ‚†๓‘““๑Žšฏ๒ฆ‰ ๐˜Šฒ

๐ณธˆ๒‹ธด๎คœ๓‚ฉ—๑ฝฐ‹๑„จช๑‰™๒€คต๓•ณท๐šฑฒ๒ตš‘๓“ถต๑€ก๓ˆก๒ฎ–ต๑œฌƒ๑ซกต๑ ฌณ๑ฌถŠ๐กฑญ๑…ผบ๒™พ๑žฌ๊ˆบ๐น–‰๐ตŒ›ใƒญ๑’ญ๔‰ซ’๓‹Ÿ‘๑”Šข๒ฝฒ“๑ฟจˆ๑‘œฐ๑‡ฟด๒ขฃ–๔Ÿฃ๒ฐฎ๓ฟ˜ฑ๓„กฃ๓น‰š๐ฌฅฟ๑‹˜™๐”ญฆ๒กŽ‘

๓ฐบฟ๓ถ„”๐ฌ†ท๓ฑ‚Œ๓‰ฌž๓พชข๑–––๑ฎŠ•๑งƒ๒พฝผ๑จคณ๐ธพ‚๐ฃ‘Œ๓ธ•–๓นดŒ๐ฌœ„๐‘ผฆ๔Œทฑ๒ฉ”ค๐–Œธ๑ฅบผ๑—Žฑ๑—ฑ๓ฒฃ‘๐ด’จ๓ดน’๐น’ช๓‹ŠŒ๓จด–๒™€๒ฟต๑ซ˜•๓‡ฏ๒‚ฏฒ๑ต•Œ๑ขœ๓นคจ๒™ข„๑จ“ฉ๒„ ‘๑ปจธ๐ปฆ๓ฆฉ’๑ฌชธ๓ปดด๑œ€›๒‡ฉ๑ผ—•๒ฃš‰๓ต›๓ฃนฅ๒‰ญž๒ฆšŽ๑€บฒ๑†›œ๓ฎŸ๑Ÿ’ซ๐ฒน‡๒‹ผก๒ทง˜๑ฟฉฌ๓Žญ๒ช†€๐›‘›๑ป‰ž๓ซจ๐กžฑใ†’

๒ผ‹ก๓ฏปญ๔ค›๑Ž”๓ธดญ๓œˆฐ๑ฑšฑํ”ฆ๓Ž†๐ขป๒ทŸ๑žฎต่˜ช๓–ฃข๐ซญช๐ฒŠบ๓ฟœš๑ˆ…—๒ฟง™๓ฟดž๑ซŒฑ่กฏ๒‰•ญ๒Šžดา›๓ด™ ๐ฑœฏ๑™ฐ๒ŠŒ๑ฎจฌ๓ชพช๒ฎ‚๐พจ†๑ฑ˜พ๑ก ‡ํŒข๒—ก—๒ฝ‘ถ๒ดธŸ๐™…ก๒Ž‹Ž๐ ฎ๓ถ–‰๒„ก๓Ÿ›ฟ๔ ฐ๒Ÿฟข๑šจฌ๐ฏขฌ๒ญซ‰๔‰Œž๑˜ฎ๓—•–๑กค๐ฎบ€๑ซ€๐”šˆ๑ท‹ญ๐™ชข๒‡ฌฃ๒›กก๑—šก๒“›๒˜Œถ๐ฆทด๑ถต๒ฎต”์ˆฅ๐ฒŸช

๑˜ฟ˜๐›€ฆ๒ตŠป๓ ‘œ๓ผพ๐™ˆจ๓นฒƒ๐ธฏ•๐œ†ฆๆท…๓ตฎ๐ฐ‚˜๑›ฆ‘๓›ซŒ๓Œฒ๓”•‡๓น™ฎ๑งƒท๑งˆต๑ ถ„๒™นซ๓“ฐ†๒’ˆ๔ƒŽข้‚ฎ๒ฃป–๑ฉน–๐“‹ฑ๐ฅฅซ๑ฉ˜†

It fails (or takes long) to find simple case like a when something starts with a space, special character or like this.

I think normal characters should be preferred to deeply Unicode things.

I think by default there should be tiers of characters classes:

  1. Bug-prone characters: whitespace, punctuation, control characters, maybe up to 5 selected Unicode queeries like zero-width space;
  2. [a-zA-Z0-9_];
  3. Unicode characters in the first plane;
  4. Everything else.

And character generator may aim for, for example, 10% of buggy chars, 40% of [a-zA-Z0-9_], 40% of basic Unicode characters, 10% of everything else.

How to make generated parameters test certain cases

I have another question about writing QuickCheck tests.

My function under test, edit_distance, has another property, which I want to quickcheck.

It is zero if and only if the strings are equal.

This requires that both parameters reasonably often are equal. I have a naive implementation here:

febeling/edit-distance@30532c2#diff-475a9530a9c1e888d097445199b74c2fR75

But when I run it and prepare the code a bit, I have seen that the the condition 'strings are equal' is never met. I guess that's a common problem in quickcheck test. Is there a good way to deal with this case?

Is there any way to run a single test

I'm having errors for one test. I'm changing code and running cargo test, but it's including the 300 other tests. Is there any way to flag the test to be the only one run? Something like #[only(quickcheck)]?

Error with quickcheck_macros

Running cargo test in the quickcheck_macros directory with the latest rust nightly 4874ca36f.

   Compiling quickcheck_macros v0.1.30 (file:///Users/sam/Dropbox/Projects/quickcheck/quickcheck_macros)
tests/macro.rs:9:1: 9:14 error: functions used as tests must have signature fn() -> ()
tests/macro.rs:9 #[quickcheck]
                 ^~~~~~~~~~~~~
tests/macro.rs:18:1: 18:14 error: functions used as tests must have signature fn() -> ()
tests/macro.rs:18 #[quickcheck]
                  ^~~~~~~~~~~~~
tests/macro.rs:22:1: 22:14 error: functions used as tests must have signature fn() -> ()
tests/macro.rs:22 #[quickcheck]
                  ^~~~~~~~~~~~~
tests/macro.rs:25:1: 25:14 error: functions used as tests must have signature fn() -> ()
tests/macro.rs:25 #[quickcheck]
                  ^~~~~~~~~~~~~
error: aborting due to 4 previous errors
Could not compile `quickcheck_macros`.

To learn more, run the command again with --verbose.

Would it be a good idea to update the .travis.yml file to also test the macros?

Using the function traits would improve usability.

At the moment when passing a function to quickcheck, its type has to specified:

fn prop() -> bool {
    true
}

quickcheck(prop as fn() -> bool);

However, bare functions can be passed as generic items implementing Fn traits without any casting:

fn run<F, T>(f: F) -> T where F: Fn() -> T {
    f()
}

fn prop() -> bool {
    true
}

fn main() {
    let t = run(prop);
    println!("{}", t);
}

If we implemented the Testable trait for F where F: Fn(A, ...) -> T instead of the concrete fn(A, ...) - T types, it would be possible to just do:

quickcheck(function);

I think this is a usability win, especially with #[quickcheck] unusable under 1.0 (people are likely to be writing quickcheck quite a bit).

I had a quick go at implementing this, but ran into trouble with the Fun trait. I couldn't work out a way to call the function (passed as &self) in a move closure passed to safe. It may be possible, or else more extensive restructuring may be required.

make vector shrinking lazy

Currently, a vector is shrunk by producing all possible shrunk values immediately. It would be much better if they were generated lazily with an iterator.

Perhaps std::iter::Unfold will be useful for this.

Seed and print the value of seed used on failure

I have tests that fail very rarely. When this happens on Travis, I'd really like to reproduce the issue locally.

If quickcheck randomly chose a seed, but printed that seed value on failure, then I could replicate test failures.

Edge cases

Is it possible to also define edge cases beside the random data without implementing my own Arbitrary type?

Of course for such cases I could just use normal testingโ€ฆ

Recursive shrink implementation requiring Clone on sub-iterators.

Ok, so my problem is wanting to implement shrink() for a type made up of other Arbitrary types:

struct Foo {
    bar: Bar,
    baz: Baz,
}

impl Arbitrary for Foo {
    fn arbitrary<G: Gen>(gen: &mut Gen) -> Foo {
        Foo {
            bar: Bar::arbitrary(),
            baz: Baz::arbitrary(),
        }
    }

    fn shrink(&self) -> Box<Iterator<Item=Foo>> {
        let bars = self.bar.shrink().chain(Some(self.bar));
        let bazs = self.baz.shrink().chain(Some(self.baz));
        Box::new(bars.cartesian_product(bazs).map(|(bar, baz)| Foo { bar: bar, baz: baz }))
    }
}

I think this shrink() impl is very elegant, but it doesn't work because cartesian_product (from itertools) requires Clone on the second iterator (of course). This is a well known problem with trait objects; making shrink() return Box<Iterator + Clone> isn't a very satisfying solution, since there's a question of "where do I stop? what about Sync + Send etc?. Furthermore, closures are neverClone` :(. Making the iterator type of shrink an associated type

pub trait Arbitrary {
  type Shrink: Iterator<Item=Self>;
  fn shrink(&self) -> Self::Shrink;
  /* ... */
}

means a default impl can't be provided (at least not without specialisation), but it would solve the issue. I know I can implement a shrink iterator by storing bar and baz itself, and creating new iterators all the time, but that is a lot of boilerplate. Is there some general solution to this problem of composing Arbitrary types?

quickcheck depends on WinAPI?

 Downloading rand v0.3.11 (reasonable - quickcheck is a randomized test)
 Downloading log v0.3.2 (reasonable - it's omnipresent)
 Downloading winapi v0.2.4 ( ??? )
 Downloading quickcheck v0.2.24

make number shrinking lazy

Currently, shrinking numbers is done by filling up a vector with all possibilities and returning it as an iterator. It would be great if this were changed so that each shrunk value wasn't generated until the iterator advanced.

get rid of `Shrinker` proxy trait

In a bygone era, I invented the Shrinker trait as a proxy so the shrink method of Arbitrary could return any iterator without regard to its underlying type. This was because Box<Iterator<A>> didn't itself implement Iterator<A>.

Now that DST is here and Rust has a more coherent story regarding trait object safety, it would be nice to remove this proxy trait and return Box<Iterator> instead.

I started working on this, but I ran into some bugs: rust-lang/rust#20605 and rust-lang/rust#20953. Once those are fixed, I think we can drop Shrinker.

add more examples

It would be great to have an example for implementing shrink with a custom type.

implement Testable for Fn*

Using rustc 1.0.0-nightly (3d0d9bb6f 2015-01-12 22:56:20 +0000):

fn reverse<T: Clone>(xs: &[T]) -> Vec<T> {
    let mut rev = vec!();
    for x in xs.iter() {
        rev.insert(0, x.clone())
    }
    rev
}

#[test]
fn reverse_identity() {
  fn prop(xs: Vec<i32>) -> bool {
    xs == reverse(reverse(xs.as_slice()).as_slice())
  }
  quickcheck(prop);
}

produces

error: the trait `quickcheck::tester::Testable` is not implemented for the type `fn(collections::vec::Vec<i32>) -> bool {tests::reverse_identity::prop}`

quickcheck(false) compiles; this appears to be specific to the fn impls, including the zero-arg fn.

Recommend Projects

  • React photo React

    A declarative, efficient, and flexible JavaScript library for building user interfaces.

  • Vue.js photo Vue.js

    ๐Ÿ–– Vue.js is a progressive, incrementally-adoptable JavaScript framework for building UI on the web.

  • Typescript photo Typescript

    TypeScript is a superset of JavaScript that compiles to clean JavaScript output.

  • TensorFlow photo TensorFlow

    An Open Source Machine Learning Framework for Everyone

  • Django photo Django

    The Web framework for perfectionists with deadlines.

  • D3 photo D3

    Bring data to life with SVG, Canvas and HTML. ๐Ÿ“Š๐Ÿ“ˆ๐ŸŽ‰

Recommend Topics

  • javascript

    JavaScript (JS) is a lightweight interpreted programming language with first-class functions.

  • web

    Some thing interesting about web. New door for the world.

  • server

    A server is a program made to process requests and deliver data to clients.

  • Machine learning

    Machine learning is a way of modeling and interpreting data that allows a piece of software to respond intelligently.

  • Game

    Some thing interesting about game, make everyone happy.

Recommend Org

  • Facebook photo Facebook

    We are working to build community through open source technology. NB: members must have two-factor auth.

  • Microsoft photo Microsoft

    Open source projects and samples from Microsoft.

  • Google photo Google

    Google โค๏ธ Open Source for everyone.

  • D3 photo D3

    Data-Driven Documents codes.