minitest-bisect 1.3.1 → 1.4.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
  SHA1:
3
- metadata.gz: e56b026d55d6353d8a291fbeb69e5988ad1a9a39
4
- data.tar.gz: 4cac6746cd45f0a1ada3b2de84d20130b86c129f
3
+ metadata.gz: 8e164f0aced9bbb7f0fb1f240ab1a32aa57bec59
4
+ data.tar.gz: ad40830eea222d045b4a19bce62905d7be7a1f9b
5
5
  SHA512:
6
- metadata.gz: 45267d07d6c66419face27df11adb64905008e99b1b52fc18044546da377988e683dbf0f643239c2f80c143bf2d6672de4211806051f09efb17ff1cf2e17d7e5
7
- data.tar.gz: 4193394a7503e8bfe62aa217874d9f838e2e5de1ee3da94a2ed0b5395631a4c2a0b1e24c90903c527e025e4c9ba4e42d629d0a5f10f359f5ddf54bf71ed7365c
6
+ metadata.gz: c427f5b79be6ee92d49ba4dbe7711362a9bc5f530a5845bd20ecb9260918f4e6bb0a540840b2f5d2d97d27f345a05068d9944543acf39d7a8418dd1211c67f0c
7
+ data.tar.gz: 82c9c80008423278b9318b9199494abbcdd425eb7b18f2ab8eb3b76d10de6d85589b53a85883a09e73542e1469dcacee8758f5580e7550e5e4320d4ef8631dac
Binary file
data.tar.gz.sig CHANGED
Binary file
@@ -1,3 +1,11 @@
1
+ === 1.4.0 / 2016-06-15
2
+
3
+ * 3 minor enhancements:
4
+
5
+ * Added better timing output, so you can estimate much of a coffee break you get.
6
+ * Added path_expander support.
7
+ * Added verification step to ensure you're not trying to bisect a non-test-order-dependency bug.
8
+
1
9
  === 1.3.1 / 2016-02-18
2
10
 
3
11
  * 2 bug fixes:
@@ -140,6 +140,43 @@ quicker with time.
140
140
 
141
141
  * sudo gem install minitest-bisect
142
142
 
143
+ == FAQ:
144
+
145
+ ==== Q: What if my tests are in multiple subdirectories?
146
+
147
+ This is assuming the following doesn't work for you:
148
+
149
+ minitest_bisect --seed 3911 test/*_test.rb
150
+
151
+ because you need it to span multiple sub-directories.
152
+
153
+ A: Use your shell to find the files for you
154
+
155
+ minitest_bisect --seed 3911 $(find test -type f -name \*_test.rb)
156
+
157
+ or
158
+
159
+ minitest_bisect --seed 3911 test/**/*_test.rb
160
+
161
+ ==== Q: How do I resolve an "invalid option: \--server" error?
162
+ $ MTB_VERBOSE=2 minitest_bisect --seed 3911 example/test*.rb
163
+ reproducing...
164
+ ruby -Ilib -e 'require "./example/test_bad1.rb" ; require "./example/test_bad4.rb"' -- --seed 3911 --server 33352
165
+
166
+ invalid option: --server
167
+
168
+ minitest options:
169
+ -h, --help Display this help.
170
+ -s, --seed SEED Sets random seed. Also via env. Eg: SEED=n rake
171
+ -v, --verbose Verbose. Show progress processing files.
172
+ -n, --name PATTERN Filter run on /regexp/ or string.
173
+
174
+ Known extensions: guard_minitest, pride
175
+ -p, --pride Pride. Show your testing pride!
176
+ Reproduction run passed? Aborting. Try running with MTB_VERBOSE=2 to verify.
177
+
178
+ A: Try adding "<tt>gem minitest-bisect</tt>" to your Gemfile and running "<tt>bundle install</tt>"
179
+
143
180
  == LICENSE:
144
181
 
145
182
  (The MIT License)
data/Rakefile CHANGED
@@ -15,6 +15,7 @@ Hoe.spec "minitest-bisect" do
15
15
 
