hypothesis-specs 0.0.15 → 0.3.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
checksums.yaml CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA256:
3
- metadata.gz: cf543607fd313031a5fd77ee32d3e8f7693f374a48e5bcd2c36695ca31f81812
4
- data.tar.gz: 572e4cb24ad38caff4a730eff87a8bbbb9aae75e759fade1061cb919214de76d
3
+ metadata.gz: 77c742dcfc65dfcab8e0eac24498eff24b10461abf17f502961ac9fc30bd1810
4
+ data.tar.gz: 6a73fbe38f860e2b2b9f9fd68cde16a33657eb05d0117a3a2e78ab5dfa8e2226
5
5
  SHA512:
6
- metadata.gz: da7954815892f7f17c8bbc41435e15403a7147457ae22ed6d2955d5f0c6a490a9679773862d3fea3e598dac04e5698516bea64e04c236378de4808d6f178fd78
7
- data.tar.gz: 2cbe4b43830f9a0ca70aaba5923d2e4c46af72d38a96c56ff67ff4be5ec6d4ae7f206d47e0860480161c78b8d4b9e19baa4862ac68da07517ecc2caa316f777a
6
+ metadata.gz: 8b0da0f3b0820c93b32af05eb040fd6d88e86748a8fd99c38609a204944a84b773f2e53bf757c103c0c0f5a6e66097fd65c839ca95c0ec8360c9ac1ae1814153
7
+ data.tar.gz: 0a2d28a1fbc0e3b87fa96452a6028d0f0f8e00d223eb1440f1daf86eb3e40ec33c25c21222c9a2fc585d053dcca63e9de6d9d0e96ca7eb277e452e0baa77c2ac
@@ -1,3 +1,34 @@
1
+ # Hypothesis for Ruby 0.3.0 (2021-01-08)
2
+
3
+ This release converts Hypothesis for Ruby to use [RuTie](https://github.com/danielpclark/rutie)
4
+ instead of the deprecated [Helix](https://github.com/tildeio/helix), restoring compatibility
5
+ with recent versions of Rust. Thanks to Alex Weisberger for taking this on!
6
+
7
+ # Hypothesis for Ruby 0.2.0 (2018-10-24)
8
+
9
+ This release adds an example database to Hypothesis for Ruby. This means that when a test fails,
10
+ it will automatically reuse the previously shown example when you rerun it, without having to
11
+ manually pass a seed.
12
+
13
+ # Hypothesis for Ruby 0.1.2 (2018-09-24)
14
+
15
+ This release makes the code useable via a direct require.
16
+ I.e. no need for rubygems or any special LOAD_PATH.
17
+
18
+ For example, if the base directory were in /opt, you'd just say:
19
+ require "/opt/hypothesis/hypothesis-ruby/lib/hypothesis"
20
+
21
+ # Hypothesis for Ruby 0.1.1 (2018-08-31)
22
+
23
+ This release fixes minor documentation issues.
24
+
25
+ Thanks to Tessa Bradbury for this contribution.
26
+
27
+ # Hypothesis for Ruby 0.1.0 (2018-07-16)
28
+
29
+ This release adds support for reporting multiple exceptions when Hypothesis
30
+ finds more than one way for the test to fail.
31
+
1
32
  # Hypothesis for Ruby 0.0.15 (2018-06-25)
2
33
 
3
34
  This release fixes an occasional `RuntimeError` that could occur
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 Wiesberger <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.2.1'
14
+ conjecture = '0.4.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
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
+ system('cargo build --release')
9
+ end
10
+
8
11
  begin
9
12
  require 'rspec/core/rake_task'
10
13
  RSpec::Core::RakeTask.new(:spec)
@@ -20,8 +23,6 @@ begin
20
23
  rescue LoadError
21
24
  end
22
25
 
23
- HelixRuntime::BuildTask.new
24
-
25
26
  def rubocop(fix:)
26
27
  sh "bundle exec rubocop #{'-a' if fix} lib spec minitests " \
27
28
  'Rakefile hypothesis-specs.gemspec'
@@ -1,10 +1,11 @@
1
1
  # frozen_string_literal: true
2
2
 
3
- require 'hypothesis/errors'
4
- require 'hypothesis/possible'
5
- require 'hypothesis/testcase'
6
- require 'hypothesis/engine'
7
- require 'hypothesis/world'
3
+ require_relative 'hypothesis/junkdrawer'
4
+ require_relative 'hypothesis/errors'
5
+ require_relative 'hypothesis/possible'
6
+ require_relative 'hypothesis/testcase'
7
+ require_relative 'hypothesis/engine'
8
+ require_relative 'hypothesis/world'
8
9
 
9
10
  # This is the main module for using Hypothesis.
10
11
  # It is expected that you will include this in your
@@ -18,6 +19,23 @@ require 'hypothesis/world'
18
19
  module Hypothesis
19
20
  # @!visibility private
20
21
  HYPOTHESIS_LOCATION = File.dirname(__FILE__)
22
+ # rubocop:disable ClassVars
23
+ @@setup_called = false
24
+ # rubocop:enable RuleByName
25
+
26
+ def self.setup_called
27
+ @@setup_called == true
28
+ end
29
+
30
+ def self.included(*)
31
+ if setup_called == false
32
+ Rutie.new(:hypothesis_ruby_core).init(
33
+ 'Init_rutie_hypothesis_core',
34
+ __dir__
35
+ )
36
+ end
37
+ @@setup_called = true
38
+ end
21
39
 
22
40
  # @!visibility private
23
41
  def hypothesis_stable_identifier
@@ -121,7 +139,7 @@ module Hypothesis
121
139
  # of the block is a *test case*.
122
140
  # A test case has three important features:
123
141
  #
124
- # * *givens* are the result of a call to self.given, and are the
142
+ # * *givens* are the result of a call to self.any, and are the
125
143
  # values that make up the test case. These might be values such
126
144
  # as strings, integers, etc. or they might be values specific to
127
145
  # your application such as a User object.
@@ -139,12 +157,21 @@ module Hypothesis
139
157
  #
140
158
  # A call to hypothesis does the following:
141
159
  #
142
- # 1. It tries to *generate* a failing test case.
143
- # 2. If it succeeded then it will *shrink* that failing test case.
144
- # 3. Finally, it will *display* the shrunk failing test case by
160
+ # 1. It first tries to *reuse* failing test cases for previous runs.
161
+ # 2. If there were no previous failing test cases then it tries to
162
+ # *generate* new failing test cases.
163
+ # 3. If either of the first two phases found failing test cases then
164
+ # it will *shrink* those failing test cases.
165
+ # 4. Finally, it will *display* the shrunk failing test case by
145
166
  # the error from its failing assertion, modified to show the
146
167
  # givens of the test case.
147
168
  #
169
+ # Reuse uses an internal representation of the test case, so examples
170
+ # from previous runs will obey all of the usual invariants of generation.
171
+ # However, this means that if you change your test then reuse may not
172
+ # work. Test cases that have become invalid or passing will be cleaned
173
+ # up automatically.
174
+ #
148
175
  # Generation consists of randomly trying test cases until one of
149
176
  # three things has happened:
150
177
  #
@@ -169,13 +196,21 @@ module Hypothesis
169
196
  #
170
197
  # @param max_valid_test_cases [Integer] The maximum number of valid test
171
198
  # cases to run without finding a failing test case before stopping.
172
- def hypothesis(max_valid_test_cases: 200, &block)
199
+ #
200
+ # @param database [String, nil, false] A path to a directory where Hypothesis
201
+ # should store previously failing test cases. If it is nil, Hypothesis
202
+ # will use a default of .hypothesis/examples in the current directory.
203
+ # May also be set to false to disable the database functionality.
204
+ def hypothesis(max_valid_test_cases: 200, database: nil, &block)
173
205
  unless World.current_engine.nil?
174
206
  raise UsageError, 'Cannot nest hypothesis calls'
175
207
  end
208
+
176
209
  begin
177
210
  World.current_engine = Engine.new(
178
- max_examples: max_valid_test_cases
211
+ hypothesis_stable_identifier,
212
+ max_examples: max_valid_test_cases,
213
+ database: database
179
214
  )
180
215
  World.current_engine.run(&block)
181
216
  ensure
@@ -1,18 +1,32 @@
1
1
  # frozen_string_literal: true
2
2
 
3
- require 'helix_runtime'
4
- require 'hypothesis-ruby/native'
3
+ require 'rutie'
4
+
5
+ require 'rspec/expectations'
5
6
 
6
7
  module Hypothesis
8
+ DEFAULT_DATABASE_PATH = File.join(Dir.pwd, '.hypothesis', 'examples')
9
+
7
10
  class Engine
11
+ include RSpec::Matchers
12
+
8
13
  attr_reader :current_source
9
14
  attr_accessor :is_find
10
15
 
11
- def initialize(options)
16
+ def initialize(name, options)
12
17
  seed = Random.rand(2**64 - 1)
18
+
19
+ database = options.fetch(:database, nil)
20
+
21
+ database = DEFAULT_DATABASE_PATH if database.nil?
22
+
23
+ database = nil if database == false
24
+
13
25
  @core_engine = HypothesisCoreEngine.new(
14
- seed, options.fetch(:max_examples)
26
+ name, database, seed, options.fetch(:max_examples)
15
27
  )
28
+
29
+ @exceptions_to_tags = Hash.new { |h, k| h[k] = h.size }
16
30
  end
17
31
 
18
32
  def run
@@ -23,7 +37,7 @@ module Hypothesis
23
37
  begin
24
38
  result = yield(@current_source)
25
39
  if is_find && result
26
- @core_engine.finish_interesting(core)
40
+ @core_engine.finish_interesting(core, 0)
27
41
  else
28
42
  @core_engine.finish_valid(core)
29
43
  end
@@ -31,54 +45,64 @@ module Hypothesis
31
45
  @core_engine.finish_invalid(core)
32
46
  rescue DataOverflow
33
47
  @core_engine.finish_overflow(core)
34
- rescue Exception
48
+ rescue Exception => e
35
49
  raise if is_find
36
- @core_engine.finish_interesting(core)
50
+ key = [
51
+ e.class,
52
+ HypothesisJunkDrawer.find_first_relevant_line(e.backtrace)
53
+ ]
54
+ @core_engine.finish_interesting(core, @exceptions_to_tags[key])
37
55
  end
38
56
  end
39
- @current_source = nil
40
- core = @core_engine.failing_example
41
- if core.nil?
57
+ if @core_engine.count_failing_examples.zero?
42
58
  raise Unsatisfiable if @core_engine.was_unsatisfiable
59
+ @current_source = nil
43
60
  return
44
61
  end
45
62
 
46
63
  if is_find
64
+ core = @core_engine.failing_example(0)
47
65
  @current_source = TestCase.new(core, record_draws: true)
48
66
  yield @current_source
49
67
  else
50
- @current_source = TestCase.new(core, print_draws: true)
68
+ exceptions = []
69
+ (0...@core_engine.count_failing_examples).each do |example|
70
+ core = @core_engine.failing_example(example)
71
+ @current_source = TestCase.new(core, print_draws: true)
51
72
 
52
- begin
53
- yield @current_source
54
- rescue Exception => e
55
- givens = @current_source.print_log
56
- given_str = givens.each_with_index.map do |(name, s), i|
57
- name = "##{i + 1}" if name.nil?
58
- "Given #{name}: #{s}"
59
- end.to_a
60
-
61
- if e.respond_to? :hypothesis_data
62
- e.hypothesis_data[0] = given_str
63
- else
64
- original_to_s = e.to_s
65
- original_inspect = e.inspect
73
+ begin
74
+ yield @current_source
75
+ rescue Exception => e
76
+ givens = @current_source.print_log
77
+ given_str = givens.each_with_index.map do |(name, s), i|
78
+ name = "##{i + 1}" if name.nil?
79
+ "Given #{name}: #{s}"
80
+ end.to_a
66
81
 
67
- class <<e
68
- attr_accessor :hypothesis_data
82
+ if e.respond_to? :hypothesis_data
83
+ e.hypothesis_data[0] = given_str
84
+ else
85
+ original_to_s = e.to_s
86
+ original_inspect = e.inspect
69
87
 
70
- def to_s
71
- ['', hypothesis_data[0], '', hypothesis_data[1]].join("\n")
72
- end
88
+ class <<e
89
+ attr_accessor :hypothesis_data
90
+
91
+ def to_s
92
+ ['', hypothesis_data[0], '', hypothesis_data[1]].join("\n")
93
+ end
73
94
 
74
- def inspect
75
- ['', hypothesis_data[0], '', hypothesis_data[2]].join("\n")
95
+ def inspect
96
+ ['', hypothesis_data[0], '', hypothesis_data[2]].join("\n")
97
+ end
76
98
  end
99
+ e.hypothesis_data = [given_str, original_to_s, original_inspect]
77
100
  end
78
- e.hypothesis_data = [given_str, original_to_s, original_inspect]
101
+ raise e if @core_engine.count_failing_examples == 1
102
+ exceptions.push(e)
79
103
  end
80
- raise e
81
104
  end
105
+ raise Hypothesis::MultipleExceptionError.new(*exceptions)
82
106
  end
83
107
  end
84
108
  end
@@ -25,4 +25,37 @@ module Hypothesis
25
25
  # @!visibility private
26
26
  class DataOverflow < HypothesisError
27
27
  end
28
+
29
+ if defined?(RSpec::Core::MultipleExceptionError)
30
+ MultipleExceptionErrorParent = RSpec::Core::MultipleExceptionError
31
+ # :nocov:
32
+ else
33
+ class MultipleExceptionErrorParent < StandardError
34
+ def initialize(*exceptions)
35
+ super()
36
+
37
+ @all_exceptions = exceptions.to_a
38
+ end
39
+
40
+ attr_reader :all_exceptions
41
+ end
42
+ end
43
+
44
+ class MultipleExceptionError < MultipleExceptionErrorParent
45
+ def message
46
+ jd = HypothesisJunkDrawer
47
+ "Test raised #{all_exceptions.length} distinct errors:\n\n" +
48
+ all_exceptions.map do |e|
49
+ location = jd.find_first_relevant_line(e.backtrace).sub(/:in.+$/, '')
50
+ backtrace = jd.prune_backtrace(e.backtrace)
51
+ "#{e.class} at #{location}:\n" \
52
+ "#{e.message}\n#{backtrace.map { |s| ' ' + s }
53
+ .join("\n")}"
54
+ end.join("\n\n")
55
+ end
56
+
57
+ def backtrace
58
+ []
59
+ end
60
+ end
28
61
  end
@@ -0,0 +1,31 @@
1
+ # frozen_string_literal: true
2
+
3
+ module Hypothesis
4
+ # @!visibility private
5
+ module HypothesisJunkDrawer
6
+ HYPOTHESIS_ROOT = File.absolute_path(File.dirname(__FILE__))
7
+
8
+ def self.prune_backtrace(backtrace)
9
+ result = []
10
+ seen_hypothesis = false
11
+ backtrace.each do |b|
12
+ if b.start_with?(HYPOTHESIS_ROOT)
13
+ seen_hypothesis = true
14
+ else
15
+ result.push(b)
16
+ break if seen_hypothesis
17
+ end
18
+ end
19
+ result
20
+ end
21
+
22
+ def self.find_first_relevant_line(backtrace)
23
+ backtrace.each do |b|
24
+ next if b.include?('minitest/assertions.rb')
25
+ next if b.start_with?(HYPOTHESIS_ROOT)
26
+ return b
27
+ end
28
+ nil
29
+ end
30
+ end
31
+ end
@@ -111,7 +111,7 @@ module Hypothesis
111
111
  # two names: A singular and a plural name. These are
112
112
  # simply aliases and are identical in every way, but are
113
113
  # provided to improve readability. For example
114
- # `any integer` reads better than `given integers`
114
+ # `any integer` reads better than `any integers`
115
115
  # but `arrays(of: integers)` reads better than
116
116
  # `arrays(of: integer)`.
117
117
  module Possibilities
@@ -124,7 +124,7 @@ module Hypothesis
124
124
  # built_as lets you chain multiple Possible values together,
125
125
  # by providing whatever value results from its block.
126
126
  #
127
- # For example the following provides a array plus some
127
+ # For example the following provides an array plus some
128
128
  # element from that array:
129
129
  #
130
130
  # ```ruby
@@ -135,6 +135,7 @@ module Hypothesis
135
135
  # assume ls.size > 0
136
136
  # i = any element_of(ls)
137
137
  # [ls, i]
138
+ # end
138
139
  # ```
139
140
  #
140
141
  # @return [Possible] A Possible whose possible values are
@@ -177,7 +178,7 @@ module Hypothesis
177
178
  # valid.
178
179
  # @param min_size [Integer] The smallest valid length for a
179
180
  # provided string
180
- # @param max_size [Integer] The smallest valid length for a
181
+ # @param max_size [Integer] The largest valid length for a
181
182
  # provided string
182
183
  def strings(codepoints: nil, min_size: 0, max_size: 10)
183
184
  codepoints = self.codepoints if codepoints.nil?
@@ -263,9 +264,9 @@ module Hypothesis
263
264
  alias array_of_shape arrays_of_shape
264
265
 
265
266
  # A Possible Array of variable shape.
266
- # This is used for arrays where all of the elements come from
267
- # the size may vary and the same values are possible at any position.
268
- # For example, arrays(booleans) might provide [false, true, false].
267
+ # This is used for arrays where the size may vary and the same values
268
+ # are possible at any position.
269
+ # For example, arrays(of: booleans) might provide [false, true, false].
269
270
  # @return [Possible]
270
271
  # @param of [Possible] The possible elements of the array.
271
272
  # @param min_size [Integer] The smallest valid size of a provided array
data/src/lib.rs CHANGED
@@ -1,180 +1,469 @@
1
- // "Bridging" root code that exists exclusively to provide
2
- // a ruby -> Hypothesis engine binding. Long term the code
3
- // in here is the only code that is going to stay in this
4
- // crate, and everything else is going to get factored out
5
- // into its own.
6
-
7
- #![recursion_limit = "256"]
8
- #![deny(warnings, missing_debug_implementations)]
9
-
10
- extern crate core;
11
1
  #[macro_use]
12
- extern crate helix;
13
- extern crate rand;
2
+ extern crate rutie;
3
+ #[macro_use]
4
+ extern crate lazy_static;
14
5
  extern crate conjecture;
15
6
 
16
7
  use std::mem;
17
8
 
18
- use conjecture::data::{DataSource, Status};
19
- use conjecture::distributions::Repeat;
9
+ use rutie::{AnyException, AnyObject, Boolean, Class, Float, Integer, NilClass, Object, RString, VM};
10
+
11
+ use conjecture::data::{DataSource, Status, TestResult};
12
+ use conjecture::database::{BoxedDatabase, DirectoryDatabase, NoDatabase};
20
13
  use conjecture::distributions;
14
+ use conjecture::distributions::Repeat;
21
15
  use conjecture::engine::Engine;
22
16
 
23
- ruby! {
24
- class HypothesisCoreDataSource {
25
- struct {
26
- source: Option<DataSource>,
17
+ pub struct HypothesisCoreDataSourceStruct {
18
+ source: Option<DataSource>,
19
+ }
20
+
21
+ impl HypothesisCoreDataSourceStruct {
22
+ fn new(engine: &mut HypothesisCoreEngineStruct) -> HypothesisCoreDataSourceStruct {
23
+ HypothesisCoreDataSourceStruct {
24
+ source: mem::take(&mut engine.pending),
25
+ }
26
+ }
27
+
28
+ fn start_draw(&mut self) {
29
+ if let Some(ref mut source) = self.source {
30
+ source.start_draw();
31
+ }
32
+ }
33
+
34
+ fn stop_draw(&mut self) {
35
+ if let Some(ref mut source) = self.source {
36
+ source.stop_draw();
37
+ }
38
+ }
39
+ }
40
+
41
+ wrappable_struct!(
42
+ HypothesisCoreDataSourceStruct,
43
+ HypothesisCoreDataSourceStructWrapper,
44
+ HYPOTHESIS_CORE_DATA_SOURCE_STRUCT_WRAPPER
45
+ );
46
+
47
+ class!(HypothesisCoreDataSource);
48
+
49
+ methods!(
50
+ HypothesisCoreDataSource,
51
+ itself,
52
+ fn ruby_hypothesis_core_data_source_start_draw() -> NilClass {
53
+ itself
54
+ .get_data_mut(&*HYPOTHESIS_CORE_DATA_SOURCE_STRUCT_WRAPPER)
55
+ .start_draw();
56
+
57
+ NilClass::new()
27
58
  }
59
+ fn ruby_hypothesis_core_data_source_stop_draw() -> NilClass {
60
+ itself
61
+ .get_data_mut(&*HYPOTHESIS_CORE_DATA_SOURCE_STRUCT_WRAPPER)
62
+ .stop_draw();
28
63
 
29
- def initialize(helix, engine: &mut HypothesisCoreEngine){
30
- let mut result = HypothesisCoreDataSource{helix, source: None};
31
- mem::swap(&mut result.source, &mut engine.pending);
32
- return result;
64
+ NilClass::new()
33
65
  }
66
+ );
34
67
 
35
- def start_draw(&mut self){
36
- if let &mut Some(ref mut source) = &mut self.source {
37
- source.start_draw();
38
- }
68
+ pub struct HypothesisCoreEngineStruct {
69
+ engine: Engine,
70
+ pending: Option<DataSource>,
71
+ interesting_examples: Vec<TestResult>,
72
+ }
73
+
74
+ impl HypothesisCoreEngineStruct {
75
+ fn new(
76
+ name: String,
77
+ database_path: Option<String>,
78
+ seed: u64,
79
+ max_examples: u64,
80
+ ) -> HypothesisCoreEngineStruct {
81
+ let xs: [u32; 2] = [seed as u32, (seed >> 32) as u32];
82
+ let db: BoxedDatabase = match database_path {
83
+ None => Box::new(NoDatabase),
84
+ Some(path) => Box::new(DirectoryDatabase::new(path)),
85
+ };
86
+
87
+ HypothesisCoreEngineStruct {
88
+ engine: Engine::new(name, max_examples, &xs, db),
89
+ pending: None,
90
+ interesting_examples: Vec::new(),
91
+ }
92
+ }
93
+
94
+ fn new_source(&mut self) -> Option<HypothesisCoreDataSourceStruct> {
95
+ match self.engine.next_source() {
96
+ None => {
97
+ self.interesting_examples = self.engine.list_minimized_examples();
98
+ None
99
+ }
100
+ Some(source) => {
101
+ self.pending = Some(source);
102
+ Some(HypothesisCoreDataSourceStruct::new(self))
103
+ }
104
+ }
105
+ }
106
+
107
+ fn count_failing_examples(&self) -> usize {
108
+ self.interesting_examples.len()
109
+ }
110
+
111
+ fn failing_example(&mut self, i: usize) -> HypothesisCoreDataSourceStruct {
112
+ self.pending = Some(DataSource::from_vec(
113
+ self.interesting_examples[i].record.clone(),
114
+ ));
115
+ HypothesisCoreDataSourceStruct::new(self)
39
116
  }
40
117
 
41
- def stop_draw(&mut self){
42
- if let &mut Some(ref mut source) = &mut self.source {
43
- source.stop_draw();
44
- }
118
+ fn was_unsatisfiable(&mut self) -> bool {
119
+ self.engine.was_unsatisfiable()
45
120
  }
46
- }
47
121
 
48
- class HypothesisCoreEngine {
49
- struct {
50
- engine: Engine,
51
- pending: Option<DataSource>,
122
+ fn finish_overflow(&mut self, child: &mut HypothesisCoreDataSourceStruct) {
123
+ mark_child_status(&mut self.engine, child, Status::Overflow);
52
124
  }
53
125
 
54
- def initialize(helix, seed: u64, max_examples: u64){
55
- let xs: [u32; 2] = [seed as u32, (seed >> 32) as u32];
56
- HypothesisCoreEngine{
57
- helix,
58
- engine: Engine::new(max_examples, &xs),
59
- pending: None,
60
- }
126
+ fn finish_valid(&mut self, child: &mut HypothesisCoreDataSourceStruct) {
127
+ mark_child_status(&mut self.engine, child, Status::Valid);
61
128
  }
62
129
 
63
- def new_source(&mut self) -> Option<HypothesisCoreDataSource> {
64
- match self.engine.next_source() {
65
- None => None,
66
- Some(source) => {
67
- self.pending = Some(source);
68
- Some(HypothesisCoreDataSource::new(self))
69
- },
70
- }
130
+ fn finish_invalid(&mut self, child: &mut HypothesisCoreDataSourceStruct) {
131
+ mark_child_status(&mut self.engine, child, Status::Invalid);
71
132
  }
72
133
 
73
- def failing_example(&mut self) -> Option<HypothesisCoreDataSource> {
74
- if let Some(source) = self.engine.best_source() {
75
- self.pending = Some(source);
76
- return Some(HypothesisCoreDataSource::new(self));
77
- } else {
78
- return None;
79
- }
134
+ fn finish_interesting(&mut self, child: &mut HypothesisCoreDataSourceStruct, label: u64) {
135
+ mark_child_status(&mut self.engine, child, Status::Interesting(label));
80
136
  }
137
+ }
138
+
139
+ wrappable_struct!(
140
+ HypothesisCoreEngineStruct,
141
+ HypothesisCoreEngineStructWrapper,
142
+ HYPOTHESIS_CORE_ENGINE_STRUCT_WRAPPER
143
+ );
144
+
145
+ class!(HypothesisCoreEngine);
81
146
 
82
- def was_unsatisfiable(&mut self) -> bool {
83
- self.engine.was_unsatisfiable()
147
+ methods!(
148
+ HypothesisCoreEngine,
149
+ itself,
150
+ fn ruby_hypothesis_core_engine_new(
151
+ name: RString,
152
+ database_path: RString,
153
+ seed: Integer,
154
+ max_example: Integer
155
+ ) -> AnyObject {
156
+ let core_engine = HypothesisCoreEngineStruct::new(
157
+ safe_access(name).to_string(),
158
+ database_path.ok().map(|p| p.to_string()),
159
+ safe_access(seed).to_u64(),
160
+ safe_access(max_example).to_u64(),
161
+ );
162
+
163
+ Class::from_existing("HypothesisCoreEngine")
164
+ .wrap_data(core_engine, &*HYPOTHESIS_CORE_ENGINE_STRUCT_WRAPPER)
165
+ }
166
+ fn ruby_hypothesis_core_engine_new_source() -> AnyObject {
167
+ match itself
168
+ .get_data_mut(&*HYPOTHESIS_CORE_ENGINE_STRUCT_WRAPPER)
169
+ .new_source()
170
+ {
171
+ Some(ds) => Class::from_existing("HypothesisCoreDataSource")
172
+ .wrap_data(ds, &*HYPOTHESIS_CORE_DATA_SOURCE_STRUCT_WRAPPER),
173
+ None => NilClass::new().into(),
174
+ }
84
175
  }
176
+ fn ruby_hypothesis_core_engine_finish_overflow(child: AnyObject) -> NilClass {
177
+ let core_engine = itself.get_data_mut(&*HYPOTHESIS_CORE_ENGINE_STRUCT_WRAPPER);
178
+ let mut rdata_source = safe_access(child);
179
+ let data_source = rdata_source.get_data_mut(&*HYPOTHESIS_CORE_DATA_SOURCE_STRUCT_WRAPPER);
180
+
181
+ core_engine.finish_overflow(data_source);
182
+
183
+ NilClass::new()
184
+ }
185
+ fn ruby_hypothesis_core_engine_finish_valid(child: AnyObject) -> NilClass {
186
+ let core_engine = itself.get_data_mut(&*HYPOTHESIS_CORE_ENGINE_STRUCT_WRAPPER);
187
+ let mut rdata_source = safe_access(child);
188
+ let data_source = rdata_source.get_data_mut(&*HYPOTHESIS_CORE_DATA_SOURCE_STRUCT_WRAPPER);
189
+
190
+ core_engine.finish_valid(data_source);
85
191
 
86
- def finish_overflow(&mut self, child: &mut HypothesisCoreDataSource){
87
- mark_child_status(&mut self.engine, child, Status::Overflow);
192
+ NilClass::new()
88
193
  }
194
+ fn ruby_hypothesis_core_engine_finish_invalid(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);
89
198
 
90
- def finish_invalid(&mut self, child: &mut HypothesisCoreDataSource){
91
- mark_child_status(&mut self.engine, child, Status::Invalid);
199
+ core_engine.finish_invalid(data_source);
200
+
201
+ NilClass::new()
92
202
  }
203
+ fn ruby_hypothesis_core_engine_finish_interesting(
204
+ child: AnyObject,
205
+ label: Integer
206
+ ) -> NilClass {
207
+ let core_engine = itself.get_data_mut(&*HYPOTHESIS_CORE_ENGINE_STRUCT_WRAPPER);
208
+ let mut rdata_source = safe_access(child);
209
+ let data_source = rdata_source.get_data_mut(&*HYPOTHESIS_CORE_DATA_SOURCE_STRUCT_WRAPPER);
210
+
211
+ core_engine.finish_interesting(data_source, safe_access(label).to_u64());
93
212
 
94
- def finish_interesting(&mut self, child: &mut HypothesisCoreDataSource){
95
- mark_child_status(&mut self.engine, child, Status::Interesting);
213
+ NilClass::new()
96
214
  }
215
+ fn ruby_hypothesis_core_engine_count_failing_examples() -> Integer {
216
+ let core_engine = itself.get_data(&*HYPOTHESIS_CORE_ENGINE_STRUCT_WRAPPER);
97
217
 
98
- def finish_valid(&mut self, child: &mut HypothesisCoreDataSource){
99
- mark_child_status(&mut self.engine, child, Status::Valid);
218
+ Integer::new(core_engine.count_failing_examples() as i64)
100
219
  }
101
- }
220
+ fn ruby_hypothesis_core_failing_example(i: Integer) -> AnyObject {
221
+ let core_engine = itself.get_data_mut(&*HYPOTHESIS_CORE_ENGINE_STRUCT_WRAPPER);
222
+ let int = safe_access(i).to_u64() as usize;
223
+
224
+ let data_source = core_engine.failing_example(int);
102
225
 
103
- class HypothesisCoreBitPossible{
104
- struct {
105
- n_bits: u64,
226
+ Class::from_existing("HypothesisCoreDataSource")
227
+ .wrap_data(data_source, &*HYPOTHESIS_CORE_DATA_SOURCE_STRUCT_WRAPPER)
106
228
  }
229
+ fn ruby_hypothesis_core_engine_was_unsatisfiable() -> Boolean {
230
+ let core_engine = itself.get_data_mut(&*HYPOTHESIS_CORE_ENGINE_STRUCT_WRAPPER);
107
231
 
108
- def initialize(helix, n_bits: u64){
109
- return HypothesisCoreBitPossible{helix, n_bits: n_bits};
232
+ Boolean::new(core_engine.was_unsatisfiable())
110
233
  }
234
+ );
235
+
236
+ pub struct HypothesisCoreIntegersStruct {
237
+ bitlengths: distributions::Sampler,
238
+ }
111
239
 
112
- def provide(&mut self, data: &mut HypothesisCoreDataSource) -> Option<u64>{
113
- match &mut data.source {
114
- &mut None => None,
115
- &mut Some(ref mut source) => source.bits(self.n_bits).ok(),
116
- }
240
+ impl HypothesisCoreIntegersStruct {
241
+ fn new() -> HypothesisCoreIntegersStruct {
242
+ HypothesisCoreIntegersStruct {
243
+ bitlengths: distributions::good_bitlengths(),
244
+ }
117
245
  }
118
- }
119
246
 
120
- class HypothesisCoreRepeatValues{
121
- struct {
122
- repeat: Repeat,
247
+ fn provide(&mut self, data: &mut HypothesisCoreDataSourceStruct) -> Option<i64> {
248
+ data.source.as_mut().and_then(|ref mut source| {
249
+ distributions::integer_from_bitlengths(source, &self.bitlengths).ok()
250
+ })
123
251
  }
252
+ }
253
+
254
+ wrappable_struct!(
255
+ HypothesisCoreIntegersStruct,
256
+ HypothesisCoreIntegersStructWrapper,
257
+ HYPOTHESIS_CORE_INTEGERS_STRUCT_WRAPPER
258
+ );
259
+
260
+ class!(HypothesisCoreIntegers);
261
+
262
+ methods!(
263
+ HypothesisCoreIntegers,
264
+ itself,
265
+ fn ruby_hypothesis_core_integers_new() -> AnyObject {
266
+ let core_integers = HypothesisCoreIntegersStruct::new();
124
267
 
125
- def initialize(helix, min_count: u64, max_count: u64, expected_count: f64){
126
- return HypothesisCoreRepeatValues{
127
- helix, repeat: Repeat::new(min_count, max_count, expected_count)
128
- }
268
+ Class::from_existing("HypothesisCoreIntegers")
269
+ .wrap_data(core_integers, &*HYPOTHESIS_CORE_INTEGERS_STRUCT_WRAPPER)
129
270
  }
271
+ fn ruby_hypothesis_core_integers_provide(data: AnyObject) -> AnyObject {
272
+ let core_integers = itself.get_data_mut(&*HYPOTHESIS_CORE_INTEGERS_STRUCT_WRAPPER);
273
+ let mut rdata = safe_access(data);
274
+ let data_source = rdata.get_data_mut(&*HYPOTHESIS_CORE_DATA_SOURCE_STRUCT_WRAPPER);
130
275
 
131
- def _should_continue(&mut self, data: &mut HypothesisCoreDataSource) -> Option<bool>{
132
- return data.source.as_mut().and_then(|ref mut source| {
133
- self.repeat.should_continue(source).ok()
134
- })
276
+ match core_integers.provide(data_source) {
277
+ Some(i) => Integer::new(i).into(),
278
+ None => NilClass::new().into(),
279
+ }
135
280
  }
281
+ );
136
282
 
137
- def reject(&mut self){
138
- self.repeat.reject();
283
+ pub struct HypothesisCoreRepeatValuesStruct {
284
+ repeat: Repeat,
285
+ }
286
+
287
+ impl HypothesisCoreRepeatValuesStruct {
288
+ fn new(
289
+ min_count: u64,
290
+ max_count: u64,
291
+ expected_count: f64,
292
+ ) -> HypothesisCoreRepeatValuesStruct {
293
+ HypothesisCoreRepeatValuesStruct {
294
+ repeat: Repeat::new(min_count, max_count, expected_count),
295
+ }
139
296
  }
140
- }
141
297
 
142
- class HypothesisCoreIntegers{
143
- struct {
144
- bitlengths: distributions::Sampler,
298
+ fn _should_continue(&mut self, data: &mut HypothesisCoreDataSourceStruct) -> Option<bool> {
299
+ return data
300
+ .source
301
+ .as_mut()
302
+ .and_then(|ref mut source| self.repeat.should_continue(source).ok());
145
303
  }
146
- def initialize(helix){
147
- return HypothesisCoreIntegers{helix,bitlengths: distributions::good_bitlengths()};
304
+
305
+ fn reject(&mut self) {
306
+ self.repeat.reject();
148
307
  }
149
- def provide(&mut self, data: &mut HypothesisCoreDataSource) -> Option<i64>{
150
- data.source.as_mut().and_then(|ref mut source| {
151
- distributions::integer_from_bitlengths(source, &self.bitlengths).ok()
152
- })
308
+ }
309
+
310
+ wrappable_struct!(
311
+ HypothesisCoreRepeatValuesStruct,
312
+ HypothesisCoreRepeatValuesStructWrapper,
313
+ HYPOTHESIS_CORE_REPEAT_VALUES_STRUCT_WRAPPER
314
+ );
315
+
316
+ class!(HypothesisCoreRepeatValues);
317
+
318
+ methods!(
319
+ HypothesisCoreRepeatValues,
320
+ itself,
321
+ fn ruby_hypothesis_core_repeat_values_new(
322
+ min_count: Integer,
323
+ max_count: Integer,
324
+ expected_count: Float
325
+ ) -> AnyObject {
326
+ let repeat_values = HypothesisCoreRepeatValuesStruct::new(
327
+ safe_access(min_count).to_u64(),
328
+ safe_access(max_count).to_u64(),
329
+ safe_access(expected_count).to_f64(),
330
+ );
331
+
332
+ Class::from_existing("HypothesisCoreRepeatValues").wrap_data(
333
+ repeat_values,
334
+ &*HYPOTHESIS_CORE_REPEAT_VALUES_STRUCT_WRAPPER,
335
+ )
153
336
  }
154
- }
337
+ fn ruby_hypothesis_core_repeat_values_should_continue(data: AnyObject) -> AnyObject {
338
+ let mut rdata = safe_access(data);
339
+ let mut data_source = rdata.get_data_mut(&*HYPOTHESIS_CORE_DATA_SOURCE_STRUCT_WRAPPER);
340
+
341
+ let should_continue = itself
342
+ .get_data_mut(&*HYPOTHESIS_CORE_REPEAT_VALUES_STRUCT_WRAPPER)
343
+ ._should_continue(data_source);
155
344
 
156
- class HypothesisCoreBoundedIntegers{
157
- struct {
158
- max_value: u64,
345
+ match should_continue {
346
+ Some(b) => Boolean::new(b).into(),
347
+ None => NilClass::new().into(),
348
+ }
159
349
  }
160
- def initialize(helix, max_value: u64){
161
- return HypothesisCoreBoundedIntegers{helix, max_value: max_value};
350
+ fn ruby_hypothesis_core_repeat_values_reject() -> NilClass {
351
+ let repeat_values = itself.get_data_mut(&*HYPOTHESIS_CORE_REPEAT_VALUES_STRUCT_WRAPPER);
352
+
353
+ repeat_values.reject();
354
+
355
+ NilClass::new()
162
356
  }
357
+ );
163
358
 
164
- def provide(&mut self, data: &mut HypothesisCoreDataSource) -> Option<u64>{
165
- data.source.as_mut().and_then(|ref mut source| {
166
- distributions::bounded_int(source, self.max_value).ok()
167
- })
359
+ pub struct HypothesisCoreBoundedIntegersStruct {
360
+ max_value: u64,
361
+ }
362
+
363
+ impl HypothesisCoreBoundedIntegersStruct {
364
+ fn provide(&mut self, data: &mut HypothesisCoreDataSourceStruct) -> Option<u64> {
365
+ data.source
366
+ .as_mut()
367
+ .and_then(|ref mut source| distributions::bounded_int(source, self.max_value).ok())
168
368
  }
169
- }
170
369
  }
171
370
 
172
- fn mark_child_status(engine: &mut Engine, child: &mut HypothesisCoreDataSource, status: Status) {
173
- let mut replacement = None;
174
- mem::swap(&mut replacement, &mut child.source);
371
+ wrappable_struct!(
372
+ HypothesisCoreBoundedIntegersStruct,
373
+ HypothesisCoreBoundedIntegersStructWrapper,
374
+ HYPOTHESIS_CORE_BOUNDED_INTEGERS_STRUCT_WRAPPER
375
+ );
376
+
377
+ class!(HypothesisCoreBoundedIntegers);
378
+
379
+ methods!(
380
+ HypothesisCoreBoundedIntegers,
381
+ itself,
382
+ fn ruby_hypothesis_core_bounded_integers_new(max_value: Integer) -> AnyObject {
383
+ let bounded_integers = HypothesisCoreBoundedIntegersStruct {
384
+ max_value: safe_access(max_value).to_u64(),
385
+ };
386
+
387
+ Class::from_existing("HypothesisCoreBoundedIntegers").wrap_data(
388
+ bounded_integers,
389
+ &*HYPOTHESIS_CORE_BOUNDED_INTEGERS_STRUCT_WRAPPER,
390
+ )
391
+ }
392
+ fn ruby_hypothesis_core_bounded_integers_provide(data: AnyObject) -> AnyObject {
393
+ let mut rdata = safe_access(data);
394
+ let data_source = rdata.get_data_mut(&*HYPOTHESIS_CORE_DATA_SOURCE_STRUCT_WRAPPER);
395
+ let bounded_integers =
396
+ itself.get_data_mut(&*HYPOTHESIS_CORE_BOUNDED_INTEGERS_STRUCT_WRAPPER);
397
+
398
+ match bounded_integers.provide(data_source) {
399
+ Some(i) => Integer::from(i).into(),
400
+ None => NilClass::new().into(),
401
+ }
402
+ }
403
+ );
404
+
405
+ #[allow(non_snake_case)]
406
+ #[no_mangle]
407
+ pub extern "C" fn Init_rutie_hypothesis_core() {
408
+ Class::new("HypothesisCoreEngine", None).define(|klass| {
409
+ klass.def_self("new", ruby_hypothesis_core_engine_new);
410
+ klass.def("new_source", ruby_hypothesis_core_engine_new_source);
411
+ klass.def(
412
+ "count_failing_examples",
413
+ ruby_hypothesis_core_engine_count_failing_examples,
414
+ );
415
+ klass.def("failing_example", ruby_hypothesis_core_failing_example);
416
+ klass.def(
417
+ "was_unsatisfiable",
418
+ ruby_hypothesis_core_engine_was_unsatisfiable,
419
+ );
420
+ klass.def(
421
+ "finish_overflow",
422
+ ruby_hypothesis_core_engine_finish_overflow,
423
+ );
424
+ klass.def("finish_valid", ruby_hypothesis_core_engine_finish_valid);
425
+ klass.def("finish_invalid", ruby_hypothesis_core_engine_finish_invalid);
426
+ klass.def(
427
+ "finish_interesting",
428
+ ruby_hypothesis_core_engine_finish_interesting,
429
+ );
430
+ });
431
+
432
+ Class::new("HypothesisCoreDataSource", None).define(|klass| {
433
+ klass.def("start_draw", ruby_hypothesis_core_data_source_start_draw);
434
+ klass.def("stop_draw", ruby_hypothesis_core_data_source_stop_draw);
435
+ });
436
+
437
+ Class::new("HypothesisCoreIntegers", None).define(|klass| {
438
+ klass.def_self("new", ruby_hypothesis_core_integers_new);
439
+ klass.def("provide", ruby_hypothesis_core_integers_provide);
440
+ });
441
+
442
+ Class::new("HypothesisCoreRepeatValues", None).define(|klass| {
443
+ klass.def_self("new", ruby_hypothesis_core_repeat_values_new);
444
+ klass.def(
445
+ "_should_continue",
446
+ ruby_hypothesis_core_repeat_values_should_continue,
447
+ );
448
+ klass.def("reject", ruby_hypothesis_core_repeat_values_reject);
449
+ });
175
450
 
176
- match replacement {
177
- Some(source) => engine.mark_finished(source, status),
178
- None => (),
451
+ Class::new("HypothesisCoreBoundedIntegers", None).define(|klass| {
452
+ klass.def_self("new", ruby_hypothesis_core_bounded_integers_new);
453
+ klass.def("provide", ruby_hypothesis_core_bounded_integers_provide);
454
+ });
455
+ }
456
+
457
+ fn mark_child_status(
458
+ engine: &mut Engine,
459
+ child: &mut HypothesisCoreDataSourceStruct,
460
+ status: Status,
461
+ ) {
462
+ if let Some(source) = mem::take(&mut child.source) {
463
+ engine.mark_finished(source, status)
179
464
  }
180
465
  }
466
+
467
+ fn safe_access<T>(value: Result<T, AnyException>) -> T {
468
+ value.map_err(VM::raise_ex).unwrap()
469
+ }
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.0.15
4
+ version: 0.3.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-06-25 00:00:00.000000000 Z
12
+ date: 2021-01-08 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
@@ -64,6 +65,7 @@ files:
64
65
  - lib/hypothesis.rb
65
66
  - lib/hypothesis/engine.rb
66
67
  - lib/hypothesis/errors.rb
68
+ - lib/hypothesis/junkdrawer.rb
67
69
  - lib/hypothesis/possible.rb
68
70
  - lib/hypothesis/testcase.rb
69
71
  - lib/hypothesis/world.rb
@@ -87,8 +89,7 @@ required_rubygems_version: !ruby/object:Gem::Requirement
87
89
  - !ruby/object:Gem::Version
88
90
  version: '0'
89
91
  requirements: []
90
- rubyforge_project:
91
- rubygems_version: 2.7.6
92
+ rubygems_version: 3.2.4
92
93
  signing_key:
93
94
  specification_version: 4
94
95
  summary: Hypothesis is a powerful, flexible, and easy to use library for property-based