ZenTest 4.11.2 → 4.12.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.
@@ -1,6 +0,0 @@
1
- #!/usr/bin/env ruby
2
-
3
- require 'autotest'
4
-
5
- Autotest.parse_options
6
- Autotest.runner.run
@@ -1,74 +0,0 @@
1
- #!/usr/bin/env ruby -w
2
-
3
- require 'multiruby'
4
-
5
- ENV.delete 'RUBYOPT'
6
-
7
- ARGV << "help" if ARGV.empty?
8
-
9
- Dir.chdir Multiruby.root_dir
10
- Multiruby.setup_dirs(false)
11
-
12
- ARGV.each do |spec|
13
- case spec
14
- when "-h", "--help", "help" then
15
- Multiruby.help
16
- exit 0
17
- when "the_usual" then # TODO: update #help
18
- ARGV.push(*Multiruby::TAGS.map { |v| "mri:tar:#{v.gsub(/_/, '.')}" })
19
- ARGV << "build" << "update:rubygems"
20
- system "multigem install --no-ri --no-rdoc rake minitest ZenTest gemcutter rubyforge hoe"
21
- when "build" then
22
- Multiruby.build_and_install
23
- when "clean" then
24
- Multiruby.clean
25
- when "list" then
26
- Multiruby.list
27
- when /rm:(.*)/ then
28
- Multiruby.rm $1
29
- when "rubygems:merge" then
30
- Multiruby.merge_rubygems
31
- when "rubygems:update", "update:rubygems" then
32
- Multiruby.update_rubygems
33
- ARGV << "build"
34
- when "update" then
35
- Multiruby.update
36
- when "tags" then
37
- p Multiruby.tags
38
- when "mri:svn:current" then
39
- ARGV << "mri:svn:releases" << "mri:svn:branches" << "build"
40
- when "mri:svn:releases" then
41
- Multiruby::TAGS.each do |v|
42
- latest = Multiruby.mri_latest_tag v
43
- abort "Can't find tag #{v}" unless latest
44
- ARGV << "mri:svn:tag:#{latest}:mri_rel_#{v}"
45
- end
46
- ARGV << "build"
47
- when /mri:svn:branch:(.*)/ then
48
- ver = "branches/ruby_#{$1}" unless ver == "trunk"
49
- Multiruby.svn_co "#{Multiruby::MRI_SVN}/#{$1}", "mri_#{$1}"
50
- ARGV << "build"
51
- when "mri:svn:branches" then
52
- Multiruby::BRANCHES.each do |v|
53
- ARGV << "mri:svn:branch:#{v}"
54
- end
55
- ARGV << "build"
56
- when /mri:svn:tag:(.*):(.*)/ then
57
- Multiruby.svn_co "#{Multiruby::MRI_SVN}/tags/#{$1}", $2
58
- ARGV << "build"
59
- when /mri:svn:tag:(.*)/ then
60
- ARGV << "mri:svn:tag:#{$1}:#{$1}" << "build"
61
- when /mri:list:(.*)/ then
62
- v = $1
63
- ver = v[/\d+\.\d+/]
64
- url = "#{Multiruby::RUBY_URL}/#{ver}/"
65
-
66
- puts Multiruby.matching_versions(url, v).join("\n")
67
- when /mri:tar:(.*)/ then
68
- Multiruby.fetch_tar $1
69
- ARGV << "build"
70
- else
71
- warn "unknown spec #{spec}"
72
- end
73
- end
74
-
@@ -1,16 +0,0 @@
1
- # -*- ruby -*-
2
-
3
- # require 'autotest/autoupdate'
4
- # require 'autotest/bundler'
5
- # require 'autotest/isolate'
6
- # require 'autotest/once'
7
- # require 'autotest/rcov'
8
- # require 'autotest/restart'
9
- # require 'autotest/timestamp'
10
-
11
- # Autotest::AutoUpdate.sleep_time = o
12
- # Autotest::AutoUpdate.update_cmd = o
13
- # Autotest::Isolate.dir = o
14
- # Autotest::RCov.command = o
15
- # Autotest::RCov.pattern = o
16
- # Autotest::RCov.options = o
@@ -1,901 +0,0 @@
1
- require "find"
2
- require "rbconfig"
3
-
4
- ##
5
- # Autotest continuously scans the files in your project for changes
6
- # and runs the appropriate tests. Test failures are run until they
7
- # have all passed. Then the full test suite is run to ensure that
8
- # nothing else was inadvertantly broken.
9
- #
10
- # If you want Autotest to start over from the top, hit ^C once. If
11
- # you want Autotest to quit, hit ^C twice.
12
- #
13
- # Rails:
14
- #
15
- # The autotest command will automatically discover a Rails directory
16
- # by looking for config/environment.rb. When Rails is discovered,
17
- # autotest uses RailsAutotest to perform file mappings and other work.
18
- # See RailsAutotest for details.
19
- #
20
- # Plugins:
21
- #
22
- # Plugins are available by creating a .autotest file either in your
23
- # project root or in your home directory. You can then write event
24
- # handlers in the form of:
25
- #
26
- # Autotest.add_hook hook_name { |autotest| ... }
27
- #
28
- # The available hooks are listed in +ALL_HOOKS+.
29
- #
30
- # See example_dot_autotest.rb for more details.
31
- #
32
- # If a hook returns a true value, it signals to autotest that the hook
33
- # was handled and should not continue executing hooks.
34
- #
35
- # Naming:
36
- #
37
- # Autotest uses a simple naming scheme to figure out how to map
38
- # implementation files to test files following the Test::Unit naming
39
- # scheme.
40
- #
41
- # * Test files must be stored in test/
42
- # * Test files names must start with test_
43
- # * Test class names must start with Test
44
- # * Implementation files must be stored in lib/
45
- # * Implementation files must match up with a test file named
46
- # test_.*<impl-name>.rb
47
- #
48
- # Strategy:
49
- #
50
- # 1. Find all files and associate them from impl <-> test.
51
- # 2. Run all tests.
52
- # 3. Scan for failures.
53
- # 4. Detect changes in ANY (ruby?. file, rerun all failures + changed files.
54
- # 5. Until 0 defects, goto 3.
55
- # 6. When 0 defects, goto 2.
56
-
57
- class Autotest
58
-
59
- RUBY19 = defined? Encoding
60
-
61
- T0 = Time.at 0
62
-
63
- ALL_HOOKS = [ :all_good, :died, :green, :initialize,
64
- :post_initialize, :interrupt, :quit, :ran_command,
65
- :red, :reset, :run_command, :updated, :waiting ]
66
-
67
- def self.options
68
- @@options ||= {}
69
- end
70
-
71
- def options
72
- self.class.options
73
- end
74
-
75
- HOOKS = Hash.new { |h,k| h[k] = [] }
76
-
77
- unless defined? WINDOZE then
78
- WINDOZE = /mswin|mingw/ =~ RbConfig::CONFIG['host_os']
79
- SEP = WINDOZE ? '&' : ';'
80
- end
81
-
82
- @@discoveries = []
83
-
84
- def self.parse_options args = ARGV
85
- require 'optparse'
86
- options = {
87
- :args => args.dup
88
- }
89
-
90
- OptionParser.new do |opts|
91
- opts.banner = <<-BANNER.gsub(/^ /, '')
92
- Continuous testing for your ruby app.
93
-
94
- Autotest automatically tests code that has changed. It assumes
95
- the code is in lib, and tests are in test/test_*.rb. Autotest
96
- uses plugins to control what happens. You configure plugins
97
- with require statements in the .autotest file in your
98
- project base directory, and a default configuration for all
99
- your projects in the .autotest file in your home directory.
100
-
101
- Usage:
102
- autotest [options]
103
- BANNER
104
-
105
- opts.on "-f", "--fast-start", "Do not run full tests at start" do
106
- options[:no_full_after_start] = true
107
- end
108
-
109
- opts.on("-c", "--no-full-after-failed",
110
- "Do not run all tests on red->green") do
111
- options[:no_full_after_failed] = true
112
- end
113
-
114
- opts.on "-d", "--debug", "Debug mode, for reporting bugs." do
115
- require "pp"
116
- options[:debug] = true
117
- end
118
-
119
- opts.on "-v", "--verbose", "Be annoyingly verbose (debugs .autotest)." do
120
- options[:verbose] = true
121
- end
122
-
123
- opts.on "-q", "--quiet", "Be quiet." do
124
- options[:quiet] = true
125
- end
126
-
127
- opts.on("-r", "--rc CONF", String, "Override path to config file") do |o|
128
- options[:rc] = Array(o)
129
- end
130
-
131
- opts.on("-s", "--style STYLE", String,
132
- "Manually specify test style. (default: autodiscover)") do |style|
133
- options[:style] = Array(style)
134
- end
135
-
136
- opts.on("-w", "--warnings", "Turn on ruby warnings") do
137
- $-w = true
138
- end
139
-
140
- opts.on "-h", "--help", "Show this." do
141
- puts opts
142
- exit 1
143
- end
144
- end.parse! args
145
-
146
- Autotest.options.merge! options
147
-
148
- options
149
- end
150
-
151
- ##
152
- # Calculates the autotest runner to use to run the tests.
153
- #
154
- # Can be overridden with --style, otherwise uses ::autodiscover.
155
-
156
- def self.runner
157
- style = options[:style] || Autotest.autodiscover
158
- target = Autotest
159
-
160
- unless style.empty? then
161
- mod = "autotest/#{style.join "_"}"
162
- puts "loading #{mod}"
163
- begin
164
- require mod
165
- rescue LoadError
166
- abort "Autotest style #{mod} doesn't seem to exist. Aborting."
167
- end
168
- target = Autotest.const_get(style.map {|s| s.capitalize}.join)
169
- end
170
-
171
- target
172
- end
173
-
174
- ##
175
- # Add a proc to the collection of discovery procs. See
176
- # +autodiscover+.
177
-
178
- def self.add_discovery &proc
179
- @@discoveries << proc
180
- end
181
-
182
- ##
183
- # Automatically find all potential autotest runner styles by
184
- # searching your loadpath, vendor/plugins, and rubygems for
185
- # "autotest/discover.rb". If found, that file is loaded and it
186
- # should register discovery procs with autotest using
187
- # +add_discovery+. That proc should return one or more strings
188
- # describing the user's current environment. Those styles are then
189
- # combined to dynamically invoke an autotest plugin to suite your
190
- # environment. That plugin should define a subclass of Autotest with
191
- # a corresponding name.
192
- #
193
- # === Process:
194
- #
195
- # 1. All autotest/discover.rb files loaded.
196
- # 2. Those procs determine your styles (eg ["rails", "rspec"]).
197
- # 3. Require file by sorting styles and joining (eg 'autotest/rails_rspec').
198
- # 4. Invoke run method on appropriate class (eg Autotest::RailsRspec.run).
199
- #
200
- # === Example autotest/discover.rb:
201
- #
202
- # Autotest.add_discovery do
203
- # "rails" if File.exist? 'config/environment.rb'
204
- # end
205
- #
206
-
207
- def self.autodiscover
208
- require 'rubygems'
209
-
210
- # *sigh*
211
- #
212
- # This is needed for rspec's hacky discovery mechanism. For some
213
- # reason rspec2 added generators that create
214
- # "autotest/discover.rb" right in the project directory instead of
215
- # keeping it in the rspec gem and properly deciding that the
216
- # project is an rspec based project or not. See the url for more
217
- # details:
218
- #
219
- # http://rubyforge.org/tracker/?func=detail&atid=1678&aid=28775&group_id=419
220
- #
221
- # For the record, the sane way to do it is the bacon way:
222
- #
223
- # "Since version 1.0, there is autotest support. You need to tag
224
- # your test directories (test/ or spec/) by creating an .bacon
225
- # file there. Autotest then will find it."
226
- #
227
- # I'm submitting a counter-patch to rspec to fix stuff properly,
228
- # but for now I'm stuck with this because their brokenness is
229
- # documented in multiple books.
230
- #
231
- # I'm removing this code once a sane rspec goes out.
232
-
233
- hacky_discovery = Gem::Specification.any? { |s| s.name =~ /^rspec/ }
234
- $: << '.' if hacky_discovery
235
-
236
- Gem.find_files("autotest/discover*").each do |f|
237
- load f
238
- end
239
-
240
- # call all discovery procs and determine the style to use
241
- @@discoveries.map{ |proc| proc.call }.flatten.compact.sort.uniq
242
- end
243
-
244
- ##
245
- # Initialize and run the system.
246
-
247
- def self.run
248
- new.run
249
- end
250
-
251
- attr_writer :known_files
252
- attr_accessor :completed_re
253
- attr_accessor :extra_class_map
254
- attr_accessor :extra_files
255
- attr_accessor :failed_results_re
256
- attr_accessor :files_to_test
257
- attr_accessor :find_directories
258
- attr_accessor :find_order
259
- attr_accessor :interrupted
260
- attr_accessor :last_mtime
261
- attr_accessor :latest_results
262
- attr_accessor :libs
263
- attr_accessor :order # TODO: deprecate and remove
264
- attr_accessor :output
265
- attr_accessor :prefix
266
- attr_accessor :results
267
- attr_accessor :sleep
268
- attr_accessor :tainted
269
- attr_accessor :test_mappings
270
- attr_accessor :testlib
271
- attr_accessor :testprefix
272
- attr_accessor :unit_diff
273
- attr_accessor :wants_to_quit
274
-
275
- alias tainted? tainted
276
-
277
- ##
278
- # Initialize the instance and then load the user's .autotest file, if any.
279
-
280
- def initialize
281
- # these two are set directly because they're wrapped with
282
- # add/remove/clear accessor methods
283
- @exception_list = []
284
- @child = nil
285
- self.test_mappings = []
286
-
287
- self.completed_re =
288
- /\d+ (tests|runs), \d+ assertions, \d+ failures, \d+ errors(, \d+ skips)?/
289
- self.extra_class_map = {}
290
- self.extra_files = []
291
- self.failed_results_re = /^\s*\d+\) (?:Failure|Error):\n(.*?)(?: [\(\[](.*?)[\)\]])?:$/
292
- self.files_to_test = new_hash_of_arrays
293
- self.find_order = []
294
- self.known_files = nil
295
- self.libs = %w[. lib test].join(File::PATH_SEPARATOR)
296
- self.order = :random
297
- self.output = $stderr
298
- self.prefix = nil
299
- self.sleep = 1
300
- self.testlib = "minitest/autorun" # TODO: rename
301
- self.testprefix = "gem 'minitest'" # TODO: rename
302
-
303
- specified_directories = ARGV.reject { |arg| arg.start_with?("-") } # options are not directories
304
- self.find_directories = specified_directories.empty? ? ['.'] : specified_directories
305
- self.unit_diff = nil
306
- self.latest_results = nil
307
-
308
- # file in /lib -> run test in /test
309
- self.add_mapping(/^lib\/.*\.rb$/) do |filename, _|
310
- possible = File.basename(filename).gsub '_', '_?'
311
- files_matching %r%^test/.*#{possible}$%
312
- end
313
-
314
- # file in /test -> run it
315
- self.add_mapping(/^test.*\/test_.*rb$/) do |filename, _|
316
- filename
317
- end
318
-
319
- default_configs = [File.expand_path('~/.autotest'), './.autotest']
320
- configs = options[:rc] || default_configs
321
-
322
- configs.each do |f|
323
- load f if File.exist? f
324
- end
325
- end
326
-
327
- def debug
328
- find_files_to_test
329
-
330
- puts "Known test files:"
331
- puts
332
- pp files_to_test.keys.sort
333
-
334
- class_map = self.class_map
335
-
336
- puts
337
- puts "Known class map:"
338
- puts
339
- pp class_map
340
- end
341
-
342
- def class_map
343
- class_map = Hash[*self.find_order.grep(/^test/).map { |f| # TODO: ugly
344
- [path_to_classname(f), f]
345
- }.flatten]
346
- class_map.merge! self.extra_class_map
347
- class_map
348
- end
349
-
350
- ##
351
- # Repeatedly run failed tests, then all tests, then wait for changes
352
- # and carry on until killed.
353
-
354
- def run
355
- hook :initialize
356
- hook :post_initialize
357
-
358
- reset
359
- add_sigint_handler
360
-
361
- self.last_mtime = Time.now if options[:no_full_after_start]
362
-
363
- self.debug if options[:debug]
364
-
365
- loop do
366
- begin # ^c handler
367
- get_to_green
368
- if tainted? and not options[:no_full_after_failed] then
369
- rerun_all_tests
370
- else
371
- hook :all_good
372
- end
373
- wait_for_changes
374
- rescue Interrupt
375
- break if wants_to_quit
376
- reset
377
- end
378
- end
379
- hook :quit
380
- puts
381
- rescue Exception => err
382
- hook(:died, err) or (
383
- warn "Unhandled exception: #{err}"
384
- warn err.backtrace.join("\n ")
385
- warn "Quitting"
386
- )
387
- end
388
-
389
- ##
390
- # Keep running the tests after a change, until all pass.
391
-
392
- def get_to_green
393
- begin
394
- run_tests
395
- wait_for_changes unless all_good
396
- end until all_good
397
- end
398
-
399
- ##
400
- # Look for files to test then run the tests and handle the results.
401
-
402
- def run_tests
403
- new_mtime = self.find_files_to_test
404
- return unless new_mtime
405
- self.last_mtime = new_mtime
406
-
407
- cmd = self.make_test_cmd self.files_to_test
408
- return if cmd.empty?
409
-
410
- hook :run_command, cmd
411
-
412
- puts cmd unless options[:quiet]
413
-
414
- old_sync = $stdout.sync
415
- $stdout.sync = true
416
- self.results = []
417
- line = []
418
- begin
419
- open "| #{cmd}", "r" do |f|
420
- until f.eof? do
421
- c = f.getc or break
422
- if RUBY19 then
423
- print c
424
- else
425
- putc c
426
- end
427
- line << c
428
- if c == ?\n then
429
- self.results << if RUBY19 then
430
- line.join
431
- else
432
- line.pack "c*"
433
- end
434
- line.clear
435
- end
436
- end
437
- end
438
- ensure
439
- $stdout.sync = old_sync
440
- end
441
- hook :ran_command
442
- self.results = self.results.join
443
-
444
- handle_results self.results
445
- end
446
-
447
- ############################################################
448
- # Utility Methods, not essential to reading of logic
449
-
450
- ##
451
- # Installs a sigint handler.
452
-
453
- def add_sigint_handler
454
- trap 'INT' do
455
- Process.kill "KILL", @child if @child
456
-
457
- if self.interrupted then
458
- self.wants_to_quit = true
459
- else
460
- unless hook :interrupt then
461
- puts "Interrupt a second time to quit"
462
- self.interrupted = true
463
- Kernel.sleep 1.5
464
- end
465
- raise Interrupt, nil # let the run loop catch it
466
- end
467
- end
468
- end
469
-
470
- ##
471
- # Installs a sigquit handler
472
-
473
- def add_sigquit_handler
474
- trap 'QUIT' do
475
- restart
476
- end
477
- end
478
-
479
- def restart
480
- Process.kill "KILL", @child if @child
481
-
482
- cmd = [$0, *options[:args]]
483
-
484
- index = $LOAD_PATH.index RbConfig::CONFIG["sitelibdir"]
485
-
486
- if index then
487
- extra = $LOAD_PATH[0...index]
488
- cmd = [Gem.ruby, "-I", extra.join(":")] + cmd
489
- end
490
-
491
- puts cmd.join(" ") if options[:verbose]
492
-
493
- exec(*cmd)
494
- end
495
-
496
- ##
497
- # If there are no files left to test (because they've all passed),
498
- # then all is good.
499
-
500
- def all_good
501
- files_to_test.empty?
502
- end
503
-
504
- ##
505
- # Convert a path in a string, s, into a class name, changing
506
- # underscores to CamelCase, etc.
507
-
508
- def path_to_classname s
509
- sep = File::SEPARATOR
510
- f = s.sub(/^test#{sep}/, '').sub(/\.rb$/, '').split sep
511
- f = f.map { |path| path.split(/_|(\d+)/).map { |seg| seg.capitalize }.join }
512
- f = f.map { |path| path =~ /^Test/ ? path : "Test#{path}" }
513
-
514
- f.join '::'
515
- end
516
-
517
- ##
518
- # Returns a hash mapping a file name to the known failures for that
519
- # file.
520
-
521
- def consolidate_failures failed
522
- filters = new_hash_of_arrays
523
-
524
- class_map = self.class_map
525
-
526
- failed.each do |method, klass|
527
- if class_map.has_key? klass then
528
- filters[class_map[klass]] << method
529
- else
530
- output.puts "Unable to map class #{klass} to a file"
531
- end
532
- end
533
-
534
- filters
535
- end
536
-
537
- ##
538
- # Find the files to process, ignoring temporary files, source
539
- # configuration management files, etc., and return a Hash mapping
540
- # filename to modification time.
541
-
542
- def find_files
543
- result = {}
544
- targets = self.find_directories + self.extra_files
545
- self.find_order.clear
546
-
547
- targets.each do |target|
548
- order = []
549
- Find.find target do |f|
550
- Find.prune if f =~ self.exceptions
551
- Find.prune if f =~ /^\.\/tmp/ # temp dir, used by isolate
552
-
553
- next unless File.file? f
554
- next if f =~ /(swp|~|rej|orig)$/ # temporary/patch files
555
- next if f =~ /(,v)$/ # RCS files
556
- next if f =~ /\/\.?#/ # Emacs autosave/cvs merge files
557
-
558
- filename = f.sub(/^\.\//, '')
559
-
560
- result[filename] = File.stat(filename).mtime rescue next
561
- order << filename
562
- end
563
- self.find_order.push(*order.sort)
564
- end
565
-
566
- result
567
- end
568
-
569
- ##
570
- # Find the files which have been modified, update the recorded
571
- # timestamps, and use this to update the files to test. Returns
572
- # the latest mtime of the files modified or nil when nothing was
573
- # modified.
574
-
575
- def find_files_to_test files = find_files
576
- updated = files.select { |filename, mtime| self.last_mtime < mtime }
577
-
578
- # nothing to update or initially run
579
- unless updated.empty? || self.last_mtime.to_i == 0 then
580
- p updated if options[:verbose]
581
-
582
- hook :updated, updated
583
- end
584
-
585
- updated.map { |f,m| test_files_for f }.flatten.uniq.each do |filename|
586
- self.files_to_test[filename] # creates key with default value
587
- end
588
-
589
- if updated.empty? then
590
- nil
591
- else
592
- files.values.max
593
- end
594
- end
595
-
596
- ##
597
- # Check results for failures, set the "bar" to red or green, and if
598
- # there are failures record this.
599
-
600
- def handle_results results
601
- results = results.gsub(/\e\[\d+m/, '') # strip ascii color
602
- failed = results.scan(self.failed_results_re).map { |m, k|
603
- k, m = $1, $2 if m =~ /(\w+)\#(\w+)/ # minitest 5 output
604
- [m, k]
605
- }
606
-
607
- completed = results[self.completed_re]
608
-
609
- if completed then
610
- completed = completed.scan(/(\d+) (\w+)/).map { |v, k| [k, v.to_i] }
611
-
612
- self.latest_results = Hash[*completed.flatten]
613
- self.files_to_test = consolidate_failures failed
614
-
615
- color = failed.empty? ? :green : :red
616
- hook color
617
- else
618
- self.latest_results = nil
619
- end
620
-
621
- self.tainted = true unless self.files_to_test.empty?
622
- end
623
-
624
- ##
625
- # Lazy accessor for the known_files hash.
626
-
627
- def known_files
628
- unless @known_files then
629
- @known_files = {}
630
- find_order.each {|f| @known_files[f] = true }
631
- end
632
- @known_files
633
- end
634
-
635
- ##
636
- # Generate the commands to test the supplied files
637
-
638
- def make_test_cmd files_to_test
639
- if options[:debug] then
640
- puts "Files to test:"
641
- puts
642
- pp files_to_test
643
- puts
644
- end
645
-
646
- cmds = []
647
- full, partial = reorder(files_to_test).partition { |k,v| v.empty? }
648
-
649
- diff = self.unit_diff
650
- diff = " | #{diff}" if diff and diff !~ /^\|/
651
-
652
- unless full.empty? then
653
- classes = full.map {|k,v| k}.flatten.uniq
654
- classes.unshift testlib
655
- classes = classes.join " "
656
- cmds << "#{ruby_cmd} -e \"#{testprefix}; %w[#{classes}].each { |f| require f }\"#{diff}"
657
- end
658
-
659
- partial.each do |klass, methods|
660
- regexp = Regexp.union(*methods).source
661
- cmds << "#{ruby_cmd} #{klass} -n \"/^(#{regexp})$/\"#{diff}"
662
- end
663
-
664
- cmds.join "#{SEP} "
665
- end
666
-
667
- def new_hash_of_arrays
668
- Hash.new { |h,k| h[k] = [] }
669
- end
670
-
671
- def reorder files_to_test
672
- case self.order
673
- when :alpha then
674
- files_to_test.sort_by { |k,v| k }
675
- when :reverse then
676
- files_to_test.sort_by { |k,v| k }.reverse
677
- when :random then
678
- max = files_to_test.size
679
- files_to_test.sort_by { |k,v| rand max }
680
- when :natural then
681
- (self.find_order & files_to_test.keys).map { |f| [f, files_to_test[f]] }
682
- else
683
- raise "unknown order type: #{self.order.inspect}"
684
- end
685
- end
686
-
687
- ##
688
- # Rerun the tests from cold (reset state)
689
-
690
- def rerun_all_tests
691
- reset
692
- run_tests
693
-
694
- hook :all_good if all_good
695
- end
696
-
697
- ##
698
- # Clear all state information about test failures and whether
699
- # interrupts will kill autotest.
700
-
701
- def reset
702
- self.files_to_test.clear
703
- self.find_order.clear
704
-
705
- self.interrupted = false
706
- self.known_files = nil
707
- self.last_mtime = T0
708
- self.tainted = false
709
- self.wants_to_quit = false
710
-
711
- hook :reset
712
- end
713
-
714
- ##
715
- # Determine and return the path of the ruby executable.
716
-
717
- def ruby
718
- ruby = ENV['RUBY']
719
- ruby ||= File.join(RbConfig::CONFIG['bindir'],
720
- RbConfig::CONFIG['ruby_install_name'])
721
-
722
- ruby.gsub! File::SEPARATOR, File::ALT_SEPARATOR if File::ALT_SEPARATOR
723
-
724
- return ruby
725
- end
726
-
727
- ##
728
- # Returns the base of the ruby command.
729
-
730
- def ruby_cmd
731
- "#{prefix}#{ruby} -I#{libs} -rubygems"
732
- end
733
-
734
- ##
735
- # Return the name of the file with the tests for filename by finding
736
- # a +test_mapping+ that matches the file and executing the mapping's
737
- # proc.
738
-
739
- def test_files_for filename
740
- result = []
741
-
742
- self.test_mappings.each do |file_re, proc|
743
- if filename =~ file_re then
744
- result = [proc.call(filename, $~)].
745
- flatten.sort.uniq.select { |f| known_files[f] }
746
- break unless result.empty?
747
- end
748
- end
749
-
750
- pp :test_file_for => [filename, result.first] if result and options[:debug]
751
-
752
- output.puts "No tests matched #{filename}" if
753
- options[:verbose] and result.empty?
754
-
755
- return result
756
- end
757
-
758
- ##
759
- # Sleep then look for files to test, until there are some.
760
-
761
- def wait_for_changes
762
- hook :waiting
763
- Kernel.sleep self.sleep until find_files_to_test
764
- end
765
-
766
- ############################################################
767
- # File Mappings:
768
-
769
- ##
770
- # Returns all known files in the codebase matching +regexp+.
771
-
772
- def files_matching regexp
773
- self.find_order.select { |k| k =~ regexp }
774
- end
775
-
776
- ##
777
- # Adds a file mapping, optionally prepending the mapping to the
778
- # front of the list if +prepend+ is true. +regexp+ should match a
779
- # file path in the codebase. +proc+ is passed a matched filename and
780
- # Regexp.last_match. +proc+ should return an array of tests to run.
781
- #
782
- # For example, if test_helper.rb is modified, rerun all tests:
783
- #
784
- # at.add_mapping(/test_helper.rb/) do |f, _|
785
- # at.files_matching(/^test.*rb$/)
786
- # end
787
-
788
- def add_mapping regexp, prepend = false, &proc
789
- if prepend then
790
- @test_mappings.unshift [regexp, proc]
791
- else
792
- @test_mappings.push [regexp, proc]
793
- end
794
- nil
795
- end
796
-
797
- ##
798
- # Removed a file mapping matching +regexp+.
799
-
800
- def remove_mapping regexp
801
- @test_mappings.delete_if do |k,v|
802
- k == regexp
803
- end
804
- nil
805
- end
806
-
807
- ##
808
- # Clears all file mappings. This is DANGEROUS as it entirely
809
- # disables autotest. You must add at least one file mapping that
810
- # does a good job of rerunning appropriate tests.
811
-
812
- def clear_mappings
813
- @test_mappings.clear
814
- nil
815
- end
816
-
817
- ############################################################
818
- # Exceptions:
819
-
820
- ##
821
- # Adds +regexp+ to the list of exceptions for find_file. This must
822
- # be called _before_ the exceptions are compiled.
823
-
824
- def add_exception regexp
825
- raise "exceptions already compiled" if defined? @exceptions
826
-
827
- @exception_list << regexp
828
- nil
829
- end
830
-
831
- ##
832
- # Removes +regexp+ to the list of exceptions for find_file. This
833
- # must be called _before_ the exceptions are compiled.
834
-
835
- def remove_exception regexp
836
- raise "exceptions already compiled" if defined? @exceptions
837
- @exception_list.delete regexp
838
- nil
839
- end
840
-
841
- ##
842
- # Clears the list of exceptions for find_file. This must be called
843
- # _before_ the exceptions are compiled.
844
-
845
- def clear_exceptions
846
- raise "exceptions already compiled" if defined? @exceptions
847
- @exception_list.clear
848
- nil
849
- end
850
-
851
- ##
852
- # Return a compiled regexp of exceptions for find_files or nil if no
853
- # filtering should take place. This regexp is generated from
854
- # +exception_list+.
855
-
856
- def exceptions
857
- unless defined? @exceptions then
858
- @exceptions = if @exception_list.empty? then
859
- nil
860
- else
861
- Regexp.union(*@exception_list)
862
- end
863
- end
864
-
865
- @exceptions
866
- end
867
-
868
- ############################################################
869
- # Hooks:
870
-
871
- ##
872
- # Call the event hook named +name+, passing in optional args
873
- # depending on the hook itself.
874
- #
875
- # Returns false if no hook handled the event.
876
- #
877
- # === Hook Writers!
878
- #
879
- # This executes all registered hooks <em>until one returns truthy</em>.
880
- # Pay attention to the return value of your block!
881
-
882
- def hook name, *args
883
- deprecated = {
884
- # none currently
885
- }
886
-
887
- if deprecated[name] and not HOOKS[name].empty? then
888
- warn "hook #{name} has been deprecated, use #{deprecated[name]}"
889
- end
890
-
891
- HOOKS[name].any? { |plugin| plugin[self, *args] }
892
- end
893
-
894
- ##
895
- # Add the supplied block to the available hooks, with the given
896
- # name.
897
-
898
- def self.add_hook name, &block
899
- HOOKS[name] << block
900
- end
901
- end