hypothesis-specs 0.0.12 → 0.0.13

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
checksums.yaml CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA256:
3
- metadata.gz: 793034b02b1dde38856e5e7d7192b6b281aae453d6ff060ddd4478147bf066fc
4
- data.tar.gz: 4fdc7f52bc5d36816a3dfc94c59e4273155af358cb2d57629402e6ad4582090b
3
+ metadata.gz: 70d4e8207879b336d81af1a5c591b0eb184068114617a87f75640cc729e2152f
4
+ data.tar.gz: 1cd583baf6d78a411ddc81756ceee110566b05025671cdc50fd8c210d41b444f
5
5
  SHA512:
6
- metadata.gz: 43cd31e4681166b72d147f0c7f5fb0a906b98677515656410897abe7442cf101673e8abbdf69079488de6edbab315be5bfcdfba6a9e9fb6adff95485c9e2ad60
7
- data.tar.gz: 34859a4362b50323fa4dd19ed195cb9e00a854a5ae0aa06d6c77c8cdb8abb0f8fd21428696fd388c7abe35e49c5eab726e83b7e09aee0ff01478fa9e15c13cac
6
+ metadata.gz: f5811a1bf2bd16b8ca011e40cc25e007afcccd5432d082dea9d4d7fd3f042e1c835d0b42f3808bf6915fe4042fafabdbd12ed7f5f7fb4dc2f46cb5cd94e6a2e1
7
+ data.tar.gz: 7be4261c0ee96f7d22eb107dba0e9ee485ec5510bbf769841fc50e0cb9d13a0dda56ef4b100b4e27b601aaccbfba1fe07c7b35a36006029ff03950df14bc3d70
data/CHANGELOG.md CHANGED
@@ -1,3 +1,8 @@
1
+ # Hypothesis for Ruby 0.0.13 (2018-06-25)
2
+
3
+ This release moves the core Rust engine into the separate Conjecture crate. It
4
+ should have no user visible effect.
5
+
1
6
  # Hypothesis for Ruby 0.0.12 (2018-06-23)
2
7
 
3
8
  This release is the beginning of splitting out the Rust core of Hypothesis
data/Cargo.toml CHANGED
@@ -9,4 +9,4 @@ crate-type = ["cdylib"]
9
9
  [dependencies]
10
10
  helix = '0.7.5'
11
11
  rand = '0.3'
12
- conjecture = '0.1.1'
12
+ conjecture = '0.2.0'
data/Rakefile CHANGED
@@ -16,11 +16,7 @@ begin
16
16
  t.verbose = true
17
17
  end
18
18
 
19
- task :rust_tests do
20
- sh 'cargo test'
21
- end
22
-
23
- task test: %i[build spec minitests rust_tests]
19
+ task test: %i[build spec minitests]
24
20
  rescue LoadError
25
21
  end
26
22
 
data/src/lib.rs CHANGED
@@ -11,17 +11,14 @@ extern crate core;
11
11
  #[macro_use]
12
12
  extern crate helix;
13
13
  extern crate rand;
14
-
15
- mod data;
16
- mod distributions;
17
- mod engine;
18
- mod intminimize;
14
+ extern crate conjecture;
19
15
 
20
16
  use std::mem;
21
17
 
22
- use data::{DataSource, Status};
23
- use distributions::Repeat;
24
- use engine::Engine;
18
+ use conjecture::data::{DataSource, Status};
19
+ use conjecture::distributions::Repeat;
20
+ use conjecture::distributions;
21
+ use conjecture::engine::Engine;
25
22
 
