diff options
author | Treehugger Robot <treehugger-gerrit@google.com> | 2022-03-08 18:52:36 +0000 |
---|---|---|
committer | Gerrit Code Review <noreply-gerritcodereview@google.com> | 2022-03-08 18:52:36 +0000 |
commit | 9224b8fb00fe82d58d8670199518275ea9299ee0 (patch) | |
tree | 27d959a1ffe9a8cdb31d32f2b534283cd9fb1573 | |
parent | 3d3bc4d87ae2aca1a6f6e3ad9e68f1140257038e (diff) | |
parent | 9c52ab3563b64a0c0e7ec265edc6aa193fcd3254 (diff) | |
download | arbitrary-9224b8fb00fe82d58d8670199518275ea9299ee0.tar.gz |
Merge "Update arbitrary to 1.1.0"
-rw-r--r-- | .cargo_vcs_info.json | 7 | ||||
-rw-r--r-- | Android.bp | 9 | ||||
-rw-r--r-- | CHANGELOG.md | 31 | ||||
-rw-r--r-- | Cargo.toml | 19 | ||||
-rw-r--r-- | Cargo.toml.orig | 4 | ||||
-rw-r--r-- | METADATA | 10 | ||||
-rw-r--r-- | src/lib.rs | 2 | ||||
-rw-r--r-- | src/unstructured.rs | 141 | ||||
-rwxr-xr-x[-rw-r--r--] | tests/derive.rs | 4 |
9 files changed, 204 insertions, 23 deletions
diff --git a/.cargo_vcs_info.json b/.cargo_vcs_info.json index 2d96982..508adbd 100644 --- a/.cargo_vcs_info.json +++ b/.cargo_vcs_info.json @@ -1,5 +1,6 @@ { "git": { - "sha1": "ef27875e0311786ca5210a734c6288cb5d920614" - } -} + "sha1": "d0d238d880276fd617c38f7e4712bf40db58aad6" + }, + "path_in_vcs": "" +}
\ No newline at end of file @@ -39,10 +39,11 @@ license { rust_test { name: "arbitrary_test_src_lib", + // has rustc warnings host_supported: true, crate_name: "arbitrary", cargo_env_compat: true, - cargo_pkg_version: "1.0.2", + cargo_pkg_version: "1.1.0", srcs: ["src/lib.rs"], test_suites: ["general-tests"], auto_gen_config: true, @@ -59,10 +60,11 @@ rust_test { rust_test { name: "arbitrary_test_tests_derive", + // has rustc warnings host_supported: true, crate_name: "arbitrary", cargo_env_compat: true, - cargo_pkg_version: "1.0.2", + cargo_pkg_version: "1.1.0", srcs: ["tests/derive.rs"], test_suites: ["general-tests"], auto_gen_config: true, @@ -82,10 +84,11 @@ rust_test { rust_library_rlib { name: "libarbitrary", + // has rustc warnings host_supported: true, crate_name: "arbitrary", cargo_env_compat: true, - cargo_pkg_version: "1.0.2", + cargo_pkg_version: "1.1.0", srcs: ["src/lib.rs"], edition: "2018", features: [ diff --git a/CHANGELOG.md b/CHANGELOG.md index 0484728..2a7c179 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -26,13 +26,42 @@ Released YYYY-MM-DD. * TODO (or remove section if none) +-------------------------------------------------------------------------------- + +## 1.1.0 + +Released 2022-02-09. + +### Added + +* Added the `Unstructured::ratio` method to generate a boolean that is `true` at + the given rate. + +* Added the `Unstructured::arbitrary_loop` method to call a function an + arbitrary number of times. + +-------------------------------------------------------------------------------- + +## 1.0.3 + +Released 2021-11-20. + +### Fixed + +* Fixed documentation for `Unstructured::fill_bytes`. We forgot to update this + way back in [#53](https://github.com/rust-fuzz/arbitrary/pull/53) when the + behavior changed. + +-------------------------------------------------------------------------------- + ## 1.0.2 Released 2021-08-25. ### Added -* `Arbitrary` impls for `HashMap`s and `HashSet`s with custom `Hasher`s [#87](https://github.com/rust-fuzz/arbitrary/pull/87) +* `Arbitrary` impls for `HashMap`s and `HashSet`s with custom `Hasher`s + [#87](https://github.com/rust-fuzz/arbitrary/pull/87) -------------------------------------------------------------------------------- @@ -12,14 +12,24 @@ [package] edition = "2018" name = "arbitrary" -version = "1.0.2" -authors = ["The Rust-Fuzz Project Developers", "Nick Fitzgerald <fitzgen@gmail.com>", "Manish Goregaokar <manishsmail@gmail.com>", "Simonas Kazlauskas <arbitrary@kazlauskas.me>", "Brian L. Troutwine <brian@troutwine.us>", "Corey Farwell <coreyf@rwell.org>"] +version = "1.1.0" +authors = [ + "The Rust-Fuzz Project Developers", + "Nick Fitzgerald <fitzgen@gmail.com>", + "Manish Goregaokar <manishsmail@gmail.com>", + "Simonas Kazlauskas <arbitrary@kazlauskas.me>", + "Brian L. Troutwine <brian@troutwine.us>", + "Corey Farwell <coreyf@rwell.org>", +] description = "The trait for generating structured data from unstructured data" documentation = "https://docs.rs/arbitrary/" readme = "README.md" -keywords = ["arbitrary", "testing"] +keywords = [ + "arbitrary", + "testing", +] categories = ["development-tools::testing"] -license = "MIT/Apache-2.0" +license = "MIT OR Apache-2.0" repository = "https://github.com/rust-fuzz/arbitrary/" [[example]] @@ -30,6 +40,7 @@ required-features = ["derive"] name = "derive" path = "./tests/derive.rs" required-features = ["derive"] + [dependencies.derive_arbitrary] version = "1.0.0" optional = true diff --git a/Cargo.toml.orig b/Cargo.toml.orig index 63787aa..df4e2a1 100644 --- a/Cargo.toml.orig +++ b/Cargo.toml.orig @@ -1,6 +1,6 @@ [package] name = "arbitrary" -version = "1.0.2" # Make sure this matches the derive crate version +version = "1.1.0" # Make sure this matches the derive crate version authors = [ "The Rust-Fuzz Project Developers", "Nick Fitzgerald <fitzgen@gmail.com>", @@ -14,7 +14,7 @@ edition = "2018" keywords = ["arbitrary", "testing"] readme = "README.md" description = "The trait for generating structured data from unstructured data" -license = "MIT/Apache-2.0" +license = "MIT OR Apache-2.0" repository = "https://github.com/rust-fuzz/arbitrary/" documentation = "https://docs.rs/arbitrary/" @@ -7,13 +7,13 @@ third_party { } url { type: ARCHIVE - value: "https://static.crates.io/crates/arbitrary/arbitrary-1.0.2.crate" + value: "https://static.crates.io/crates/arbitrary/arbitrary-1.1.0.crate" } - version: "1.0.2" + version: "1.1.0" license_type: NOTICE last_upgrade_date { - year: 2021 - month: 9 - day: 22 + year: 2022 + month: 3 + day: 1 } } @@ -99,7 +99,7 @@ use std::sync::{Arc, Mutex}; /// Implementing `Arbitrary` mostly involves nested calls to other `Arbitrary` /// arbitrary implementations for each of your `struct` or `enum`'s members. But /// sometimes you need some amount of raw data, or you need to generate a -/// variably-sized collection type, or you something of that sort. The +/// variably-sized collection type, or something of that sort. The /// [`Unstructured`][crate::Unstructured] type helps you with these tasks. /// /// ``` diff --git a/src/unstructured.rs b/src/unstructured.rs index 968d41a..ff3e1f3 100644 --- a/src/unstructured.rs +++ b/src/unstructured.rs @@ -10,6 +10,7 @@ use crate::{Arbitrary, Error, Result}; use std::marker::PhantomData; +use std::ops::ControlFlow; use std::{mem, ops}; /// A source of unstructured data. @@ -382,6 +383,41 @@ impl<'a> Unstructured<'a> { Ok(&choices[idx]) } + /// Generate a boolean according to the given ratio. + /// + /// # Panics + /// + /// Panics when the numerator and denominator do not meet these constraints: + /// + /// * `0 < numerator <= denominator` + /// + /// # Example + /// + /// Generate a boolean that is `true` five sevenths of the time: + /// + /// ``` + /// # fn foo() -> arbitrary::Result<()> { + /// use arbitrary::Unstructured; + /// + /// # let my_data = [1, 2, 3, 4, 5, 6, 7, 8, 9, 0]; + /// let mut u = Unstructured::new(&my_data); + /// + /// if u.ratio(5, 7)? { + /// // Take this branch 5/7 of the time. + /// } + /// # Ok(()) + /// # } + /// ``` + pub fn ratio<T>(&mut self, numerator: T, denominator: T) -> Result<bool> + where + T: Int, + { + assert!(T::ZERO < numerator); + assert!(numerator <= denominator); + let x = self.int_in_range(T::ONE..=denominator)?; + Ok(x <= numerator) + } + /// Fill a `buffer` with bytes from the underlying raw data. /// /// This should only be called within an `Arbitrary` implementation. This is @@ -389,8 +425,8 @@ impl<'a> Unstructured<'a> { /// `Arbitrary` implementations like `<Vec<u8>>::arbitrary` and /// `String::arbitrary` over using this method directly. /// - /// If this `Unstructured` does not have enough data to fill the whole - /// `buffer`, an error is returned. + /// If this `Unstructured` does not have enough underlying data to fill the + /// whole `buffer`, it pads the buffer out with zeros. /// /// # Example /// @@ -400,8 +436,15 @@ impl<'a> Unstructured<'a> { /// let mut u = Unstructured::new(&[1, 2, 3, 4]); /// /// let mut buf = [0; 2]; + /// + /// assert!(u.fill_buffer(&mut buf).is_ok()); + /// assert_eq!(buf, [1, 2]); + /// /// assert!(u.fill_buffer(&mut buf).is_ok()); + /// assert_eq!(buf, [3, 4]); + /// /// assert!(u.fill_buffer(&mut buf).is_ok()); + /// assert_eq!(buf, [0, 0]); /// ``` pub fn fill_buffer(&mut self, buffer: &mut [u8]) -> Result<()> { let n = std::cmp::min(buffer.len(), self.data.len()); @@ -516,6 +559,100 @@ impl<'a> Unstructured<'a> { _marker: PhantomData, }) } + + /// Call the given function an arbitrary number of times. + /// + /// The function is given this `Unstructured` so that it can continue to + /// generate arbitrary data and structures. + /// + /// You may optionaly specify minimum and maximum bounds on the number of + /// times the function is called. + /// + /// You may break out of the loop early by returning + /// `Ok(std::ops::ControlFlow::Break)`. To continue the loop, return + /// `Ok(std::ops::ControlFlow::Continue)`. + /// + /// # Panics + /// + /// Panics if `min > max`. + /// + /// # Example + /// + /// Call a closure that generates an arbitrary type inside a context an + /// arbitrary number of times: + /// + /// ``` + /// use arbitrary::{Result, Unstructured}; + /// use std::ops::ControlFlow; + /// + /// enum Type { + /// /// A boolean type. + /// Bool, + /// + /// /// An integer type. + /// Int, + /// + /// /// A list of the `i`th type in this type's context. + /// List(usize), + /// } + /// + /// fn arbitrary_types_context(u: &mut Unstructured) -> Result<Vec<Type>> { + /// let mut context = vec![]; + /// + /// u.arbitrary_loop(Some(10), Some(20), |u| { + /// let num_choices = if context.is_empty() { + /// 2 + /// } else { + /// 3 + /// }; + /// let ty = match u.int_in_range::<u8>(1..=num_choices)? { + /// 1 => Type::Bool, + /// 2 => Type::Int, + /// 3 => Type::List(u.int_in_range(0..=context.len() - 1)?), + /// _ => unreachable!(), + /// }; + /// context.push(ty); + /// Ok(ControlFlow::Continue(())) + /// })?; + /// + /// // The number of loop iterations are constrained by the min/max + /// // bounds that we provided. + /// assert!(context.len() >= 10); + /// assert!(context.len() <= 20); + /// + /// Ok(context) + /// } + /// ``` + pub fn arbitrary_loop( + &mut self, + min: Option<u32>, + max: Option<u32>, + mut f: impl FnMut(&mut Self) -> Result<ControlFlow<(), ()>>, + ) -> Result<()> { + let min = min.unwrap_or(0); + let max = max.unwrap_or(u32::MAX); + assert!(min <= max); + + for _ in 0..min { + match f(self)? { + ControlFlow::Continue(_) => continue, + ControlFlow::Break(_) => return Ok(()), + } + } + + for _ in 0..(max - min) { + let keep_going = self.arbitrary().unwrap_or(false); + if !keep_going { + break; + } + match f(self)? { + ControlFlow::Continue(_) => continue, + ControlFlow::Break(_) => break, + } + } + + Ok(()) + } } /// Utility iterator produced by [`Unstructured::arbitrary_iter`] diff --git a/tests/derive.rs b/tests/derive.rs index 9dfbbd5..adf1188 100644..100755 --- a/tests/derive.rs +++ b/tests/derive.rs @@ -164,7 +164,7 @@ fn one_lifetime() { assert_eq!("abc", lifetime.alpha); let (lower, upper) = <OneLifetime as Arbitrary>::size_hint(0); - assert_eq!(lower, 8); + assert_eq!(lower, std::mem::size_of::<usize>()); assert_eq!(upper, None); } @@ -183,6 +183,6 @@ fn two_lifetimes() { assert_eq!("def", lifetime.beta); let (lower, upper) = <TwoLifetimes as Arbitrary>::size_hint(0); - assert_eq!(lower, 16); + assert_eq!(lower, std::mem::size_of::<usize>() * 2); assert_eq!(upper, None); } |