tconsole 1.1.0pre8 → 1.1.0pre9
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 +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
|