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.
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: