rspec-search-and-destroy 0.0.3 → 0.0.4
Sign up to get free protection for your applications and to get access to all the features.
- 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
|