hypothesis-specs 0.0.3

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