test-loop 9.2.0 → 9.3.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/README.md +50 -34
- data/lib/test/loop.rb +58 -51
- metadata +3 -13
data/README.md
CHANGED
@@ -1,5 +1,5 @@
|
|
1
1
|
test-loop - Continuous testing for Ruby with fork/eval
|
2
|
-
|
2
|
+
==============================================================================
|
3
3
|
|
4
4
|
test-loop is a fast continuous testing tool for Ruby that automatically
|
5
5
|
detects and tests changes in your application in an efficient manner:
|
@@ -8,9 +8,9 @@ detects and tests changes in your application in an efficient manner:
|
|
8
8
|
2. Forks to run your test files without overhead and in parallel.
|
9
9
|
3. Avoids running unchanged test blocks inside changed test files.
|
10
10
|
|
11
|
-
|
11
|
+
------------------------------------------------------------------------------
|
12
12
|
Features
|
13
|
-
|
13
|
+
------------------------------------------------------------------------------
|
14
14
|
|
15
15
|
* Tests *changes* in your Ruby application: avoids running (1) unchanged
|
16
16
|
test files and (2) unchanged test blocks inside changed test files.
|
@@ -30,11 +30,11 @@ Features
|
|
30
30
|
|
31
31
|
* Configurable through a `.test-loop` file in your current working directory.
|
32
32
|
|
33
|
-
* Implemented in less than
|
34
|
-
|
33
|
+
* Implemented in less than 200 lines (SLOC) of pure Ruby code! :-)
|
35
34
|
|
35
|
+
------------------------------------------------------------------------------
|
36
36
|
Installation
|
37
|
-
|
37
|
+
------------------------------------------------------------------------------
|
38
38
|
|
39
39
|
As a Ruby gem:
|
40
40
|
|
@@ -43,12 +43,11 @@ As a Ruby gem:
|
|
43
43
|
As a Git clone:
|
44
44
|
|
45
45
|
gem install diff-lcs -v '>= 1.1.2'
|
46
|
-
gem install ansi -v '>= 1.2.2'
|
47
46
|
git clone git://github.com/sunaku/test-loop
|
48
47
|
|
49
|
-
|
48
|
+
------------------------------------------------------------------------------
|
50
49
|
Invocation
|
51
|
-
|
50
|
+
------------------------------------------------------------------------------
|
52
51
|
|
53
52
|
If installed as a Ruby gem:
|
54
53
|
|
@@ -58,9 +57,13 @@ If installed as a Git clone:
|
|
58
57
|
|
59
58
|
env RUBYLIB=lib ruby bin/test-loop
|
60
59
|
|
60
|
+
You can monitor your test processes in another terminal:
|
61
|
+
|
62
|
+
watch "ps xf | egrep 'test-loop|(test|spec)/.+\.rb' | sed '1,3d'"
|
61
63
|
|
64
|
+
------------------------------------------------------------------------------
|
62
65
|
Operation
|
63
|
-
|
66
|
+
------------------------------------------------------------------------------
|
64
67
|
|
65
68
|
* Press Control-Z or send the SIGTSTP signal to forcibly run all
|
66
69
|
tests, even if there are no changes in your Ruby application.
|
@@ -70,25 +73,9 @@ Operation
|
|
70
73
|
|
71
74
|
* Press Control-C or send the SIGINT signal to quit the test loop.
|
72
75
|
|
73
|
-
|
74
|
-
Configuration Presets
|
75
|
-
---------------------
|
76
|
-
|
77
|
-
The following sub-libraries provide "preset" configurations. To use them,
|
78
|
-
simply add the require() lines shown below to your `.test-loop` file or to
|
79
|
-
your `{test,spec}/{test,spec}_helper.rb` files.
|
80
|
-
|
81
|
-
* Defaults for Ruby on Rails testing:
|
82
|
-
|
83
|
-
require 'test/loop/rails'
|
84
|
-
|
85
|
-
* OSD notifications on test failures:
|
86
|
-
|
87
|
-
require 'test/loop/notify'
|
88
|
-
|
89
|
-
|
76
|
+
------------------------------------------------------------------------------
|
90
77
|
Configuration
|
91
|
-
|
78
|
+
------------------------------------------------------------------------------
|
92
79
|
|
93
80
|
test-loop looks for a configuration file named `.test-loop` in the current
|
94
81
|
working directory. This configuration file is a normal Ruby script in which
|
@@ -112,8 +99,8 @@ you can query and modify the `Test::Loop` OpenStruct configuration as follows:
|
|
112
99
|
For example, if test files had the same names as their source files followed
|
113
100
|
by an underscore and the file name in reverse like this:
|
114
101
|
|
115
|
-
* lib/hello.rb => test/hello_olleh.rb
|
116
|
-
* app/world.rb => spec/world_ldrow.rb
|
102
|
+
* `lib/hello.rb` => `test/hello_olleh.rb`
|
103
|
+
* `app/world.rb` => `spec/world_ldrow.rb`
|
117
104
|
|
118
105
|
Then you would add the following to your configuration file:
|
119
106
|
|
@@ -161,6 +148,13 @@ you can query and modify the `Test::Loop` OpenStruct configuration as follows:
|
|
161
148
|
execution began, and (5) how many seconds it took for the overall test
|
162
149
|
execution to complete.
|
163
150
|
|
151
|
+
For example, to delete log files for successful tests, add the following to
|
152
|
+
your configuration file:
|
153
|
+
|
154
|
+
Test::Loop.after_each_test = lambda do |test_file, log_file, run_status, started_at, elapsed_time|
|
155
|
+
File.delete(log_file) if run_status.success?
|
156
|
+
end
|
157
|
+
|
164
158
|
For example, to see on-screen-display notifications only about test
|
165
159
|
failures, add the following to your configuration file:
|
166
160
|
|
@@ -178,6 +172,10 @@ you can query and modify the `Test::Loop` OpenStruct configuration as follows:
|
|
178
172
|
end
|
179
173
|
end
|
180
174
|
|
175
|
+
Note that the above functionality is available as a configuration preset:
|
176
|
+
|
177
|
+
require 'test/loop/notify'
|
178
|
+
|
181
179
|
For example, to see on-screen-display notifications about completed test
|
182
180
|
runs, regardless of whether they passed or failed, add the following to your
|
183
181
|
configuration file:
|
@@ -197,9 +195,27 @@ you can query and modify the `Test::Loop` OpenStruct configuration as follows:
|
|
197
195
|
end
|
198
196
|
end
|
199
197
|
|
198
|
+
------------------------------------------------------------------------------
|
199
|
+
Configuration Presets
|
200
|
+
------------------------------------------------------------------------------
|
200
201
|
|
202
|
+
The following sub-libraries provide "preset" configurations. To use them,
|
203
|
+
simply add the require() lines shown below to your `.test-loop` file or to
|
204
|
+
your application's `test/test_helper.rb` or `spec/spec_helper.rb` file.
|
205
|
+
|
206
|
+
* Support for Ruby on Rails testing:
|
207
|
+
|
208
|
+
require 'test/loop/rails'
|
209
|
+
|
210
|
+
* On-screen-display notifications for test failures:
|
211
|
+
|
212
|
+
require 'test/loop/notify'
|
213
|
+
|
214
|
+
------------------------------------------------------------------------------
|
201
215
|
Known issues
|
202
|
-
|
216
|
+
------------------------------------------------------------------------------
|
217
|
+
|
218
|
+
If using Ruby on Rails:
|
203
219
|
|
204
220
|
* Ensure that your `config/environments/test.rb` file disables class caching:
|
205
221
|
|
@@ -214,8 +230,8 @@ Known issues
|
|
214
230
|
adapter for SQLite3]( https://github.com/mvz/memory_test_fix ) or by using
|
215
231
|
different database software (such as MySQL) for your test environment.
|
216
232
|
|
217
|
-
|
233
|
+
------------------------------------------------------------------------------
|
218
234
|
License
|
219
|
-
|
235
|
+
------------------------------------------------------------------------------
|
220
236
|
|
221
|
-
Released under the ISC license. See the
|
237
|
+
Released under the ISC license. See the LICENSE file for details.
|
data/lib/test/loop.rb
CHANGED
@@ -1,29 +1,5 @@
|
|
1
|
-
#
|
2
|
-
# test-loop - Continuous testing for Ruby with fork/eval
|
3
|
-
# https://github.com/sunaku/test-loop#readme
|
4
|
-
#
|
5
|
-
####
|
6
|
-
#
|
7
|
-
# (the ISC license)
|
8
|
-
#
|
9
|
-
# Copyright 2010 Suraj N. Kurapati <sunaku@gmail.com>
|
10
|
-
#
|
11
|
-
# Permission to use, copy, modify, and/or distribute this software for any
|
12
|
-
# purpose with or without fee is hereby granted, provided that the above
|
13
|
-
# copyright notice and this permission notice appear in all copies.
|
14
|
-
#
|
15
|
-
# THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
|
16
|
-
# WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
|
17
|
-
# MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
|
18
|
-
# ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
|
19
|
-
# WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
|
20
|
-
# ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
|
21
|
-
# OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
|
22
|
-
#
|
23
|
-
|
24
1
|
require 'ostruct'
|
25
2
|
require 'diff/lcs'
|
26
|
-
require 'ansi'
|
27
3
|
|
28
4
|
module Test
|
29
5
|
Loop = OpenStruct.new
|
@@ -77,6 +53,9 @@ module Test
|
|
77
53
|
load_user_config
|
78
54
|
absorb_overhead
|
79
55
|
run_test_loop
|
56
|
+
rescue SystemExit
|
57
|
+
# allow exit() to terminate the test loop
|
58
|
+
notify 'Goodbye!'
|
80
59
|
rescue Exception => error
|
81
60
|
STDERR.puts error.inspect, error.backtrace
|
82
61
|
pause_momentarily
|
@@ -86,31 +65,47 @@ module Test
|
|
86
65
|
private
|
87
66
|
|
88
67
|
EXEC_VECTOR = [$0, *ARGV].map {|s| s.dup.freeze }.freeze
|
68
|
+
RESUME_ENV_KEY = 'TEST_LOOP_RESUME_FILES'.freeze
|
69
|
+
|
70
|
+
ANSI_CLEAR_LINE = "\e[2K\e[0G".freeze
|
71
|
+
ANSI_GREEN = "\e[32m%s\e[0m".freeze
|
72
|
+
ANSI_RED = "\e[31m%s\e[0m".freeze
|
89
73
|
|
90
74
|
def notify message
|
91
75
|
# using print() because puts() is not an atomic operation
|
92
76
|
print "test-loop: #{message}\n"
|
93
77
|
end
|
94
78
|
|
79
|
+
def init_test_loop
|
80
|
+
@running_files = []
|
81
|
+
@running_files_lock = Mutex.new
|
82
|
+
@lines_by_file = {} # path => readlines
|
83
|
+
@last_ran_at = @started_at = Time.now
|
84
|
+
end
|
85
|
+
|
95
86
|
def register_signals
|
96
|
-
#
|
87
|
+
# clear line to shield normal output from control-key interference:
|
97
88
|
# some shells like BASH emit text when control-key combos are pressed
|
98
|
-
trap(:INT)
|
99
|
-
trap(:QUIT) {
|
100
|
-
trap(:TSTP) {
|
101
|
-
end
|
89
|
+
trap(:INT) { print ANSI_CLEAR_LINE; kill_workers; exit }
|
90
|
+
trap(:QUIT) { print ANSI_CLEAR_LINE; reload_master_process }
|
91
|
+
trap(:TSTP) { print ANSI_CLEAR_LINE; forcibly_run_all_tests }
|
102
92
|
|
103
|
-
|
104
|
-
|
93
|
+
master_pid = $$
|
94
|
+
trap(:TERM) { exit unless $$ == master_pid }
|
105
95
|
end
|
106
96
|
|
107
|
-
def
|
108
|
-
notify '
|
109
|
-
|
97
|
+
def kill_workers
|
98
|
+
notify 'Stopping tests...'
|
99
|
+
Process.kill :TERM, -$$
|
100
|
+
Process.waitall
|
110
101
|
end
|
111
102
|
|
112
|
-
|
113
|
-
|
103
|
+
# The given test files are passed down (along with currently running
|
104
|
+
# test files) to the next incarnation of test-loop for resumption.
|
105
|
+
def reload_master_process test_files = []
|
106
|
+
@running_files_lock.synchronize { test_files.concat @running_files }
|
107
|
+
kill_workers
|
108
|
+
exec({RESUME_ENV_KEY => test_files.inspect}, *EXEC_VECTOR)
|
114
109
|
end
|
115
110
|
|
116
111
|
def load_user_config
|
@@ -128,14 +123,12 @@ module Test
|
|
128
123
|
end
|
129
124
|
end
|
130
125
|
|
131
|
-
def
|
132
|
-
|
133
|
-
@running_files_lock = Mutex.new
|
134
|
-
@lines_by_file = {} # path => readlines
|
135
|
-
@last_ran_at = @started_at = Time.now
|
126
|
+
def pause_momentarily
|
127
|
+
sleep 1
|
136
128
|
end
|
137
129
|
|
138
130
|
def forcibly_run_all_tests
|
131
|
+
notify 'Running all tests...'
|
139
132
|
@last_ran_at = Time.at(0)
|
140
133
|
@lines_by_file.clear
|
141
134
|
end
|
@@ -143,22 +136,36 @@ module Test
|
|
143
136
|
def run_test_loop
|
144
137
|
notify 'Ready for testing!'
|
145
138
|
loop do
|
146
|
-
#
|
139
|
+
# find test files that have been modified since the last run
|
147
140
|
test_files = test_file_matchers.map do |source_glob, test_matcher|
|
148
141
|
Dir[source_glob].select {|file| File.mtime(file) > @last_ran_at }.
|
149
142
|
map {|path| Dir[test_matcher.call path] }
|
150
143
|
end.flatten.uniq
|
151
144
|
|
145
|
+
# resume test files stopped by the previous incarnation of test-loop
|
146
|
+
if ENV.key? RESUME_ENV_KEY
|
147
|
+
resume_files = eval(ENV.delete(RESUME_ENV_KEY))
|
148
|
+
unless resume_files.empty?
|
149
|
+
notify 'Resuming tests...'
|
150
|
+
test_files.concat(resume_files).uniq!
|
151
|
+
end
|
152
|
+
end
|
153
|
+
|
154
|
+
# reabsorb test execution overhead as necessary
|
155
|
+
if Dir[*reabsorb_file_globs].any? {|f| File.mtime(f) > @started_at }
|
156
|
+
notify 'Overhead changed!'
|
157
|
+
reload_master_process test_files
|
158
|
+
end
|
159
|
+
|
160
|
+
# fork workers to run the test files in parallel,
|
161
|
+
# excluding test files that are already running
|
152
162
|
test_files = @running_files_lock.
|
153
163
|
synchronize { test_files - @running_files }
|
154
164
|
|
155
|
-
|
156
|
-
|
157
|
-
|
158
|
-
|
159
|
-
# reabsorb test execution overhead as necessary
|
160
|
-
reload_master_process if Dir[*reabsorb_file_globs].
|
161
|
-
any? {|file| File.mtime(file) > @started_at }
|
165
|
+
unless test_files.empty?
|
166
|
+
@last_ran_at = Time.now
|
167
|
+
test_files.each {|file| run_test_file file }
|
168
|
+
end
|
162
169
|
|
163
170
|
pause_momentarily
|
164
171
|
end
|
@@ -213,9 +220,9 @@ module Test
|
|
213
220
|
|
214
221
|
# report test results along with any failure logs
|
215
222
|
if run_status.success?
|
216
|
-
notify
|
223
|
+
notify ANSI_GREEN % "PASS #{test_file}"
|
217
224
|
else
|
218
|
-
notify
|
225
|
+
notify ANSI_RED % "FAIL #{test_file}"
|
219
226
|
STDERR.print File.read(log_file)
|
220
227
|
end
|
221
228
|
|
metadata
CHANGED
@@ -2,15 +2,16 @@
|
|
2
2
|
name: test-loop
|
3
3
|
version: !ruby/object:Gem::Version
|
4
4
|
prerelease:
|
5
|
-
version: 9.
|
5
|
+
version: 9.3.0
|
6
6
|
platform: ruby
|
7
7
|
authors:
|
8
8
|
- Suraj N. Kurapati
|
9
|
+
- Brian D. Burns
|
9
10
|
autorequire:
|
10
11
|
bindir: bin
|
11
12
|
cert_chain: []
|
12
13
|
|
13
|
-
date: 2011-
|
14
|
+
date: 2011-04-01 00:00:00 -07:00
|
14
15
|
default_executable:
|
15
16
|
dependencies:
|
16
17
|
- !ruby/object:Gem::Dependency
|
@@ -24,17 +25,6 @@ dependencies:
|
|
24
25
|
version: 1.1.2
|
25
26
|
type: :runtime
|
26
27
|
version_requirements: *id001
|
27
|
-
- !ruby/object:Gem::Dependency
|
28
|
-
name: ansi
|
29
|
-
prerelease: false
|
30
|
-
requirement: &id002 !ruby/object:Gem::Requirement
|
31
|
-
none: false
|
32
|
-
requirements:
|
33
|
-
- - ">="
|
34
|
-
- !ruby/object:Gem::Version
|
35
|
-
version: 1.2.2
|
36
|
-
type: :runtime
|
37
|
-
version_requirements: *id002
|
38
28
|
description:
|
39
29
|
email:
|
40
30
|
executables:
|