tconsole-rails4 2.2.0

Sign up to get free protection for your applications and to get access to all the features.
@@ -0,0 +1,150 @@
1
+ module TConsole
2
+ class Console
3
+ KNOWN_COMMANDS = ["exit", "reload", "help", "info", "!failed", "!timings", "set"]
4
+
5
+ attr_accessor :config, :reporter
6
+
7
+ def initialize(config, reporter)
8
+ self.config = config
9
+ self.reporter = reporter
10
+ read_history
11
+ end
12
+
13
+ def define_autocomplete(pipe_server)
14
+ Readline.completion_append_character = ""
15
+
16
+ # Proc for helping us figure out autocompletes
17
+ Readline.completion_proc = Proc.new do |str|
18
+ known_commands = KNOWN_COMMANDS.grep(/^#{Regexp.escape(str)}/)
19
+ known_commands.concat(@config.file_sets.keys.grep(/^#{Regexp.escape(str)}/))
20
+
21
+ known_elements = []
22
+ unless pipe_server.nil?
23
+ known_elements = send_message(pipe_server, :autocomplete, str)
24
+ end
25
+
26
+ known_commands.concat(known_elements)
27
+ end
28
+ end
29
+
30
+ # Returns true if the app should keep running, false otherwise
31
+ def read_and_execute(pipe_server)
32
+ prompt = "#{config.app}> "
33
+
34
+ trap("SIGTSTP", "SYSTEM_DEFAULT")
35
+ trap("SIGCONT") do
36
+ print prompt
37
+ end
38
+
39
+ # Run any commands that have been passed
40
+ result = process_command(pipe_server, @config.run_command)
41
+ @config.run_command = ""
42
+ if result == :exit || @config.once
43
+ return false
44
+ elsif result == :reload
45
+ return true
46
+ end
47
+
48
+ define_autocomplete(pipe_server)
49
+
50
+ # The command entry loop
51
+ while command = Readline.readline(prompt, false)
52
+ command.strip!
53
+ result = process_command(pipe_server, command)
54
+
55
+ if result == :exit
56
+ return false
57
+ elsif result == :reload
58
+ return true
59
+ end
60
+ end
61
+
62
+ send_message(:stop)
63
+ false
64
+ end
65
+
66
+ # Public: Process a command however it needs to be handled.
67
+ #
68
+ # pipe_server - The pipe server we're working with
69
+ # command - The command we need to parse and handle
70
+ def process_command(pipe_server, command)
71
+ args = Shellwords.shellwords(command)
72
+
73
+ # save the command unless we're exiting or repeating the last command
74
+ unless args[0] == "exit" || (Readline::HISTORY.length > 0 && Readline::HISTORY[Readline::HISTORY.length - 1] == command)
75
+ Readline::HISTORY << command
76
+ end
77
+
78
+ if command == ""
79
+ # do nothing
80
+ elsif args[0] == "exit"
81
+ send_message(pipe_server, :stop)
82
+ return :exit
83
+ elsif args[0] == "reload"
84
+ send_message(pipe_server, :stop)
85
+ return :reload
86
+ elsif args[0] == "help"
87
+ reporter.help_message
88
+ elsif args[0] == "!failed"
89
+ send_message(pipe_server, :run_failed)
90
+ elsif args[0] == "!timings"
91
+ send_message(pipe_server, :show_performance, args[1])
92
+ elsif args[0] == "info"
93
+ send_message(pipe_server, :run_info)
94
+ elsif args[0] == "set"
95
+ send_message(pipe_server, :set, args[1], args[2])
96
+ elsif args[0].start_with?(".")
97
+ shell_command(command[1, command.length - 1])
98
+ elsif @config.file_sets.has_key?(args[0])
99
+ send_message(pipe_server, :run_file_set, args[0])
100
+ else
101
+ send_message(pipe_server, :run_all_tests, args)
102
+ end
103
+
104
+ nil
105
+ end
106
+
107
+ def send_message(pipe_server, message, *args)
108
+ pipe_server.write({:action => message.to_sym, :args => args})
109
+ pipe_server.read
110
+ end
111
+
112
+ # Internal: Runs a shell command on the console and outputs the results.
113
+ def shell_command(command)
114
+ system(command)
115
+
116
+ result = $?
117
+
118
+ reporter.info
119
+ if result.exitstatus == 0
120
+ reporter.exclaim("Command exited with status code: 0")
121
+ else
122
+ reporter.error("Command exited with status code: #{result.exitstatus}")
123
+ end
124
+ end
125
+
126
+ def history_file
127
+ File.join(ENV['HOME'], ".tconsole_history")
128
+ end
129
+
130
+ # Stores last 50 items in history to $HOME/.tconsole_history
131
+ def store_history
132
+ if ENV['HOME']
133
+ File.open(history_file, "w") do |f|
134
+ Readline::HISTORY.to_a.reverse[0..49].each do |item|
135
+ f.puts(item)
136
+ end
137
+ end
138
+ end
139
+ end
140
+
141
+ # Loads history from past sessions
142
+ def read_history
143
+ if ENV['HOME'] && File.exist?(history_file)
144
+ File.readlines(history_file).reverse.each do |line|
145
+ Readline::HISTORY << line
146
+ end
147
+ end
148
+ end
149
+ end
150
+ end
@@ -0,0 +1,93 @@
1
+ # -*- coding: utf-8 -*-
2
+ require 'minitest'
3
+ module TConsole
4
+ class MiniTestHandler
5
+ attr_accessor :interrupted
6
+
7
+ def initialize(config)
8
+ @reporter = ::TConsole::MinitestReporter.new
9
+ @reporter.tc_results.suite_counts = config.cached_suite_counts
10
+ @reporter.tc_results.elements = config.cached_elements
11
+ @interrupted = false
12
+ end
13
+
14
+ def match_and_run(match_patterns, config)
15
+
16
+ suites = Minitest::Runnable.runnables
17
+
18
+ @reporter.ready
19
+ suites.each do |suite|
20
+ suite_id = @reporter.tc_results.elements[suite.to_s]
21
+
22
+ suite_printed = false
23
+ suite.methods_matching(/^test/).map do |method|
24
+ if @interrupted
25
+ return @reporter.tc_results
26
+ end
27
+
28
+ id = @reporter.tc_results.add_element(suite, method)
29
+
30
+ unless match_patterns.nil?
31
+ match = match_patterns.find do |pattern|
32
+ pattern == suite.to_s ||
33
+ pattern == "#{suite.to_s}##{method.to_s}" ||
34
+ pattern == suite_id.to_s ||
35
+ pattern == id
36
+ end
37
+ end
38
+
39
+ if !suite_printed && (match_patterns.nil? || match_patterns.empty? || !match.nil?)
40
+ print(::Term::ANSIColor.cyan, suite, ::Term::ANSIColor.reset,
41
+ ::Term::ANSIColor.magenta, " #{suite_id} \n")
42
+ suite_printed = true
43
+ end
44
+
45
+ if match_patterns.nil? || match_patterns.empty? || !match.nil?
46
+ @reporter.current_element_id = id
47
+ # TODO мб понтово свой метод run в минитест захуярить,
48
+ # который список suites принимать будет
49
+ Minitest::Runnable.run_one_method(suite, method, @reporter)
50
+ end
51
+ end
52
+
53
+ if suite_printed
54
+ puts
55
+ end
56
+ end
57
+
58
+ [@reporter.tc_results, @reporter]
59
+ end
60
+
61
+ # Preloads our element cache for autocompletion. Assumes tests are already loaded
62
+ def self.preload_elements
63
+ patch_minitest
64
+ results = TConsole::TestResult.new
65
+ suites = Minitest::Runnable.runnables
66
+ suites.each do |suite|
67
+ suite.methods_matching(/^test/).map do |method|
68
+ id = results.add_element(suite, method)
69
+ end
70
+ end
71
+
72
+ results
73
+ end
74
+
75
+ # We're basically breaking Minitest autorun here, since we want to manually run our
76
+ # tests and Rails relies on autorun
77
+ # A big reason for the need for this is that we're trying to work in the Rake environment
78
+ # rather than rebuilding all of the code in Rake just to get test prep happening
79
+ # correctly.
80
+ def self.patch_minitest
81
+ Minitest.class_eval do
82
+ class << self
83
+ alias_method :old_run, :run
84
+ def run(args = [])
85
+ end
86
+ end
87
+ end
88
+ end
89
+ end
90
+ end
91
+
92
+ # Make sure that output is only colored when it should be
93
+ Term::ANSIColor::coloring = STDOUT.isatty
@@ -0,0 +1,207 @@
1
+ # -*- coding: utf-8 -*-
2
+ require 'minitest'
3
+ require 'ansi/code'
4
+
5
+ module TConsole
6
+ # Turn-like reporter that reads like a spec.
7
+ #
8
+ # Based upon
9
+ # https://github.com/kern/minitest-reporters/blob/master/lib%2Fminitest%2Freporters%2Fbase_reporter.rb and spec_reporter
10
+ class BaseReporter < Minitest::StatisticsReporter
11
+ attr_accessor :tests
12
+
13
+ def initialize(options={})
14
+ super($stdout, options)
15
+ self.tests = []
16
+ end
17
+
18
+ def add_defaults(defaults)
19
+ self.options = defaults.merge(options)
20
+ end
21
+
22
+ # called by our own before hooks
23
+ def before_test(test)
24
+ last_test = tests.last
25
+ if last_test.class != test.class
26
+ after_suite(last_test.class) if last_test
27
+ before_suite(test.class)
28
+ end
29
+ end
30
+
31
+ def record(test)
32
+ super
33
+ tests << test
34
+ end
35
+
36
+ # called by our own after hooks
37
+ def after_test(test)
38
+ end
39
+
40
+ def report
41
+ super
42
+ after_suite(tests.last.class)
43
+ end
44
+
45
+ protected
46
+
47
+ def after_suite(test)
48
+ end
49
+
50
+ def before_suite(test)
51
+ end
52
+
53
+ def result(test)
54
+ if test.error?
55
+ :error
56
+ elsif test.skipped?
57
+ :skip
58
+ elsif test.failure
59
+ :fail
60
+ else
61
+ :pass
62
+ end
63
+ end
64
+
65
+ def print_colored_status(test)
66
+ if test.passed?
67
+ print(green { pad_mark( result(test).to_s.upcase ) })
68
+ elsif test.skipped?
69
+ print(yellow { pad_mark( result(test).to_s.upcase ) })
70
+ else
71
+ print(red { pad_mark( result(test).to_s.upcase ) })
72
+ end
73
+ end
74
+
75
+ def total_time
76
+ super || Time.now - start_time
77
+ end
78
+
79
+ def total_count
80
+ options[:total_count]
81
+ end
82
+
83
+ def filter_backtrace(backtrace)
84
+ Minitest.filter_backtrace(backtrace)
85
+ end
86
+
87
+ def puts(*args)
88
+ io.puts(*args)
89
+ end
90
+
91
+ def print(*args)
92
+ io.print(*args)
93
+ end
94
+
95
+ def print_info(e, name=true)
96
+ e.message.each_line { |line| print_with_info_padding(line) }
97
+
98
+ trace = filter_backtrace(e.backtrace)
99
+ trace.each { |line| print_with_info_padding(line) }
100
+ end
101
+ end
102
+
103
+ module RelativePosition
104
+ TEST_PADDING = 2
105
+ TEST_SIZE = 63
106
+ MARK_SIZE = 5
107
+ INFO_PADDING = 8
108
+
109
+ private
110
+
111
+ def print_with_info_padding(line)
112
+ puts pad(line, INFO_PADDING)
113
+ end
114
+
115
+ def pad(str, size = INFO_PADDING)
116
+ ' ' * size + str
117
+ end
118
+
119
+ def pad_mark(str)
120
+ "%#{MARK_SIZE}s" % str
121
+ end
122
+
123
+ def pad_test(str)
124
+ pad("%-#{TEST_SIZE}s" % str, TEST_PADDING)
125
+ end
126
+ end
127
+
128
+ class MinitestReporter < BaseReporter
129
+ include ANSI::Code
130
+ include RelativePosition
131
+ attr_accessor :tc_results
132
+ attr_accessor :current_element_id
133
+
134
+ def initialize(*args)
135
+ @current_element_id = 0
136
+ @tc_results = TConsole::TestResult.new
137
+ super
138
+ end
139
+
140
+ def start # not used
141
+ #super
142
+ end
143
+
144
+ def ready
145
+ #super
146
+ if defined? Minitest.clock_time
147
+ self.start_time = Minitest.clock_time
148
+ else
149
+ self.start_time = Time.now
150
+ end
151
+ end
152
+
153
+ def report
154
+ super
155
+ puts('Finished in %.5fs' % total_time)
156
+ res_str = '%d tests, ' % [count]
157
+ res_str += '%d assertions, ' % assertions
158
+ passed = count - (failures + errors + skips)
159
+ res_str += green { '%d passed, ' } % passed
160
+ color = failures.zero? && errors.zero? ? :green : :red
161
+ res_str += send(color) { '%d failures, %d errors, ' } % [failures, errors]
162
+ res_str += yellow { '%d skips' } % skips
163
+ puts(res_str)
164
+ if failures == 0 && errors == 0
165
+ puts
166
+ puts(green { "All tests passed! You are good!" })
167
+ end
168
+ puts
169
+ end
170
+
171
+ def record(test)
172
+ super
173
+ # TODO хорошо бы избавиться от @current_element_id
174
+ str = "#{::Term::ANSIColor.magenta(@current_element_id)} #{colored_string(test.name, test)}"
175
+ print pad_test(str)
176
+ print_colored_status(test)
177
+ print(" (%.2fs)" % test.time)
178
+ print()
179
+ puts
180
+ if !test.skipped? && test.failure
181
+ @tc_results.failures << @current_element_id
182
+ print_info(test.failure)
183
+ puts
184
+ end
185
+ end
186
+
187
+ protected
188
+
189
+ def colored_string(str, test)
190
+ if test.passed?
191
+ ::Term::ANSIColor.green(str)
192
+ elsif test.skipped?
193
+ ::Term::ANSIColor.yellow(str)
194
+ else
195
+ ::Term::ANSIColor.red(str)
196
+ end
197
+ end
198
+
199
+ def before_suite(suite)
200
+ puts suite
201
+ end
202
+
203
+ def after_suite(suite)
204
+ puts
205
+ end
206
+ end
207
+ end