ruber 0.0.1.1
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/COPYING +339 -0
- data/INSTALL +137 -0
- data/LICENSE +8 -0
- data/bin/ruber +65 -0
- data/data/share/apps/ruber/core_components.yaml +31 -0
- data/data/share/apps/ruber/ruberui.rc +109 -0
- data/data/share/icons/ruber.png +0 -0
- data/data/share/pixmaps/ruby.png +0 -0
- data/icons/ruber-16.png +0 -0
- data/icons/ruber-32.png +0 -0
- data/icons/ruber-48.png +0 -0
- data/icons/ruber-8.png +0 -0
- data/lib/ruber/application/application.rb +288 -0
- data/lib/ruber/application/plugin.yaml +11 -0
- data/lib/ruber/component_manager.rb +899 -0
- data/lib/ruber/config/config.rb +82 -0
- data/lib/ruber/config/plugin.yaml +3 -0
- data/lib/ruber/document_project.rb +209 -0
- data/lib/ruber/documents/document_list.rb +416 -0
- data/lib/ruber/documents/plugin.yaml +4 -0
- data/lib/ruber/editor/document.rb +506 -0
- data/lib/ruber/editor/editor_view.rb +167 -0
- data/lib/ruber/editor/ktexteditor_wrapper.rb +202 -0
- data/lib/ruber/exception_widgets.rb +245 -0
- data/lib/ruber/external_program_plugin.rb +397 -0
- data/lib/ruber/filtered_output_widget.rb +342 -0
- data/lib/ruber/gui_states_handler.rb +231 -0
- data/lib/ruber/kde_config_option_backend.rb +167 -0
- data/lib/ruber/kde_sugar.rb +249 -0
- data/lib/ruber/main_window/choose_plugins_dlg.rb +353 -0
- data/lib/ruber/main_window/main_window.rb +524 -0
- data/lib/ruber/main_window/main_window_actions.rb +537 -0
- data/lib/ruber/main_window/main_window_internal.rb +239 -0
- data/lib/ruber/main_window/open_file_in_project_dlg.rb +212 -0
- data/lib/ruber/main_window/output_color_widget.rb +35 -0
- data/lib/ruber/main_window/plugin.yaml +58 -0
- data/lib/ruber/main_window/save_modified_files_dlg.rb +89 -0
- data/lib/ruber/main_window/status_bar.rb +156 -0
- data/lib/ruber/main_window/ui/choose_plugins_widget.rb +90 -0
- data/lib/ruber/main_window/ui/choose_plugins_widget.ui +77 -0
- data/lib/ruber/main_window/ui/main_window_settings_widget.rb +108 -0
- data/lib/ruber/main_window/ui/main_window_settings_widget.ui +89 -0
- data/lib/ruber/main_window/ui/new_project_widget.rb +119 -0
- data/lib/ruber/main_window/ui/new_project_widget.ui +178 -0
- data/lib/ruber/main_window/ui/open_file_in_project_dlg.rb +109 -0
- data/lib/ruber/main_window/ui/open_file_in_project_dlg.ui +168 -0
- data/lib/ruber/main_window/ui/output_color_widget.rb +241 -0
- data/lib/ruber/main_window/ui/output_color_widget.ui +204 -0
- data/lib/ruber/main_window/workspace.rb +442 -0
- data/lib/ruber/output_widget.rb +1093 -0
- data/lib/ruber/plugin.rb +264 -0
- data/lib/ruber/plugin_like.rb +589 -0
- data/lib/ruber/plugin_specification.rb +106 -0
- data/lib/ruber/plugin_specification_reader.rb +451 -0
- data/lib/ruber/project.rb +493 -0
- data/lib/ruber/project_backend.rb +105 -0
- data/lib/ruber/projects/plugin.yaml +11 -0
- data/lib/ruber/projects/project_files_list.rb +314 -0
- data/lib/ruber/projects/project_files_widget.rb +301 -0
- data/lib/ruber/projects/project_list.rb +314 -0
- data/lib/ruber/projects/ui/project_files_rule_chooser_widget.rb +74 -0
- data/lib/ruber/projects/ui/project_files_rule_chooser_widget.ui +61 -0
- data/lib/ruber/projects/ui/project_files_widget.rb +117 -0
- data/lib/ruber/projects/ui/project_files_widget.ui +123 -0
- data/lib/ruber/qt_sugar.rb +673 -0
- data/lib/ruber/settings_container.rb +515 -0
- data/lib/ruber/settings_dialog.rb +244 -0
- data/lib/ruber/settings_dialog_manager.rb +503 -0
- data/lib/ruber/utils.rb +414 -0
- data/lib/ruber/yaml_option_backend.rb +159 -0
- data/outsider_files +15 -0
- data/plugins/autosave/autosave.rb +404 -0
- data/plugins/autosave/plugin.yaml +16 -0
- data/plugins/autosave/ui/autosave_config_widget.rb +83 -0
- data/plugins/autosave/ui/autosave_config_widget.ui +68 -0
- data/plugins/command/command.png +0 -0
- data/plugins/command/command.rb +74 -0
- data/plugins/command/plugin.yaml +11 -0
- data/plugins/find_in_files/find_in_files.rb +337 -0
- data/plugins/find_in_files/find_in_files_dlg.rb +411 -0
- data/plugins/find_in_files/find_in_files_ui.rc +11 -0
- data/plugins/find_in_files/find_in_files_widgets.rb +485 -0
- data/plugins/find_in_files/plugin.yaml +23 -0
- data/plugins/find_in_files/ui/config_widget.rb +58 -0
- data/plugins/find_in_files/ui/config_widget.ui +41 -0
- data/plugins/find_in_files/ui/find_in_files_widget.rb +260 -0
- data/plugins/find_in_files/ui/find_in_files_widget.ui +324 -0
- data/plugins/project_browser/plugin.yaml +10 -0
- data/plugins/project_browser/project_browser.rb +245 -0
- data/plugins/rake/plugin.yaml +39 -0
- data/plugins/rake/rake.png +0 -0
- data/plugins/rake/rake.rb +567 -0
- data/plugins/rake/rake_extension.rb +153 -0
- data/plugins/rake/rake_widgets.rb +615 -0
- data/plugins/rake/rakeui.rc +27 -0
- data/plugins/rake/ui/add_quick_task_widget.rb +71 -0
- data/plugins/rake/ui/add_quick_task_widget.ui +59 -0
- data/plugins/rake/ui/choose_task_widget.rb +77 -0
- data/plugins/rake/ui/choose_task_widget.ui +72 -0
- data/plugins/rake/ui/config_widget.rb +127 -0
- data/plugins/rake/ui/config_widget.ui +123 -0
- data/plugins/rake/ui/project_widget.rb +217 -0
- data/plugins/rake/ui/project_widget.ui +246 -0
- data/plugins/rspec/plugin.yaml +30 -0
- data/plugins/rspec/rspec.png +0 -0
- data/plugins/rspec/rspec.rb +945 -0
- data/plugins/rspec/rspec.svg +90 -0
- data/plugins/rspec/rspecui.rc +20 -0
- data/plugins/rspec/ruber_rspec_formatter.rb +312 -0
- data/plugins/rspec/ui/rspec_project_widget.rb +170 -0
- data/plugins/rspec/ui/rspec_project_widget.ui +193 -0
- data/plugins/ruby_development/plugin.yaml +27 -0
- data/plugins/ruby_development/ruby_development.png +0 -0
- data/plugins/ruby_development/ruby_development.rb +453 -0
- data/plugins/ruby_development/ruby_developmentui.rc +19 -0
- data/plugins/ruby_development/ui/project_widget.rb +112 -0
- data/plugins/ruby_development/ui/project_widget.ui +108 -0
- data/plugins/ruby_runner/config_widget.rb +116 -0
- data/plugins/ruby_runner/plugin.yaml +26 -0
- data/plugins/ruby_runner/project_widget.rb +62 -0
- data/plugins/ruby_runner/ruby.png +0 -0
- data/plugins/ruby_runner/ruby_interpretersui.rc +26 -0
- data/plugins/ruby_runner/ruby_runner.rb +411 -0
- data/plugins/ruby_runner/ui/config_widget.rb +92 -0
- data/plugins/ruby_runner/ui/config_widget.ui +91 -0
- data/plugins/ruby_runner/ui/project_widget.rb +60 -0
- data/plugins/ruby_runner/ui/project_widget.ui +48 -0
- data/plugins/ruby_runner/ui/ruby_runnner_plugin_option_widget.rb +59 -0
- data/plugins/ruby_runner/ui/ruby_runnner_plugin_option_widget.ui +44 -0
- data/plugins/state/plugin.yaml +28 -0
- data/plugins/state/state.rb +520 -0
- data/plugins/state/ui/config_widget.rb +92 -0
- data/plugins/state/ui/config_widget.ui +89 -0
- data/plugins/syntax_checker/plugin.yaml +18 -0
- data/plugins/syntax_checker/syntax_checker.rb +662 -0
- data/ruber.desktop +10 -0
- data/spec/annotation_model_spec.rb +174 -0
- data/spec/common.rb +119 -0
- data/spec/component_manager_spec.rb +1259 -0
- data/spec/document_list_spec.rb +626 -0
- data/spec/document_project_spec.rb +373 -0
- data/spec/document_spec.rb +779 -0
- data/spec/editor_view_spec.rb +167 -0
- data/spec/external_program_plugin_spec.rb +676 -0
- data/spec/filtered_output_widget_spec.rb +642 -0
- data/spec/gui_states_handler_spec.rb +304 -0
- data/spec/kde_config_option_backend_spec.rb +214 -0
- data/spec/kde_sugar_spec.rb +101 -0
- data/spec/ktexteditor_wrapper_spec.rb +305 -0
- data/spec/output_widget_spec.rb +1703 -0
- data/spec/plugin_spec.rb +1393 -0
- data/spec/plugin_specification_reader_spec.rb +1765 -0
- data/spec/plugin_specification_spec.rb +401 -0
- data/spec/project_backend_spec.rb +172 -0
- data/spec/project_files_list_spec.rb +401 -0
- data/spec/project_list_spec.rb +511 -0
- data/spec/project_spec.rb +990 -0
- data/spec/qt_sugar_spec.rb +328 -0
- data/spec/settings_container_spec.rb +617 -0
- data/spec/settings_dialog_manager_spec.rb +773 -0
- data/spec/settings_dialog_spec.rb +419 -0
- data/spec/state_spec.rb +991 -0
- data/spec/utils_spec.rb +406 -0
- data/spec/workspace_spec.rb +869 -0
- data/spec/yaml_option_backend_spec.rb +246 -0
- metadata +284 -0
|
@@ -0,0 +1,30 @@
|
|
|
1
|
+
name: rspec
|
|
2
|
+
version: 0.0.1
|
|
3
|
+
about:
|
|
4
|
+
authors: [Stefano Crocco, stefano.crocco@alice.it]
|
|
5
|
+
license: :gpl
|
|
6
|
+
description: Frontend to RSpec
|
|
7
|
+
bug_address: http://github.com/stcrocco/ruber/issues
|
|
8
|
+
icon: rspec.png
|
|
9
|
+
class: Ruber::RSpec::Plugin
|
|
10
|
+
require: rspec
|
|
11
|
+
deps: [ruby_development, autosave]
|
|
12
|
+
ui_file: rspecui.rc
|
|
13
|
+
project_widgets:
|
|
14
|
+
- {caption: RSpec, class: 'Ruber::RSpec::ProjectWidget', pixmap: rspec.png}
|
|
15
|
+
project_options:
|
|
16
|
+
rspec:
|
|
17
|
+
executable: {default: 'Ruber::RSpec::Plugin.find_default_executable'}
|
|
18
|
+
options: {default: []}
|
|
19
|
+
spec_directory: {relative_path: true, default: spec}
|
|
20
|
+
spec_files: {default: '*_spec.rb'}
|
|
21
|
+
spec_pattern: {default: ['%f_spec.rb']}
|
|
22
|
+
full_backtraces: {default: true}
|
|
23
|
+
tool_widgets: {class: 'Ruber::RSpec::ToolWidget', pixmap: rspec.png, caption: RSpec}
|
|
24
|
+
actions:
|
|
25
|
+
rspec-go_to_spec: {text: Switch to &Spec, shortcut: 'Alt+Shift+R, S', slot: go_to_spec(), states: [active_project_exists, current_document]}
|
|
26
|
+
rspec-go_to_file: {text: Switch to &File, shortcut: 'Alt+Shift+R, F', slot: go_to_file(), states: [active_project_exists, current_document]}
|
|
27
|
+
rspec-run_all: {text: Run &Project Specs, shortcut: 'Alt+Shift+R, P', slot: run_all(), states: [active_project_exists, rspec_running]}
|
|
28
|
+
rspec-run_current: {text: Run Specs for &Current File, shortcut: 'Alt+Shift+R, C', slot: run_current(), states: [active_project_exists, current_document, rspec_running]}
|
|
29
|
+
rspec-run_current_line: {text: Run Current Spec, shortcut: 'Alt+Shift+R, L', slot: run_current_line(), states: [active_project_exists, current_document, rspec_running]}
|
|
30
|
+
rspec-stop: {text: S&top, shortcut: Esc, icon: process-stop, slot: stop_process(), state: rspec_running}
|
|
Binary file
|
|
@@ -0,0 +1,945 @@
|
|
|
1
|
+
require 'ruby_runner/ruby_runner'
|
|
2
|
+
require_relative 'ui/rspec_project_widget'
|
|
3
|
+
|
|
4
|
+
require 'strscan'
|
|
5
|
+
require 'yaml'
|
|
6
|
+
require 'open3'
|
|
7
|
+
|
|
8
|
+
module Ruber
|
|
9
|
+
|
|
10
|
+
=begin rdoc
|
|
11
|
+
Frontend plugin to RSpec
|
|
12
|
+
|
|
13
|
+
This plugin provides commands to run examples for all the files in the current
|
|
14
|
+
project or on the current document (only if it's part of a project). In the latter
|
|
15
|
+
case, the command can be issued while viewing either the spec or the code file.
|
|
16
|
+
Besides, an action to switch from spec file to code file and one to do the reverse
|
|
17
|
+
switch are provided.
|
|
18
|
+
|
|
19
|
+
The pattern to extract the name of the spec file from that of the code file can
|
|
20
|
+
be customized in the project configuration dialog (by default the plugin assumes
|
|
21
|
+
that the spec file for for a file called @code.rb@ is @code_spec.rb@). The directory
|
|
22
|
+
where the spec files are can also be customized (by default it's the @spec@ subdirectory
|
|
23
|
+
under the project directory)
|
|
24
|
+
|
|
25
|
+
@api feature rspec
|
|
26
|
+
@plugin
|
|
27
|
+
@config_option rspec ruby_options [<String>] the options to pass to ruby when executing
|
|
28
|
+
the spec command
|
|
29
|
+
@project_option rspec executable [String] the path to the spec executable
|
|
30
|
+
@project_option rspec options [<String>] the options to pass to the _spec_ command
|
|
31
|
+
@project_option rspec spec_directory [String] the directory where the example files
|
|
32
|
+
are
|
|
33
|
+
@project_option rspec spec_pattern [<String>] patterns to generate the spec files
|
|
34
|
+
from the code files. The name of the code file is obtained by using the special
|
|
35
|
+
sequence @%f@ in the strings. Only the file name of the code file is taken into
|
|
36
|
+
account (not the directory). All the spec file names are relative to the directory
|
|
37
|
+
specified in the @rspec/spec_directory@ option. If more than one entry is specified,
|
|
38
|
+
all of them will be tried
|
|
39
|
+
=end
|
|
40
|
+
module RSpec
|
|
41
|
+
|
|
42
|
+
=begin rdoc
|
|
43
|
+
Plugin object for the RSpec plugin
|
|
44
|
+
@api_method #specs_for_file
|
|
45
|
+
@api_method #file_for_spec
|
|
46
|
+
@api_method #run_rspec
|
|
47
|
+
@api_method #run_rspec_for
|
|
48
|
+
=end
|
|
49
|
+
class Plugin < RubyRunner::RubyRunnerPlugin
|
|
50
|
+
|
|
51
|
+
=begin rdoc
|
|
52
|
+
The starting delimiter of the data written by the formatter
|
|
53
|
+
=end
|
|
54
|
+
STARTING_DELIMITER = /^####%%%%####KRUBY_BEGIN$/
|
|
55
|
+
|
|
56
|
+
=begin rdoc
|
|
57
|
+
The ending delimiter of the data written by the formatter
|
|
58
|
+
=end
|
|
59
|
+
ENDING_DELIMITER = /^####%%%%####KRUBY_END$/
|
|
60
|
+
|
|
61
|
+
=begin rdoc
|
|
62
|
+
Finds the rspec program to use by default
|
|
63
|
+
|
|
64
|
+
It looks for an executable called @rspec@ or @spec@ (this is to support both RSpec
|
|
65
|
+
1 and 2) in @PATH@ (using the @which@ command).
|
|
66
|
+
@return [String] a string with the path to the rspec program or an empty string
|
|
67
|
+
if no rspec program was found
|
|
68
|
+
=end
|
|
69
|
+
def self.find_default_executable
|
|
70
|
+
path = Open3.popen3('which rspec'){|stdin, stdout, stderr| stdout.read}.strip
|
|
71
|
+
if path.empty?
|
|
72
|
+
path = Open3.popen3('which spec'){|stdin, stdout, stderr| stdout.read}.strip
|
|
73
|
+
end
|
|
74
|
+
path
|
|
75
|
+
end
|
|
76
|
+
|
|
77
|
+
|
|
78
|
+
slots :go_to_spec, :go_to_file, :run_all, :run_current, :run_current_line
|
|
79
|
+
|
|
80
|
+
=begin rdoc
|
|
81
|
+
@param [Ruber::PluginSpecification] the plugin specification object associated with
|
|
82
|
+
the plugin
|
|
83
|
+
=end
|
|
84
|
+
def initialize psf
|
|
85
|
+
super psf, :rspec, {:scope => [:global]}, nil, false
|
|
86
|
+
Ruber[:autosave].register_plugin self, true
|
|
87
|
+
@formatter = File.join File.dirname(__FILE__), 'ruber_rspec_formatter'
|
|
88
|
+
self.connect(SIGNAL('process_finished(int, QString)')){Ruber[:main_window].set_state 'rspec_running', false}
|
|
89
|
+
Ruber[:main_window].set_state 'rspec_running', false
|
|
90
|
+
|
|
91
|
+
switch_prc = Proc.new{|states| states['active_project_exists'] and states['current_document']}
|
|
92
|
+
register_action_handler 'rspec-go_to_file', &switch_prc
|
|
93
|
+
register_action_handler 'rspec-go_to_spec', &switch_prc
|
|
94
|
+
register_action_handler 'rspec-run_all' do |states|
|
|
95
|
+
states['active_project_exists'] and !states['rspec_running']
|
|
96
|
+
end
|
|
97
|
+
current_prc = Proc.new do |states|
|
|
98
|
+
states['active_project_exists'] and states['current_document'] and
|
|
99
|
+
!states['rspec_running']
|
|
100
|
+
end
|
|
101
|
+
register_action_handler 'rspec-run_current', ¤t_prc
|
|
102
|
+
register_action_handler 'rspec-run_current_line', ¤t_prc
|
|
103
|
+
|
|
104
|
+
Ruber[:components].connect(SIGNAL('feature_loaded(QString, QObject*)')) do |f, o|
|
|
105
|
+
o.register_plugin self, true if f == 'autosave'
|
|
106
|
+
end
|
|
107
|
+
@output_widget = @widget
|
|
108
|
+
end
|
|
109
|
+
|
|
110
|
+
=begin rdoc
|
|
111
|
+
Runs rspec for the given files
|
|
112
|
+
|
|
113
|
+
The output of spec is displayed in the associated output widget.
|
|
114
|
+
|
|
115
|
+
Files are autosaved before running rspec.
|
|
116
|
+
|
|
117
|
+
If spec is already running, or if autosaving fails, noting is done.
|
|
118
|
+
|
|
119
|
+
@param [Array<String>] files the spec files to run. Nonexisting files are ignored
|
|
120
|
+
@param [Hash] opts options to fine-tune the behaviour of spec
|
|
121
|
+
@param [Hash] autosave_opts options telling whether and how autosave files
|
|
122
|
+
@param [Proc] blk a block to pass to autosave. If not given, no block will be
|
|
123
|
+
passed to autosave
|
|
124
|
+
|
|
125
|
+
@option opts [String] :ruby (Ruber[:config][:ruby, :ruby]) the ruby interpreter
|
|
126
|
+
to use
|
|
127
|
+
@option opts [Array<String>] :ruby_options (Ruber[:config][:ruby, :ruby_options])
|
|
128
|
+
the options to pass to the ruby interpreter
|
|
129
|
+
@option opts [String] :spec ('spec') the path of the spec command to use
|
|
130
|
+
@option opts [Array<String>] :spec_options ([]) the options to pass to the spec
|
|
131
|
+
program
|
|
132
|
+
@option opts [String] :dir ('.') the directory to run spec from
|
|
133
|
+
@option opts [Boolean] :full_backtraces (nil) whether or not to pass the @-b@ option
|
|
134
|
+
to the spec program
|
|
135
|
+
|
|
136
|
+
@option autosave_opts [Array<Document>,Symbol] :files (nil) the documents to autosave.
|
|
137
|
+
It has the same meaning as second parameter to {Autosave::AutosavePlugin#autosave}.
|
|
138
|
+
If it's *nil*, autosave won't be used
|
|
139
|
+
@option autosave_opts [Symbol, Plugin] :plugin (Ruber[:rspec]) the value to pass
|
|
140
|
+
as first argument to {Autosave::AutosavePlugin#autosave}
|
|
141
|
+
@option autosave_opts [Boolean] :stop_on_failure (nil) as in {Autosave::AutosavePlugin#autosave}
|
|
142
|
+
@option autosave_opts [Symbol] :on_failure (nil) as in {Autosave::AutosavePlugin#autosave}
|
|
143
|
+
@option autosave_opts [String] :message (nil) as in {Autosave::AutosavePlugin#autosave}
|
|
144
|
+
|
|
145
|
+
@return [Boolean] *true* if the process is started and *false* otherwise
|
|
146
|
+
(including the case when the process was already running or autosaving failed)
|
|
147
|
+
=end
|
|
148
|
+
def run_rspec files, opts, autosave_opts = {}, &blk
|
|
149
|
+
default_opts = {
|
|
150
|
+
:ruby => Ruber[:config][:ruby, :ruby],
|
|
151
|
+
:ruby_options => Ruber[:config][:ruby, :ruby_options],
|
|
152
|
+
:spec => 'spec',
|
|
153
|
+
:spec_options => [],
|
|
154
|
+
:dir => '.'
|
|
155
|
+
}
|
|
156
|
+
opts = default_opts.merge opts
|
|
157
|
+
return false if @process.state != Qt::Process::NotRunning
|
|
158
|
+
@widget.clear_output
|
|
159
|
+
files = files.select{|f| File.exist? f}
|
|
160
|
+
# if files.empty?
|
|
161
|
+
# KDE::MessageBox.sorry nil, 'No spec file has been found. RSpec won\'t be run'
|
|
162
|
+
# return false
|
|
163
|
+
# end
|
|
164
|
+
if autosave_opts[:files]
|
|
165
|
+
plug = autosave_opts[:plugin] || self
|
|
166
|
+
what = autosave_opts[:files]
|
|
167
|
+
return false unless Ruber[:autosave].autosave plug,
|
|
168
|
+
what, autosave_opts, &blk
|
|
169
|
+
end
|
|
170
|
+
full_backtraces = opts[:full_backtraces] ? %w[-b] : []
|
|
171
|
+
args = [opts[:spec]] + %W[-r #{@formatter} -f Ruber::RSpec::Formatter] +
|
|
172
|
+
opts[:spec_options] + full_backtraces + files
|
|
173
|
+
@widget.working_directory = opts[:dir]
|
|
174
|
+
@display_standard_error = opts[:stderr]
|
|
175
|
+
Ruber[:main_window].activate_tool(@widget)
|
|
176
|
+
Ruber[:main_window].change_state 'rspec_running', true
|
|
177
|
+
title = ([opts[:spec].split('/')[-1]] + opts[:spec_options]+ full_backtraces + files).join ' '
|
|
178
|
+
run_process opts[:ruby], opts[:dir], opts[:ruby_options] + args, title
|
|
179
|
+
@widget.model.item(0,0).tool_tip = ([opts[:ruby]] + opts[:ruby_options] + args).join " "
|
|
180
|
+
true
|
|
181
|
+
end
|
|
182
|
+
|
|
183
|
+
private
|
|
184
|
+
|
|
185
|
+
=begin rdoc
|
|
186
|
+
Override of {ExternalProgramPlugin#process_standard_output}
|
|
187
|
+
|
|
188
|
+
It parses the output from the _spec_ program (generated with the
|
|
189
|
+
{Ruber::RSpec::Formatter Ruber rspec formatter}) and displays the data it contains
|
|
190
|
+
appropriately.
|
|
191
|
+
@param [Array] lines the output, split in lines
|
|
192
|
+
@return [nil]
|
|
193
|
+
=end
|
|
194
|
+
def process_standard_output lines
|
|
195
|
+
items = parse_spec_output lines.join "\n"
|
|
196
|
+
items.each do |it|
|
|
197
|
+
if it.is_a? String then @widget.model.insert_lines it, :output, nil
|
|
198
|
+
else @widget.display_example it
|
|
199
|
+
end
|
|
200
|
+
end
|
|
201
|
+
nil
|
|
202
|
+
end
|
|
203
|
+
|
|
204
|
+
=begin rdoc
|
|
205
|
+
Override of {ExternalProgramPlugin#process_standard_error}
|
|
206
|
+
@param [Array] lines the standard error output, split in lines
|
|
207
|
+
@return [nil]
|
|
208
|
+
=end
|
|
209
|
+
def process_standard_error lines
|
|
210
|
+
@widget.model.insert_lines lines, :output1, nil
|
|
211
|
+
nil
|
|
212
|
+
end
|
|
213
|
+
|
|
214
|
+
=begin rdoc
|
|
215
|
+
Parses the output of the _spec_ command
|
|
216
|
+
|
|
217
|
+
This method only works if the {Ruber::RSpec::Formatter Ruber rspec formatter}
|
|
218
|
+
is used.
|
|
219
|
+
|
|
220
|
+
The output is split according in regions between {STARTING_DELIMITER} and
|
|
221
|
+
{ENDING_DELIMITER}. All text inside these regions is considered the YAML dump of
|
|
222
|
+
a hash and converted back to a hash. All text which is not between a pair of
|
|
223
|
+
delimiters, as well as the text which can't be converted by YAML is left as is
|
|
224
|
+
@param [String] str the output to parse
|
|
225
|
+
@return [<Hash, String>] the result of the parse. The text which was successfully
|
|
226
|
+
parsed by YAML is stored as a hash, while the rest is stored as strings. Order is
|
|
227
|
+
preserved.
|
|
228
|
+
=end
|
|
229
|
+
def parse_spec_output str
|
|
230
|
+
sc = StringScanner.new str
|
|
231
|
+
res = []
|
|
232
|
+
until sc.eos?
|
|
233
|
+
match = sc.scan_until(STARTING_DELIMITER) || sc.rest
|
|
234
|
+
found = match.sub!(STARTING_DELIMITER, '')
|
|
235
|
+
res << match
|
|
236
|
+
if found
|
|
237
|
+
yaml = sc.scan_until(ENDING_DELIMITER) || sc.rest
|
|
238
|
+
yaml.sub! ENDING_DELIMITER, ''
|
|
239
|
+
res << (YAML.load(yaml) rescue yaml)
|
|
240
|
+
else sc.terminate
|
|
241
|
+
end
|
|
242
|
+
end
|
|
243
|
+
res
|
|
244
|
+
end
|
|
245
|
+
|
|
246
|
+
=begin rdoc
|
|
247
|
+
Runs all the specs for the project.
|
|
248
|
+
@return [nil]
|
|
249
|
+
=end
|
|
250
|
+
def run_all
|
|
251
|
+
prj = Ruber[:projects].current_project
|
|
252
|
+
unless prj
|
|
253
|
+
KDE::MessageBox.error nil, "You must have an open project to choose this entry.\nYOU SHOULD NEVER SEE THIS MESSAGE"
|
|
254
|
+
return
|
|
255
|
+
end
|
|
256
|
+
opts = options prj
|
|
257
|
+
opts[:files] = Dir.glob File.join(opts[:specs_dir], opts[:filter])
|
|
258
|
+
run_rspec_for prj, opts, :files => :project_files, :on_failure => :ask, :message => 'Do you want to run the tests all the same?'
|
|
259
|
+
nil
|
|
260
|
+
end
|
|
261
|
+
|
|
262
|
+
=begin rdoc
|
|
263
|
+
Runs the specs corresponding to the current file
|
|
264
|
+
|
|
265
|
+
If the current file is a code file, the corresponding spec file will be run. If
|
|
266
|
+
it is a spec file, the file itself will be run.
|
|
267
|
+
|
|
268
|
+
To decide whether the current file is a spec or a code file, this method uses
|
|
269
|
+
the patters stored in the @rspec/spec_pattern@ project setting to build the names
|
|
270
|
+
of the spec files associated with the current file, which is assumed to be a code
|
|
271
|
+
file. If none of these files exist, then the current file is treated as a spec file,
|
|
272
|
+
otherwise one of the spec files thus generated is run.
|
|
273
|
+
|
|
274
|
+
*Note:* the way this method works implies that if it is called on a code
|
|
275
|
+
file which doesn't have an associated spec file, rspec will be run on that file,
|
|
276
|
+
which, most likely, will cause it to fail.
|
|
277
|
+
|
|
278
|
+
@return [Boolean] *true* if the spec program is started and *false* otherwise
|
|
279
|
+
(including the case when the process was already running or autosaving failed)
|
|
280
|
+
=end
|
|
281
|
+
def run_current
|
|
282
|
+
prj = Ruber[:projects].current_project
|
|
283
|
+
unless prj
|
|
284
|
+
KDE::MessageBox.error nil, "You must have an open project to choose this entry.\nYOU SHOULD NEVER SEE THIS MESSAGE"
|
|
285
|
+
return
|
|
286
|
+
end
|
|
287
|
+
opts = options prj
|
|
288
|
+
doc = Ruber[:main_window].current_document
|
|
289
|
+
unless doc
|
|
290
|
+
KDE::MessageBox.error nil, "You must have an open document to choose this entry.\nYOU SHOULD NEVER SEE THIS MESSAGE"
|
|
291
|
+
return
|
|
292
|
+
end
|
|
293
|
+
files = specs_for_file opts, doc.path
|
|
294
|
+
files.reject!{|f| !File.exist? f}
|
|
295
|
+
opts[:files] = files.empty? ? [doc.path] : files
|
|
296
|
+
run_rspec_for prj, opts, :files => :documents_with_file, :on_failure => :ask,
|
|
297
|
+
:message => 'Do you want to run the tests all the same?'
|
|
298
|
+
end
|
|
299
|
+
|
|
300
|
+
=begin rdoc
|
|
301
|
+
Runs the example(s) in the current line
|
|
302
|
+
|
|
303
|
+
Similar to {#run_current}, but tells spec to run only the example or example
|
|
304
|
+
group corresponding to the line where the cursor is (using spec's -l option).
|
|
305
|
+
Besides, unlike {#run_current}, this method can only be called when the
|
|
306
|
+
current file is the example file, not the source.
|
|
307
|
+
|
|
308
|
+
@return [Boolean] *true* if the spec program is started and *false* otherwise
|
|
309
|
+
(including the case when the process was already running or autosaving failed)
|
|
310
|
+
=end
|
|
311
|
+
def run_current_line
|
|
312
|
+
prj = Ruber[:projects].current_project
|
|
313
|
+
unless prj
|
|
314
|
+
KDE::MessageBox.error nil, "You must have an open project to choose this entry.\nYOU SHOULD NEVER SEE THIS MESSAGE"
|
|
315
|
+
return
|
|
316
|
+
end
|
|
317
|
+
opts = options prj
|
|
318
|
+
doc = Ruber[:main_window].current_document
|
|
319
|
+
unless doc
|
|
320
|
+
KDE::MessageBox.error nil, "You must have an open document to choose this entry.\nYOU SHOULD NEVER SEE THIS MESSAGE"
|
|
321
|
+
return
|
|
322
|
+
end
|
|
323
|
+
line = doc.view.cursor_position.line + 1
|
|
324
|
+
files = specs_for_file opts, doc.path
|
|
325
|
+
files.reject!{|f| !File.exist? f}
|
|
326
|
+
opts[:files] = files.empty? ? [doc.path] : files
|
|
327
|
+
opts[:spec_options] += ["-l", line.to_s]
|
|
328
|
+
run_rspec_for prj, opts, :files => :documents_with_file, :on_failure => :ask,
|
|
329
|
+
:message => 'Do you want to run the tests all the same?'
|
|
330
|
+
end
|
|
331
|
+
|
|
332
|
+
=begin rdoc
|
|
333
|
+
Runs the spec command for the given object
|
|
334
|
+
|
|
335
|
+
It works like {RubyRunner::RubyRunnerPluginInternal#ruby_command_for RubyRunner::RubyRunnerPlugin#ruby_command_for}
|
|
336
|
+
but already takes care of the user's settings regarding the path of the spec program.
|
|
337
|
+
|
|
338
|
+
@param [AbstractProject, Document, String, nil] origin see the _target_ argument
|
|
339
|
+
of {RubyRunner::RubyRunnerPluginInternal#option_for RubyRunner::RubyRunnerPlugin#option_for}
|
|
340
|
+
@param [Hash] opts the options to pass to the _spec_ program. It has the same
|
|
341
|
+
meaning as the _opts_ argument to {#run_rspec}, but can also contain an additional
|
|
342
|
+
entry (see below)
|
|
343
|
+
@param [Hash] autosave_opts see the _autosave_opts_ argument of {#run_rspec}
|
|
344
|
+
@param [Proc] blk see the _blk_ argument of {#run_rspec}
|
|
345
|
+
|
|
346
|
+
@option opts [Array<String>] :files the files to pass to the spec program
|
|
347
|
+
|
|
348
|
+
@return [Boolean] *true* if the process is started and *false* otherwise
|
|
349
|
+
(including the case when the process was already running or autosaving failed)
|
|
350
|
+
=end
|
|
351
|
+
def run_rspec_for origin, opts, autosave_opts = {}, &blk
|
|
352
|
+
process.kill
|
|
353
|
+
ruby, *cmd = ruby_command_for origin, opts[:dir]
|
|
354
|
+
opts = {:ruby => ruby, :ruby_options => cmd}.merge opts
|
|
355
|
+
run_rspec opts[:files], opts, autosave_opts, &blk
|
|
356
|
+
end
|
|
357
|
+
|
|
358
|
+
=begin rdoc
|
|
359
|
+
Collects all the options relative to this plugin from a project
|
|
360
|
+
|
|
361
|
+
*Note:* *never* use destructive methods on the values contained in this hash. Doing so will
|
|
362
|
+
change the options stored in the project, which most likely isn't what you want.
|
|
363
|
+
If you need to change the options, make duplicates of the values
|
|
364
|
+
|
|
365
|
+
@param [AbstractProject] prj the project to retrieve options from
|
|
366
|
+
@return [Hash] an hash containing the options stored in the project. The correspondence
|
|
367
|
+
between options and entries in this hash is the following:
|
|
368
|
+
* @:rspec/executable@ → @:spec@
|
|
369
|
+
* @:rspec/options@ → @:spec_options@
|
|
370
|
+
* @:rspec/spec_directory@ → @:specs_dir@
|
|
371
|
+
* @:rspec/spec_files@ → @:filter@
|
|
372
|
+
* @:rspec/spec_pattern@ → @:pattern@
|
|
373
|
+
* @:rspec/full_backtraces@ → @:full_backtraces@
|
|
374
|
+
|
|
375
|
+
Besides, the above entries, the hash also contains a @:dir@ entry which contains
|
|
376
|
+
the project directory.
|
|
377
|
+
=end
|
|
378
|
+
def options prj
|
|
379
|
+
res = {}
|
|
380
|
+
res[:spec] = prj[:rspec, :executable]
|
|
381
|
+
res[:spec_options] = prj[:rspec, :options]
|
|
382
|
+
res[:specs_dir] = prj[:rspec, :spec_directory, :absolute]
|
|
383
|
+
res[:filter] = prj[:rspec, :spec_files]
|
|
384
|
+
res[:pattern] = prj[:rspec, :spec_pattern]
|
|
385
|
+
res[:dir] = prj.project_directory
|
|
386
|
+
res[:full_backtraces] = prj[:rspec, :full_backtraces]
|
|
387
|
+
res
|
|
388
|
+
end
|
|
389
|
+
|
|
390
|
+
=begin rdoc
|
|
391
|
+
Opens the spec file(s) associated with the current document in the editor
|
|
392
|
+
|
|
393
|
+
@return [nil]
|
|
394
|
+
=end
|
|
395
|
+
def go_to_spec
|
|
396
|
+
prj = Ruber[:projects].current_project
|
|
397
|
+
unless prj
|
|
398
|
+
KDE::MessageBox.error nil, "You must have an open project to choose this entry.\nYOU SHOULD NEVER SEE THIS MESSAGE"
|
|
399
|
+
return
|
|
400
|
+
end
|
|
401
|
+
opts = options prj
|
|
402
|
+
doc = Ruber[:main_window].current_document
|
|
403
|
+
unless prj
|
|
404
|
+
KDE::MessageBox.error nil, "You must have an open document to choose this entry.\nYOU SHOULD NEVER SEE THIS MESSAGE"
|
|
405
|
+
return
|
|
406
|
+
end
|
|
407
|
+
files = specs_for_file opts, doc.path
|
|
408
|
+
files.each{|f| Ruber[:main_window].display_document f if File.exist? f}
|
|
409
|
+
nil
|
|
410
|
+
end
|
|
411
|
+
|
|
412
|
+
=begin rdoc
|
|
413
|
+
Opens the code file associated with the current spec file in the editor
|
|
414
|
+
|
|
415
|
+
This method assumes the current file is a spec file and attempts to find the
|
|
416
|
+
correspondent code file and open it in the editor. It does nothing if no correspondent
|
|
417
|
+
code file can be found.
|
|
418
|
+
@return [nil]
|
|
419
|
+
@see #file_for_spec
|
|
420
|
+
=end
|
|
421
|
+
def go_to_file
|
|
422
|
+
prj = Ruber[:projects].current_project
|
|
423
|
+
unless prj
|
|
424
|
+
KDE::MessageBox.error nil, "You must have an open project to choose this entry.\nYOU SHOULD NEVER SEE THIS MESSAGE"
|
|
425
|
+
return
|
|
426
|
+
end
|
|
427
|
+
opts = options prj
|
|
428
|
+
doc = Ruber[:main_window].current_document
|
|
429
|
+
unless prj
|
|
430
|
+
KDE::MessageBox.error nil, "You must have an open document to choose this entry.\nYOU SHOULD NEVER SEE THIS MESSAGE"
|
|
431
|
+
return
|
|
432
|
+
end
|
|
433
|
+
file = file_for_spec prj, doc.path
|
|
434
|
+
Ruber[:main_window].display_document file if file and File.exist? file
|
|
435
|
+
nil
|
|
436
|
+
end
|
|
437
|
+
|
|
438
|
+
=begin rdoc
|
|
439
|
+
Determines all possible specs files associated with a code file
|
|
440
|
+
|
|
441
|
+
The names of the possible spec files are obtained replacing the @%f@ tag in each
|
|
442
|
+
entry of the @rspec/pattern@ setting with the name of the file (without checking
|
|
443
|
+
whether the files actually exist).
|
|
444
|
+
|
|
445
|
+
@param [String] file the name of the code file
|
|
446
|
+
@return [<String>] the names of the possible spec file associated with _file_
|
|
447
|
+
=end
|
|
448
|
+
def specs_for_file opts, file
|
|
449
|
+
file = File.basename file, '.rb'
|
|
450
|
+
res = opts[:pattern].map{|i| File.join opts[:specs_dir], i.gsub('%f', file)}
|
|
451
|
+
res
|
|
452
|
+
end
|
|
453
|
+
|
|
454
|
+
=begin rdoc
|
|
455
|
+
The name of the code file associated with a given spec file
|
|
456
|
+
|
|
457
|
+
To find out which code file is associated with the given spec file, this method
|
|
458
|
+
takes all the project files and constructs the file names of all the specs associated
|
|
459
|
+
to them according to the @rspec/spec_pattern@ project option. As soon as one of
|
|
460
|
+
the generated file names matches the given spec file, the generating file is returned.
|
|
461
|
+
|
|
462
|
+
@param [Ruber::AbstractProject] prj the project containing the settings to use
|
|
463
|
+
@param [String] file the name of the spec file to find the code file for
|
|
464
|
+
@return [String] the absolute path of a file _file_ is a spec of, according
|
|
465
|
+
to the settings contained in _prj_.
|
|
466
|
+
=end
|
|
467
|
+
def file_for_spec prj, file
|
|
468
|
+
pattern = prj[:spec_pattern]
|
|
469
|
+
opts = options prj
|
|
470
|
+
prj.project_files.abs.find{|f| specs_for_file( opts, f).include? file}
|
|
471
|
+
end
|
|
472
|
+
|
|
473
|
+
=begin rdoc
|
|
474
|
+
Override of {ExternalProgramPlugin#display_exit_message}
|
|
475
|
+
|
|
476
|
+
It works as the base class's method except when the program exits successfully,
|
|
477
|
+
in which case it does nothing.
|
|
478
|
+
|
|
479
|
+
See {ExternalProgramPlugin#display_exit_message} for the meaning of the parameters
|
|
480
|
+
|
|
481
|
+
@return nil
|
|
482
|
+
=end
|
|
483
|
+
def display_exit_message code, reason
|
|
484
|
+
super unless reason.empty?
|
|
485
|
+
end
|
|
486
|
+
|
|
487
|
+
end
|
|
488
|
+
|
|
489
|
+
=begin rdoc
|
|
490
|
+
Filter model used by the RSpec output widget
|
|
491
|
+
|
|
492
|
+
It allows to choose whether to accept items corresponding to output to standard error or to reject
|
|
493
|
+
it. To find out if a given item corresponds to the output of standard error or
|
|
494
|
+
standard output, this model uses the data contained in a custom role in the output.
|
|
495
|
+
The index of this role is {RSpec::OutputWidget::OutputTypeRole}.
|
|
496
|
+
=end
|
|
497
|
+
class FilterModel < FilteredOutputWidget::FilterModel
|
|
498
|
+
|
|
499
|
+
slots 'toggle_display_stderr(bool)'
|
|
500
|
+
|
|
501
|
+
=begin rdoc
|
|
502
|
+
Whether output from standard error should be displayed or not
|
|
503
|
+
@return [Boolean]
|
|
504
|
+
=end
|
|
505
|
+
attr_reader :display_stderr
|
|
506
|
+
|
|
507
|
+
=begin rdoc
|
|
508
|
+
Create a new instance
|
|
509
|
+
|
|
510
|
+
The new instance is set not to show the output from standard error
|
|
511
|
+
|
|
512
|
+
@param [Qt::Object, nil] parent the parent object
|
|
513
|
+
=end
|
|
514
|
+
def initialize parent = nil
|
|
515
|
+
super
|
|
516
|
+
@display_stderr = false
|
|
517
|
+
end
|
|
518
|
+
|
|
519
|
+
=begin rdoc
|
|
520
|
+
Sets whether to display or ignore items corresponding to output to standard error
|
|
521
|
+
|
|
522
|
+
If this choice has changed, the model is invalidated.
|
|
523
|
+
|
|
524
|
+
@param [Boolean] val whether to display or ignore the output to standard error
|
|
525
|
+
@return [Boolean] _val_
|
|
526
|
+
=end
|
|
527
|
+
def display_stderr= val
|
|
528
|
+
old, @display_stderr = @display_stderr, val
|
|
529
|
+
invalidate if old != @display_stderr
|
|
530
|
+
@display_standard_error
|
|
531
|
+
end
|
|
532
|
+
alias_method :toggle_display_stderr, :display_stderr=
|
|
533
|
+
|
|
534
|
+
=begin rdoc
|
|
535
|
+
Override of {FilteredOutputWidget::FilterModel#filterAcceptsRow}
|
|
536
|
+
|
|
537
|
+
According to the value of {#display_stderr}, it can filter out items corresponding
|
|
538
|
+
to standard error. In all other respects, it behaves as the base class method.
|
|
539
|
+
@param [Integer] r the row number
|
|
540
|
+
@param [Qt::ModelIndex] parent the parent index
|
|
541
|
+
@return [Boolean] *true* if the row should be displayed and *false* otherwise
|
|
542
|
+
=end
|
|
543
|
+
def filterAcceptsRow r, parent
|
|
544
|
+
if !@display_stderr
|
|
545
|
+
idx = source_model.index(r,0,parent)
|
|
546
|
+
return false if idx.data(OutputWidget::OutputTypeRole).to_string == 'output1'
|
|
547
|
+
end
|
|
548
|
+
super
|
|
549
|
+
end
|
|
550
|
+
|
|
551
|
+
end
|
|
552
|
+
|
|
553
|
+
=begin rdoc
|
|
554
|
+
Tool widget used by the rspec plugin.
|
|
555
|
+
|
|
556
|
+
It displays the output from the spec program in a multi column tree. The name of
|
|
557
|
+
failing or pending examples are displayed in a full line; all other information,
|
|
558
|
+
such as the location of the example, the error message and so on are displayed
|
|
559
|
+
in child items.
|
|
560
|
+
|
|
561
|
+
While the examples are being run, a progress bar is shown.
|
|
562
|
+
=end
|
|
563
|
+
class ToolWidget < FilteredOutputWidget
|
|
564
|
+
|
|
565
|
+
slots :spec_started, 'spec_finished(int, QString)'
|
|
566
|
+
|
|
567
|
+
=begin rdoc
|
|
568
|
+
@param [Qt::Widget, nil] parent the parent widget
|
|
569
|
+
=end
|
|
570
|
+
def initialize parent = nil
|
|
571
|
+
super parent, :view => :tree, :filter => FilterModel.new
|
|
572
|
+
@ignore_word_wrap_option = true
|
|
573
|
+
view.word_wrap = true
|
|
574
|
+
view.horizontal_scroll_mode = Qt::AbstractItemView::ScrollPerItem
|
|
575
|
+
# view.text_elide_mode = Qt::ElideNone
|
|
576
|
+
model.append_column [] if model.column_count < 2
|
|
577
|
+
@progress_bar = Qt::ProgressBar.new(self){|w| w.hide}
|
|
578
|
+
layout.add_widget @progress_bar, 1,0
|
|
579
|
+
view.header_hidden = true
|
|
580
|
+
view.header.resize_mode = Qt::HeaderView::ResizeToContents
|
|
581
|
+
connect Ruber[:rspec], SIGNAL(:process_started), self, SLOT(:spec_started)
|
|
582
|
+
connect Ruber[:rspec], SIGNAL('process_finished(int, QString)'), self, SLOT('spec_finished(int, QString)')
|
|
583
|
+
filter.connect(SIGNAL('rowsInserted(QModelIndex, int, int)')) do |par, st, en|
|
|
584
|
+
if !par.valid?
|
|
585
|
+
st.upto(en) do |i|
|
|
586
|
+
view.set_first_column_spanned i, par, true
|
|
587
|
+
end
|
|
588
|
+
end
|
|
589
|
+
end
|
|
590
|
+
setup_actions
|
|
591
|
+
end
|
|
592
|
+
|
|
593
|
+
=begin rdoc
|
|
594
|
+
Displays the data relative to an example in the widget
|
|
595
|
+
|
|
596
|
+
Actually, this method simply passes its argument to a more specific method, depending
|
|
597
|
+
on the data it contains.
|
|
598
|
+
|
|
599
|
+
@param [Hash] data a hash containing the data describing the results of running
|
|
600
|
+
the example. This hash must contain the @:type@ key, which tells which kind of
|
|
601
|
+
event the hash describes. The other entries change depending on the method which
|
|
602
|
+
will be called, which is determined according to the @:type@ entry:
|
|
603
|
+
* @:success@: {#display_successful_example}
|
|
604
|
+
* @:failure@: {#display_failed_example}
|
|
605
|
+
* @:pending@: {#display_pending_example}
|
|
606
|
+
* @:new_example@: {#change_current_example}
|
|
607
|
+
* @:start@: {#set_example_count}
|
|
608
|
+
* @:summary@: {#display_summary}
|
|
609
|
+
If the @:type@ entry doesn't have one of the previous values, the hash will be
|
|
610
|
+
converted to a string and displayed in the widget
|
|
611
|
+
=end
|
|
612
|
+
def display_example data
|
|
613
|
+
unless data.is_a?(Hash)
|
|
614
|
+
model.insert_lines data.to_s, :output, nil
|
|
615
|
+
return
|
|
616
|
+
end
|
|
617
|
+
case data[:type]
|
|
618
|
+
when :success then display_successful_example data
|
|
619
|
+
when :failure then display_failed_example data
|
|
620
|
+
when :pending then display_pending_example data
|
|
621
|
+
when :new_example then change_current_example data
|
|
622
|
+
when :start then set_example_count data
|
|
623
|
+
when :summary then display_summary data
|
|
624
|
+
else model.insert_lines data.to_s, :output, nil
|
|
625
|
+
end
|
|
626
|
+
end
|
|
627
|
+
|
|
628
|
+
=begin rdoc
|
|
629
|
+
Changes the current example
|
|
630
|
+
|
|
631
|
+
Currently, this only affects the tool tip displayed by the progress bar.
|
|
632
|
+
|
|
633
|
+
@param [Hash] data the data to use. It must contain the @:description@ entry,
|
|
634
|
+
which contains the text of the tool tip to use.
|
|
635
|
+
@return [nil]
|
|
636
|
+
=end
|
|
637
|
+
def change_current_example data
|
|
638
|
+
@progress_bar.tool_tip = data[:description]
|
|
639
|
+
nil
|
|
640
|
+
end
|
|
641
|
+
|
|
642
|
+
=begin rdoc
|
|
643
|
+
Sets the number of examples found by the spec program.
|
|
644
|
+
|
|
645
|
+
This is used to set the maximum value of the progress bar.
|
|
646
|
+
|
|
647
|
+
@param [Hash] data the data to use. It must contain the @:count@ entry,
|
|
648
|
+
which contains the number of examples
|
|
649
|
+
@return [nil]
|
|
650
|
+
=end
|
|
651
|
+
def set_example_count data
|
|
652
|
+
@progress_bar.maximum = data[:count]
|
|
653
|
+
nil
|
|
654
|
+
end
|
|
655
|
+
|
|
656
|
+
|
|
657
|
+
=begin rdoc
|
|
658
|
+
Updates the progress bar by incrementing its value by one
|
|
659
|
+
|
|
660
|
+
@param [Hash] data the data to use. Currently it's unused
|
|
661
|
+
@return [nil]
|
|
662
|
+
=end
|
|
663
|
+
def display_successful_example data
|
|
664
|
+
@progress_bar.value += 1
|
|
665
|
+
nil
|
|
666
|
+
end
|
|
667
|
+
|
|
668
|
+
=begin rdoc
|
|
669
|
+
Displays information about a failed example in the tool widget.
|
|
670
|
+
|
|
671
|
+
@param [Hash] data the data about the example.
|
|
672
|
+
|
|
673
|
+
@option data [String] :location the line number where the error occurred
|
|
674
|
+
@option data [String] :description the name of the failed example
|
|
675
|
+
@option data [String] :message the explaination of why the example failed
|
|
676
|
+
@option data [String] :exception the content of the exception
|
|
677
|
+
@option data [String] :backtrace the backtrace of the exception (a single new-line separated string)
|
|
678
|
+
@return [nil]
|
|
679
|
+
=end
|
|
680
|
+
def display_failed_example data
|
|
681
|
+
@progress_bar.value += 1
|
|
682
|
+
top = model.insert("[FAILURE] #{data[:description]}", :error, nil).first
|
|
683
|
+
model.insert ['From:', data[:location]], :message, nil, :parent => top
|
|
684
|
+
ex_label = model.insert('Exception:', :message, nil, :parent => top).first
|
|
685
|
+
exception_body = "#{data[:message]} (#{data[:exception]})".split_lines.delete_if{|l| l.strip.empty?}
|
|
686
|
+
#exception_body may contain more than one line and some of them may be empty
|
|
687
|
+
model.set exception_body.shift, :message, ex_label.row, :col => 1, :parent => top
|
|
688
|
+
exception_body.each do |l|
|
|
689
|
+
unless l.strip.empty?
|
|
690
|
+
model.set l, :message, top.row_count, :col => 1, :parent => top
|
|
691
|
+
end
|
|
692
|
+
end
|
|
693
|
+
backtrace = data[:backtrace].split_lines
|
|
694
|
+
back_label, back = model.insert(['Backtrace:', backtrace.shift], :message, nil, :parent => top)
|
|
695
|
+
backtrace.each do |l|
|
|
696
|
+
model.insert [nil, l], :message, nil, :parent => back_label
|
|
697
|
+
end
|
|
698
|
+
top_index = filter.map_from_source(top.index)
|
|
699
|
+
view.collapse top_index
|
|
700
|
+
view.set_first_column_spanned top_index.row, Qt::ModelIndex.new, true
|
|
701
|
+
view.expand filter.map_from_source(back_label.index)
|
|
702
|
+
nil
|
|
703
|
+
end
|
|
704
|
+
|
|
705
|
+
=begin rdoc
|
|
706
|
+
Displays information about a pending example in the tool widget
|
|
707
|
+
|
|
708
|
+
@param [Hash] data
|
|
709
|
+
@option data [String] :location the line number where the error occurred
|
|
710
|
+
@option data [String] :description the name of the failed example
|
|
711
|
+
@option data [String] :message the explaination of why the example failed
|
|
712
|
+
@return [nil]
|
|
713
|
+
=end
|
|
714
|
+
def display_pending_example data
|
|
715
|
+
@progress_bar.value += 1
|
|
716
|
+
top = model.insert("[PENDING] #{data[:description]}", :warning, nil)[0]
|
|
717
|
+
model.insert ['From:', data[:location]], :message, nil, :parent => top
|
|
718
|
+
model.insert ['Message: ', "#{data[:message]} (#{data[:exception]})"], :message, nil, :parent => top
|
|
719
|
+
nil
|
|
720
|
+
end
|
|
721
|
+
|
|
722
|
+
=begin rdoc
|
|
723
|
+
Displays a summary of the spec run in the tool widget
|
|
724
|
+
|
|
725
|
+
The summary is a single title line which contains the number or successful, pending
|
|
726
|
+
and failed example.
|
|
727
|
+
|
|
728
|
+
@param [Hash] data
|
|
729
|
+
@option data [Integer] :total the number of run examples
|
|
730
|
+
@option data [Integer] :passed the number of passed examples
|
|
731
|
+
@option data [Integer] :failed the number of failed examples
|
|
732
|
+
@option data [Integer] :pending the number of pending examples
|
|
733
|
+
@return [nil]
|
|
734
|
+
=end
|
|
735
|
+
def display_summary data
|
|
736
|
+
@progress_bar.hide
|
|
737
|
+
if data[:passed] == data[:total]
|
|
738
|
+
self.title = "[SUMMARY] All #{data[:total]} examples passed"
|
|
739
|
+
set_output_type model.index(0,0), :message_good
|
|
740
|
+
else
|
|
741
|
+
text = "[SUMMARY] Examples: #{data[:total]}"
|
|
742
|
+
text << " Failed: #{data[:failure]}" if data[:failure] > 0
|
|
743
|
+
text << " Pending: #{data[:pending]}" if data[:pending] > 0
|
|
744
|
+
text << " Passed: #{data[:passed]}"
|
|
745
|
+
self.title = text
|
|
746
|
+
type = data[:failure] > 0 ? :message_bad : :message
|
|
747
|
+
set_output_type model.index(0,0), type
|
|
748
|
+
end
|
|
749
|
+
nil
|
|
750
|
+
end
|
|
751
|
+
|
|
752
|
+
=begin rdoc
|
|
753
|
+
Override of {OutputWidget#title=}
|
|
754
|
+
|
|
755
|
+
It's needed to have the title element span all columns
|
|
756
|
+
|
|
757
|
+
@param [String] val the new title
|
|
758
|
+
=end
|
|
759
|
+
def title= val
|
|
760
|
+
super
|
|
761
|
+
model.item(0,0).tool_tip = val
|
|
762
|
+
view.set_first_column_spanned 0, Qt::ModelIndex.new, true
|
|
763
|
+
end
|
|
764
|
+
|
|
765
|
+
private
|
|
766
|
+
|
|
767
|
+
=begin rdoc
|
|
768
|
+
Resets the tool widget and sets the cursor to busy
|
|
769
|
+
@return [nil]
|
|
770
|
+
=end
|
|
771
|
+
def spec_started
|
|
772
|
+
@progress_bar.maximum = 0
|
|
773
|
+
@progress_bar.value = 0
|
|
774
|
+
@progress_bar.show
|
|
775
|
+
@progress_bar.tool_tip = ''
|
|
776
|
+
actions['show_stderr'].checked = false
|
|
777
|
+
self.cursor = Qt::Cursor.new(Qt::BusyCursor)
|
|
778
|
+
nil
|
|
779
|
+
end
|
|
780
|
+
|
|
781
|
+
=begin rdoc
|
|
782
|
+
Does the necessary cleanup for when spec finishes running
|
|
783
|
+
|
|
784
|
+
It hides the progress widget and restores the default cursor.
|
|
785
|
+
|
|
786
|
+
@param [Integer] code the exit code
|
|
787
|
+
@param [String] reason why the program exited
|
|
788
|
+
@return [nil]
|
|
789
|
+
=end
|
|
790
|
+
def spec_finished code, reason
|
|
791
|
+
@progress_bar.hide
|
|
792
|
+
@progress_bar.value = 0
|
|
793
|
+
@progress_bar.maximum = 100
|
|
794
|
+
self.set_focus
|
|
795
|
+
unset_cursor
|
|
796
|
+
unless reason == 'killed'
|
|
797
|
+
non_stderr_types = %w[message message_good message_bad warning error]
|
|
798
|
+
only_stderr = !model.item(0,0).text.match(/^\[SUMMARY\]/)
|
|
799
|
+
if only_stderr
|
|
800
|
+
1.upto(model.row_count - 1) do |i|
|
|
801
|
+
if non_stderr_types.include? model.item(i,0).data(OutputWidget::OutputTypeRole).to_string
|
|
802
|
+
only_stderr = false
|
|
803
|
+
break
|
|
804
|
+
end
|
|
805
|
+
end
|
|
806
|
+
end
|
|
807
|
+
if only_stderr
|
|
808
|
+
actions['show_stderr'].checked = true
|
|
809
|
+
model.insert "spec wasn't able to run the examples", :message_bad, nil
|
|
810
|
+
end
|
|
811
|
+
end
|
|
812
|
+
nil
|
|
813
|
+
end
|
|
814
|
+
|
|
815
|
+
=begin rdoc
|
|
816
|
+
Creates the additional actions.
|
|
817
|
+
|
|
818
|
+
It adds a single action, which allows the user to chose whether messages from
|
|
819
|
+
standard error should be displayed or not.
|
|
820
|
+
|
|
821
|
+
@return [nil]
|
|
822
|
+
=end
|
|
823
|
+
def setup_actions
|
|
824
|
+
action_list << nil << 'show_stderr'
|
|
825
|
+
a = KDE::ToggleAction.new 'S&how Standard Error', self
|
|
826
|
+
actions['show_stderr'] = a
|
|
827
|
+
a.checked = false
|
|
828
|
+
connect a, SIGNAL('toggled(bool)'), filter, SLOT('toggle_display_stderr(bool)')
|
|
829
|
+
end
|
|
830
|
+
|
|
831
|
+
=begin rdoc
|
|
832
|
+
Override of {OutputWidget#find_filename_in_index}
|
|
833
|
+
|
|
834
|
+
It works as the base class method, but, if it doesn't find a result in _idx_,
|
|
835
|
+
it looks for it in the parent indexes
|
|
836
|
+
|
|
837
|
+
@param [Qt::ModelIndex] idx the index where to look for a file name
|
|
838
|
+
@return [Array<String,Integer>,String,nil] see {OutputWidget#find_filename_in_index}
|
|
839
|
+
=end
|
|
840
|
+
def find_filename_in_index idx
|
|
841
|
+
res = super
|
|
842
|
+
unless res
|
|
843
|
+
idx = idx.parent while idx.parent.valid?
|
|
844
|
+
idx = idx.child(0,1)
|
|
845
|
+
res = super idx if idx.valid?
|
|
846
|
+
end
|
|
847
|
+
res
|
|
848
|
+
end
|
|
849
|
+
|
|
850
|
+
=begin rdoc
|
|
851
|
+
Override of {OutputWidget#text_for_clipboard}
|
|
852
|
+
|
|
853
|
+
@param [<Qt::ModelIndex>] idxs the selected indexes
|
|
854
|
+
@return [QString] the text to copy to the clipboard
|
|
855
|
+
=end
|
|
856
|
+
def text_for_clipboard idxs
|
|
857
|
+
order = {}
|
|
858
|
+
idxs.each do |i|
|
|
859
|
+
val = []
|
|
860
|
+
parent = i
|
|
861
|
+
while parent.parent.valid?
|
|
862
|
+
parent = parent.parent
|
|
863
|
+
val.unshift parent.row
|
|
864
|
+
end
|
|
865
|
+
val << [i.row, i.column]
|
|
866
|
+
order[val] = i
|
|
867
|
+
end
|
|
868
|
+
order = order.sort do |a, b|
|
|
869
|
+
a, b = a[0], b[0]
|
|
870
|
+
res = a[0..-2] <=> b[0..-2]
|
|
871
|
+
if res == 0 then a[-1] <=> b[-1]
|
|
872
|
+
else res
|
|
873
|
+
end
|
|
874
|
+
end
|
|
875
|
+
prev = order.shift[1]
|
|
876
|
+
text = prev.data.valid? ? prev.data.to_string : ''
|
|
877
|
+
order.each do |_, v|
|
|
878
|
+
text << ( (prev.parent == v.parent and prev.row == v.row) ? "\t" : "\n")
|
|
879
|
+
text << (v.data.valid? ? v.data.to_string : '')
|
|
880
|
+
prev = v
|
|
881
|
+
end
|
|
882
|
+
text
|
|
883
|
+
end
|
|
884
|
+
|
|
885
|
+
end
|
|
886
|
+
|
|
887
|
+
=begin rdoc
|
|
888
|
+
Project widget for the RSpec frontend plugin
|
|
889
|
+
=end
|
|
890
|
+
class ProjectWidget < ProjectConfigWidget
|
|
891
|
+
|
|
892
|
+
=begin rdoc
|
|
893
|
+
@param [Qt::Widget,nil] prj the parent widget
|
|
894
|
+
=end
|
|
895
|
+
def initialize prj
|
|
896
|
+
super
|
|
897
|
+
@ui = Ui::RSpecProjectWidget.new
|
|
898
|
+
@ui.setupUi self
|
|
899
|
+
end
|
|
900
|
+
|
|
901
|
+
private
|
|
902
|
+
|
|
903
|
+
=begin rdoc
|
|
904
|
+
Sets the text of the pattern widget
|
|
905
|
+
@param [Array<String>] the pattern to use. They'll be joined with commas to create
|
|
906
|
+
the text to put in the widget
|
|
907
|
+
=end
|
|
908
|
+
def pattern= value
|
|
909
|
+
value.join ', '
|
|
910
|
+
end
|
|
911
|
+
|
|
912
|
+
=begin rdoc
|
|
913
|
+
Parses the content of the pattern widget
|
|
914
|
+
@return [Array<String>] an array containing the patterns
|
|
915
|
+
=end
|
|
916
|
+
def pattern
|
|
917
|
+
@ui._rspec__spec_pattern.text.split(/;\s*/)
|
|
918
|
+
end
|
|
919
|
+
|
|
920
|
+
=begin rdoc
|
|
921
|
+
Changes the text of the "RSpec options" widget
|
|
922
|
+
|
|
923
|
+
@param [Array<String>] value the options to pass to spec. They'll be joined with
|
|
924
|
+
spaces
|
|
925
|
+
=end
|
|
926
|
+
def spec_options= value
|
|
927
|
+
@ui._rspec__options.text = value.join ' '
|
|
928
|
+
end
|
|
929
|
+
|
|
930
|
+
=begin rdoc
|
|
931
|
+
Parses the text of the "RSpec options" widget
|
|
932
|
+
|
|
933
|
+
@return [Array<String>] an array with the options to pass to spec (options with
|
|
934
|
+
quotes around them keep them, as described in {Shellwords.split_with_quotes})
|
|
935
|
+
=end
|
|
936
|
+
def spec_options
|
|
937
|
+
Shellwords.split_with_quotes @ui._rspec__options.text
|
|
938
|
+
end
|
|
939
|
+
|
|
940
|
+
end
|
|
941
|
+
|
|
942
|
+
|
|
943
|
+
end
|
|
944
|
+
|
|
945
|
+
end
|