16
16
  dependency "rake", "> 0", :development
17
17
  dependency "minitest-server", "~> 1.0"
18
+ dependency "path_expander", "~> 1.0"
18
19
  end
19
20
 
20
21
  require "rake/testtask"
@@ -45,7 +46,7 @@ def req glob
45
46
  Dir["#{glob}.rb"].map { |s| "require #{s.inspect}" }.join ";"
46
47
  end
47
48
 
48
- task :repro do
49
+ task :repro => :isolate do
49
50
  unless ENV.key? "SLEEP" then
50
51
  warn "NOTE: Defaulting to sleeping 0.01 seconds per test."
51
52
  warn "NOTE: Use SLEEP=0 to disable or any other value to simulate your tests."
@@ -60,7 +61,7 @@ task :repro do
60
61
  run "#{ruby} bin/minitest_bisect -Ilib --seed 3911 example/test*.rb"
61
62
  end
62
63
 
63
- task :many do
64
+ task :many => :isolate do
64
65
  unless ENV.key? "SLEEP" then
65
66
  warn "NOTE: Defaulting to sleeping 0.01 seconds per test."
66
67
  warn "NOTE: Use SLEEP=0 to disable or any other value to simulate your tests."
@@ -2,9 +2,41 @@ require "minitest/find_minimal_combination"
2
2
  require "minitest/server"
3
3
  require "shellwords"
4
4
  require "rbconfig"
5
+ require "path_expander"
5
6
 
6
7
  class Minitest::Bisect
7
- VERSION = "1.3.1"
8
+ VERSION = "1.4.0"
9
+
10
+ class PathExpander < ::PathExpander
11
+ TEST_GLOB = "**/{test_*,*_test,spec_*,*_spec}.rb" # :nodoc:
12
+
13
+ attr_accessor :rb_flags
14
+
15
+ def initialize args = ARGV # :nodoc:
16
+ super args, TEST_GLOB
17
+ self.rb_flags = []
18
+ end
19
+
20
+ ##
21
+ # Overrides PathExpander#process_flags to filter out ruby flags
22
+ # from minitest flags. Only supports -I<paths>, -d, and -w for
23
+ # ruby.
24
+
25
+ def process_flags flags
26
+ flags.reject { |flag| # all hits are truthy, so this works out well
27
+ case flag
28
+ when /^-I(.*)/ then
29
+ rb_flags << flag
30
+ when /^-d/ then
31
+ rb_flags << flag
32
+ when /^-w/ then
33
+ rb_flags << flag
34
+ else
35
+ false
36
+ end
37
+ }
38
+ end
39
+ end
8
40
 
9
41
  mtbv = ENV["MTB_VERBOSE"].to_i
10
42
  SHH = case
@@ -41,19 +73,22 @@ class Minitest::Bisect
41
73
  # not clearing culprits on purpose
42
74
  end
43
75
 
44
- def run files
76
+ def run args
45
77
  Minitest::Server.run self
46
78
 
47
79
  cmd = nil
48
80
 
49
81
  if :until_I_have_negative_filtering_in_minitest != 0 then
50
- files, flags = files.partition { |arg| File.file? arg }
51
- rb_flags, mt_flags = flags.partition { |arg| arg =~ /^-I/ }
82
+ mt_flags = args.dup
83
+ expander = Minitest::Bisect::PathExpander.new mt_flags
84
+
85
+ files = expander.process
86
+ rb_flags = expander.rb_flags
52
87
  mt_flags += ["--server", $$]
53
88
 
54
89
  cmd = bisect_methods build_files_cmd(files, rb_flags, mt_flags)
55
90
  else
56
- cmd = bisect_methods bisect_files files
91
+ cmd = bisect_methods bisect_files args
57
92
  end
58
93
 
59
94
  puts "Final reproduction:"
@@ -95,25 +130,31 @@ class Minitest::Bisect
95
130
  def bisect_methods cmd
96
131
  self.mode = :methods
97
132
 
