Skip to content

Commit 056d6f7

Browse files
committed
partial solution to proptest-rs#13 - added collection::{CollectionStrategy, subsequence}
1 parent 2693321 commit 056d6f7

File tree

2 files changed

+145
-0
lines changed

2 files changed

+145
-0
lines changed

CHANGELOG.md

+7
Original file line numberDiff line numberDiff line change
@@ -20,6 +20,13 @@
2020

2121
- bumped dependency `rand = "0.3.18"`.
2222

23+
- Added `proptest::collection::subsequence<T: Clone>(Vec<T>, Range<usize>)`
24+
which returns a strategy generating subsequences, of the source `Vec`,
25+
with a size within the given `Range`.
26+
27+
- Added the trait `CollectionStrategy` which provides `.prop_sample` directly
28+
on collection types. The operation is the same as `subsequence` for `Vec`.
29+
2330
## 0.3.1
2431

2532
### New Additions

src/collection.rs

+138
Original file line numberDiff line numberDiff line change
@@ -16,6 +16,7 @@ use std::collections::*;
1616
use std::fmt;
1717
use std::hash::Hash;
1818
use std::ops::Range;
19+
use std::slice;
1920

2021
use bit_set::BitSet;
2122
use rand;
@@ -25,6 +26,143 @@ use strategy::*;
2526
use tuple::TupleValueTree;
2627
use test_runner::*;
2728

29+
// TODO: add more CollectionStrategy impls for different types such as
30+
// HashSet.
31+
32+
// TODO: tests for CollectionStrategy impls.
33+
34+
/// Returns a `Strategy` that generates subsequences of `source` where the
35+
/// where the subsequences have a size: `size.start <= seq.len() < size.end`.
36+
pub fn subsequence<T>(source: Vec<T>, size: Range<usize>) -> SubseqStrategy<T>
37+
where
38+
T: Clone + fmt::Debug
39+
{
40+
source.prop_sample(size)
41+
}
42+
43+
/// A collection, such as `Vec<T>` is itself a strategy to create samples
44+
/// (or in the case of `Vec`: subsequences) with the elements of the collection.
45+
pub trait CollectionStrategy: Sized + fmt::Debug {
46+
/// The `Strategy` generating the samples.
47+
type Strategy: Strategy<Value = Self::ValueTree>;
48+
49+
/// Always the same as `Self::Strategy::Value`.
50+
/// Unfortunately this can't be removed until `impl Trait` lands.
51+
type ValueTree: ValueTree<Value = Self>;
52+
53+
/// Constructs a `Strategy` to generate samples of `self` given a range
54+
/// (low..high) where the output sample has a number of elements: len.
55+
/// The following always holds for the sample: `low <= len < high`.
56+
///
57+
/// # Safety
58+
///
59+
/// A panic must occur at least when:
60+
///
61+
/// 1. `low > high`,
62+
/// 2. `self.len() < low + 1`.
63+
fn prop_sample(self, size: Range<usize>) -> Self::Strategy;
64+
}
65+
66+
impl<T: Clone + fmt::Debug> CollectionStrategy for Vec<T> {
67+
type Strategy = SubseqStrategy<T>;
68+
type ValueTree = SubseqValueTree<T>;
69+
fn prop_sample(self, size: Range<usize>) -> Self::Strategy {
70+
SubseqStrategy::new(self, size)
71+
}
72+
}
73+
74+
/// A `Strategy` to generate subsequences, of a `Vec<T>`,
75+
/// where the subsequences have a size: `min <= seq.len() < max`.
76+
#[derive(Debug, Clone)]
77+
pub struct SubseqStrategy<T: Clone + fmt::Debug> {
78+
full: Vec<T>,
79+
size: Range<usize>,
80+
}
81+
82+
impl<T: Clone + fmt::Debug> SubseqStrategy<T> {
83+
/// Constructs a `SubseqStrategy` which will generate subsequences of
84+
/// `source` where the subsequences have a size: `[size.start, size.end)`.
85+
///
86+
/// # Safety
87+
///
88+
/// A panic occurs when:
89+
///
90+
/// 1. `size.start >= size.end`,
91+
/// 2. `source.len() < size.start + 1`.
92+
pub fn new(source: Vec<T>, size: Range<usize>) -> Self {
93+
assert!(!(size.start >= size.end));
94+
assert!(!(source.len() < size.start + 1));
95+
Self {
96+
full: source,
97+
size: size,
98+
}
99+
}
100+
}
101+
102+
impl<T: Clone + fmt::Debug> Strategy for SubseqStrategy<T> {
103+
type Value = SubseqValueTree<T>;
104+
105+
fn new_value(&self, runner: &mut TestRunner)
106+
-> Result<Self::Value, String>
107+
{
108+
// Generate the amount of elements in result:
109+
let rng = runner.rng();
110+
let amount = rand::distributions::Range::new(
111+
self.size.start, self.size.end).ind_sample(rng);
112+
113+
// Generate a subsequence of amount size.
114+
let subvec = rand::sample(rng, self.full.iter().cloned(), amount);
115+
116+
// We're done, yield the value tree.
117+
Ok(SubseqValueTree {
118+
data: subvec,
119+
len: amount,
120+
min: self.size.start,
121+
})
122+
}
123+
}
124+
125+
/// The `ValueTree` for `SubseqStrategy`.
126+
pub struct SubseqValueTree<T: Clone + fmt::Debug> {
127+
data: Vec<T>,
128+
len: usize,
129+
min: usize
130+
}
131+
132+
impl<T: Clone + fmt::Debug> ValueTree for SubseqValueTree<T> {
133+
type Value = Vec<T>;
134+
135+
fn current(&self) -> Self::Value {
136+
// We verify manually that self.len <= self.data.len(),
137+
// therefore it is safe. The assertion should always hold.
138+
assert!(self.len <= self.data.len());
139+
unsafe {
140+
slice::from_raw_parts(self.data.as_ptr(), self.len)
141+
}.into()
142+
}
143+
144+
fn simplify(&mut self) -> bool {
145+
if self.len == self.min {
146+
// We're not allowed to shrink further.
147+
false
148+
} else {
149+
// Still possible to shrink. Do so by 1 element.
150+
self.len -= 1;
151+
true
152+
}
153+
}
154+
155+
fn complicate(&mut self) -> bool {
156+
if self.len == self.data.len() {
157+
// We've reached the full subsequence, can't complicate more.
158+
false
159+
} else {
160+
self.len += 1;
161+
true
162+
}
163+
}
164+
}
165+
28166
/// Strategy to create `Vec`s with a length in a certain range.
29167
///
30168
/// Created by the `vec()` function in the same module.

0 commit comments

Comments
 (0)