testr 14.0.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.
data/LICENSE ADDED
@@ -0,0 +1,19 @@
1
+ (the ISC license)
2
+
3
+ Copyright 2010 Suraj N. Kurapati <sunaku@gmail.com>
4
+ Copyright 2011 Brian D. Burns <burns180@gmail.com>
5
+ Copyright 2011 Daniel Pittman <daniel@rimspace.net>
6
+ Copyright 2011 Jacob Helwig <jacob@technosorcery.net>
7
+ Copyright 2011 Corné Verbruggen <corne@g-majeur.nl>
8
+
9
+ Permission to use, copy, modify, and/or distribute this software for any
10
+ purpose with or without fee is hereby granted, provided that the above
11
+ copyright notice and this permission notice appear in all copies.
12
+
13
+ THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
14
+ WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
15
+ MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
16
+ ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
17
+ WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
18
+ ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
19
+ OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
@@ -0,0 +1,269 @@
1
+ TestR - Continuous testing tool for Ruby
2
+ ==============================================================================
3
+
4
+ TestR is a continuous testing tool for Ruby that automatically detects and
5
+ tests changes in your Ruby application or test suite in an efficient manner:
6
+
7
+ 1. Absorbs test execution overhead into the master Ruby process.
8
+
9
+ 2. Forks to run your test files in parallel and without overhead.
10
+
11
+ 3. Avoids running unchanged tests inside changed test files.
12
+
13
+ ------------------------------------------------------------------------------
14
+ Features
15
+ ------------------------------------------------------------------------------
16
+
17
+ * Executes test files in parallel, making full use of multi-core CPUs.
18
+
19
+ * Tests *changes* in your Ruby application: avoids running (1) unchanged
20
+ test files and (2) unchanged tests inside changed test files.
21
+
22
+ * Supports MiniTest, Test::Unit, RSpec, and any testing framework that (1)
23
+ reflects failures in the process' exit status and (2) is loaded by your
24
+ application's `test/test_helper.rb` or `spec/spec_helper.rb` file.
25
+
26
+ * Logs the output from your tests into separate files: one log per test.
27
+ The path of a log file is simply the path of its test file plus ".log".
28
+
29
+ * Configurable through a Ruby script in your current working directory.
30
+
31
+ * Implemented in less than 360 lines (SLOC) of pure Ruby code! :-)
32
+
33
+ ------------------------------------------------------------------------------
34
+ Architecture
35
+ ------------------------------------------------------------------------------
36
+
37
+ Following UNIX philosophy, TestR is made of simple text-based programs:
38
+
39
+ * `testr` is an interactive command-line user interface (CLI) for driver
40
+ * `testr-herald` monitors current directory tree and reports changed files
41
+ * `testr-driver` tells master to run tests and keeps track of test results
42
+ * `testr-master` absorbs test execution overhead and forks to run your tests
43
+
44
+ You can build your own custom TestR user interface by wrapping `testr-driver`!
45
+
46
+ ------------------------------------------------------------------------------
47
+ Prerequisites
48
+ ------------------------------------------------------------------------------
49
+
50
+ * Ruby 1.8.7 or 1.9.2 or newer.
51
+
52
+ * Operating system that supports POSIX signals and the `fork()` system call.
53
+
54
+ To check if your system qualifies, launch `irb` and enter the following:
55
+
56
+ Process.respond_to? :fork # must be true
57
+ Signal.list.key? 'TERM' # must be true
58
+ Signal.list.key? 'KILL' # must be true
59
+
60
+ ------------------------------------------------------------------------------
61
+ Installation
62
+ ------------------------------------------------------------------------------
63
+
64
+ As a Ruby gem:
65
+
66
+ gem install testr
67
+
68
+ As a Git clone:
69
+
70
+ git clone git://github.com/sunaku/testr
71
+ cd testr
72
+ bundle install
73
+
74
+ ------------------------------------------------------------------------------
75
+ Invocation
76
+ ------------------------------------------------------------------------------
77
+
78
+ If installed as a Ruby gem:
79
+
80
+ testr
81
+
82
+ If installed as a Git clone:
83
+
84
+ bundle exec ruby -Ilib bin/testr
85
+
86
+ You can monitor your test processes in another terminal:
87
+
88
+ watch 'ps xuw | sed -n "1p; /test[r]/p" | fgrep -v sed'
89
+
90
+ You can forcefully terminate TestR from another terminal:
91
+
92
+ pkill -f testr
93
+
94
+ ------------------------------------------------------------------------------
95
+ Configuration
96
+ ------------------------------------------------------------------------------
97
+
98
+ TestR looks for a configuration file named `.testr.rb` in its current working
99
+ directory. The configuration file is a normal Ruby script. Inside it, you
100
+ can query and modify the `TestR::Config` object (OpenStruct) according to the
101
+ configuration options listed below.
102
+
103
+ ------------------------------------------------------------------------------
104
+ Configuration options
105
+ ------------------------------------------------------------------------------
106
+
107
+ ### TestR::Config.max_forked_workers
108
+
109
+ Maximum number of worker processes at any given time. The default value is
110
+ the number of processors detected on your system, or 1 if detection fails.
111
+
112
+ ### TestR::Config.overhead_load_paths
113
+
114
+ Array of paths that are prepended to Ruby's `$LOAD_PATH` before the
115
+ test execution overhead is loaded into `testr-master`.
116
+
117
+ ### TestR::Config.overhead_file_globs
118
+
119
+ Array of file globbing patterns that describe a set of Ruby scripts that are
120
+ loaded into `testr-master` as test execution overhead.
121
+
122
+ ### TestR::Config.reabsorb_file_greps
123
+
124
+ Array of regular expressions that describe a set of file paths that cause the
125
+ test execution overhead to be reabsorbed in `testr-master` when they change.
126
+
127
+ ### TestR::Config.all_test_file_globs
128
+
129
+ Array of file globbing patterns that describe the set of all test files in
130
+ your Ruby application.
131
+
132
+ ### TestR::Config.test_file_globbers
133
+
134
+ Hash that maps (1) a regular expression describing a set of file paths to (2)
135
+ a lambda function yielding a file globbing pattern describing a set of
136
+ test files that need to be run. In other words, whenever the source files
137
+ (the hash key; left-hand side of the mapping) change, their associated test
138
+ files (the hash value; right-hand side of the mapping) are run.
139
+
140
+ For example, if test files had the same names as their source files followed
141
+ by an underscore and the file name in reverse like this:
142
+
143
+ * `lib/hello.rb` => `test/hello_olleh.rb`
144
+ * `app/world.rb` => `spec/world_ldrow.rb`
145
+
146
+ Then you would add the following to your configuration file:
147
+
148
+ TestR::Config.test_file_globbers[%r<^(lib|app)/.+\.rb$>] = lambda do |path|
149
+ name = File.basename(path, '.rb')
150
+ "{test,spec}/**/#{name}_#{name.reverse}.rb"
151
+ end
152
+
153
+ In addition, these lambda functions can return `nil` if they do not wish for a
154
+ particular source file to be tested. For example, to ignore tests for all
155
+ source files except those within a `models/` directory, you would write:
156
+
157
+ TestR::Config.test_file_globbers[%r<^(lib|app)/.+\.rb$>] = lambda do |path|
158
+ if path.include? '/models/'
159
+ "{test,spec}/**/#{File.basename(path)}"
160
+ end
161
+ end
162
+
163
+ ### TestR::Config.test_name_extractor
164
+
165
+ Lambda function that is given a line of source code to determine whether it
166
+ can be considered as a test definition. In which case, the function must
167
+ extract and return the name of the test being defined.
168
+
169
+ ### TestR::Config.before_fork_hooks
170
+
171
+ Array of lambda functions that are executed inside `testr-master` before a
172
+ worker process is forked to run a test file. These functions are given:
173
+
174
+ 1. The sequence number of the worker process that will be forked shortly.
175
+
176
+ 2. The path of the log file containing the live output of the worker process.
177
+
178
+ 3. The path of the test file that will be run by the worker process.
179
+
180
+ 4. An array of names of tests inside the test file that will be run. If this
181
+ array is empty, then all tests in the test file will be run.
182
+
183
+ For example, to see some real values:
184
+
185
+ TestR::Config.before_fork_hooks << lambda {
186
+ |worker_number, log_file, test_file, test_names|
187
+
188
+ p :before_fork_hooks => {
189
+ :worker_number => worker_number,
190
+ :log_file => log_file,
191
+ :test_file => test_file,
192
+ :test_names => test_names,
193
+ }
194
+ }
195
+
196
+ ### TestR::Config.after_fork_hooks
197
+
198
+ Array of lambda functions that are executed inside a worker process forked
199
+ by `testr-master`. These functions are given:
200
+
201
+ 1. The sequence number of the worker process.
202
+
203
+ 2. The path of the log file containing the live output of the worker process.
204
+
205
+ 3. The path of the test file that will be run by the worker process.
206
+
207
+ 4. An array of names of tests inside the test file that will be run. If this
208
+ array is empty, then all tests in the test file will be run.
209
+
210
+ For example, to see some real values, including the worker process' PID:
211
+
212
+ TestR::Config.after_fork_hooks << lambda {
213
+ |worker_number, log_file, test_file, test_names|
214
+
215
+ p :after_fork_hooks => {
216
+ :worker_pid => $$,
217
+ :worker_number => worker_number,
218
+ :log_file => log_file,
219
+ :test_file => test_file,
220
+ :test_names => test_names,
221
+ }
222
+ }
223
+
224
+ The first function in this array instructs Test::Unit and RSpec to only run
225
+ those tests that correspond to the given `test_names` values. This
226
+ accelerates your test-driven development cycle and improves productivity!
227
+
228
+ ------------------------------------------------------------------------------
229
+ Configuration helpers
230
+ ------------------------------------------------------------------------------
231
+
232
+ The following libraries assist you with configuring TestR. To use them,
233
+ simply add the require() lines shown below to your configuration file.
234
+
235
+ ### require 'testr/config/rails'
236
+
237
+ Support for the [Ruby on Rails](http://rubyonrails.org) web framework.
238
+
239
+ ### require 'testr/config/parallel_tests'
240
+
241
+ Support for the [parallel_tests](https://github.com/grosser/parallel_tests)
242
+ library.
243
+
244
+ ------------------------------------------------------------------------------
245
+ Known issues
246
+ ------------------------------------------------------------------------------
247
+
248
+ ### Ruby on Rails
249
+
250
+ * Ensure that your `config/environments/test.rb` file disables class caching
251
+ as follows (**NOTE:** if you are using Rails 3, the `testr/config/rails`
252
+ configuration helper can do this for you automatically):
253
+
254
+ config.cache_classes = false
255
+
256
+ Otherwise, TestR will appear to ignore source-code changes in your
257
+ models, controllers, helpers, and other Ruby source files.
258
+
259
+ * SQLite3 [raises `SQLite3::BusyException: database is locked` errors](
260
+ https://github.com/sunaku/test-loop/issues/2 ) because TestR runs your
261
+ test files in parallel. You can work around this by using an [in-memory
262
+ adapter for SQLite3]( https://github.com/mvz/memory_test_fix ) or by using
263
+ different database software (such as MySQL) for your test environment.
264
+
265
+ ------------------------------------------------------------------------------
266
+ License
267
+ ------------------------------------------------------------------------------
268
+
269
+ Released under the ISC license. See the LICENSE file for details.
@@ -0,0 +1 @@
1
+ require "bundler/gem_tasks"
@@ -0,0 +1,55 @@
1
+ #!/usr/bin/env ruby
2
+
3
+ require 'json'
4
+ require 'testr/client'
5
+
6
+ @driver = TestR::Client::Transceiver.new('testr-driver') do |line|
7
+ event, *details = JSON.load(line)
8
+
9
+ case event = event.to_sym
10
+ when :load then warn 'testr: Overhead absorbed; Ready for testing!'
11
+ when :over then warn 'testr: Reabsorbing changed overhead files...'
12
+ else
13
+ test_file, test_names, *details = details
14
+ message = [event.upcase, test_file, test_names.inspect, details].join(' ')
15
+
16
+ color = case event
17
+ when :pass then "\e[32m%s\e[0m" # green
18
+ when :fail then "\e[31m%s\e[0m" # red
19
+ end
20
+ message = color % message if color and STDOUT.tty?
21
+ message = [message, File.read(test_file + '.log'), message] if event == :fail
22
+
23
+ puts message
24
+ end
25
+ end
26
+
27
+ COMMANDS = {
28
+ 'r' => :run_all_test_files,
29
+ 's' => :stop_running_test_files,
30
+ 'p' => :rerun_passed_test_files,
31
+ 'f' => :rerun_failed_test_files,
32
+ 'o' => :reabsorb_overhead_files,
33
+ 'q' => :quit,
34
+ }
35
+
36
+ def COMMANDS.show
37
+ each {|key, cmd| warn "testr: Type #{key} to #{cmd.to_s.tr('_', ' ')}." }
38
+ end
39
+
40
+ COMMANDS.show # instruct newbies
41
+
42
+ while key = STDIN.gets.chomp
43
+ if command = COMMANDS[key]
44
+ if command == :quit
45
+ @driver.quit
46
+ break
47
+ else
48
+ @driver.send [command]
49
+ end
50
+ else
51
+ COMMANDS.show
52
+ end
53
+ end
54
+
55
+ Process.waitall
@@ -0,0 +1,4 @@
1
+ #!/usr/bin/env ruby
2
+ require 'testr/driver'
3
+ TestR::Driver.loop
4
+ Process.waitall
@@ -0,0 +1,9 @@
1
+ #!/usr/bin/env ruby
2
+ STDOUT.sync = true
3
+
4
+ require 'guard'
5
+ require 'guard/listener'
6
+
7
+ listener = Guard::Listener.select_and_init
8
+ listener.on_change {|files| puts files }
9
+ listener.start
@@ -0,0 +1,4 @@
1
+ #!/usr/bin/env ruby
2
+ require 'testr/master'
3
+ TestR::Master.loop
4
+ raise SystemExit # prevent empty test suite from running in the master process
@@ -0,0 +1,39 @@
1
+ module TestR
2
+ module Client
3
+
4
+ class Receiver < Thread
5
+ def initialize *popen_args
6
+ (@io = IO.popen(*popen_args)).sync = true
7
+ super() { loop { yield @io.gets } }
8
+ end
9
+
10
+ def quit
11
+ kill # stop the receive loop
12
+ Process.kill :SIGTERM, @io.pid
13
+ Process.wait @io.pid # reap zombie
14
+ @io.close # prevent subsequent I/O
15
+ end
16
+ end
17
+
18
+ class Transceiver < Receiver
19
+ def initialize *popen_args
20
+ @io_write_lock = Mutex.new
21
+ popen_args[1] = 'w+'
22
+ super
23
+ end
24
+
25
+ def send command
26
+ @io_write_lock.synchronize do
27
+ warn "#{caller[2]} SEND #{command.inspect}" if $DEBUG
28
+ @io.puts JSON.dump(command)
29
+ end
30
+ end
31
+
32
+ def quit
33
+ send [:quit]
34
+ super
35
+ end
36
+ end
37
+
38
+ end
39
+ end
@@ -0,0 +1,63 @@
1
+ require 'ostruct'
2
+
3
+ module TestR
4
+ _user_config_file = '.testr.rb'
5
+
6
+ Config = OpenStruct.new
7
+
8
+ Config.max_forked_workers = [
9
+ # http://stackoverflow.com/questions/891537#6420817
10
+ 'fgrep -c processor /proc/cpuinfo', # Linux
11
+ 'sysctl -n hw.ncpu', # BSD
12
+ 'hwprefs cpu_count', # Darwin 9
13
+ 'hwprefs thread_count', # Darwin 10
14
+ ].map {|cmd| `#{cmd} 2>/dev/null`.to_i }.push(1).max
15
+
16
+ Config.overhead_load_paths = ['lib', 'test', 'spec']
17
+
18
+ Config.overhead_file_globs = ['{test,spec}/{test,spec}_helper.rb']
19
+
20
+ Config.reabsorb_file_greps = [/^#{Regexp.quote(_user_config_file)}$/,
21
+ %r<(test|spec)/\1_helper\.rb>]
22
+
23
+ Config.all_test_file_globs = ['{test,spec}/**/*_{test,spec}.rb']
24
+
25
+ Config.test_file_globbers = {
26
+ # source files that correspond to test files
27
+ %r<^lib/.+\.rb$> => lambda do |path|
28
+ base = File.basename(path, '.rb')
29
+ "{test,spec}/**/#{base}_{test,spec}.rb"
30
+ end,
31
+
32
+ # the actual test files themselves
33
+ %r<^(test|spec)/.+_\1\.rb$> => lambda {|path| path }
34
+ }
35
+
36
+ Config.test_name_extractor = lambda do |line|
37
+ case line
38
+ when /^\s*def\s+test_(\w+)/ then $1
39
+ when /^\s*(test|context|should|describe|it)\b.+?(['"])(.*?)\2/ then $3
40
+ end
41
+ end
42
+
43
+ Config.before_fork_hooks = []
44
+
45
+ Config.after_fork_hooks = [
46
+ # tell testing framework to only run the named tests inside the test file
47
+ lambda do |worker_number, log_file, test_file, test_names|
48
+ unless test_names.empty?
49
+ regexp = Regexp.union(test_names.map {|name|
50
+ # sanitize string interpolations and invalid method name characters
51
+ name.gsub(/\#\{.*?\}/, ' ').strip.gsub(/\W+/, '.*')
52
+ })
53
+
54
+ case File.basename(test_file)
55
+ when /(\b|_)test(\b|_)/ then ARGV.push '--name', regexp.inspect
56
+ when /(\b|_)spec(\b|_)/ then ARGV.push '--example', regexp.source
57
+ end
58
+ end
59
+ end
60
+ ]
61
+
62
+ load _user_config_file if File.exist? _user_config_file
63
+ end