hypothesis-specs 0.0.12 → 0.0.13

Sign up to get free protection for your applications and to get access to all the features.
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
- }