98
- puts "reproducing..."
99
- system "#{build_methods_cmd cmd} #{SHH}"
100
- abort "Reproduction run passed? Aborting. Try running with MTB_VERBOSE=2 to verify." unless tainted?
101
- puts "reproduced"
133
+ time_it "reproducing...", build_methods_cmd(cmd)
102
134
 
103
- # from: {"file.rb"=>{"Class"=>["test_method1", "test_method2"]}}
104
- # to: ["Class#test_method1", "Class#test_method2"]
105
- bad = failures.values.map { |h|
106
- h.map { |k,vs| vs.map { |v| "#{k}##{v}" } }
107
- }.flatten
135
+ unless tainted? then
136
+ $stderr.puts "Reproduction run passed? Aborting."
137
+ abort "Try running with MTB_VERBOSE=2 to verify."
138
+ end
139
+
140
+ bad = map_failures
108
141
 
109
142
  raise "Nothing to verify against because every test failed. Aborting." if
110
143
  culprits.empty? && seen_bad
111
144
 
145
+ time_it "verifying...", build_methods_cmd(cmd, [], bad)
146
+
147
+ new_bad = map_failures
148
+
149
+ if bad == new_bad then
150
+ warn "Tests fail by themselves. This may not be an ordering issue."
151
+ end
152
+
112
153
  # culprits populated by initial reproduction via minitest/server
113
154
  found, count = culprits.find_minimal_combination_and_count do |test|
114
- puts "# of culprit methods: #{test.size}"
155
+ prompt = "# of culprit methods: #{test.size}"
115
156
 
116
- system "#{build_methods_cmd cmd, test, bad} #{SHH}"
157
+ time_it prompt, build_methods_cmd(cmd, test, bad)
117
158
 
118
159
  self.tainted?
119
160
  end
@@ -127,6 +168,21 @@ class Minitest::Bisect
127
168
  cmd
128
169
  end
129
170
 
171
+ def time_it prompt, cmd
172
+ print prompt
173
+ t0 = Time.now
174
+ system "#{cmd} #{SHH}"
175
+ puts " in %.2f sec" % (Time.now - t0)
176
+ end
177
+
178
+ def map_failures
179
+ # from: {"file.rb"=>{"Class"=>["test_method1", "test_method2"]}}
180
+ # to: ["Class#test_method1", "Class#test_method2"]
181
+ failures.values.map { |h|
182
+ h.map { |k,vs| vs.map { |v| "#{k}##{v}" } }
183
+ }.flatten.sort
184
+ end
185
+
130
186
  def build_files_cmd culprits, rb, mt
131
187
  reset
132
188
 
