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