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.
@@ -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