@@ -135,26 +191,32 @@ class Minitest::Bisect
135
191
  %(#{RUBY} #{rb.shelljoin} -e '#{tests}' -- #{mt.map(&:to_s).shelljoin})
136
192
  end
137
193
 
138
- def build_methods_cmd cmd, culprits = [], bad = nil
139
- reset
194
+ def build_re bad
195
+ re = []
140
196
 
141
- if bad then
142
- re = []
197
+ # bad by class, you perv
198
+ bbc = bad.map { |s| s.split(/#/, 2) }.group_by(&:first)
143
199
 
144
- # bad by class, you perv
145
- bbc = (culprits + bad).map { |s| s.split(/#/, 2) }.group_by(&:first)
200
+ bbc.each do |klass, methods|
201
+ methods = methods.map(&:last).flatten.uniq.map { |method|
202
+ method.gsub(/([`'"!?&\[\]\(\)\|\+])/, '\\\\\1')
203
+ }
146
204
 
147
- bbc.each do |klass, methods|
148
- methods = methods.map(&:last).flatten.uniq.map { |method|
149
- method.gsub(/([`'"!?&\[\]\(\)\|\+])/, '\\\\\1')
150
- }
205
+ re << /#{klass}#(?:#{methods.join "|"})/.to_s[7..-2] # (?-mix:...)
206
+ end
151
207
 
152
- re << /#{klass}#(?:#{methods.join "|"})/.to_s[7..-2] # (?-mix:...)
153
- end
208
+ re = re.join("|").to_s.gsub(/-mix/, "")
209
+
210
+ "/^(?:#{re})$/"
211
+ end
154
212
 
155
- re = re.join("|").to_s.gsub(/-mix/, "")
213
+ def build_methods_cmd cmd, culprits = [], bad = nil
214
+ reset
215
+
216
+ if bad then
217
+ re = build_re culprits + bad
156
218
 
157
- cmd += " -n \"/^(?:#{re})$/\"" if bad
219
+ cmd += " -n \"#{re}\"" if bad
158
220
  end
159
221
 
160
222
  if ENV["MTB_VERBOSE"].to_i >= 1 then
@@ -4,7 +4,236 @@ require "minitest/bisect"
4
4
  module TestMinitest; end
5
5
 
6
6
  class TestMinitest::TestBisect < Minitest::Test
7
+ attr_accessor :bisect
8
+
9
+ def setup
10
+ self.bisect = Minitest::Bisect.new
11
+ end
12
+
13
+ def test_class_run
14
+ skip "Need to write test_class_run"
15
+ end
16
+
17
+ def test_bisect_files
18
+ skip "Need to write test_bisect_files"
19
+ end
20
+
21
+ def test_bisect_methods
22
+ skip "Need to write test_bisect_methods"
23
+ end
24
+
25
+ def test_build_files_cmd
26
+ files = %w[a.rb b.rb c.rb]
27
+ rb = %w[-Ilib:test]
28
+ mt = %w[--seed 42]
29
+
30
+ ruby = Minitest::Bisect::RUBY
31
+ body = "require \"./a.rb\" ; require \"./b.rb\" ; require \"./c.rb\""
32
+
33
+ exp = "#{ruby} -Ilib:test -e '#{body}' -- --seed 42"
34
+ act = bisect.build_files_cmd(files, rb, mt)
35
+
36
+ assert_equal exp, act
37
+ end
38
+
39
+ def test_build_methods_cmd
40
+ cmd = "cmd"
41
+ assert_equal "cmd", bisect.build_methods_cmd(cmd)
42
+ end
43
+
44
+ def test_build_methods_cmd_verify
45
+ cmd = "cmd"
46
+ cul = []
47
+ bad = %w[A#test_1 B#test_2]
48
+
49
+ exp = "cmd -n \"/^(?:A#(?:test_1)|B#(?:test_2))$/\""
50
+
51
+ assert_equal exp, bisect.build_methods_cmd(cmd, cul, bad)
52
+ end
53
+
54
+ def test_build_methods_cmd_verify_same
55
+ cmd = "cmd"
56
+ cul = []
57
+ bad = %w[C#test_5 C#test_6]
58
+
59
+ exp = "cmd -n \"/^(?:C#(?:test_5|test_6))$/\""
60
+
61
+ assert_equal exp, bisect.build_methods_cmd(cmd, cul, bad)
62
+ end
63
+
64
+ def test_build_methods_cmd_full
65
+ cmd = "cmd"
66
+ cul = %w[A#test_1 A#test_2 B#test_3 B#test_4]
67
+ bad = %w[C#test_5 C#test_6]
68
+
69
+ a = "A#(?:test_1|test_2)"
70
+ b = "B#(?:test_3|test_4)"
71
+ c = "C#(?:test_5|test_6)"
72
+ exp = "cmd -n \"/^(?:#{a}|#{b}|#{c})$/\""
73
+
74
+ assert_equal exp, bisect.build_methods_cmd(cmd, cul, bad)
75
+ end
76
+
77
+ def test_build_re
78
+ bad = %w[A#test_1 B#test_2]
79
+
80
+ exp = "/^(?:A#(?:test_1)|B#(?:test_2))$/"
81
+
82
+ assert_equal exp, bisect.build_re(bad)
83
+ end
84
+
85
+ def test_build_re_same
86
+ bad = %w[C#test_5 C#test_6]
87
+
88
+ exp = "/^(?:C#(?:test_5|test_6))$/"
89
+
90
+ assert_equal exp, bisect.build_re(bad)
91
+ end
92
+
93
+ def test_build_re_escaping
94
+ bad = ["Some Class#It shouldn't care what the name is"]
95
+
96
+ exp = "/^(?:Some Class#(?:It shouldn\\'t care what the name is))$/"
97
+
98
+ assert_equal exp, bisect.build_re(bad)
99
+ end
100
+
101
+ def test_map_failures
102
+ bisect.failures =
103
+ {
104
+ "file.rb" => { "Class" => %w[test_method1 test_method2] },
105
+ "blah.rb" => { "Apple" => %w[test_method3 test_method4] },
106
+ }
107
+
108
+ exp = %w[
109
+ Apple#test_method3
110
+ Apple#test_method4
111
+ Class#test_method1
112
+ Class#test_method2
113
+ ]
114
+
115
+ assert_equal exp, bisect.map_failures
116
+ end
117
+
118
+ def test_minitest_result
119
+ bisect.mode = :methods
120
+ bisect.reset
121
+
122
+ bisect.minitest_result "file.rb", "TestClass", "test_method", [], 1, 1
123
+
124
+ assert_equal false, bisect.tainted
125
+ assert_empty bisect.failures
126
+ assert_equal ["TestClass#test_method"], bisect.culprits
127
+ end
128
+
129
+ def test_minitest_result_skip
130
+ bisect.mode = :methods
131
+ bisect.reset
132
+
133
+ fail = Minitest::Skip.new("woot")
134
+
135
+ bisect.minitest_result "file.rb", "TestClass", "test_method", [fail], 1, 1
136
+
137
+ assert_equal false, bisect.tainted
138
+ assert_empty bisect.failures
139
+ assert_equal ["TestClass#test_method"], bisect.culprits
140
+ end
141
+
142
+ def test_minitest_result_fail
143
+ bisect.mode = :methods
144
+ bisect.reset
145
+
146
+ fail = Minitest::Assertion.new "msg"
147
+
148
+ bisect.minitest_result "file.rb", "TestClass", "test_method", [fail], 1, 1
149
+
150
+ exp = {"file.rb" => {"TestClass" => ["test_method"] }}
151
+
152
+ assert_equal true, bisect.tainted
153
+ assert_equal exp, bisect.failures
154
+ assert_empty bisect.culprits
155
+ end
156
+
157
+ def test_minitest_result_error
158
+ bisect.mode = :methods
159
+ bisect.reset
160
+
161
+ fail = Minitest::UnexpectedError.new RuntimeError.new("woot")
162
+
163
+ bisect.minitest_result "file.rb", "TestClass", "test_method", [fail], 1, 1
164
+
165
+ exp = {"file.rb" => {"TestClass" => ["test_method"] }}
166
+
167
+ assert_equal true, bisect.tainted
168
+ assert_equal exp, bisect.failures
169
+ assert_empty bisect.culprits
170
+ end
171
+
172
+ def test_minitest_start
173
+ bisect.failures["file.rb"]["Class"] << "test_bad1"
174
+
175
+ bisect.minitest_start
176
+
177
+ assert_empty bisect.failures
178
+ end
179
+
180
+ def test_reset
181
+ bisect.seen_bad = true
182
+ bisect.tainted = true
183
+ bisect.failures["file.rb"]["Class"] << "test_bad1"
184
+ bisect.culprits << "A#test_1" << "B#test_2"
185
+
186
+ bisect.reset
187
+
188
+ assert_equal false, bisect.seen_bad
189
+ assert_equal false, bisect.tainted
190
+ assert_empty bisect.failures
191
+ assert_equal %w[A#test_1 B#test_2], bisect.culprits
192
+ end
193
+
194
+ def test_run
195
+ skip "Need to write test_run"
196
+ end
197
+
198
+ def test_time_it
199
+ exp = /\Ado stuff: in 0.\d\d sec\n\z/
200
+
201
+ assert_output exp, "" do
202
+ bisect.time_it "do stuff:", "echo you should not see me"
203
+ end
204
+ end
205
+ end
206
+
207
+ class TestMinitest::TestBisect::TestPathExpander < Minitest::Test
7
208
  def test_sanity
8
- # nothing yet... ugh.
209
+ args = %w[1 -Iblah 2 -d 3 -w 4 5 6]
210
+
211
+ mtbpe = Minitest::Bisect::PathExpander
212
+ expander = mtbpe.new args
213
+
214
+ assert_kind_of PathExpander, expander
215
+ assert_equal [], expander.rb_flags
216
+ assert_same mtbpe::TEST_GLOB, expander.glob
217
+ end
218
+
219
+ def test_process_flags
220
+ args = %w[1 -Iblah 2 -d 3 -w 4 5 6]
221
+
222
+ expander = Minitest::Bisect::PathExpander.new args
223
+
224
+ exp_files = %w[1 2 3 4 5 6]
225
+ exp_flags = %w[-Iblah -d -w]
226
+
227
+ files = expander.process_flags(args)
228
+
229
+ assert_equal files, exp_files
230
+
231
+ # process_flags only filters and does not mutate args
232
+ assert_same args, expander.args
233
+ refute_equal args, exp_files
234
+ refute_equal files, args
235
+
236
+ # separates rb_flags out for separate handling
237
+ assert_equal exp_flags, expander.rb_flags
9
238
  end
10
239
  end
metadata CHANGED
@@ -1,7 +1,7 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: minitest-bisect
3
3
  version: !ruby/object:Gem::Version
4
- version: 1.3.1
4
+ version: 1.4.0
5
5
  platform: ruby
6
6
  authors:
7
7
  - Ryan Davis
@@ -29,7 +29,7 @@ cert_chain:
29
29
  qx3h45R1CAsObX0SQDIT+rRbQrtKz1GHIZTOFYvEJjUY1XmRTZupD3CJ8Q7sDqSy
30
30
  NLq5jm1fq6Y9Uolu3RJbmycf
31
31
  -----END CERTIFICATE-----
32
- date: 2016-02-19 00:00:00.000000000 Z
32
+ date: 2016-06-15 00:00:00.000000000 Z
33
33
  dependencies:
34
34
  - !ruby/object:Gem::Dependency
35
35
  name: minitest-server
@@ -46,19 +46,19 @@ dependencies:
46
46
  - !ruby/object:Gem::Version
47
47
  version: '1.0'
48
48
  - !ruby/object:Gem::Dependency
49
- name: minitest
49
+ name: path_expander
50
50
  requirement: !ruby/object:Gem::Requirement
51
51
  requirements:
52
52
  - - ~>
53
53
  - !ruby/object:Gem::Version
54
- version: '5.8'
55
- type: :development
54
+ version: '1.0'
55
+ type: :runtime
56
56
  prerelease: false
57
57
  version_requirements: !ruby/object:Gem::Requirement
58
58
  requirements:
59
59
  - - ~>
60
60
  - !ruby/object:Gem::Version
61
- version: '5.8'
61
+ version: '1.0'
62
62
  - !ruby/object:Gem::Dependency
63
63
  name: rdoc
64
64
  requirement: !ruby/object:Gem::Requirement
@@ -93,14 +93,14 @@ dependencies:
93
93
  requirements:
94
94
  - - ~>
95
95
  - !ruby/object:Gem::Version
96
- version: '3.14'
96
+ version: '3.15'
97
97
  type: :development
98
98
  prerelease: false
99
99
  version_requirements: !ruby/object:Gem::Requirement
100
100
  requirements:
101
101
  - - ~>
102
102
  - !ruby/object:Gem::Version
103
- version: '3.14'
103
+ version: '3.15'
104
104
  description: |-
105
105
  Hunting down random test failures can be very very difficult,
106
106
  sometimes impossible, but minitest-bisect makes it easy.
metadata.gz.sig CHANGED
Binary file