hypothesis-specs 0.1.2 → 0.6.0

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: 8e514de73ab86bfeae1d5e5801ee47e8865c4a06d90f2053f4318f9dd9df19cb
4
- data.tar.gz: 1aff7880e50e37ee73f287ac2cd318a9201cf2ba09d45ca79ad9b778eeb2d3bd
3
+ metadata.gz: 8be81d7cc1c3be388507a5776c3ec411b382e8292be1678bc30ffcccac5678f7
4
+ data.tar.gz: 269f363638fe7d27823b159564772edd31a078127d04ac65fc04dd6b1404f2f0
5
5
  SHA512:
6
- metadata.gz: fef51bf7876fc9b0b27363e8f73f84fa4ffde68a6ac2312b9f70bd780da1dd9a8923eeff14e092679c52d1fc74136a2529ae468a4e748ad021212e391846156b
7
- data.tar.gz: 1ba9eb0a28cde4f29a127086ecb2fdce722566cc38b3cd4cfc741ec91b704501d293c10b00828ae7efcd4df931e44518e01e312da15b8a55ef0b946573b90c6b
6
+ metadata.gz: 34ec25611305e1742428cbba1f1a171d2b5fc5068d96a6bacf565c24fe38c7ad4b5ff3100cb86138f744bd7276a413e3c03935704593be352063d176dacbb9ba
7
+ data.tar.gz: 27212f3409766297782d0e7025aff4012d72bf20c61325e456172adee58076cd48b159ea912c06fe7b3c5045f99631f987df79aa5f65139d7f39b70705a658f6
@@ -1,3 +1,43 @@
1
+ # Hypothesis for Ruby 0.6.0 (2021-01-27)
2
+
3
+ Adds support for skipping shrinking. While shrinking is extremely helpful and important in general, it has the potential to be quite time consuming. It can be useful to observe a raw failure before choosing to allow the engine to try to shrink. [hypothesis-python](https://hypothesis.readthedocs.io/en/latest/settings.html#phases) already provides the ability to skip shrinking, so there is precedent for this being useful. While `hypothesis-ruby` does not have the concept of other "Phases" yet, we can still start off the API by using this concept.
4
+
5
+ Usage:
6
+
7
+ ```
8
+ hypothesis(phases: Phase.excluding(:shrink)) do
9
+ # Failures here will be displayed directly and shrinking will be avoided
10
+ end
11
+ ```
12
+
13
+ # Hypothesis for Ruby 0.5.0 (2021-01-25)
14
+
15
+ Adds support for skipping shrinking. While shrinking is extremely helpful and important in general, it has the potential to be quite time consuming. It can be useful to observe a raw failure before choosing to allow the engine to try to shrink. [hypothesis-python](https://hypothesis.readthedocs.io/en/latest/settings.html#phases) already provides the ability to skip shrinking, so there is precedent for this being useful. While `hypothesis-ruby` does not have the concept of other "Phases" yet, we can still start off the API by using this concept.
16
+
17
+ Usage:
18
+
19
+ ```
20
+ hypothesis(phases: Phase.excluding(:shrink)) do
21
+ # Failures here will be displayed directly and shrinking will be avoided
22
+ end
23
+ ```
24
+
25
+ # Hypothesis for Ruby 0.4.0 (2021-01-12)
26
+
27
+ This removes hypothesis-ruby's dependence on RSpec. Now, it can be used with any Ruby test runner.
28
+
29
+ # Hypothesis for Ruby 0.3.0 (2021-01-08)
30
+
31
+ This release converts Hypothesis for Ruby to use [RuTie](https://github.com/danielpclark/rutie)
32
+ instead of the deprecated [Helix](https://github.com/tildeio/helix), restoring compatibility
33
+ with recent versions of Rust. Thanks to Alex Weisberger for taking this on!
34
+
35
+ # Hypothesis for Ruby 0.2.0 (2018-10-24)
36
+
37
+ This release adds an example database to Hypothesis for Ruby. This means that when a test fails,
38
+ it will automatically reuse the previously shown example when you rerun it, without having to
39
+ manually pass a seed.
40
+
1
41
  # Hypothesis for Ruby 0.1.2 (2018-09-24)
2
42
 
3
43
  This release makes the code useable via a direct require.
data/Cargo.toml CHANGED
@@ -1,12 +1,14 @@
1
1
  [package]
2
2
  name = "hypothesis-ruby"
3
3
  version = "0.1.0"
4
- authors = ["David R. MacIver <david@drmaciver.com>"]
4
+ authors = ["David R. MacIver <david@drmaciver.com>", "Alex Weisberger <alex.m.weisberger@gmail.com>"]
5
5
 
6
6
  [lib]
7
+ name="hypothesis_ruby_core"
7
8
  crate-type = ["cdylib"]
8
9
 
9
10
  [dependencies]
10
- helix = '0.7.5'
11
+ rutie = {version="0.8.1"}
12
+ lazy_static = "1.4.0"
11
13
  rand = '0.3'
12
- conjecture = '0.3.0'
14
+ conjecture = '0.6.0'
@@ -1,7 +1,7 @@
1
1
  Copyright (c) 2018, David R. MacIver
2
2
 
3
3
  All code in this repository except where explicitly noted otherwise is released
4
- under the Mozilla Public License v 2.0. You can obtain a copy at http://mozilla.org/MPL/2.0/.
4
+ under the Mozilla Public License v 2.0. You can obtain a copy at https://mozilla.org/MPL/2.0/.
5
5
 
6
6
  Some code in this repository may come from other projects. Where applicable, the
7
7
  original copyright and license are noted and any modifications made are released
@@ -32,7 +32,7 @@ RSpec.describe "removing an element from a list" do
32
32
 
33
33
  values.delete_at(values.index(to_remove))
34
34
 
35
- # Will fail if the value ws duplicated in the list.
35
+ # Will fail if the value was duplicated in the list.
36
36
  expect(values.include?(to_remove)).to be false
37
37
 
38
38
  end
@@ -71,7 +71,7 @@ Right now this is really more to allow you to try it out and provide feedback th
71
71
  The more feedback we get, the sooner it will get there!
72
72
 
73
73
  Note that in order to use Hypothesis for Ruby, you will need a rust toolchain installed.
74
- Please go to [https://www.rustup.rs](https://www.rustup.rs) and follow the instructions if you do not already have one.
74
+ Please go to [https://www.rustup.rs](https://www.rustup.rs) and follow the instructions if you do not already have one. You will most likely need to compile your Ruby executable with the `--enable-shared` option. See [here](https://github.com/danielpclark/rutie#dynamic-vs-static-builds).
75
75
 
76
76
  ## Project Status
77
77
 
data/Rakefile CHANGED
@@ -1,10 +1,13 @@
1
1
  # frozen_string_literal: true
2
2
 
3
3
  require 'rubygems'
4
- require 'helix_runtime/build_task'
5
4
  require 'date'
6
5
  require 'open3'
7
6
 
7
+ task :build do
8
+ sh('cargo build --release')
9
+ end
10
+
8
11
  begin
9
12
  require 'rspec/core/rake_task'
10
13
  RSpec::Core::RakeTask.new(:spec)
@@ -17,10 +20,24 @@ begin
17
20
  end
18
21
 
19
22
  task test: %i[build spec minitests]
23
+ task rspec: %i[build spec]
24
+ task minitest: %i[build without_rspec] do
25
+ begin
26
+ Rake::Task['minitests'].execute
27
+ ensure
28
+ Rake::Task['with_rspec'].execute
29
+ end
30
+ end
20
31
  rescue LoadError
21
32
  end
22
33
 
23
- HelixRuntime::BuildTask.new
34
+ task :without_rspec do
35
+ sh('bundle config set without rspec')
36
+ end
37
+
38
+ task :with_rspec do
39
+ sh('bundle config unset without')
40
+ end
24
41
 
25
42
  def rubocop(fix:)
26
43
  sh "bundle exec rubocop #{'-a' if fix} lib spec minitests " \
@@ -7,6 +7,28 @@ require_relative 'hypothesis/testcase'
7
7
  require_relative 'hypothesis/engine'
8
8
  require_relative 'hypothesis/world'
9
9
 
10
+ module Phase
11
+ SHRINK = :shrink
12
+
13
+ module_function
14
+
15
+ def all
16
+ [SHRINK]
17
+ end
18
+
19
+ def excluding(*phases)
20
+ unknown_phases = phases.reject { |phase| Phase.all.include?(phase) }
21
+ unless unknown_phases.empty?
22
+ raise(
23
+ ArgumentError,
24
+ "Attempting to exclude unknown phases: #{unknown_phases}"
25
+ )
26
+ end
27
+
28
+ all - phases
29
+ end
30
+ end
31
+
10
32
  # This is the main module for using Hypothesis.
11
33
  # It is expected that you will include this in your
12
34
  # tests, but its methods are also available on the
@@ -19,6 +41,23 @@ require_relative 'hypothesis/world'
19
41
  module Hypothesis
20
42
  # @!visibility private
21
43
  HYPOTHESIS_LOCATION = File.dirname(__FILE__)
44
+ # rubocop:disable ClassVars
45
+ @@setup_called = false
46
+ # rubocop:enable RuleByName
47
+
48
+ def self.setup_called
49
+ @@setup_called == true
50
+ end
51
+
52
+ def self.included(*)
53
+ if setup_called == false
54
+ Rutie.new(:hypothesis_ruby_core).init(
55
+ 'Init_rutie_hypothesis_core',
56
+ __dir__
57
+ )
58
+ end
59
+ @@setup_called = true
60
+ end
22
61
 
23
62
  # @!visibility private
24
63
  def hypothesis_stable_identifier
@@ -140,12 +179,21 @@ module Hypothesis
140
179
  #
141
180
  # A call to hypothesis does the following:
142
181
  #
143
- # 1. It tries to *generate* a failing test case.
144
- # 2. If it succeeded then it will *shrink* that failing test case.
145
- # 3. Finally, it will *display* the shrunk failing test case by
182
+ # 1. It first tries to *reuse* failing test cases for previous runs.
183
+ # 2. If there were no previous failing test cases then it tries to
184
+ # *generate* new failing test cases.
185
+ # 3. If either of the first two phases found failing test cases then
186
+ # it will *shrink* those failing test cases.
187
+ # 4. Finally, it will *display* the shrunk failing test case by
146
188
  # the error from its failing assertion, modified to show the
147
189
  # givens of the test case.
148
190
  #
191
+ # Reuse uses an internal representation of the test case, so examples
192
+ # from previous runs will obey all of the usual invariants of generation.
193
+ # However, this means that if you change your test then reuse may not
194
+ # work. Test cases that have become invalid or passing will be cleaned
195
+ # up automatically.
196
+ #
149
197
  # Generation consists of randomly trying test cases until one of
150
198
  # three things has happened:
151
199
  #
@@ -170,13 +218,27 @@ module Hypothesis
170
218
  #
171
219
  # @param max_valid_test_cases [Integer] The maximum number of valid test
172
220
  # cases to run without finding a failing test case before stopping.
173
- def hypothesis(max_valid_test_cases: 200, &block)
221
+ #
222
+ # @param database [String, nil, false] A path to a directory where Hypothesis
223
+ # should store previously failing test cases. If it is nil, Hypothesis
224
+ # will use a default of .hypothesis/examples in the current directory.
225
+ # May also be set to false to disable the database functionality.
226
+ def hypothesis(
227
+ max_valid_test_cases: 200,
228
+ phases: Phase.all,
229
+ database: nil,
230
+ &block
231
+ )
174
232
  unless World.current_engine.nil?
175
233
  raise UsageError, 'Cannot nest hypothesis calls'
176
234
  end
235
+
177
236
  begin
178
237
  World.current_engine = Engine.new(
179
- max_examples: max_valid_test_cases
238
+ hypothesis_stable_identifier,
239
+ max_examples: max_valid_test_cases,
240
+ phases: phases,
241
+ database: database
180
242
  )
181
243
  World.current_engine.run(&block)
182
244
  ensure
@@ -1,22 +1,29 @@
1
1
  # frozen_string_literal: true
2
2
 
3
- require 'helix_runtime'
4
-
5
- require_relative '../hypothesis-ruby/native'
6
-
7
- require 'rspec/expectations'
3
+ require 'rutie'
8
4
 
9
5
  module Hypothesis
10
- class Engine
11
- include RSpec::Matchers
6
+ DEFAULT_DATABASE_PATH = File.join(Dir.pwd, '.hypothesis', 'examples')
12
7
 
8
+ class Engine
13
9
  attr_reader :current_source
14
10
  attr_accessor :is_find
15
11
 
16
- def initialize(options)
12
+ def initialize(name, options)
17
13
  seed = Random.rand(2**64 - 1)
14
+
15
+ database = options.fetch(:database, nil)
16
+
17
+ database = DEFAULT_DATABASE_PATH if database.nil?
18
+
19
+ database = nil if database == false
20
+
18
21
  @core_engine = HypothesisCoreEngine.new(
19
- seed, options.fetch(:max_examples)
22
+ name,
23
+ database,
24
+ seed,
25
+ options.fetch(:max_examples),
26
+ options.fetch(:phases)
20
27
  )
21
28
 
22
29
  @exceptions_to_tags = Hash.new { |h, k| h[k] = h.size }
data/src/lib.rs CHANGED
@@ -1,184 +1,487 @@
1
- // "Bridging" root code that exists exclusively to provide
2
- // a ruby -> Hypothesis engine binding.
3
-
4
- #![recursion_limit = "256"]
5
- #![deny(warnings, missing_debug_implementations)]
6
-
7
- extern crate core;
8
1
  #[macro_use]
9
- extern crate helix;
10
- extern crate rand;
2
+ extern crate rutie;
3
+ #[macro_use]
4
+ extern crate lazy_static;
11
5
  extern crate conjecture;
12
6
 
7
+ use std::convert::TryFrom;
13
8
  use std::mem;
14
9
 
10
+ use rutie::{
11
+ AnyException, AnyObject, Array, Boolean, Class, Exception, Float, Integer, NilClass, Object,
12
+ RString, Symbol, VM,
13
+ };
14
+
15
15
  use conjecture::data::{DataSource, Status, TestResult};
16
- use conjecture::distributions::Repeat;
16
+ use conjecture::database::{BoxedDatabase, DirectoryDatabase, NoDatabase};
17
17
  use conjecture::distributions;
18
- use conjecture::engine::Engine;
18
+ use conjecture::distributions::Repeat;
19
+ use conjecture::engine::{Engine, Phase};
20
+
21
+ pub struct HypothesisCoreDataSourceStruct {
22
+ source: Option<DataSource>,
23
+ }
24
+
25
+ impl HypothesisCoreDataSourceStruct {
26
+ fn new(engine: &mut HypothesisCoreEngineStruct) -> HypothesisCoreDataSourceStruct {
27
+ HypothesisCoreDataSourceStruct {
28
+ source: mem::take(&mut engine.pending),
29
+ }
30
+ }
31
+
32
+ fn start_draw(&mut self) {
33
+ if let Some(ref mut source) = self.source {
34
+ source.start_draw();
35
+ }
36
+ }
37
+
38
+ fn stop_draw(&mut self) {
39
+ if let Some(ref mut source) = self.source {
40
+ source.stop_draw();
41
+ }
42
+ }
43
+ }
44
+
45
+ wrappable_struct!(
46
+ HypothesisCoreDataSourceStruct,
47
+ HypothesisCoreDataSourceStructWrapper,
48
+ HYPOTHESIS_CORE_DATA_SOURCE_STRUCT_WRAPPER
49
+ );
50
+
51
+ class!(HypothesisCoreDataSource);
52
+
53
+ methods!(
54
+ HypothesisCoreDataSource,
55
+ itself,
56
+ fn ruby_hypothesis_core_data_source_start_draw() -> NilClass {
57
+ itself
58
+ .get_data_mut(&*HYPOTHESIS_CORE_DATA_SOURCE_STRUCT_WRAPPER)
59
+ .start_draw();
19
60
 
20
- ruby! {
21
- class HypothesisCoreDataSource {
22
- struct {
23
- source: Option<DataSource>,
61
+ NilClass::new()
24
62
  }
63
+ fn ruby_hypothesis_core_data_source_stop_draw() -> NilClass {
64
+ itself
65
+ .get_data_mut(&*HYPOTHESIS_CORE_DATA_SOURCE_STRUCT_WRAPPER)
66
+ .stop_draw();
25
67
 
26
- def initialize(helix, engine: &mut HypothesisCoreEngine){
27
- let mut result = HypothesisCoreDataSource{helix, source: None};
28
- mem::swap(&mut result.source, &mut engine.pending);
29
- return result;
68
+ NilClass::new()
30
69
  }
70
+ );
31
71
 
32
- def start_draw(&mut self){
33
- if let &mut Some(ref mut source) = &mut self.source {
34
- source.start_draw();
35
- }
72
+ pub struct HypothesisCoreEngineStruct {
73
+ engine: Engine,
74
+ pending: Option<DataSource>,
75
+ interesting_examples: Vec<TestResult>,
76
+ }
77
+
78
+ impl HypothesisCoreEngineStruct {
79
+ fn new(
80
+ name: String,
81
+ database_path: Option<String>,
82
+ seed: u64,
83
+ max_examples: u64,
84
+ phases: Vec<Phase>,
85
+ ) -> HypothesisCoreEngineStruct {
86
+ let xs: [u32; 2] = [seed as u32, (seed >> 32) as u32];
87
+ let db: BoxedDatabase = match database_path {
88
+ None => Box::new(NoDatabase),
89
+ Some(path) => Box::new(DirectoryDatabase::new(path)),
90
+ };
91
+
92
+ HypothesisCoreEngineStruct {
93
+ engine: Engine::new(name, max_examples, phases, &xs, db),
94
+ pending: None,
95
+ interesting_examples: Vec::new(),
96
+ }
97
+ }
98
+
99
+ fn new_source(&mut self) -> Option<HypothesisCoreDataSourceStruct> {
100
+ match self.engine.next_source() {
101
+ None => {
102
+ self.interesting_examples = self.engine.list_minimized_examples();
103
+ None
104
+ }
105
+ Some(source) => {
106
+ self.pending = Some(source);
107
+ Some(HypothesisCoreDataSourceStruct::new(self))
108
+ }
109
+ }
110
+ }
111
+
112
+ fn count_failing_examples(&self) -> usize {
113
+ self.interesting_examples.len()
114
+ }
115
+
116
+ fn failing_example(&mut self, i: usize) -> HypothesisCoreDataSourceStruct {
117
+ self.pending = Some(DataSource::from_vec(
118
+ self.interesting_examples[i].record.clone(),
119
+ ));
120
+ HypothesisCoreDataSourceStruct::new(self)
36
121
  }
37
122
 
38
- def stop_draw(&mut self){
39
- if let &mut Some(ref mut source) = &mut self.source {
40
- source.stop_draw();
41
- }
123
+ fn was_unsatisfiable(&mut self) -> bool {
124
+ self.engine.was_unsatisfiable()
42
125
  }
43
- }
44
126
 
45
- class HypothesisCoreEngine {
46
- struct {
47
- engine: Engine,
48
- pending: Option<DataSource>,
49
- interesting_examples: Vec<TestResult>,
127
+ fn finish_overflow(&mut self, child: &mut HypothesisCoreDataSourceStruct) {
128
+ mark_child_status(&mut self.engine, child, Status::Overflow);
50
129
  }
51
130
 
52
- def initialize(helix, seed: u64, max_examples: u64){
53
- let xs: [u32; 2] = [seed as u32, (seed >> 32) as u32];
54
- HypothesisCoreEngine{
55
- helix,
56
- engine: Engine::new(max_examples, &xs),
57
- pending: None,
58
- interesting_examples: Vec::new(),
59
- }
131
+ fn finish_valid(&mut self, child: &mut HypothesisCoreDataSourceStruct) {
132
+ mark_child_status(&mut self.engine, child, Status::Valid);
60
133
  }
61
134
 
62
- def new_source(&mut self) -> Option<HypothesisCoreDataSource> {
63
- match self.engine.next_source() {
64
- None => {
65
- self.interesting_examples = self.engine.list_minimized_examples();
66
- None
67
- },
68
- Some(source) => {
69
- self.pending = Some(source);
70
- Some(HypothesisCoreDataSource::new(self))
71
- },
72
- }
135
+ fn finish_invalid(&mut self, child: &mut HypothesisCoreDataSourceStruct) {
136
+ mark_child_status(&mut self.engine, child, Status::Invalid);
73
137
  }
74
138
 
75
- def count_failing_examples(&self) -> usize {
76
- self.interesting_examples.len()
139
+ fn finish_interesting(&mut self, child: &mut HypothesisCoreDataSourceStruct, label: u64) {
140
+ mark_child_status(&mut self.engine, child, Status::Interesting(label));
77
141
  }
142
+ }
143
+
144
+ wrappable_struct!(
145
+ HypothesisCoreEngineStruct,
146
+ HypothesisCoreEngineStructWrapper,
147
+ HYPOTHESIS_CORE_ENGINE_STRUCT_WRAPPER
148
+ );
149
+
150
+ class!(HypothesisCoreEngine);
151
+
152
+ methods!(
153
+ HypothesisCoreEngine,
154
+ itself,
155
+ fn ruby_hypothesis_core_engine_new(
156
+ name: RString,
157
+ database_path: RString,
158
+ seed: Integer,
159
+ max_example: Integer,
160
+ phases: Array
161
+ ) -> AnyObject {
162
+ let rust_phases = safe_access(phases)
163
+ .into_iter()
164
+ .map(|ruby_phase| {
165
+ let phase_sym = safe_access(ruby_phase.try_convert_to::<Symbol>());
166
+ let phase = Phase::try_from(phase_sym.to_str())
167
+ .map_err(|e| AnyException::new("ArgumentError", Some(&e)));
168
+
169
+ safe_access(phase)
170
+ })
171
+ .collect();
78
172
 
79
- def failing_example(&mut self, i: usize) -> HypothesisCoreDataSource {
80
- self.pending = Some(
81
- DataSource::from_vec(self.interesting_examples[i].record.clone())
82
- );
83
- HypothesisCoreDataSource::new(self)
173
+ let core_engine = HypothesisCoreEngineStruct::new(
174
+ safe_access(name).to_string(),
175
+ database_path.ok().map(|p| p.to_string()),
176
+ safe_access(seed).to_u64(),
177
+ safe_access(max_example).to_u64(),
178
+ rust_phases,
179
+ );
180
+
181
+ Class::from_existing("HypothesisCoreEngine")
182
+ .wrap_data(core_engine, &*HYPOTHESIS_CORE_ENGINE_STRUCT_WRAPPER)
183
+ }
184
+ fn ruby_hypothesis_core_engine_new_source() -> AnyObject {
185
+ match itself
186
+ .get_data_mut(&*HYPOTHESIS_CORE_ENGINE_STRUCT_WRAPPER)
187
+ .new_source()
188
+ {
189
+ Some(ds) => Class::from_existing("HypothesisCoreDataSource")
190
+ .wrap_data(ds, &*HYPOTHESIS_CORE_DATA_SOURCE_STRUCT_WRAPPER),
191
+ None => NilClass::new().into(),
192
+ }
84
193
  }
194
+ fn ruby_hypothesis_core_engine_finish_overflow(child: AnyObject) -> NilClass {
195
+ let core_engine = itself.get_data_mut(&*HYPOTHESIS_CORE_ENGINE_STRUCT_WRAPPER);
196
+ let mut rdata_source = safe_access(child);
197
+ let data_source = rdata_source.get_data_mut(&*HYPOTHESIS_CORE_DATA_SOURCE_STRUCT_WRAPPER);
85
198
 
86
- def was_unsatisfiable(&mut self) -> bool {
87
- self.engine.was_unsatisfiable()
199
+ core_engine.finish_overflow(data_source);
200
+
201
+ NilClass::new()
88
202
  }
203
+ fn ruby_hypothesis_core_engine_finish_valid(child: AnyObject) -> NilClass {
204
+ let core_engine = itself.get_data_mut(&*HYPOTHESIS_CORE_ENGINE_STRUCT_WRAPPER);
205
+ let mut rdata_source = safe_access(child);
206
+ let data_source = rdata_source.get_data_mut(&*HYPOTHESIS_CORE_DATA_SOURCE_STRUCT_WRAPPER);
207
+
208
+ core_engine.finish_valid(data_source);
89
209
 
90
- def finish_overflow(&mut self, child: &mut HypothesisCoreDataSource){
91
- mark_child_status(&mut self.engine, child, Status::Overflow);
210
+ NilClass::new()
92
211
  }
212
+ fn ruby_hypothesis_core_engine_finish_invalid(child: AnyObject) -> NilClass {
213
+ let core_engine = itself.get_data_mut(&*HYPOTHESIS_CORE_ENGINE_STRUCT_WRAPPER);
214
+ let mut rdata_source = safe_access(child);
215
+ let data_source = rdata_source.get_data_mut(&*HYPOTHESIS_CORE_DATA_SOURCE_STRUCT_WRAPPER);
93
216
 
94
- def finish_invalid(&mut self, child: &mut HypothesisCoreDataSource){
95
- mark_child_status(&mut self.engine, child, Status::Invalid);
217
+ core_engine.finish_invalid(data_source);
218
+
219
+ NilClass::new()
96
220
  }
221
+ fn ruby_hypothesis_core_engine_finish_interesting(
222
+ child: AnyObject,
223
+ label: Integer
224
+ ) -> NilClass {
225
+ let core_engine = itself.get_data_mut(&*HYPOTHESIS_CORE_ENGINE_STRUCT_WRAPPER);
226
+ let mut rdata_source = safe_access(child);
227
+ let data_source = rdata_source.get_data_mut(&*HYPOTHESIS_CORE_DATA_SOURCE_STRUCT_WRAPPER);
228
+
229
+ core_engine.finish_interesting(data_source, safe_access(label).to_u64());
97
230
 
98
- def finish_interesting(&mut self, child: &mut HypothesisCoreDataSource, label: u64){
99
- mark_child_status(&mut self.engine, child, Status::Interesting(label));
231
+ NilClass::new()
100
232
  }
233
+ fn ruby_hypothesis_core_engine_count_failing_examples() -> Integer {
234
+ let core_engine = itself.get_data(&*HYPOTHESIS_CORE_ENGINE_STRUCT_WRAPPER);
101
235
 
102
- def finish_valid(&mut self, child: &mut HypothesisCoreDataSource){
103
- mark_child_status(&mut self.engine, child, Status::Valid);
236
+ Integer::new(core_engine.count_failing_examples() as i64)
104
237
  }
105
- }
238
+ fn ruby_hypothesis_core_failing_example(i: Integer) -> AnyObject {
239
+ let core_engine = itself.get_data_mut(&*HYPOTHESIS_CORE_ENGINE_STRUCT_WRAPPER);
240
+ let int = safe_access(i).to_u64() as usize;
241
+
242
+ let data_source = core_engine.failing_example(int);
106
243
 
107
- class HypothesisCoreBitPossible{
108
- struct {
109
- n_bits: u64,
244
+ Class::from_existing("HypothesisCoreDataSource")
245
+ .wrap_data(data_source, &*HYPOTHESIS_CORE_DATA_SOURCE_STRUCT_WRAPPER)
110
246
  }
247
+ fn ruby_hypothesis_core_engine_was_unsatisfiable() -> Boolean {
248
+ let core_engine = itself.get_data_mut(&*HYPOTHESIS_CORE_ENGINE_STRUCT_WRAPPER);
111
249
 
112
- def initialize(helix, n_bits: u64){
113
- return HypothesisCoreBitPossible{helix, n_bits: n_bits};
250
+ Boolean::new(core_engine.was_unsatisfiable())
114
251
  }
252
+ );
253
+
254
+ pub struct HypothesisCoreIntegersStruct {
255
+ bitlengths: distributions::Sampler,
256
+ }
115
257
 
116
- def provide(&mut self, data: &mut HypothesisCoreDataSource) -> Option<u64>{
117
- match &mut data.source {
118
- &mut None => None,
119
- &mut Some(ref mut source) => source.bits(self.n_bits).ok(),
120
- }
258
+ impl HypothesisCoreIntegersStruct {
259
+ fn new() -> HypothesisCoreIntegersStruct {
260
+ HypothesisCoreIntegersStruct {
261
+ bitlengths: distributions::good_bitlengths(),
262
+ }
121
263
  }
122
- }
123
264
 
124
- class HypothesisCoreRepeatValues{
125
- struct {
126
- repeat: Repeat,
265
+ fn provide(&mut self, data: &mut HypothesisCoreDataSourceStruct) -> Option<i64> {
266
+ data.source.as_mut().and_then(|ref mut source| {
267
+ distributions::integer_from_bitlengths(source, &self.bitlengths).ok()
268
+ })
127
269
  }
270
+ }
271
+
272
+ wrappable_struct!(
273
+ HypothesisCoreIntegersStruct,
274
+ HypothesisCoreIntegersStructWrapper,
275
+ HYPOTHESIS_CORE_INTEGERS_STRUCT_WRAPPER
276
+ );
277
+
278
+ class!(HypothesisCoreIntegers);
279
+
280
+ methods!(
281
+ HypothesisCoreIntegers,
282
+ itself,
283
+ fn ruby_hypothesis_core_integers_new() -> AnyObject {
284
+ let core_integers = HypothesisCoreIntegersStruct::new();
128
285
 
129
- def initialize(helix, min_count: u64, max_count: u64, expected_count: f64){
130
- return HypothesisCoreRepeatValues{
131
- helix, repeat: Repeat::new(min_count, max_count, expected_count)
132
- }
286
+ Class::from_existing("HypothesisCoreIntegers")
287
+ .wrap_data(core_integers, &*HYPOTHESIS_CORE_INTEGERS_STRUCT_WRAPPER)
133
288
  }
289
+ fn ruby_hypothesis_core_integers_provide(data: AnyObject) -> AnyObject {
290
+ let core_integers = itself.get_data_mut(&*HYPOTHESIS_CORE_INTEGERS_STRUCT_WRAPPER);
291
+ let mut rdata = safe_access(data);
292
+ let data_source = rdata.get_data_mut(&*HYPOTHESIS_CORE_DATA_SOURCE_STRUCT_WRAPPER);
134
293
 
135
- def _should_continue(&mut self, data: &mut HypothesisCoreDataSource) -> Option<bool>{
136
- return data.source.as_mut().and_then(|ref mut source| {
137
- self.repeat.should_continue(source).ok()
138
- })
294
+ match core_integers.provide(data_source) {
295
+ Some(i) => Integer::new(i).into(),
296
+ None => NilClass::new().into(),
297
+ }
139
298
  }
299
+ );
140
300
 
141
- def reject(&mut self){
142
- self.repeat.reject();
301
+ pub struct HypothesisCoreRepeatValuesStruct {
302
+ repeat: Repeat,
303
+ }
304
+
305
+ impl HypothesisCoreRepeatValuesStruct {
306
+ fn new(
307
+ min_count: u64,
308
+ max_count: u64,
309
+ expected_count: f64,
310
+ ) -> HypothesisCoreRepeatValuesStruct {
311
+ HypothesisCoreRepeatValuesStruct {
312
+ repeat: Repeat::new(min_count, max_count, expected_count),
313
+ }
143
314
  }
144
- }
145
315
 
146
- class HypothesisCoreIntegers{
147
- struct {
148
- bitlengths: distributions::Sampler,
316
+ fn _should_continue(&mut self, data: &mut HypothesisCoreDataSourceStruct) -> Option<bool> {
317
+ return data
318
+ .source
319
+ .as_mut()
320
+ .and_then(|ref mut source| self.repeat.should_continue(source).ok());
149
321
  }
150
- def initialize(helix){
151
- return HypothesisCoreIntegers{helix,bitlengths: distributions::good_bitlengths()};
322
+
323
+ fn reject(&mut self) {
324
+ self.repeat.reject();
152
325
  }
153
- def provide(&mut self, data: &mut HypothesisCoreDataSource) -> Option<i64>{
154
- data.source.as_mut().and_then(|ref mut source| {
155
- distributions::integer_from_bitlengths(source, &self.bitlengths).ok()
156
- })
326
+ }
327
+
328
+ wrappable_struct!(
329
+ HypothesisCoreRepeatValuesStruct,
330
+ HypothesisCoreRepeatValuesStructWrapper,
331
+ HYPOTHESIS_CORE_REPEAT_VALUES_STRUCT_WRAPPER
332
+ );
333
+
334
+ class!(HypothesisCoreRepeatValues);
335
+
336
+ methods!(
337
+ HypothesisCoreRepeatValues,
338
+ itself,
339
+ fn ruby_hypothesis_core_repeat_values_new(
340
+ min_count: Integer,
341
+ max_count: Integer,
342
+ expected_count: Float
343
+ ) -> AnyObject {
344
+ let repeat_values = HypothesisCoreRepeatValuesStruct::new(
345
+ safe_access(min_count).to_u64(),
346
+ safe_access(max_count).to_u64(),
347
+ safe_access(expected_count).to_f64(),
348
+ );
349
+
350
+ Class::from_existing("HypothesisCoreRepeatValues").wrap_data(
351
+ repeat_values,
352
+ &*HYPOTHESIS_CORE_REPEAT_VALUES_STRUCT_WRAPPER,
353
+ )
157
354
  }
158
- }
355
+ fn ruby_hypothesis_core_repeat_values_should_continue(data: AnyObject) -> AnyObject {
356
+ let mut rdata = safe_access(data);
357
+ let mut data_source = rdata.get_data_mut(&*HYPOTHESIS_CORE_DATA_SOURCE_STRUCT_WRAPPER);
358
+
359
+ let should_continue = itself
360
+ .get_data_mut(&*HYPOTHESIS_CORE_REPEAT_VALUES_STRUCT_WRAPPER)
361
+ ._should_continue(data_source);
159
362
 
160
- class HypothesisCoreBoundedIntegers{
161
- struct {
162
- max_value: u64,
363
+ match should_continue {
364
+ Some(b) => Boolean::new(b).into(),
365
+ None => NilClass::new().into(),
366
+ }
163
367
  }
164
- def initialize(helix, max_value: u64){
165
- return HypothesisCoreBoundedIntegers{helix, max_value: max_value};
368
+ fn ruby_hypothesis_core_repeat_values_reject() -> NilClass {
369
+ let repeat_values = itself.get_data_mut(&*HYPOTHESIS_CORE_REPEAT_VALUES_STRUCT_WRAPPER);
370
+
371
+ repeat_values.reject();
372
+
373
+ NilClass::new()
166
374
  }
375
+ );
167
376
 
168
- def provide(&mut self, data: &mut HypothesisCoreDataSource) -> Option<u64>{
169
- data.source.as_mut().and_then(|ref mut source| {
170
- distributions::bounded_int(source, self.max_value).ok()
171
- })
377
+ pub struct HypothesisCoreBoundedIntegersStruct {
378
+ max_value: u64,
379
+ }
380
+
381
+ impl HypothesisCoreBoundedIntegersStruct {
382
+ fn provide(&mut self, data: &mut HypothesisCoreDataSourceStruct) -> Option<u64> {
383
+ data.source
384
+ .as_mut()
385
+ .and_then(|ref mut source| distributions::bounded_int(source, self.max_value).ok())
172
386
  }
173
- }
174
387
  }
175
388
 
176
- fn mark_child_status(engine: &mut Engine, child: &mut HypothesisCoreDataSource, status: Status) {
177
- let mut replacement = None;
178
- mem::swap(&mut replacement, &mut child.source);
389
+ wrappable_struct!(
390
+ HypothesisCoreBoundedIntegersStruct,
391
+ HypothesisCoreBoundedIntegersStructWrapper,
392
+ HYPOTHESIS_CORE_BOUNDED_INTEGERS_STRUCT_WRAPPER
393
+ );
394
+
395
+ class!(HypothesisCoreBoundedIntegers);
396
+
397
+ methods!(
398
+ HypothesisCoreBoundedIntegers,
399
+ itself,
400
+ fn ruby_hypothesis_core_bounded_integers_new(max_value: Integer) -> AnyObject {
401
+ let bounded_integers = HypothesisCoreBoundedIntegersStruct {
402
+ max_value: safe_access(max_value).to_u64(),
403
+ };
404
+
405
+ Class::from_existing("HypothesisCoreBoundedIntegers").wrap_data(
406
+ bounded_integers,
407
+ &*HYPOTHESIS_CORE_BOUNDED_INTEGERS_STRUCT_WRAPPER,
408
+ )
409
+ }
410
+ fn ruby_hypothesis_core_bounded_integers_provide(data: AnyObject) -> AnyObject {
411
+ let mut rdata = safe_access(data);
412
+ let data_source = rdata.get_data_mut(&*HYPOTHESIS_CORE_DATA_SOURCE_STRUCT_WRAPPER);
413
+ let bounded_integers =
414
+ itself.get_data_mut(&*HYPOTHESIS_CORE_BOUNDED_INTEGERS_STRUCT_WRAPPER);
415
+
416
+ match bounded_integers.provide(data_source) {
417
+ Some(i) => Integer::from(i).into(),
418
+ None => NilClass::new().into(),
419
+ }
420
+ }
421
+ );
422
+
423
+ #[allow(non_snake_case)]
424
+ #[no_mangle]
425
+ pub extern "C" fn Init_rutie_hypothesis_core() {
426
+ Class::new("HypothesisCoreEngine", None).define(|klass| {
427
+ klass.def_self("new", ruby_hypothesis_core_engine_new);
428
+ klass.def("new_source", ruby_hypothesis_core_engine_new_source);
429
+ klass.def(
430
+ "count_failing_examples",
431
+ ruby_hypothesis_core_engine_count_failing_examples,
432
+ );
433
+ klass.def("failing_example", ruby_hypothesis_core_failing_example);
434
+ klass.def(
435
+ "was_unsatisfiable",
436
+ ruby_hypothesis_core_engine_was_unsatisfiable,
437
+ );
438
+ klass.def(
439
+ "finish_overflow",
440
+ ruby_hypothesis_core_engine_finish_overflow,
441
+ );
442
+ klass.def("finish_valid", ruby_hypothesis_core_engine_finish_valid);
443
+ klass.def("finish_invalid", ruby_hypothesis_core_engine_finish_invalid);
444
+ klass.def(
445
+ "finish_interesting",
446
+ ruby_hypothesis_core_engine_finish_interesting,
447
+ );
448
+ });
449
+
450
+ Class::new("HypothesisCoreDataSource", None).define(|klass| {
451
+ klass.def("start_draw", ruby_hypothesis_core_data_source_start_draw);
452
+ klass.def("stop_draw", ruby_hypothesis_core_data_source_stop_draw);
453
+ });
454
+
455
+ Class::new("HypothesisCoreIntegers", None).define(|klass| {
456
+ klass.def_self("new", ruby_hypothesis_core_integers_new);
457
+ klass.def("provide", ruby_hypothesis_core_integers_provide);
458
+ });
459
+
460
+ Class::new("HypothesisCoreRepeatValues", None).define(|klass| {
461
+ klass.def_self("new", ruby_hypothesis_core_repeat_values_new);
462
+ klass.def(
463
+ "_should_continue",
464
+ ruby_hypothesis_core_repeat_values_should_continue,
465
+ );
466
+ klass.def("reject", ruby_hypothesis_core_repeat_values_reject);
467
+ });
179
468
 
180
- match replacement {
181
- Some(source) => engine.mark_finished(source, status),
182
- None => (),
469
+ Class::new("HypothesisCoreBoundedIntegers", None).define(|klass| {
470
+ klass.def_self("new", ruby_hypothesis_core_bounded_integers_new);
471
+ klass.def("provide", ruby_hypothesis_core_bounded_integers_provide);
472
+ });
473
+ }
474
+
475
+ fn mark_child_status(
476
+ engine: &mut Engine,
477
+ child: &mut HypothesisCoreDataSourceStruct,
478
+ status: Status,
479
+ ) {
480
+ if let Some(source) = mem::take(&mut child.source) {
481
+ engine.mark_finished(source, status)
183
482
  }
184
483
  }
484
+
485
+ fn safe_access<T>(value: Result<T, AnyException>) -> T {
486
+ value.map_err(VM::raise_ex).unwrap()
487
+ }
metadata CHANGED
@@ -1,29 +1,30 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: hypothesis-specs
3
3
  version: !ruby/object:Gem::Version
4
- version: 0.1.2
4
+ version: 0.6.0
5
5
  platform: ruby
6
6
  authors:
7
7
  - David R. Maciver
8
+ - Alex Weisberger
8
9
  autorequire:
9
10
  bindir: bin
10
11
  cert_chain: []
11
- date: 2018-09-24 00:00:00.000000000 Z
12
+ date: 2021-01-27 00:00:00.000000000 Z
12
13
  dependencies:
13
14
  - !ruby/object:Gem::Dependency
14
- name: helix_runtime
15
+ name: rutie
15
16
  requirement: !ruby/object:Gem::Requirement
16
17
  requirements:
17
18
  - - "~>"
18
19
  - !ruby/object:Gem::Version
19
- version: 0.7.0
20
+ version: 0.0.3
20
21
  type: :runtime
21
22
  prerelease: false
22
23
  version_requirements: !ruby/object:Gem::Requirement
23
24
  requirements:
24
25
  - - "~>"
25
26
  - !ruby/object:Gem::Version
26
- version: 0.7.0
27
+ version: 0.0.3
27
28
  - !ruby/object:Gem::Dependency
28
29
  name: rake
29
30
  requirement: !ruby/object:Gem::Requirement
@@ -88,8 +89,7 @@ required_rubygems_version: !ruby/object:Gem::Requirement
88
89
  - !ruby/object:Gem::Version
89
90
  version: '0'
90
91
  requirements: []
91
- rubyforge_project:
92
- rubygems_version: 2.7.6
92
+ rubygems_version: 3.2.7
93
93
  signing_key:
94
94
  specification_version: 4
95
95
  summary: Hypothesis is a powerful, flexible, and easy to use library for property-based