rspec-search-and-destroy 0.0.3 → 0.0.4
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.
- data/README.md +1 -1
- data/bin/rspec-sad +3 -12
- data/features/support/steps.rb +1 -1
- data/lib/{rspec-sad.rb → rspec-search-and-destroy.rb} +0 -0
- data/lib/rspec-search-and-destroy/bisection_progress.rb +41 -0
- data/lib/rspec-search-and-destroy/bisector.rb +15 -8
- data/lib/rspec-search-and-destroy/io_output.rb +47 -0
- data/lib/rspec-search-and-destroy/version.rb +1 -1
- data/spec/bisector_spec.rb +36 -6
- data/spec/io_output_spec.rb +55 -0
- data/spec/rspec_example_ordering_spec.rb +1 -1
- metadata +7 -3
data/README.md
CHANGED
data/bin/rspec-sad
CHANGED
@@ -1,21 +1,13 @@
|
|
1
1
|
#!/usr/bin/env ruby
|
2
2
|
|
3
|
-
require 'rspec-
|
4
|
-
|
3
|
+
require 'rspec-search-and-destroy'
|
5
4
|
require 'rspec-search-and-destroy/binary_chop_example_selector'
|
6
5
|
require 'rspec-search-and-destroy/rspec_driver'
|
7
6
|
require 'rspec-search-and-destroy/bisector'
|
7
|
+
require 'rspec-search-and-destroy/io_output'
|
8
8
|
|
9
9
|
include RSpecSearchAndDestroy
|
10
10
|
|
11
|
-
class TTYOutput
|
12
|
-
def found(causal_example, failed_example)
|
13
|
-
puts "Culprit found"
|
14
|
-
puts "Run #{causal_example[:location]}"
|
15
|
-
puts "Before #{failed_example[:location]}"
|
16
|
-
end
|
17
|
-
end
|
18
|
-
|
19
11
|
require 'optparse'
|
20
12
|
|
21
13
|
driver_options = {}
|
@@ -35,8 +27,7 @@ BANNER
|
|
35
27
|
end
|
36
28
|
option_parser.parse!
|
37
29
|
|
38
|
-
|
39
|
-
output = TTYOutput.new
|
30
|
+
output = IOOutput.new
|
40
31
|
selector = BinaryChopExampleSelector.new
|
41
32
|
driver = RSpecDriver.new(driver_options)
|
42
33
|
|
data/features/support/steps.rb
CHANGED
File without changes
|
@@ -0,0 +1,41 @@
|
|
1
|
+
class BisectionProgress
|
2
|
+
attr_reader :iteration, :enabled_examples, :total_examples, :start_time
|
3
|
+
|
4
|
+
def initialize(attributes = {})
|
5
|
+
@iteration = attributes.fetch(:iteration) { 1 }
|
6
|
+
@enabled_examples = attributes.fetch(:enabled_examples)
|
7
|
+
@total_examples = attributes.fetch(:total_examples)
|
8
|
+
@time_provider = attributes.fetch(:time_provider) { Time }
|
9
|
+
@start_time = attributes.fetch(:start_time) { @time_provider.now }
|
10
|
+
end
|
11
|
+
|
12
|
+
def next_iteration(enabled_examples)
|
13
|
+
self.class.new(iteration: iteration + 1,
|
14
|
+
enabled_examples: enabled_examples,
|
15
|
+
total_examples: total_examples,
|
16
|
+
time_provider: @time_provider,
|
17
|
+
start_time: start_time)
|
18
|
+
end
|
19
|
+
|
20
|
+
def run_time
|
21
|
+
@time_provider.now - @start_time
|
22
|
+
end
|
23
|
+
|
24
|
+
def ==(other)
|
25
|
+
values.map do |v|
|
26
|
+
self.send(v) == other.send(v)
|
27
|
+
end.all?
|
28
|
+
end
|
29
|
+
|
30
|
+
def hash
|
31
|
+
values.map do |v|
|
32
|
+
self.send(v).hash
|
33
|
+
end.reduce(:^)
|
34
|
+
end
|
35
|
+
|
36
|
+
private
|
37
|
+
|
38
|
+
def values
|
39
|
+
[:iteration, :enabled_examples, :total_examples]
|
40
|
+
end
|
41
|
+
end
|
@@ -1,3 +1,5 @@
|
|
1
|
+
require_relative 'bisection_progress'
|
2
|
+
|
1
3
|
module RSpecSearchAndDestroy
|
2
4
|
class Bisector
|
3
5
|
attr_reader :output, :selector, :executor
|
@@ -8,7 +10,7 @@ module RSpecSearchAndDestroy
|
|
8
10
|
@executor = executor
|
9
11
|
end
|
10
12
|
|
11
|
-
def bisect(causal_examples, failed_example)
|
13
|
+
def bisect(causal_examples, failed_example, progress = nil)
|
12
14
|
case causal_examples.size
|
13
15
|
when 1
|
14
16
|
output.found(causal_examples.first, failed_example)
|
@@ -19,17 +21,22 @@ module RSpecSearchAndDestroy
|
|
19
21
|
end
|
20
22
|
|
21
23
|
enabled, disabled = selector.enable_set(causal_examples)
|
22
|
-
|
23
24
|
to_run = enabled + [failed_example]
|
24
|
-
executor.run_examples(to_run)
|
25
25
|
|
26
|
+
progress =
|
27
|
+
if progress
|
28
|
+
progress.next_iteration(enabled.size)
|
29
|
+
else
|
30
|
+
BisectionProgress.new(total_examples: causal_examples.size,
|
31
|
+
enabled_examples: enabled.size)
|
32
|
+
end
|
33
|
+
output.progress(progress)
|
34
|
+
|
35
|
+
executor.run_examples(to_run)
|
26
36
|
results = executor.load_run_results
|
27
37
|
|
28
|
-
|
29
|
-
|
30
|
-
else
|
31
|
-
bisect(disabled, failed_example)
|
32
|
-
end
|
38
|
+
next_set = results.failed? ? enabled : disabled
|
39
|
+
bisect(next_set, failed_example, progress)
|
33
40
|
end
|
34
41
|
end
|
35
42
|
end
|
@@ -0,0 +1,47 @@
|
|
1
|
+
class IOOutput
|
2
|
+
attr_reader :io
|
3
|
+
|
4
|
+
SEPARATOR = "-" * 20
|
5
|
+
|
6
|
+
def initialize(io = STDOUT)
|
7
|
+
@io = io
|
8
|
+
end
|
9
|
+
|
10
|
+
def found(causal_example, failed_example)
|
11
|
+
io.puts <<FOUND
|
12
|
+
#{SEPARATOR}
|
13
|
+
Culprit found
|
14
|
+
Run #{causal_example[:location]}
|
15
|
+
Before #{failed_example[:location]}
|
16
|
+
FOUND
|
17
|
+
end
|
18
|
+
|
19
|
+
def progress(state)
|
20
|
+
run_time = Duration.new(state.run_time)
|
21
|
+
|
22
|
+
io.puts <<PROGRESS
|
23
|
+
#{SEPARATOR}
|
24
|
+
Iteration #{state.iteration}
|
25
|
+
#{state.enabled_examples} / #{state.total_examples} examples enabled
|
26
|
+
Running for #{run_time}
|
27
|
+
PROGRESS
|
28
|
+
end
|
29
|
+
|
30
|
+
private
|
31
|
+
|
32
|
+
class Duration
|
33
|
+
attr_reader :hours, :minutes, :seconds
|
34
|
+
|
35
|
+
def initialize(duration_as_seconds)
|
36
|
+
total_seconds = duration_as_seconds.to_f.ceil.to_i
|
37
|
+
|
38
|
+
@seconds = total_seconds % 60
|
39
|
+
@minutes = (total_seconds / 60) % 60
|
40
|
+
@hours = total_seconds / (60 * 60)
|
41
|
+
end
|
42
|
+
|
43
|
+
def to_s
|
44
|
+
"%02d:%02d:%02d" % [hours, minutes, seconds]
|
45
|
+
end
|
46
|
+
end
|
47
|
+
end
|
data/spec/bisector_spec.rb
CHANGED
@@ -1,14 +1,15 @@
|
|
1
1
|
require 'spec_helper'
|
2
2
|
require 'rspec-search-and-destroy/bisector.rb'
|
3
|
+
require 'rspec-search-and-destroy/binary_chop_example_selector.rb'
|
3
4
|
|
4
5
|
describe RSpecSearchAndDestroy::Bisector do
|
5
6
|
subject(:bisector) do
|
6
7
|
RSpecSearchAndDestroy::Bisector.new(output, selector, executor)
|
7
8
|
end
|
8
9
|
|
9
|
-
let(:output) { double("output") }
|
10
|
+
let(:output) { double("output").as_null_object }
|
10
11
|
let(:selector) { double("selector") }
|
11
|
-
let(:executor) { double("executor") }
|
12
|
+
let(:executor) { double("executor").as_null_object }
|
12
13
|
|
13
14
|
let(:failing_example) { double("failing example") }
|
14
15
|
|
@@ -34,7 +35,7 @@ describe RSpecSearchAndDestroy::Bisector do
|
|
34
35
|
end
|
35
36
|
|
36
37
|
context "when executing examples" do
|
37
|
-
let(:potential_causes) { 2
|
38
|
+
let(:potential_causes) { build_examples(2) }
|
38
39
|
let(:enabled_examples) { [potential_causes.first] }
|
39
40
|
let(:disabled_examples) { [potential_causes.last] }
|
40
41
|
|
@@ -46,7 +47,6 @@ describe RSpecSearchAndDestroy::Bisector do
|
|
46
47
|
# they are just needed to prevent failure
|
47
48
|
results = double("results", :failed? => true)
|
48
49
|
executor.stub(:load_run_results).and_return(results)
|
49
|
-
output.stub(:found)
|
50
50
|
end
|
51
51
|
|
52
52
|
it "executes enabled examples" do
|
@@ -75,7 +75,6 @@ describe RSpecSearchAndDestroy::Bisector do
|
|
75
75
|
|
76
76
|
context "when the executed tests fail" do
|
77
77
|
before do
|
78
|
-
executor.stub(:run_examples)
|
79
78
|
results = double("results", :failed? => true)
|
80
79
|
executor.stub(:load_run_results).and_return(results)
|
81
80
|
end
|
@@ -90,7 +89,6 @@ describe RSpecSearchAndDestroy::Bisector do
|
|
90
89
|
|
91
90
|
context "when the executed tests do not fail" do
|
92
91
|
before do
|
93
|
-
executor.stub(:run_examples)
|
94
92
|
results = double("results", :failed? => false)
|
95
93
|
executor.stub(:load_run_results).and_return(results)
|
96
94
|
end
|
@@ -103,4 +101,36 @@ describe RSpecSearchAndDestroy::Bisector do
|
|
103
101
|
end
|
104
102
|
end
|
105
103
|
end
|
104
|
+
|
105
|
+
context "when reporting progress" do
|
106
|
+
let(:potential_causes) { build_examples(3) }
|
107
|
+
let(:selector) { RSpecSearchAndDestroy::BinaryChopExampleSelector.new }
|
108
|
+
|
109
|
+
before do
|
110
|
+
results = double("results", :failed? => false)
|
111
|
+
executor.stub(:load_run_results).and_return(results)
|
112
|
+
end
|
113
|
+
|
114
|
+
it "reports once for each iteration" do
|
115
|
+
progress_1 = BisectionProgress.new(iteration: 1,
|
116
|
+
total_examples: 3,
|
117
|
+
enabled_examples: 1)
|
118
|
+
|
119
|
+
progress_2 = BisectionProgress.new(iteration: 2,
|
120
|
+
total_examples: 3,
|
121
|
+
enabled_examples: 1)
|
122
|
+
|
123
|
+
expect(output).to receive(:progress)
|
124
|
+
.with(progress_1).ordered
|
125
|
+
|
126
|
+
expect(output).to receive(:progress)
|
127
|
+
.with(progress_2).ordered
|
128
|
+
|
129
|
+
bisector.bisect(potential_causes, failing_example)
|
130
|
+
end
|
131
|
+
end
|
132
|
+
|
133
|
+
def build_examples(count)
|
134
|
+
count.times.map {|i| double("potential example #{i}")}
|
135
|
+
end
|
106
136
|
end
|
@@ -0,0 +1,55 @@
|
|
1
|
+
require 'spec_helper'
|
2
|
+
require 'rspec-search-and-destroy/io_output.rb'
|
3
|
+
|
4
|
+
module RSpecSearchAndDestroy
|
5
|
+
describe IOOutput do
|
6
|
+
let(:output) { IOOutput.new(io) }
|
7
|
+
let(:io) { StringIO.new }
|
8
|
+
subject(:string) { io.string }
|
9
|
+
|
10
|
+
context "when reporting the culprit" do
|
11
|
+
let(:causal_example) { {location: "cause location"} }
|
12
|
+
let(:failed_example) { {location: "fail location"} }
|
13
|
+
|
14
|
+
before do
|
15
|
+
output.found(causal_example, failed_example)
|
16
|
+
end
|
17
|
+
|
18
|
+
it "includes the example that causes the problem" do
|
19
|
+
expect(string).to match /Run\s+cause location/
|
20
|
+
end
|
21
|
+
|
22
|
+
it "includes the example that fails" do
|
23
|
+
expect(string).to match /Before\s+fail location/
|
24
|
+
end
|
25
|
+
end
|
26
|
+
|
27
|
+
context "when reporting progress" do
|
28
|
+
let(:progress) do
|
29
|
+
BisectionProgress.new(iteration: 5,
|
30
|
+
enabled_examples: 54,
|
31
|
+
total_examples: 999)
|
32
|
+
end
|
33
|
+
|
34
|
+
before do
|
35
|
+
output.progress(progress)
|
36
|
+
end
|
37
|
+
|
38
|
+
it "includes the current iteration" do
|
39
|
+
expect(string).to match /Iteration 5/
|
40
|
+
end
|
41
|
+
|
42
|
+
it "includes the number of enabled examples" do
|
43
|
+
expect(string).to match /54.*examples/
|
44
|
+
end
|
45
|
+
|
46
|
+
it "includes the total number of examples" do
|
47
|
+
expect(string).to match /999.*examples/
|
48
|
+
end
|
49
|
+
|
50
|
+
it "includes the running time" do
|
51
|
+
expect(string).to match /Running for \d{2}:\d{2}:\d{2}/
|
52
|
+
end
|
53
|
+
end
|
54
|
+
end
|
55
|
+
end
|
metadata
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
--- !ruby/object:Gem::Specification
|
2
2
|
name: rspec-search-and-destroy
|
3
3
|
version: !ruby/object:Gem::Version
|
4
|
-
version: 0.0.
|
4
|
+
version: 0.0.4
|
5
5
|
prerelease:
|
6
6
|
platform: ruby
|
7
7
|
authors:
|
@@ -9,7 +9,7 @@ authors:
|
|
9
9
|
autorequire:
|
10
10
|
bindir: bin
|
11
11
|
cert_chain: []
|
12
|
-
date: 2013-09-
|
12
|
+
date: 2013-09-20 00:00:00.000000000 Z
|
13
13
|
dependencies:
|
14
14
|
- !ruby/object:Gem::Dependency
|
15
15
|
name: childprocess
|
@@ -98,9 +98,11 @@ files:
|
|
98
98
|
- features/running_rspec_without_sad.feature
|
99
99
|
- features/support/env.rb
|
100
100
|
- features/support/steps.rb
|
101
|
-
- lib/rspec-
|
101
|
+
- lib/rspec-search-and-destroy.rb
|
102
102
|
- lib/rspec-search-and-destroy/binary_chop_example_selector.rb
|
103
|
+
- lib/rspec-search-and-destroy/bisection_progress.rb
|
103
104
|
- lib/rspec-search-and-destroy/bisector.rb
|
105
|
+
- lib/rspec-search-and-destroy/io_output.rb
|
104
106
|
- lib/rspec-search-and-destroy/location_source.rb
|
105
107
|
- lib/rspec-search-and-destroy/order_formatter.rb
|
106
108
|
- lib/rspec-search-and-destroy/reorder_and_filter.rb
|
@@ -110,6 +112,7 @@ files:
|
|
110
112
|
- rspec-search-and-destroy.gemspec
|
111
113
|
- spec/binary_chop_example_selector_spec.rb
|
112
114
|
- spec/bisector_spec.rb
|
115
|
+
- spec/io_output_spec.rb
|
113
116
|
- spec/rspec_example_ordering_spec.rb
|
114
117
|
- spec/spec_helper.rb
|
115
118
|
homepage: https://github.com/shepmaster/rspec-search-and-destroy
|
@@ -147,5 +150,6 @@ test_files:
|
|
147
150
|
- features/support/steps.rb
|
148
151
|
- spec/binary_chop_example_selector_spec.rb
|
149
152
|
- spec/bisector_spec.rb
|
153
|
+
- spec/io_output_spec.rb
|
150
154
|
- spec/rspec_example_ordering_spec.rb
|
151
155
|
- spec/spec_helper.rb
|