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 CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA256:
3
- metadata.gz: cf543607fd313031a5fd77ee32d3e8f7693f374a48e5bcd2c36695ca31f81812
4
- data.tar.gz: 572e4cb24ad38caff4a730eff87a8bbbb9aae75e759fade1061cb919214de76d
3
+ metadata.gz: 0c8b0daf467cfba3ff5d8713fca58165154c19dbe24a9c063337da10244a4858
4
+ data.tar.gz: 5220bf53ee4d96028f54ffc0cb3513f263e06b5edb53a549a00bc3e4720ddbaf
5
5
  SHA512:
6
- metadata.gz: da7954815892f7f17c8bbc41435e15403a7147457ae22ed6d2955d5f0c6a490a9679773862d3fea3e598dac04e5698516bea64e04c236378de4808d6f178fd78
7
- data.tar.gz: 2cbe4b43830f9a0ca70aaba5923d2e4c46af72d38a96c56ff67ff4be5ec6d4ae7f206d47e0860480161c78b8d4b9e19baa4862ac68da07517ecc2caa316f777a
6
+ metadata.gz: a33f06784dec07894b9d0bb3ff0bd8366c78d0a332e25c9fac5e5589b6026f7cacc22a12575c1010416116f3527e07b9603a2da96e763148b9bdaa3ca1d085b9
7
+ data.tar.gz: a92b046f6384b42a6e28d836a29b6f654944fc9a8cccd6ff27ff6b8682a9d9588a7c326177aca48e6376866754fcda85c15b0734e728cc905d69fae146ac7683
@@ -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
@@ -9,4 +9,4 @@ crate-type = ["cdylib"]
9
9
  [dependencies]
10
10
  helix = '0.7.5'
11
11
  rand = '0.3'
12
- conjecture = '0.2.1'
12
+ conjecture = '0.3.0'
@@ -1,5 +1,6 @@
1
1
  # frozen_string_literal: true
2
2
 
3
+ require 'hypothesis/junkdrawer'
3
4
  require 'hypothesis/errors'
4
5
  require 'hypothesis/possible'
5
6
  require 'hypothesis/testcase'
@@ -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
- @core_engine.finish_interesting(core)
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
- @current_source = nil
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
- @current_source = TestCase.new(core, print_draws: true)
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
- 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
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
- 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
+ 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
- class <<e
68
- attr_accessor :hypothesis_data
79
+ class <<e
80
+ attr_accessor :hypothesis_data
69
81
 
70
- def to_s
71
- ['', hypothesis_data[0], '', hypothesis_data[1]].join("\n")
72
- end
82
+ def to_s
83
+ ['', hypothesis_data[0], '', hypothesis_data[1]].join("\n")
84
+ end
73
85
 
74
- def inspect
75
- ['', hypothesis_data[0], '', hypothesis_data[2]].join("\n")
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.hypothesis_data = [given_str, original_to_s, original_inspect]
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
@@ -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 => 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 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
- }
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.15
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-06-25 00:00:00.000000000 Z
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