test-loop 4.0.1 → 5.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/README.md +23 -23
- data/bin/test-loop +22 -25
- metadata +4 -4
data/README.md
CHANGED
@@ -5,8 +5,8 @@ test-loop is a fast continuous testing tool for Ruby that continuously detects
|
|
5
5
|
and tests changes in your Ruby application in an efficient manner, whereby it:
|
6
6
|
|
7
7
|
1. Absorbs the test execution overhead into the main Ruby process.
|
8
|
-
2. Forks to
|
9
|
-
3.
|
8
|
+
2. Forks to run (eval) your test files directly, without overhead.
|
9
|
+
3. Avoids running unchanged test blocks inside changed test files.
|
10
10
|
|
11
11
|
It relies on file modification times to determine what parts of your Ruby
|
12
12
|
application have changed, applies a lambda mapping function to determine which
|
@@ -17,8 +17,8 @@ find and run only those test blocks that have changed inside your test files.
|
|
17
17
|
Features
|
18
18
|
--------
|
19
19
|
|
20
|
-
* Tests *changes* in your Ruby application:
|
21
|
-
|
20
|
+
* Tests *changes* in your Ruby application: avoids running (1) unchanged
|
21
|
+
test files and (2) unchanged test blocks inside changed test files.
|
22
22
|
|
23
23
|
* Reabsorbs test execution overhead if the test or spec helper file changes.
|
24
24
|
|
@@ -75,27 +75,27 @@ Configuration
|
|
75
75
|
-------------
|
76
76
|
|
77
77
|
test-loop looks for a configuration file named `.test-loop` in the current
|
78
|
-
working directory. This configuration file is a normal Ruby
|
79
|
-
|
78
|
+
working directory. This configuration file is a normal Ruby file whose last
|
79
|
+
statement yields a hash that may optionally contain the following entries:
|
80
80
|
|
81
|
-
*
|
81
|
+
* `:overhead_file_globs` is an array of file globbing patterns that describe a
|
82
82
|
set of Ruby scripts that are loaded into the main Ruby process as overhead.
|
83
83
|
|
84
|
-
*
|
84
|
+
* `:reabsorb_file_globs` is an array of file globbing patterns that describe a
|
85
85
|
set of files which cause the overhead to be reabsorbed whenever they change.
|
86
86
|
|
87
|
-
*
|
88
|
-
|
89
|
-
|
90
|
-
|
91
|
-
|
87
|
+
* `:test_file_matchers` is a hash that maps a file globbing pattern
|
88
|
+
describing a set of source files to a lambda function yielding a file
|
89
|
+
globbing pattern describing a set of test files that need to be run. In
|
90
|
+
other words, whenever the source files (the hash key; left-hand side of the
|
91
|
+
mapping) change, their associated test files (the hash value; right-hand
|
92
92
|
side of the mapping) are run.
|
93
93
|
|
94
94
|
For example, if test files had the same names as their source files but the
|
95
|
-
letters were in reverse order, then you would add the following
|
96
|
-
`.test-loop` file:
|
95
|
+
letters were in reverse order, then you would add the following hash entry
|
96
|
+
to your `.test-loop` file:
|
97
97
|
|
98
|
-
|
98
|
+
:test_file_matchers => {
|
99
99
|
'{lib,app}/**/*.rb' => lambda do |path|
|
100
100
|
extn = File.extname(path)
|
101
101
|
name = File.basename(path, extn)
|
@@ -103,11 +103,11 @@ define the following instance variables:
|
|
103
103
|
end
|
104
104
|
}
|
105
105
|
|
106
|
-
*
|
106
|
+
* `:test_name_parser` is a lambda function that is passed a line of source
|
107
107
|
code to determine whether that line can be considered as a test definition,
|
108
108
|
in which case it must return the name of the test being defined.
|
109
109
|
|
110
|
-
*
|
110
|
+
* `:before_each_test` is a lambda function that is executed inside the worker
|
111
111
|
process before loading the test file. It is passed the path to the test
|
112
112
|
file and the names of tests (identified by `@test_name_parser`) inside the
|
113
113
|
test file that have changed since the last time the test file was run.
|
@@ -119,13 +119,13 @@ define the following instance variables:
|
|
119
119
|
* `@after_all_tests` is a lambda function that is executed inside the master
|
120
120
|
process after all tests have finished running. It is passed four things:
|
121
121
|
whether all tests had passed, the time when test execution began, a list of
|
122
|
-
test files, and the exit statuses of the worker processes that
|
123
|
-
those test files.
|
122
|
+
test files, and the exit statuses of the worker processes that ran them.
|
124
123
|
|
125
124
|
For example, to display a summary of the test execution results as an OSD
|
126
|
-
notification via libnotify, add the following to your
|
125
|
+
notification via libnotify, add the following hash entry to your
|
126
|
+
`.test-loop` file:
|
127
127
|
|
128
|
-
|
128
|
+
:after_all_tests => lambda do |success, ran_at, files, statuses|
|
129
129
|
icon = success ? 'apple-green' : 'apple-red'
|
130
130
|
title = "#{success ? 'PASS' : 'FAIL'} at #{ran_at}"
|
131
131
|
details = files.zip(statuses).map do |file, status|
|
@@ -134,7 +134,7 @@ define the following instance variables:
|
|
134
134
|
system 'notify-send', '-i', icon, title, details.join("\n")
|
135
135
|
end
|
136
136
|
|
137
|
-
Also add the following at the
|
137
|
+
Also add the following at the top of the file if you use Ruby 1.9.x:
|
138
138
|
|
139
139
|
# encoding: utf-8
|
140
140
|
|
data/bin/test-loop
CHANGED
@@ -32,15 +32,16 @@ begin
|
|
32
32
|
# load user's configuration and supply default values
|
33
33
|
notify.call 'Loading configuration...'
|
34
34
|
config_file = File.join(Dir.pwd, '.test-loop')
|
35
|
-
|
35
|
+
config_data = File.read(config_file) if File.exist? config_file
|
36
|
+
config = eval(config_data.to_s, TOPLEVEL_BINDING, config_file) || {}
|
36
37
|
|
37
|
-
(
|
38
|
+
(config[:overhead_file_globs] ||= []).
|
38
39
|
push('{test,spec}/*{test,spec}_helper.rb').uniq!
|
39
40
|
|
40
|
-
(
|
41
|
+
(config[:reabsorb_file_globs] ||= []).concat(config[:overhead_file_globs]).
|
41
42
|
push(config_file, 'config/*.{rb,yml}', 'Gemfile').uniq!
|
42
43
|
|
43
|
-
(
|
44
|
+
(config[:test_file_matchers] ||= {}).merge!(
|
44
45
|
# source files that correspond to test files
|
45
46
|
'{lib,app}/**/*.rb' => lambda do |path|
|
46
47
|
extn = File.extname(path)
|
@@ -52,16 +53,14 @@ begin
|
|
52
53
|
'{test,spec}/**/*_{test,spec}.rb' => lambda {|path| path }
|
53
54
|
)
|
54
55
|
|
55
|
-
|
56
|
-
|
57
|
-
@test_name_parser ||= lambda do |line|
|
56
|
+
config[:test_name_parser] ||= lambda do |line|
|
58
57
|
case line
|
59
58
|
when /^\s*def test_(\w+)/ then $1
|
60
59
|
when /^\s*(test|context|should|describe|it)\b (['"])(.*?)\2/ then $3.strip
|
61
60
|
end
|
62
61
|
end
|
63
62
|
|
64
|
-
|
63
|
+
config[:before_each_test] ||= lambda do |test_file, test_names|
|
65
64
|
unless test_names.empty?
|
66
65
|
case File.basename(test_file)
|
67
66
|
|
@@ -75,31 +74,29 @@ begin
|
|
75
74
|
end
|
76
75
|
end
|
77
76
|
|
78
|
-
|
77
|
+
config[:after_all_tests] ||= lambda {|success, ran_at, files, statuses|}
|
79
78
|
|
80
79
|
# absorb test execution overhead into master process
|
81
80
|
$LOAD_PATH.unshift 'lib', 'test', 'spec'
|
82
81
|
|
83
82
|
notify.call 'Absorbing overhead...'
|
84
|
-
Dir[
|
83
|
+
Dir[*config[:overhead_file_globs]].each do |file|
|
85
84
|
require File.basename(file, File.extname(file))
|
86
85
|
end
|
87
86
|
|
88
87
|
# continuously watch for and test changed code
|
89
|
-
|
88
|
+
test_file_cache = {} # path => readlines
|
90
89
|
started_at = last_ran_at = Time.now
|
91
|
-
trap(:QUIT) { started_at =
|
92
|
-
trap(:TSTP) { last_ran_at =
|
90
|
+
trap(:QUIT) { started_at = Time.at(0) }
|
91
|
+
trap(:TSTP) { last_ran_at = Time.at(0); test_file_cache.clear }
|
93
92
|
|
94
93
|
notify.call 'Ready for testing!'
|
95
94
|
loop do
|
96
95
|
# figure out what test files need to be run
|
97
|
-
test_files =
|
98
|
-
|
99
|
-
|
100
|
-
|
101
|
-
map {|path| Dir[test_file_glob_mapper.call(path)] }
|
102
|
-
end.flatten.uniq
|
96
|
+
test_files = config[:test_file_matchers].map do |source_glob, test_matcher|
|
97
|
+
Dir[source_glob].select {|file| File.mtime(file) > last_ran_at }.
|
98
|
+
map {|path| Dir[test_matcher.call path] }
|
99
|
+
end.flatten.uniq
|
103
100
|
|
104
101
|
# fork worker processes to run the test files in parallel
|
105
102
|
unless test_files.empty?
|
@@ -109,8 +106,8 @@ begin
|
|
109
106
|
test_files.each do |test_file|
|
110
107
|
# cache the contents of the test file for diffing below
|
111
108
|
new_lines = File.readlines(test_file)
|
112
|
-
old_lines =
|
113
|
-
|
109
|
+
old_lines = test_file_cache[test_file] || new_lines
|
110
|
+
test_file_cache[test_file] = new_lines
|
114
111
|
|
115
112
|
fork do
|
116
113
|
# determine which test blocks have changed inside the test file
|
@@ -119,14 +116,14 @@ begin
|
|
119
116
|
# search backwards from the line that changed up to
|
120
117
|
# the first line in the file for test definitions
|
121
118
|
diff[0][1].downto(0) do |i| # [[+/-, line number, line value]]
|
122
|
-
if test_name =
|
119
|
+
if test_name = config[:test_name_parser].call(new_lines[i])
|
123
120
|
throw :found, test_name
|
124
121
|
end
|
125
122
|
end; nil # prevent unsuccessful search from returning an integer
|
126
123
|
end
|
127
124
|
end.compact.uniq
|
128
125
|
|
129
|
-
|
126
|
+
config[:before_each_test].call test_file, test_names
|
130
127
|
|
131
128
|
load test_file
|
132
129
|
|
@@ -147,11 +144,11 @@ begin
|
|
147
144
|
end
|
148
145
|
notify.call "Ran in #{Time.now - last_ran_at} seconds"
|
149
146
|
|
150
|
-
|
147
|
+
config[:after_all_tests].call success, last_ran_at, test_files, statuses
|
151
148
|
end
|
152
149
|
|
153
150
|
# reabsorb test execution overhead as necessary
|
154
|
-
if Dir[
|
151
|
+
if Dir[*config[:reabsorb_file_globs]].any? {|file| File.mtime(file) > started_at }
|
155
152
|
notify.call 'Restarting loop...'
|
156
153
|
exec(*process_invocation_vector)
|
157
154
|
end
|
metadata
CHANGED
@@ -3,10 +3,10 @@ name: test-loop
|
|
3
3
|
version: !ruby/object:Gem::Version
|
4
4
|
prerelease: false
|
5
5
|
segments:
|
6
|
-
-
|
6
|
+
- 5
|
7
7
|
- 0
|
8
|
-
-
|
9
|
-
version:
|
8
|
+
- 0
|
9
|
+
version: 5.0.0
|
10
10
|
platform: ruby
|
11
11
|
authors:
|
12
12
|
- Suraj N. Kurapati
|
@@ -14,7 +14,7 @@ autorequire:
|
|
14
14
|
bindir: bin
|
15
15
|
cert_chain: []
|
16
16
|
|
17
|
-
date: 2011-01-
|
17
|
+
date: 2011-01-17 00:00:00 -08:00
|
18
18
|
default_executable:
|
19
19
|
dependencies:
|
20
20
|
- !ruby/object:Gem::Dependency
|