robe-server 1.0.2

Sign up to get free protection for your applications and to get access to all the features.
@@ -0,0 +1,35 @@
1
+ require 'spec_helper'
2
+ require 'robe/sash/includes_tracker'
3
+
4
+ describe Robe::Sash::IncludesTracker do
5
+ klass = described_class
6
+
7
+ let(:m) { Module.new { def foo; end } }
8
+ let(:names) { Names.new }
9
+
10
+ context "after change in environment" do
11
+ before do
12
+ expect(klass.method_owner_and_inst(m, names)).to eq([nil, true])
13
+ end
14
+
15
+ it "detects an included module" do
16
+ n = Module.new
17
+ stub_const("N", n)
18
+ n.send(:include, m)
19
+ expect(klass.method_owner_and_inst(m, names)).to eq(["N", true])
20
+ end
21
+
22
+ it "detects an extended module" do
23
+ n = Module.new
24
+ stub_const("N", n)
25
+ n.send(:extend, m)
26
+ expect(klass.method_owner_and_inst(m, names)).to eq(["N", nil])
27
+ end
28
+ end
29
+
30
+ class Names
31
+ def [](mod)
32
+ mod.__name__
33
+ end
34
+ end
35
+ end
@@ -0,0 +1,420 @@
1
+ require 'spec_helper'
2
+ require 'support/mocks'
3
+ require 'robe/sash'
4
+
5
+ describe Robe::Sash do
6
+ klass = described_class
7
+
8
+ context "#modules" do
9
+ it "returns module names" do
10
+ mock_space = BlindVisor.new(*%w(A B C).map { |n| OpenStruct.new(__name__: n) })
11
+ expect(klass.new(mock_space).modules).to eq %w(A B C)
12
+ end
13
+ end
14
+
15
+ context "#class_locations" do
16
+ it "shows location when class has methods" do
17
+ k = klass.new(double(resolve_context: Class.new { def foo; end }))
18
+ expect(k.class_locations(nil, nil)).to eq([__FILE__])
19
+ end
20
+
21
+ it "shows no location for class without methods" do
22
+ k = klass.new(double(resolve_context: Class.new))
23
+ expect(k.class_locations(nil, nil)).to be_empty
24
+ end
25
+ end
26
+
27
+ context "#targets" do
28
+ context "value is a module" do
29
+ let(:m) do
30
+ Module.new do
31
+ def foo; end
32
+ private
33
+ def baz; end
34
+ class << self
35
+ alias_method :inspect, :name
36
+ def oom; end
37
+ private
38
+ def tee; end
39
+ end
40
+ end
41
+ end
42
+
43
+ before { stub_const("M", m) }
44
+
45
+ let(:k) { klass.new }
46
+
47
+ subject { k.targets("M")[1..-1] }
48
+
49
+ specify { expect(k.targets("M")[0]).to eq("M") }
50
+
51
+ it { should include_spec("M#foo") }
52
+ it { should include_spec("M#baz") }
53
+ it { should include_spec("M.oom") }
54
+ it { expect(subject.select { |(_, _, m)| m == :tee }).to be_empty }
55
+ end
56
+
57
+ it "looks at the class if the value is not a module" do
58
+ expect(klass.new.targets("Math::E")).to include_spec("Float#ceil")
59
+ end
60
+ end
61
+
62
+ context "#find_method" do
63
+ let(:k) { klass.new }
64
+
65
+ it { expect(k.find_method(String, true, :gsub).name).to eq(:gsub) }
66
+ it { expect(k.find_method(String, nil, :freeze).name).to eq(:freeze) }
67
+ end
68
+
69
+ context "#find_method_owner" do
70
+ let(:k) { klass.new }
71
+
72
+ it { expect(k.find_method_owner(File, nil, :open)).to eq(IO.singleton_class)}
73
+ it { expect(k.find_method_owner(String, true, :split)).to eq(String)}
74
+ it { expect(k.find_method_owner(Class.new, nil, :boo)).to be_nil}
75
+ end
76
+
77
+ context "#method_spec" do
78
+ let(:k) { klass.new }
79
+
80
+ it "works on String#gsub" do
81
+ match = if RUBY_ENGINE == "rbx"
82
+ start_with("String", true, :gsub)
83
+ else
84
+ eq(["String", true, :gsub, [[:rest]]])
85
+ end
86
+ expect(k.method_spec(String.instance_method(:gsub))).to match
87
+ end
88
+
89
+ it "includes method location" do
90
+ m = Module.new { def foo; end }
91
+ expect(k.method_spec(m.instance_method(:foo))[4..5])
92
+ .to eq([__FILE__, __LINE__ - 2])
93
+ end
94
+
95
+ it "includes method parameters" do
96
+ m = Module.new { def foo(a, *b, &c); end }
97
+ expect(k.method_spec(m.instance_method(:foo)))
98
+ .to eq([nil, true, :foo, [[:req, :a], [:rest, :b], [:block, :c]],
99
+ __FILE__, anything])
100
+ end
101
+
102
+ it "ignores overridden name method" do
103
+ # Celluloid::Actor in celluloid <~ 0.15
104
+ # https://github.com/celluloid/celluloid/issues/354
105
+
106
+ m = Module.new do
107
+ def self.name
108
+ raise TooCoolForSchoolError
109
+ end
110
+
111
+ def self.__name__
112
+ "baa"
113
+ end
114
+
115
+ def qux
116
+ end
117
+ end
118
+
119
+ expect(k.method_spec(m.instance_method(:qux)))
120
+ .to eq(["baa", true, :qux, [], __FILE__, anything])
121
+ end
122
+
123
+ context "eigenclass" do
124
+ let(:c) do
125
+ Class.new do
126
+ class << self
127
+ def foo; end
128
+ end
129
+ end
130
+ end
131
+
132
+ it "substitutes eigenclass with the actual class name" do
133
+ stub_const("M::C", c)
134
+ expect(k.method_spec(c.singleton_class.instance_method(:foo))[0])
135
+ .to eq("M::C")
136
+ end
137
+
138
+ it "skips anonymous one" do
139
+ expect(k.method_spec(c.singleton_class.instance_method(:foo))[0])
140
+ .to be_nil
141
+ end
142
+
143
+ it "recognizes ActiveRecord classes" do
144
+ arc = Class.new do
145
+ class << self
146
+ def bar; end
147
+ def inspect
148
+ "Record(id: integer)"
149
+ end
150
+ end
151
+ end
152
+
153
+ stub_const("Record", arc)
154
+
155
+ expect(k.method_spec(arc.singleton_class.instance_method(:bar))[0]).to eq("Record")
156
+ end
157
+ end
158
+
159
+ context "anonymous owner" do
160
+ let(:m) { Module.new { def foo; end} }
161
+
162
+ it "returns nil first element" do
163
+ expect(k.method_spec(m.instance_method(:foo))[0]).to be_nil
164
+ end
165
+
166
+ it "substitutes anonymous module with including class name" do
167
+ stub_const("C", Class.new.send(:include, m) )
168
+ spec = k.method_spec(m.instance_method(:foo))
169
+ expect(spec[0]).to eq("C")
170
+ expect(spec[1]).to eq(true)
171
+ end
172
+
173
+ it "substitutes anonymous modules with extending module name" do
174
+ stub_const("M", Module.new.send(:extend, m) )
175
+ spec = k.method_spec(m.instance_method(:foo))
176
+ expect(spec[0]).to eq("M")
177
+ expect(spec[1]).to eq(nil)
178
+ end
179
+ end
180
+ end
181
+
182
+ context "#doc_for" do
183
+ it "returns doc hash for instance method" do
184
+ k = klass.new
185
+ hash = k.doc_for("Set", true, "replace")
186
+ expect(hash[:docstring]).to start_with("Replaces the contents")
187
+ end
188
+
189
+ it "returns doc hash for module method" do
190
+ k = klass.new
191
+ hash = k.doc_for("Set", nil, "[]")
192
+ expect(hash[:docstring]).to start_with("Creates a new set containing")
193
+ end
194
+ end
195
+
196
+ context "#method_targets" do
197
+ it "returns empty array when not found" do
198
+ k = klass.new(ScopedVisor.new)
199
+ expect(k.method_targets("a", "b", "c", true, nil, nil)).to be_empty
200
+ end
201
+
202
+ context "examples" do
203
+ let(:k) { klass.new }
204
+
205
+ it "returns class method candidate" do
206
+ expect(k.method_targets("open", "File", nil, nil, nil, nil))
207
+ .to have_one_spec("IO.open")
208
+ end
209
+
210
+ it "returns the method on Class" do
211
+ expect(k.method_targets("superclass", "Object", nil, nil, nil, nil))
212
+ .to have_one_spec("Class#superclass")
213
+ end
214
+
215
+ it "returns the non-overridden method" do
216
+ targets = k.method_targets("new", "Object", nil, nil, nil, nil)
217
+ expect(targets).to include_spec("BasicObject#initialize")
218
+ expect(targets).not_to include_spec("Class#new")
219
+ end
220
+
221
+ it "returns #new overridden in the given class" do
222
+ c = Class.new do
223
+ def self.new
224
+ end
225
+ end
226
+
227
+ stub_const("C", c)
228
+
229
+ targets = k.method_targets("new", "C", nil, nil, nil, nil)
230
+ expect(targets).to include_spec("C.new")
231
+ expect(targets).not_to include_spec("Class#new")
232
+ expect(targets).not_to include_spec("BasicObject#initialize")
233
+ end
234
+
235
+ it "doesn't return overridden method" do
236
+ expect(k.method_targets("to_s", "Hash", nil, true, nil, nil))
237
+ .to have_one_spec("Hash#to_s")
238
+ end
239
+
240
+ context "unknown target" do
241
+ it "returns String method candidate" do
242
+ expect(k.method_targets("split", "s", nil, true, nil, nil))
243
+ .to include_spec("String#split")
244
+ end
245
+
246
+ it "does not return wrong candidates" do
247
+ candidates = k.method_targets("split", "s", nil, true, nil, nil)
248
+ expect(candidates).to be_all { |c| c[2] == :split }
249
+ end
250
+ end
251
+
252
+ it "returns no candidates for target when conservative" do
253
+ expect(k.method_targets("split", nil, nil, true, nil, true))
254
+ .to be_empty
255
+ end
256
+
257
+ it "returns single instance method from superclass" do
258
+ expect(k.method_targets("map", nil, "Array", true, true, nil))
259
+ .to have_one_spec("Enumerable#map")
260
+ end
261
+
262
+ it "returns single method from target class" do
263
+ expect(k.method_targets("map", nil, "Array", true, nil, nil))
264
+ .to have_one_spec("Array#map")
265
+ end
266
+
267
+ it "checks for instance Kernel methods when the target is a module" do
268
+ # Not 100% accurate: the including class may derive from BasicObject
269
+ stub_const("M", Module.new)
270
+ expect(k.method_targets("puts", nil, "M", true, nil, true))
271
+ .to have_one_spec("Kernel#puts")
272
+ end
273
+
274
+ it "checks private Kernel methods when no primary candidates" do
275
+ k = klass.new(BlindVisor.new)
276
+ expect(k.method_targets("puts", nil, nil, true, nil, nil))
277
+ .to have_one_spec("Kernel#puts")
278
+ end
279
+
280
+ it "sorts results list" do
281
+ extend ScannerHelper
282
+
283
+ a = named_module("A", "a", "b", "c", "d")
284
+ b = named_module("A::B", "a", "b", "c", "d")
285
+ c = new_module("a", "b", "c", "d")
286
+ k = klass.new(ScopedVisor.new(*[b, c, a].shuffle))
287
+ expect(k.method_targets("a", nil, nil, true, nil, nil).map(&:first))
288
+ .to eq(["A", "A::B", nil])
289
+ end
290
+ end
291
+ end
292
+
293
+ context "#complete_method" do
294
+ let(:k) { klass.new }
295
+
296
+ it "completes instance methods" do
297
+ expect(k.complete_method("gs", nil, nil, true))
298
+ .to include_spec("String#gsub", "String#gsub!")
299
+ end
300
+
301
+ context "class methods" do
302
+ let(:k) { klass.new(ScopedVisor.new(Class, {"Object" => Object})) }
303
+
304
+ it "completes public" do
305
+ expect(k.complete_method("su", nil, nil, nil))
306
+ .to include_spec("Class#superclass")
307
+ end
308
+
309
+ it "no private methods with explicit target" do
310
+ expect(k.complete_method("attr", "Object", nil, nil))
311
+ .not_to include_spec("Module#attr_reader")
312
+ end
313
+
314
+ it "no private methods with no target at all" do
315
+ expect(k.complete_method("attr", "Object", nil, nil))
316
+ .not_to include_spec("Module#attr_reader")
317
+ end
318
+
319
+ it "completes private methods with implicit target" do
320
+ expect(k.complete_method("attr", nil, "Object", nil))
321
+ .to include_spec("Module#attr_reader", "Module#attr_writer")
322
+ end
323
+ end
324
+ end
325
+
326
+ context "#complete_const" do
327
+ let(:m) do
328
+ Module.new do
329
+ self::ACONST = 1
330
+
331
+ module self::AMOD; end
332
+ module self::BMOD
333
+ module self::C; end
334
+ end
335
+
336
+ class self::ACLS; end
337
+ end
338
+ end
339
+ let(:v) { ScopedVisor.new({"Test" => m})}
340
+ let(:k) { klass.new(v) }
341
+
342
+ context "sandboxed" do
343
+ it "completes all constants" do
344
+ expect(k.complete_const("Test::A", nil))
345
+ .to match_array(%w(Test::ACONST Test::AMOD Test::ACLS))
346
+ end
347
+
348
+ it "requires names to begin with prefix" do
349
+ expect(k.complete_const("Test::MOD", nil)).to be_empty
350
+ end
351
+
352
+ it "completes the constant in the nesting" do
353
+ expect(k.complete_const("A", "Test")).to include("ACONST")
354
+ end
355
+ end
356
+
357
+ it "completes with bigger nesting" do
358
+ expect(k.complete_const("BMOD::C", "Test")).to eq(["BMOD::C"])
359
+ end
360
+
361
+ it "completes global constants" do
362
+ expect(k.complete_const("Ob", nil)).to include("Object", "ObjectSpace")
363
+ end
364
+
365
+ it "completes the constants in all containing scopes" do
366
+ k = klass.new
367
+ expect(k.complete_const("C", "Encoding"))
368
+ .to include("Converter", "Class", "Complex")
369
+ end
370
+
371
+ it "uses the full access path from the request" do
372
+ k = klass.new
373
+ expect(k.complete_const("Object::File::S", nil)).to include("Object::File::Stat")
374
+ end
375
+
376
+ it "keeps the global qualifier" do
377
+ k = klass.new
378
+ expect(k.complete_const("::Obj", nil)).to match_array(["::Object", "::ObjectSpace"])
379
+ end
380
+ end
381
+
382
+ context "#load_path" do
383
+ it 'returns an appropriate value' do
384
+ expect(klass.new.load_path).to eq($:)
385
+ end
386
+ end
387
+
388
+ it { expect(klass.new.ping).to be_true }
389
+
390
+ RSpec::Matchers.define :include_spec do |*specs|
391
+ match do |candidates|
392
+ actual = candidates.map { |mod, instance, sym|
393
+ MethodSpec.to_str(mod, instance, sym)
394
+ }
395
+ RSpec::Matchers::BuiltIn::Include.new(*specs).matches?(actual)
396
+ end
397
+ end
398
+
399
+ RSpec::Matchers.define :have_one_spec do |spec|
400
+ match do |candidates|
401
+ candidates.length == 1 and
402
+ MethodSpec.new(spec) == candidates[0]
403
+ end
404
+ end
405
+
406
+ class MethodSpec
407
+ def initialize(str)
408
+ @str = str
409
+ end
410
+
411
+ def self.to_str(mod, inst, sym)
412
+ "#{mod}#{inst ? '#' : '.'}#{sym}"
413
+ end
414
+
415
+ def ==(other)
416
+ other.is_a?(Array) && other.length > 2 &&
417
+ self.class.to_str(*other[0..2]) == @str
418
+ end
419
+ end
420
+ end