testr 14.0.0

Sign up to get free protection for your applications and to get access to all the features.
@@ -0,0 +1,9 @@
1
+ require 'testr/config'
2
+
3
+ TestR::Config.after_fork_hooks << lambda {
4
+ |worker_number, log_file, test_file, test_names|
5
+
6
+ # for compatitibilty with parallel_tests gem,
7
+ # store numbers as strings: "", "2", "3", "4"
8
+ ENV['TEST_ENV_NUMBER'] = (worker_number + 1).to_s if worker_number > 0
9
+ }
@@ -0,0 +1,37 @@
1
+ require 'testr/config'
2
+ require 'active_support/inflector'
3
+
4
+ TestR::Config.reabsorb_file_globs.push(
5
+ 'config/**/*.{rb,yml}',
6
+ 'db/schema.rb',
7
+ 'Gemfile.lock'
8
+ )
9
+
10
+ TestR::Config.test_file_matchers[%r<^(app|lib|test|spec)/.+\.rb$>] =
11
+ lambda do |path|
12
+ base = File.basename(path, '.rb')
13
+ poly = ActiveSupport::Inflector.pluralize(base)
14
+ "{test,spec}/**/{#{base},#{poly}_*}_{test,spec}.rb"
15
+ end
16
+
17
+ TestR::Config.test_file_matchers[%r<^(test|spec)/factories/.+_factory\.rb$>] =
18
+ lambda do |path|
19
+ base = File.basename(path, '_factory.rb')
20
+ poly = ActiveSupport::Inflector.pluralize(base)
21
+ "{test,spec}/**/{#{base},#{poly}_*}_{test,spec}.rb"
22
+ end
23
+
24
+ begin
25
+ require 'rails/railtie'
26
+ Class.new Rails::Railtie do
27
+ config.before_initialize do |app|
28
+ if app.config.cache_classes
29
+ warn "testr/config/rails: Setting #{app.class}.config.cache_classes = false"
30
+ app.config.cache_classes = false
31
+ end
32
+ end
33
+ end
34
+ rescue LoadError
35
+ warn "testr/config/rails: Railtie not available; please manually set:\n\t"\
36
+ "config.cache_classes = false"
37
+ end
@@ -0,0 +1,129 @@
1
+ require 'json'
2
+ require 'diff/lcs'
3
+ require 'testr/client'
4
+ require 'testr/server'
5
+ require 'testr/config'
6
+
7
+ module TestR
8
+ module Driver
9
+
10
+ extend Server
11
+ extend self
12
+
13
+ def run_all_test_files
14
+ run_test_files Dir[*Config.all_test_file_globs]
15
+ end
16
+
17
+ def stop_running_test_files
18
+ @master.send [:stop]
19
+ @running_test_files.clear
20
+ end
21
+
22
+ def rerun_passed_test_files
23
+ run_test_files @passed_test_files
24
+ end
25
+
26
+ def rerun_failed_test_files
27
+ run_test_files @failed_test_files
28
+ end
29
+
30
+ def reabsorb_overhead_files very_first_time = false
31
+ quit_herald_and_master unless very_first_time
32
+
33
+ @master = Client::Transceiver.new('testr-master') do |line|
34
+ event, file = JSON.load(line)
35
+
36
+ case event.to_sym
37
+ when :test
38
+ @running_test_files.push file
39
+
40
+ when :pass
41
+ @passed_test_files.push file unless @passed_test_files.include? file
42
+ @running_test_files.delete file
43
+
44
+ when :fail
45
+ @failed_test_files.push file unless @failed_test_files.include? file
46
+ @running_test_files.delete file
47
+ end
48
+
49
+ @upstream.print line
50
+ end
51
+
52
+ @master.send [:load, Config.overhead_load_paths,
53
+ Dir[*Config.overhead_file_globs]]
54
+
55
+ @herald = Client::Receiver.new('testr-herald') do |line|
56
+ changed_file = line.chomp
57
+ warn "testr-driver: herald: #{changed_file}" if $DEBUG
58
+
59
+ # find and run the tests that correspond to the changed file
60
+ Config.test_file_globbers.each do |source_regexp, test_globber|
61
+ if source_regexp =~ changed_file
62
+ run_test_files Dir[test_globber.call(changed_file).to_s]
63
+ end
64
+ end
65
+
66
+ # reabsorb text execution overhead if overhead files changed
67
+ if Config.reabsorb_file_greps.any? {|r| r =~ changed_file }
68
+ @upstream.puts JSON.dump([:over, changed_file])
69
+ # NOTE: new thread because reabsorb_overhead_files will kill this one
70
+ Thread.new { reabsorb_overhead_files }.join
71
+ end
72
+ end
73
+
74
+ rerun_running_test_files
75
+ end
76
+
77
+ def loop
78
+ reabsorb_overhead_files true
79
+ super
80
+ quit_herald_and_master
81
+ end
82
+
83
+ private
84
+
85
+ def quit_herald_and_master
86
+ @herald.quit
87
+ @master.quit
88
+ end
89
+
90
+ @running_test_files = []
91
+ @passed_test_files = []
92
+ @failed_test_files = []
93
+
94
+ def rerun_running_test_files
95
+ run_test_files @running_test_files
96
+ end
97
+
98
+ def run_test_files files
99
+ files.each {|f| run_test_file f }
100
+ end
101
+
102
+ def run_test_file file
103
+ @master.send [:test, file, find_changed_test_names(file)]
104
+ end
105
+
106
+ @lines_by_file = {}
107
+
108
+ def find_changed_test_names test_file
109
+ # cache the contents of the test file for diffing below
110
+ new_lines = File.readlines(test_file)
111
+ old_lines = @lines_by_file[test_file] || new_lines
112
+ @lines_by_file[test_file] = new_lines
113
+
114
+ # find which tests have changed inside the given test file
115
+ Diff::LCS.diff(old_lines, new_lines).flatten.map do |change|
116
+ catch :found do
117
+ # search backwards from the line that changed up to
118
+ # the first line in the file for test definitions
119
+ change.position.downto(0) do |i|
120
+ if test_name = Config.test_name_extractor.call(new_lines[i])
121
+ throw :found, test_name
122
+ end
123
+ end; nil # prevent unsuccessful search from returning an integer
124
+ end
125
+ end.compact.uniq
126
+ end
127
+
128
+ end
129
+ end
@@ -0,0 +1,100 @@
1
+ require 'json'
2
+ require 'testr/server'
3
+ require 'testr/config'
4
+
5
+ module TestR
6
+ module Master
7
+
8
+ extend Server
9
+ extend self
10
+
11
+ def load paths, files
12
+ $LOAD_PATH.unshift(*paths)
13
+
14
+ files.each do |file|
15
+ branch, leaf = File.split(file)
16
+ file = leaf if paths.include? branch
17
+ require file.sub(/\.rb$/, '')
18
+ end
19
+
20
+ @upstream.print @command_line
21
+ end
22
+
23
+ def test test_file, test_names
24
+ # throttle forking rate to meet the maximum concurrent workers limit
25
+ # NOTE: the next SIGCHLD signal will wake us from this eternal sleep
26
+ sleep until @command_by_worker_pid.size < Config.max_forked_workers
27
+
28
+ log_file = test_file + '.log'
29
+ worker_number = @worker_number_pool.shift
30
+
31
+ Config.before_fork_hooks.each do |hook|
32
+ hook.call worker_number, log_file, test_file, test_names
33
+ end
34
+
35
+ worker_pid = fork do
36
+ # make the process title Test::Unit friendly and ps(1) searchable
37
+ $0 = "testr-worker[#{worker_number}] #{test_file}"
38
+
39
+ # detach worker process from master process' group for kill -pgrp
40
+ Process.setsid
41
+
42
+ # detach worker process from master process' standard input stream
43
+ STDIN.reopen IO.pipe.first
44
+
45
+ # capture test output in log file because tests are run in parallel
46
+ # which makes it difficult to understand interleaved output thereof
47
+ STDERR.reopen(STDOUT.reopen(log_file, 'w')).sync = true
48
+
49
+ Config.after_fork_hooks.each do |hook|
50
+ hook.call worker_number, log_file, test_file, test_names
51
+ end
52
+
53
+ # after loading the user's test file, the at_exit() hook of the user's
54
+ # testing framework will take care of running the tests and reflecting
55
+ # any failures in the worker process' exit status, which will then be
56
+ # handled by the SIGCHLD trap registered in the master process (above)
57
+ Kernel.load test_file
58
+ end
59
+
60
+ @command_by_worker_pid[worker_pid] = @command.push(worker_number)
61
+ @upstream.print @command_line
62
+ end
63
+
64
+ def stop
65
+ # NOTE: the SIGCHLD handler will reap these killed worker processes
66
+ Process.kill :SIGTERM, *@command_by_worker_pid.keys.map {|pid| -pid }
67
+ rescue ArgumentError, SystemCallError
68
+ # some workers might have already exited before we sent them the signal
69
+ end
70
+
71
+ def loop
72
+ super
73
+ stop
74
+ end
75
+
76
+ private
77
+
78
+ @worker_number_pool = (0 ... Config.max_forked_workers).to_a
79
+ @command_by_worker_pid = {}
80
+
81
+ # process exited child processes and report finished workers to upstream
82
+ trap :SIGCHLD do
83
+ begin
84
+ while wait2_array = Process.wait2(-1, Process::WNOHANG)
85
+ child_pid, child_status = wait2_array
86
+ if command = @command_by_worker_pid.delete(child_pid)
87
+ @worker_number_pool.push command.pop
88
+ command[0] = child_status.success? ? 'pass' : 'fail'
89
+ @upstream.puts JSON.dump(command.push(child_status))
90
+ else
91
+ warn "testr-master: unknown child exited: #{wait2_array.inspect}"
92
+ end
93
+ end
94
+ rescue SystemCallError
95
+ # raised by wait2() when there are currently no child processes
96
+ end
97
+ end
98
+
99
+ end
100
+ end
@@ -0,0 +1,32 @@
1
+ require 'json'
2
+
3
+ module TestR
4
+ module Server
5
+
6
+ def quit
7
+ throw :testr_server_quit
8
+ end
9
+
10
+ def loop
11
+ (@upstream = STDOUT.dup).sync = true
12
+ STDOUT.reopen(STDERR).sync = true
13
+
14
+ catch :testr_server_quit do
15
+ while line = STDIN.gets
16
+ warn "#{caller[2]} RECV #{line.chomp}" if $DEBUG
17
+
18
+ command = JSON.load(line)
19
+ method = command.first
20
+
21
+ if respond_to? method and method != __method__ # prevent loops
22
+ @command, @command_line = command, line
23
+ __send__(*command)
24
+ else
25
+ warn "#{self}: bad command: #{method}"
26
+ end
27
+ end
28
+ end
29
+ end
30
+
31
+ end
32
+ end
@@ -0,0 +1,3 @@
1
+ module TestR
2
+ VERSION = "14.0.0"
3
+ end
@@ -0,0 +1,25 @@
1
+ # -*- encoding: utf-8 -*-
2
+ $:.push File.expand_path("../lib", __FILE__)
3
+ require "testr/version"
4
+
5
+ Gem::Specification.new do |s|
6
+ s.name = "testr"
7
+ s.version = TestR::VERSION
8
+ s.authors,
9
+ s.email = File.read('LICENSE').scan(/Copyright \d+ (.+) <(.+?)>/).transpose
10
+ s.homepage = "http://github.com/sunaku/testr"
11
+ s.summary = "Continuous testing tool for Ruby"
12
+ s.description = nil
13
+
14
+ s.files = `git ls-files`.split("\n")
15
+ s.test_files = `git ls-files -- {test,spec,features}/*`.split("\n")
16
+ s.executables = `git ls-files -- bin/*`.split("\n").map{ |f| File.basename(f) }
17
+ s.require_paths = ["lib"]
18
+
19
+ # specify any dependencies here; for example:
20
+ # s.add_development_dependency "rspec"
21
+ # s.add_runtime_dependency "rest-client"
22
+ s.add_runtime_dependency "json", ">= 1.6.1"
23
+ s.add_runtime_dependency "guard", ">= 0.8.4"
24
+ s.add_runtime_dependency "diff-lcs", ">= 1.1.2"
25
+ end
metadata ADDED
@@ -0,0 +1,109 @@
1
+ --- !ruby/object:Gem::Specification
2
+ name: testr
3
+ version: !ruby/object:Gem::Version
4
+ version: 14.0.0
5
+ prerelease:
6
+ platform: ruby
7
+ authors:
8
+ - Suraj N. Kurapati
9
+ - Brian D. Burns
10
+ - Daniel Pittman
11
+ - Jacob Helwig
12
+ - Corné Verbruggen
13
+ autorequire:
14
+ bindir: bin
15
+ cert_chain: []
16
+ date: 2011-10-09 00:00:00.000000000 Z
17
+ dependencies:
18
+ - !ruby/object:Gem::Dependency
19
+ name: json
20
+ requirement: &20744840 !ruby/object:Gem::Requirement
21
+ none: false
22
+ requirements:
23
+ - - ! '>='
24
+ - !ruby/object:Gem::Version
25
+ version: 1.6.1
26
+ type: :runtime
27
+ prerelease: false
28
+ version_requirements: *20744840
29
+ - !ruby/object:Gem::Dependency
30
+ name: guard
31
+ requirement: &20743380 !ruby/object:Gem::Requirement
32
+ none: false
33
+ requirements:
34
+ - - ! '>='
35
+ - !ruby/object:Gem::Version
36
+ version: 0.8.4
37
+ type: :runtime
38
+ prerelease: false
39
+ version_requirements: *20743380
40
+ - !ruby/object:Gem::Dependency
41
+ name: diff-lcs
42
+ requirement: &20742260 !ruby/object:Gem::Requirement
43
+ none: false
44
+ requirements:
45
+ - - ! '>='
46
+ - !ruby/object:Gem::Version
47
+ version: 1.1.2
48
+ type: :runtime
49
+ prerelease: false
50
+ version_requirements: *20742260
51
+ description: ''
52
+ email:
53
+ - sunaku@gmail.com
54
+ - burns180@gmail.com
55
+ - daniel@rimspace.net
56
+ - jacob@technosorcery.net
57
+ - corne@g-majeur.nl
58
+ executables:
59
+ - testr
60
+ - testr-driver
61
+ - testr-herald
62
+ - testr-master
63
+ extensions: []
64
+ extra_rdoc_files: []
65
+ files:
66
+ - .gitignore
67
+ - Gemfile
68
+ - HISTORY.md
69
+ - LICENSE
70
+ - README.md
71
+ - Rakefile
72
+ - bin/testr
73
+ - bin/testr-driver
74
+ - bin/testr-herald
75
+ - bin/testr-master
76
+ - lib/testr/client.rb
77
+ - lib/testr/config.rb
78
+ - lib/testr/config/parallel_tests.rb
79
+ - lib/testr/config/rails.rb
80
+ - lib/testr/driver.rb
81
+ - lib/testr/master.rb
82
+ - lib/testr/server.rb
83
+ - lib/testr/version.rb
84
+ - testr.gemspec
85
+ homepage: http://github.com/sunaku/testr
86
+ licenses: []
87
+ post_install_message:
88
+ rdoc_options: []
89
+ require_paths:
90
+ - lib
91
+ required_ruby_version: !ruby/object:Gem::Requirement
92
+ none: false
93
+ requirements:
94
+ - - ! '>='
95
+ - !ruby/object:Gem::Version
96
+ version: '0'
97
+ required_rubygems_version: !ruby/object:Gem::Requirement
98
+ none: false
99
+ requirements:
100
+ - - ! '>='
101
+ - !ruby/object:Gem::Version
102
+ version: '0'
103
+ requirements: []
104
+ rubyforge_project:
105
+ rubygems_version: 1.8.11
106
+ signing_key:
107
+ specification_version: 3
108
+ summary: Continuous testing tool for Ruby
109
+ test_files: []