test-loop 9.2.0 → 9.3.0

Sign up to get free protection for your applications and to get access to all the features.
Files changed (3) hide show
  1. data/README.md +50 -34
  2. data/lib/test/loop.rb +58 -51
  3. 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 150 lines (SLOC) of pure Ruby code! :-)
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 `bin/test-loop` file for details.
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
- # puts newline to shield normal output from control-key interference:
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) { puts; kill_master_and_workers }
99
- trap(:QUIT) { puts; reload_master_process }
100
- trap(:TSTP) { puts; forcibly_run_all_tests }
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
- def kill_master_and_workers
104
- Process.kill :KILL, -$$
93
+ master_pid = $$
94
+ trap(:TERM) { exit unless $$ == master_pid }
105
95
  end
106
96
 
107
- def reload_master_process
108
- notify 'Restarting loop...'
109
- exec(*EXEC_VECTOR)
97
+ def kill_workers
98
+ notify 'Stopping tests...'
99
+ Process.kill :TERM, -$$
100
+ Process.waitall
110
101
  end
111
102
 
112
- def pause_momentarily
113
- sleep 1
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 init_test_loop
132
- @running_files = []
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
- # figure out what test files need to be run
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
- # fork worker processes to run the test files in parallel
156
- @last_ran_at = Time.now
157
- test_files.each {|file| run_test_file file }
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 ANSI::Code.green("PASS #{test_file}")
223
+ notify ANSI_GREEN % "PASS #{test_file}"
217
224
  else
218
- notify ANSI::Code.red("FAIL #{test_file}")
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.2.0
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-03-28 00:00:00 -07:00
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: