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