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
data/ruber.desktop
ADDED
|
@@ -0,0 +1,10 @@
|
|
|
1
|
+
[Desktop Entry]
|
|
2
|
+
Name=Ruber
|
|
3
|
+
Comment=An IDE for the Ruby programming language written in Ruby
|
|
4
|
+
Comment[it]=Un IDE per il linguaggio Ruby scritto in Ruby
|
|
5
|
+
TryExec=ruber
|
|
6
|
+
Exec=ruber %u
|
|
7
|
+
Type=Application
|
|
8
|
+
Categories=Development;IDE;Qt;KDE;
|
|
9
|
+
Icon=ruber
|
|
10
|
+
MimeType=application/x-ruby
|
|
@@ -0,0 +1,174 @@
|
|
|
1
|
+
require 'spec/common'
|
|
2
|
+
|
|
3
|
+
require 'ruber/editor/document'
|
|
4
|
+
|
|
5
|
+
describe Ruber::AnnotationModel do
|
|
6
|
+
|
|
7
|
+
before do
|
|
8
|
+
@components = flexmock{|m| m.should_ignore_missing}
|
|
9
|
+
@w = Qt::Widget.new
|
|
10
|
+
flexmock(Ruber).should_receive(:[]).with(:components).and_return(@components).by_default
|
|
11
|
+
@doc = Ruber::Document.new @app
|
|
12
|
+
@model = Ruber::AnnotationModel.new @doc
|
|
13
|
+
end
|
|
14
|
+
|
|
15
|
+
it 'should inherit KTextEditor::AnnotationModel' do
|
|
16
|
+
@model.should be_kind_of( KTextEditor::AnnotationModel )
|
|
17
|
+
end
|
|
18
|
+
|
|
19
|
+
it 'should include the Enumerable module' do
|
|
20
|
+
Ruber::AnnotationModel.ancestors.include?(Enumerable).should be_true
|
|
21
|
+
end
|
|
22
|
+
|
|
23
|
+
it 'should allow to add annotation types at class level' do
|
|
24
|
+
b = Qt::Brush.new(Qt.yellow)
|
|
25
|
+
@model.class.register_annotation_type :test, b
|
|
26
|
+
h = @model.class.instance_variable_get(:@annotation_types)
|
|
27
|
+
h.should have_key(:test)
|
|
28
|
+
h[:test][0].value.should == b
|
|
29
|
+
h[:test][1].should be_null
|
|
30
|
+
end
|
|
31
|
+
|
|
32
|
+
it 'should raise ArgumentError when registering an already registered annotation type' do
|
|
33
|
+
@model.class.register_annotation_type :test
|
|
34
|
+
lambda{@model.class.register_annotation_type( :test)}.should raise_error( ArgumentError )
|
|
35
|
+
end
|
|
36
|
+
|
|
37
|
+
it 'should allow to add an annotation for an existing line using three parameters' do
|
|
38
|
+
@model.class.register_annotation_type :test
|
|
39
|
+
@doc.text = "abc\ndef\nghi"
|
|
40
|
+
lambda{ @model.add_annotation :test, 1, "message", "tool tip"}.should_not raise_error
|
|
41
|
+
@model.data(1, Qt::DisplayRole).to_string.should == 'message'
|
|
42
|
+
@model.data(1, Qt::ToolTipRole).to_string.should == 'tool tip'
|
|
43
|
+
end
|
|
44
|
+
|
|
45
|
+
it 'should allow to add an annotation for an existing line using one parameter' do
|
|
46
|
+
@model.class.register_annotation_type :test
|
|
47
|
+
@doc.text = "abc\ndef\nghi"
|
|
48
|
+
lambda{ @model.add_annotation Ruber::AnnotationModel::Annotation.new(:test, 1, "message", "tool tip")}.should_not raise_error
|
|
49
|
+
@model.data(1, Qt::DisplayRole).to_string.should == 'message'
|
|
50
|
+
@model.data(1, Qt::ToolTipRole).to_string.should == 'tool tip'
|
|
51
|
+
end
|
|
52
|
+
|
|
53
|
+
it 'should raise IndexError when an annotation is added to a nonexisting line' do
|
|
54
|
+
@model.class.register_annotation_type :test
|
|
55
|
+
@doc.text = "abc\ndef\nghi"
|
|
56
|
+
pending "There's something weird going on here"
|
|
57
|
+
lambda{ @model.add_annotation :test, 5, "message", "tool tip"}.should raise_error(IndexError)
|
|
58
|
+
lambda{ @model.add_annotation Ruber::AnnotationModel::Annotation.new(:test, 3, "message", "tool tip")}.should raise_error(IndexError)
|
|
59
|
+
end
|
|
60
|
+
|
|
61
|
+
it 'should allow to retreive the annotation for a given line' do
|
|
62
|
+
@model.class.register_annotation_type :test
|
|
63
|
+
@doc.text = "abc\ndef\ghi"
|
|
64
|
+
@model.add_annotation :test, 1, "message", "tool tip"
|
|
65
|
+
@model[1].should == Ruber::AnnotationModel::Annotation.new(:test, 1, "message", "tool tip")
|
|
66
|
+
@model.annotation(1).should == Ruber::AnnotationModel::Annotation.new(:test, 1, "message", "tool tip")
|
|
67
|
+
end
|
|
68
|
+
|
|
69
|
+
it 'should tell whether there\'s an annotation for a given line' do
|
|
70
|
+
@model.class.register_annotation_type :test
|
|
71
|
+
@doc.text = "abc\ndef\nghi"
|
|
72
|
+
@model.add_annotation :test, 1, "message", "tool tip"
|
|
73
|
+
@model.should have_annotation(1)
|
|
74
|
+
@model.should_not have_annotation(2)
|
|
75
|
+
end
|
|
76
|
+
|
|
77
|
+
it 'should tell whether there are annotations or not' do
|
|
78
|
+
@model.should_not have_annotations
|
|
79
|
+
@model.should be_empty
|
|
80
|
+
@model.class.register_annotation_type :test
|
|
81
|
+
@doc.text = "abc\ndef\ghi"
|
|
82
|
+
@model.add_annotation :test, 1, "message", "tool tip"
|
|
83
|
+
@model.should have_annotations
|
|
84
|
+
@model.should_not be_empty
|
|
85
|
+
end
|
|
86
|
+
|
|
87
|
+
it 'should allow to remove all annotations' do
|
|
88
|
+
@model.class.register_annotation_type :test
|
|
89
|
+
@doc.text = "abc\ndef\nghi"
|
|
90
|
+
@model.add_annotation :test, 1, "message", "tool tip"
|
|
91
|
+
@model.add_annotation :test, 2, "message", "tool tip"
|
|
92
|
+
@model.clear
|
|
93
|
+
@model.should be_empty
|
|
94
|
+
end
|
|
95
|
+
|
|
96
|
+
it 'should allow to remove the annotations for one line' do
|
|
97
|
+
@model.class.register_annotation_type :test
|
|
98
|
+
@doc.text = "abc\ndef\nghi"
|
|
99
|
+
@model.add_annotation :test, 1, "message", "tool tip"
|
|
100
|
+
@model.add_annotation :test, 2, "message", "tool tip"
|
|
101
|
+
@model.remove_annotation 1
|
|
102
|
+
@model.should_not be_empty
|
|
103
|
+
@model.should_not have_annotation(1)
|
|
104
|
+
end
|
|
105
|
+
|
|
106
|
+
it 'should allow to iterate on all annotations in order' do
|
|
107
|
+
@model.class.register_annotation_type :test
|
|
108
|
+
@doc.text = "abc\ndef\nghi"
|
|
109
|
+
3.times{|i| @model.add_annotation :test, (i+1)%3, ((i+1)%3).to_s, "tool tip"}
|
|
110
|
+
m = flexmock('mock')
|
|
111
|
+
m.should_receive(:test).once.globally.ordered.with("0")
|
|
112
|
+
m.should_receive(:test).once.globally.ordered.with("1")
|
|
113
|
+
m.should_receive(:test).once.globally.ordered.with("2")
|
|
114
|
+
@model.each{|a| m.test(a.msg)}
|
|
115
|
+
end
|
|
116
|
+
|
|
117
|
+
it 'should emit the "annotations_changed()" signal when an annotation is added or removed or the model is cleared' do
|
|
118
|
+
@model.class.register_annotation_type :test
|
|
119
|
+
@doc.text = "abc\ndef\nghi"
|
|
120
|
+
m = flexmock
|
|
121
|
+
m.should_receive(:annotations_changed).times(3)
|
|
122
|
+
@model.connect(SIGNAL('annotations_changed()')){m.annotations_changed}
|
|
123
|
+
@model.add_annotation :test, 1, "message", "tool tip"
|
|
124
|
+
@model.remove_annotation 1
|
|
125
|
+
@model.remove_annotation 0
|
|
126
|
+
@model.block_signals(true)
|
|
127
|
+
@model.add_annotation :test, 2, "message", "tool tip"
|
|
128
|
+
@model.add_annotation :test, 1, "message", "tool tip"
|
|
129
|
+
@model.block_signals(false)
|
|
130
|
+
@model.clear
|
|
131
|
+
end
|
|
132
|
+
|
|
133
|
+
it 'should emit the "annotations_changed(int)" signal when an annotation is added or removed' do
|
|
134
|
+
@model.class.register_annotation_type :test
|
|
135
|
+
@doc.text = "abc\ndef\nghi"
|
|
136
|
+
m = flexmock
|
|
137
|
+
m.should_receive(:annotation_changed).once.globally.ordered.with(1)
|
|
138
|
+
m.should_receive(:annotation_changed).once.globally.ordered.with(1)
|
|
139
|
+
@model.connect(SIGNAL('annotation_changed(int)')){|i| m.annotation_changed(i)}
|
|
140
|
+
@model.add_annotation :test, 1, "message", "tool tip"
|
|
141
|
+
@model.remove_annotation 1
|
|
142
|
+
@model.remove_annotation 0
|
|
143
|
+
end
|
|
144
|
+
|
|
145
|
+
it 'should emit the "lineChanged(int)" signal when an annotation is added or removed' do
|
|
146
|
+
@model.class.register_annotation_type :test
|
|
147
|
+
@doc.text = "abc\ndef\nghi"
|
|
148
|
+
m = flexmock
|
|
149
|
+
m.should_receive(:lineChanged).once.globally.ordered.with(1)
|
|
150
|
+
m.should_receive(:lineChanged).once.globally.ordered.with(1)
|
|
151
|
+
@model.connect(SIGNAL('lineChanged(int)')){|i| m.lineChanged(i)}
|
|
152
|
+
@model.add_annotation :test, 1, "message", "tool tip"
|
|
153
|
+
@model.remove_annotation 1
|
|
154
|
+
@model.remove_annotation 0
|
|
155
|
+
end
|
|
156
|
+
|
|
157
|
+
it 'should emit the "reset()" signal when the model is cleared' do
|
|
158
|
+
@model.class.register_annotation_type :test
|
|
159
|
+
@doc.text = "abc\ndef\nghi"
|
|
160
|
+
m = flexmock
|
|
161
|
+
m.should_receive(:reset).once
|
|
162
|
+
@model.connect(SIGNAL('reset()')){m.reset}
|
|
163
|
+
@model.add_annotation :test, 1, "message", "tool tip"
|
|
164
|
+
@model.clear
|
|
165
|
+
end
|
|
166
|
+
|
|
167
|
+
after do
|
|
168
|
+
@model.class.instance_variable_get(:@annotation_types).clear
|
|
169
|
+
@doc.instance_variable_get(:@doc).closeUrl false
|
|
170
|
+
@doc.dispose
|
|
171
|
+
# @app.quit
|
|
172
|
+
end
|
|
173
|
+
|
|
174
|
+
end
|
data/spec/common.rb
ADDED
|
@@ -0,0 +1,119 @@
|
|
|
1
|
+
require 'fileutils'
|
|
2
|
+
require 'yaml'
|
|
3
|
+
require 'flexmock/argument_types'
|
|
4
|
+
require 'korundum4'
|
|
5
|
+
require 'kio'
|
|
6
|
+
require 'ostruct'
|
|
7
|
+
require 'facets/kernel/require_relative'
|
|
8
|
+
|
|
9
|
+
module QtEnumerable
|
|
10
|
+
|
|
11
|
+
include Enumerable
|
|
12
|
+
|
|
13
|
+
begin
|
|
14
|
+
alias :count_items :count
|
|
15
|
+
undef_method :count
|
|
16
|
+
rescue NoMethodError
|
|
17
|
+
end
|
|
18
|
+
|
|
19
|
+
end
|
|
20
|
+
|
|
21
|
+
require 'ruber/qt_sugar'
|
|
22
|
+
require 'ruber/kde_sugar'
|
|
23
|
+
require 'ruber/utils'
|
|
24
|
+
|
|
25
|
+
#Distinguish between rspec 1.* and 2.*
|
|
26
|
+
RSPEC = begin require 'rspec/core'
|
|
27
|
+
RSpec
|
|
28
|
+
rescue LoadError
|
|
29
|
+
Spec
|
|
30
|
+
end
|
|
31
|
+
(RSPEC.name == 'RSpec' ? RSPEC : RSPEC::Runner).configure do |config|
|
|
32
|
+
config.mock_with :flexmock
|
|
33
|
+
end
|
|
34
|
+
|
|
35
|
+
OS = OpenStruct
|
|
36
|
+
|
|
37
|
+
=begin rdoc
|
|
38
|
+
[
|
|
39
|
+
[dir1, [dir2, [fi1e1, file2, [dir3, file3] ]],
|
|
40
|
+
[dir4, file4]
|
|
41
|
+
]
|
|
42
|
+
produces
|
|
43
|
+
dir1
|
|
44
|
+
dir2
|
|
45
|
+
dir3
|
|
46
|
+
file3
|
|
47
|
+
file1
|
|
48
|
+
file2
|
|
49
|
+
dir4
|
|
50
|
+
file4
|
|
51
|
+
=end
|
|
52
|
+
def make_dir_tree contents, base = '/tmp/', file_contents = {}
|
|
53
|
+
letters = ('A'..'Z').to_a + ('a'..'z').to_a
|
|
54
|
+
temp_dir = File.join base, 10.times.map{letters[rand(52)]}.join
|
|
55
|
+
dirs = []
|
|
56
|
+
files = []
|
|
57
|
+
contents = YAML.load(contents) if contents.is_a? String
|
|
58
|
+
mktree = lambda do |dir, current|
|
|
59
|
+
if current.is_a? Array
|
|
60
|
+
current_full = File.join(dir, current[0])
|
|
61
|
+
dirs << current_full
|
|
62
|
+
current[1..-1].each do |c|
|
|
63
|
+
mktree.call current_full, c
|
|
64
|
+
end
|
|
65
|
+
else files << File.join(dir, current)
|
|
66
|
+
end
|
|
67
|
+
end
|
|
68
|
+
contents.each {|d| mktree.call temp_dir, d}
|
|
69
|
+
FileUtils.mkdir temp_dir
|
|
70
|
+
file_contents = file_contents.map{|k, v| [File.join( temp_dir, k), v]}.to_h
|
|
71
|
+
dirs.each{|d| FileUtils.mkdir d}
|
|
72
|
+
files.each do |f|
|
|
73
|
+
if file_contents[f] then File.open(f, 'w'){|out| out.write file_contents[f]}
|
|
74
|
+
else `touch #{f}`
|
|
75
|
+
end
|
|
76
|
+
end
|
|
77
|
+
temp_dir
|
|
78
|
+
end
|
|
79
|
+
|
|
80
|
+
=begin rdoc
|
|
81
|
+
Returns a random string of length _length_ made of characters from 'a' to 'z'
|
|
82
|
+
=end
|
|
83
|
+
def random_string length = 5
|
|
84
|
+
('a'..'z').to_a.sample(length).join
|
|
85
|
+
end
|
|
86
|
+
|
|
87
|
+
if RUBY_VERSION =~ /9/
|
|
88
|
+
RSPEC::Matchers.define :include do |it|
|
|
89
|
+
match do |cont|
|
|
90
|
+
cont.include? it
|
|
91
|
+
end
|
|
92
|
+
end
|
|
93
|
+
end
|
|
94
|
+
|
|
95
|
+
RSPEC::Matchers.define :have_entries do |hash|
|
|
96
|
+
|
|
97
|
+
match do |obj|
|
|
98
|
+
if obj.respond_to? :[]
|
|
99
|
+
hash.each_pair do |k, v|
|
|
100
|
+
return false unless obj[k] == v
|
|
101
|
+
end
|
|
102
|
+
else
|
|
103
|
+
hash.each_pair do |k, v|
|
|
104
|
+
return false unless obj.send(k) == v
|
|
105
|
+
end
|
|
106
|
+
end
|
|
107
|
+
end
|
|
108
|
+
|
|
109
|
+
end
|
|
110
|
+
|
|
111
|
+
RSPEC::Matchers.define :be_the_same_as do |exp|
|
|
112
|
+
match{|obj| obj.equal? exp}
|
|
113
|
+
end
|
|
114
|
+
|
|
115
|
+
data = KDE::AboutData.new "test", "", KDE::ki18n("test"), "0.0.0"
|
|
116
|
+
KDE::CmdLineArgs.init [], data
|
|
117
|
+
KDE::Application.new
|
|
118
|
+
at_exit{Qt::Internal.application_terminated = true}
|
|
119
|
+
GC.disable
|
|
@@ -0,0 +1,1259 @@
|
|
|
1
|
+
require 'spec/common'
|
|
2
|
+
|
|
3
|
+
require 'ruber/component_manager'
|
|
4
|
+
require 'ruber/plugin'
|
|
5
|
+
require 'ruber/plugin_specification'
|
|
6
|
+
require 'ruber/config/config'
|
|
7
|
+
|
|
8
|
+
include FlexMock::ArgumentTypes
|
|
9
|
+
|
|
10
|
+
describe 'Ruber::ComponentManager#w, when created' do
|
|
11
|
+
|
|
12
|
+
before do
|
|
13
|
+
@manager = Ruber::ComponentManager.new
|
|
14
|
+
end
|
|
15
|
+
|
|
16
|
+
it 'should add itself to the list of features' do
|
|
17
|
+
@manager.instance_variable_get(:@features).should == {:components => @manager}
|
|
18
|
+
end
|
|
19
|
+
|
|
20
|
+
it 'should add itself to the "components" hash, under the :components key' do
|
|
21
|
+
@manager[:components].should equal(@manager)
|
|
22
|
+
end
|
|
23
|
+
|
|
24
|
+
it 'should have a plugin_description method which returns the plugin description' do
|
|
25
|
+
res = @manager.plugin_description
|
|
26
|
+
res.should be_a(Ruber::PluginSpecification)
|
|
27
|
+
res.name.should == :components
|
|
28
|
+
res.features.should == [:components]
|
|
29
|
+
res.class_obj.should == Ruber::ComponentManager
|
|
30
|
+
end
|
|
31
|
+
|
|
32
|
+
it 'should have a plugin_name and component_name methods which return :components' do
|
|
33
|
+
@manager.plugin_name.should == :components
|
|
34
|
+
@manager.component_name.should == :components
|
|
35
|
+
end
|
|
36
|
+
|
|
37
|
+
end
|
|
38
|
+
|
|
39
|
+
describe 'Ruber::ComponentManager#[]' do
|
|
40
|
+
|
|
41
|
+
it 'should return the entry stored in the @features instance variable with the same name as the argument' do
|
|
42
|
+
man = Ruber::ComponentManager.new
|
|
43
|
+
mk = flexmock('test component')
|
|
44
|
+
man.instance_variable_get(:@components)[:test] = mk
|
|
45
|
+
features = man.instance_variable_get(:@features)
|
|
46
|
+
features[:x] = man
|
|
47
|
+
features[:test] = mk
|
|
48
|
+
features[:y] = mk
|
|
49
|
+
man[:components].should equal(man)
|
|
50
|
+
man[:x].should equal(man)
|
|
51
|
+
man[:test].should equal(mk)
|
|
52
|
+
man[:y].should equal(mk)
|
|
53
|
+
end
|
|
54
|
+
|
|
55
|
+
end
|
|
56
|
+
|
|
57
|
+
describe 'Ruber::ComponentManager#each_component' do
|
|
58
|
+
|
|
59
|
+
it 'should call the block passing each component in turn, in reverse loading order' do
|
|
60
|
+
man = Ruber::ComponentManager.new
|
|
61
|
+
components = 5.times.map{|i| flexmock(i.to_s, :component_name => i.to_s, :plugin_description => OpenStruct.new({:features => []}))}
|
|
62
|
+
m = flexmock do |mk|
|
|
63
|
+
components.reverse_each{|c| mk.should_receive(:test).once.with(c).globally.ordered}
|
|
64
|
+
end
|
|
65
|
+
man.instance_variable_get(:@components).clear
|
|
66
|
+
components.each{|c| man.add c}
|
|
67
|
+
man.each_component{|i| m.test i}
|
|
68
|
+
end
|
|
69
|
+
|
|
70
|
+
end
|
|
71
|
+
|
|
72
|
+
describe 'Ruber::ComponentManager#components' do
|
|
73
|
+
|
|
74
|
+
it 'should call the block passing each component in turn, in reverse loading order' do
|
|
75
|
+
man = Ruber::ComponentManager.new
|
|
76
|
+
components = 5.times.map{|i| flexmock(i.to_s, :component_name => i.to_s, :plugin_description => OpenStruct.new({:features => []}))}
|
|
77
|
+
man.instance_variable_get(:@components).clear
|
|
78
|
+
components.each{|c| man.add c}
|
|
79
|
+
man.components.should == components
|
|
80
|
+
end
|
|
81
|
+
|
|
82
|
+
end
|
|
83
|
+
|
|
84
|
+
describe 'Ruber::ComponentManager#each_plugin' do
|
|
85
|
+
|
|
86
|
+
it 'should call the block passing each plugin in turn, in reverse loading order' do
|
|
87
|
+
man = Ruber::ComponentManager.new
|
|
88
|
+
components = 5.times.map{|i| flexmock("component #{i}", :component_name => :"#{i}",:plugin_description => OpenStruct.new({:features => []}))}
|
|
89
|
+
m = flexmock("test")
|
|
90
|
+
plugins = [1, 3, 4]
|
|
91
|
+
components.each_with_index do |c, i|
|
|
92
|
+
is_plugin = plugins.include?(i)
|
|
93
|
+
c.should_receive(:is_a?).with(Ruber::Plugin).and_return( is_plugin)
|
|
94
|
+
if is_plugin then m.should_receive(:test).with(c).once.globally.ordered
|
|
95
|
+
else m.should_receive(:test).with(c).never
|
|
96
|
+
end
|
|
97
|
+
end
|
|
98
|
+
components.reverse_each{|c| man.add c}
|
|
99
|
+
man.each_plugin{|i| m.test i}
|
|
100
|
+
end
|
|
101
|
+
|
|
102
|
+
end
|
|
103
|
+
|
|
104
|
+
describe 'Ruber::ComponentManager#plugins' do
|
|
105
|
+
|
|
106
|
+
it 'should return an array containing all the loading plugins, in loading order' do
|
|
107
|
+
man = Ruber::ComponentManager.new
|
|
108
|
+
components = 5.times.map{|i| flexmock("component #{i}", :component_name => :"#{i}",:plugin_description => OpenStruct.new({:features => []}))}
|
|
109
|
+
m = flexmock("test")
|
|
110
|
+
plugins = [1, 3, 4]
|
|
111
|
+
components.each_with_index do |c, i|
|
|
112
|
+
is_plugin = plugins.include?(i)
|
|
113
|
+
c.should_receive(:is_a?).with(Ruber::Plugin).and_return( is_plugin)
|
|
114
|
+
end
|
|
115
|
+
components.reverse_each{|c| man.add c}
|
|
116
|
+
man.plugins.should == [components[4], components[3], components[1]]
|
|
117
|
+
end
|
|
118
|
+
|
|
119
|
+
end
|
|
120
|
+
|
|
121
|
+
describe 'Ruber::ComponentManager#add' do
|
|
122
|
+
|
|
123
|
+
before do
|
|
124
|
+
@manager = Ruber::ComponentManager.new
|
|
125
|
+
end
|
|
126
|
+
|
|
127
|
+
it 'should add the argument at the end of the component list' do
|
|
128
|
+
plug = flexmock('plugin', :component_name => :test, :plugin_description => OpenStruct.new({:features => []}))
|
|
129
|
+
@manager.add plug
|
|
130
|
+
@manager.instance_variable_get(:@components)[:test].should equal(plug)
|
|
131
|
+
@manager.instance_variable_get(:@components).keys.last.should == :test
|
|
132
|
+
end
|
|
133
|
+
|
|
134
|
+
it 'should add the argument to the list of features for each of its features' do
|
|
135
|
+
pdf = OpenStruct.new({:features => [:test, :x, :y]})
|
|
136
|
+
plug = flexmock('plugin', :component_name => :test, :plugin_description => pdf)
|
|
137
|
+
@manager.add plug
|
|
138
|
+
@manager.instance_variable_get(:@features).should == {:components => @manager, :test => plug, :x => plug, :y => plug}
|
|
139
|
+
end
|
|
140
|
+
|
|
141
|
+
end
|
|
142
|
+
|
|
143
|
+
describe 'Ruber::ComponentManager.load_component' do
|
|
144
|
+
|
|
145
|
+
before(:all) do
|
|
146
|
+
cls = Class.new(Qt::Object) do
|
|
147
|
+
attr_accessor :plugin_description
|
|
148
|
+
def initialize manager, pdf
|
|
149
|
+
super()
|
|
150
|
+
end
|
|
151
|
+
|
|
152
|
+
def setup;end
|
|
153
|
+
end
|
|
154
|
+
Object.const_set(:TestComponent, cls)
|
|
155
|
+
end
|
|
156
|
+
|
|
157
|
+
before do
|
|
158
|
+
@manager = Ruber::ComponentManager.new
|
|
159
|
+
end
|
|
160
|
+
|
|
161
|
+
after(:all) do
|
|
162
|
+
Object.send :remove_const, :TestComponent
|
|
163
|
+
end
|
|
164
|
+
|
|
165
|
+
it 'should add the component directory to KDE::StandardDirs if an instance of the application exists' do
|
|
166
|
+
yaml = <<-EOS
|
|
167
|
+
name: test
|
|
168
|
+
class: TestComponent
|
|
169
|
+
EOS
|
|
170
|
+
dir = File.expand_path('lib/ruber/main_window')
|
|
171
|
+
flexmock(KDE::Application).should_receive(:instance).and_return Qt::Object.new
|
|
172
|
+
#needed because resource_dirs return directory names ending in /, while
|
|
173
|
+
#File.expand_path doesn't
|
|
174
|
+
dir = File.join dir, ''
|
|
175
|
+
file = File.join dir, 'plugin.yaml'
|
|
176
|
+
flexmock(File).should_receive(:read).with(file).once.and_return yaml
|
|
177
|
+
@manager.load_component 'main_window'
|
|
178
|
+
KDE::Global.dirs.resource_dirs('pixmap').should include(dir)
|
|
179
|
+
KDE::Global.dirs.resource_dirs('data').should include(dir)
|
|
180
|
+
KDE::Global.dirs.resource_dirs('appdata').should include(dir)
|
|
181
|
+
end
|
|
182
|
+
|
|
183
|
+
it 'should not add the component directory to KDE::StandardDirs if no instance of the application exist' do
|
|
184
|
+
yaml = <<-EOS
|
|
185
|
+
name: test
|
|
186
|
+
class: TestComponent
|
|
187
|
+
EOS
|
|
188
|
+
dir = File.expand_path('lib/ruber/projects')
|
|
189
|
+
flexmock(KDE::Application).should_receive(:instance).and_return nil
|
|
190
|
+
#needed because resource_dirs return directory names ending in /, while
|
|
191
|
+
#File.expand_path doesn't
|
|
192
|
+
dir = File.join dir, ''
|
|
193
|
+
file = File.join dir, 'plugin.yaml'
|
|
194
|
+
flexmock(File).should_receive(:read).with(file).once.and_return yaml
|
|
195
|
+
@manager.load_component 'projects'
|
|
196
|
+
KDE::Global.dirs.resource_dirs('pixmap').should_not include(dir)
|
|
197
|
+
KDE::Global.dirs.resource_dirs('data').should_not include(dir)
|
|
198
|
+
KDE::Global.dirs.resource_dirs('appdata').should_not include(dir)
|
|
199
|
+
end
|
|
200
|
+
|
|
201
|
+
|
|
202
|
+
it 'should read the full PDF from the plugin.yaml file contained in the directory with the same name as the component, in the same directory as the component_manager.rb file' do
|
|
203
|
+
file = File.expand_path 'lib/ruber/test/plugin.yaml'
|
|
204
|
+
yaml = <<-EOS
|
|
205
|
+
name: test
|
|
206
|
+
class: TestComponent
|
|
207
|
+
EOS
|
|
208
|
+
pdf = Ruber::PluginSpecification.full YAML.load(yaml)
|
|
209
|
+
flexmock(Ruber::PluginSpecification).should_receive(:full).once.with(file).and_return pdf
|
|
210
|
+
@manager.load_component 'test'
|
|
211
|
+
end
|
|
212
|
+
|
|
213
|
+
it 'should raise SystemCallError if the file couldn\'t be found' do
|
|
214
|
+
file = File.expand_path 'lib/ruber/test/plugin.yaml'
|
|
215
|
+
lambda{@manager.load_component 'test'}.should raise_error(SystemCallError)
|
|
216
|
+
end
|
|
217
|
+
|
|
218
|
+
it 'should raise ArgumentError if the file isn\'t a valid YAML file' do
|
|
219
|
+
file = File.expand_path 'lib/ruber/test/plugin.yaml'
|
|
220
|
+
yaml = <<-EOS
|
|
221
|
+
name: {
|
|
222
|
+
EOS
|
|
223
|
+
flexmock(File).should_receive(:read).with(file).once.and_return yaml
|
|
224
|
+
lambda{@manager.load_component 'test'}.should raise_error(ArgumentError)
|
|
225
|
+
end
|
|
226
|
+
|
|
227
|
+
it 'should raise Ruber::PluginSpecification::PSFError if the file isn\'t a valid PDF, then re-raise the exception' do
|
|
228
|
+
file = File.expand_path 'lib/ruber/test/plugin.yaml'
|
|
229
|
+
yaml = '{}'
|
|
230
|
+
flexmock(File).should_receive(:read).with(file).once.and_return yaml
|
|
231
|
+
lambda{@manager.load_component 'test'}.should raise_error(Ruber::PluginSpecification::PSFError)
|
|
232
|
+
end
|
|
233
|
+
|
|
234
|
+
it 'should store the component directory in the "directory" attribute of the PDF' do
|
|
235
|
+
dir = File.expand_path 'lib/ruber/test'
|
|
236
|
+
file = File.join dir, 'plugin.yaml'
|
|
237
|
+
yaml = <<-EOS
|
|
238
|
+
name: test
|
|
239
|
+
class: TestComponent
|
|
240
|
+
EOS
|
|
241
|
+
flexmock(File).should_receive(:read).with(file).and_return yaml
|
|
242
|
+
pdf = Ruber::PluginSpecification.full(YAML.load(yaml))
|
|
243
|
+
flexmock(Ruber::PluginSpecification).should_receive(:full).with(file).once.and_return pdf
|
|
244
|
+
comp = TestComponent.new(@manager, pdf)
|
|
245
|
+
flexmock(TestComponent).should_receive(:new).once.with(@manager, pdf).and_return(comp)
|
|
246
|
+
@manager.load_component 'test'
|
|
247
|
+
end
|
|
248
|
+
|
|
249
|
+
it 'should create an instance of the class mentioned in the PDF, passing the component manager and the pdf as argument' do
|
|
250
|
+
file = File.expand_path 'lib/ruber/test/plugin.yaml'
|
|
251
|
+
yaml = <<-EOS
|
|
252
|
+
name: test
|
|
253
|
+
class: TestComponent
|
|
254
|
+
EOS
|
|
255
|
+
flexmock(File).should_receive(:read).with(file).and_return yaml
|
|
256
|
+
pdf = Ruber::PluginSpecification.full(YAML.load(yaml))
|
|
257
|
+
flexmock(Ruber::PluginSpecification).should_receive(:full).and_return pdf
|
|
258
|
+
comp = TestComponent.new(@manager, pdf)
|
|
259
|
+
flexmock(TestComponent).should_receive(:new).once.with(@manager, pdf).and_return(comp)
|
|
260
|
+
@manager.load_component 'test'
|
|
261
|
+
end
|
|
262
|
+
|
|
263
|
+
it 'should not call the load_settings method of the component if no components with name :config exist' do
|
|
264
|
+
file = File.expand_path 'lib/ruber/test/plugin.yaml'
|
|
265
|
+
yaml = <<-EOS
|
|
266
|
+
name: test
|
|
267
|
+
class: TestComponent
|
|
268
|
+
EOS
|
|
269
|
+
flexmock(File).should_receive(:read).with(file).and_return yaml
|
|
270
|
+
comp = TestComponent.new @manager, Ruber::PluginSpecification.full(YAML.load(yaml))
|
|
271
|
+
flexmock(TestComponent).should_receive(:new).once.and_return(comp)
|
|
272
|
+
flexmock(comp).should_receive(:send).with(:setup)
|
|
273
|
+
flexmock(comp).should_receive(:send).with(:load_settings).never
|
|
274
|
+
@manager.load_component 'test'
|
|
275
|
+
end
|
|
276
|
+
|
|
277
|
+
it 'should emit the "component_loaded(QObject*)" signal as the last thing' do
|
|
278
|
+
file = File.expand_path 'lib/ruber/test/plugin.yaml'
|
|
279
|
+
yaml = <<-EOS
|
|
280
|
+
name: test
|
|
281
|
+
class: TestComponent
|
|
282
|
+
EOS
|
|
283
|
+
flexmock(File).should_receive(:read).with(file).once.and_return yaml
|
|
284
|
+
@manager.instance_variable_get(:@components)[:config] = TestComponent.new @manager, OpenStruct.new
|
|
285
|
+
comp = TestComponent.new @manager, Ruber::PluginSpecification.full(YAML.load(yaml))
|
|
286
|
+
def comp.load_settings;end
|
|
287
|
+
flexmock(TestComponent).should_receive(:new).once.globally.ordered.with(@manager, Ruber::PluginSpecification).and_return(comp)
|
|
288
|
+
m = flexmock{|mk| mk.should_receive(:component_added).with(comp).once.globally.ordered}
|
|
289
|
+
@manager.connect(SIGNAL('component_loaded(QObject*)')){|c| m.component_added(c)}
|
|
290
|
+
@manager.load_component 'test'
|
|
291
|
+
end
|
|
292
|
+
|
|
293
|
+
it 'should return the component' do
|
|
294
|
+
file = File.expand_path 'lib/ruber/test/plugin.yaml'
|
|
295
|
+
yaml = <<-EOS
|
|
296
|
+
name: test
|
|
297
|
+
class: TestComponent
|
|
298
|
+
EOS
|
|
299
|
+
flexmock(File).should_receive(:read).with(file).once.and_return yaml
|
|
300
|
+
comp = TestComponent.new @manager, Ruber::PluginSpecification.full(YAML.load(yaml))
|
|
301
|
+
flexmock(TestComponent).should_receive(:new).once.with(@manager, Ruber::PluginSpecification).and_return(comp)
|
|
302
|
+
@manager.load_component( 'test').should equal(comp)
|
|
303
|
+
|
|
304
|
+
end
|
|
305
|
+
|
|
306
|
+
end
|
|
307
|
+
|
|
308
|
+
describe 'Ruber::ComponentManager#load_plugin' do
|
|
309
|
+
|
|
310
|
+
before(:all) do
|
|
311
|
+
cls = Class.new(Qt::Object) do
|
|
312
|
+
attr_accessor :plugin_description
|
|
313
|
+
def initialize pdf
|
|
314
|
+
super()
|
|
315
|
+
@plugin_description = pdf
|
|
316
|
+
end
|
|
317
|
+
|
|
318
|
+
define_method(:setup){}
|
|
319
|
+
define_method(:load_settings){}
|
|
320
|
+
define_method(:delayed_initialize){}
|
|
321
|
+
end
|
|
322
|
+
Object.const_set(:TestPlugin, cls)
|
|
323
|
+
end
|
|
324
|
+
|
|
325
|
+
before do
|
|
326
|
+
@manager = Ruber::ComponentManager.new
|
|
327
|
+
@dir = File.expand_path "~/test"
|
|
328
|
+
@file = File.join @dir, 'plugin.yaml'
|
|
329
|
+
@yaml = <<-EOS
|
|
330
|
+
name: test
|
|
331
|
+
class: TestPlugin
|
|
332
|
+
EOS
|
|
333
|
+
@pdf = Ruber::PluginSpecification.full YAML.load(@yaml)
|
|
334
|
+
flexmock(File).should_receive(:read).with(@file).and_return(@yaml).by_default
|
|
335
|
+
end
|
|
336
|
+
|
|
337
|
+
after(:all) do
|
|
338
|
+
Object.send :remove_const, :TestPlugin
|
|
339
|
+
end
|
|
340
|
+
|
|
341
|
+
it 'should add the plugin directory to KDE::StandardDirs' do
|
|
342
|
+
@dir = File.expand_path(File.dirname(__FILE__))
|
|
343
|
+
#needed because resource_dirs return directory names ending in /, while
|
|
344
|
+
#File.expand_path doesn't
|
|
345
|
+
@dir = File.join @dir, ''
|
|
346
|
+
@file = File.join @dir, 'plugin.yaml'
|
|
347
|
+
flexmock(File).should_receive(:read).with(@file).once.and_return @yaml
|
|
348
|
+
@manager.load_plugin @dir
|
|
349
|
+
KDE::Global.dirs.resource_dirs('pixmap').should include(@dir)
|
|
350
|
+
KDE::Global.dirs.resource_dirs('data').should include(@dir)
|
|
351
|
+
KDE::Global.dirs.resource_dirs('appdata').should include(@dir)
|
|
352
|
+
end
|
|
353
|
+
|
|
354
|
+
it 'should read the full PDF from the plugin.yaml file contained in the directory passed as argument' do
|
|
355
|
+
flexmock(File).should_receive(:read).with(@file).once.and_return @yaml
|
|
356
|
+
pdf = Ruber::PluginSpecification.full YAML.load(@yaml)
|
|
357
|
+
flexmock(Ruber::PluginSpecification).should_receive(:full).once.with(YAML.load(@yaml), @dir).and_return pdf
|
|
358
|
+
@manager.load_plugin @dir
|
|
359
|
+
end
|
|
360
|
+
|
|
361
|
+
it 'should raise SystemCallError if the plugin file doesn\'t exist' do
|
|
362
|
+
dir = File.expand_path "~/test"
|
|
363
|
+
file = File.join dir, 'plugin.yaml'
|
|
364
|
+
yaml = <<-EOS
|
|
365
|
+
name: test
|
|
366
|
+
class: TestPlugin
|
|
367
|
+
EOS
|
|
368
|
+
flexmock(File).should_receive(:read).once.with(@file).and_raise(SystemCallError.new(0))
|
|
369
|
+
lambda{@manager.load_plugin @dir}.should raise_error(SystemCallError)
|
|
370
|
+
end
|
|
371
|
+
|
|
372
|
+
it 'should raise ArgumentError if the plugin file isn\'t a valid YAML file' do
|
|
373
|
+
dir = File.expand_path "#{ENV['HOME']}/test"
|
|
374
|
+
yaml = <<-EOS
|
|
375
|
+
name: {
|
|
376
|
+
EOS
|
|
377
|
+
file = File.join dir, 'plugin.yaml'
|
|
378
|
+
flexmock(File).should_receive(:read).with(file).once.and_return yaml
|
|
379
|
+
lambda{@manager.load_plugin @dir}.should raise_error(ArgumentError)
|
|
380
|
+
end
|
|
381
|
+
|
|
382
|
+
it 'should raise Ruber::PluginSpecification::PSFError if the file isn\'t a valid PDF' do
|
|
383
|
+
dir = File.expand_path "#{ENV['HOME']}/test"
|
|
384
|
+
yaml = '{}'
|
|
385
|
+
file = File.join dir, 'plugin.yaml'
|
|
386
|
+
flexmock(File).should_receive(:read).with(file).once.and_return yaml
|
|
387
|
+
lambda{@manager.load_plugin @dir}.should raise_error(Ruber::PluginSpecification::PSFError)
|
|
388
|
+
end
|
|
389
|
+
|
|
390
|
+
it 'should create an instance of the class mentioned in the PDF, passing the PluginSpecification object as argument, and return it' do
|
|
391
|
+
pdf = Ruber::PluginSpecification.full(YAML.load(@yaml))
|
|
392
|
+
flexmock(Ruber::PluginSpecification).should_receive(:full).and_return pdf
|
|
393
|
+
plug = TestPlugin.new(pdf)
|
|
394
|
+
flexmock(TestPlugin).should_receive(:new).once.with(pdf).and_return(plug)
|
|
395
|
+
res = @manager.load_plugin @dir
|
|
396
|
+
res.should equal(plug)
|
|
397
|
+
end
|
|
398
|
+
|
|
399
|
+
it 'should emit the "component_loaded(QObject*)" signal, passing the plugin as argument' do
|
|
400
|
+
pdf = Ruber::PluginSpecification.full(YAML.load(@yaml))
|
|
401
|
+
flexmock(Ruber::PluginSpecification).should_receive(:full).and_return pdf
|
|
402
|
+
plug = TestPlugin.new(pdf)
|
|
403
|
+
flexmock(TestPlugin).should_receive(:new).once.with(pdf).and_return(plug)
|
|
404
|
+
m = flexmock{|mk| mk.should_receive(:component_loaded).once.with(plug)}
|
|
405
|
+
@manager.connect(SIGNAL('component_loaded(QObject*)')){|o| m.component_loaded o}
|
|
406
|
+
@manager.load_plugin @dir
|
|
407
|
+
end
|
|
408
|
+
|
|
409
|
+
it 'should emit the "feature_loaded(QString, QObject*)" signal for each feature provided by the plugin' do
|
|
410
|
+
pdf = Ruber::PluginSpecification.full(YAML.load(@yaml))
|
|
411
|
+
pdf.features << :x << :y
|
|
412
|
+
flexmock(Ruber::PluginSpecification).should_receive(:full).and_return pdf
|
|
413
|
+
plug = TestPlugin.new(pdf)
|
|
414
|
+
flexmock(TestPlugin).should_receive(:new).once.with(pdf).and_return(plug)
|
|
415
|
+
m = flexmock do |mk|
|
|
416
|
+
mk.should_receive(:feature_loaded).with('test', plug).once
|
|
417
|
+
mk.should_receive(:feature_loaded).with('x', plug).once
|
|
418
|
+
mk.should_receive(:feature_loaded).with('y', plug).once
|
|
419
|
+
end
|
|
420
|
+
@manager.connect(SIGNAL('feature_loaded(QString, QObject*)')){|s, o| m.feature_loaded s, o}
|
|
421
|
+
@manager.load_plugin @dir
|
|
422
|
+
end
|
|
423
|
+
|
|
424
|
+
it 'calls the new plugin\'s delayed_initialize method, after emitting the feature_loaded signals' do
|
|
425
|
+
pdf = Ruber::PluginSpecification.full(YAML.load(@yaml))
|
|
426
|
+
pdf.features << :x << :y
|
|
427
|
+
flexmock(Ruber::PluginSpecification).should_receive(:full).and_return pdf
|
|
428
|
+
plug = TestPlugin.new(pdf)
|
|
429
|
+
flexmock(TestPlugin).should_receive(:new).once.with(pdf).and_return(plug)
|
|
430
|
+
m = flexmock do |mk|
|
|
431
|
+
mk.should_receive(:feature_loaded).with('test', plug).once.ordered(:signals)
|
|
432
|
+
mk.should_receive(:feature_loaded).with('x', plug).once.ordered(:signals)
|
|
433
|
+
mk.should_receive(:feature_loaded).with('y', plug).once.ordered(:signals)
|
|
434
|
+
end
|
|
435
|
+
@manager.connect(SIGNAL('feature_loaded(QString, QObject*)')){|s, o| m.feature_loaded s, o}
|
|
436
|
+
flexmock(plug).should_receive(:delayed_initialize).once.ordered
|
|
437
|
+
@manager.load_plugin @dir
|
|
438
|
+
end
|
|
439
|
+
|
|
440
|
+
it 'calls the new plugin\'s delayed_initialize method even if it\'s private' do
|
|
441
|
+
pdf = Ruber::PluginSpecification.full(YAML.load(@yaml))
|
|
442
|
+
flexmock(Ruber::PluginSpecification).should_receive(:full).and_return pdf
|
|
443
|
+
plug = TestPlugin.new(pdf)
|
|
444
|
+
flexmock(TestPlugin).should_receive(:new).once.with(pdf).and_return(plug)
|
|
445
|
+
class << plug
|
|
446
|
+
private :delayed_initialize
|
|
447
|
+
end
|
|
448
|
+
flexmock(plug).should_receive(:delayed_initialize).once
|
|
449
|
+
@manager.load_plugin @dir
|
|
450
|
+
end
|
|
451
|
+
|
|
452
|
+
it 'should create a signal "unloading_*(QObject*)" for each feature provided by the plugin' do
|
|
453
|
+
pdf = Ruber::PluginSpecification.full(YAML.load(@yaml))
|
|
454
|
+
pdf.features << :x << :y
|
|
455
|
+
flexmock(Ruber::PluginSpecification).should_receive(:full).and_return pdf
|
|
456
|
+
plug = TestPlugin.new(pdf)
|
|
457
|
+
flexmock(TestPlugin).should_receive(:new).once.with(pdf).and_return(plug)
|
|
458
|
+
m = flexmock do |mk|
|
|
459
|
+
mk.should_receive(:unloading_x).with(plug).once
|
|
460
|
+
mk.should_receive(:unloading_y).with(plug).once
|
|
461
|
+
mk.should_receive(:unloading_test).with(plug).once
|
|
462
|
+
end
|
|
463
|
+
@manager.load_plugin @dir
|
|
464
|
+
@manager.connect(SIGNAL('unloading_x(QObject*)')){|o| m.unloading_x o}
|
|
465
|
+
@manager.connect(SIGNAL('unloading_y(QObject*)')){|o| m.unloading_y o}
|
|
466
|
+
@manager.connect(SIGNAL('unloading_test(QObject*)')){|o| m.unloading_test o}
|
|
467
|
+
@manager.instance_eval{emit unloading_x(plug)}
|
|
468
|
+
@manager.instance_eval{emit unloading_y(plug)}
|
|
469
|
+
@manager.instance_eval{emit unloading_test(plug)}
|
|
470
|
+
end
|
|
471
|
+
|
|
472
|
+
end
|
|
473
|
+
|
|
474
|
+
describe 'Ruber::ComponentManager.sort_plugins' do
|
|
475
|
+
|
|
476
|
+
it 'should sort the plugins alphabetically if no dependencies exist' do
|
|
477
|
+
pdfs = [
|
|
478
|
+
{:name => :c, :deps => []},
|
|
479
|
+
{:name => :a, :deps => []},
|
|
480
|
+
{:name => :s, :deps => []},
|
|
481
|
+
{:name => :m, :deps => []}
|
|
482
|
+
].map{|i| OpenStruct.new i}
|
|
483
|
+
res = Ruber::ComponentManager.sort_plugins(pdfs)
|
|
484
|
+
res.map{|i| i.name}.should == [:a, :c, :m, :s]
|
|
485
|
+
end
|
|
486
|
+
|
|
487
|
+
it 'should return an array sorted according to dependencies' do
|
|
488
|
+
pdfs = [
|
|
489
|
+
{:name => :c, :deps => [:s, :a]},
|
|
490
|
+
{:name => :a, :deps => [:x]},
|
|
491
|
+
{:name => :s, :deps => []},
|
|
492
|
+
{:name => :m, :deps => [:x, :c]},
|
|
493
|
+
{:name => :x, :deps => []}
|
|
494
|
+
].map{|i| OpenStruct.new i}
|
|
495
|
+
res = Ruber::ComponentManager.sort_plugins(pdfs)
|
|
496
|
+
res.map{|i| i.name}.should == [:s, :x, :a, :c, :m]
|
|
497
|
+
end
|
|
498
|
+
|
|
499
|
+
it 'should raise Ruber::ComponentManager::UnresolvedDep if a dependency can\'t be resolved' do
|
|
500
|
+
pdfs = [
|
|
501
|
+
{:name => :c, :deps => [:s, :a]},
|
|
502
|
+
{:name => :a, :deps => [:x]},
|
|
503
|
+
{:name => :s, :deps => []},
|
|
504
|
+
{:name => :m, :deps => [:x, :r]},
|
|
505
|
+
{:name => :x, :deps => []},
|
|
506
|
+
{:name => :l, :deps => [:r, :t]}
|
|
507
|
+
].map{|i| OpenStruct.new i}
|
|
508
|
+
lambda{Ruber::ComponentManager.sort_plugins(pdfs)}.should raise_error(Ruber::ComponentManager::UnresolvedDep){|e| e.missing.should == {:r => [:m, :l], :t => [:l]}}
|
|
509
|
+
end
|
|
510
|
+
|
|
511
|
+
it 'should raise Ruber::ComponentManager::CircularDep if there are circular dependencies' do
|
|
512
|
+
pdfs = [
|
|
513
|
+
{:name => :c, :deps => [:s, :a]},
|
|
514
|
+
{:name => :a, :deps => [:x]},
|
|
515
|
+
{:name => :s, :deps => []},
|
|
516
|
+
{:name => :x, :deps => [:c]},
|
|
517
|
+
].map{|i| OpenStruct.new i}
|
|
518
|
+
circ = [[:a, :x], [:x, :c], [:c, :a]]
|
|
519
|
+
lambda{Ruber::ComponentManager.sort_plugins(pdfs)}.should raise_error(Ruber::ComponentManager::CircularDep){|e| circ.should include(e.circular_deps)}
|
|
520
|
+
end
|
|
521
|
+
|
|
522
|
+
it 'should not count features passed as second argument' do
|
|
523
|
+
pdfs = [
|
|
524
|
+
{:name => :c, :deps => [:s, :a, :b]},
|
|
525
|
+
{:name => :a, :deps => [:x, :l]},
|
|
526
|
+
{:name => :s, :deps => []},
|
|
527
|
+
{:name => :m, :deps => [:x, :c]},
|
|
528
|
+
{:name => :x, :deps => [:l, :b]}
|
|
529
|
+
].map{|i| OpenStruct.new i}
|
|
530
|
+
res = Ruber::ComponentManager.sort_plugins(pdfs, [:b, :l])
|
|
531
|
+
res.map{|i| i.name}.should == [:s, :x, :a, :c, :m]
|
|
532
|
+
end
|
|
533
|
+
|
|
534
|
+
end
|
|
535
|
+
|
|
536
|
+
describe 'Ruber::ComponentManager.resolve_features' do
|
|
537
|
+
|
|
538
|
+
it 'should return an array of pdfs where the dependencies have been changed according to the features provided by the plugins' do
|
|
539
|
+
pdfs = [
|
|
540
|
+
{:name => :c, :deps => [:s, :a, :d], :features => [:c]},
|
|
541
|
+
{:name => :a, :deps => [:x, :r], :features => [:a]},
|
|
542
|
+
{:name => :s, :deps => [], :features => [:s, :r]},
|
|
543
|
+
{:name => :m, :deps => [:x, :c, :d], :features => [:m]},
|
|
544
|
+
{:name => :x, :deps => [], :features => [:x, :d]}
|
|
545
|
+
].map{|i| OpenStruct.new i}
|
|
546
|
+
res = Ruber::ComponentManager.resolve_features pdfs
|
|
547
|
+
res[0].deps.should =~ [:s, :a, :x]
|
|
548
|
+
res[1].deps.should =~ [:x, :s]
|
|
549
|
+
res[2].deps.should == []
|
|
550
|
+
res[3].deps.should =~ [:x, :c]
|
|
551
|
+
res[4].deps.should == []
|
|
552
|
+
end
|
|
553
|
+
|
|
554
|
+
it 'should raise Ruber::ComponentManager::UnresolvedDep if some features can\'t be found' do
|
|
555
|
+
pdfs = [
|
|
556
|
+
{:name => :c, :deps => [:s, :a, :d, :y], :features => [:c]},
|
|
557
|
+
{:name => :a, :deps => [:x, :r], :features => [:a]},
|
|
558
|
+
{:name => :s, :deps => [], :features => [:s, :r]},
|
|
559
|
+
{:name => :m, :deps => [:x, :c, :d, :z, :y], :features => [:m]},
|
|
560
|
+
{:name => :x, :deps => [], :features => [:x, :d]}
|
|
561
|
+
].map{|i| OpenStruct.new i}
|
|
562
|
+
lambda{Ruber::ComponentManager.resolve_features pdfs}.should raise_error(Ruber::ComponentManager::UnresolvedDep){|e| e.missing.should == {:c => [:y], :m => [:z, :y]}}
|
|
563
|
+
end
|
|
564
|
+
|
|
565
|
+
it 'should also take into account pdfs passed as second argument in the resolution' do
|
|
566
|
+
pdfs = [
|
|
567
|
+
{:name => :c, :deps => [:s, :a, :d, :y, :k], :features => [:c]},
|
|
568
|
+
{:name => :a, :deps => [:x, :r], :features => [:a]},
|
|
569
|
+
{:name => :s, :deps => [], :features => [:s, :r]},
|
|
570
|
+
{:name => :m, :deps => [:x, :c, :d, :z, :y], :features => [:m]},
|
|
571
|
+
{:name => :x, :deps => [], :features => [:x, :d]}
|
|
572
|
+
].map{|i| OpenStruct.new i}
|
|
573
|
+
extra = [
|
|
574
|
+
{:name => :A, :features => [:A, :z]},
|
|
575
|
+
{:name => :B, :features => [:B, :y, :k]}
|
|
576
|
+
].map{|i| OpenStruct.new i}
|
|
577
|
+
res = Ruber::ComponentManager.resolve_features pdfs, extra
|
|
578
|
+
res[0].deps.should =~ [:s, :a, :x, :B]
|
|
579
|
+
res[1].deps.should =~ [:x, :s]
|
|
580
|
+
res[2].deps.should == []
|
|
581
|
+
res[3].deps.should =~ [:x, :c, :A, :B]
|
|
582
|
+
res[4].deps.should == []
|
|
583
|
+
end
|
|
584
|
+
|
|
585
|
+
it 'should not modifiy the pdfs passed as argument' do
|
|
586
|
+
pdfs = [
|
|
587
|
+
{:name => :c, :deps => [:s, :a, :d], :features => [:c]},
|
|
588
|
+
{:name => :a, :deps => [:x, :r], :features => [:a]},
|
|
589
|
+
{:name => :s, :deps => [], :features => [:s, :r]},
|
|
590
|
+
{:name => :m, :deps => [:x, :c, :d], :features => [:m]},
|
|
591
|
+
{:name => :x, :deps => [], :features => [:x, :d]}
|
|
592
|
+
].map{|i| OpenStruct.new i}
|
|
593
|
+
res = Ruber::ComponentManager.resolve_features pdfs
|
|
594
|
+
res.each_with_index{|pl, i| pl.should_not equal(pdfs[i])}
|
|
595
|
+
end
|
|
596
|
+
|
|
597
|
+
end
|
|
598
|
+
|
|
599
|
+
describe 'Ruber::ComponentManager#load_plugins' do
|
|
600
|
+
|
|
601
|
+
before do
|
|
602
|
+
@manager = Ruber::ComponentManager.new
|
|
603
|
+
flexmock(Ruber).should_receive(:[]).with(:app).and_return(Qt::Object.new).by_default
|
|
604
|
+
flexmock(Ruber).should_receive(:[]).with(:components).and_return(@manager).by_default
|
|
605
|
+
flexmock(Ruber).should_receive(:[]).with(:config).and_return(nil).by_default
|
|
606
|
+
@data = %q{[[d1, [p1, plugin.yaml], [p2, plugin.yaml]], [d2, [p3, plugin.yaml], [p1, plugin.yaml]]]}
|
|
607
|
+
@tree = YAML.load @data
|
|
608
|
+
@dir = nil
|
|
609
|
+
Object.const_set :P1, Class.new(Ruber::Plugin)
|
|
610
|
+
Object.const_set :P2, Class.new(Ruber::Plugin)
|
|
611
|
+
Object.const_set :P3, Class.new(Ruber::Plugin)
|
|
612
|
+
end
|
|
613
|
+
|
|
614
|
+
after do
|
|
615
|
+
FileUtils.rm_r @dir if @dir
|
|
616
|
+
Object.send :remove_const, :P1
|
|
617
|
+
Object.send :remove_const, :P2
|
|
618
|
+
Object.send :remove_const, :P3
|
|
619
|
+
end
|
|
620
|
+
|
|
621
|
+
it 'should find the plugins in the given directories and load them in the alphabetically order if there aren\'t dependencies among them' do
|
|
622
|
+
contents = {
|
|
623
|
+
'd1/p1/plugin.yaml' => '{name: p1, class: P1}',
|
|
624
|
+
'd1/p2/plugin.yaml' => '{name: p2, class: P2}',
|
|
625
|
+
'd2/p3/plugin.yaml' => '{name: p3, class: P3}'
|
|
626
|
+
}
|
|
627
|
+
@dir = make_dir_tree(@tree, '/tmp/', contents)
|
|
628
|
+
@manager.load_plugins(%w[p2 p1 p3], %w[d1 d2].map{|d| File.join(@dir, d)})
|
|
629
|
+
@manager.plugins.map{|i| i.class}.should == [P1, P2, P3]
|
|
630
|
+
end
|
|
631
|
+
|
|
632
|
+
it 'should work with both strings and symbols' do
|
|
633
|
+
contents = {
|
|
634
|
+
'd1/p1/plugin.yaml' => '{name: p1, class: P1}',
|
|
635
|
+
'd1/p2/plugin.yaml' => '{name: p2, class: P2}',
|
|
636
|
+
'd2/p3/plugin.yaml' => '{name: p3, class: P3}'
|
|
637
|
+
}
|
|
638
|
+
@dir = make_dir_tree(@tree, '/tmp/', contents)
|
|
639
|
+
@manager.load_plugins([:p2, :p1, :p3], %w[d1 d2].map{|d| File.join(@dir, d)})
|
|
640
|
+
@manager.plugins.map{|i| i.class}.should == [P1, P2, P3]
|
|
641
|
+
|
|
642
|
+
end
|
|
643
|
+
|
|
644
|
+
it 'should find the plugins in the given directories and load them in dependecy order' do
|
|
645
|
+
contents = {
|
|
646
|
+
'd1/p1/plugin.yaml' => '{name: p1, class: P1, deps: :p3}',
|
|
647
|
+
'd1/p2/plugin.yaml' => '{name: p2, class: P2}',
|
|
648
|
+
'd2/p3/plugin.yaml' => '{name: p3, class: P3, deps: :p2}'
|
|
649
|
+
}
|
|
650
|
+
@dir = make_dir_tree(@tree, '/tmp/', contents)
|
|
651
|
+
@manager.load_plugins(%w[p1 p2 p3], %w[d1 d2].map{|d| File.join(@dir, d)})
|
|
652
|
+
@manager.plugins.map{|i| i.class}.should == [P2, P3, P1]
|
|
653
|
+
end
|
|
654
|
+
|
|
655
|
+
it 'should resolve dependencies among the plugins' do
|
|
656
|
+
contents = {
|
|
657
|
+
'd1/p1/plugin.yaml' => '{name: p1, class: P1, deps: :p4}',
|
|
658
|
+
'd1/p2/plugin.yaml' => '{name: p2, class: P2}',
|
|
659
|
+
'd2/p3/plugin.yaml' => '{name: p3, class: P3, deps: :p2, features: [:p4]}'
|
|
660
|
+
}
|
|
661
|
+
@dir = make_dir_tree(@tree, '/tmp/', contents)
|
|
662
|
+
@manager.load_plugins(%w[p1 p2 p3], %w[d1 d2].map{|d| File.join(@dir, d)})
|
|
663
|
+
@manager.plugins.map{|i| i.class}.should == [P2, P3, P1]
|
|
664
|
+
end
|
|
665
|
+
|
|
666
|
+
it 'should load the plugins using the pdfs with the unresolved dependencies' do
|
|
667
|
+
contents = {
|
|
668
|
+
'd1/p1/plugin.yaml' => '{name: p1, class: P1, deps: :p4}',
|
|
669
|
+
'd1/p2/plugin.yaml' => '{name: p2, class: P2}',
|
|
670
|
+
'd2/p3/plugin.yaml' => '{name: p3, class: P3, deps: :p2, features: [:p4]}'
|
|
671
|
+
}
|
|
672
|
+
@dir = make_dir_tree(@tree, '/tmp/', contents)
|
|
673
|
+
@manager.load_plugins(%w[p1 p2 p3], %w[d1 d2].map{|d| File.join(@dir, d)})
|
|
674
|
+
@manager[:p1].plugin_description.deps.should == [:p4]
|
|
675
|
+
end
|
|
676
|
+
|
|
677
|
+
it 'should use the already-loaded plugins, if any, to compute dependencies' do
|
|
678
|
+
Object.const_set :P4, Class.new(Ruber::Plugin)
|
|
679
|
+
Object.const_set :P5, Class.new(Ruber::Plugin)
|
|
680
|
+
loaded = [{:name => :p4, :class => P4 }, {:name => :p5, :class => P5, :features => [:p6]}].map{|i| Ruber::PluginSpecification.full i}
|
|
681
|
+
P4.new loaded[0]
|
|
682
|
+
P5.new loaded[1]
|
|
683
|
+
contents = {
|
|
684
|
+
'd1/p1/plugin.yaml' => '{name: p1, class: P1, deps: :p4}',
|
|
685
|
+
'd1/p2/plugin.yaml' => '{name: p2, class: P2, deps: p6}',
|
|
686
|
+
'd2/p3/plugin.yaml' => '{name: p3, class: P3, deps: :p2}'
|
|
687
|
+
}
|
|
688
|
+
@dir = make_dir_tree(@tree, '/tmp/', contents)
|
|
689
|
+
@manager.load_plugins(%w[p1 p2 p3], %w[d1 d2].map{|d| File.join(@dir, d)})
|
|
690
|
+
lambda{@manager.load_plugins(%w[p1 p2 p3], %w[d1 d2].map{|d| File.join(@dir, d)})}.should_not raise_error
|
|
691
|
+
end
|
|
692
|
+
|
|
693
|
+
it 'should raise Ruber::ComponentManager::MissingPlugins if some plugins couldn\'t be found' do
|
|
694
|
+
contents = {
|
|
695
|
+
'd1/p1/plugin.yaml' => '{name: p1, class: P1}',
|
|
696
|
+
'd1/p2/plugin.yaml' => '{name: p2, class: P2}',
|
|
697
|
+
'd2/p3/plugin.yaml' => '{name: p3, class: P3}'
|
|
698
|
+
}
|
|
699
|
+
@dir = make_dir_tree(@tree, '/tmp/', contents)
|
|
700
|
+
lambda{@manager.load_plugins(%w[p1 p2 p3 p4 p5], %w[d1 d2].map{|d| File.join(@dir, d)})}.should raise_error(Ruber::ComponentManager::MissingPlugins) do |e|
|
|
701
|
+
e.missing.should =~ ['p4', 'p5']
|
|
702
|
+
end
|
|
703
|
+
end
|
|
704
|
+
|
|
705
|
+
it 'should raise Ruber::ComponentManager::InvalidPDF if the PDF for some plugins was invalid' do
|
|
706
|
+
contents = {
|
|
707
|
+
'd1/p1/plugin.yaml' => '{name: p1, class: P1',
|
|
708
|
+
'd1/p2/plugin.yaml' => '{class: P2}',
|
|
709
|
+
'd2/p3/plugin.yaml' => '{name: p3, class: P3}'
|
|
710
|
+
}
|
|
711
|
+
@dir = make_dir_tree(@tree, '/tmp/', contents)
|
|
712
|
+
lambda{@manager.load_plugins(%w[p1 p2 p3], %w[d1 d2].map{|d| File.join(@dir, d)})}.should raise_error(Ruber::ComponentManager::InvalidPDF) do |e|
|
|
713
|
+
e.files.should =~ %w[d1/p1/plugin.yaml d1/p2/plugin.yaml].map{|f| File.join @dir, f}
|
|
714
|
+
end
|
|
715
|
+
end
|
|
716
|
+
|
|
717
|
+
it 'should raise an exception if loading a plugin raises an exception and no block is given' do
|
|
718
|
+
contents = {
|
|
719
|
+
'd1/p1/plugin.yaml' => '{name: p1, class: X1}',
|
|
720
|
+
'd1/p2/plugin.yaml' => '{name: p2, class: P2}',
|
|
721
|
+
'd2/p3/plugin.yaml' => '{name: p3, class: P3}'
|
|
722
|
+
}
|
|
723
|
+
@dir = make_dir_tree(@tree, '/tmp/', contents)
|
|
724
|
+
lambda{@manager.load_plugins(%w[p1 p2 p3], %w[d1 d2].map{|d| File.join(@dir, d)})}.should raise_error(NameError)
|
|
725
|
+
end
|
|
726
|
+
|
|
727
|
+
it 'should call the block if an exception occurs while loading a plugin and return false as soon as the block returns false' do
|
|
728
|
+
P1.class_eval{def initialize pdf; raise NoMethodError;end}
|
|
729
|
+
P3.class_eval{def initialize pdf; raise ArgumentError;end}
|
|
730
|
+
contents = {
|
|
731
|
+
'd1/p1/plugin.yaml' => '{name: p1, class: P1}',
|
|
732
|
+
'd1/p2/plugin.yaml' => '{name: p2, class: P2}',
|
|
733
|
+
'd2/p3/plugin.yaml' => '{name: p3, class: P3}'
|
|
734
|
+
}
|
|
735
|
+
@dir = make_dir_tree(@tree, '/tmp/', contents)
|
|
736
|
+
m = flexmock do |mk|
|
|
737
|
+
mk.should_receive(:test).with(Ruber::PluginSpecification, NoMethodError).once.and_return(true)
|
|
738
|
+
mk.should_receive(:test).with(Ruber::PluginSpecification, ArgumentError).once.and_return(false)
|
|
739
|
+
end
|
|
740
|
+
(@manager.load_plugins(%w[p1 p2 p3], %w[d1 d2].map{|d| File.join(@dir, d)}){|pl, e|m.test pl, e}).should be_false
|
|
741
|
+
end
|
|
742
|
+
|
|
743
|
+
it 'should call the block if an exception occurs while loading a plugin and stop loading plugins and return true if the block returns :skip' do
|
|
744
|
+
P1.class_eval{def initialize pdf; raise NoMethodError;end}
|
|
745
|
+
P2.class_eval{def initialize pdf; raise ArgumentError;end}
|
|
746
|
+
contents = {
|
|
747
|
+
'd1/p1/plugin.yaml' => '{name: p1, class: P1}',
|
|
748
|
+
'd1/p2/plugin.yaml' => '{name: p2, class: P2}',
|
|
749
|
+
'd2/p3/plugin.yaml' => '{name: p3, class: P3}'
|
|
750
|
+
}
|
|
751
|
+
@dir = make_dir_tree(@tree, '/tmp/', contents)
|
|
752
|
+
m = flexmock do |mk|
|
|
753
|
+
mk.should_receive(:test).with(Ruber::PluginSpecification, NoMethodError).once.and_return(true)
|
|
754
|
+
mk.should_receive(:test).with(Ruber::PluginSpecification, ArgumentError).once.and_return(:skip)
|
|
755
|
+
end
|
|
756
|
+
flexmock(P3).should_receive(:new).never
|
|
757
|
+
(@manager.load_plugins(%w[p1 p2 p3], %w[d1 d2].map{|d| File.join(@dir, d)}){|pl, e|m.test pl, e}).should be_true
|
|
758
|
+
end
|
|
759
|
+
|
|
760
|
+
it 'should call the block if an exception occurs while loading a plugin, stop calling it for following errors and return true if the block returns :silent' do
|
|
761
|
+
P1.class_eval{def initialize pdf; raise NoMethodError;end}
|
|
762
|
+
P2.class_eval{def initialize pdf; raise ArgumentError;end}
|
|
763
|
+
contents = {
|
|
764
|
+
'd1/p1/plugin.yaml' => '{name: p1, class: P1}',
|
|
765
|
+
'd1/p2/plugin.yaml' => '{name: p2, class: P2}',
|
|
766
|
+
'd2/p3/plugin.yaml' => '{name: p3, class: P3}'
|
|
767
|
+
}
|
|
768
|
+
@dir = make_dir_tree(@tree, '/tmp/', contents)
|
|
769
|
+
m = flexmock do |mk|
|
|
770
|
+
mk.should_receive(:test).with(Ruber::PluginSpecification, NoMethodError).once.and_return(:silent)
|
|
771
|
+
mk.should_receive(:test).with(Ruber::PluginSpecification, ArgumentError).never
|
|
772
|
+
end
|
|
773
|
+
(@manager.load_plugins(%w[p1 p2 p3], %w[d1 d2].map{|d| File.join(@dir, d)}){|pl, e|m.test pl, e}).should be_true
|
|
774
|
+
@manager.plugins.last.should be_a(P3)
|
|
775
|
+
end
|
|
776
|
+
|
|
777
|
+
it 'should return true if no error occurs' do
|
|
778
|
+
contents = {
|
|
779
|
+
'd1/p1/plugin.yaml' => '{name: p1, class: P1}',
|
|
780
|
+
'd1/p2/plugin.yaml' => '{name: p2, class: P2}',
|
|
781
|
+
'd2/p3/plugin.yaml' => '{name: p3, class: P3}'
|
|
782
|
+
}
|
|
783
|
+
@dir = make_dir_tree(@tree, '/tmp/', contents)
|
|
784
|
+
@manager.load_plugins(%w[p2 p1 p3], %w[d1 d2].map{|d| File.join(@dir, d)}).should be_true
|
|
785
|
+
end
|
|
786
|
+
|
|
787
|
+
end
|
|
788
|
+
|
|
789
|
+
describe 'Ruber::ComponentManager#shutdown' do
|
|
790
|
+
|
|
791
|
+
before do
|
|
792
|
+
# @cls = Class.new(Qt::Object) do
|
|
793
|
+
# include Ruber::PluginLike
|
|
794
|
+
# def initialize pdf
|
|
795
|
+
# super Ruber[:app]
|
|
796
|
+
# initialize_plugin pdf
|
|
797
|
+
# end
|
|
798
|
+
# # def connect *args
|
|
799
|
+
# # end
|
|
800
|
+
#
|
|
801
|
+
# def is_a? cls
|
|
802
|
+
# true
|
|
803
|
+
# end
|
|
804
|
+
# end
|
|
805
|
+
@manager = Ruber::ComponentManager.new
|
|
806
|
+
|
|
807
|
+
flexmock(Ruber).should_receive(:[]).with(:app).and_return(Qt::Object.new).by_default
|
|
808
|
+
flexmock(Ruber).should_receive(:[]).with(:components).and_return(@manager).by_default
|
|
809
|
+
@config = Qt::Object.new
|
|
810
|
+
flexmock(@config).should_receive(:save_settings).by_default
|
|
811
|
+
flexmock(@config).should_receive(:write).by_default
|
|
812
|
+
flexmock(@config).should_receive(:shutdown).by_default
|
|
813
|
+
@manager.instance_variable_get(:@components)[:config] = @config
|
|
814
|
+
flexmock(Ruber).should_receive(:[]).with(:config).and_return(@config).by_default
|
|
815
|
+
end
|
|
816
|
+
|
|
817
|
+
it 'should call the "save_settings" method of each plugin, except for itself' do
|
|
818
|
+
comps = @manager.instance_variable_get(:@components)
|
|
819
|
+
components = 5.times.map{|i| Ruber::Plugin.new(Ruber::PluginSpecification.full({:name => "c#{i}"}))}
|
|
820
|
+
components.reverse_each{|o| flexmock(o).should_receive(:save_settings).once.globally.ordered}
|
|
821
|
+
flexmock(@manager).should_receive(:save_settings).never
|
|
822
|
+
@manager.shutdown
|
|
823
|
+
end
|
|
824
|
+
|
|
825
|
+
it 'calls the write method of the config object after the plugins\' save_settings method' do
|
|
826
|
+
comps = @manager.instance_variable_get(:@components)
|
|
827
|
+
components = 5.times.map{|i| Ruber::Plugin.new(Ruber::PluginSpecification.full({:name => "c#{i}"}))}
|
|
828
|
+
components.each{|o| flexmock(o).should_receive(:save_settings).once.ordered('components')}
|
|
829
|
+
flexmock(@config).should_receive(:write).once.ordered
|
|
830
|
+
@manager.shutdown
|
|
831
|
+
end
|
|
832
|
+
|
|
833
|
+
# it 'should emit the "unloading_*(QObject*)" for features provided by plugins' do
|
|
834
|
+
# comps = @manager.instance_variable_get(:@components)
|
|
835
|
+
# features = @manager.instance_variable_get(:@features)
|
|
836
|
+
# plugin_pdfs = [
|
|
837
|
+
# {:name => :p1, :features => [:x]},
|
|
838
|
+
# {:name => :p2, :features => [:y, :z]}
|
|
839
|
+
# ].map{|i| Ruber::PluginSpecification.full(i)}
|
|
840
|
+
# component_pdfs = [ {:name => :c1}, {:name => :c2} ].map{|i| Ruber::PluginSpecification.full(i)}
|
|
841
|
+
# components = component_pdfs.map do |c|
|
|
842
|
+
# comp = Ruber::Plugin.new c
|
|
843
|
+
# flexmock(comp).should_receive(:is_a?).with(Ruber::Plugin).and_return false
|
|
844
|
+
# comp
|
|
845
|
+
# end
|
|
846
|
+
# plugins = plugin_pdfs.map{|pl| Ruber::Plugin.new pl}
|
|
847
|
+
# (components + plugins).reverse_each{|i| flexmock(i).should_receive(:shutdown).once.globally.ordered}
|
|
848
|
+
# plugins.reverse_each{|pl| flexmock(pl).should_receive( :delete_later ).globally.ordered}
|
|
849
|
+
# @manager.class.class_eval do
|
|
850
|
+
# signals( *%w[p1 x p2 y z].map{|s| "unloading_#{s}(QObject*)"})
|
|
851
|
+
# end
|
|
852
|
+
# m = flexmock do |mk|
|
|
853
|
+
# %w[x y z].each do |x|
|
|
854
|
+
# mk.should_receive("unloading_#{x}".to_sym).once.with(features[x.to_sym])
|
|
855
|
+
# @manager.connect(SIGNAL("unloading_#{x}(QObject*)")){|o| mk.send("unloading_#{x}", o)}
|
|
856
|
+
# end
|
|
857
|
+
# end
|
|
858
|
+
# @manager.shutdown
|
|
859
|
+
# end
|
|
860
|
+
#
|
|
861
|
+
# it 'should emit the "unloading_component(QObject*)" signal for each component except itself, in reverse loading order' do
|
|
862
|
+
# comps = @manager.instance_variable_get(:@components)
|
|
863
|
+
# components = 5.times.map do |i|
|
|
864
|
+
# flexmock(@manager).should_receive("unloading_c#{i}")
|
|
865
|
+
# Ruber::Plugin.new(Ruber::PluginSpecification.full({:name => "c#{i}"}))
|
|
866
|
+
# end
|
|
867
|
+
# m = flexmock do |mk|
|
|
868
|
+
# components.reverse_each{|c| mk.should_receive(:test).with(c).once.globally.ordered}
|
|
869
|
+
# mk.should_receive(:test).once.with(@config)
|
|
870
|
+
# end
|
|
871
|
+
# @manager.connect( SIGNAL('unloading_component(QObject*)')){|o| m.test o}
|
|
872
|
+
# @manager.shutdown
|
|
873
|
+
# end
|
|
874
|
+
#
|
|
875
|
+
it 'should call the "shutdown" methods of all the components, except itself, in reverse loading order' do
|
|
876
|
+
comps = @manager.instance_variable_get(:@components)
|
|
877
|
+
components = 5.times.map do |i|
|
|
878
|
+
flexmock(@manager).should_receive("unloading_c#{i}")
|
|
879
|
+
Ruber::Plugin.new(Ruber::PluginSpecification.full({:name => "c#{i}"}))
|
|
880
|
+
end
|
|
881
|
+
components.reverse_each{|o| flexmock(o).should_receive(:shutdown).once.globally.ordered}
|
|
882
|
+
@manager.shutdown
|
|
883
|
+
end
|
|
884
|
+
#
|
|
885
|
+
# it 'should call the "delete_later" method of all plugins, after having call the "shutdown" methods' do
|
|
886
|
+
# cls = Class.new(Qt::Object) do
|
|
887
|
+
# include Ruber::PluginLike
|
|
888
|
+
# def initialize pdf
|
|
889
|
+
# super Ruber[:app]
|
|
890
|
+
# initialize_plugin pdf
|
|
891
|
+
# end
|
|
892
|
+
#
|
|
893
|
+
# def is_a? cls
|
|
894
|
+
# false
|
|
895
|
+
# end
|
|
896
|
+
# end
|
|
897
|
+
#
|
|
898
|
+
# components = 3.times.map{|i| cls.new(Ruber::PluginSpecification.full({:name => "c#{i}"}))}
|
|
899
|
+
# plugins=3.times.map{|i| Ruber::Plugin.new(Ruber::PluginSpecification.full({:name => "p#{i}"}))}
|
|
900
|
+
# (components + plugins).reverse_each{|i| flexmock(i).should_receive(:shutdown).once.globally.ordered}
|
|
901
|
+
# plugins.reverse_each{|pl| flexmock(pl).should_receive( :delete_later ).globally.ordered}
|
|
902
|
+
# @manager.class.class_eval do
|
|
903
|
+
# signals( *%w[p0 p1 p2].map{|s| "unloading_#{s}(QObject*)"})
|
|
904
|
+
# end
|
|
905
|
+
# @manager.shutdown
|
|
906
|
+
# end
|
|
907
|
+
#
|
|
908
|
+
# it 'should remove all plugins from the list of components' do
|
|
909
|
+
# cls = Class.new(Qt::Object) do
|
|
910
|
+
# include Ruber::PluginLike
|
|
911
|
+
# def initialize pdf
|
|
912
|
+
# super Ruber[:app]
|
|
913
|
+
# initialize_plugin pdf
|
|
914
|
+
# end
|
|
915
|
+
#
|
|
916
|
+
# def is_a? cls
|
|
917
|
+
# false
|
|
918
|
+
# end
|
|
919
|
+
# end
|
|
920
|
+
#
|
|
921
|
+
# components = 3.times.map{|i| cls.new(Ruber::PluginSpecification.full({:name => "c#{i}"}))}
|
|
922
|
+
# components.unshift @config
|
|
923
|
+
# plugins=3.times.map{|i| Ruber::Plugin.new(Ruber::PluginSpecification.full({:name => "p#{i}"}))}
|
|
924
|
+
# comps = @manager.instance_variable_get(:@components)
|
|
925
|
+
# (components + plugins).reverse_each{|i| flexmock(i).should_receive(:shutdown).once.globally.ordered}
|
|
926
|
+
# plugins.reverse_each{|pl| flexmock(pl).should_receive( :delete_later ).globally.ordered}
|
|
927
|
+
# @manager.class.class_eval do
|
|
928
|
+
# signals( *%w[p0 p1 p2].map{|s| "unloading_#{s}(QObject*)"})
|
|
929
|
+
# end
|
|
930
|
+
# @manager.shutdown
|
|
931
|
+
# @manager.instance_variable_get(:@components).values.sort_by{|o| o.object_id}.should == ([@manager] + components).sort_by{|o| o.object_id}
|
|
932
|
+
# end
|
|
933
|
+
#
|
|
934
|
+
# it 'should remove all features provided by plugins' do
|
|
935
|
+
# plugin_pdfs = [
|
|
936
|
+
# {:name => :p0},
|
|
937
|
+
# {:name => :p1, :features => [:x, :y]},
|
|
938
|
+
# {:name => :p2, :features => [:w, :z]}
|
|
939
|
+
# ].map{|i| Ruber::PluginSpecification.full(i)}
|
|
940
|
+
# component_pdfs = [
|
|
941
|
+
# {:name => :c0, :features => [:a, :b]},
|
|
942
|
+
# {:name => :c1, :features => [:c]}
|
|
943
|
+
# ].map{|i| Ruber::PluginSpecification.full(i)}
|
|
944
|
+
# cls = Class.new(Qt::Object) do
|
|
945
|
+
# include Ruber::PluginLike
|
|
946
|
+
# def initialize pdf
|
|
947
|
+
# super Ruber[:app]
|
|
948
|
+
# initialize_plugin pdf
|
|
949
|
+
# end
|
|
950
|
+
#
|
|
951
|
+
# def is_a? cls
|
|
952
|
+
# false
|
|
953
|
+
# end
|
|
954
|
+
# end
|
|
955
|
+
# components = component_pdfs.map{|i| cls.new(i)}
|
|
956
|
+
# plugins = plugin_pdfs.map{|i| Ruber::Plugin.new(i)}
|
|
957
|
+
# (components + plugins).reverse_each{|i| flexmock(i).should_receive(:shutdown).once.globally.ordered}
|
|
958
|
+
# plugins.reverse_each{|pl| flexmock(pl).should_receive( :delete_later ).globally.ordered}
|
|
959
|
+
# @manager.class.class_eval do
|
|
960
|
+
# signals( *%w[p0 p1 p2 x y w z].map{|s| "unloading_#{s}(QObject*)"})
|
|
961
|
+
# end
|
|
962
|
+
# @manager.shutdown
|
|
963
|
+
# @manager.instance_variable_get(:@features).should == {:components => @manager, :c0 => components[0], :a => components[0], :b => components[0], :c1 => components[1], :c => components[1]}
|
|
964
|
+
# end
|
|
965
|
+
|
|
966
|
+
end
|
|
967
|
+
|
|
968
|
+
describe 'Ruber::ComponentManager#unload_plugin' do
|
|
969
|
+
|
|
970
|
+
before do
|
|
971
|
+
@cls = Class.new(Qt::Object) do
|
|
972
|
+
include Ruber::PluginLike
|
|
973
|
+
def initialize pdf
|
|
974
|
+
super Ruber[:app]
|
|
975
|
+
initialize_plugin pdf
|
|
976
|
+
end
|
|
977
|
+
def is_a? cls
|
|
978
|
+
false
|
|
979
|
+
end
|
|
980
|
+
end
|
|
981
|
+
@manager = Ruber::ComponentManager.new
|
|
982
|
+
flexmock(Ruber).should_receive(:[]).with(:app).and_return(Qt::Object.new).by_default
|
|
983
|
+
flexmock(Ruber).should_receive(:[]).with(:components).and_return(@manager).by_default
|
|
984
|
+
flexmock(Ruber).should_receive(:[]).with(:config).and_return(nil).by_default
|
|
985
|
+
|
|
986
|
+
plugin_pdfs = [
|
|
987
|
+
{:name => :p0},
|
|
988
|
+
{:name => :p1, :features => [:x, :y]},
|
|
989
|
+
{:name => :p2, :features => [:w, :z]}
|
|
990
|
+
].map{|i| Ruber::PluginSpecification.full(i)}
|
|
991
|
+
component_pdfs = [
|
|
992
|
+
{:name => :c0, :features => [:a, :b]},
|
|
993
|
+
{:name => :c1, :features => [:c]}
|
|
994
|
+
].map{|i| Ruber::PluginSpecification.full(i)}
|
|
995
|
+
@components = component_pdfs.map{|i| @cls.new(i)}
|
|
996
|
+
@plugins = plugin_pdfs.map{|i| Ruber::Plugin.new(i)}
|
|
997
|
+
@manager.class.class_eval do
|
|
998
|
+
signals( *%w[p0 p1 p2 x y w z].map{|s| "unloading_#{s}(QObject*)"})
|
|
999
|
+
end
|
|
1000
|
+
|
|
1001
|
+
end
|
|
1002
|
+
|
|
1003
|
+
it 'should emit the "unloading_*(QObject*)" signal for each feature provided by the plugin' do
|
|
1004
|
+
m = flexmock do |mk|
|
|
1005
|
+
mk.should_receive(:unloading_p1).once.with(@plugins[1])
|
|
1006
|
+
mk.should_receive(:unloading_x).once.with(@plugins[1])
|
|
1007
|
+
mk.should_receive(:unloading_y).once.with(@plugins[1])
|
|
1008
|
+
end
|
|
1009
|
+
@manager.class.class_eval do
|
|
1010
|
+
signals( *%w[p0 p1 p2 x y w z].map{|s| "unloading_#{s}(QObject*)"})
|
|
1011
|
+
end
|
|
1012
|
+
%w[p1 x y].each{|f| @manager.connect(SIGNAL("unloading_#{f}(QObject*)")){|o| m.send("unloading_#{f}", o)}}
|
|
1013
|
+
@manager.unload_plugin :p1
|
|
1014
|
+
end
|
|
1015
|
+
|
|
1016
|
+
it 'should emit the "unloading_component(QObject*) signal passing the plugin as argument' do
|
|
1017
|
+
m = flexmock{|mk| mk.should_receive(:unloading_component).once.with(@plugins[1])}
|
|
1018
|
+
@manager.connect(SIGNAL("unloading_component(QObject*)")){|o| m.unloading_component o}
|
|
1019
|
+
@manager.unload_plugin :p1
|
|
1020
|
+
end
|
|
1021
|
+
|
|
1022
|
+
it 'should call the "unload" method of the plugin after emitting the unloading_component signal' do
|
|
1023
|
+
m = flexmock{|mk| mk.should_receive(:unloading_component).once.with(@plugins[1]).globally.ordered}
|
|
1024
|
+
@manager.connect(SIGNAL("unloading_component(QObject*)")){|o| m.unloading_component o}
|
|
1025
|
+
flexmock(@plugins[1]).should_receive(:unload).once.globally.ordered
|
|
1026
|
+
@manager.unload_plugin :p1
|
|
1027
|
+
end
|
|
1028
|
+
|
|
1029
|
+
it 'should call the "delete_later" method of the plugin after calling shutdown' do
|
|
1030
|
+
flexmock(@plugins[1]).should_receive(:shutdown).once.globally.ordered
|
|
1031
|
+
flexmock(@plugins[1]).should_receive(:delete_later).once.globally.ordered
|
|
1032
|
+
@manager.unload_plugin :p1
|
|
1033
|
+
end
|
|
1034
|
+
|
|
1035
|
+
it 'should remove the plugin from the list of components and its features from the list of features' do
|
|
1036
|
+
@manager.unload_plugin :p1
|
|
1037
|
+
@manager.instance_variable_get(:@features).keys.should =~ [:p0, :p2, :w, :z, :c0, :a, :b, :c1, :c, :components]
|
|
1038
|
+
@manager.instance_variable_get(:@components).keys.map(&:to_s).should =~ [:p0, :p2, :c0, :c1, :components].map(&:to_s)
|
|
1039
|
+
end
|
|
1040
|
+
|
|
1041
|
+
it 'should raise ArgumentError if the name corresponds to a component instead of a plugin' do
|
|
1042
|
+
lambda{@manager.unload_plugin(:c1)}.should raise_error(ArgumentError, "A component can't be unloaded")
|
|
1043
|
+
end
|
|
1044
|
+
|
|
1045
|
+
end
|
|
1046
|
+
|
|
1047
|
+
describe 'Ruber::ComponentManager#query_close' do
|
|
1048
|
+
|
|
1049
|
+
before do
|
|
1050
|
+
@manager = Ruber::ComponentManager.new
|
|
1051
|
+
end
|
|
1052
|
+
|
|
1053
|
+
it 'should call the query_close method of every component, except itself' do
|
|
1054
|
+
h = @manager.instance_variable_get(:@components)
|
|
1055
|
+
5.times do |i|
|
|
1056
|
+
mk = flexmock(:name => :"c#{i}"){|m| m.should_receive(:query_close).once.and_return true}
|
|
1057
|
+
h << [mk.name, mk]
|
|
1058
|
+
end
|
|
1059
|
+
@manager.query_close
|
|
1060
|
+
end
|
|
1061
|
+
|
|
1062
|
+
it 'should return false as soon as one of the components\' query_close method returns false' do
|
|
1063
|
+
h = @manager.instance_variable_get(:@components)
|
|
1064
|
+
5.times do |i|
|
|
1065
|
+
mk = flexmock("c#{i}", :name => "c#{i}")
|
|
1066
|
+
if i > 2 then mk.should_receive(:query_close).once.and_return true
|
|
1067
|
+
elsif i == 2 then mk.should_receive(:query_close).once.and_return false
|
|
1068
|
+
else mk.should_receive(:query_close).never
|
|
1069
|
+
end
|
|
1070
|
+
h << [mk.name, mk]
|
|
1071
|
+
end
|
|
1072
|
+
@manager.query_close.should be_false
|
|
1073
|
+
end
|
|
1074
|
+
|
|
1075
|
+
it 'should return true if all components\' query_close methods return true' do
|
|
1076
|
+
h = @manager.instance_variable_get(:@components)
|
|
1077
|
+
5.times do |i|
|
|
1078
|
+
mk = flexmock(:name => :"c#{i}"){|m| m.should_receive(:query_close).once.and_return true}
|
|
1079
|
+
h << [mk.name, mk]
|
|
1080
|
+
end
|
|
1081
|
+
@manager.query_close.should be_true
|
|
1082
|
+
end
|
|
1083
|
+
|
|
1084
|
+
end
|
|
1085
|
+
|
|
1086
|
+
describe Ruber::ComponentManager do
|
|
1087
|
+
|
|
1088
|
+
before do
|
|
1089
|
+
@manager = Ruber::ComponentManager.new
|
|
1090
|
+
end
|
|
1091
|
+
|
|
1092
|
+
describe '#session_data' do
|
|
1093
|
+
|
|
1094
|
+
it 'calls the session_data of each other component' do
|
|
1095
|
+
h = @manager.instance_variable_get(:@components)
|
|
1096
|
+
5.times do |i|
|
|
1097
|
+
mk = flexmock(:name => i.to_s){|m| m.should_receive(:session_data).once.with_no_args.and_return({})}
|
|
1098
|
+
h << [mk.name, mk]
|
|
1099
|
+
end
|
|
1100
|
+
@manager.session_data
|
|
1101
|
+
end
|
|
1102
|
+
|
|
1103
|
+
it 'returns a hash obtained by merging the hashes returned by each components\' session_data method' do
|
|
1104
|
+
h = @manager.instance_variable_get(:@components)
|
|
1105
|
+
5.times do |i|
|
|
1106
|
+
mk = flexmock(:name => i.to_s){|m| m.should_receive(:session_data).once.and_return({i.to_s => i})}
|
|
1107
|
+
h << [mk.name, mk]
|
|
1108
|
+
end
|
|
1109
|
+
res = @manager.session_data
|
|
1110
|
+
res.should == {'0' => 0, '1' => 1, '2' => 2, '3' => 3, '4' => 4}
|
|
1111
|
+
end
|
|
1112
|
+
|
|
1113
|
+
end
|
|
1114
|
+
|
|
1115
|
+
describe '#restore_session' do
|
|
1116
|
+
|
|
1117
|
+
it 'calls the restore_session of each other component passing it the argument' do
|
|
1118
|
+
cfg = KDE::ConfigGroup.new
|
|
1119
|
+
h = @manager.instance_variable_get(:@components)
|
|
1120
|
+
5.times do |i|
|
|
1121
|
+
mk = flexmock(:name => i.to_s){|m| m.should_receive(:restore_session).once.with(cfg)}
|
|
1122
|
+
h << [mk.name, mk]
|
|
1123
|
+
end
|
|
1124
|
+
@manager.restore_session cfg
|
|
1125
|
+
end
|
|
1126
|
+
|
|
1127
|
+
end
|
|
1128
|
+
|
|
1129
|
+
|
|
1130
|
+
end
|
|
1131
|
+
|
|
1132
|
+
describe 'Ruber::ComponentManager.find_plugins' do
|
|
1133
|
+
|
|
1134
|
+
before do
|
|
1135
|
+
@manager = Ruber::ComponentManager.new
|
|
1136
|
+
end
|
|
1137
|
+
|
|
1138
|
+
it 'should return a hash containing the full path of the plugin directories in the given directories as values and the plugin names as keys, if the second argument is false' do
|
|
1139
|
+
dir = make_dir_tree YAML.load(%q{[[d1, [p1, plugin.yaml], [p2, plugin.yaml]], [d2, [p3, plugin.yaml], [p4, plugin.yaml]]]})
|
|
1140
|
+
exp = {:p1 => 'd1/p1', :p2 => 'd1/p2', :p3 => 'd2/p3', :p4 => 'd2/p4'}.map{|p, d| [p, File.join(dir, d)]}.to_h
|
|
1141
|
+
dirs = %w[d1 d2].map{|d| File.join dir, d}
|
|
1142
|
+
Ruber::ComponentManager.find_plugins(dirs).should == exp
|
|
1143
|
+
Ruber::ComponentManager.find_plugins(dirs, false).should == exp
|
|
1144
|
+
FileUtils.rm_r dir
|
|
1145
|
+
end
|
|
1146
|
+
|
|
1147
|
+
it 'should return a hash containing the names of the plugins (as symbols) as keys and the corresponding PluginSpecification (with the directory set to the plugin file) as values' do
|
|
1148
|
+
contents = {
|
|
1149
|
+
'd1/p1/plugin.yaml' => 'name: p1',
|
|
1150
|
+
'd1/p2/plugin.yaml' => 'name: p2',
|
|
1151
|
+
'd2/p3/plugin.yaml' => 'name: p3',
|
|
1152
|
+
'd2/p4/plugin.yaml' => 'name: p4',
|
|
1153
|
+
}
|
|
1154
|
+
dir = make_dir_tree YAML.load(%q{[[d1, [p1, plugin.yaml], [p2, plugin.yaml]], [d2, [p3, plugin.yaml], [p4, plugin.yaml]]]}), '/tmp', contents
|
|
1155
|
+
exp = %w[d1/p1 d1/p2 d2/p3 d2/p4].map do |d|
|
|
1156
|
+
pdf = File.join d, 'plugin.yaml'
|
|
1157
|
+
data = YAML.load contents[pdf]
|
|
1158
|
+
[File.basename(d).to_sym, Ruber::PluginSpecification.intro(data)]
|
|
1159
|
+
end.to_h
|
|
1160
|
+
dirs = %w[d1 d2].map{|d| File.join dir, d}
|
|
1161
|
+
res = Ruber::ComponentManager.find_plugins(dirs, true)
|
|
1162
|
+
res.should == exp
|
|
1163
|
+
res[:p1].directory.should == File.join(dirs[0], 'p1')
|
|
1164
|
+
res[:p2].directory.should == File.join(dirs[0], 'p2')
|
|
1165
|
+
res[:p3].directory.should == File.join(dirs[1], 'p3')
|
|
1166
|
+
res[:p4].directory.should == File.join(dirs[1], 'p4')
|
|
1167
|
+
FileUtils.rm_r dir
|
|
1168
|
+
end
|
|
1169
|
+
|
|
1170
|
+
it 'should return only the file in the earliest directory, if more than one directory contain the same plugin' do
|
|
1171
|
+
dir = make_dir_tree YAML.load(%q{[[d1, [p1, plugin.yaml], [p2, plugin.yaml]], [d2, [p3, plugin.yaml], [p1, plugin.yaml]]]})
|
|
1172
|
+
exp = {:p1 => 'd1/p1', :p2 => 'd1/p2', :p3 => 'd2/p3'}.map{|p, d| [p, File.join(dir, d)]}.to_h
|
|
1173
|
+
dirs = %w[d1 d2].map{|d| File.join dir, d}
|
|
1174
|
+
Ruber::ComponentManager.find_plugins(dirs).should == exp
|
|
1175
|
+
Ruber::ComponentManager.find_plugins(dirs, false).should == exp
|
|
1176
|
+
FileUtils.rm_r dir
|
|
1177
|
+
contents = {
|
|
1178
|
+
'd1/p1/plugin.yaml' => 'name: p1',
|
|
1179
|
+
'd1/p2/plugin.yaml' => 'name: p2',
|
|
1180
|
+
'd2/p3/plugin.yaml' => 'name: p3',
|
|
1181
|
+
'd2/p1/plugin.yaml' => 'name: p1',
|
|
1182
|
+
}
|
|
1183
|
+
dir = make_dir_tree YAML.load(%q{[[d1, [p1, plugin.yaml], [p2, plugin.yaml]], [d2, [p3, plugin.yaml], [p1, plugin.yaml]]]}), '/tmp', contents
|
|
1184
|
+
exp = %w[d1/p1 d1/p2 d2/p3].map do |d|
|
|
1185
|
+
pdf = File.join d, 'plugin.yaml'
|
|
1186
|
+
data = YAML.load contents[pdf]
|
|
1187
|
+
[File.basename(d).to_sym, Ruber::PluginSpecification.intro(data)]
|
|
1188
|
+
end.to_h
|
|
1189
|
+
dirs = %w[d1 d2].map{|d| File.join dir, d}
|
|
1190
|
+
Ruber::ComponentManager.find_plugins(dirs, true).should == exp
|
|
1191
|
+
FileUtils.rm_r dir
|
|
1192
|
+
end
|
|
1193
|
+
|
|
1194
|
+
end
|
|
1195
|
+
|
|
1196
|
+
describe 'Ruber::ComponentManager.fill_dependencies' do
|
|
1197
|
+
|
|
1198
|
+
it 'should return an empty array if plugins passed as first argument only don\'t have dependencies' do
|
|
1199
|
+
pdfs = [{:name => :p1}, {:name => :p2}, {:name => :p3}, {:name => :p4}].map{|pl| Ruber::PluginSpecification.intro(pl)}
|
|
1200
|
+
Ruber::ComponentManager.fill_dependencies(pdfs[0..1], pdfs).should == []
|
|
1201
|
+
end
|
|
1202
|
+
|
|
1203
|
+
it 'should return an empty array if the plugins passed as first argument only depend on features having the names of other of those plugins' do
|
|
1204
|
+
pdfs = [{:name => :p1, :deps => :p2}, {:name => :p2, :deps => :p3}, {:name => :p3}, {:name => :p4}].map{|pl| Ruber::PluginSpecification.intro(pl)}
|
|
1205
|
+
Ruber::ComponentManager.fill_dependencies(pdfs[0..2], pdfs).should == []
|
|
1206
|
+
end
|
|
1207
|
+
|
|
1208
|
+
it 'should return an emtpy arra if the plugins passed as first argument only depend on features provided by of other plugins in the first argument' do
|
|
1209
|
+
pdfs = [{:name => :p1, :deps => :f2}, {:name => :p2, :deps => :f3, :features => :f2}, {:name => :p3, :features => [:f3, :f5]}, {:name => :p4}].map{|pl| Ruber::PluginSpecification.intro(pl)}
|
|
1210
|
+
Ruber::ComponentManager.fill_dependencies(pdfs[0..2], pdfs).should == []
|
|
1211
|
+
end
|
|
1212
|
+
|
|
1213
|
+
it 'should return an array containing the names of the plugins in the second argument whose name matches dependencies in the first argument not satisfied otherwise' do
|
|
1214
|
+
pdfs = [{:name => :p1, :deps => [:f2, :p4]}, {:name => :p2, :deps => :p3, :features => :f2}, {:name => :p3}, {:name => :p4 }, {:name => :p5 }].map{|pl| Ruber::PluginSpecification.intro(pl)}
|
|
1215
|
+
Ruber::ComponentManager.fill_dependencies(pdfs[0..1], pdfs).map{|i| i.to_s}.should =~ [:p3, :p4].map{|i| i.to_s}
|
|
1216
|
+
end
|
|
1217
|
+
|
|
1218
|
+
it 'should return an array containing the dependencies\' dependencies, if any' do
|
|
1219
|
+
pdfs = [
|
|
1220
|
+
{:name => :p1, :deps => [:f2, :p4]},
|
|
1221
|
+
{:name => :p2, :deps => :p3, :features => :f2},
|
|
1222
|
+
{:name => :p3, :deps => :p6},
|
|
1223
|
+
{:name => :p4, :deps => :p5 },
|
|
1224
|
+
{:name => :p5 },
|
|
1225
|
+
{:name => :p6}
|
|
1226
|
+
]
|
|
1227
|
+
pdfs.map!{|pl| Ruber::PluginSpecification.intro(pl)}
|
|
1228
|
+
Ruber::ComponentManager.fill_dependencies(pdfs[0..1], pdfs).map{|i| i.to_s}.should =~ [:p3, :p4, :p5, :p6].map{|i| i.to_s}
|
|
1229
|
+
end
|
|
1230
|
+
|
|
1231
|
+
it 'should not add a plugin with the same name of a feature as a dependecy if another plugin already provides that feature' do
|
|
1232
|
+
pdfs = [
|
|
1233
|
+
{:name => :p1, :deps => [:f2, :p4]},
|
|
1234
|
+
{:name => :p2, :deps => :p3, :features => :f2},
|
|
1235
|
+
{:name => :p3, :deps => :p6, :features => :p5},
|
|
1236
|
+
{:name => :p4, :deps => :p5 },
|
|
1237
|
+
{:name => :p5, :deps => :p7 },
|
|
1238
|
+
{:name => :p6},
|
|
1239
|
+
{:name => :p7}
|
|
1240
|
+
]
|
|
1241
|
+
pdfs.map!{|pl| Ruber::PluginSpecification.intro(pl)}
|
|
1242
|
+
Ruber::ComponentManager.fill_dependencies(pdfs[0..1], pdfs).map{|i| i.to_s}.should =~ [:p3, :p4, :p6].map{|i| i.to_s}
|
|
1243
|
+
end
|
|
1244
|
+
|
|
1245
|
+
it 'should raise UnresolvedDep if some dependencies can\'t be resolved' do
|
|
1246
|
+
pdfs = [
|
|
1247
|
+
{:name => :p1, :deps => [:f2, :p4, :p7]},
|
|
1248
|
+
{:name => :p2, :deps => :p3, :features => :f2},
|
|
1249
|
+
{:name => :p3, :deps => :p6, :features => :p5},
|
|
1250
|
+
{:name => :p4, :deps => :p5 },
|
|
1251
|
+
{:name => :p5, :deps => :p7 },
|
|
1252
|
+
]
|
|
1253
|
+
pdfs.map!{|pl| Ruber::PluginSpecification.intro(pl)}
|
|
1254
|
+
lambda{Ruber::ComponentManager.fill_dependencies(pdfs[0..1], pdfs)}.should raise_error(Ruber::ComponentManager::UnresolvedDep) do |e|
|
|
1255
|
+
e.missing.should == {:p7 => [:p1], :p6 => [:p3]}
|
|
1256
|
+
end
|
|
1257
|
+
end
|
|
1258
|
+
|
|
1259
|
+
end
|