hypothesis-specs 0.1.0 → 0.4.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 +4 -4
- data/CHANGELOG.md +30 -0
- data/Cargo.toml +5 -3
- data/LICENSE.txt +1 -1
- data/README.markdown +2 -2
- data/Rakefile +19 -2
- data/lib/hypothesis.rb +46 -12
- data/lib/hypothesis/engine.rb +12 -7
- data/lib/hypothesis/possible.rb +7 -6
- data/src/lib.rs +405 -123
- metadata +7 -7
checksums.yaml
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
---
|
2
2
|
SHA256:
|
3
|
-
metadata.gz:
|
4
|
-
data.tar.gz:
|
3
|
+
metadata.gz: 748dfd8fb0c05573aecd17da1fb658a3d3add0ae731e16dc8f3f9274cd5db006
|
4
|
+
data.tar.gz: ffd23f44bd8fa7eaca616cc00bc497033d6c9557761c8ffacdc756d3b6bad080
|
5
5
|
SHA512:
|
6
|
-
metadata.gz:
|
7
|
-
data.tar.gz:
|
6
|
+
metadata.gz: a1e26c552c6baaf4fcf3fa1ef3784ad911c5a06c1582b3153daf09b6f618900656ac08f2377c9fba834e5a1ecd2a2ae90d6f33a542b457fb23db30760265b058
|
7
|
+
data.tar.gz: 2fd4b38d077f32f978010612c72a613b4692f6299b4d5b66885afffe6b22180a4f834d5769f456b29f00a7eb3dc2b31246cae7a7410e2532d183aa61c5651b02
|
data/CHANGELOG.md
CHANGED
@@ -1,3 +1,33 @@
|
|
1
|
+
# Hypothesis for Ruby 0.4.0 (2021-01-12)
|
2
|
+
|
3
|
+
This removes hypothesis-ruby's dependence on RSpec. Now, it can be used with any Ruby test runner.
|
4
|
+
|
5
|
+
# Hypothesis for Ruby 0.3.0 (2021-01-08)
|
6
|
+
|
7
|
+
This release converts Hypothesis for Ruby to use [RuTie](https://github.com/danielpclark/rutie)
|
8
|
+
instead of the deprecated [Helix](https://github.com/tildeio/helix), restoring compatibility
|
9
|
+
with recent versions of Rust. Thanks to Alex Weisberger for taking this on!
|
10
|
+
|
11
|
+
# Hypothesis for Ruby 0.2.0 (2018-10-24)
|
12
|
+
|
13
|
+
This release adds an example database to Hypothesis for Ruby. This means that when a test fails,
|
14
|
+
it will automatically reuse the previously shown example when you rerun it, without having to
|
15
|
+
manually pass a seed.
|
16
|
+
|
17
|
+
# Hypothesis for Ruby 0.1.2 (2018-09-24)
|
18
|
+
|
19
|
+
This release makes the code useable via a direct require.
|
20
|
+
I.e. no need for rubygems or any special LOAD_PATH.
|
21
|
+
|
22
|
+
For example, if the base directory were in /opt, you'd just say:
|
23
|
+
require "/opt/hypothesis/hypothesis-ruby/lib/hypothesis"
|
24
|
+
|
25
|
+
# Hypothesis for Ruby 0.1.1 (2018-08-31)
|
26
|
+
|
27
|
+
This release fixes minor documentation issues.
|
28
|
+
|
29
|
+
Thanks to Tessa Bradbury for this contribution.
|
30
|
+
|
1
31
|
# Hypothesis for Ruby 0.1.0 (2018-07-16)
|
2
32
|
|
3
33
|
This release adds support for reporting multiple exceptions when Hypothesis
|
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
|
-
|
11
|
+
rutie = {version="0.8.1"}
|
12
|
+
lazy_static = "1.4.0"
|
11
13
|
rand = '0.3'
|
12
|
-
conjecture = '0.
|
14
|
+
conjecture = '0.4.0'
|
data/LICENSE.txt
CHANGED
@@ -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
|
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
|
data/README.markdown
CHANGED
@@ -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
|
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
|
-
|
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 " \
|
data/lib/hypothesis.rb
CHANGED
@@ -1,11 +1,11 @@
|
|
1
1
|
# frozen_string_literal: true
|
2
2
|
|
3
|
-
|
4
|
-
|
5
|
-
|
6
|
-
|
7
|
-
|
8
|
-
|
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'
|
9
9
|
|
10
10
|
# This is the main module for using Hypothesis.
|
11
11
|
# It is expected that you will include this in your
|
@@ -19,6 +19,23 @@ require 'hypothesis/world'
|
|
19
19
|
module Hypothesis
|
20
20
|
# @!visibility private
|
21
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
|
22
39
|
|
23
40
|
# @!visibility private
|
24
41
|
def hypothesis_stable_identifier
|
@@ -122,7 +139,7 @@ module Hypothesis
|
|
122
139
|
# of the block is a *test case*.
|
123
140
|
# A test case has three important features:
|
124
141
|
#
|
125
|
-
# * *givens* are the result of a call to self.
|
142
|
+
# * *givens* are the result of a call to self.any, and are the
|
126
143
|
# values that make up the test case. These might be values such
|
127
144
|
# as strings, integers, etc. or they might be values specific to
|
128
145
|
# your application such as a User object.
|
@@ -140,12 +157,21 @@ module Hypothesis
|
|
140
157
|
#
|
141
158
|
# A call to hypothesis does the following:
|
142
159
|
#
|
143
|
-
# 1. It tries to *
|
144
|
-
# 2. If
|
145
|
-
#
|
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
|
146
166
|
# the error from its failing assertion, modified to show the
|
147
167
|
# givens of the test case.
|
148
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
|
+
#
|
149
175
|
# Generation consists of randomly trying test cases until one of
|
150
176
|
# three things has happened:
|
151
177
|
#
|
@@ -170,13 +196,21 @@ module Hypothesis
|
|
170
196
|
#
|
171
197
|
# @param max_valid_test_cases [Integer] The maximum number of valid test
|
172
198
|
# cases to run without finding a failing test case before stopping.
|
173
|
-
|
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)
|
174
205
|
unless World.current_engine.nil?
|
175
206
|
raise UsageError, 'Cannot nest hypothesis calls'
|
176
207
|
end
|
208
|
+
|
177
209
|
begin
|
178
210
|
World.current_engine = Engine.new(
|
179
|
-
|
211
|
+
hypothesis_stable_identifier,
|
212
|
+
max_examples: max_valid_test_cases,
|
213
|
+
database: database
|
180
214
|
)
|
181
215
|
World.current_engine.run(&block)
|
182
216
|
ensure
|
data/lib/hypothesis/engine.rb
CHANGED
@@ -1,20 +1,25 @@
|
|
1
1
|
# frozen_string_literal: true
|
2
2
|
|
3
|
-
require '
|
4
|
-
require 'hypothesis-ruby/native'
|
5
|
-
require 'rspec/expectations'
|
3
|
+
require 'rutie'
|
6
4
|
|
7
5
|
module Hypothesis
|
8
|
-
|
9
|
-
include RSpec::Matchers
|
6
|
+
DEFAULT_DATABASE_PATH = File.join(Dir.pwd, '.hypothesis', 'examples')
|
10
7
|
|
8
|
+
class Engine
|
11
9
|
attr_reader :current_source
|
12
10
|
attr_accessor :is_find
|
13
11
|
|
14
|
-
def initialize(options)
|
12
|
+
def initialize(name, options)
|
15
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
|
+
|
16
21
|
@core_engine = HypothesisCoreEngine.new(
|
17
|
-
seed, options.fetch(:max_examples)
|
22
|
+
name, database, seed, options.fetch(:max_examples)
|
18
23
|
)
|
19
24
|
|
20
25
|
@exceptions_to_tags = Hash.new { |h, k| h[k] = h.size }
|
data/lib/hypothesis/possible.rb
CHANGED
@@ -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 `
|
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
|
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
|
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
|
267
|
-
#
|
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,187 +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
|
13
|
-
|
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
|
|
9
|
+
use rutie::{AnyException, AnyObject, Boolean, Class, Float, Integer, NilClass, Object, RString, VM};
|
10
|
+
|
18
11
|
use conjecture::data::{DataSource, Status, TestResult};
|
19
|
-
use conjecture::
|
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
|
-
|
24
|
-
|
25
|
-
|
26
|
-
|
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
|
+
}
|
27
32
|
}
|
28
33
|
|
29
|
-
|
30
|
-
|
31
|
-
|
32
|
-
|
34
|
+
fn stop_draw(&mut self) {
|
35
|
+
if let Some(ref mut source) = self.source {
|
36
|
+
source.stop_draw();
|
37
|
+
}
|
33
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();
|
34
56
|
|
35
|
-
|
36
|
-
if let &mut Some(ref mut source) = &mut self.source {
|
37
|
-
source.start_draw();
|
38
|
-
}
|
57
|
+
NilClass::new()
|
39
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();
|
40
63
|
|
41
|
-
|
42
|
-
if let &mut Some(ref mut source) = &mut self.source {
|
43
|
-
source.stop_draw();
|
44
|
-
}
|
64
|
+
NilClass::new()
|
45
65
|
}
|
46
|
-
|
66
|
+
);
|
47
67
|
|
48
|
-
|
49
|
-
|
50
|
-
|
51
|
-
|
52
|
-
|
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
|
+
}
|
53
92
|
}
|
54
93
|
|
55
|
-
|
56
|
-
|
57
|
-
|
58
|
-
|
59
|
-
|
60
|
-
|
61
|
-
|
62
|
-
|
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
|
+
}
|
63
105
|
}
|
64
106
|
|
65
|
-
|
66
|
-
|
67
|
-
None => {
|
68
|
-
self.interesting_examples = self.engine.list_minimized_examples();
|
69
|
-
None
|
70
|
-
},
|
71
|
-
Some(source) => {
|
72
|
-
self.pending = Some(source);
|
73
|
-
Some(HypothesisCoreDataSource::new(self))
|
74
|
-
},
|
75
|
-
}
|
107
|
+
fn count_failing_examples(&self) -> usize {
|
108
|
+
self.interesting_examples.len()
|
76
109
|
}
|
77
110
|
|
78
|
-
|
79
|
-
|
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)
|
80
116
|
}
|
81
117
|
|
82
|
-
|
83
|
-
|
84
|
-
DataSource::from_vec(self.interesting_examples[i].record.clone())
|
85
|
-
);
|
86
|
-
HypothesisCoreDataSource::new(self)
|
118
|
+
fn was_unsatisfiable(&mut self) -> bool {
|
119
|
+
self.engine.was_unsatisfiable()
|
87
120
|
}
|
88
121
|
|
89
|
-
|
90
|
-
|
122
|
+
fn finish_overflow(&mut self, child: &mut HypothesisCoreDataSourceStruct) {
|
123
|
+
mark_child_status(&mut self.engine, child, Status::Overflow);
|
91
124
|
}
|
92
125
|
|
93
|
-
|
94
|
-
|
126
|
+
fn finish_valid(&mut self, child: &mut HypothesisCoreDataSourceStruct) {
|
127
|
+
mark_child_status(&mut self.engine, child, Status::Valid);
|
95
128
|
}
|
96
129
|
|
97
|
-
|
98
|
-
|
130
|
+
fn finish_invalid(&mut self, child: &mut HypothesisCoreDataSourceStruct) {
|
131
|
+
mark_child_status(&mut self.engine, child, Status::Invalid);
|
132
|
+
}
|
133
|
+
|
134
|
+
fn finish_interesting(&mut self, child: &mut HypothesisCoreDataSourceStruct, label: u64) {
|
135
|
+
mark_child_status(&mut self.engine, child, Status::Interesting(label));
|
136
|
+
}
|
137
|
+
}
|
138
|
+
|
139
|
+
wrappable_struct!(
|
140
|
+
HypothesisCoreEngineStruct,
|
141
|
+
HypothesisCoreEngineStructWrapper,
|
142
|
+
HYPOTHESIS_CORE_ENGINE_STRUCT_WRAPPER
|
143
|
+
);
|
144
|
+
|
145
|
+
class!(HypothesisCoreEngine);
|
146
|
+
|
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
|
+
}
|
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);
|
191
|
+
|
192
|
+
NilClass::new()
|
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);
|
198
|
+
|
199
|
+
core_engine.finish_invalid(data_source);
|
200
|
+
|
201
|
+
NilClass::new()
|
99
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);
|
100
210
|
|
101
|
-
|
102
|
-
|
211
|
+
core_engine.finish_interesting(data_source, safe_access(label).to_u64());
|
212
|
+
|
213
|
+
NilClass::new()
|
103
214
|
}
|
215
|
+
fn ruby_hypothesis_core_engine_count_failing_examples() -> Integer {
|
216
|
+
let core_engine = itself.get_data(&*HYPOTHESIS_CORE_ENGINE_STRUCT_WRAPPER);
|
104
217
|
|
105
|
-
|
106
|
-
mark_child_status(&mut self.engine, child, Status::Valid);
|
218
|
+
Integer::new(core_engine.count_failing_examples() as i64)
|
107
219
|
}
|
108
|
-
|
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);
|
109
225
|
|
110
|
-
|
111
|
-
|
112
|
-
n_bits: u64,
|
226
|
+
Class::from_existing("HypothesisCoreDataSource")
|
227
|
+
.wrap_data(data_source, &*HYPOTHESIS_CORE_DATA_SOURCE_STRUCT_WRAPPER)
|
113
228
|
}
|
229
|
+
fn ruby_hypothesis_core_engine_was_unsatisfiable() -> Boolean {
|
230
|
+
let core_engine = itself.get_data_mut(&*HYPOTHESIS_CORE_ENGINE_STRUCT_WRAPPER);
|
114
231
|
|
115
|
-
|
116
|
-
return HypothesisCoreBitPossible{helix, n_bits: n_bits};
|
232
|
+
Boolean::new(core_engine.was_unsatisfiable())
|
117
233
|
}
|
234
|
+
);
|
118
235
|
|
119
|
-
|
120
|
-
|
121
|
-
|
122
|
-
|
123
|
-
|
236
|
+
pub struct HypothesisCoreIntegersStruct {
|
237
|
+
bitlengths: distributions::Sampler,
|
238
|
+
}
|
239
|
+
|
240
|
+
impl HypothesisCoreIntegersStruct {
|
241
|
+
fn new() -> HypothesisCoreIntegersStruct {
|
242
|
+
HypothesisCoreIntegersStruct {
|
243
|
+
bitlengths: distributions::good_bitlengths(),
|
244
|
+
}
|
124
245
|
}
|
125
|
-
}
|
126
246
|
|
127
|
-
|
128
|
-
|
129
|
-
|
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
|
+
})
|
130
251
|
}
|
252
|
+
}
|
131
253
|
|
132
|
-
|
133
|
-
|
134
|
-
|
135
|
-
|
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();
|
267
|
+
|
268
|
+
Class::from_existing("HypothesisCoreIntegers")
|
269
|
+
.wrap_data(core_integers, &*HYPOTHESIS_CORE_INTEGERS_STRUCT_WRAPPER)
|
136
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);
|
137
275
|
|
138
|
-
|
139
|
-
|
140
|
-
|
141
|
-
|
276
|
+
match core_integers.provide(data_source) {
|
277
|
+
Some(i) => Integer::new(i).into(),
|
278
|
+
None => NilClass::new().into(),
|
279
|
+
}
|
142
280
|
}
|
281
|
+
);
|
282
|
+
|
283
|
+
pub struct HypothesisCoreRepeatValuesStruct {
|
284
|
+
repeat: Repeat,
|
285
|
+
}
|
143
286
|
|
144
|
-
|
145
|
-
|
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
|
+
}
|
146
296
|
}
|
147
|
-
}
|
148
297
|
|
149
|
-
|
150
|
-
|
151
|
-
|
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());
|
152
303
|
}
|
153
|
-
|
154
|
-
|
304
|
+
|
305
|
+
fn reject(&mut self) {
|
306
|
+
self.repeat.reject();
|
155
307
|
}
|
156
|
-
|
157
|
-
|
158
|
-
|
159
|
-
|
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
|
+
)
|
160
336
|
}
|
161
|
-
|
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);
|
162
340
|
|
163
|
-
|
164
|
-
|
165
|
-
|
341
|
+
let should_continue = itself
|
342
|
+
.get_data_mut(&*HYPOTHESIS_CORE_REPEAT_VALUES_STRUCT_WRAPPER)
|
343
|
+
._should_continue(data_source);
|
344
|
+
|
345
|
+
match should_continue {
|
346
|
+
Some(b) => Boolean::new(b).into(),
|
347
|
+
None => NilClass::new().into(),
|
348
|
+
}
|
166
349
|
}
|
167
|
-
|
168
|
-
|
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()
|
169
356
|
}
|
357
|
+
);
|
358
|
+
|
359
|
+
pub struct HypothesisCoreBoundedIntegersStruct {
|
360
|
+
max_value: u64,
|
361
|
+
}
|
170
362
|
|
171
|
-
|
172
|
-
|
173
|
-
|
174
|
-
|
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())
|
175
368
|
}
|
176
|
-
}
|
177
369
|
}
|
178
370
|
|
179
|
-
|
180
|
-
|
181
|
-
|
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
|
+
};
|
182
386
|
|
183
|
-
|
184
|
-
|
185
|
-
|
387
|
+
Class::from_existing("HypothesisCoreBoundedIntegers").wrap_data(
|
388
|
+
bounded_integers,
|
389
|
+
&*HYPOTHESIS_CORE_BOUNDED_INTEGERS_STRUCT_WRAPPER,
|
390
|
+
)
|
186
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
|
+
});
|
450
|
+
|
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)
|
464
|
+
}
|
465
|
+
}
|
466
|
+
|
467
|
+
fn safe_access<T>(value: Result<T, AnyException>) -> T {
|
468
|
+
value.map_err(VM::raise_ex).unwrap()
|
187
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.
|
4
|
+
version: 0.4.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:
|
12
|
+
date: 2021-01-12 00:00:00.000000000 Z
|
12
13
|
dependencies:
|
13
14
|
- !ruby/object:Gem::Dependency
|
14
|
-
name:
|
15
|
+
name: rutie
|
15
16
|
requirement: !ruby/object:Gem::Requirement
|
16
17
|
requirements:
|
17
18
|
- - "~>"
|
18
19
|
- !ruby/object:Gem::Version
|
19
|
-
version: 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.
|
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
|
-
|
92
|
-
rubygems_version: 2.7.6
|
92
|
+
rubygems_version: 3.2.5
|
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
|