hypothesis-specs 0.0.15 → 0.1.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 +5 -0
- data/Cargo.toml +1 -1
- data/lib/hypothesis.rb +1 -0
- data/lib/hypothesis/engine.rb +44 -29
- data/lib/hypothesis/errors.rb +33 -0
- data/lib/hypothesis/junkdrawer.rb +31 -0
- data/src/lib.rs +18 -11
- metadata +3 -2
checksums.yaml
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
---
|
2
2
|
SHA256:
|
3
|
-
metadata.gz:
|
4
|
-
data.tar.gz:
|
3
|
+
metadata.gz: 0c8b0daf467cfba3ff5d8713fca58165154c19dbe24a9c063337da10244a4858
|
4
|
+
data.tar.gz: 5220bf53ee4d96028f54ffc0cb3513f263e06b5edb53a549a00bc3e4720ddbaf
|
5
5
|
SHA512:
|
6
|
-
metadata.gz:
|
7
|
-
data.tar.gz:
|
6
|
+
metadata.gz: a33f06784dec07894b9d0bb3ff0bd8366c78d0a332e25c9fac5e5589b6026f7cacc22a12575c1010416116f3527e07b9603a2da96e763148b9bdaa3ca1d085b9
|
7
|
+
data.tar.gz: a92b046f6384b42a6e28d836a29b6f654944fc9a8cccd6ff27ff6b8682a9d9588a7c326177aca48e6376866754fcda85c15b0734e728cc905d69fae146ac7683
|
data/CHANGELOG.md
CHANGED
@@ -1,3 +1,8 @@
|
|
1
|
+
# Hypothesis for Ruby 0.1.0 (2018-07-16)
|
2
|
+
|
3
|
+
This release adds support for reporting multiple exceptions when Hypothesis
|
4
|
+
finds more than one way for the test to fail.
|
5
|
+
|
1
6
|
# Hypothesis for Ruby 0.0.15 (2018-06-25)
|
2
7
|
|
3
8
|
This release fixes an occasional `RuntimeError` that could occur
|
data/Cargo.toml
CHANGED
data/lib/hypothesis.rb
CHANGED
data/lib/hypothesis/engine.rb
CHANGED
@@ -2,9 +2,12 @@
|
|
2
2
|
|
3
3
|
require 'helix_runtime'
|
4
4
|
require 'hypothesis-ruby/native'
|
5
|
+
require 'rspec/expectations'
|
5
6
|
|
6
7
|
module Hypothesis
|
7
8
|
class Engine
|
9
|
+
include RSpec::Matchers
|
10
|
+
|
8
11
|
attr_reader :current_source
|
9
12
|
attr_accessor :is_find
|
10
13
|
|
@@ -13,6 +16,8 @@ module Hypothesis
|
|
13
16
|
@core_engine = HypothesisCoreEngine.new(
|
14
17
|
seed, options.fetch(:max_examples)
|
15
18
|
)
|
19
|
+
|
20
|
+
@exceptions_to_tags = Hash.new { |h, k| h[k] = h.size }
|
16
21
|
end
|
17
22
|
|
18
23
|
def run
|
@@ -23,7 +28,7 @@ module Hypothesis
|
|
23
28
|
begin
|
24
29
|
result = yield(@current_source)
|
25
30
|
if is_find && result
|
26
|
-
@core_engine.finish_interesting(core)
|
31
|
+
@core_engine.finish_interesting(core, 0)
|
27
32
|
else
|
28
33
|
@core_engine.finish_valid(core)
|
29
34
|
end
|
@@ -31,54 +36,64 @@ module Hypothesis
|
|
31
36
|
@core_engine.finish_invalid(core)
|
32
37
|
rescue DataOverflow
|
33
38
|
@core_engine.finish_overflow(core)
|
34
|
-
rescue Exception
|
39
|
+
rescue Exception => e
|
35
40
|
raise if is_find
|
36
|
-
|
41
|
+
key = [
|
42
|
+
e.class,
|
43
|
+
HypothesisJunkDrawer.find_first_relevant_line(e.backtrace)
|
44
|
+
]
|
45
|
+
@core_engine.finish_interesting(core, @exceptions_to_tags[key])
|
37
46
|
end
|
38
47
|
end
|
39
|
-
@
|
40
|
-
core = @core_engine.failing_example
|
41
|
-
if core.nil?
|
48
|
+
if @core_engine.count_failing_examples.zero?
|
42
49
|
raise Unsatisfiable if @core_engine.was_unsatisfiable
|
50
|
+
@current_source = nil
|
43
51
|
return
|
44
52
|
end
|
45
53
|
|
46
54
|
if is_find
|
55
|
+
core = @core_engine.failing_example(0)
|
47
56
|
@current_source = TestCase.new(core, record_draws: true)
|
48
57
|
yield @current_source
|
49
58
|
else
|
50
|
-
|
59
|
+
exceptions = []
|
60
|
+
(0...@core_engine.count_failing_examples).each do |example|
|
61
|
+
core = @core_engine.failing_example(example)
|
62
|
+
@current_source = TestCase.new(core, print_draws: true)
|
51
63
|
|
52
|
-
|
53
|
-
|
54
|
-
|
55
|
-
|
56
|
-
|
57
|
-
|
58
|
-
|
59
|
-
|
64
|
+
begin
|
65
|
+
yield @current_source
|
66
|
+
rescue Exception => e
|
67
|
+
givens = @current_source.print_log
|
68
|
+
given_str = givens.each_with_index.map do |(name, s), i|
|
69
|
+
name = "##{i + 1}" if name.nil?
|
70
|
+
"Given #{name}: #{s}"
|
71
|
+
end.to_a
|
60
72
|
|
61
|
-
|
62
|
-
|
63
|
-
|
64
|
-
|
65
|
-
|
73
|
+
if e.respond_to? :hypothesis_data
|
74
|
+
e.hypothesis_data[0] = given_str
|
75
|
+
else
|
76
|
+
original_to_s = e.to_s
|
77
|
+
original_inspect = e.inspect
|
66
78
|
|
67
|
-
|
68
|
-
|
79
|
+
class <<e
|
80
|
+
attr_accessor :hypothesis_data
|
69
81
|
|
70
|
-
|
71
|
-
|
72
|
-
|
82
|
+
def to_s
|
83
|
+
['', hypothesis_data[0], '', hypothesis_data[1]].join("\n")
|
84
|
+
end
|
73
85
|
|
74
|
-
|
75
|
-
|
86
|
+
def inspect
|
87
|
+
['', hypothesis_data[0], '', hypothesis_data[2]].join("\n")
|
88
|
+
end
|
76
89
|
end
|
90
|
+
e.hypothesis_data = [given_str, original_to_s, original_inspect]
|
77
91
|
end
|
78
|
-
e
|
92
|
+
raise e if @core_engine.count_failing_examples == 1
|
93
|
+
exceptions.push(e)
|
79
94
|
end
|
80
|
-
raise e
|
81
95
|
end
|
96
|
+
raise Hypothesis::MultipleExceptionError.new(*exceptions)
|
82
97
|
end
|
83
98
|
end
|
84
99
|
end
|
data/lib/hypothesis/errors.rb
CHANGED
@@ -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
|
data/src/lib.rs
CHANGED
@@ -15,7 +15,7 @@ extern crate conjecture;
|
|
15
15
|
|
16
16
|
use std::mem;
|
17
17
|
|
18
|
-
use conjecture::data::{DataSource, Status};
|
18
|
+
use conjecture::data::{DataSource, Status, TestResult};
|
19
19
|
use conjecture::distributions::Repeat;
|
20
20
|
use conjecture::distributions;
|
21
21
|
use conjecture::engine::Engine;
|
@@ -49,6 +49,7 @@ ruby! {
|
|
49
49
|
struct {
|
50
50
|
engine: Engine,
|
51
51
|
pending: Option<DataSource>,
|
52
|
+
interesting_examples: Vec<TestResult>,
|
52
53
|
}
|
53
54
|
|
54
55
|
def initialize(helix, seed: u64, max_examples: u64){
|
@@ -57,12 +58,16 @@ ruby! {
|
|
57
58
|
helix,
|
58
59
|
engine: Engine::new(max_examples, &xs),
|
59
60
|
pending: None,
|
61
|
+
interesting_examples: Vec::new(),
|
60
62
|
}
|
61
63
|
}
|
62
64
|
|
63
65
|
def new_source(&mut self) -> Option<HypothesisCoreDataSource> {
|
64
66
|
match self.engine.next_source() {
|
65
|
-
None =>
|
67
|
+
None => {
|
68
|
+
self.interesting_examples = self.engine.list_minimized_examples();
|
69
|
+
None
|
70
|
+
},
|
66
71
|
Some(source) => {
|
67
72
|
self.pending = Some(source);
|
68
73
|
Some(HypothesisCoreDataSource::new(self))
|
@@ -70,13 +75,15 @@ ruby! {
|
|
70
75
|
}
|
71
76
|
}
|
72
77
|
|
73
|
-
def
|
74
|
-
|
75
|
-
|
76
|
-
|
77
|
-
|
78
|
-
|
79
|
-
|
78
|
+
def count_failing_examples(&self) -> usize {
|
79
|
+
self.interesting_examples.len()
|
80
|
+
}
|
81
|
+
|
82
|
+
def failing_example(&mut self, i: usize) -> HypothesisCoreDataSource {
|
83
|
+
self.pending = Some(
|
84
|
+
DataSource::from_vec(self.interesting_examples[i].record.clone())
|
85
|
+
);
|
86
|
+
HypothesisCoreDataSource::new(self)
|
80
87
|
}
|
81
88
|
|
82
89
|
def was_unsatisfiable(&mut self) -> bool {
|
@@ -91,8 +98,8 @@ ruby! {
|
|
91
98
|
mark_child_status(&mut self.engine, child, Status::Invalid);
|
92
99
|
}
|
93
100
|
|
94
|
-
def finish_interesting(&mut self, child: &mut HypothesisCoreDataSource){
|
95
|
-
mark_child_status(&mut self.engine, child, Status::Interesting);
|
101
|
+
def finish_interesting(&mut self, child: &mut HypothesisCoreDataSource, label: u64){
|
102
|
+
mark_child_status(&mut self.engine, child, Status::Interesting(label));
|
96
103
|
}
|
97
104
|
|
98
105
|
def finish_valid(&mut self, child: &mut HypothesisCoreDataSource){
|
metadata
CHANGED
@@ -1,14 +1,14 @@
|
|
1
1
|
--- !ruby/object:Gem::Specification
|
2
2
|
name: hypothesis-specs
|
3
3
|
version: !ruby/object:Gem::Version
|
4
|
-
version: 0.0
|
4
|
+
version: 0.1.0
|
5
5
|
platform: ruby
|
6
6
|
authors:
|
7
7
|
- David R. Maciver
|
8
8
|
autorequire:
|
9
9
|
bindir: bin
|
10
10
|
cert_chain: []
|
11
|
-
date: 2018-
|
11
|
+
date: 2018-07-16 00:00:00.000000000 Z
|
12
12
|
dependencies:
|
13
13
|
- !ruby/object:Gem::Dependency
|
14
14
|
name: helix_runtime
|
@@ -64,6 +64,7 @@ files:
|
|
64
64
|
- lib/hypothesis.rb
|
65
65
|
- lib/hypothesis/engine.rb
|
66
66
|
- lib/hypothesis/errors.rb
|
67
|
+
- lib/hypothesis/junkdrawer.rb
|
67
68
|
- lib/hypothesis/possible.rb
|
68
69
|
- lib/hypothesis/testcase.rb
|
69
70
|
- lib/hypothesis/world.rb
|