hypothesis-specs 0.0.3

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.
@@ -0,0 +1,44 @@
1
+ # frozen_string_literal: true
2
+
3
+ module Hypothesis
4
+ # A TestCase class provides a concrete representation of
5
+ # an executing test case. You do not normally need to use this
6
+ # within the body of the test, but it exists to be used as
7
+ # an argument to {Hypothesis::Possibilities::built_as}.
8
+ # @!visibility private
9
+ class TestCase
10
+ # @!visibility private
11
+ attr_reader :draws, :print_log, :print_draws, :wrapped_data
12
+
13
+ # @!visibility private
14
+ def initialize(wrapped_data, print_draws: false, record_draws: false)
15
+ @wrapped_data = wrapped_data
16
+
17
+ @draws = [] if record_draws
18
+ @print_log = [] if print_draws
19
+ @depth = 0
20
+ end
21
+
22
+ def assume(condition)
23
+ raise UnsatisfiedAssumption unless condition
24
+ end
25
+
26
+ # @!visibility private
27
+ def any(possible = nil, name: nil, &block)
28
+ top_level = @depth.zero?
29
+
30
+ begin
31
+ @depth += 1
32
+ possible ||= block
33
+ result = possible.provide(&block)
34
+ if top_level
35
+ draws&.push(result)
36
+ print_log&.push([name, result.inspect])
37
+ end
38
+ result
39
+ ensure
40
+ @depth -= 1
41
+ end
42
+ end
43
+ end
44
+ end
@@ -0,0 +1,9 @@
1
+ # frozen_string_literal: true
2
+
3
+ module Hypothesis
4
+ module World
5
+ class << self
6
+ attr_accessor :current_engine
7
+ end
8
+ end
9
+ end
data/src/data.rs ADDED
@@ -0,0 +1,99 @@
1
+ // Module representing core data types that Hypothesis
2
+ // needs.
3
+
4
+ use rand::{ChaChaRng, Rng};
5
+
6
+ pub type DataStream = Vec<u64>;
7
+
8
+ #[derive(Debug, Clone)]
9
+ pub struct FailedDraw;
10
+
11
+ #[derive(Debug, Clone)]
12
+ enum BitGenerator {
13
+ Random(ChaChaRng),
14
+ Recorded(DataStream),
15
+ }
16
+
17
+ // Main entry point for running a test:
18
+ // A test function takes a DataSource, uses it to
19
+ // produce some data, and the DataSource records the
20
+ // relevant information about what they did.
21
+ #[derive(Debug, Clone)]
22
+ pub struct DataSource {
23
+ bitgenerator: BitGenerator,
24
+ record: DataStream,
25
+ }
26
+
27
+ impl DataSource {
28
+ pub fn bits(&mut self, n_bits: u64) -> Result<u64, FailedDraw> {
29
+ let mut result = match self.bitgenerator {
30
+ BitGenerator::Random(ref mut random) => random.next_u64(),
31
+ BitGenerator::Recorded(ref mut v) => if self.record.len() >= v.len() {
32
+ return Err(FailedDraw);
33
+ } else {
34
+ v[self.record.len()]
35
+ },
36
+ };
37
+
38
+ if n_bits < 64 {
39
+ let mask = (1 << n_bits) - 1;
40
+ result &= mask;
41
+ };
42
+
43
+ self.record.push(result);
44
+
45
+ return Ok(result);
46
+ }
47
+
48
+ fn new(generator: BitGenerator) -> DataSource {
49
+ return DataSource {
50
+ bitgenerator: generator,
51
+ record: DataStream::new(),
52
+ };
53
+ }
54
+
55
+ pub fn from_random(random: ChaChaRng) -> DataSource {
56
+ return DataSource::new(BitGenerator::Random(random));
57
+ }
58
+
59
+ pub fn from_vec(record: DataStream) -> DataSource {
60
+ return DataSource::new(BitGenerator::Recorded(record));
61
+ }
62
+
63
+ pub fn to_result(self, status: Status) -> TestResult {
64
+ TestResult {
65
+ record: self.record,
66
+ status: status,
67
+ }
68
+ }
69
+ }
70
+
71
+ // Status indicates the result that we got from completing
72
+ // a single test execution.
73
+ #[derive(Debug, Clone, Eq, PartialEq)]
74
+ pub enum Status {
75
+ // The test tried to read more data than we had for it.
76
+ Overflow,
77
+
78
+ // Some important precondition of the test was not
79
+ // satisfied.
80
+ Invalid,
81
+
82
+ // This test ran successfully to completion without
83
+ // anything of note happening.
84
+ Valid,
85
+
86
+ // This was an interesting test execution! (Usually this
87
+ // means failing, but for things like find it may not).
88
+ Interesting,
89
+ }
90
+
91
+ // Once a data source is finished it "decays" to a
92
+ // TestResult, that retains a trace of all the information
93
+ // we needed from the DataSource. It is these we keep around,
94
+ // not the original DataSource objects.
95
+ #[derive(Debug, Clone)]
96
+ pub struct TestResult {
97
+ pub record: DataStream,
98
+ pub status: Status,
99
+ }
@@ -0,0 +1,238 @@
1
+ use data::{DataSource, FailedDraw};
2
+
3
+ use std::collections::BinaryHeap;
4
+ use std::mem;
5
+ use std::cmp::{Ord, Ordering, PartialOrd, Reverse};
6
+
7
+ type Draw<T> = Result<T, FailedDraw>;
8
+
9
+ pub fn weighted(source: &mut DataSource, probability: f64) -> Result<bool, FailedDraw> {
10
+ // TODO: Less bit-hungry implementation.
11
+
12
+ let truthy = (probability * (u64::max_value() as f64 + 1.0)).floor() as u64;
13
+ let probe = source.bits(64)?;
14
+ return Ok(probe >= u64::max_value() - truthy + 1);
15
+ }
16
+
17
+ pub fn bounded_int(source: &mut DataSource, max: u64) -> Draw<u64> {
18
+ let bitlength = 64 - max.leading_zeros() as u64;
19
+ if bitlength == 0 {
20
+ return Ok(0);
21
+ }
22
+ loop {
23
+ let probe = source.bits(bitlength)?;
24
+ if probe <= max {
25
+ return Ok(probe);
26
+ }
27
+ }
28
+ }
29
+
30
+ #[derive(Debug, Clone)]
31
+ pub struct Repeat {
32
+ min_count: u64,
33
+ max_count: u64,
34
+ p_continue: f64,
35
+
36
+ current_count: u64,
37
+ }
38
+
39
+ impl Repeat {
40
+ pub fn new(min_count: u64, max_count: u64, expected_count: f64) -> Repeat {
41
+ Repeat {
42
+ min_count: min_count,
43
+ max_count: max_count,
44
+ p_continue: 1.0 - 1.0 / (1.0 + expected_count),
45
+ current_count: 0,
46
+ }
47
+ }
48
+
49
+ fn draw_until(&self, source: &mut DataSource, value: bool) -> Result<(), FailedDraw> {
50
+ // Force a draw until we get the desired outcome. By having this we get much better
51
+ // shrinking when min_size or max_size are set because all decisions are represented
52
+ // somewhere in the bit stream.
53
+ loop {
54
+ let d = weighted(source, self.p_continue)?;
55
+ if d == value {
56
+ return Ok(());
57
+ }
58
+ }
59
+ }
60
+
61
+ pub fn reject(&mut self) {
62
+ assert!(self.current_count > 0);
63
+ self.current_count -= 1;
64
+ }
65
+
66
+ pub fn should_continue(&mut self, source: &mut DataSource) -> Result<bool, FailedDraw> {
67
+ if self.current_count < self.min_count {
68
+ self.draw_until(source, true)?;
69
+ self.current_count += 1;
70
+ return Ok(true);
71
+ } else if self.current_count >= self.max_count {
72
+ self.draw_until(source, false)?;
73
+ return Ok(false);
74
+ }
75
+
76
+ let result = weighted(source, self.p_continue)?;
77
+ if result {
78
+ self.current_count += 1;
79
+ } else {
80
+ }
81
+ return Ok(result);
82
+ }
83
+ }
84
+
85
+ #[derive(Debug, Clone)]
86
+ struct SamplerEntry {
87
+ primary: usize,
88
+ alternate: usize,
89
+ use_alternate: f32,
90
+ }
91
+
92
+ impl SamplerEntry {
93
+ fn single(i: usize) -> SamplerEntry {
94
+ SamplerEntry {
95
+ primary: i,
96
+ alternate: i,
97
+ use_alternate: 0.0,
98
+ }
99
+ }
100
+ }
101
+
102
+ impl Ord for SamplerEntry {
103
+ fn cmp(&self, other: &SamplerEntry) -> Ordering {
104
+ return self.primary
105
+ .cmp(&other.primary)
106
+ .then(self.alternate.cmp(&other.alternate));
107
+ }
108
+ }
109
+
110
+ impl PartialOrd for SamplerEntry {
111
+ fn partial_cmp(&self, other: &SamplerEntry) -> Option<Ordering> {
112
+ return Some(self.cmp(other));
113
+ }
114
+ }
115
+
116
+ impl PartialEq for SamplerEntry {
117
+ fn eq(&self, other: &SamplerEntry) -> bool {
118
+ return self.cmp(other) == Ordering::Equal;
119
+ }
120
+ }
121
+
122
+ impl Eq for SamplerEntry {}
123
+
124
+ #[derive(Debug, Clone)]
125
+ pub struct Sampler {
126
+ table: Vec<SamplerEntry>,
127
+ }
128
+
129
+ impl Sampler {
130
+ pub fn new(weights: Vec<f32>) -> Sampler {
131
+ // FIXME: The correct thing to do here is to allow this,
132
+ // return early, and make this reject the data, but we don't
133
+ // currently have the status built into our data properly...
134
+ assert!(weights.len() > 0);
135
+
136
+ let mut table = Vec::new();
137
+
138
+ let mut small = BinaryHeap::new();
139
+ let mut large = BinaryHeap::new();
140
+
141
+ let total: f32 = weights.iter().sum();
142
+
143
+ let mut scaled_probabilities = Vec::new();
144
+
145
+ let n = weights.len() as f32;
146
+
147
+ for (i, w) in weights.iter().enumerate() {
148
+ let scaled = n * w / total;
149
+ scaled_probabilities.push(scaled);
150
+ if scaled == 1.0 {
151
+ table.push(SamplerEntry::single(i))
152
+ } else if scaled > 1.0 {
153
+ large.push(Reverse(i));
154
+ } else {
155
+ assert!(scaled < 1.0);
156
+ small.push(Reverse(i));
157
+ }
158
+ }
159
+
160
+ while !(small.is_empty() || large.is_empty()) {
161
+ let Reverse(lo) = small.pop().unwrap();
162
+ let Reverse(hi) = large.pop().unwrap();
163
+
164
+ assert!(lo != hi);
165
+ assert!(scaled_probabilities[hi] > 1.0);
166
+ assert!(scaled_probabilities[lo] < 1.0);
167
+ scaled_probabilities[hi] = (scaled_probabilities[hi] + scaled_probabilities[lo]) - 1.0;
168
+ table.push(SamplerEntry {
169
+ primary: lo,
170
+ alternate: hi,
171
+ use_alternate: 1.0 - scaled_probabilities[lo],
172
+ });
173
+
174
+ if scaled_probabilities[hi] < 1.0 {
175
+ small.push(Reverse(hi))
176
+ } else if scaled_probabilities[hi] > 1.0 {
177
+ large.push(Reverse(hi))
178
+ } else {
179
+ table.push(SamplerEntry::single(hi))
180
+ }
181
+ }
182
+ for &Reverse(i) in small.iter() {
183
+ table.push(SamplerEntry::single(i))
184
+ }
185
+ for &Reverse(i) in large.iter() {
186
+ table.push(SamplerEntry::single(i))
187
+ }
188
+
189
+ for ref mut entry in table.iter_mut() {
190
+ if entry.alternate < entry.primary {
191
+ mem::swap(&mut entry.primary, &mut entry.alternate);
192
+ entry.use_alternate = 1.0 - entry.use_alternate;
193
+ }
194
+ }
195
+
196
+ table.sort();
197
+ assert!(table.len() > 0);
198
+ return Sampler { table: table };
199
+ }
200
+
201
+ pub fn sample(&self, source: &mut DataSource) -> Draw<usize> {
202
+ assert!(self.table.len() > 0);
203
+ let i = bounded_int(source, self.table.len() as u64 - 1)? as usize;
204
+ let entry = &self.table[i];
205
+ let use_alternate = weighted(source, entry.use_alternate as f64)?;
206
+ if use_alternate {
207
+ Ok(entry.alternate)
208
+ } else {
209
+ Ok(entry.primary)
210
+ }
211
+ }
212
+ }
213
+
214
+ pub fn good_bitlengths() -> Sampler {
215
+ let weights = vec!(
216
+ 4.0, 4.0, 4.0, 4.0, 4.0, 4.0, 4.0, 4.0, // 1 byte
217
+ 2.0, 2.0, 2.0, 2.0, 2.0, 2.0, 2.0, 2.0, // 2 bytes
218
+ 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, // 3 bytes
219
+ 0.5, 0.5, 0.5, 0.5, 0.5, 0.5, 0.5, 0.5, // 4 bytes
220
+ 0.1, 0.1, 0.1, 0.1, 0.1, 0.1, 0.1, 0.1, // 5 bytes
221
+ 0.1, 0.1, 0.1, 0.1, 0.1, 0.1, 0.1, 0.1, // 6 bytes
222
+ 0.1, 0.1, 0.1, 0.1, 0.1, 0.1, 0.1, 0.1, // 7 bytes
223
+ 0.1, 0.1, 0.1, 0.1, 0.1, 0.1, 0.1, // 8 bytes (last bit spare for sign)
224
+ );
225
+ assert!(weights.len() == 63);
226
+ Sampler::new(weights)
227
+ }
228
+
229
+ pub fn integer_from_bitlengths(source: &mut DataSource, bitlengths: &Sampler) -> Draw<i64> {
230
+ let bitlength = bitlengths.sample(source)? as u64 + 1;
231
+ let base = source.bits(bitlength)? as i64;
232
+ let sign = source.bits(1)?;
233
+ if sign > 0 {
234
+ Ok(-base)
235
+ } else {
236
+ Ok(base)
237
+ }
238
+ }
data/src/engine.rs ADDED
@@ -0,0 +1,400 @@
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::sync::mpsc::{sync_channel, Receiver, SyncSender};
7
+ use std::thread;
8
+ use std::mem;
9
+
10
+ use data::{DataSource, DataStream, Status, TestResult};
11
+
12
+ #[derive(Debug, Clone)]
13
+ enum LoopExitReason {
14
+ Complete,
15
+ MaxExamples,
16
+ //MaxShrinks,
17
+ Shutdown,
18
+ //Error(String),
19
+ }
20
+
21
+ #[derive(Debug)]
22
+ enum LoopCommand {
23
+ RunThis(DataSource),
24
+ Finished(LoopExitReason, MainGenerationLoop),
25
+ }
26
+
27
+ #[derive(Debug)]
28
+ struct MainGenerationLoop {
29
+ receiver: Receiver<TestResult>,
30
+ sender: SyncSender<LoopCommand>,
31
+ max_examples: u64,
32
+ random: ChaChaRng,
33
+
34
+ best_example: Option<TestResult>,
35
+
36
+ valid_examples: u64,
37
+ invalid_examples: u64,
38
+ interesting_examples: u64,
39
+ }
40
+
41
+ type StepResult = Result<(), LoopExitReason>;
42
+
43
+ impl MainGenerationLoop {
44
+ fn run(mut self) {
45
+ let result = self.loop_body();
46
+ match result {
47
+ // Silent shutdown when the main thread terminates
48
+ Err(LoopExitReason::Shutdown) => (),
49
+ Err(reason) => {
50
+ // Must clone because otherwise it is borrowed.
51
+ let shutdown_sender = self.sender.clone();
52
+ shutdown_sender
53
+ .send(LoopCommand::Finished(reason, self))
54
+ .unwrap()
55
+ }
56
+ Ok(_) => panic!("BUG: Generation loop was not supposed to return normally."),
57
+ }
58
+ }
59
+
60
+ fn loop_body(&mut self) -> StepResult {
61
+ let interesting_example = self.generate_examples()?;
62
+
63
+ let mut shrinker = Shrinker::new(self, interesting_example, |r| {
64
+ r.status == Status::Interesting
65
+ });
66
+
67
+ shrinker.run()?;
68
+
69
+ return Err(LoopExitReason::Complete);
70
+ }
71
+
72
+ fn generate_examples(&mut self) -> Result<TestResult, LoopExitReason> {
73
+ while self.valid_examples < self.max_examples
74
+ && self.invalid_examples < 10 * self.max_examples
75
+ {
76
+ let r = self.random.gen();
77
+ let result = self.execute(DataSource::from_random(r))?;
78
+ if result.status == Status::Interesting {
79
+ return Ok(result);
80
+ }
81
+ }
82
+ return Err(LoopExitReason::MaxExamples);
83
+ }
84
+
85
+ fn execute(&mut self, source: DataSource) -> Result<TestResult, LoopExitReason> {
86
+ let result = match self.sender.send(LoopCommand::RunThis(source)) {
87
+ Ok(_) => match self.receiver.recv() {
88
+ Ok(t) => t,
89
+ Err(_) => return Err(LoopExitReason::Shutdown),
90
+ },
91
+ Err(_) => return Err(LoopExitReason::Shutdown),
92
+ };
93
+ match result.status {
94
+ Status::Overflow => (),
95
+ Status::Invalid => self.invalid_examples += 1,
96
+ Status::Valid => self.valid_examples += 1,
97
+ Status::Interesting => {
98
+ self.best_example = Some(result.clone());
99
+ self.interesting_examples += 1;
100
+ }
101
+ }
102
+
103
+ Ok(result)
104
+ }
105
+ }
106
+
107
+ struct Shrinker<'owner, Predicate> {
108
+ _predicate: Predicate,
109
+ shrink_target: TestResult,
110
+ changes: u64,
111
+ main_loop: &'owner mut MainGenerationLoop,
112
+ }
113
+
114
+ impl<'owner, Predicate> Shrinker<'owner, Predicate>
115
+ where
116
+ Predicate: Fn(&TestResult) -> bool,
117
+ {
118
+ fn new(
119
+ main_loop: &'owner mut MainGenerationLoop,
120
+ shrink_target: TestResult,
121
+ predicate: Predicate,
122
+ ) -> Shrinker<'owner, Predicate> {
123
+ assert!(predicate(&shrink_target));
124
+ Shrinker {
125
+ main_loop: main_loop,
126
+ _predicate: predicate,
127
+ shrink_target: shrink_target,
128
+ changes: 0,
129
+ }
130
+ }
131
+
132
+ fn predicate(&mut self, result: &TestResult) -> bool {
133
+ let succeeded = (self._predicate)(result);
134
+ if succeeded {
135
+ self.changes += 1;
136
+ self.shrink_target = result.clone();
137
+ }
138
+ succeeded
139
+ }
140
+
141
+ fn run(&mut self) -> StepResult {
142
+ let mut prev = self.changes + 1;
143
+
144
+ while prev != self.changes {
145
+ prev = self.changes;
146
+ self.binary_search_blocks()?;
147
+ self.remove_intervals()?;
148
+ }
149
+ Ok(())
150
+ }
151
+
152
+ fn remove_intervals(&mut self) -> StepResult {
153
+ // TODO: Actually track the data we need to make this
154
+ // not quadratic.
155
+ let mut i = 0;
156
+ while i < self.shrink_target.record.len() {
157
+ let start_length = self.shrink_target.record.len();
158
+
159
+ let mut j = i + 1;
160
+ while j < self.shrink_target.record.len() {
161
+ assert!(j > i);
162
+ let mut attempt = self.shrink_target.record.clone();
163
+ attempt.drain(i..j);
164
+ assert!(attempt.len() + (j - i) == self.shrink_target.record.len());
165
+ let deleted = self.incorporate(&attempt)?;
166
+ if !deleted {
167
+ j += 1;
168
+ }
169
+ }
170
+ if start_length == self.shrink_target.record.len() {
171
+ i += 1;
172
+ }
173
+ }
174
+ Ok(())
175
+ }
176
+
177
+ fn binary_search_blocks(&mut self) -> StepResult {
178
+ let mut i = 0;
179
+
180
+ let mut attempt = self.shrink_target.record.clone();
181
+
182
+ while i < self.shrink_target.record.len() {
183
+ assert!(attempt.len() >= self.shrink_target.record.len());
184
+
185
+ let mut hi = self.shrink_target.record[i];
186
+
187
+ if hi > 0 {
188
+ attempt[i] = 0;
189
+ let zeroed = self.incorporate(&attempt)?;
190
+ if !zeroed {
191
+ let mut lo = 0;
192
+ // Binary search to find the smallest value we can
193
+ // replace this with.
194
+ while lo + 1 < hi {
195
+ let mid = lo + (hi - lo) / 2;
196
+ attempt[i] = mid;
197
+ let succeeded = self.incorporate(&attempt)?;
198
+ if succeeded {
199
+ attempt = self.shrink_target.record.clone();
200
+ hi = mid;
201
+ } else {
202
+ attempt[i] = self.shrink_target.record[i];
203
+ lo = mid;
204
+ }
205
+ }
206
+ attempt[i] = hi;
207
+ } else {
208
+ attempt = self.shrink_target.record.clone();
209
+ }
210
+ }
211
+
212
+ i += 1;
213
+ }
214
+
215
+ Ok(())
216
+ }
217
+
218
+ fn incorporate(&mut self, buf: &DataStream) -> Result<bool, LoopExitReason> {
219
+ assert!(
220
+ buf.len() <= self.shrink_target.record.len(),
221
+ "Expected incorporate to not increase length, but buf.len() = {} \
222
+ while shrink target was {}",
223
+ buf.len(),
224
+ self.shrink_target.record.len()
225
+ );
226
+ if buf.len() == self.shrink_target.record.len() {
227
+ assert!(buf < &self.shrink_target.record);
228
+ }
229
+ if self.shrink_target.record.starts_with(buf) {
230
+ return Ok(false);
231
+ }
232
+ let result = self.main_loop.execute(DataSource::from_vec(buf.clone()))?;
233
+ return Ok(self.predicate(&result));
234
+ }
235
+ }
236
+
237
+ #[derive(Debug, Clone, Eq, PartialEq)]
238
+ enum EngineState {
239
+ AwaitingCompletion,
240
+ ReadyToProvide,
241
+ }
242
+
243
+ #[derive(Debug)]
244
+ pub struct Engine {
245
+ // The next response from the main loop. Once
246
+ // this is set to Some(Finished(_)) it stays that way,
247
+ // otherwise it is cleared on access.
248
+ loop_response: Option<LoopCommand>,
249
+
250
+ state: EngineState,
251
+
252
+ // Communication channels with the main testing loop
253
+ handle: Option<thread::JoinHandle<()>>,
254
+ receiver: Receiver<LoopCommand>,
255
+ sender: SyncSender<TestResult>,
256
+ }
257
+
258
+ impl Clone for Engine {
259
+ fn clone(&self) -> Engine {
260
+ panic!("BUG: The Engine was unexpectedly cloned");
261
+ }
262
+ }
263
+
264
+ impl Engine {
265
+ pub fn new(max_examples: u64, seed: &[u32]) -> Engine {
266
+ let (send_local, recv_remote) = sync_channel(1);
267
+ let (send_remote, recv_local) = sync_channel(1);
268
+
269
+ let main_loop = MainGenerationLoop {
270
+ max_examples: max_examples,
271
+ random: ChaChaRng::from_seed(seed),
272
+ sender: send_remote,
273
+ receiver: recv_remote,
274
+ best_example: None,
275
+ valid_examples: 0,
276
+ invalid_examples: 0,
277
+ interesting_examples: 0,
278
+ };
279
+
280
+ let handle = thread::Builder::new()
281
+ .name("Hypothesis main loop".to_string())
282
+ .spawn(move || {
283
+ main_loop.run();
284
+ })
285
+ .unwrap();
286
+
287
+ Engine {
288
+ loop_response: None,
289
+ sender: send_local,
290
+ receiver: recv_local,
291
+ handle: Some(handle),
292
+ state: EngineState::ReadyToProvide,
293
+ }
294
+ }
295
+
296
+ pub fn mark_finished(&mut self, source: DataSource, status: Status) -> () {
297
+ self.consume_test_result(source.to_result(status))
298
+ }
299
+
300
+ pub fn next_source(&mut self) -> Option<DataSource> {
301
+ assert!(self.state == EngineState::ReadyToProvide);
302
+ self.state = EngineState::AwaitingCompletion;
303
+
304
+ self.await_loop_response();
305
+
306
+ let mut local_result = None;
307
+ mem::swap(&mut local_result, &mut self.loop_response);
308
+
309
+ match local_result {
310
+ Some(LoopCommand::RunThis(source)) => return Some(source),
311
+ None => panic!("BUG: Loop response should not be empty at this point"),
312
+ _ => {
313
+ self.loop_response = local_result;
314
+ return None;
315
+ }
316
+ }
317
+ }
318
+
319
+ pub fn best_source(&self) -> Option<DataSource> {
320
+ match &self.loop_response {
321
+ &Some(LoopCommand::Finished(
322
+ _,
323
+ MainGenerationLoop {
324
+ best_example: Some(ref result),
325
+ ..
326
+ },
327
+ )) => Some(DataSource::from_vec(result.record.clone())),
328
+ _ => None,
329
+ }
330
+ }
331
+
332
+ fn consume_test_result(&mut self, result: TestResult) -> () {
333
+ assert!(self.state == EngineState::AwaitingCompletion);
334
+ self.state = EngineState::ReadyToProvide;
335
+
336
+ if self.has_shutdown() {
337
+ return ();
338
+ }
339
+
340
+ // NB: Deliberately not matching on result. If this fails,
341
+ // that's OK - it means the loop has shut down and when we ask
342
+ // for data from it we'll get its shutdown response.
343
+ let _ = self.sender.send(result);
344
+ }
345
+
346
+ pub fn was_unsatisfiable(&self) -> bool {
347
+ match &self.loop_response {
348
+ &Some(LoopCommand::Finished(_, ref main_loop)) => {
349
+ main_loop.interesting_examples == 0 && main_loop.valid_examples == 0
350
+ }
351
+ _ => false,
352
+ }
353
+ }
354
+
355
+ fn has_shutdown(&mut self) -> bool {
356
+ match &self.loop_response {
357
+ &Some(LoopCommand::Finished(..)) => true,
358
+ _ => false,
359
+ }
360
+ }
361
+
362
+ fn await_thread_termination(&mut self) {
363
+ let mut maybe_handle = None;
364
+ mem::swap(&mut self.handle, &mut maybe_handle);
365
+ if let Some(handle) = maybe_handle {
366
+ if let Err(boxed_msg) = handle.join() {
367
+ // FIXME: This is awful but as far as I can tell this is
368
+ // genuinely the only way to get the actual message out of the
369
+ // panic in the child thread! It's boxed as an Any, and the
370
+ // debug of Any just says "Any". Fortunately the main loop is
371
+ // very much under our control so this doesn't matter too much
372
+ // here, but yuck!
373
+ if let Some(msg) = boxed_msg.downcast_ref::<&str>() {
374
+ panic!(msg.to_string());
375
+ } else if let Some(msg) = boxed_msg.downcast_ref::<String>() {
376
+ panic!(msg.clone());
377
+ } else {
378
+ panic!("BUG: Unexpected panic format in main loop");
379
+ }
380
+ }
381
+ }
382
+ }
383
+
384
+ fn await_loop_response(&mut self) -> () {
385
+ if self.loop_response.is_none() {
386
+ match self.receiver.recv() {
387
+ Ok(response) => {
388
+ self.loop_response = Some(response);
389
+ if self.has_shutdown() {
390
+ self.await_thread_termination();
391
+ }
392
+ }
393
+ Err(_) => {
394
+ self.await_thread_termination();
395
+ panic!("BUG: Unexpected silent termination of generation loop.")
396
+ }
397
+ }
398
+ }
399
+ }
400
+ }