tconsole-rails4 2.2.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.
- checksums.yaml +7 -0
- data/.gitignore +6 -0
- data/.gitmodules +3 -0
- data/Gemfile +8 -0
- data/README.md +293 -0
- data/Rakefile +1 -0
- data/bin/rconsole +5 -0
- data/bin/tconsole +5 -0
- data/cibuild +4 -0
- data/lib/tconsole.rb +18 -0
- data/lib/tconsole/config.rb +251 -0
- data/lib/tconsole/console.rb +150 -0
- data/lib/tconsole/minitest_handler.rb +93 -0
- data/lib/tconsole/minitest_reporter.rb +207 -0
- data/lib/tconsole/minitest_server.rb +123 -0
- data/lib/tconsole/reporter.rb +113 -0
- data/lib/tconsole/rspec_server.rb +108 -0
- data/lib/tconsole/runner.rb +139 -0
- data/lib/tconsole/server.rb +174 -0
- data/lib/tconsole/test_result.rb +65 -0
- data/lib/tconsole/util.rb +33 -0
- data/lib/tconsole/version.rb +3 -0
- data/spec/config_spec.rb +94 -0
- data/spec/runner_spec.rb +38 -0
- data/spec/sample_config +3 -0
- data/spec/spec_helper.rb +4 -0
- data/spec/util_spec.rb +26 -0
- data/tconsole.gemspec +27 -0
- data/test/lib/tconsole/config_test.rb +15 -0
- data/test/lib/tconsole_test.rb +6 -0
- data/test/test_helper.rb +8 -0
- metadata +121 -0
@@ -0,0 +1,123 @@
|
|
1
|
+
# -*- coding: utf-8 -*-
|
2
|
+
module TConsole
|
3
|
+
class MinitestServer < Server
|
4
|
+
|
5
|
+
# Loads the files that match globs and then executes tests against them. Limit tests
|
6
|
+
# with class names, method names, and test ids using match_patterns.
|
7
|
+
def run_tests(globs, match_patterns, message = "Running tests...")
|
8
|
+
time = Benchmark.realtime do
|
9
|
+
reporter.info(message)
|
10
|
+
reporter.info
|
11
|
+
|
12
|
+
paths = []
|
13
|
+
globs.each do |glob|
|
14
|
+
paths.concat(Dir.glob(glob))
|
15
|
+
end
|
16
|
+
|
17
|
+
if paths.length == 0
|
18
|
+
reporter.warn("No test files match your requested test set: #{globs.join(",")}.")
|
19
|
+
reporter.warn("Skipping execution.")
|
20
|
+
return nil
|
21
|
+
end
|
22
|
+
|
23
|
+
self.last_result = run_in_fork do
|
24
|
+
|
25
|
+
paths.each do |path|
|
26
|
+
reporter.trace("Requested path `#{path}` doesn't exist.") unless File.exist?(path)
|
27
|
+
require File.expand_path(path)
|
28
|
+
end
|
29
|
+
|
30
|
+
reporter.trace("Running before_test_run callback")
|
31
|
+
config.before_test_run!
|
32
|
+
reporter.trace("Completed before_test_run callback")
|
33
|
+
|
34
|
+
result = nil
|
35
|
+
if defined?(::Minitest)
|
36
|
+
reporter.trace("Detected minitest.")
|
37
|
+
require File.join(File.dirname(__FILE__), "minitest_handler")
|
38
|
+
require File.join(File.dirname(__FILE__), "minitest_reporter")
|
39
|
+
|
40
|
+
reporter.trace("Running tests.")
|
41
|
+
runner = MiniTestHandler.new(config)
|
42
|
+
|
43
|
+
# TODO надо починить Ctrl+C
|
44
|
+
# Handle trapping interrupts
|
45
|
+
trap("SIGINT") do
|
46
|
+
reporter.warn
|
47
|
+
reporter.warn("Trapped interrupt. Halting tests.")
|
48
|
+
|
49
|
+
runner.interrupted = true
|
50
|
+
end
|
51
|
+
|
52
|
+
|
53
|
+
result, res_reporter = runner.match_and_run(match_patterns, config)
|
54
|
+
|
55
|
+
res_reporter.report
|
56
|
+
# Make sure minitest doesn't run automatically
|
57
|
+
MiniTestHandler.patch_minitest
|
58
|
+
|
59
|
+
reporter.trace("Finished running tests.")
|
60
|
+
|
61
|
+
if runner.interrupted
|
62
|
+
reporter.error("Test run was interrupted.")
|
63
|
+
end
|
64
|
+
|
65
|
+
elsif defined?(::Test::Unit)
|
66
|
+
reporter.error("Sorry, but #{config.app} doesn't support Test::Unit")
|
67
|
+
end
|
68
|
+
|
69
|
+
result
|
70
|
+
end
|
71
|
+
|
72
|
+
if self.last_result == nil
|
73
|
+
# Just in case anything crazy goes down with marshalling
|
74
|
+
self.last_result = TConsole::TestResult.new
|
75
|
+
end
|
76
|
+
|
77
|
+
config.cache_test_ids(self.last_result)
|
78
|
+
|
79
|
+
true
|
80
|
+
end
|
81
|
+
|
82
|
+
reporter.info
|
83
|
+
reporter.info("Tests ran in #{"%0.6f" % time}s. Finished at #{Time.now.strftime('%Y-%m-%d %l:%M:%S %p')}.")
|
84
|
+
reporter.info
|
85
|
+
end
|
86
|
+
|
87
|
+
# Preloads our autocomplete cache
|
88
|
+
def preload_test_ids
|
89
|
+
result = run_in_fork do
|
90
|
+
paths = []
|
91
|
+
config.file_sets["all"].each do |glob|
|
92
|
+
paths.concat(Dir.glob(glob))
|
93
|
+
end
|
94
|
+
|
95
|
+
paths.each { |path| require File.expand_path(path) }
|
96
|
+
|
97
|
+
require File.join(File.dirname(__FILE__), "minitest_handler")
|
98
|
+
MiniTestHandler.preload_elements
|
99
|
+
end
|
100
|
+
|
101
|
+
config.cache_test_ids(result) unless result.nil?
|
102
|
+
end
|
103
|
+
|
104
|
+
# Runs all tests against the match patterns given
|
105
|
+
def run_all_tests(match_patterns = nil)
|
106
|
+
run_tests(config.file_sets["all"], match_patterns)
|
107
|
+
end
|
108
|
+
|
109
|
+
# Runs a file set out of the config
|
110
|
+
def run_file_set(set)
|
111
|
+
run_tests(config.file_sets[set], nil)
|
112
|
+
end
|
113
|
+
|
114
|
+
def run_failed
|
115
|
+
if last_result.failures.empty?
|
116
|
+
reporter.info("No tests failed in your last run, or you haven't run any tests in this session yet.")
|
117
|
+
reporter.info
|
118
|
+
else
|
119
|
+
run_tests(config.file_sets["all"], last_result.failures)
|
120
|
+
end
|
121
|
+
end
|
122
|
+
end
|
123
|
+
end
|
@@ -0,0 +1,113 @@
|
|
1
|
+
# Manages all output for TConsole
|
2
|
+
module TConsole
|
3
|
+
class Reporter
|
4
|
+
attr_accessor :config
|
5
|
+
|
6
|
+
def initialize(config)
|
7
|
+
self.config = config
|
8
|
+
end
|
9
|
+
|
10
|
+
# Public: Ouptuts an informative message.
|
11
|
+
def info(message = "")
|
12
|
+
puts message
|
13
|
+
end
|
14
|
+
|
15
|
+
# Public: Outputs a positive informative message.
|
16
|
+
# Colors it green if the console supports it.
|
17
|
+
def exclaim(message = "")
|
18
|
+
puts ::Term::ANSIColor.green(message)
|
19
|
+
end
|
20
|
+
|
21
|
+
# Public: Outputs a warning message.
|
22
|
+
def warn(message = "")
|
23
|
+
puts ::Term::ANSIColor.yellow(message)
|
24
|
+
end
|
25
|
+
|
26
|
+
# Public: Outputs an error message.
|
27
|
+
def error(message = "")
|
28
|
+
puts ::Term::ANSIColor.red(message)
|
29
|
+
end
|
30
|
+
|
31
|
+
|
32
|
+
# Public: Outputs a trace message, when needed.
|
33
|
+
def trace(message = "")
|
34
|
+
puts "[tconsole trace] #{message}" if config.trace?
|
35
|
+
end
|
36
|
+
|
37
|
+
# Public: Prints a backtrace out.
|
38
|
+
def trace_backtrace(exception)
|
39
|
+
trace("===========")
|
40
|
+
trace(exception.backtrace.join("\n"))
|
41
|
+
trace("===========")
|
42
|
+
end
|
43
|
+
|
44
|
+
# Public: Outputs a timing for the timings command, using the proper
|
45
|
+
# color logic
|
46
|
+
def timing(timing, test_id)
|
47
|
+
output = "#{"%0.6f" % timing[:time]}s #{timing[:name]}"
|
48
|
+
if timing[:time] > 1
|
49
|
+
print ::Term::ANSIColor.red, output, ::Term::ANSIColor.reset
|
50
|
+
else
|
51
|
+
print ::Term::ANSIColor.green, output, ::Term::ANSIColor.reset
|
52
|
+
end
|
53
|
+
|
54
|
+
print ::Term::ANSIColor.magenta, " #{last_result}", ::Term::ANSIColor.reset, "\n"
|
55
|
+
end
|
56
|
+
|
57
|
+
# Public: Prints a list of available commands
|
58
|
+
def help_message
|
59
|
+
puts
|
60
|
+
puts "Available commands:"
|
61
|
+
puts
|
62
|
+
puts "reload # Reload your Rails environment"
|
63
|
+
puts "set [variable] [value] # Sets a runtime variable (see below for details)"
|
64
|
+
puts "exit # Exit the console"
|
65
|
+
puts "!failed # Runs the last set of failing tests"
|
66
|
+
puts "!timings [limit] # Lists the timings for the last test run, sorted."
|
67
|
+
puts "[filename] [test_pattern] # Run the tests contained in the given file"
|
68
|
+
puts ".[command] # Executes the given command in a subshell"
|
69
|
+
puts
|
70
|
+
puts "Running file sets"
|
71
|
+
puts
|
72
|
+
puts "File sets are sets of files that are typically run together. For example,"
|
73
|
+
puts "in Rails projects it's common to run `rake test:units` to run all of the"
|
74
|
+
puts "tests under the units directory."
|
75
|
+
puts
|
76
|
+
puts "Available file sets:"
|
77
|
+
|
78
|
+
config.file_sets.each do |set, paths|
|
79
|
+
puts set
|
80
|
+
end
|
81
|
+
|
82
|
+
puts
|
83
|
+
puts "Working with test patterns:"
|
84
|
+
puts
|
85
|
+
puts "All of the test execution commands include an optional test_pattern argument. A"
|
86
|
+
puts "test pattern can be given to filter the executed tests to only those tests whose"
|
87
|
+
puts "name matches the pattern given. This is especially useful when rerunning a failing"
|
88
|
+
puts "test."
|
89
|
+
puts
|
90
|
+
puts "Runtime Variables"
|
91
|
+
puts
|
92
|
+
puts "You can set runtime variables with the set command. This helps out with changing"
|
93
|
+
puts "features that you may want to change at runtime. At present, the following"
|
94
|
+
puts "runtime variables are available:"
|
95
|
+
puts
|
96
|
+
puts "fast # Turns on fail fast mode. Values: on, off"
|
97
|
+
puts
|
98
|
+
|
99
|
+
end
|
100
|
+
|
101
|
+
# Public: Outputs the tconsole welcome message
|
102
|
+
def welcome_message
|
103
|
+
info
|
104
|
+
info("Welcome to #{config.app} (v#{TConsole::VERSION}). Type 'help' for help or 'exit' to quit.")
|
105
|
+
end
|
106
|
+
|
107
|
+
# Public: Outputs the tconsole exit message
|
108
|
+
def exit_message
|
109
|
+
info
|
110
|
+
info("Exiting. Bye!")
|
111
|
+
end
|
112
|
+
end
|
113
|
+
end
|
@@ -0,0 +1,108 @@
|
|
1
|
+
module TConsole
|
2
|
+
class RSpecServer < Server
|
3
|
+
|
4
|
+
# Loads the files that match globs and then executes tests against them. Limit tests
|
5
|
+
# with class names, method names, and test ids using match_patterns.
|
6
|
+
def run_tests(globs, match_patterns, message = "Running tests...")
|
7
|
+
time = Benchmark.realtime do
|
8
|
+
reporter.info(message)
|
9
|
+
reporter.info
|
10
|
+
|
11
|
+
paths = []
|
12
|
+
globs.each do |glob|
|
13
|
+
paths.concat(Dir.glob(glob))
|
14
|
+
end
|
15
|
+
|
16
|
+
if paths.length == 0
|
17
|
+
reporter.warn("No test files match your requested test set: #{globs.join(",")}.")
|
18
|
+
reporter.warn("Skipping execution.")
|
19
|
+
return nil
|
20
|
+
end
|
21
|
+
|
22
|
+
self.last_result = run_in_fork do
|
23
|
+
|
24
|
+
# Make sure rspec is loaded up
|
25
|
+
require 'rspec'
|
26
|
+
|
27
|
+
paths.each do |path|
|
28
|
+
reporter.trace("Requested path `#{path}` doesn't exist.") unless File.exist?(path)
|
29
|
+
require File.expand_path(path)
|
30
|
+
end
|
31
|
+
|
32
|
+
reporter.trace("Running before_test_run callback")
|
33
|
+
config.before_test_run!
|
34
|
+
reporter.trace("Completed before_test_run callback")
|
35
|
+
|
36
|
+
result = nil
|
37
|
+
if defined?(::RSpec)
|
38
|
+
reporter.trace("Detected rspec.")
|
39
|
+
|
40
|
+
reporter.trace("Running tests.")
|
41
|
+
|
42
|
+
# Handle trapping interrupts
|
43
|
+
trap("SIGINT") do
|
44
|
+
reporter.warn
|
45
|
+
reporter.warn("Trapped interrupt. Halting tests.")
|
46
|
+
end
|
47
|
+
|
48
|
+
# Actually run the tests!
|
49
|
+
configuration = RSpec::configuration
|
50
|
+
world = RSpec::world
|
51
|
+
options = RSpec::Core::ConfigurationOptions.new([])
|
52
|
+
options.parse_options
|
53
|
+
|
54
|
+
configuration.error_stream = STDERR
|
55
|
+
configuration.output_stream = STDOUT
|
56
|
+
|
57
|
+
options.configure(configuration)
|
58
|
+
|
59
|
+
configuration.files_to_run = paths
|
60
|
+
|
61
|
+
configuration.reporter.report(world.example_count, configuration.randomize? ? configuration.seed : nil) do |reporter|
|
62
|
+
begin
|
63
|
+
configuration.run_hook(:before, :suite)
|
64
|
+
world.example_groups.ordered.map {|g| g.run(reporter)}.all? ? 0 : configuration.failure_exit_code
|
65
|
+
ensure
|
66
|
+
configuration.run_hook(:after, :suite)
|
67
|
+
end
|
68
|
+
end
|
69
|
+
|
70
|
+
# Patch RSpec to disable autorun
|
71
|
+
::RSpec::Core::Runner.class_eval do
|
72
|
+
def self.run(args = [], err=$stderr, out=$stdout)
|
73
|
+
# do nothing
|
74
|
+
end
|
75
|
+
end
|
76
|
+
|
77
|
+
reporter.trace("Finished running tests.")
|
78
|
+
end
|
79
|
+
|
80
|
+
nil
|
81
|
+
end
|
82
|
+
|
83
|
+
if self.last_result == nil
|
84
|
+
# Just in case anything crazy goes down with marshalling
|
85
|
+
self.last_result = TConsole::TestResult.new
|
86
|
+
end
|
87
|
+
|
88
|
+
config.cache_test_ids(self.last_result)
|
89
|
+
|
90
|
+
true
|
91
|
+
end
|
92
|
+
|
93
|
+
reporter.info
|
94
|
+
reporter.info("Tests ran in #{"%0.6f" % time}s. Finished at #{Time.now.strftime('%Y-%m-%d %l:%M:%S %p')}.")
|
95
|
+
reporter.info
|
96
|
+
end
|
97
|
+
|
98
|
+
# Runs all tests against the match patterns given
|
99
|
+
def run_all_tests(match_patterns = nil)
|
100
|
+
run_tests(config.file_sets["all"], match_patterns)
|
101
|
+
end
|
102
|
+
|
103
|
+
# Runs a file set out of the config
|
104
|
+
def run_file_set(set)
|
105
|
+
run_tests(config.file_sets[set], nil)
|
106
|
+
end
|
107
|
+
end
|
108
|
+
end
|
@@ -0,0 +1,139 @@
|
|
1
|
+
module TConsole
|
2
|
+
class Runner
|
3
|
+
|
4
|
+
attr_accessor :mode, :config, :reporter, :console, :stty_save
|
5
|
+
|
6
|
+
# Public: Sets up the new runner's config.
|
7
|
+
def initialize(mode, argv = [])
|
8
|
+
self.mode = mode
|
9
|
+
|
10
|
+
# try to load the default configs
|
11
|
+
Config.load_config(File.join(Dir.home, ".#{Config.app(mode)}"))
|
12
|
+
Config.load_config(File.join(Dir.pwd, ".#{Config.app(mode)}"))
|
13
|
+
self.config = Config.configure(mode, argv)
|
14
|
+
self.reporter = Reporter.new(config)
|
15
|
+
end
|
16
|
+
|
17
|
+
# Spawns a new environment. Looks at the results of the environment to determine
|
18
|
+
# whether to stop or keep running
|
19
|
+
def run
|
20
|
+
prepare_process
|
21
|
+
reporter.welcome_message
|
22
|
+
exit(1) if print_config_errors
|
23
|
+
|
24
|
+
# Set up our console input handling and history
|
25
|
+
console = Console.new(config, reporter)
|
26
|
+
|
27
|
+
# Start the server
|
28
|
+
while console_run_loop(console)
|
29
|
+
# just need to run the loop
|
30
|
+
end
|
31
|
+
|
32
|
+
console.store_history
|
33
|
+
|
34
|
+
cleanup_process
|
35
|
+
end
|
36
|
+
|
37
|
+
# Internal: Set up the process and console.
|
38
|
+
def prepare_process
|
39
|
+
self.stty_save = `stty -g`.chomp
|
40
|
+
|
41
|
+
trap("SIGINT", "IGNORE")
|
42
|
+
trap("SIGTSTP", "IGNORE")
|
43
|
+
end
|
44
|
+
|
45
|
+
# Internal: Cleans up the process at the end of execution.
|
46
|
+
def cleanup_process
|
47
|
+
reporter.exit_message
|
48
|
+
system("stty", self.stty_save);
|
49
|
+
end
|
50
|
+
|
51
|
+
# Internal: Prints config errors, if there are any.
|
52
|
+
#
|
53
|
+
# Returns true if there were errors, false otherwise.
|
54
|
+
def print_config_errors
|
55
|
+
config_errors = @config.validation_errors
|
56
|
+
if config_errors.length > 0
|
57
|
+
reporter.error
|
58
|
+
reporter.error(config_errors.first)
|
59
|
+
true
|
60
|
+
else
|
61
|
+
false
|
62
|
+
end
|
63
|
+
end
|
64
|
+
|
65
|
+
# Internal: Environment reload run loop.
|
66
|
+
#
|
67
|
+
# This run loop handles spawning a new tconsole environment - it's basically
|
68
|
+
# just there to handle reloads. Also calls out to the input loop for the
|
69
|
+
# console.
|
70
|
+
#
|
71
|
+
# Returns false if tconsole needs to stop, true otherwise.
|
72
|
+
def console_run_loop(console)
|
73
|
+
pipe_server = ChattyProc::PipeServer.new
|
74
|
+
|
75
|
+
reporter.trace("Forking test server.")
|
76
|
+
server_pid = fork do
|
77
|
+
server_run_loop(pipe_server)
|
78
|
+
end
|
79
|
+
|
80
|
+
pipe_server.caller!
|
81
|
+
unless load_environment(pipe_server)
|
82
|
+
pipe_server.write({:action => "exit"})
|
83
|
+
return false
|
84
|
+
end
|
85
|
+
|
86
|
+
continue = console.read_and_execute(pipe_server)
|
87
|
+
reporter.trace("Console read loop returned - continue: #{continue}")
|
88
|
+
|
89
|
+
Process.waitall
|
90
|
+
|
91
|
+
continue
|
92
|
+
end
|
93
|
+
|
94
|
+
# Internal: Asks the server to load the environment.
|
95
|
+
#
|
96
|
+
# Returns true if the environment was loaded, or false otherwise.
|
97
|
+
def load_environment(pipe_server)
|
98
|
+
reporter.trace("Attempting to load environment.")
|
99
|
+
pipe_server.write({:action => "load_environment"})
|
100
|
+
|
101
|
+
if pipe_server.read
|
102
|
+
reporter.trace("Environment loaded successfully.")
|
103
|
+
true
|
104
|
+
else
|
105
|
+
reporter.error("Couldn't load the test environment. Exiting.")
|
106
|
+
false
|
107
|
+
end
|
108
|
+
end
|
109
|
+
|
110
|
+
# Internal: Run loop for the server.
|
111
|
+
def server_run_loop(pipe_server)
|
112
|
+
pipe_server.callee!
|
113
|
+
|
114
|
+
if mode == :minitest
|
115
|
+
server = MinitestServer.new(config, reporter)
|
116
|
+
elsif mode == :rspec
|
117
|
+
server = RSpecServer.new(config, reporter)
|
118
|
+
else
|
119
|
+
reporter.error
|
120
|
+
reporter.error("The given test mode isn't supported.")
|
121
|
+
reporter.error
|
122
|
+
exit
|
123
|
+
end
|
124
|
+
|
125
|
+
while message = pipe_server.read
|
126
|
+
reporter.trace("Server Received Message: #{message[:action]}")
|
127
|
+
begin
|
128
|
+
result = server.handle(message)
|
129
|
+
pipe_server.write(result)
|
130
|
+
rescue => e
|
131
|
+
reporter.error
|
132
|
+
reporter.error("An error occured: #{e.message}")
|
133
|
+
reporter.trace_backtrace(e)
|
134
|
+
pipe_server.write(nil)
|
135
|
+
end
|
136
|
+
end
|
137
|
+
end
|
138
|
+
end
|
139
|
+
end
|