minitest-bisect 1.3.1 → 1.4.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
  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