test-loop 12.0.0 → 12.0.1
Sign up to get free protection for your applications and to get access to all the features.
- data/README.md +34 -24
- data/lib/test/loop.rb +23 -25
- metadata +2 -2
data/README.md
CHANGED
@@ -4,39 +4,49 @@ test-loop - Continuous testing for Ruby with fork/eval
|
|
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:
|
6
6
|
|
7
|
-
1. Absorbs the test execution overhead into the main Ruby process.
|
8
|
-
|
9
|
-
|
7
|
+
1. Absorbs the test execution overhead into the main Ruby process.
|
8
|
+
|
9
|
+
2. Forks to run your test files without overhead and in parallel.
|
10
|
+
|
11
|
+
3. Avoids running unchanged test blocks inside changed test files.
|
10
12
|
|
11
13
|
------------------------------------------------------------------------------
|
12
14
|
Features
|
13
15
|
------------------------------------------------------------------------------
|
14
16
|
|
15
|
-
* Tests *changes* in your Ruby application: avoids running (1) unchanged
|
16
|
-
|
17
|
+
* Tests *changes* in your Ruby application: avoids running (1) unchanged
|
18
|
+
test files and (2) unchanged test blocks inside changed test files.
|
17
19
|
|
18
|
-
* Supports Test::Unit, RSpec, and any other testing framework that (1)
|
19
|
-
|
20
|
-
|
20
|
+
* Supports Test::Unit, RSpec, and any other testing framework that (1)
|
21
|
+
reflects failures in the process' exit status and (2) is loaded by your
|
22
|
+
application's `test/test_helper.rb` or `spec/spec_helper.rb` file.
|
21
23
|
|
22
|
-
* Reabsorbs test execution overhead if the test or spec helper file changes.
|
24
|
+
* Reabsorbs test execution overhead if the test or spec helper file changes.
|
23
25
|
|
24
|
-
* Executes test files in parallel, making full use of multiple processors.
|
26
|
+
* Executes test files in parallel, making full use of multiple processors.
|
25
27
|
|
26
|
-
* Logs the output from your tests into separate files: one log per test
|
27
|
-
|
28
|
+
* Logs the output from your tests into separate files: one log per test.
|
29
|
+
The path to a log file is simply the path of its test file plus ".log".
|
28
30
|
|
29
|
-
* Generally I/O bound, so you can
|
31
|
+
* Generally I/O bound, so you can keep it running without CPU slowdown.
|
30
32
|
|
31
|
-
* Configurable through a `.test-loop` file in your
|
33
|
+
* Configurable through a `.test-loop` file in your working directory.
|
32
34
|
|
33
|
-
* Implemented in less than
|
35
|
+
* Implemented in less than 220 lines (SLOC) of pure Ruby code! :-)
|
34
36
|
|
35
37
|
------------------------------------------------------------------------------
|
36
38
|
Prerequisites
|
37
39
|
------------------------------------------------------------------------------
|
38
40
|
|
39
|
-
*
|
41
|
+
* Ruby 1.8.7 or 1.9.2 or newer.
|
42
|
+
|
43
|
+
* Operating system that supports POSIX signals and the `fork()` system call.
|
44
|
+
|
45
|
+
To check if your system qualifies, launch `irb` and enter the following:
|
46
|
+
|
47
|
+
Process.respond_to? :fork # must be true
|
48
|
+
|
49
|
+
Signal.list.keys & %w[INT TSTP QUIT TERM CHLD] # must not be empty
|
40
50
|
|
41
51
|
------------------------------------------------------------------------------
|
42
52
|
Installation
|
@@ -65,7 +75,7 @@ If installed as a Git clone:
|
|
65
75
|
|
66
76
|
You can monitor your test processes in another terminal:
|
67
77
|
|
68
|
-
watch 'ps xf |
|
78
|
+
watch 'ps xf | fgrep test-loop | fgrep -v fgrep'
|
69
79
|
|
70
80
|
If it stops responding, you can annihilate test-loop from another terminal:
|
71
81
|
|
@@ -75,13 +85,13 @@ If it stops responding, you can annihilate test-loop from another terminal:
|
|
75
85
|
Operation
|
76
86
|
------------------------------------------------------------------------------
|
77
87
|
|
78
|
-
* Press Control-Z or send the SIGTSTP signal to forcibly run all
|
79
|
-
|
88
|
+
* Press Control-Z or send the SIGTSTP signal to forcibly run all
|
89
|
+
tests, even if there are no changes in your Ruby application.
|
80
90
|
|
81
|
-
* Press Control-\ or send the SIGQUIT signal to forcibly reabsorb
|
82
|
-
|
91
|
+
* Press Control-\ or send the SIGQUIT signal to forcibly reabsorb
|
92
|
+
the test execution overhead, even if its sources have not changed.
|
83
93
|
|
84
|
-
* Press Control-C or send the SIGINT signal to quit the test loop.
|
94
|
+
* Press Control-C or send the SIGINT signal to quit the test loop.
|
85
95
|
|
86
96
|
------------------------------------------------------------------------------
|
87
97
|
Configuration
|
@@ -112,8 +122,8 @@ hash key; left-hand side of the mapping) change, their associated test files
|
|
112
122
|
For example, if test files had the same names as their source files followed
|
113
123
|
by an underscore and the file name in reverse like this:
|
114
124
|
|
115
|
-
* `lib/hello.rb` => `test/hello_olleh.rb`
|
116
|
-
* `app/world.rb` => `spec/world_ldrow.rb`
|
125
|
+
* `lib/hello.rb` => `test/hello_olleh.rb`
|
126
|
+
* `app/world.rb` => `spec/world_ldrow.rb`
|
117
127
|
|
118
128
|
Then you would add the following to your configuration file:
|
119
129
|
|
data/lib/test/loop.rb
CHANGED
@@ -70,9 +70,9 @@ module Test
|
|
70
70
|
|
71
71
|
private
|
72
72
|
|
73
|
-
|
73
|
+
MASTER_EXECV = [$0, *ARGV].map {|s| s.dup.freeze }.freeze
|
74
|
+
MASTER_ENV = Hash[ENV.map {|k,v| [k.freeze, v.freeze] }].freeze
|
74
75
|
RESUME_ENV_KEY = 'TEST_LOOP_RESUME_FILES'.freeze
|
75
|
-
MASTER_ENV = ENV.to_hash.delete_if {|k,v| k == RESUME_ENV_KEY }.freeze
|
76
76
|
|
77
77
|
ANSI_CLEAR_LINE = "\e[2K\e[0G".freeze
|
78
78
|
ANSI_GREEN = "\e[32m%s\e[0m".freeze
|
@@ -97,7 +97,7 @@ module Test
|
|
97
97
|
def register_signals
|
98
98
|
# this signal is ignored in master and honored in workers, so all
|
99
99
|
# workers can be killed by sending it to the entire process group
|
100
|
-
trap :TERM,
|
100
|
+
trap :TERM, 'IGNORE'
|
101
101
|
|
102
102
|
trap :CHLD do
|
103
103
|
finished_at = Time.now
|
@@ -106,24 +106,22 @@ module Test
|
|
106
106
|
worker_pid = Process.wait
|
107
107
|
run_status = $?
|
108
108
|
|
109
|
-
worker = @worker_by_pid.delete(worker_pid)
|
110
|
-
|
111
|
-
|
112
|
-
|
113
|
-
|
114
|
-
|
115
|
-
|
116
|
-
|
117
|
-
|
118
|
-
|
119
|
-
|
120
|
-
|
121
|
-
|
122
|
-
|
109
|
+
if worker = @worker_by_pid.delete(worker_pid)
|
110
|
+
@running_files.delete worker.test_file
|
111
|
+
|
112
|
+
# report test results along with any failure logs
|
113
|
+
if run_status.success?
|
114
|
+
notify ANSI_GREEN % "PASS #{worker.test_file}"
|
115
|
+
elsif run_status.exited?
|
116
|
+
notify ANSI_RED % "FAIL #{worker.test_file}"
|
117
|
+
STDERR.print File.read(worker.log_file)
|
118
|
+
end
|
119
|
+
|
120
|
+
after_each_test.each do |hook|
|
121
|
+
hook.call worker.test_file, worker.log_file, run_status,
|
122
|
+
worker.started_at, finished_at - worker.started_at
|
123
|
+
end
|
123
124
|
end
|
124
|
-
|
125
|
-
@running_files.delete worker.test_file
|
126
|
-
|
127
125
|
rescue Errno::ECHILD
|
128
126
|
# could not get the terminated child's PID.
|
129
127
|
# Ruby's backtick operator can cause this:
|
@@ -132,8 +130,8 @@ module Test
|
|
132
130
|
end
|
133
131
|
|
134
132
|
trap(:INT) { raise Interrupt }
|
135
|
-
trap(:QUIT) { reload_master_process }
|
136
133
|
trap(:TSTP) { forcibly_run_all_tests }
|
134
|
+
trap(:QUIT) { reload_master_process }
|
137
135
|
end
|
138
136
|
|
139
137
|
def kill_workers
|
@@ -147,8 +145,8 @@ module Test
|
|
147
145
|
def reload_master_process test_files = []
|
148
146
|
test_files.concat @running_files
|
149
147
|
kill_workers
|
150
|
-
|
151
|
-
|
148
|
+
ENV.replace MASTER_ENV.merge(RESUME_ENV_KEY => test_files.inspect)
|
149
|
+
exec(*MASTER_EXECV)
|
152
150
|
end
|
153
151
|
|
154
152
|
def load_user_config
|
@@ -226,10 +224,10 @@ module Test
|
|
226
224
|
started_at = Time.now
|
227
225
|
worker_pid = fork do
|
228
226
|
# handle signals meant for worker process
|
229
|
-
[:TERM, :CHLD].each {|sig| trap sig,
|
227
|
+
[:TERM, :CHLD].each {|sig| trap sig, 'DEFAULT' }
|
230
228
|
|
231
229
|
# ignore signals meant for master process
|
232
|
-
[:INT, :
|
230
|
+
[:INT, :TSTP, :QUIT].each {|sig| trap sig, 'IGNORE' }
|
233
231
|
|
234
232
|
# detach worker from master's terminal device so that
|
235
233
|
# it does not receieve the user's control-key presses
|
metadata
CHANGED
@@ -2,7 +2,7 @@
|
|
2
2
|
name: test-loop
|
3
3
|
version: !ruby/object:Gem::Version
|
4
4
|
prerelease:
|
5
|
-
version: 12.0.
|
5
|
+
version: 12.0.1
|
6
6
|
platform: ruby
|
7
7
|
authors:
|
8
8
|
- Suraj N. Kurapati
|
@@ -11,7 +11,7 @@ autorequire:
|
|
11
11
|
bindir: bin
|
12
12
|
cert_chain: []
|
13
13
|
|
14
|
-
date: 2011-04-
|
14
|
+
date: 2011-04-20 00:00:00 -07:00
|
15
15
|
default_executable:
|
16
16
|
dependencies:
|
17
17
|
- !ruby/object:Gem::Dependency
|