ruber 0.0.1.1
Sign up to get free protection for your applications and to get access to all the features.
- 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
|