tconsole 1.1.0pre8 → 1.1.0pre9
Sign up to get free protection for your applications and to get access to all the features.
- data/README.md +25 -38
- data/drb_connection_test.rb +63 -0
- data/lib/tconsole.rb +30 -154
- data/lib/tconsole/config.rb +30 -0
- data/lib/tconsole/console.rb +136 -0
- data/lib/tconsole/minitest_handler.rb +39 -19
- data/lib/tconsole/server.rb +18 -61
- data/lib/tconsole/test_result.rb +28 -13
- data/lib/tconsole/version.rb +1 -1
- metadata +6 -4
data/README.md
CHANGED
@@ -29,9 +29,9 @@ willing to merge it in, though.
|
|
29
29
|
Installing tconsole
|
30
30
|
------
|
31
31
|
gem install tconsole
|
32
|
-
|
32
|
+
|
33
33
|
Prereleases of tconsole come out pretty frequently. You can install the latest prerelease version with:
|
34
|
-
|
34
|
+
|
35
35
|
gem installt console --pre
|
36
36
|
|
37
37
|
How to use tconsole
|
@@ -39,68 +39,55 @@ How to use tconsole
|
|
39
39
|
In your shell of choice, cd into your Rails project's directory and then run `bundle exec tconsole` to fire up the console. You should see something like this:
|
40
40
|
|
41
41
|
bundle exec tconsole
|
42
|
-
|
42
|
+
|
43
43
|
Loading your Rails environment...
|
44
44
|
Environment loaded in 7.160264s.
|
45
|
-
|
46
|
-
>
|
47
|
-
|
45
|
+
|
46
|
+
>
|
47
|
+
|
48
48
|
Now that you're in the console, let's test out the all command! Running all from the console runs all of your unit, functional, and integration tests:
|
49
49
|
|
50
50
|
> all
|
51
51
|
Running tests...
|
52
|
-
|
53
|
-
Run options:
|
52
|
+
|
53
|
+
Run options:
|
54
54
|
|
55
55
|
# Running tests:
|
56
56
|
|
57
57
|
....................................................................................
|
58
|
-
|
58
|
+
|
59
59
|
Finished tests in 6.054574s, 6.4999 tests/s, 10.5822 assertions/s.
|
60
60
|
|
61
61
|
39 tests, 45 assertions, 0 failures, 0 errors, 0 skips
|
62
62
|
|
63
63
|
Test time (including load): 82.806741s
|
64
|
-
|
65
|
-
>
|
66
|
-
|
67
|
-
If you want to focus in on a particular subset of your tests, like units, functionals, or integration, just enter that keyword:
|
68
64
|
|
69
|
-
>
|
70
|
-
|
71
|
-
> functionals
|
72
|
-
|
73
|
-
> integration
|
74
|
-
|
65
|
+
>
|
75
66
|
|
76
|
-
If you
|
77
|
-
you've made:
|
67
|
+
If you want to focus in on a particular subset of your tests, like units, functionals, or integration, just enter that keyword:
|
78
68
|
|
79
|
-
>
|
69
|
+
> units
|
80
70
|
|
81
|
-
|
82
|
-
last commit:
|
71
|
+
> functionals
|
83
72
|
|
84
|
-
>
|
73
|
+
> integration
|
85
74
|
|
86
|
-
You can also
|
75
|
+
You can also specify to run all tests in a specific class:
|
87
76
|
|
88
|
-
>
|
77
|
+
> UserTest
|
89
78
|
|
90
|
-
You can go one bit deeper and just run a particular test in that file
|
91
|
-
|
79
|
+
You can go one bit deeper and just run a particular test in that file as
|
80
|
+
well:
|
92
81
|
|
93
|
-
>
|
82
|
+
> UserTest#test_that_user_is_healthy
|
94
83
|
|
95
|
-
|
96
|
-
|
97
|
-
argument to any tconsole command that runs tests.
|
84
|
+
You can list more than just one class or class and method to run, and
|
85
|
+
tconsole will run them all.
|
98
86
|
|
99
|
-
There are a few special ! commands that use data from past test runs. The `!failed` command will rerun
|
100
|
-
that included failing tests in the last test run:
|
87
|
+
There are a few special ! commands that use data from past test runs. The `!failed` command will rerun the set of tests that failed during the last run:
|
101
88
|
|
102
89
|
> !failed
|
103
|
-
|
90
|
+
|
104
91
|
There's also a `!timings` command that will show you a listing of your last test run's test times, sorted to help you
|
105
92
|
improve slow tests:
|
106
93
|
|
@@ -125,11 +112,11 @@ improve slow tests:
|
|
125
112
|
If you update your environment, maybe by editing your Gemfile or changing one of your application's configuration files, you can use the `reload` command to reload the entire environment:
|
126
113
|
|
127
114
|
> reload
|
128
|
-
|
115
|
+
|
129
116
|
And then finally, you can run the `exit` command to quit:
|
130
117
|
|
131
118
|
> exit
|
132
|
-
|
119
|
+
|
133
120
|
Reporting Issues and Contributing
|
134
121
|
------
|
135
122
|
|
@@ -0,0 +1,63 @@
|
|
1
|
+
#!/usr/bin/env ruby
|
2
|
+
|
3
|
+
require "drb/drb"
|
4
|
+
|
5
|
+
class Server
|
6
|
+
def connected?
|
7
|
+
true
|
8
|
+
end
|
9
|
+
|
10
|
+
def load_environment
|
11
|
+
ENV['RAILS_ENV'] ||= "test"
|
12
|
+
|
13
|
+
require './config/application'
|
14
|
+
|
15
|
+
::Rails.application
|
16
|
+
::Rails::Engine.class_eval do
|
17
|
+
def eager_load!
|
18
|
+
# turn off eager_loading
|
19
|
+
end
|
20
|
+
end
|
21
|
+
|
22
|
+
true
|
23
|
+
end
|
24
|
+
|
25
|
+
def stop
|
26
|
+
DRb.stop_service
|
27
|
+
end
|
28
|
+
end
|
29
|
+
|
30
|
+
socket = "/tmp/test.#{Process.pid}"
|
31
|
+
|
32
|
+
server_pid = fork do
|
33
|
+
server = Server.new
|
34
|
+
|
35
|
+
drb_server = DRb.start_service("drbunix:#{socket}", server)
|
36
|
+
DRb.thread.join
|
37
|
+
end
|
38
|
+
|
39
|
+
wait_until = Time.now + 10
|
40
|
+
DRb.start_service
|
41
|
+
|
42
|
+
loaded = false
|
43
|
+
until loaded || Time.now > wait_until
|
44
|
+
begin
|
45
|
+
puts "Trying to load environment"
|
46
|
+
server = DRbObject.new_with_uri("drbunix:#{socket}")
|
47
|
+
loaded = server.connected?
|
48
|
+
rescue
|
49
|
+
puts "Couldn't connect. Waiting."
|
50
|
+
sleep(1)
|
51
|
+
end
|
52
|
+
end
|
53
|
+
|
54
|
+
if !loaded
|
55
|
+
puts "Wasn't able to connect"
|
56
|
+
end
|
57
|
+
|
58
|
+
puts "Connected! Attempting to load environment"
|
59
|
+
server.load_environment
|
60
|
+
puts "Environment loaded!"
|
61
|
+
|
62
|
+
# Clean it all up
|
63
|
+
server.stop
|
data/lib/tconsole.rb
CHANGED
@@ -1,5 +1,6 @@
|
|
1
1
|
require "tconsole/version"
|
2
2
|
require "tconsole/config"
|
3
|
+
require "tconsole/console"
|
3
4
|
require "tconsole/server"
|
4
5
|
require "tconsole/test_result"
|
5
6
|
require "tconsole/util"
|
@@ -8,24 +9,7 @@ require "readline"
|
|
8
9
|
require "benchmark"
|
9
10
|
require "drb/drb"
|
10
11
|
require "term/ansicolor"
|
11
|
-
|
12
|
-
Readline.completion_append_character = ""
|
13
|
-
|
14
|
-
# Proc for helping us figure out autocompletes
|
15
|
-
Readline.completion_proc = Proc.new do |str|
|
16
|
-
known_commands = TConsole::Console::KNOWN_COMMANDS.grep(/^#{Regexp.escape(str)}/)
|
17
|
-
|
18
|
-
files = Dir[str+'*'].grep(/^#{Regexp.escape(str)}/)
|
19
|
-
formatted_files = files.collect do |filename|
|
20
|
-
if File.directory?(filename)
|
21
|
-
filename + File::SEPARATOR
|
22
|
-
else
|
23
|
-
filename
|
24
|
-
end
|
25
|
-
end
|
26
|
-
|
27
|
-
known_commands.concat(formatted_files)
|
28
|
-
end
|
12
|
+
require "shellwords"
|
29
13
|
|
30
14
|
module TConsole
|
31
15
|
class Runner
|
@@ -42,14 +26,12 @@ module TConsole
|
|
42
26
|
puts
|
43
27
|
puts "Welcome to tconsole (v#{TConsole::VERSION}). Type 'help' for help or 'exit' to quit."
|
44
28
|
|
45
|
-
# Set up our console input handling and history
|
46
|
-
console = Console.new
|
47
|
-
|
48
29
|
# set up the config
|
49
30
|
config = Config.configure
|
50
31
|
config.trace_execution = true if argv.include?("--trace")
|
51
32
|
|
52
|
-
|
33
|
+
# Set up our console input handling and history
|
34
|
+
console = Console.new(config)
|
53
35
|
|
54
36
|
# Start the server
|
55
37
|
while running
|
@@ -60,7 +42,7 @@ module TConsole
|
|
60
42
|
begin
|
61
43
|
server = Server.new(config)
|
62
44
|
|
63
|
-
drb_server = DRb.start_service("
|
45
|
+
drb_server = DRb.start_service("drbunix:/tmp/tconsole.#{Process.pid}", server)
|
64
46
|
DRb.thread.join
|
65
47
|
rescue Interrupt
|
66
48
|
# do nothing here since the outer process will shut things down for us
|
@@ -72,34 +54,41 @@ module TConsole
|
|
72
54
|
# Set up our client connection to the server
|
73
55
|
config.trace("Connecting to testing server.")
|
74
56
|
DRb.start_service
|
75
|
-
server =
|
57
|
+
server = nil
|
76
58
|
|
77
59
|
loaded = false
|
78
60
|
until loaded || Time.now > wait_until
|
79
61
|
begin
|
80
|
-
|
81
|
-
|
82
|
-
|
83
|
-
|
84
|
-
|
85
|
-
|
86
|
-
config.trace(
|
87
|
-
|
88
|
-
loaded = false
|
89
|
-
rescue Interrupt
|
90
|
-
# do nothing if we get an interrupt
|
91
|
-
puts "Interrupted in client"
|
62
|
+
server = DRbObject.new_with_uri("drbunix:/tmp/tconsole.#{server_pid}")
|
63
|
+
|
64
|
+
config.trace("Testing connection to test server.")
|
65
|
+
loaded = server.connected?
|
66
|
+
rescue
|
67
|
+
# do nothing
|
68
|
+
config.trace("Not connected to server yet. Retrying.")
|
69
|
+
sleep(1)
|
92
70
|
end
|
93
|
-
|
94
|
-
# Give drb a second to get set up
|
95
|
-
sleep(1)
|
96
71
|
end
|
97
72
|
|
98
|
-
|
73
|
+
unless loaded
|
99
74
|
puts
|
100
|
-
puts "Couldn't connect to test environment. Exiting."
|
75
|
+
puts "Couldn't connect to the test environment. Exiting."
|
101
76
|
exit(1)
|
102
77
|
end
|
78
|
+
|
79
|
+
begin
|
80
|
+
config.trace("Attempting to load environment.")
|
81
|
+
running = server.load_environment
|
82
|
+
rescue => e
|
83
|
+
config.trace("Could not load environment: #{e.message}")
|
84
|
+
config.trace("==== Backtrace ====")
|
85
|
+
config.trace(e.backtrace.join("\n"))
|
86
|
+
config.trace("==== End Backtrace ====")
|
87
|
+
|
88
|
+
puts "Couldn't load the test environment. Exiting."
|
89
|
+
exit(1)
|
90
|
+
end
|
91
|
+
|
103
92
|
config.trace("Environment loaded successfully.")
|
104
93
|
|
105
94
|
running = console.read_and_execute(server) if running
|
@@ -115,119 +104,6 @@ module TConsole
|
|
115
104
|
system("stty", stty_save);
|
116
105
|
end
|
117
106
|
end
|
118
|
-
|
119
|
-
class Console
|
120
|
-
KNOWN_COMMANDS = ["exit", "reload", "help", "units", "functionals", "integration", "recent", "uncommitted", "all", "info", "!failed", "!timings", "set"]
|
121
|
-
|
122
|
-
def initialize
|
123
|
-
read_history
|
124
|
-
end
|
125
|
-
|
126
|
-
# Returns true if the app should keep running, false otherwise
|
127
|
-
def read_and_execute(server)
|
128
|
-
while line = Readline.readline("tconsole> ", false)
|
129
|
-
# TODO: Avoid pushing duplicate commands onto the history
|
130
|
-
Readline::HISTORY << line
|
131
|
-
|
132
|
-
line.strip!
|
133
|
-
args = line.split(/\s/)
|
134
|
-
|
135
|
-
if line == ""
|
136
|
-
# do nothing
|
137
|
-
elsif args[0] == "exit"
|
138
|
-
return false
|
139
|
-
elsif args[0] == "reload"
|
140
|
-
return true
|
141
|
-
elsif args[0] == "help"
|
142
|
-
print_help
|
143
|
-
elsif args[0] == "units" || args[0] == "unit"
|
144
|
-
server.run_tests(["test/unit/**/*_test.rb"], args[1])
|
145
|
-
elsif args[0] == "functionals" || args[0] == "functional"
|
146
|
-
server.run_tests(["test/functional/**/*_test.rb"], args[1])
|
147
|
-
elsif args[0] == "integration"
|
148
|
-
server.run_tests(["test/integration/**/*_test.rb"], args[1])
|
149
|
-
elsif args[0] == "recent"
|
150
|
-
server.run_recent(args[1])
|
151
|
-
elsif args[0] == "uncommitted"
|
152
|
-
server.run_uncommitted(args[1])
|
153
|
-
elsif args[0] == "all"
|
154
|
-
server.run_tests(["test/unit/**/*_test.rb", "test/functional/**/*_test.rb", "test/integration/**/*_test.rb"], args[1])
|
155
|
-
elsif args[0] == "!failed"
|
156
|
-
server.run_failed
|
157
|
-
elsif args[0] == "!timings"
|
158
|
-
server.show_performance(args[1])
|
159
|
-
elsif args[0] == "info"
|
160
|
-
server.run_info
|
161
|
-
elsif args[0] == "set"
|
162
|
-
server.set(args[1], args[2])
|
163
|
-
else
|
164
|
-
server.run_tests([args[0]], args[1])
|
165
|
-
end
|
166
|
-
end
|
167
|
-
|
168
|
-
true
|
169
|
-
end
|
170
|
-
|
171
|
-
# Prints a list of available commands
|
172
|
-
def print_help
|
173
|
-
puts
|
174
|
-
puts "Available commands:"
|
175
|
-
puts
|
176
|
-
puts "all [test_pattern] # Run all test types (units, functionals, integration)"
|
177
|
-
puts "units [test_pattern] # Run unit tests"
|
178
|
-
puts "functionals [test_pattern] # Run functional tests"
|
179
|
-
puts "integration [test_pattern] # Run integration tests"
|
180
|
-
puts "recent [test_pattern] # Run tests for recently changed files"
|
181
|
-
puts "uncommitted [test_pattern] # Run tests for uncommitted changes"
|
182
|
-
puts "!failed # Runs the last set of failing tests"
|
183
|
-
puts "!timings [limit] # Lists the timings for the last test run, sorted."
|
184
|
-
puts "[filename] [test_pattern] # Run the tests contained in the given file"
|
185
|
-
puts "reload # Reload your Rails environment"
|
186
|
-
puts "set [variable] [value] # Sets a runtime variable (see below for details)"
|
187
|
-
puts "exit # Exit the console"
|
188
|
-
puts
|
189
|
-
puts "Working with test patterns:"
|
190
|
-
puts
|
191
|
-
puts "All of the test execution commands include an optional test_pattern argument. A"
|
192
|
-
puts "test pattern can be given to filter the executed tests to only those tests whose"
|
193
|
-
puts "name matches the pattern given. This is especially useful when rerunning a failing"
|
194
|
-
puts "test."
|
195
|
-
puts
|
196
|
-
puts "Runtime Variables"
|
197
|
-
puts
|
198
|
-
puts "You can set runtime variables with the set command. This helps out with changing"
|
199
|
-
puts "features of TConsole that you may want to change at runtime. At present, the"
|
200
|
-
puts "following runtime variables are available:"
|
201
|
-
puts
|
202
|
-
puts "fast # Turns on fail fast mode. Values: on, off"
|
203
|
-
puts
|
204
|
-
|
205
|
-
end
|
206
|
-
|
207
|
-
def history_file
|
208
|
-
File.join(ENV['HOME'], ".tconsole_history")
|
209
|
-
end
|
210
|
-
|
211
|
-
# Stores last 50 items in history to $HOME/.tconsole_history
|
212
|
-
def store_history
|
213
|
-
if ENV['HOME']
|
214
|
-
File.open(history_file, "w") do |f|
|
215
|
-
Readline::HISTORY.to_a.reverse[0..49].each do |item|
|
216
|
-
f.puts(item)
|
217
|
-
end
|
218
|
-
end
|
219
|
-
end
|
220
|
-
end
|
221
|
-
|
222
|
-
# Loads history from past sessions
|
223
|
-
def read_history
|
224
|
-
if ENV['HOME'] && File.exist?(history_file)
|
225
|
-
File.readlines(history_file).reverse.each do |line|
|
226
|
-
Readline::HISTORY << line
|
227
|
-
end
|
228
|
-
end
|
229
|
-
end
|
230
|
-
end
|
231
107
|
end
|
232
108
|
|
233
109
|
|
data/lib/tconsole/config.rb
CHANGED
@@ -22,16 +22,31 @@ module TConsole
|
|
22
22
|
# test fails. Defaults to false.
|
23
23
|
attr_accessor :fail_fast
|
24
24
|
|
25
|
+
# Defines the file set commands that are available
|
26
|
+
attr_accessor :file_sets
|
27
|
+
|
28
|
+
# Counts of tests in suites
|
29
|
+
attr_accessor :cached_suite_counts
|
30
|
+
|
31
|
+
# Element names we know
|
32
|
+
attr_accessor :cached_elements
|
33
|
+
|
25
34
|
def initialize
|
26
35
|
self.trace_execution = false
|
27
36
|
self.test_dir = "./test"
|
28
37
|
self.include_paths = ["./test", "./lib"]
|
29
38
|
self.preload_paths = []
|
30
39
|
self.fail_fast = false
|
40
|
+
self.file_sets = {
|
41
|
+
"all" => ["#{test_dir}/**/*_test.rb"]
|
42
|
+
}
|
31
43
|
|
32
44
|
@after_load = nil
|
33
45
|
@before_load = nil
|
34
46
|
@before_test_run = nil
|
47
|
+
|
48
|
+
@cached_suite_counts = {}
|
49
|
+
@cached_elements = {}
|
35
50
|
end
|
36
51
|
|
37
52
|
def trace?
|
@@ -71,12 +86,27 @@ module TConsole
|
|
71
86
|
@before_test_run.call unless @before_test_run.nil?
|
72
87
|
end
|
73
88
|
|
89
|
+
def cache_test_ids(result)
|
90
|
+
self.cached_suite_counts = result.suite_counts
|
91
|
+
self.cached_elements = result.elements
|
92
|
+
end
|
93
|
+
|
74
94
|
# Returns an appropriate tconsole config based on the environment
|
75
95
|
def self.configure
|
76
96
|
if is_rails?
|
77
97
|
config = Config.new
|
78
98
|
config.preload_paths = ["./config/application"]
|
79
99
|
config.include_paths = ["./test"]
|
100
|
+
config.file_sets = {
|
101
|
+
"all" => ["#{config.test_dir}/unit/**/*_test.rb", "#{config.test_dir}/functional/**/*_test.rb",
|
102
|
+
"#{config.test_dir}/integration/**/*_test.rb"],
|
103
|
+
"units" => ["#{config.test_dir}/unit/**/*_test.rb"],
|
104
|
+
"unit" => ["#{config.test_dir}/unit/**/*_test.rb"],
|
105
|
+
"functionals" => ["#{config.test_dir}/functional/**/*_test.rb"],
|
106
|
+
"functional" => ["#{config.test_dir}/functional/**/*_test.rb"],
|
107
|
+
"integration" => ["#{config.test_dir}/integration/**/*_test.rb"]
|
108
|
+
}
|
109
|
+
|
80
110
|
|
81
111
|
config.before_load do
|
82
112
|
ENV["RAILS_ENV"] ||= "test"
|
@@ -0,0 +1,136 @@
|
|
1
|
+
module TConsole
|
2
|
+
class Console
|
3
|
+
KNOWN_COMMANDS = ["exit", "reload", "help", "info", "!failed", "!timings", "set"]
|
4
|
+
|
5
|
+
def initialize(config)
|
6
|
+
@config = config
|
7
|
+
read_history
|
8
|
+
|
9
|
+
define_autocomplete
|
10
|
+
end
|
11
|
+
|
12
|
+
def define_autocomplete
|
13
|
+
Readline.completion_append_character = ""
|
14
|
+
|
15
|
+
# Proc for helping us figure out autocompletes
|
16
|
+
Readline.completion_proc = Proc.new do |str|
|
17
|
+
known_commands = KNOWN_COMMANDS.grep(/^#{Regexp.escape(str)}/)
|
18
|
+
|
19
|
+
files = Dir[str+'*'].grep(/^#{Regexp.escape(str)}/)
|
20
|
+
formatted_files = files.collect do |filename|
|
21
|
+
if File.directory?(filename)
|
22
|
+
filename + File::SEPARATOR
|
23
|
+
else
|
24
|
+
filename
|
25
|
+
end
|
26
|
+
end
|
27
|
+
|
28
|
+
known_commands.concat(formatted_files).concat(@config.file_sets.keys)
|
29
|
+
end
|
30
|
+
end
|
31
|
+
|
32
|
+
# Returns true if the app should keep running, false otherwise
|
33
|
+
def read_and_execute(server)
|
34
|
+
while line = Readline.readline("tconsole> ", false)
|
35
|
+
line.strip!
|
36
|
+
args = Shellwords.shellwords(line)
|
37
|
+
|
38
|
+
# save the line unless we're exiting or repeating the last command
|
39
|
+
unless args[0] == "exit" || Readline::HISTORY[Readline::HISTORY.length - 1] == line
|
40
|
+
Readline::HISTORY << line
|
41
|
+
end
|
42
|
+
|
43
|
+
if line == ""
|
44
|
+
# do nothing
|
45
|
+
elsif args[0] == "exit"
|
46
|
+
return false
|
47
|
+
elsif args[0] == "reload"
|
48
|
+
return true
|
49
|
+
elsif args[0] == "help"
|
50
|
+
print_help
|
51
|
+
elsif args[0] == "!failed"
|
52
|
+
server.run_failed
|
53
|
+
elsif args[0] == "!timings"
|
54
|
+
server.show_performance(args[1])
|
55
|
+
elsif args[0] == "info"
|
56
|
+
server.run_info
|
57
|
+
elsif args[0] == "set"
|
58
|
+
server.set(args[1], args[2])
|
59
|
+
elsif @config.file_sets.has_key?(args[0])
|
60
|
+
server.run_file_set(args[0])
|
61
|
+
else
|
62
|
+
server.run_all_tests(args)
|
63
|
+
end
|
64
|
+
end
|
65
|
+
|
66
|
+
true
|
67
|
+
end
|
68
|
+
|
69
|
+
# Prints a list of available commands
|
70
|
+
def print_help
|
71
|
+
puts
|
72
|
+
puts "Available commands:"
|
73
|
+
puts
|
74
|
+
puts "reload # Reload your Rails environment"
|
75
|
+
puts "set [variable] [value] # Sets a runtime variable (see below for details)"
|
76
|
+
puts "exit # Exit the console"
|
77
|
+
puts "!failed # Runs the last set of failing tests"
|
78
|
+
puts "!timings [limit] # Lists the timings for the last test run, sorted."
|
79
|
+
puts "[filename] [test_pattern] # Run the tests contained in the given file"
|
80
|
+
puts
|
81
|
+
puts "Running file sets"
|
82
|
+
puts
|
83
|
+
puts "File sets are sets of files that are typically run together. For example,"
|
84
|
+
puts "in Rails projects it's common to run `rake test:units` to run all of the"
|
85
|
+
puts "tests under the units directory."
|
86
|
+
puts
|
87
|
+
puts "Available file sets:"
|
88
|
+
|
89
|
+
@config.file_sets.each do |set, paths|
|
90
|
+
puts set
|
91
|
+
end
|
92
|
+
|
93
|
+
puts
|
94
|
+
puts "Working with test patterns:"
|
95
|
+
puts
|
96
|
+
puts "All of the test execution commands include an optional test_pattern argument. A"
|
97
|
+
puts "test pattern can be given to filter the executed tests to only those tests whose"
|
98
|
+
puts "name matches the pattern given. This is especially useful when rerunning a failing"
|
99
|
+
puts "test."
|
100
|
+
puts
|
101
|
+
puts "Runtime Variables"
|
102
|
+
puts
|
103
|
+
puts "You can set runtime variables with the set command. This helps out with changing"
|
104
|
+
puts "features of TConsole that you may want to change at runtime. At present, the"
|
105
|
+
puts "following runtime variables are available:"
|
106
|
+
puts
|
107
|
+
puts "fast # Turns on fail fast mode. Values: on, off"
|
108
|
+
puts
|
109
|
+
|
110
|
+
end
|
111
|
+
|
112
|
+
def history_file
|
113
|
+
File.join(ENV['HOME'], ".tconsole_history")
|
114
|
+
end
|
115
|
+
|
116
|
+
# Stores last 50 items in history to $HOME/.tconsole_history
|
117
|
+
def store_history
|
118
|
+
if ENV['HOME']
|
119
|
+
File.open(history_file, "w") do |f|
|
120
|
+
Readline::HISTORY.to_a.reverse[0..49].each do |item|
|
121
|
+
f.puts(item)
|
122
|
+
end
|
123
|
+
end
|
124
|
+
end
|
125
|
+
end
|
126
|
+
|
127
|
+
# Loads history from past sessions
|
128
|
+
def read_history
|
129
|
+
if ENV['HOME'] && File.exist?(history_file)
|
130
|
+
File.readlines(history_file).reverse.each do |line|
|
131
|
+
Readline::HISTORY << line
|
132
|
+
end
|
133
|
+
end
|
134
|
+
end
|
135
|
+
end
|
136
|
+
end
|
@@ -1,21 +1,16 @@
|
|
1
1
|
module TConsole
|
2
2
|
class MiniTestHandler
|
3
|
-
def self.run(
|
4
|
-
args = []
|
5
|
-
unless name_pattern.nil?
|
6
|
-
args = ["--name", name_pattern]
|
7
|
-
end
|
8
|
-
|
3
|
+
def self.run(match_patterns, config)
|
9
4
|
# Make sure we have a recent version of minitest, and use it
|
10
5
|
if ::MiniTest::Unit.respond_to?(:runner=)
|
11
|
-
::MiniTest::Unit.runner = TConsole::MiniTestUnit.new(config)
|
6
|
+
::MiniTest::Unit.runner = TConsole::MiniTestUnit.new(match_patterns, config)
|
12
7
|
else
|
13
8
|
raise "MiniTest v#{MiniTest::Unit::VERSION} is not compatible with tconsole. Please load a more recent version of MiniTest"
|
14
9
|
end
|
15
10
|
|
16
11
|
# Run it
|
17
12
|
runner = MiniTest::Unit.runner
|
18
|
-
runner.run
|
13
|
+
runner.run
|
19
14
|
|
20
15
|
# Make sure that minitest doesn't run automatically when the process exits
|
21
16
|
patch_minitest
|
@@ -48,12 +43,18 @@ module TConsole
|
|
48
43
|
"P" => ::Term::ANSIColor.green
|
49
44
|
}
|
50
45
|
|
51
|
-
attr_accessor :config, :results
|
46
|
+
attr_accessor :match_patterns, :config, :results
|
47
|
+
|
48
|
+
def initialize(match_patterns, config)
|
49
|
+
self.match_patterns = match_patterns
|
50
|
+
self.match_patterns = [] unless self.match_patterns.is_a?(Array)
|
52
51
|
|
53
|
-
def initialize(config)
|
54
52
|
self.config = config
|
55
53
|
self.results = TConsole::TestResult.new
|
56
54
|
|
55
|
+
results.suite_counts = config.cached_suite_counts
|
56
|
+
results.elements = config.cached_elements
|
57
|
+
|
57
58
|
super()
|
58
59
|
end
|
59
60
|
|
@@ -96,21 +97,39 @@ module TConsole
|
|
96
97
|
end
|
97
98
|
|
98
99
|
def _run_suite(suite, type)
|
100
|
+
@last_suite ||= nil
|
99
101
|
@failed_fast ||= false
|
100
102
|
|
101
|
-
|
102
|
-
|
103
|
+
assertions = suite.send("#{type}_methods").map do |method|
|
104
|
+
skip = false
|
105
|
+
|
106
|
+
# Get our unique id for this particular element
|
107
|
+
id = results.add_element(suite, method)
|
108
|
+
suite_id = results.elements[suite.to_s]
|
109
|
+
|
110
|
+
# If we're using failed fast mode and we already failed, just return
|
111
|
+
skip = true if @failed_fast
|
103
112
|
|
104
|
-
|
105
|
-
if
|
113
|
+
# If we've got match patterns, see if this matches them
|
114
|
+
if !match_patterns.empty?
|
115
|
+
match = match_patterns.find do |pattern|
|
116
|
+
pattern == suite.to_s || pattern == "#{suite.to_s}##{method.to_s}" || pattern == suite_id || pattern == id
|
117
|
+
end
|
118
|
+
|
119
|
+
skip = true unless match
|
120
|
+
end
|
121
|
+
|
122
|
+
if skip
|
106
123
|
0
|
107
124
|
else
|
108
125
|
inst = suite.new method
|
109
126
|
inst._assertions = 0
|
110
127
|
|
111
128
|
# Print the suite name if needed
|
112
|
-
|
113
|
-
print("\n\n", ::Term::ANSIColor.cyan, suite, ::Term::ANSIColor.reset,
|
129
|
+
unless @last_suite == suite
|
130
|
+
print("\n\n", ::Term::ANSIColor.cyan, suite, ::Term::ANSIColor.reset,
|
131
|
+
::Term::ANSIColor.magenta, " #{suite_id}", ::Term::ANSIColor.reset, "\n")
|
132
|
+
@last_suite = suite
|
114
133
|
end
|
115
134
|
|
116
135
|
@start_time = Time.now
|
@@ -120,13 +139,16 @@ module TConsole
|
|
120
139
|
|
121
140
|
result = "P" if result == "."
|
122
141
|
|
142
|
+
results.failures << id unless result == "P"
|
143
|
+
|
123
144
|
if config.fail_fast && result != "P"
|
124
145
|
@failed_fast = true
|
125
146
|
end
|
126
147
|
|
127
148
|
output = "#{result} #{method}"
|
128
149
|
|
129
|
-
print COLOR_MAP[result], " #{output}", ::Term::ANSIColor.reset, " #{time}s
|
150
|
+
print COLOR_MAP[result], " #{output}", ::Term::ANSIColor.reset, " #{time}s ",
|
151
|
+
::Term::ANSIColor.magenta, "#{id}", ::Term::ANSIColor.reset, "\n"
|
130
152
|
|
131
153
|
if @failed_fast
|
132
154
|
print "\n", COLOR_MAP["E"], "Halting tests because of failure.", ::Term::ANSIColor.reset, "\n"
|
@@ -149,12 +171,10 @@ module TConsole
|
|
149
171
|
when MiniTest::Assertion then
|
150
172
|
@failures += 1
|
151
173
|
results.failures += 1
|
152
|
-
results.append_failure_details(klass, meth)
|
153
174
|
"Failure:\n#{meth}(#{klass}) [#{location e}]:\n#{e.message}\n"
|
154
175
|
else
|
155
176
|
@errors += 1
|
156
177
|
results.errors += 1
|
157
|
-
results.append_failure_details(klass, meth)
|
158
178
|
bt = MiniTest::filter_backtrace(e.backtrace).join "\n "
|
159
179
|
"Error:\n#{meth}(#{klass}):\n#{e.class}: #{e.message}\n #{bt}\n"
|
160
180
|
end
|
data/lib/tconsole/server.rb
CHANGED
@@ -7,6 +7,11 @@ module TConsole
|
|
7
7
|
self.last_result = TConsole::TestResult.new
|
8
8
|
end
|
9
9
|
|
10
|
+
# Basically just a noop that helps us figure out if we're connected or not
|
11
|
+
def connected?
|
12
|
+
true
|
13
|
+
end
|
14
|
+
|
10
15
|
def stop
|
11
16
|
DRb.stop_service
|
12
17
|
end
|
@@ -51,7 +56,9 @@ module TConsole
|
|
51
56
|
result
|
52
57
|
end
|
53
58
|
|
54
|
-
|
59
|
+
# Loads the files that match globs and then executes tests against them. Limit tests
|
60
|
+
# with class names, method names, and test ids using match_patterns.
|
61
|
+
def run_tests(globs, match_patterns, message = "Running tests...")
|
55
62
|
time = Benchmark.realtime do
|
56
63
|
# Pipe for communicating with child so we can get its results back
|
57
64
|
read, write = IO.pipe
|
@@ -81,7 +88,7 @@ module TConsole
|
|
81
88
|
require File.join(File.dirname(__FILE__), "minitest_handler")
|
82
89
|
|
83
90
|
config.trace("Running tests.")
|
84
|
-
result = MiniTestHandler.run(
|
91
|
+
result = MiniTestHandler.run(match_patterns, config)
|
85
92
|
config.trace("Finished running tests.")
|
86
93
|
|
87
94
|
config.trace("Writing test results back to server.")
|
@@ -102,6 +109,7 @@ module TConsole
|
|
102
109
|
begin
|
103
110
|
config.trace("Reading test results from console.")
|
104
111
|
self.last_result = Marshal.load(response.unpack("m")[0])
|
112
|
+
config.cache_test_ids(self.last_result)
|
105
113
|
config.trace("Finished reading test results from console.")
|
106
114
|
rescue => e
|
107
115
|
config.trace("Exception: #{e.message}")
|
@@ -109,7 +117,6 @@ module TConsole
|
|
109
117
|
config.trace(e.backtrace.join("\n"))
|
110
118
|
config.trace("==== End Backtrace ====")
|
111
119
|
|
112
|
-
|
113
120
|
puts "ERROR: Unable to process test results."
|
114
121
|
puts
|
115
122
|
|
@@ -127,68 +134,18 @@ module TConsole
|
|
127
134
|
puts
|
128
135
|
end
|
129
136
|
|
130
|
-
#
|
131
|
-
def
|
132
|
-
|
133
|
-
files = recent_files(touched_since, "app/models/**/*.rb", "test/unit")
|
134
|
-
files.concat(recent_files(touched_since, "app/controllers/**/*.rb", "test/functional"))
|
135
|
-
|
136
|
-
message = "Running #{files.length} #{files.length == 1 ? "test file" : "test files"} based on changed files..."
|
137
|
-
run_tests(files, test_pattern, message)
|
138
|
-
end
|
139
|
-
|
140
|
-
def run_failed
|
141
|
-
file_names = last_result.failure_details.map { |detail| filenameify(detail[:class]) }
|
142
|
-
files_to_rerun = []
|
143
|
-
|
144
|
-
files_to_rerun << file_names.map {|file| (file.match(/controller/)) ? "test/functional/#{file}.rb" : "test/unit/#{file}.rb"}
|
145
|
-
run_tests(files_to_rerun, nil, "Running last failed tests: #{files_to_rerun.join(", ")}")
|
137
|
+
# Runs all tests against the match patterns given
|
138
|
+
def run_all_tests(match_patterns = nil)
|
139
|
+
run_tests(config.file_sets["all"], match_patterns)
|
146
140
|
end
|
147
141
|
|
148
|
-
|
149
|
-
|
150
|
-
|
151
|
-
tests = []
|
152
|
-
source_dir = File.dirname(path).split("/")
|
153
|
-
source_file = File.basename(path, '.rb')
|
154
|
-
|
155
|
-
# Support subdirs in app/models and app/controllers
|
156
|
-
modified_test_path = source_dir.length > 2 ? "#{test_path}/" << source_dir[1..source_dir.length].join('/') : test_path
|
157
|
-
|
158
|
-
# For modified files in app/ run the tests for it. ex. /test/functional/account_controller.rb
|
159
|
-
test = "#{modified_test_path}/#{source_file}_test.rb"
|
160
|
-
tests.push test if File.exist?(test)
|
161
|
-
|
162
|
-
# For modified files in app, run tests in subdirs too. ex. /test/functional/account/*_test.rb
|
163
|
-
test = "#{modified_test_path}/#{File.basename(path, '.rb').sub("_controller","")}"
|
164
|
-
File.glob("#{test}/*_test.rb").each { |f| tests.push f } if File.exist?(test)
|
165
|
-
|
166
|
-
return tests
|
167
|
-
|
168
|
-
end
|
169
|
-
end.flatten.compact
|
142
|
+
# Runs a file set out of the config
|
143
|
+
def run_file_set(set)
|
144
|
+
run_tests(config.file_sets[set], nil)
|
170
145
|
end
|
171
146
|
|
172
|
-
|
173
|
-
|
174
|
-
if File.directory?(".svn")
|
175
|
-
changed_since_checkin = silence_stderr { `svn status` }.split.map { |path| path.chomp[7 .. -1] }
|
176
|
-
elsif File.directory?(".git")
|
177
|
-
changed_since_checkin = silence_stderr { `git ls-files --modified --others` }.split.map { |path| path.chomp }
|
178
|
-
else
|
179
|
-
puts "Not a Subversion or Git checkout."
|
180
|
-
return
|
181
|
-
end
|
182
|
-
|
183
|
-
models = changed_since_checkin.select { |path| path =~ /app[\\\/]models[\\\/].*\.rb$/ }
|
184
|
-
controllers = changed_since_checkin.select { |path| path =~ /app[\\\/]controllers[\\\/].*\.rb$/ }
|
185
|
-
|
186
|
-
unit_tests = models.map { |model| "test/unit/#{File.basename(model, '.rb')}_test.rb" }
|
187
|
-
functional_tests = controllers.map { |controller| "test/functional/#{File.basename(controller, '.rb')}_test.rb" }
|
188
|
-
files = (unit_tests + functional_tests).uniq.select { |file| File.exist?(file) }
|
189
|
-
|
190
|
-
message = "Running #{files.length} #{files.length == 1 ? "test file" : "test files"} based on uncommitted changes..."
|
191
|
-
run_tests(files, test_pattern, message)
|
147
|
+
def run_failed
|
148
|
+
run_tests(config.file_sets["all"], last_result.failures)
|
192
149
|
end
|
193
150
|
|
194
151
|
def run_info
|
data/lib/tconsole/test_result.rb
CHANGED
@@ -18,29 +18,44 @@ module TConsole
|
|
18
18
|
# The timings for the tests we've run
|
19
19
|
attr_accessor :timings
|
20
20
|
|
21
|
+
# The element id lookup hash
|
22
|
+
attr_accessor :elements
|
23
|
+
|
24
|
+
# Test counts within various suites
|
25
|
+
attr_accessor :suite_counts
|
26
|
+
|
21
27
|
def initialize
|
22
28
|
self.failures = 0
|
23
29
|
self.errors = 0
|
24
30
|
self.skips = 0
|
25
|
-
self.
|
31
|
+
self.failures = []
|
26
32
|
self.suites = {}
|
27
33
|
self.timings = []
|
28
|
-
end
|
29
34
|
|
30
|
-
|
31
|
-
|
32
|
-
self.failure_details << { :class => klass.to_s, :method => meth.to_s }
|
35
|
+
self.suite_counts = {}
|
36
|
+
self.elements = {}
|
33
37
|
end
|
34
38
|
|
35
|
-
|
36
|
-
|
37
|
-
|
38
|
-
if
|
39
|
-
|
40
|
-
|
41
|
-
suites[suite.to_s] = true
|
42
|
-
true
|
39
|
+
def add_element(suite, method)
|
40
|
+
canonical_name = "#{suite}##{method}"
|
41
|
+
|
42
|
+
# Just return the id if we already know about this
|
43
|
+
if id = elements[canonical_name]
|
44
|
+
return id
|
43
45
|
end
|
46
|
+
|
47
|
+
# See if we know about this suite already
|
48
|
+
unless suite_id = elements[suite.to_s]
|
49
|
+
suite_id = self.suite_counts.length + 1
|
50
|
+
elements[suite.to_s] = suite_id
|
51
|
+
suite_counts[suite.to_s] ||= 0
|
52
|
+
end
|
53
|
+
|
54
|
+
suite_counts[suite.to_s] += 1
|
55
|
+
id = "#{suite_id}-#{suite_counts[suite.to_s]}"
|
56
|
+
elements[canonical_name] = id
|
57
|
+
|
58
|
+
id
|
44
59
|
end
|
45
60
|
|
46
61
|
def add_timing(suite, method, time)
|
data/lib/tconsole/version.rb
CHANGED
metadata
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
--- !ruby/object:Gem::Specification
|
2
2
|
name: tconsole
|
3
3
|
version: !ruby/object:Gem::Version
|
4
|
-
version: 1.1.
|
4
|
+
version: 1.1.0pre9
|
5
5
|
prerelease: 5
|
6
6
|
platform: ruby
|
7
7
|
authors:
|
@@ -9,11 +9,11 @@ authors:
|
|
9
9
|
autorequire:
|
10
10
|
bindir: bin
|
11
11
|
cert_chain: []
|
12
|
-
date: 2012-02-
|
12
|
+
date: 2012-02-20 00:00:00.000000000 Z
|
13
13
|
dependencies:
|
14
14
|
- !ruby/object:Gem::Dependency
|
15
15
|
name: term-ansicolor
|
16
|
-
requirement: &
|
16
|
+
requirement: &70167646243500 !ruby/object:Gem::Requirement
|
17
17
|
none: false
|
18
18
|
requirements:
|
19
19
|
- - ~>
|
@@ -21,7 +21,7 @@ dependencies:
|
|
21
21
|
version: 1.0.7
|
22
22
|
type: :runtime
|
23
23
|
prerelease: false
|
24
|
-
version_requirements: *
|
24
|
+
version_requirements: *70167646243500
|
25
25
|
description: ! " tconsole allows Rails developers to easily and quickly run their
|
26
26
|
tests as a whole or in subsets. It forks the testing processes from\n a preloaded
|
27
27
|
test environment to ensure that developers don't have to reload their entire Rails
|
@@ -39,8 +39,10 @@ files:
|
|
39
39
|
- README.md
|
40
40
|
- Rakefile
|
41
41
|
- bin/tconsole
|
42
|
+
- drb_connection_test.rb
|
42
43
|
- lib/tconsole.rb
|
43
44
|
- lib/tconsole/config.rb
|
45
|
+
- lib/tconsole/console.rb
|
44
46
|
- lib/tconsole/minitest_handler.rb
|
45
47
|
- lib/tconsole/server.rb
|
46
48
|
- lib/tconsole/test_result.rb
|