From d8e858c97f54f2a9f13872082bfe5d163510c792 Mon Sep 17 00:00:00 2001 From: Alexis Sellier Date: Sat, 3 Dec 2022 17:58:29 +0100 Subject: [PATCH 01/14] Run clippy on codebase --- examples/reverse_single.rs | 2 +- examples/sort.rs | 12 ++++++------ quickcheck_macros/src/lib.rs | 8 +++----- src/arbitrary.rs | 32 +++++++++++++++++--------------- src/tester.rs | 8 +++++++- src/tests.rs | 14 ++++++-------- 6 files changed, 40 insertions(+), 36 deletions(-) diff --git a/examples/reverse_single.rs b/examples/reverse_single.rs index 6112509..dcfffbe 100644 --- a/examples/reverse_single.rs +++ b/examples/reverse_single.rs @@ -13,7 +13,7 @@ fn main() { if xs.len() != 1 { return TestResult::discard(); } - TestResult::from_bool(xs == reverse(&*xs)) + TestResult::from_bool(xs == reverse(&xs)) } quickcheck(prop as fn(Vec) -> TestResult); } diff --git a/examples/sort.rs b/examples/sort.rs index 0f495a0..574d803 100644 --- a/examples/sort.rs +++ b/examples/sort.rs @@ -4,18 +4,18 @@ use quickcheck::quickcheck; fn smaller_than(xs: &[T], pivot: &T) -> Vec { - xs.iter().filter(|&x| *x < *pivot).map(|x| x.clone()).collect() + xs.iter().filter(|&x| *x < *pivot).cloned().collect() } fn larger_than(xs: &[T], pivot: &T) -> Vec { - xs.iter().filter(|&x| *x > *pivot).map(|x| x.clone()).collect() + xs.iter().filter(|&x| *x > *pivot).cloned().collect() } fn sortk(x: &T, xs: &[T]) -> Vec { - let mut result: Vec = sort(&*smaller_than(xs, x)); - let last_part = sort(&*larger_than(xs, x)); + let mut result: Vec = sort(&smaller_than(xs, x)); + let last_part = sort(&larger_than(xs, x)); result.push(x.clone()); - result.extend(last_part.iter().map(|x| x.clone())); + result.extend(last_part.iter().cloned()); result } @@ -38,7 +38,7 @@ fn main() { } fn keeps_length(xs: Vec) -> bool { - xs.len() == sort(&*xs).len() + xs.len() == sort(&xs).len() } quickcheck(keeps_length as fn(Vec) -> bool); diff --git a/quickcheck_macros/src/lib.rs b/quickcheck_macros/src/lib.rs index 132e8e0..1072e77 100644 --- a/quickcheck_macros/src/lib.rs +++ b/quickcheck_macros/src/lib.rs @@ -3,8 +3,6 @@ extern crate proc_macro2; extern crate quote; extern crate syn; -use std::mem; - use proc_macro::TokenStream; use quote::quote; use syn::{ @@ -31,11 +29,11 @@ pub fn quickcheck(_args: TokenStream, input: TokenStream) -> TokenStream { }); if errors.is_empty() { - let attrs = mem::replace(&mut item_fn.attrs, Vec::new()); + let attrs = std::mem::take(&mut item_fn.attrs); let name = &item_fn.sig.ident; let fn_type = syn::TypeBareFn { lifetimes: None, - unsafety: item_fn.sig.unsafety.clone(), + unsafety: item_fn.sig.unsafety, abi: item_fn.sig.abi.clone(), fn_token: ::default(), paren_token: syn::token::Paren::default(), @@ -60,7 +58,7 @@ pub fn quickcheck(_args: TokenStream, input: TokenStream) -> TokenStream { } } Ok(syn::Item::Static(mut item_static)) => { - let attrs = mem::replace(&mut item_static.attrs, Vec::new()); + let attrs = std::mem::take(&mut item_static.attrs); let name = &item_static.ident; quote! { diff --git a/src/arbitrary.rs b/src/arbitrary.rs index 92f893b..e3d5ec5 100644 --- a/src/arbitrary.rs +++ b/src/arbitrary.rs @@ -1,3 +1,5 @@ +#![allow(clippy::new_ret_no_self)] +#![allow(clippy::or_fun_call)] use std::char; use std::collections::{ BTreeMap, BTreeSet, BinaryHeap, HashMap, HashSet, LinkedList, VecDeque, @@ -47,7 +49,7 @@ impl Gen { /// randomly generated number. (Unless that number is used to control the /// size of a data structure.) pub fn new(size: usize) -> Gen { - Gen { rng: rand::rngs::SmallRng::from_entropy(), size: size } + Gen { rng: rand::rngs::SmallRng::from_entropy(), size } } /// Returns the size configured with this generator. @@ -131,9 +133,7 @@ pub trait Arbitrary: Clone + 'static { } impl Arbitrary for () { - fn arbitrary(_: &mut Gen) -> () { - () - } + fn arbitrary(_: &mut Gen) {} } impl Arbitrary for bool { @@ -282,8 +282,8 @@ impl VecShrinker { }; let size = seed.len(); Box::new(VecShrinker { - seed: seed, - size: size, + seed, + size, offset: size, element_shrinker: es, }) @@ -567,7 +567,7 @@ impl Arbitrary for OsString { fn shrink(&self) -> Box> { let mystring: String = self.clone().into_string().unwrap(); - Box::new(mystring.shrink().map(|s| OsString::from(s))) + Box::new(mystring.shrink().map(OsString::from)) } } @@ -745,7 +745,7 @@ macro_rules! unsigned_shrinker { Box::new( vec![0] .into_iter() - .chain(UnsignedShrinker { x: x, i: x / 2 }), + .chain(UnsignedShrinker { x, i: x / 2 }), ) } } @@ -756,7 +756,7 @@ macro_rules! unsigned_shrinker { fn next(&mut self) -> Option<$ty> { if self.x - self.i < self.x { let result = Some(self.x - self.i); - self.i = self.i / 2; + self.i /= 2; result } else { None @@ -811,7 +811,7 @@ macro_rules! signed_shrinker { if x == 0 { super::empty_shrinker() } else { - let shrinker = SignedShrinker { x: x, i: x / 2 }; + let shrinker = SignedShrinker { x, i: x / 2 }; let mut items = vec![0]; if shrinker.i < 0 && shrinker.x != <$ty>::MIN { items.push(shrinker.x.abs()); @@ -828,7 +828,7 @@ macro_rules! signed_shrinker { || (self.x - self.i).abs() < self.x.abs() { let result = Some(self.x - self.i); - self.i = self.i / 2; + self.i /= 2; result } else { None @@ -921,7 +921,7 @@ macro_rules! unsigned_non_zero_shrinker { } else { Box::new( std::iter::once(1).chain( - UnsignedNonZeroShrinker { x: x, i: x / 2 }, + UnsignedNonZeroShrinker { x, i: x / 2 }, ), ) } @@ -934,7 +934,7 @@ macro_rules! unsigned_non_zero_shrinker { fn next(&mut self) -> Option<$ty> { if self.x - self.i < self.x { let result = Some(self.x - self.i); - self.i = self.i / 2; + self.i /= 2; result } else { None @@ -1125,6 +1125,7 @@ impl Arbitrary for SystemTime { } #[cfg(test)] +#[allow(clippy::unit_cmp)] mod test { use std::collections::{ BTreeMap, BTreeSet, BinaryHeap, HashMap, HashSet, LinkedList, VecDeque, @@ -1365,11 +1366,11 @@ mod test { for n in v { let found = shrunk.iter().any(|&i| i == n); if !found { - panic!(format!( + panic!( "Element {:?} was not found \ in shrink results {:?}", n, shrunk - )); + ); } } } @@ -1561,6 +1562,7 @@ mod test { } #[test] + #[allow(clippy::reversed_empty_ranges)] fn ranges() { ordered_eq(0..0, vec![]); ordered_eq(1..1, vec![0..1, 1..0]); diff --git a/src/tester.rs b/src/tester.rs index e2eaa20..aa5430d 100644 --- a/src/tester.rs +++ b/src/tester.rs @@ -162,7 +162,7 @@ impl QuickCheck { let n_tests_passed = match self.quicktest(f) { Ok(n_tests_passed) => n_tests_passed, - Err(result) => panic!(result.failed_msg()), + Err(result) => panic!("{}", result.failed_msg()), }; if n_tests_passed >= self.min_tests_passed { @@ -176,6 +176,12 @@ impl QuickCheck { } } +impl Default for QuickCheck { + fn default() -> Self { + Self::new() + } +} + /// Convenience function for running QuickCheck. /// /// This is an alias for `QuickCheck::new().quickcheck(f)`. diff --git a/src/tests.rs b/src/tests.rs index 465ef15..51317ea 100644 --- a/src/tests.rs +++ b/src/tests.rs @@ -1,4 +1,4 @@ -use std::cmp::Ord; +#![allow(clippy::single_match)] use std::collections::hash_map::DefaultHasher; use std::collections::{HashMap, HashSet}; use std::ffi::CString; @@ -19,16 +19,15 @@ fn prop_oob() { but instead it passed {} tests.", n ), - _ => return, + _ => (), } } #[test] fn prop_reverse_reverse() { fn prop(xs: Vec) -> bool { - let rev: Vec<_> = xs.clone().into_iter().rev().collect(); - let revrev: Vec<_> = rev.into_iter().rev().collect(); - xs == revrev + let rev: Vec<_> = xs.clone().into_iter().rev().rev().collect(); + xs == rev } quickcheck(prop as fn(Vec) -> bool); } @@ -68,9 +67,8 @@ fn reverse_app() { app.extend(ys.iter().cloned()); let app_rev: Vec = app.into_iter().rev().collect(); - let rxs: Vec = xs.into_iter().rev().collect(); let mut rev_app = ys.into_iter().rev().collect::>(); - rev_app.extend(rxs.into_iter()); + rev_app.extend(xs.into_iter().rev()); app_rev == rev_app } @@ -92,7 +90,7 @@ fn max() { #[test] fn sort() { fn prop(mut xs: Vec) -> bool { - xs.sort_by(|x, y| x.cmp(y)); + xs.sort(); for i in xs.windows(2) { if i[0] > i[1] { return false; From 01622194fa0f59cfcd334106e835e0dece6cd15c Mon Sep 17 00:00:00 2001 From: Alexis Sellier Date: Sat, 3 Dec 2022 17:59:10 +0100 Subject: [PATCH 02/14] Switch to rust edition 2021 --- Cargo.toml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/Cargo.toml b/Cargo.toml index 17cc17a..8a660be 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -11,7 +11,7 @@ keywords = ["testing", "quickcheck", "property", "shrinking", "fuzz"] categories = ["development-tools::testing"] license = "Unlicense/MIT" exclude = ["/.travis.yml", "/Makefile", "/ctags.rust", "/session.vim"] -edition = "2018" +edition = "2021" [workspace] members = ["quickcheck_macros"] From 51330958c82446e678a90221b3f221e639e868e6 Mon Sep 17 00:00:00 2001 From: Alexis Sellier Date: Sat, 3 Dec 2022 18:01:45 +0100 Subject: [PATCH 03/14] Remove logging features --- Cargo.toml | 7 ------- README.md | 13 ------------- src/lib.rs | 20 -------------------- src/tester.rs | 9 +-------- 4 files changed, 1 insertion(+), 48 deletions(-) diff --git a/Cargo.toml b/Cargo.toml index 8a660be..8e626fa 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -16,15 +16,8 @@ edition = "2021" [workspace] members = ["quickcheck_macros"] -[features] -default = ["regex", "use_logging"] -use_logging = ["log", "env_logger"] -regex = ["env_logger/regex"] - [lib] name = "quickcheck" [dependencies] -env_logger = { version = "0.8.2", default-features = false, optional = true } -log = { version = "0.4", optional = true } rand = { version = "0.8", default-features = false, features = ["getrandom", "small_rng"] } diff --git a/README.md b/README.md index 2546a63..f27c177 100644 --- a/README.md +++ b/README.md @@ -114,19 +114,6 @@ 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.46.0`. diff --git a/src/lib.rs b/src/lib.rs index 58dc99d..a7336f5 100644 --- a/src/lib.rs +++ b/src/lib.rs @@ -66,26 +66,6 @@ macro_rules! quickcheck { ) } -#[cfg(feature = "use_logging")] -fn env_logger_init() -> Result<(), log::SetLoggerError> { - env_logger::try_init() -} -#[cfg(feature = "use_logging")] -macro_rules! info { - ($($tt:tt)*) => { - log::info!($($tt)*) - }; -} - -#[cfg(not(feature = "use_logging"))] -fn env_logger_init() {} -#[cfg(not(feature = "use_logging"))] -macro_rules! info { - ($($_ignore:tt)*) => { - () - }; -} - mod arbitrary; mod tester; diff --git a/src/tester.rs b/src/tester.rs index aa5430d..49cc1e2 100644 --- a/src/tester.rs +++ b/src/tester.rs @@ -135,10 +135,6 @@ impl QuickCheck { /// It is appropriate to use this method with Rust's unit testing /// infrastructure. /// - /// Note that if the environment variable `RUST_LOG` is set to enable - /// `info` level log messages for the `quickcheck` crate, then this will - /// include output on how many QuickCheck tests were passed. - /// /// # Example /// /// ```rust @@ -157,16 +153,13 @@ impl QuickCheck { where A: Testable, { - // Ignore log init failures, implying it has already been done. - let _ = crate::env_logger_init(); - let n_tests_passed = match self.quicktest(f) { Ok(n_tests_passed) => n_tests_passed, Err(result) => panic!("{}", result.failed_msg()), }; if n_tests_passed >= self.min_tests_passed { - info!("(Passed {} QuickCheck tests.)", n_tests_passed) + // Do nothing. } else { panic!( "(Unable to generate enough tests, {} not discarded.)", From fde9e5c402a6090a6452eb3158ffa386aa67e375 Mon Sep 17 00:00:00 2001 From: Mathspy Date: Mon, 6 Jun 2022 19:46:17 -0400 Subject: [PATCH 04/14] Add support for arrays powered by const generics --- src/arbitrary.rs | 20 ++++++++++++++++++++ 1 file changed, 20 insertions(+) diff --git a/src/arbitrary.rs b/src/arbitrary.rs index e3d5ec5..ce29c37 100644 --- a/src/arbitrary.rs +++ b/src/arbitrary.rs @@ -248,6 +248,26 @@ impl_arb_for_tuples! { (H, 7), } +impl Arbitrary for [A; N] { + fn arbitrary(g: &mut Gen) -> [A; N] { + [(); N].map(|_| A::arbitrary(g)) + } + + fn shrink(&self) -> Box> { + let cloned = self.clone(); + let iter = (0..N).flat_map(move |n| { + let cloned = cloned.clone(); + cloned[n].shrink().map(move |shr_value| { + let mut result = cloned.clone(); + result[n] = shr_value; + result + }) + }); + + Box::new(iter) + } +} + impl Arbitrary for Vec { fn arbitrary(g: &mut Gen) -> Vec { let size = { From f1277e6733891c1c2594dc9c7fa389b7d19c4ce0 Mon Sep 17 00:00:00 2001 From: Mathspy Date: Mon, 6 Jun 2022 19:55:42 -0400 Subject: [PATCH 05/14] Add shrinking tests for arrays Heavily based on the tuples and vectors tests --- src/arbitrary.rs | 39 +++++++++++++++++++++++++++++++++++++++ 1 file changed, 39 insertions(+) diff --git a/src/arbitrary.rs b/src/arbitrary.rs index ce29c37..b1e29de 100644 --- a/src/arbitrary.rs +++ b/src/arbitrary.rs @@ -1431,6 +1431,45 @@ mod test { eq(Wrapping(0i32), vec![]); } + #[test] + fn arrays() { + eq([false, false], vec![]); + eq([true, false], vec![[false, false]]); + eq([true, true], vec![[false, true], [true, false]]); + + eq([false, false, false], vec![]); + eq([true, false, false], vec![[false, false, false]]); + eq( + [true, true, false], + vec![[false, true, false], [true, false, false]], + ); + + eq([false, false, false, false], vec![]); + eq([true, false, false, false], vec![[false, false, false, false]]); + eq( + [true, true, false, false], + vec![[false, true, false, false], [true, false, false, false]], + ); + + eq( + { + let it: [isize; 0] = []; + it + }, + vec![], + ); + eq( + { + let it: [[isize; 0]; 1] = [[]]; + it + }, + vec![], + ); + eq([1isize; 1], vec![[0; 1]]); + eq([11isize; 1], vec![[0; 1], [6; 1], [9; 1], [10; 1]]); + eq([3isize, 5], vec![[0, 5], [2, 5], [3, 0], [3, 3], [3, 4]]); + } + #[test] fn vecs() { eq( From 0dfab711d59f9095be7e3e91f9c3e45f9d88bbc6 Mon Sep 17 00:00:00 2001 From: Alex Touchet Date: Thu, 5 May 2022 22:01:17 -0700 Subject: [PATCH 06/14] Use SPDX license format and remove Travis --- Cargo.toml | 4 ++-- quickcheck_macros/Cargo.toml | 2 +- 2 files changed, 3 insertions(+), 3 deletions(-) diff --git a/Cargo.toml b/Cargo.toml index 8e626fa..1520c23 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -9,8 +9,8 @@ repository = "https://github.com/BurntSushi/quickcheck" readme = "README.md" keywords = ["testing", "quickcheck", "property", "shrinking", "fuzz"] categories = ["development-tools::testing"] -license = "Unlicense/MIT" -exclude = ["/.travis.yml", "/Makefile", "/ctags.rust", "/session.vim"] +license = "Unlicense OR MIT" +exclude = ["/Makefile", "/ctags.rust", "/session.vim"] edition = "2021" [workspace] diff --git a/quickcheck_macros/Cargo.toml b/quickcheck_macros/Cargo.toml index 1d9041d..4b990e6 100644 --- a/quickcheck_macros/Cargo.toml +++ b/quickcheck_macros/Cargo.toml @@ -8,7 +8,7 @@ homepage = "https://github.com/BurntSushi/quickcheck" repository = "https://github.com/BurntSushi/quickcheck" readme = "../README.md" keywords = ["testing", "quickcheck", "property", "shrinking", "fuzz"] -license = "Unlicense/MIT" +license = "Unlicense OR MIT" autotests = false [lib] From ea44fb1b74dd1f56bb3c2dc3e5a6576c3bb2974f Mon Sep 17 00:00:00 2001 From: Alex Touchet Date: Mon, 21 Nov 2022 20:06:17 -0800 Subject: [PATCH 07/14] Fix crates.io badge --- README.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/README.md b/README.md index f27c177..d144a86 100644 --- a/README.md +++ b/README.md @@ -15,7 +15,7 @@ the input space quickly. (It should be the same strategy used in Haskell](https://hackage.haskell.org/package/QuickCheck).) [![Build status](https://github.com/BurntSushi/quickcheck/workflows/ci/badge.svg)](https://github.com/BurntSushi/quickcheck/actions) -[![](https://meritbadge.herokuapp.com/quickcheck)](https://crates.io/crates/quickcheck) +[![crates.io](https://img.shields.io/crates/v/quickcheck.svg)](https://crates.io/crates/quickcheck) Dual-licensed under MIT or the [UNLICENSE](https://unlicense.org). From 2d4ba3b394e8120a9f51f8b4140d6e473fc13b24 Mon Sep 17 00:00:00 2001 From: David Rusu Date: Wed, 29 Dec 2021 23:15:04 -0500 Subject: [PATCH 08/14] Only build debug representations of arguments > This cuts my prop test run-times nearly in half! --- src/tester.rs | 32 +++++++++++++------------------- 1 file changed, 13 insertions(+), 19 deletions(-) diff --git a/src/tester.rs b/src/tester.rs index 49cc1e2..7803ab2 100644 --- a/src/tester.rs +++ b/src/tester.rs @@ -188,7 +188,7 @@ pub fn quickcheck(f: A) { #[derive(Clone, Debug)] pub struct TestResult { status: Status, - arguments: Vec, + arguments: Option>, err: Option, } @@ -223,7 +223,7 @@ impl TestResult { /// When a test is discarded, `quickcheck` will replace it with a /// fresh one (up to a certain limit). pub fn discard() -> TestResult { - TestResult { status: Discard, arguments: vec![], err: None } + TestResult { status: Discard, arguments: None, err: None } } /// Converts a `bool` to a `TestResult`. A `true` value indicates that @@ -232,7 +232,7 @@ impl TestResult { pub fn from_bool(b: bool) -> TestResult { TestResult { status: if b { Pass } else { Fail }, - arguments: vec![], + arguments: None, err: None, } } @@ -265,16 +265,15 @@ impl TestResult { } fn failed_msg(&self) -> String { + let arguments_msg = match self.arguments { + None => String::from("No Arguments Provided"), + Some(ref args) => format!("Arguments: ({})", args.join(", ")), + }; match self.err { - None => format!( - "[quickcheck] TEST FAILED. Arguments: ({})", - self.arguments.join(", ") - ), + None => format!("[quickcheck] TEST FAILED. {}", arguments_msg), Some(ref err) => format!( - "[quickcheck] TEST FAILED (runtime error). \ - Arguments: ({})\nError: {}", - self.arguments.join(", "), - err + "[quickcheck] TEST FAILED (runtime error). {}\nError: {}", + arguments_msg, err ), } } @@ -349,7 +348,7 @@ impl r, Fail => { @@ -428,7 +422,7 @@ mod test { .quicktest(thetest as fn(vals: Vec) -> bool) .unwrap_err(); let expected_argument = format!("{:?}", [true, true]); - assert_eq!(failing_case.arguments, vec![expected_argument]); + assert_eq!(failing_case.arguments, Some(vec![expected_argument])); } #[test] From 01abd13b544c567c08f43b4e51a028014ce8ada5 Mon Sep 17 00:00:00 2001 From: Aleksa Sarai Date: Sun, 23 May 2021 11:50:34 +1000 Subject: [PATCH 09/14] Add `Gen::fill` method This allows users to fairly easily migrate from CoreRng::fill_byte() and similar constructions without requiring them to fuss around with a new SeedableRng (or copy-paste this boilerplate) when their slice is just full of Arbitrary-able types. Signed-off-by: Aleksa Sarai --- src/arbitrary.rs | 10 ++++++++++ 1 file changed, 10 insertions(+) diff --git a/src/arbitrary.rs b/src/arbitrary.rs index b1e29de..abb1144 100644 --- a/src/arbitrary.rs +++ b/src/arbitrary.rs @@ -64,6 +64,16 @@ impl Gen { slice.choose(&mut self.rng) } + /// Fill a mutable slice of any Arbitrary-compatible type with Arbitrary + /// values. + pub fn fill(&mut self, mut slice: S) + where + T: Arbitrary, + S: AsMut<[T]>, + { + slice.as_mut().fill_with(|| T::arbitrary(self)) + } + fn gen(&mut self) -> T where rand::distributions::Standard: rand::distributions::Distribution, From 207efd10f09fced41c87e4daf2a64a583eb1cce1 Mon Sep 17 00:00:00 2001 From: Jakob Schikowski Date: Tue, 9 Feb 2021 02:13:10 +0100 Subject: [PATCH 10/14] api: add Gen::set_size and Gen::from_seed The seed still can't be set by QuickCheck users, but the new Gen constructor is useful for other crates that use QuickCheck only for its Arbitrary trait. Closes #277 --- src/arbitrary.rs | 24 +++++++++++++++++++++--- src/tester.rs | 2 +- 2 files changed, 22 insertions(+), 4 deletions(-) diff --git a/src/arbitrary.rs b/src/arbitrary.rs index abb1144..2e61c19 100644 --- a/src/arbitrary.rs +++ b/src/arbitrary.rs @@ -41,15 +41,33 @@ pub struct Gen { } impl Gen { - /// Returns a `Gen` with the given size configuration. + pub(crate) const DEFAULT_SIZE: usize = 100; + + /// Returns a `Gen` with a random seed and the given size configuration. + pub fn new(size: usize) -> Gen { + Gen { rng: rand::rngs::SmallRng::from_entropy(), size } + } + + /// Returns a `Gen` with the given seed and a default size configuration. + /// + /// Two `Gen`s created with the same seed will generate the same values. Though the values + /// may vary between QuickCheck releases. + pub fn from_seed(seed: u64) -> Gen { + Gen { + rng: rand::rngs::SmallRng::seed_from_u64(seed), + size: Self::DEFAULT_SIZE, + } + } + + /// Sets the size configuration for this generator. /// /// The `size` parameter controls the size of random values generated. /// For example, it specifies the maximum length of a randomly generated /// vector, but is and should not be used to control the range of a /// randomly generated number. (Unless that number is used to control the /// size of a data structure.) - pub fn new(size: usize) -> Gen { - Gen { rng: rand::rngs::SmallRng::from_entropy(), size } + pub fn set_size(&mut self, size: usize) { + self.size = size; } /// Returns the size configured with this generator. diff --git a/src/tester.rs b/src/tester.rs index 7803ab2..c393e10 100644 --- a/src/tester.rs +++ b/src/tester.rs @@ -33,7 +33,7 @@ fn qc_max_tests() -> u64 { } fn qc_gen_size() -> usize { - let default = 100; + let default = Gen::DEFAULT_SIZE; match env::var("QUICKCHECK_GENERATOR_SIZE") { Ok(val) => val.parse().unwrap_or(default), Err(_) => default, From df995c53904ffee22b8ff75a92bd38c2f9bf796a Mon Sep 17 00:00:00 2001 From: Alexis Sellier Date: Sat, 3 Dec 2022 18:22:04 +0100 Subject: [PATCH 11/14] Add `Gen: Default` instance --- src/arbitrary.rs | 6 ++++++ 1 file changed, 6 insertions(+) diff --git a/src/arbitrary.rs b/src/arbitrary.rs index 2e61c19..d3a34f0 100644 --- a/src/arbitrary.rs +++ b/src/arbitrary.rs @@ -108,6 +108,12 @@ impl Gen { } } +impl Default for Gen { + fn default() -> Self { + Self::new(Gen::DEFAULT_SIZE) + } +} + /// Creates a shrinker with zero elements. pub fn empty_shrinker() -> Box> { Box::new(empty()) From f4ab9b10e1d2fe5b2a644864157091c4b3255cf0 Mon Sep 17 00:00:00 2001 From: Alexis Sellier Date: Sat, 3 Dec 2022 18:27:36 +0100 Subject: [PATCH 12/14] Rename crate to `qcheck` --- Cargo.toml | 23 +- README.md | 452 +----------------- .../Cargo.toml | 15 +- .../LICENSE-MIT | 0 .../UNLICENSE | 0 .../examples/attribute.rs | 6 +- .../src/lib.rs | 4 +- qcheck/Cargo.toml | 23 + qcheck/README.md | 1 + .../examples}/btree_set_range.rs | 2 +- .../examples}/out_of_bounds.rs | 2 +- {examples => qcheck/examples}/reverse.rs | 2 +- .../examples}/reverse_single.rs | 2 +- {examples => qcheck/examples}/sieve.rs | 2 +- {examples => qcheck/examples}/sort.rs | 4 +- {src => qcheck/src}/arbitrary.rs | 0 {src => qcheck/src}/lib.rs | 18 +- {src => qcheck/src}/tester.rs | 2 +- {src => qcheck/src}/tests.rs | 0 {quickcheck_macros => qcheck}/tests/macro.rs | 12 +- quickcheck_macros/COPYING | 1 - 21 files changed, 55 insertions(+), 516 deletions(-) rename {quickcheck_macros => qcheck-macros}/Cargo.toml (53%) rename {quickcheck_macros => qcheck-macros}/LICENSE-MIT (100%) rename {quickcheck_macros => qcheck-macros}/UNLICENSE (100%) rename {quickcheck_macros => qcheck-macros}/examples/attribute.rs (75%) rename {quickcheck_macros => qcheck-macros}/src/lib.rs (95%) create mode 100644 qcheck/Cargo.toml create mode 120000 qcheck/README.md rename {examples => qcheck/examples}/btree_set_range.rs (97%) rename {examples => qcheck/examples}/out_of_bounds.rs (88%) rename {examples => qcheck/examples}/reverse.rs (92%) rename {examples => qcheck/examples}/reverse_single.rs (90%) rename {examples => qcheck/examples}/sieve.rs (97%) rename {examples => qcheck/examples}/sort.rs (97%) rename {src => qcheck/src}/arbitrary.rs (100%) rename {src => qcheck/src}/lib.rs (72%) rename {src => qcheck/src}/tester.rs (99%) rename {src => qcheck/src}/tests.rs (100%) rename {quickcheck_macros => qcheck}/tests/macro.rs (77%) delete mode 120000 quickcheck_macros/COPYING diff --git a/Cargo.toml b/Cargo.toml index 1520c23..9135e2d 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -1,23 +1,2 @@ -[package] -name = "quickcheck" -version = "1.0.3" #:version -authors = ["Andrew Gallant "] -description = "Automatic property based testing with shrinking." -documentation = "https://docs.rs/quickcheck" -homepage = "https://github.com/BurntSushi/quickcheck" -repository = "https://github.com/BurntSushi/quickcheck" -readme = "README.md" -keywords = ["testing", "quickcheck", "property", "shrinking", "fuzz"] -categories = ["development-tools::testing"] -license = "Unlicense OR MIT" -exclude = ["/Makefile", "/ctags.rust", "/session.vim"] -edition = "2021" - [workspace] -members = ["quickcheck_macros"] - -[lib] -name = "quickcheck" - -[dependencies] -rand = { version = "0.8", default-features = false, features = ["getrandom", "small_rng"] } +members = ["qcheck", "qcheck-macros"] diff --git a/README.md b/README.md index d144a86..75a4d71 100644 --- a/README.md +++ b/README.md @@ -10,51 +10,12 @@ 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](https://hackage.haskell.org/package/QuickCheck).) - -[![Build status](https://github.com/BurntSushi/quickcheck/workflows/ci/badge.svg)](https://github.com/BurntSushi/quickcheck/actions) -[![crates.io](https://img.shields.io/crates/v/quickcheck.svg)](https://crates.io/crates/quickcheck) - -Dual-licensed under MIT or the [UNLICENSE](https://unlicense.org). - +the input space quickly. ### Documentation The API is fully documented: -[https://docs.rs/quickcheck](https://docs.rs/quickcheck). - - -### Simple example - -Here's an example that tests a function that reverses a vector: - -```rust -#[cfg(test)] -#[macro_use] -extern crate quickcheck; - -fn reverse(xs: &[T]) -> Vec { - let mut rev = vec!(); - for x in xs.iter() { - rev.insert(0, x.clone()) - } - rev -} - -#[cfg(test)] -mod tests { - quickcheck! { - fn prop(xs: Vec) -> bool { - xs == reverse(&reverse(&xs)) - } - } -} -``` - -This example uses the `quickcheck!` macro, which is backwards compatible with -old versions of Rust. +[https://docs.rs/qcheck](https://docs.rs/qcheck). ### The `#[quickcheck]` attribute @@ -65,12 +26,6 @@ To use the `#[quickcheck]` attribute, you must import the `quickcheck` macro from the `quickcheck_macros` crate: ```rust -#[cfg(test)] -extern crate quickcheck; -#[cfg(test)] -#[macro_use(quickcheck)] -extern crate quickcheck_macros; - #[cfg(test)] mod tests { fn reverse(xs: &[T]) -> Vec { @@ -88,191 +43,6 @@ mod tests { } ``` - -### Installation - -`quickcheck` is on `crates.io`, so you can include it in your project like so: - -```toml -[dependencies] -quickcheck = "1" -``` - -If you're only using `quickcheck` in your test code, then you can add it as a -development dependency instead: - -```toml -[dev-dependencies] -quickcheck = "1" -``` - -If you want to use the `#[quickcheck]` attribute, then add `quickcheck_macros` - -```toml -[dev-dependencies] -quickcheck = "1" -quickcheck_macros = "1" -``` - -### Minimum Rust version policy - -This crate's minimum supported `rustc` version is `1.46.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`](https://docs.rs/proptest) crate is inspired by the -[Hypothesis](https://hypothesis.works) framework for Python. -You can read a comparison between `proptest` and `quickcheck` -[here](https://github.com/AltSysrq/proptest/blob/master/proptest/README.md#differences-between-quickcheck-and-proptest) -and -[here](https://github.com/AltSysrq/proptest/issues/15#issuecomment-348382287). -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](https://rust-fuzz.github.io/book/introduction.html) -and the -[`arbitrary`](https://crates.io/crates/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: - -```rust -pub fn quickcheck(f: A) { - // elided -} -``` - -So `quickcheck` can test any value with a type that satisfies the `Testable` -trait. Great, so what is this `Testable` business? - -```rust -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: - -```rust -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! - -```rust -impl 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`: - -```rust -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. - -```rust -fn prop(xs: Vec) -> TestResult { - if xs.len() != 1 { - return TestResult::discard() - } - TestResult::from_bool(xs == reverse(&xs)) -} -quickcheck(prop as fn(Vec) -> TestResult); -``` - -(A full working program for this example is in -[`examples/reverse_single.rs`](https://github.com/BurntSushi/quickcheck/blob/master/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](https://docs.rs/quickcheck/*/quickcheck/struct.TestResult.html). -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`](https://docs.rs/quickcheck/*/quickcheck/struct.QuickCheck.html#method.tests) -and [`QuickCheck::max_tests`](https://docs.rs/quickcheck/*/quickcheck/struct.QuickCheck.html#method.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 @@ -314,48 +84,6 @@ guaranteed to get this counter-example every time: 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`](https://github.com/rust-fuzz/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. - -```bash -#!/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 @@ -367,16 +95,6 @@ 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 @@ -393,7 +111,7 @@ In order to generate a random `Point` instance, you need to implement the trait `Arbitrary` for the struct `Point`: ```rust -use quickcheck::{Arbitrary, Gen}; +use qcheck::{Arbitrary, Gen}; impl Arbitrary for Point { fn arbitrary(g: &mut Gen) -> Point { @@ -405,166 +123,6 @@ impl Arbitrary for Point { } ``` +## License -### Case study: The Sieve of Eratosthenes - -The [Sieve of Eratosthenes](https://en.wikipedia.org/wiki/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: - -```rust -fn sieve(n: usize) -> Vec { - 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: - -```rust -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: - -```rust -fn prop_all_prime(n: usize) -> bool { - sieve(n).into_iter().all(is_prime) -} -``` - -And finally, we need to invoke `quickcheck` with our property: - -```rust -fn main() { - quickcheck(prop_all_prime as fn(usize) -> bool); -} -``` - -A fully working source file with this code is in -[`examples/sieve.rs`](https://github.com/BurntSushi/quickcheck/blob/master/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: - -```rust -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: - -```rust -fn prop_prime_iff_in_the_sieve(n: usize) -> bool { - sieve(n) == (0..(n + 1)).filter(|&i| is_prime(i)).collect::>() -} -``` - -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: - -```rust -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](https://github.com/BurntSushi/quickcheck/issues/56) -for more details on why. +Dual-licensed under MIT or the [UNLICENSE](https://unlicense.org). diff --git a/quickcheck_macros/Cargo.toml b/qcheck-macros/Cargo.toml similarity index 53% rename from quickcheck_macros/Cargo.toml rename to qcheck-macros/Cargo.toml index 4b990e6..af86223 100644 --- a/quickcheck_macros/Cargo.toml +++ b/qcheck-macros/Cargo.toml @@ -1,25 +1,20 @@ [package] -name = "quickcheck_macros" -version = "1.0.0" #:version +name = "qcheck-macros" +version = "1.0.0" authors = ["Andrew Gallant "] description = "A macro attribute for quickcheck." -documentation = "https://docs.rs/quickcheck" -homepage = "https://github.com/BurntSushi/quickcheck" -repository = "https://github.com/BurntSushi/quickcheck" +documentation = "https://docs.rs/qcheck" +homepage = "https://github.com/cloudhead/qcheck" +repository = "https://github.com/cloudhead/qcheck" readme = "../README.md" keywords = ["testing", "quickcheck", "property", "shrinking", "fuzz"] license = "Unlicense OR MIT" autotests = false [lib] -name = "quickcheck_macros" -path = "src/lib.rs" proc-macro = true [dependencies] proc-macro2 = "1.0" quote = "1.0" syn = { version = "1.0", features = ["full"] } - -[dev-dependencies] -quickcheck = { path = "..", version = "1.0.0" } diff --git a/quickcheck_macros/LICENSE-MIT b/qcheck-macros/LICENSE-MIT similarity index 100% rename from quickcheck_macros/LICENSE-MIT rename to qcheck-macros/LICENSE-MIT diff --git a/quickcheck_macros/UNLICENSE b/qcheck-macros/UNLICENSE similarity index 100% rename from quickcheck_macros/UNLICENSE rename to qcheck-macros/UNLICENSE diff --git a/quickcheck_macros/examples/attribute.rs b/qcheck-macros/examples/attribute.rs similarity index 75% rename from quickcheck_macros/examples/attribute.rs rename to qcheck-macros/examples/attribute.rs index f00b2a9..9a05889 100644 --- a/quickcheck_macros/examples/attribute.rs +++ b/qcheck-macros/examples/attribute.rs @@ -1,9 +1,7 @@ #![allow(dead_code)] +extern crate qcheck_macros; -extern crate quickcheck; -extern crate quickcheck_macros; - -use quickcheck_macros::quickcheck; +use qcheck_macros::quickcheck; fn reverse(xs: &[T]) -> Vec { let mut rev = vec![]; diff --git a/quickcheck_macros/src/lib.rs b/qcheck-macros/src/lib.rs similarity index 95% rename from quickcheck_macros/src/lib.rs rename to qcheck-macros/src/lib.rs index 1072e77..3a0d057 100644 --- a/quickcheck_macros/src/lib.rs +++ b/qcheck-macros/src/lib.rs @@ -47,7 +47,7 @@ pub fn quickcheck(_args: TokenStream, input: TokenStream) -> TokenStream { #(#attrs)* fn #name() { #item_fn - ::quickcheck::quickcheck(#name as #fn_type) + ::qcheck::quickcheck(#name as #fn_type) } } } else { @@ -66,7 +66,7 @@ pub fn quickcheck(_args: TokenStream, input: TokenStream) -> TokenStream { #(#attrs)* fn #name() { #item_static - ::quickcheck::quickcheck(#name) + ::qcheck::quickcheck(#name) } } } diff --git a/qcheck/Cargo.toml b/qcheck/Cargo.toml new file mode 100644 index 0000000..26366da --- /dev/null +++ b/qcheck/Cargo.toml @@ -0,0 +1,23 @@ +[package] +name = "qcheck" +version = "1.0.0" +authors = ["Andrew Gallant "] +description = "Automatic property based testing with shrinking." +documentation = "https://docs.rs/qcheck" +homepage = "https://github.com/cloudhead/qcheck" +repository = "https://github.com/cloudhead/qcheck" +readme = "README.md" +keywords = ["testing", "quickcheck", "property", "shrinking", "fuzz"] +categories = ["development-tools::testing"] +license = "Unlicense OR MIT" +exclude = ["/Makefile", "/ctags.rust", "/session.vim"] +edition = "2021" + +[lib] +name = "qcheck" + +[dependencies] +rand = { version = "0.8", default-features = false, features = ["getrandom", "small_rng"] } + +[dev-dependencies] +qcheck-macros = { version = "1", path = "../qcheck-macros" } diff --git a/qcheck/README.md b/qcheck/README.md new file mode 120000 index 0000000..32d46ee --- /dev/null +++ b/qcheck/README.md @@ -0,0 +1 @@ +../README.md \ No newline at end of file diff --git a/examples/btree_set_range.rs b/qcheck/examples/btree_set_range.rs similarity index 97% rename from examples/btree_set_range.rs rename to qcheck/examples/btree_set_range.rs index 0e7418b..a3bed51 100644 --- a/examples/btree_set_range.rs +++ b/qcheck/examples/btree_set_range.rs @@ -1,7 +1,7 @@ use std::collections::BTreeSet; use std::ops::Bound::{self, *}; -use quickcheck::{quickcheck, TestResult}; +use qcheck::{quickcheck, TestResult}; /// Covers every `std::ops::Range*` plus variants with exclusive start. type RangeAny = (Bound, Bound); diff --git a/examples/out_of_bounds.rs b/qcheck/examples/out_of_bounds.rs similarity index 88% rename from examples/out_of_bounds.rs rename to qcheck/examples/out_of_bounds.rs index 10da938..5ea814b 100644 --- a/examples/out_of_bounds.rs +++ b/qcheck/examples/out_of_bounds.rs @@ -1,4 +1,4 @@ -use quickcheck::{quickcheck, TestResult}; +use qcheck::{quickcheck, TestResult}; fn main() { fn prop(length: usize, index: usize) -> TestResult { diff --git a/examples/reverse.rs b/qcheck/examples/reverse.rs similarity index 92% rename from examples/reverse.rs rename to qcheck/examples/reverse.rs index fff6e71..2c17e76 100644 --- a/examples/reverse.rs +++ b/qcheck/examples/reverse.rs @@ -1,4 +1,4 @@ -use quickcheck::quickcheck; +use qcheck::quickcheck; fn reverse(xs: &[T]) -> Vec { let mut rev = vec![]; diff --git a/examples/reverse_single.rs b/qcheck/examples/reverse_single.rs similarity index 90% rename from examples/reverse_single.rs rename to qcheck/examples/reverse_single.rs index dcfffbe..05b0c6b 100644 --- a/examples/reverse_single.rs +++ b/qcheck/examples/reverse_single.rs @@ -1,4 +1,4 @@ -use quickcheck::{quickcheck, TestResult}; +use qcheck::{quickcheck, TestResult}; fn reverse(xs: &[T]) -> Vec { let mut rev = vec![]; diff --git a/examples/sieve.rs b/qcheck/examples/sieve.rs similarity index 97% rename from examples/sieve.rs rename to qcheck/examples/sieve.rs index f05b8e3..3696914 100644 --- a/examples/sieve.rs +++ b/qcheck/examples/sieve.rs @@ -1,4 +1,4 @@ -use quickcheck::quickcheck; +use qcheck::quickcheck; fn sieve(n: usize) -> Vec { if n <= 1 { diff --git a/examples/sort.rs b/qcheck/examples/sort.rs similarity index 97% rename from examples/sort.rs rename to qcheck/examples/sort.rs index 574d803..9c23f62 100644 --- a/examples/sort.rs +++ b/qcheck/examples/sort.rs @@ -1,7 +1,7 @@ // This is a buggy quick sort implementation, QuickCheck will find the bug for // you. -use quickcheck::quickcheck; +use qcheck::quickcheck; fn smaller_than(xs: &[T], pivot: &T) -> Vec { xs.iter().filter(|&x| *x < *pivot).cloned().collect() @@ -40,7 +40,7 @@ fn main() { fn keeps_length(xs: Vec) -> bool { xs.len() == sort(&xs).len() } - quickcheck(keeps_length as fn(Vec) -> bool); + quickcheck(keeps_length as fn(Vec) -> bool); quickcheck(is_sorted as fn(Vec) -> bool) } diff --git a/src/arbitrary.rs b/qcheck/src/arbitrary.rs similarity index 100% rename from src/arbitrary.rs rename to qcheck/src/arbitrary.rs diff --git a/src/lib.rs b/qcheck/src/lib.rs similarity index 72% rename from src/lib.rs rename to qcheck/src/lib.rs index a7336f5..44f87a2 100644 --- a/src/lib.rs +++ b/qcheck/src/lib.rs @@ -1,19 +1,3 @@ -/*! -This crate is a port of -[Haskell's QuickCheck](https://hackage.haskell.org/package/QuickCheck). - -For detailed examples, please see the -[README](https://github.com/BurntSushi/quickcheck). - -# 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. -*/ - pub use crate::arbitrary::{empty_shrinker, single_shrinker, Arbitrary, Gen}; pub use crate::tester::{quickcheck, QuickCheck, TestResult, Testable}; @@ -29,7 +13,7 @@ pub use crate::tester::{quickcheck, QuickCheck, TestResult, Testable}; /// # Example /// /// ```rust -/// # #[macro_use] extern crate quickcheck; fn main() { +/// # #[macro_use] extern crate qcheck; fn main() { /// quickcheck! { /// fn prop_reverse_reverse(xs: Vec) -> bool { /// let rev: Vec<_> = xs.clone().into_iter().rev().collect(); diff --git a/src/tester.rs b/qcheck/src/tester.rs similarity index 99% rename from src/tester.rs rename to qcheck/src/tester.rs index c393e10..270fd90 100644 --- a/src/tester.rs +++ b/qcheck/src/tester.rs @@ -138,7 +138,7 @@ impl QuickCheck { /// # Example /// /// ```rust - /// use quickcheck::QuickCheck; + /// use qcheck::QuickCheck; /// /// fn prop_reverse_reverse() { /// fn revrev(xs: Vec) -> bool { diff --git a/src/tests.rs b/qcheck/src/tests.rs similarity index 100% rename from src/tests.rs rename to qcheck/src/tests.rs diff --git a/quickcheck_macros/tests/macro.rs b/qcheck/tests/macro.rs similarity index 77% rename from quickcheck_macros/tests/macro.rs rename to qcheck/tests/macro.rs index 19358d2..90d6337 100644 --- a/quickcheck_macros/tests/macro.rs +++ b/qcheck/tests/macro.rs @@ -1,10 +1,10 @@ #![allow(non_upper_case_globals)] -extern crate quickcheck; -extern crate quickcheck_macros; +extern crate qcheck; +extern crate qcheck_macros; -use quickcheck::TestResult; -use quickcheck_macros::quickcheck; +use qcheck::TestResult; +use qcheck_macros::quickcheck; #[quickcheck] fn min(x: isize, y: isize) -> TestResult { @@ -17,7 +17,9 @@ fn min(x: isize, y: isize) -> TestResult { #[quickcheck] #[should_panic] -fn fail_fn() -> bool { false } +fn fail_fn() -> bool { + false +} #[quickcheck] static static_bool: bool = true; diff --git a/quickcheck_macros/COPYING b/quickcheck_macros/COPYING deleted file mode 120000 index 012065c..0000000 --- a/quickcheck_macros/COPYING +++ /dev/null @@ -1 +0,0 @@ -../COPYING \ No newline at end of file From 0d0b96230aa6acec60e69f2b9588eead70caaa1b Mon Sep 17 00:00:00 2001 From: Alexis Sellier Date: Sat, 3 Dec 2022 19:35:41 +0100 Subject: [PATCH 13/14] Use defaults for `rustfmt` --- qcheck-macros/src/lib.rs | 3 +- qcheck/examples/btree_set_range.rs | 4 +- qcheck/src/arbitrary.rs | 113 ++++++++++++++--------------- qcheck/src/tester.rs | 21 +++++- qcheck/src/tests.rs | 16 ++-- rustfmt.toml | 2 - 6 files changed, 81 insertions(+), 78 deletions(-) delete mode 100644 rustfmt.toml diff --git a/qcheck-macros/src/lib.rs b/qcheck-macros/src/lib.rs index 3a0d057..ce7b59c 100644 --- a/qcheck-macros/src/lib.rs +++ b/qcheck-macros/src/lib.rs @@ -72,8 +72,7 @@ pub fn quickcheck(_args: TokenStream, input: TokenStream) -> TokenStream { } _ => { let span = proc_macro2::TokenStream::from(input).span(); - let msg = - "#[quickcheck] is only supported on statics and functions"; + let msg = "#[quickcheck] is only supported on statics and functions"; syn::parse::Error::new(span, msg).to_compile_error() } diff --git a/qcheck/examples/btree_set_range.rs b/qcheck/examples/btree_set_range.rs index a3bed51..081de1a 100644 --- a/qcheck/examples/btree_set_range.rs +++ b/qcheck/examples/btree_set_range.rs @@ -44,9 +44,7 @@ fn check_range(set: BTreeSet, range: RangeAny) -> TestResult { TestResult::discard() } else { let xs: BTreeSet<_> = set.range(range).cloned().collect(); - TestResult::from_bool( - set.iter().all(|x| range.contains(x) == xs.contains(x)), - ) + TestResult::from_bool(set.iter().all(|x| range.contains(x) == xs.contains(x))) } } diff --git a/qcheck/src/arbitrary.rs b/qcheck/src/arbitrary.rs index d3a34f0..740f395 100644 --- a/qcheck/src/arbitrary.rs +++ b/qcheck/src/arbitrary.rs @@ -1,24 +1,15 @@ #![allow(clippy::new_ret_no_self)] #![allow(clippy::or_fun_call)] use std::char; -use std::collections::{ - BTreeMap, BTreeSet, BinaryHeap, HashMap, HashSet, LinkedList, VecDeque, -}; +use std::collections::{BTreeMap, BTreeSet, BinaryHeap, HashMap, HashSet, LinkedList, VecDeque}; use std::env; use std::ffi::{CString, OsString}; use std::hash::{BuildHasher, Hash}; use std::iter::{empty, once}; -use std::net::{ - IpAddr, Ipv4Addr, Ipv6Addr, SocketAddr, SocketAddrV4, SocketAddrV6, -}; +use std::net::{IpAddr, Ipv4Addr, Ipv6Addr, SocketAddr, SocketAddrV4, SocketAddrV6}; use std::num::Wrapping; -use std::num::{ - NonZeroU128, NonZeroU16, NonZeroU32, NonZeroU64, NonZeroU8, NonZeroUsize, -}; -use std::ops::{ - Bound, Range, RangeFrom, RangeFull, RangeInclusive, RangeTo, - RangeToInclusive, -}; +use std::num::{NonZeroU128, NonZeroU16, NonZeroU32, NonZeroU64, NonZeroU8, NonZeroUsize}; +use std::ops::{Bound, Range, RangeFrom, RangeFull, RangeInclusive, RangeTo, RangeToInclusive}; use std::path::PathBuf; use std::sync::Arc; use std::time::{Duration, SystemTime, UNIX_EPOCH}; @@ -45,7 +36,10 @@ impl Gen { /// Returns a `Gen` with a random seed and the given size configuration. pub fn new(size: usize) -> Gen { - Gen { rng: rand::rngs::SmallRng::from_entropy(), size } + Gen { + rng: rand::rngs::SmallRng::from_entropy(), + size, + } } /// Returns a `Gen` with the given seed and a default size configuration. @@ -425,16 +419,14 @@ impl Arbitrary for BTreeMap { fn shrink(&self) -> Box>> { let vec: Vec<(K, V)> = self.clone().into_iter().collect(); Box::new( - vec.shrink().map(|v| v.into_iter().collect::>()), + vec.shrink() + .map(|v| v.into_iter().collect::>()), ) } } -impl< - K: Arbitrary + Eq + Hash, - V: Arbitrary, - S: BuildHasher + Default + Clone + 'static, - > Arbitrary for HashMap +impl Arbitrary + for HashMap { fn arbitrary(g: &mut Gen) -> Self { let vec: Vec<(K, V)> = Arbitrary::arbitrary(g); @@ -468,13 +460,14 @@ impl Arbitrary for BinaryHeap { fn shrink(&self) -> Box>> { let vec: Vec = self.clone().into_iter().collect(); Box::new( - vec.shrink().map(|v| v.into_iter().collect::>()), + vec.shrink() + .map(|v| v.into_iter().collect::>()), ) } } -impl - Arbitrary for HashSet +impl Arbitrary + for HashSet { fn arbitrary(g: &mut Gen) -> Self { let vec: Vec = Arbitrary::arbitrary(g); @@ -496,7 +489,8 @@ impl Arbitrary for LinkedList { fn shrink(&self) -> Box>> { let vec: Vec = self.clone().into_iter().collect(); Box::new( - vec.shrink().map(|v| v.into_iter().collect::>()), + vec.shrink() + .map(|v| v.into_iter().collect::>()), ) } } @@ -567,8 +561,7 @@ impl Arbitrary for PathBuf { fn arbitrary(g: &mut Gen) -> PathBuf { // use some real directories as guesses, so we may end up with // actual working directories in case that is relevant. - let here = - env::current_dir().unwrap_or(PathBuf::from("/test/directory")); + let here = env::current_dir().unwrap_or(PathBuf::from("/test/directory")); let temp = env::temp_dir(); #[allow(deprecated)] let home = env::home_dir().unwrap_or(PathBuf::from("/home/user")); @@ -673,10 +666,8 @@ impl Arbitrary for CString { // Use the implementation for a vec here, but make sure null characters // are filtered out. Box::new(VecShrinker::new(self.as_bytes().to_vec()).map(|bytes| { - CString::new( - bytes.into_iter().filter(|&c| c != 0).collect::>(), - ) - .expect("null characters should have been filtered out") + CString::new(bytes.into_iter().filter(|&c| c != 0).collect::>()) + .expect("null characters should have been filtered out") })) } } @@ -701,11 +692,10 @@ impl Arbitrary for char { 60..=84 => { // Characters often used in programming languages g.choose(&[ - ' ', ' ', ' ', '\t', '\n', '~', '`', '!', '@', '#', '$', - '%', '^', '&', '*', '(', ')', '_', '-', '=', '+', '[', - ']', '{', '}', ':', ';', '\'', '"', '\\', '|', ',', '<', - '>', '.', '/', '?', '0', '1', '2', '3', '4', '5', '6', - '7', '8', '9', + ' ', ' ', ' ', '\t', '\n', '~', '`', '!', '@', '#', '$', '%', '^', '&', '*', + '(', ')', '_', '-', '=', '+', '[', ']', '{', '}', ':', ';', '\'', '"', '\\', + '|', ',', '<', '>', '.', '/', '?', '0', '1', '2', '3', '4', '5', '6', '7', '8', + '9', ]) .unwrap() .to_owned() @@ -796,11 +786,7 @@ macro_rules! unsigned_shrinker { if x == 0 { super::empty_shrinker() } else { - Box::new( - vec![0] - .into_iter() - .chain(UnsignedShrinker { x, i: x / 2 }), - ) + Box::new(vec![0].into_iter().chain(UnsignedShrinker { x, i: x / 2 })) } } } @@ -878,9 +864,7 @@ macro_rules! signed_shrinker { impl Iterator for SignedShrinker { type Item = $ty; fn next(&mut self) -> Option<$ty> { - if self.x == <$ty>::MIN - || (self.x - self.i).abs() < self.x.abs() - { + if self.x == <$ty>::MIN || (self.x - self.i).abs() < self.x.abs() { let result = Some(self.x - self.i); self.i /= 2; result @@ -928,7 +912,15 @@ macro_rules! float_problem_values { ($path:path) => {{ // hack. see: https://github.com/rust-lang/rust/issues/48067 use $path as p; - &[p::NAN, p::NEG_INFINITY, p::MIN, -0., 0., p::MAX, p::INFINITY] + &[ + p::NAN, + p::NEG_INFINITY, + p::MIN, + -0., + 0., + p::MAX, + p::INFINITY, + ] }}; } @@ -973,11 +965,7 @@ macro_rules! unsigned_non_zero_shrinker { if x == 1 { super::empty_shrinker() } else { - Box::new( - std::iter::once(1).chain( - UnsignedNonZeroShrinker { x, i: x / 2 }, - ), - ) + Box::new(std::iter::once(1).chain(UnsignedNonZeroShrinker { x, i: x / 2 })) } } } @@ -1050,12 +1038,8 @@ impl Arbitrary for Bound { } fn shrink(&self) -> Box>> { match *self { - Bound::Included(ref x) => { - Box::new(x.shrink().map(Bound::Included)) - } - Bound::Excluded(ref x) => { - Box::new(x.shrink().map(Bound::Excluded)) - } + Bound::Included(ref x) => Box::new(x.shrink().map(Bound::Included)), + Bound::Excluded(ref x) => Box::new(x.shrink().map(Bound::Excluded)), Bound::Unbounded => empty_shrinker(), } } @@ -1067,7 +1051,9 @@ impl Arbitrary for Range { } fn shrink(&self) -> Box>> { Box::new( - (self.start.clone(), self.end.clone()).shrink().map(|(s, e)| s..e), + (self.start.clone(), self.end.clone()) + .shrink() + .map(|(s, e)| s..e), ) } } @@ -1327,7 +1313,10 @@ mod test { #[test] fn quads() { eq((false, false, false, false), vec![]); - eq((true, false, false, false), vec![(false, false, false, false)]); + eq( + (true, false, false, false), + vec![(false, false, false, false)], + ); eq( (true, true, false, false), vec![(false, true, false, false), (true, false, false, false)], @@ -1479,7 +1468,10 @@ mod test { ); eq([false, false, false, false], vec![]); - eq([true, false, false, false], vec![[false, false, false, false]]); + eq( + [true, false, false, false], + vec![[false, false, false, false]], + ); eq( [true, true, false, false], vec![[false, true, false, false], [true, false, false, false]], @@ -1521,7 +1513,10 @@ mod test { vec![vec![]], ); eq(vec![1isize], vec![vec![], vec![0]]); - eq(vec![11isize], vec![vec![], vec![0], vec![6], vec![9], vec![10]]); + eq( + vec![11isize], + vec![vec![], vec![0], vec![6], vec![9], vec![10]], + ); eq( vec![3isize, 5], vec![ diff --git a/qcheck/src/tester.rs b/qcheck/src/tester.rs index 270fd90..80f2702 100644 --- a/qcheck/src/tester.rs +++ b/qcheck/src/tester.rs @@ -64,7 +64,12 @@ impl QuickCheck { let max_tests = cmp::max(tests, qc_max_tests()); let min_tests_passed = qc_min_tests_passed(); - QuickCheck { tests, max_tests, min_tests_passed, gen } + QuickCheck { + tests, + max_tests, + min_tests_passed, + gen, + } } /// Set the random number generator to be used by QuickCheck. @@ -120,7 +125,9 @@ impl QuickCheck { } match f.result(&mut self.gen) { TestResult { status: Pass, .. } => n_tests_passed += 1, - TestResult { status: Discard, .. } => continue, + TestResult { + status: Discard, .. + } => continue, r @ TestResult { status: Fail, .. } => return Err(r), } } @@ -223,7 +230,11 @@ impl TestResult { /// When a test is discarded, `quickcheck` will replace it with a /// fresh one (up to a certain limit). pub fn discard() -> TestResult { - TestResult { status: Discard, arguments: None, err: None } + TestResult { + status: Discard, + arguments: None, + err: None, + } } /// Converts a `bool` to a `TestResult`. A `true` value indicates that @@ -430,7 +441,9 @@ mod test { fn t(_: i8) -> bool { true } - QuickCheck::new().gen(Gen::new(129)).quickcheck(t as fn(i8) -> bool); + QuickCheck::new() + .gen(Gen::new(129)) + .quickcheck(t as fn(i8) -> bool); } #[test] diff --git a/qcheck/src/tests.rs b/qcheck/src/tests.rs index 51317ea..977c453 100644 --- a/qcheck/src/tests.rs +++ b/qcheck/src/tests.rs @@ -52,9 +52,7 @@ fn reverse_single() { if xs.len() != 1 { TestResult::discard() } else { - TestResult::from_bool( - xs == xs.clone().into_iter().rev().collect::>(), - ) + TestResult::from_bool(xs == xs.clone().into_iter().rev().collect::>()) } } quickcheck(prop as fn(Vec) -> TestResult); @@ -178,7 +176,9 @@ fn regression_issue_83() { fn prop(_: u8) -> bool { true } - QuickCheck::new().gen(Gen::new(1024)).quickcheck(prop as fn(u8) -> bool) + QuickCheck::new() + .gen(Gen::new(1024)) + .quickcheck(prop as fn(u8) -> bool) } #[test] @@ -186,7 +186,9 @@ fn regression_issue_83_signed() { fn prop(_: i8) -> bool { true } - QuickCheck::new().gen(Gen::new(1024)).quickcheck(prop as fn(i8) -> bool) + QuickCheck::new() + .gen(Gen::new(1024)) + .quickcheck(prop as fn(i8) -> bool) } // Test that we can show the message after panic @@ -229,9 +231,7 @@ fn regression_issue_107_hang() { } #[test] -#[should_panic( - expected = "(Unable to generate enough tests, 0 not discarded.)" -)] +#[should_panic(expected = "(Unable to generate enough tests, 0 not discarded.)")] fn all_tests_discarded_min_tests_passed_set() { fn prop_discarded(_: u8) -> TestResult { TestResult::discard() diff --git a/rustfmt.toml b/rustfmt.toml deleted file mode 100644 index aa37a21..0000000 --- a/rustfmt.toml +++ /dev/null @@ -1,2 +0,0 @@ -max_width = 79 -use_small_heuristics = "max" From 5bea00b0f786038cd8b488b7ea88b7c463c08cdb Mon Sep 17 00:00:00 2001 From: Alexis Sellier Date: Sat, 3 Dec 2022 19:37:32 +0100 Subject: [PATCH 14/14] Remove redundant files --- COPYING | 3 --- Makefile | 14 -------------- ctags.rust | 11 ----------- session.vim | 1 - 4 files changed, 29 deletions(-) delete mode 100644 COPYING delete mode 100644 Makefile delete mode 100644 ctags.rust delete mode 100644 session.vim diff --git a/COPYING b/COPYING deleted file mode 100644 index bb9c20a..0000000 --- a/COPYING +++ /dev/null @@ -1,3 +0,0 @@ -This project is dual-licensed under the Unlicense and MIT licenses. - -You may use this code under the terms of either license. diff --git a/Makefile b/Makefile deleted file mode 100644 index 9f956f4..0000000 --- a/Makefile +++ /dev/null @@ -1,14 +0,0 @@ -all: - echo Nothing to do... - -ctags: - ctags --recurse --options=ctags.rust --languages=Rust - -docs: - cargo doc - in-dir ./target/doc fix-perms - rscp ./target/doc/* gopher:~/www/burntsushi.net/rustdoc/ - -push: - git push origin master - git push github master diff --git a/ctags.rust b/ctags.rust deleted file mode 100644 index b42edf7..0000000 --- a/ctags.rust +++ /dev/null @@ -1,11 +0,0 @@ ---langdef=Rust ---langmap=Rust:.rs ---regex-Rust=/^[ \t]*(#\[[^\]]\][ \t]*)*(pub[ \t]+)?(extern[ \t]+)?("[^"]+"[ \t]+)?(unsafe[ \t]+)?fn[ \t]+([a-zA-Z0-9_]+)/\6/f,functions,function definitions/ ---regex-Rust=/^[ \t]*(pub[ \t]+)?type[ \t]+([a-zA-Z0-9_]+)/\2/T,types,type definitions/ ---regex-Rust=/^[ \t]*(pub[ \t]+)?enum[ \t]+([a-zA-Z0-9_]+)/\2/g,enum,enumeration names/ ---regex-Rust=/^[ \t]*(pub[ \t]+)?struct[ \t]+([a-zA-Z0-9_]+)/\2/s,structure names/ ---regex-Rust=/^[ \t]*(pub[ \t]+)?mod[ \t]+([a-zA-Z0-9_]+)/\2/m,modules,module names/ ---regex-Rust=/^[ \t]*(pub[ \t]+)?static[ \t]+([a-zA-Z0-9_]+)/\2/c,consts,static constants/ ---regex-Rust=/^[ \t]*(pub[ \t]+)?trait[ \t]+([a-zA-Z0-9_]+)/\2/t,traits,traits/ ---regex-Rust=/^[ \t]*(pub[ \t]+)?impl([ \t\n]+<.*>)?[ \t]+([a-zA-Z0-9_]+)/\3/i,impls,trait implementations/ ---regex-Rust=/^[ \t]*macro_rules![ \t]+([a-zA-Z0-9_]+)/\1/d,macros,macro definitions/ diff --git a/session.vim b/session.vim deleted file mode 100644 index 213c956..0000000 --- a/session.vim +++ /dev/null @@ -1 +0,0 @@ -au BufWritePost *.rs silent!make ctags > /dev/null 2>&1