26
23
  ruby! {
27
24
  class HypothesisCoreDataSource {
metadata CHANGED
@@ -1,7 +1,7 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: hypothesis-specs
3
3
  version: !ruby/object:Gem::Version
4
- version: 0.0.12
4
+ version: 0.0.13
5
5
  platform: ruby
6
6
  authors:
7
7
  - David R. Maciver
@@ -67,10 +67,6 @@ files:
67
67
  - lib/hypothesis/possible.rb
68
68
  - lib/hypothesis/testcase.rb
69
69
  - lib/hypothesis/world.rb
70
- - src/data.rs
71
- - src/distributions.rs
72
- - src/engine.rs
73
- - src/intminimize.rs
74
70
  - src/lib.rs
75
71
  homepage: https://github.com/HypothesisWorks/hypothesis/tree/master/hypothesis-ruby
76
72
  licenses:
data/src/data.rs DELETED
@@ -1,184 +0,0 @@
1
- // Module representing core data types that Hypothesis
2
- // needs.
3
-
4
- use rand::{ChaChaRng, Rng};
5
- use std::collections::HashSet;
6
-
7
- pub type DataStream = Vec<u64>;
8
-
9
- #[derive(Debug, Clone)]
10
- pub struct FailedDraw;
11
-
12
- #[derive(Debug, Clone)]
13
- enum BitGenerator {
14
- Random(ChaChaRng),
15
- Recorded(DataStream),
16
- }
17
-
18
- // Records information corresponding to a single draw call.
19
- #[derive(Debug, Clone)]
20
- pub struct DrawInProgress {
21
- depth: usize,
22
- start: usize,
23
- end: Option<usize>,
24
- }
25
-
26
- // Records information corresponding to a single draw call.
27
- #[derive(Debug, Clone)]
28
- pub struct Draw {
29
- pub depth: usize,
30
- pub start: usize,
31
- pub end: usize,
32
- }
33
-
34
- // Main entry point for running a test:
35
- // A test function takes a DataSource, uses it to
36
- // produce some data, and the DataSource records the
37
- // relevant information about what they did.
38
- #[derive(Debug, Clone)]
39
- pub struct DataSource {
40
- bitgenerator: BitGenerator,
41
- record: DataStream,
42
- sizes: Vec<u64>,
43
- draws: Vec<DrawInProgress>,
44
- draw_stack: Vec<usize>,
45
- written_indices: HashSet<usize>,
46
- }
47
-
48
- impl DataSource {
49
- fn new(generator: BitGenerator) -> DataSource {
50
- return DataSource {
51
- bitgenerator: generator,
52
- record: DataStream::new(),
53
- sizes: Vec::new(),
54
- draws: Vec::new(),
55
- draw_stack: Vec::new(),
56
- written_indices: HashSet::new(),
57
- };
58
- }
59
-
60
- pub fn from_random(random: ChaChaRng) -> DataSource {
61
- return DataSource::new(BitGenerator::Random(random));
62
- }
63
-
64
- pub fn from_vec(record: DataStream) -> DataSource {
65
- return DataSource::new(BitGenerator::Recorded(record));
66
- }
67
-
68
- pub fn start_draw(&mut self) {
69
- let i = self.draws.len();
70
- let depth = self.draw_stack.len();
71
- let start = self.record.len();
72
-
73
- self.draw_stack.push(i);
74
- self.draws.push(DrawInProgress {
75
- start: start,
76
- end: None,
77
- depth: depth,
78
- });
79
- }
80
-
81
- pub fn stop_draw(&mut self) {
82
- assert!(self.draws.len() > 0);
83
- assert!(self.draw_stack.len() > 0);
84
- let i = self.draw_stack.pop().unwrap();
85
- let end = self.record.len();
86
- self.draws[i].end = Some(end);
87
- }
88
-
89
- pub fn write(&mut self, value: u64) -> Result<(), FailedDraw> {
90
- match self.bitgenerator {
91
- BitGenerator::Recorded(ref mut v) if self.record.len() >= v.len() => Err(FailedDraw),
92
- _ => {
93
- self.sizes.push(0);
94
- self.record.push(value);
95
- Ok(())
96
- }
97
- }
98
- }
99
-
100
- pub fn bits(&mut self, n_bits: u64) -> Result<u64, FailedDraw> {
101
- self.sizes.push(n_bits);
102
- let mut result = match self.bitgenerator {
103
- BitGenerator::Random(ref mut random) => random.next_u64(),
104
- BitGenerator::Recorded(ref mut v) => if self.record.len() >= v.len() {
105
- return Err(FailedDraw);
106
- } else {
107
- v[self.record.len()]
108
- },
109
- };
110
-
111
- if n_bits < 64 {
112
- let mask = (1 << n_bits) - 1;
113
- result &= mask;
114
- };
115
-
116
- self.record.push(result);
117
-
118
- return Ok(result);
119
- }
120
-
121
- pub fn to_result(mut self, status: Status) -> TestResult {
122
- TestResult {
123
- record: self.record,
124
- status: status,
125
- written_indices: self.written_indices,
126
- sizes: self.sizes,
127
- draws: self.draws
128
- .drain(..)
129
- .filter_map(|d| match d {
130
- DrawInProgress {
131
- depth,
132
- start,
133
- end: Some(end),
134
- } if start < end =>
135
- {
136
- Some(Draw {
137
- start: start,
138
- end: end,
139
- depth: depth,
140
- })
141
- }
142
- DrawInProgress { end: None, .. } => {
143
- assert!(status == Status::Invalid || status == Status::Overflow);
144
- None
145
- }
146
- _ => None,
147
- })
148
- .collect(),
149
- }
150
- }
151
- }
152
-
153
- // Status indicates the result that we got from completing
154
- // a single test execution.
155
- #[derive(Debug, Clone, Eq, PartialEq, Copy)]
156
- pub enum Status {
157
- // The test tried to read more data than we had for it.
158
- Overflow,
159
-
160
- // Some important precondition of the test was not
161
- // satisfied.
162
- Invalid,
163
-
164
- // This test ran successfully to completion without
165
- // anything of note happening.
166
- Valid,
167
-
168
- // This was an interesting test execution! (Usually this
169
- // means failing, but for things like find it may not).
170
- Interesting,
171
- }
172
-
173
- // Once a data source is finished it "decays" to a
174
- // TestResult, that retains a trace of all the information
175
- // we needed from the DataSource. It is these we keep around,
176
- // not the original DataSource objects.
177
- #[derive(Debug, Clone)]
178
- pub struct TestResult {
179
- pub record: DataStream,
180
- pub status: Status,
181
- pub draws: Vec<Draw>,
182
- pub sizes: Vec<u64>,
183
- pub written_indices: HashSet<usize>,
184
- }
data/src/distributions.rs DELETED
@@ -1,297 +0,0 @@
1
- use data::{DataSource, FailedDraw};
2
-
3
- use std::cmp::{Ord, Ordering, PartialOrd, Reverse};
4
- use std::collections::BinaryHeap;
5
- use std::mem;
6
-
7
- use std::u64::MAX as MAX64;
8
-
9
- type Draw<T> = Result<T, FailedDraw>;
10
-
11
- pub fn weighted(source: &mut DataSource, probability: f64) -> Result<bool, FailedDraw> {
12
- // TODO: Less bit-hungry implementation.
13
-
14
- let truthy = (probability * (u64::max_value() as f64 + 1.0)).floor() as u64;
15
- let probe = source.bits(64)?;
16
- Ok(match (truthy, probe) {
17
- (0, _) => false,
18
- (MAX64, _) => true,
19
- (_, 0) => false,
20
- (_, 1) => true,
21
- _ => probe >= MAX64 - truthy,
22
- })
23
- }
24
-
25
- pub fn bounded_int(source: &mut DataSource, max: u64) -> Draw<u64> {
26
- let bitlength = 64 - max.leading_zeros() as u64;
27
- if bitlength == 0 {
28
- source.write(0)?;
29
- return Ok(0);
30
- }
31
- loop {
32
- let probe = source.bits(bitlength)?;
33
- if probe <= max {
34
- return Ok(probe);
35
- }
36
- }
37
- }
38
-
39
- #[derive(Debug, Clone)]
40
- pub struct Repeat {
41
- min_count: u64,
42
- max_count: u64,
43
- p_continue: f64,
44
-
45
- current_count: u64,
46
- }
47
-
48
- impl Repeat {
49
- pub fn new(min_count: u64, max_count: u64, expected_count: f64) -> Repeat {
50
- Repeat {
51
- min_count: min_count,
52
- max_count: max_count,
53
- p_continue: 1.0 - 1.0 / (1.0 + expected_count),
54
- current_count: 0,
55
- }
56
- }
57
-
58
- pub fn reject(&mut self) {
59
- assert!(self.current_count > 0);
60
- self.current_count -= 1;
61
- }
62
-
63
- pub fn should_continue(&mut self, source: &mut DataSource) -> Result<bool, FailedDraw> {
64
- if self.min_count == self.max_count {
65
- if self.current_count < self.max_count {
66
- self.current_count += 1;
67
- return Ok(true);
68
- } else {
69
- return Ok(false);
70
- }
71
- } else if self.current_count < self.min_count {
72
- source.write(1)?;
73
- self.current_count += 1;
74
- return Ok(true);
75
- } else if self.current_count >= self.max_count {
76
- source.write(0)?;
77
- return Ok(false);
78
- }
79
-
80
- let result = weighted(source, self.p_continue)?;
81
- if result {
82
- self.current_count += 1;
83
- } else {
84
- }
85
- return Ok(result);
86
- }
87
- }
88
-
89
- #[derive(Debug, Clone)]
90
- struct SamplerEntry {
91
- primary: usize,
92
- alternate: usize,
93
- use_alternate: f32,
94
- }
95
-
96
- impl SamplerEntry {
97
- fn single(i: usize) -> SamplerEntry {
98
- SamplerEntry {
99
- primary: i,
100
- alternate: i,
101
- use_alternate: 0.0,
102
- }
103
- }
104
- }
105
-
106
- impl Ord for SamplerEntry {
107
- fn cmp(&self, other: &SamplerEntry) -> Ordering {
108
- return self.primary
109
- .cmp(&other.primary)
110
- .then(self.alternate.cmp(&other.alternate));
111
- }
112
- }
113
-
114
- impl PartialOrd for SamplerEntry {
115
- fn partial_cmp(&self, other: &SamplerEntry) -> Option<Ordering> {
116
- return Some(self.cmp(other));
117
- }
118
- }
119
-
120
- impl PartialEq for SamplerEntry {
121
- fn eq(&self, other: &SamplerEntry) -> bool {
122
- return self.cmp(other) == Ordering::Equal;
123
- }
124
- }
125
-
126
- impl Eq for SamplerEntry {}
127
-
128
- #[derive(Debug, Clone)]
129
- pub struct Sampler {
130
- table: Vec<SamplerEntry>,
131
- }
132
-
133
- impl Sampler {
134
- pub fn new(weights: Vec<f32>) -> Sampler {
135
- // FIXME: The correct thing to do here is to allow this,
136
- // return early, and make this reject the data, but we don't
137
- // currently have the status built into our data properly...
138
- assert!(weights.len() > 0);
139
-
140
- let mut table = Vec::new();
141
-
142
- let mut small = BinaryHeap::new();
143
- let mut large = BinaryHeap::new();
144
-
145
- let total: f32 = weights.iter().sum();
146
-
147
- let mut scaled_probabilities = Vec::new();
148
-
149
- let n = weights.len() as f32;
150
-
151
- for (i, w) in weights.iter().enumerate() {
152
- let scaled = n * w / total;
153
- scaled_probabilities.push(scaled);
154
- if scaled == 1.0 {
155
- table.push(SamplerEntry::single(i))
156
- } else if scaled > 1.0 {
157
- large.push(Reverse(i));
158
- } else {
159
- assert!(scaled < 1.0);
160
- small.push(Reverse(i));
161
- }
162
- }
163
-
164
- while !(small.is_empty() || large.is_empty()) {
165
- let Reverse(lo) = small.pop().unwrap();
166
- let Reverse(hi) = large.pop().unwrap();
167
-
168
- assert!(lo != hi);
169
- assert!(scaled_probabilities[hi] > 1.0);
170
- assert!(scaled_probabilities[lo] < 1.0);
171
- scaled_probabilities[hi] = (scaled_probabilities[hi] + scaled_probabilities[lo]) - 1.0;
172
- table.push(SamplerEntry {
173
- primary: lo,
174
- alternate: hi,
175
- use_alternate: 1.0 - scaled_probabilities[lo],
176
- });
177
-
178
- if scaled_probabilities[hi] < 1.0 {
179
- small.push(Reverse(hi))
180
- } else if scaled_probabilities[hi] > 1.0 {
181
- large.push(Reverse(hi))
182
- } else {
183
- table.push(SamplerEntry::single(hi))
184
- }
185
- }
186
- for &Reverse(i) in small.iter() {
187
- table.push(SamplerEntry::single(i))
188
- }
189
- for &Reverse(i) in large.iter() {
190
- table.push(SamplerEntry::single(i))
191
- }
192
-
193
- for ref mut entry in table.iter_mut() {
194
- if entry.alternate < entry.primary {
195
- mem::swap(&mut entry.primary, &mut entry.alternate);
196
- entry.use_alternate = 1.0 - entry.use_alternate;
197
- }
198
- }
199
-
200
- table.sort();
201
- assert!(table.len() > 0);
202
- return Sampler { table: table };
203
- }
204
-
205
- pub fn sample(&self, source: &mut DataSource) -> Draw<usize> {
206
- assert!(self.table.len() > 0);
207
- let i = bounded_int(source, self.table.len() as u64 - 1)? as usize;
208
- let entry = &self.table[i];
209
- let use_alternate = weighted(source, entry.use_alternate as f64)?;
210
- if use_alternate {
211
- Ok(entry.alternate)
212
- } else {
213
- Ok(entry.primary)
214
- }
215
- }
216
- }
217
-
218
- pub fn good_bitlengths() -> Sampler {
219
- let weights = vec![
220
- 4.0,
221
- 4.0,
222
- 4.0,
223
- 4.0,
224
- 4.0,
225
- 4.0,
226
- 4.0,
227
- 4.0, // 1 byte
228
- 2.0,
229
- 2.0,
230
- 2.0,
231
- 2.0,
232
- 2.0,
233
- 2.0,
234
- 2.0,
235
- 2.0, // 2 bytes
236
- 1.0,
237
- 1.0,
238
- 1.0,
239
- 1.0,
240
- 1.0,
241
- 1.0,
242
- 1.0,
243
- 1.0, // 3 bytes
244
- 0.5,
245
- 0.5,
246
- 0.5,
247
- 0.5,
248
- 0.5,
249
- 0.5,
250
- 0.5,
251
- 0.5, // 4 bytes
252
- 0.1,
253
- 0.1,
254
- 0.1,
255
- 0.1,
256
- 0.1,
257
- 0.1,
258
- 0.1,
259
- 0.1, // 5 bytes
260
- 0.1,
261
- 0.1,
262
- 0.1,
263
- 0.1,
264
- 0.1,
265
- 0.1,
266
- 0.1,
267
- 0.1, // 6 bytes
268
- 0.1,
269
- 0.1,
270
- 0.1,
271
- 0.1,
272
- 0.1,
273
- 0.1,
274
- 0.1,
275
- 0.1, // 7 bytes
276
- 0.1,
277
- 0.1,
278
- 0.1,
279
- 0.1,
280
- 0.1,
281
- 0.1,
282
- 0.1, // 8 bytes (last bit spare for sign)
283
- ];
284
- assert!(weights.len() == 63);
285
- Sampler::new(weights)
286
- }
287
-
288
- pub fn integer_from_bitlengths(source: &mut DataSource, bitlengths: &Sampler) -> Draw<i64> {
289
- let bitlength = bitlengths.sample(source)? as u64 + 1;
290
- let base = source.bits(bitlength)? as i64;
291
- let sign = source.bits(1)?;
292
- if sign > 0 {
293
- Ok(-base)
294
- } else {
295
- Ok(base)
296
- }
297
- }
data/src/engine.rs DELETED
@@ -1,620 +0,0 @@
1
- // Core module that provides a main execution loop and
2
- // the API that can be used to get test data from it.
3
-
4
- use rand::{ChaChaRng, Rng, SeedableRng};
5
-
6
- use std::cmp::Reverse;
7
- use std::collections::HashMap;
8
- use std::mem;
9
- use std::sync::mpsc::{sync_channel, Receiver, SyncSender};
10
- use std::thread;
11
-
12
- use data::{DataSource, DataStream, Status, TestResult};
13
- use intminimize::minimize_integer;
14
-
15
- #[derive(Debug, Clone)]
16
- enum LoopExitReason {
17
- Complete,
18
- MaxExamples,
19
- Shutdown,
20
- }
21
-
22
- #[derive(Debug)]
23
- enum LoopCommand {
24
- RunThis(DataSource),
25
- Finished(LoopExitReason, MainGenerationLoop),
26
- }
27
-
28
- #[derive(Debug)]
29
- struct MainGenerationLoop {
30
- receiver: Receiver<TestResult>,
31
- sender: SyncSender<LoopCommand>,
32
- max_examples: u64,
33
- random: ChaChaRng,
34
-
35
- best_example: Option<TestResult>,
36
-
37
- valid_examples: u64,
38
- invalid_examples: u64,
39
- interesting_examples: u64,
40
- }
41
-
42
- type StepResult = Result<(), LoopExitReason>;
43
-
44
- impl MainGenerationLoop {
45
- fn run(mut self) {
46
- let result = self.loop_body();
47
- match result {
48
- // Silent shutdown when the main thread terminates
49
- Err(LoopExitReason::Shutdown) => (),
50
- Err(reason) => {
51
- // Must clone because otherwise it is borrowed.
52
- let shutdown_sender = self.sender.clone();
53
- shutdown_sender
54
- .send(LoopCommand::Finished(reason, self))
55
- .unwrap()
56
- }
57
- Ok(_) => panic!("BUG: Generation loop was not supposed to return normally."),
58
- }
59
- }
60
-
61
- fn loop_body(&mut self) -> StepResult {
62
- let interesting_example = self.generate_examples()?;
63
-
64
- let mut shrinker = Shrinker::new(self, interesting_example, |r| {
65
- r.status == Status::Interesting
66
- });
67
-
68
- shrinker.run()?;
69
-
70
- return Err(LoopExitReason::Complete);
71
- }
72
-
73
- fn generate_examples(&mut self) -> Result<TestResult, LoopExitReason> {
74
- while self.valid_examples < self.max_examples
75
- && self.invalid_examples < 10 * self.max_examples
76
- {
77
- let r = self.random.gen();
78
- let result = self.execute(DataSource::from_random(r))?;
79
- if result.status == Status::Interesting {
80
- return Ok(result);
81
- }
82
- }
83
- return Err(LoopExitReason::MaxExamples);
84
- }
85
-
86
- fn execute(&mut self, source: DataSource) -> Result<TestResult, LoopExitReason> {
87
- let result = match self.sender.send(LoopCommand::RunThis(source)) {
88
- Ok(_) => match self.receiver.recv() {
89
- Ok(t) => t,
90
- Err(_) => return Err(LoopExitReason::Shutdown),
91
- },
92
- Err(_) => return Err(LoopExitReason::Shutdown),
93
- };
94
- match result.status {
95
- Status::Overflow => (),
96
- Status::Invalid => self.invalid_examples += 1,
97
- Status::Valid => self.valid_examples += 1,
98
- Status::Interesting => {
99
- self.best_example = Some(result.clone());
100
- self.interesting_examples += 1;
101
- }
102
- }
103
-
104
- Ok(result)
105
- }
106
- }
107
-
108
- struct Shrinker<'owner, Predicate> {
109
- _predicate: Predicate,
110
- shrink_target: TestResult,
111
- changes: u64,
112
- expensive_passes_enabled: bool,
113
- main_loop: &'owner mut MainGenerationLoop,
114
- }
115
-
116
- impl<'owner, Predicate> Shrinker<'owner, Predicate>
117
- where
118
- Predicate: Fn(&TestResult) -> bool,
119
- {
120
- fn new(
121
- main_loop: &'owner mut MainGenerationLoop,
122
- shrink_target: TestResult,
123
- predicate: Predicate,
124
- ) -> Shrinker<'owner, Predicate> {
125
- assert!(predicate(&shrink_target));
126
- Shrinker {
127
- main_loop: main_loop,
128
- _predicate: predicate,
129
- shrink_target: shrink_target,
130
- changes: 0,
131
- expensive_passes_enabled: false,
132
- }
133
- }
134
-
135
- fn predicate(&mut self, result: &TestResult) -> bool {
136
- let succeeded = (self._predicate)(result);
137
- if succeeded
138
- && (
139
- // In the presence of writes it may be the case that we thought
140
- // we were going to shrink this but didn't actually succeed because
141
- // the written value was used.
142
- result.record.len() < self.shrink_target.record.len() || (
143
- result.record.len() == self.shrink_target.record.len() &&
144
- result.record < self.shrink_target.record
145
- )
146
- ) {
147
- self.changes += 1;
148
- self.shrink_target = result.clone();
149
- }
150
- succeeded
151
- }
152
-
153
- fn run(&mut self) -> StepResult {
154
- let mut prev = self.changes + 1;
155
-
156
- while prev != self.changes {
157
- prev = self.changes;
158
- self.adaptive_delete()?;
159
- self.minimize_individual_blocks()?;
160
- self.minimize_duplicated_blocks()?;
161
- if prev == self.changes {
162
- self.expensive_passes_enabled = true;
163
- }
164
- if !self.expensive_passes_enabled {
165
- continue;
166
- }
167
-
168
- self.reorder_blocks()?;
169
- self.lower_and_delete()?;
170
- self.delete_all_ranges()?;
171
- }
172
- Ok(())
173
- }
174
-
175
- fn lower_and_delete(&mut self) -> StepResult {
176
- let mut i = 0;
177
- while i < self.shrink_target.record.len() {
178
- if self.shrink_target.record[i] > 0 {
179
- let mut attempt = self.shrink_target.record.clone();
180
- attempt[i] -= 1;
181
- let (succeeded, result) = self.execute(&attempt)?;
182
- if !succeeded && result.record.len() < self.shrink_target.record.len() {
183
- let mut j = 0;
184
- while j < self.shrink_target.draws.len() {
185
- // Having to copy this is an annoying consequence of lexical lifetimes -
186
- // if we borrowed it immutably then we'd not be allowed to call self.incorporate
187
- // down below. Fortunately these things are tiny structs of integers so it doesn't
188
- // really matter.
189
- let d = self.shrink_target.draws[j].clone();
190
- if d.start > i {
191
- let mut attempt2 = attempt.clone();
192
- attempt2.drain(d.start..d.end);
193
- if self.incorporate(&attempt2)? {
194
- break;
195
- }
196
- }
197
- j += 1;
198
- }
199
- }
200
- }
201
- i += 1;
202
- }
203
- Ok(())
204
- }
205
-
206
- fn reorder_blocks(&mut self) -> StepResult {
207
- let mut i = 0;
208
- while i < self.shrink_target.record.len() {
209
- let mut j = i + 1;
210
- while j < self.shrink_target.record.len() {
211
- assert!(i < self.shrink_target.record.len());
212
- if self.shrink_target.record[i] == 0 {
213
- break;
214
- }
215
- if self.shrink_target.record[j] < self.shrink_target.record[i] {
216
- let mut attempt = self.shrink_target.record.clone();
217
- attempt.swap(i, j);
218
- self.incorporate(&attempt)?;
219
- }
220
- j += 1;
221
- }
222
- i += 1;
223
- }
224
- Ok(())
225
- }
226
-
227
- fn try_delete_range(
228
- &mut self,
229
- target: &TestResult,
230
- i: usize,
231
- k: usize,
232
- ) -> Result<bool, LoopExitReason> {
233
- // Attempts to delete k non-overlapping draws starting from the draw at index i.
234
-
235
- let mut stack: Vec<(usize, usize)> = Vec::new();
236
- let mut j = i;
237
- while j < target.draws.len() && stack.len() < k {
238
- let m = target.draws[j].start;
239
- let n = target.draws[j].end;
240
- assert!(m < n);
241
- if m < n && (stack.len() == 0 || stack[stack.len() - 1].1 <= m) {
242
- stack.push((m, n))
243
- }
244
- j += 1;
245
- }
246
-
247
- let mut attempt = target.record.clone();
248
- while stack.len() > 0 {
249
- let (m, n) = stack.pop().unwrap();
250
- attempt.drain(m..n);
251
- }
252
-
253
- if attempt.len() >= self.shrink_target.record.len() {
254
- Ok(false)
255
- } else {
256
- self.incorporate(&attempt)
257
- }
258
- }
259
-
260
- fn adaptive_delete(&mut self) -> StepResult {
261
- let mut i = 0;
262
- let target = self.shrink_target.clone();
263
-
264
- while i < target.draws.len() {
265
- // This is an adaptive pass loosely modelled after timsort. If
266
- // little or nothing is deletable here then we don't try any more
267
- // deletions than the naive greedy algorithm would, but if it looks
268
- // like we have an opportunity to delete a lot then we try to do so.
269
-
270
- // What we're trying to do is to find a large k such that we can
271
- // delete k but not k + 1 draws starting from this point, and we
272
- // want to do that in O(log(k)) rather than O(k) test executions.
273
-
274
- // We try a quite careful sequence of small shrinks here before we
275
- // move on to anything big. This is because if we try to be
276
- // aggressive too early on we'll tend to find that we lose out when
277
- // the example is "nearly minimal".
278
- if self.try_delete_range(&target, i, 2)? {
279
- if self.try_delete_range(&target, i, 3)? && self.try_delete_range(&target, i, 4)? {
280
- let mut hi = 5;
281
- // At this point it looks like we've got a pretty good
282
- // opportunity for a long run here. We do an exponential
283
- // probe upwards to try and find some k where we can't
284
- // delete many intervals. We do this rather than choosing
285
- // that upper bound to immediately be large because we
286
- // don't really expect k to be huge. If it turns out that
287
- // it is, the subsequent example is going to be so tiny that
288
- // it doesn't really matter if we waste a bit of extra time
289
- // here.
290
- while self.try_delete_range(&target, i, hi)? {
291
- assert!(hi <= target.draws.len());
292
- hi *= 2;
293
- }
294
- // We now know that we can delete the first lo intervals but
295
- // not the first hi. We preserve that property while doing
296
- // a binary search to find the point at which we stop being
297
- // able to delete intervals.
298
- let mut lo = 4;
299
- while lo + 1 < hi {
300
- let mid = lo + (hi - lo) / 2;
301
- if self.try_delete_range(&target, i, mid)? {
302
- lo = mid;
303
- } else {
304
- hi = mid;
305
- }
306
- }
307
- }
308
- } else {
309
- self.try_delete_range(&target, i, 1)?;
310
- }
311
- // We unconditionally bump i because we have always tried deleting
312
- // one more example than we succeeded at deleting, so we expect the
313
- // next example to be undeletable.
314
- i += 1;
315
- }
316
- return Ok(());
317
- }
318
-
319
- fn delete_all_ranges(&mut self) -> StepResult {
320
- let mut i = 0;
321
- while i < self.shrink_target.record.len() {
322
- let start_length = self.shrink_target.record.len();
323
-
324
- let mut j = i + 1;
325
- while j < self.shrink_target.record.len() {
326
- assert!(j > i);
327
- let mut attempt = self.shrink_target.record.clone();
328
- attempt.drain(i..j);
329
- assert!(attempt.len() + (j - i) == self.shrink_target.record.len());
330
- let deleted = self.incorporate(&attempt)?;
331
- if !deleted {
332
- j += 1;
333
- }
334
- }
335
- if start_length == self.shrink_target.record.len() {
336
- i += 1;
337
- }
338
- }
339
- Ok(())
340
- }
341
-
342
- fn try_lowering_value(&mut self, i: usize, v: u64) -> Result<bool, LoopExitReason> {
343
- if v >= self.shrink_target.record[i] {
344
- return Ok(false);
345
- }
346
-
347
- let mut attempt = self.shrink_target.record.clone();
348
- attempt[i] = v;
349
- let (succeeded, result) = self.execute(&attempt)?;
350
- assert!(result.record.len() <= self.shrink_target.record.len());
351
- let lost_bytes = self.shrink_target.record.len() - result.record.len();
352
- if !succeeded && result.status == Status::Valid && lost_bytes > 0 {
353
- attempt.drain(i + 1..i + lost_bytes + 1);
354
- assert!(attempt.len() + lost_bytes == self.shrink_target.record.len());
355
- self.incorporate(&attempt)
356
- } else {
357
- Ok(succeeded)
358
- }
359
- }
360
-
361
- fn minimize_individual_blocks(&mut self) -> StepResult {
362
- let mut i = 0;
363
-
364
- while i < self.shrink_target.record.len() {
365
- if !self.shrink_target.written_indices.contains(&i) {
366
- minimize_integer(self.shrink_target.record[i], |v| {
367
- self.try_lowering_value(i, v)
368
- })?;
369
- }
370
-
371
- i += 1;
372
- }
373
-
374
- Ok(())
375
- }
376
-
377
- fn calc_duplicates(&self) -> Vec<Vec<usize>> {
378
- assert!(self.shrink_target.record.len() == self.shrink_target.sizes.len());
379
- let mut duplicates: HashMap<(u64, u64), Vec<usize>> = HashMap::new();
380
- for (i, (u, v)) in self.shrink_target
381
- .record
382
- .iter()
383
- .zip(self.shrink_target.sizes.iter())
384
- .enumerate()
385
- {
386
- if !self.shrink_target.written_indices.contains(&i) {
387
- duplicates
388
- .entry((*u, *v))
389
- .or_insert_with(|| Vec::new())
390
- .push(i);
391
- }
392
- }
393
-
394
- let mut result: Vec<Vec<usize>> = duplicates
395
- .drain()
396
- .filter_map(|(_, elements)| {
397
- if elements.len() > 1 {
398
- Some(elements)
399
- } else {
400
- None
401
- }
402
- })
403
- .collect();
404
- result.sort_by_key(|v| Reverse(v.len()));
405
- result
406
- }
407
-
408
- fn minimize_duplicated_blocks(&mut self) -> StepResult {
409
- let mut i = 0;
410
- let mut targets = self.calc_duplicates();
411
- while i < targets.len() {
412
- let target = mem::replace(&mut targets[i], Vec::new());
413
- i += 1;
414
- assert!(target.len() > 0);
415
- let v = self.shrink_target.record[target[0]];
416
- let base = self.shrink_target.record.clone();
417
-
418
- let w = minimize_integer(v, |t| {
419
- let mut attempt = base.clone();
420
- for i in &target {
421
- attempt[*i] = t
422
- }
423
- self.incorporate(&attempt)
424
- })?;
425
- if w != v {
426
- targets = self.calc_duplicates();
427
- }
428
- }
429
- Ok(())
430
- }
431
-
432
- fn execute(&mut self, buf: &DataStream) -> Result<(bool, TestResult), LoopExitReason> {
433
- // TODO: Later there will be caching here
434
- let result = self.main_loop.execute(DataSource::from_vec(buf.clone()))?;
435
- Ok((self.predicate(&result), result))
436
- }
437
-
438
- fn incorporate(&mut self, buf: &DataStream) -> Result<bool, LoopExitReason> {
439
- assert!(
440
- buf.len() <= self.shrink_target.record.len(),
441
- "Expected incorporate to not increase length, but buf.len() = {} \
442
- while shrink target was {}",
443
- buf.len(),
444
- self.shrink_target.record.len()
445
- );
446
- if buf.len() == self.shrink_target.record.len() {
447
- assert!(buf < &self.shrink_target.record);
448
- }
449
- if self.shrink_target.record.starts_with(buf) {
450
- return Ok(false);
451
- }
452
- let (succeeded, _) = self.execute(buf)?;
453
- Ok(succeeded)
454
- }
455
- }
456
-
457
- #[derive(Debug, Clone, Eq, PartialEq)]
458
- enum EngineState {
459
- AwaitingCompletion,
460
- ReadyToProvide,
461
- }
462
-
463
- #[derive(Debug)]
464
- pub struct Engine {
465
- // The next response from the main loop. Once
466
- // this is set to Some(Finished(_)) it stays that way,
467
- // otherwise it is cleared on access.
468
- loop_response: Option<LoopCommand>,
469
-
470
- state: EngineState,
471
-
472
- // Communication channels with the main testing loop
473
- handle: Option<thread::JoinHandle<()>>,
474
- receiver: Receiver<LoopCommand>,
475
- sender: SyncSender<TestResult>,
476
- }
477
-
478
- impl Clone for Engine {
479
- fn clone(&self) -> Engine {
480
- panic!("BUG: The Engine was unexpectedly cloned");
481
- }
482
- }
483
-
484
- impl Engine {
485
- pub fn new(max_examples: u64, seed: &[u32]) -> Engine {
486
- let (send_local, recv_remote) = sync_channel(1);
487
- let (send_remote, recv_local) = sync_channel(1);
488
-
489
- let main_loop = MainGenerationLoop {
490
- max_examples: max_examples,
491
- random: ChaChaRng::from_seed(seed),
492
- sender: send_remote,
493
- receiver: recv_remote,
494
- best_example: None,
495
- valid_examples: 0,
496
- invalid_examples: 0,
497
- interesting_examples: 0,
498
- };
499
-
500
- let handle = thread::Builder::new()
501
- .name("Hypothesis main loop".to_string())
502
- .spawn(move || {
503
- main_loop.run();
504
- })
505
- .unwrap();
506
-
507
- Engine {
508
- loop_response: None,
509
- sender: send_local,
510
- receiver: recv_local,
511
- handle: Some(handle),
512
- state: EngineState::ReadyToProvide,
513
- }
514
- }
515
-
516
- pub fn mark_finished(&mut self, source: DataSource, status: Status) -> () {
517
- self.consume_test_result(source.to_result(status))
518
- }
519
-
520
- pub fn next_source(&mut self) -> Option<DataSource> {
521
- assert!(self.state == EngineState::ReadyToProvide);
522
- self.state = EngineState::AwaitingCompletion;
523
-
524
- self.await_loop_response();
525
-
526
- let mut local_result = None;
527
- mem::swap(&mut local_result, &mut self.loop_response);
528
-
529
- match local_result {
530
- Some(LoopCommand::RunThis(source)) => return Some(source),
531
- None => panic!("BUG: Loop response should not be empty at this point"),
532
- _ => {
533
- self.loop_response = local_result;
534
- return None;
535
- }
536
- }
537
- }
538
-
539
- pub fn best_source(&self) -> Option<DataSource> {
540
- match &self.loop_response {
541
- &Some(LoopCommand::Finished(
542
- _,
543
- MainGenerationLoop {
544
- best_example: Some(ref result),
545
- ..
546
- },
547
- )) => Some(DataSource::from_vec(result.record.clone())),
548
- _ => None,
549
- }
550
- }
551
-
552
- fn consume_test_result(&mut self, result: TestResult) -> () {
553
- assert!(self.state == EngineState::AwaitingCompletion);
554
- self.state = EngineState::ReadyToProvide;
555
-
556
- if self.has_shutdown() {
557
- return ();
558
- }
559
-
560
- // NB: Deliberately not matching on result. If this fails,
561
- // that's OK - it means the loop has shut down and when we ask
562
- // for data from it we'll get its shutdown response.
563
- let _ = self.sender.send(result);
564
- }
565
-
566
- pub fn was_unsatisfiable(&self) -> bool {
567
- match &self.loop_response {
568
- &Some(LoopCommand::Finished(_, ref main_loop)) => {
569
- main_loop.interesting_examples == 0 && main_loop.valid_examples == 0
570
- }
571
- _ => false,
572
- }
573
- }
574
-
575
- fn has_shutdown(&mut self) -> bool {
576
- match &self.loop_response {
577
- &Some(LoopCommand::Finished(..)) => true,
578
- _ => false,
579
- }
580
- }
581
-
582
- fn await_thread_termination(&mut self) {
583
- let mut maybe_handle = None;
584
- mem::swap(&mut self.handle, &mut maybe_handle);
585
- if let Some(handle) = maybe_handle {
586
- if let Err(boxed_msg) = handle.join() {
587
- // FIXME: This is awful but as far as I can tell this is
588
- // genuinely the only way to get the actual message out of the
589
- // panic in the child thread! It's boxed as an Any, and the
590
- // debug of Any just says "Any". Fortunately the main loop is
591
- // very much under our control so this doesn't matter too much
592
- // here, but yuck!
593
- if let Some(msg) = boxed_msg.downcast_ref::<&str>() {
594
- panic!(msg.to_string());
595
- } else if let Some(msg) = boxed_msg.downcast_ref::<String>() {
596
- panic!(msg.clone());
597
- } else {
598
- panic!("BUG: Unexpected panic format in main loop");
599
- }
600
- }
601
- }
602
- }
603
-
604
- fn await_loop_response(&mut self) -> () {
605
- if self.loop_response.is_none() {
606
- match self.receiver.recv() {
607
- Ok(response) => {
608
- self.loop_response = Some(response);
609
- if self.has_shutdown() {
610
- self.await_thread_termination();
611
- }
612
- }
613
- Err(_) => {
614
- self.await_thread_termination();
615
- panic!("BUG: Unexpected silent termination of generation loop.")
616
- }
617
- }
618
- }
619
- }
620
- }
data/src/intminimize.rs DELETED
@@ -1,149 +0,0 @@
1
- use std::cmp::min;
2
-
3
- const SMALL: u64 = 5;
4
-
5
- struct Minimizer<'a, F: 'a> {
6
- criterion: &'a mut F,
7
- best: u64,
8
- }
9
-
10
- impl<'a, F, T> Minimizer<'a, F>
11
- where
12
- F: 'a + FnMut(u64) -> Result<bool, T>,
13
- {
14
- fn test(&mut self, candidate: u64) -> Result<bool, T> {
15
- if candidate == self.best {
16
- return Ok(true);
17
- }
18
- if candidate > self.best {
19
- return Ok(false);
20
- }
21
- let result = (self.criterion)(candidate)?;
22
- if result {
23
- self.best = candidate;
24
- }
25
- Ok(result)
26
- }
27
-
28
- fn modify<G>(&mut self, g: G) -> Result<bool, T>
29
- where
30
- G: Fn(u64) -> u64,
31
- {
32
- let x = g(self.best);
33
- self.test(x)
34
- }
35
- }
36
-
37
- pub fn minimize_integer<F, T>(start: u64, mut criterion: F) -> Result<u64, T>
38
- where
39
- F: FnMut(u64) -> Result<bool, T>,
40
- {
41
- if start == 0 {
42
- return Ok(start);
43
- }
44
-
45
- for i in 0..min(start, SMALL) {
46
- if criterion(i)? {
47
- return Ok(i);
48
- }
49
- }
50
- if start <= SMALL {
51
- return Ok(start);
52
- }
53
-
54
- let mut minimizer = Minimizer {
55
- best: start,
56
- criterion: &mut criterion,
57
- };
58
-
59
- loop {
60
- if !minimizer.modify(|x| x >> 1)? {
61
- break;
62
- }
63
- }
64
-
65
- for i in 0..64 {
66
- minimizer.modify(|x| x ^ (1 << i))?;
67
- }
68
-
69
- assert!(minimizer.best >= SMALL);
70
-
71
- for i in 0..64 {
72
- let left_mask = 1 << i;
73
- let mut right_mask = left_mask >> 1;
74
- while right_mask != 0 {
75
- minimizer.modify(|x| {
76
- if x & left_mask == 0 || x & right_mask != 0 {
77
- x
78
- } else {
79
- x ^ (right_mask | left_mask)
80
- }
81
- })?;
82
- right_mask >>= 1;
83
- }
84
- }
85
-
86
- if !minimizer.modify(|x| x - 1)? {
87
- return Ok(minimizer.best);
88
- }
89
-
90
- let mut lo = 0;
91
- let mut hi = minimizer.best;
92
- while lo + 1 < hi {
93
- let mid = lo + (hi - lo) / 2;
94
- if minimizer.test(mid)? {
95
- hi = mid;
96
- } else {
97
- lo = mid;
98
- }
99
- }
100
-
101
- Ok(minimizer.best)
102
- }
103
-
104
- #[cfg(test)]
105
- mod tests {
106
- use super::*;
107
-
108
- fn non_failing_minimize<F>(start: u64, criterion: F) -> u64
109
- where
110
- F: Fn(u64) -> bool,
111
- {
112
- let mut best = start;
113
-
114
- loop {
115
- let ran: Result<u64, ()> = minimize_integer(best, |x| Ok(criterion(x)));
116
- let result = ran.unwrap();
117
- assert!(result <= best);
118
- if result == best {
119
- return best;
120
- }
121
- best = result;
122
- }
123
- }
124
-
125
- #[test]
126
- fn minimize_down_to() {
127
- let n = non_failing_minimize(100, |x| x >= 10);
128
- assert_eq!(n, 10);
129
- }
130
-
131
- #[test]
132
- fn unset_relevant_bits() {
133
- let x = 0b101010101010;
134
- let y = 0b111111111111;
135
- let n = non_failing_minimize(y, |k| k & x == x);
136
- assert_eq!(n, x);
137
- }
138
-
139
- #[test]
140
- fn sort_bits() {
141
- let x: u64 = 0b1011011011000111;
142
- let y: u64 = 0b0000001111111111;
143
- let c = x.count_ones();
144
- assert_eq!(c, y.count_ones());
145
-
146
- let n = non_failing_minimize(x, |k| k.count_ones() == c);
147
- assert_eq!(y, n);
148
- }
149
- }