hypothesis-specs 0.0.15 → 0.3.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: 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