1# frozen_string_literal: true
2require 'minitest_helper'
3
4class TestRDocRIDriver < RDoc::TestCase
5
6  def setup
7    super
8
9    @tmpdir = File.join Dir.tmpdir, "test_rdoc_ri_driver_#{$$}"
10    @home_ri = File.join @tmpdir, 'dot_ri'
11
12    FileUtils.mkdir_p @tmpdir
13    FileUtils.mkdir_p @home_ri
14
15    @orig_ri = ENV['RI']
16    @orig_home = ENV['HOME']
17    ENV['HOME'] = @tmpdir
18    ENV.delete 'RI'
19
20    @options = RDoc::RI::Driver.default_options
21    @options[:use_system] = false
22    @options[:use_site]   = false
23    @options[:use_home]   = false
24    @options[:use_gems]   = false
25
26    @options[:home]       = @tmpdir
27    @options[:use_stdout] = true
28    @options[:formatter]  = @RM::ToRdoc
29
30    @driver = RDoc::RI::Driver.new @options
31  end
32
33  def teardown
34    super
35
36    ENV['HOME'] = @orig_home
37    ENV['RI'] = @orig_ri
38    FileUtils.rm_rf @tmpdir
39  end
40
41  DUMMY_PAGER = ":;\n"
42
43  def with_dummy_pager
44    pager_env, ENV['RI_PAGER'] = ENV['RI_PAGER'], DUMMY_PAGER
45    yield
46  ensure
47    ENV['RI_PAGER'] = pager_env
48  end
49
50  def test_self_dump
51    util_store
52
53    out, = capture_io do
54      RDoc::RI::Driver.dump @store1.cache_path
55    end
56
57    assert_match %r%:class_methods%,    out
58    assert_match %r%:modules%,          out
59    assert_match %r%:instance_methods%, out
60    assert_match %r%:ancestors%,        out
61  end
62
63  def test_add_also_in_empty
64    out = @RM::Document.new
65
66    @driver.add_also_in out, []
67
68    assert_empty out
69  end
70
71  def test_add_also_in
72    util_multi_store
73    @store1.type = :system
74    @store2.type = :home
75
76    out = @RM::Document.new
77
78    @driver.add_also_in out, [@store1, @store2]
79
80    expected = @RM::Document.new(
81      @RM::Rule.new(1),
82      @RM::Paragraph.new('Also found in:'),
83      @RM::Verbatim.new("ruby core", "\n",
84                        "~/.rdoc", "\n"))
85
86    assert_equal expected, out
87  end
88
89  def test_add_class
90    util_multi_store
91
92    out = @RM::Document.new
93
94    @driver.add_class out, 'Bar', [@cBar]
95
96    expected = @RM::Document.new(
97      @RM::Heading.new(1, 'Bar < Foo'),
98      @RM::BlankLine.new)
99
100    assert_equal expected, out
101  end
102
103  def test_add_from
104    util_store
105    @store1.type = :system
106
107    out = @RM::Document.new
108
109    @driver.add_from out, @store1
110
111    expected = @RM::Document.new @RM::Paragraph.new("(from ruby core)")
112
113    assert_equal expected, out
114  end
115
116  def test_add_extends
117    util_store
118
119    out = @RM::Document.new
120
121    @driver.add_extends out, [[[@cFooExt], @store1]]
122
123    expected = @RM::Document.new(
124      @RM::Rule.new(1),
125      @RM::Heading.new(1, "Extended by:"),
126      @RM::Paragraph.new("Ext (from #{@store1.friendly_path})"),
127      @RM::BlankLine.new,
128      @RM::Paragraph.new("Extend thingy"),
129      @RM::BlankLine.new)
130
131    assert_equal expected, out
132  end
133
134  def test_add_extension_modules_empty
135    out = @RM::Document.new
136
137    @driver.add_extension_modules out, 'Includes', []
138
139    assert_empty out
140  end
141
142  def test_add_extension_modules_many
143    util_store
144
145    out = @RM::Document.new
146
147    enum = RDoc::Include.new 'Enumerable', nil
148    @cFoo.add_include enum
149
150    @driver.add_extension_modules out, 'Includes', [[[@cFooInc, enum], @store1]]
151
152    expected = @RM::Document.new(
153      @RM::Rule.new(1),
154      @RM::Heading.new(1, "Includes:"),
155      @RM::Paragraph.new("(from #{@store1.friendly_path})"),
156      @RM::BlankLine.new,
157      @RM::Paragraph.new("Inc"),
158      @RM::BlankLine.new,
159      @RM::Paragraph.new("Include thingy"),
160      @RM::BlankLine.new,
161      @RM::Verbatim.new("Enumerable", "\n"))
162
163    assert_equal expected, out
164  end
165
166  def test_add_extension_modules_many_no_doc
167    util_store
168
169    out = @RM::Document.new
170
171    enum = RDoc::Include.new 'Enumerable', nil
172    @cFoo.add_include enum
173    @cFooInc.instance_variable_set :@comment, ''
174
175    @driver.add_extension_modules out, 'Includes', [[[@cFooInc, enum], @store1]]
176
177    expected = @RM::Document.new(
178      @RM::Rule.new(1),
179      @RM::Heading.new(1, "Includes:"),
180      @RM::Paragraph.new("(from #{@store1.friendly_path})"),
181      @RM::Verbatim.new("Inc", "\n",
182                        "Enumerable", "\n"))
183
184    assert_equal expected, out
185  end
186
187  def test_add_extension_modules_one
188    util_store
189
190    out = @RM::Document.new
191
192    @driver.add_extension_modules out, 'Includes', [[[@cFooInc], @store1]]
193
194    expected = @RM::Document.new(
195      @RM::Rule.new(1),
196      @RM::Heading.new(1, "Includes:"),
197      @RM::Paragraph.new("Inc (from #{@store1.friendly_path})"),
198      @RM::BlankLine.new,
199      @RM::Paragraph.new("Include thingy"),
200      @RM::BlankLine.new)
201
202    assert_equal expected, out
203  end
204
205  def test_add_includes
206    util_store
207
208    out = @RM::Document.new
209
210    @driver.add_includes out, [[[@cFooInc], @store1]]
211
212    expected = @RM::Document.new(
213      @RM::Rule.new(1),
214      @RM::Heading.new(1, "Includes:"),
215      @RM::Paragraph.new("Inc (from #{@store1.friendly_path})"),
216      @RM::BlankLine.new,
217      @RM::Paragraph.new("Include thingy"),
218      @RM::BlankLine.new)
219
220    assert_equal expected, out
221  end
222
223  def test_add_method
224    util_store
225
226    out = doc
227
228    @driver.add_method out, 'Foo::Bar#blah'
229
230    expected =
231      doc(
232        head(1, 'Foo::Bar#blah'),
233        blank_line,
234        para('(from ~/.rdoc)'),
235        head(3, 'Implementation from Bar'),
236        rule(1),
237        verb("blah(5) => 5\n",
238             "blah(6) => 6\n"),
239        rule(1),
240        blank_line,
241        blank_line)
242
243    assert_equal expected, out
244  end
245
246  def test_add_method_that_is_alias_for_original
247    util_store
248
249    out = doc
250
251    @driver.add_method out, 'Qux#aliased'
252
253    expected =
254      doc(
255        head(1, 'Qux#aliased'),
256        blank_line,
257        para('(from ~/.rdoc)'),
258        rule(1),
259        blank_line,
260        para('alias comment'),
261        blank_line,
262        blank_line,
263        para('(This method is an alias for Qux#original.)'),
264        blank_line,
265        para('original comment'),
266        blank_line,
267        blank_line)
268
269    assert_equal expected, out
270  end
271
272  def test_add_method_attribute
273    util_store
274
275    out = doc
276
277    @driver.add_method out, 'Foo::Bar#attr'
278
279    expected =
280      doc(
281        head(1, 'Foo::Bar#attr'),
282        blank_line,
283        para('(from ~/.rdoc)'),
284        rule(1),
285        blank_line,
286        blank_line)
287
288    assert_equal expected, out
289  end
290
291  def test_add_method_inherited
292    util_multi_store
293
294    out = doc
295
296    @driver.add_method out, 'Bar#inherit'
297
298    expected =
299      doc(
300        head(1, 'Bar#inherit'),
301        blank_line,
302        para('(from ~/.rdoc)'),
303        head(3, 'Implementation from Foo'),
304        rule(1),
305        blank_line,
306        blank_line)
307
308    assert_equal expected, out
309  end
310
311  def test_add_method_overridden
312    util_multi_store
313
314    out = doc
315
316    @driver.add_method out, 'Bar#override'
317
318    expected =
319      doc(
320        head(1, 'Bar#override'),
321        blank_line,
322        para("(from #{@store2.path})"),
323        rule(1),
324        blank_line,
325        para('must be displayed'),
326        blank_line,
327        blank_line)
328
329    assert_equal expected, out
330  end
331
332  def test_add_method_documentation
333    util_store
334
335    out = doc()
336
337    missing = RDoc::AnyMethod.new nil, 'missing'
338    @cFoo.add_method missing
339
340    @driver.add_method_documentation out, @cFoo
341
342    expected =
343      doc(
344        head(1, 'Foo#inherit'),
345        blank_line,
346        para('(from ~/.rdoc)'),
347        rule(1),
348        blank_line,
349        blank_line,
350        head(1, 'Foo#override'),
351        blank_line,
352        para('(from ~/.rdoc)'),
353        rule(1),
354        blank_line,
355        para('must not be displayed in Bar#override'),
356        blank_line,
357        blank_line)
358
359    assert_equal expected, out
360  end
361
362  def test_add_method_list
363    out = @RM::Document.new
364
365    @driver.add_method_list out, %w[new parse], 'Class methods'
366
367    expected = @RM::Document.new(
368      @RM::Heading.new(1, 'Class methods:'),
369      @RM::BlankLine.new,
370      @RM::Verbatim.new('new'),
371      @RM::Verbatim.new('parse'),
372      @RM::BlankLine.new)
373
374    assert_equal expected, out
375  end
376
377  def test_output_width
378    @options[:width] = 10
379    driver = RDoc::RI::Driver.new @options
380
381    doc = @RM::Document.new
382    doc << @RM::IndentedParagraph.new(0, 'new, parse, foo, bar, baz')
383
384    out, = capture_io do
385      driver.display doc
386    end
387
388    expected = "new, parse, foo,\nbar, baz\n"
389
390    assert_equal expected, out
391  end
392
393  def test_add_method_list_interative
394    @options[:interactive] = true
395    driver = RDoc::RI::Driver.new @options
396
397    out = @RM::Document.new
398
399    driver.add_method_list out, %w[new parse], 'Class methods'
400
401    expected = @RM::Document.new(
402      @RM::Heading.new(1, 'Class methods:'),
403      @RM::BlankLine.new,
404      @RM::IndentedParagraph.new(2, 'new, parse'),
405      @RM::BlankLine.new)
406
407    assert_equal expected, out
408  end
409
410  def test_add_method_list_none
411    out = @RM::Document.new
412
413    @driver.add_method_list out, [], 'Class'
414
415    assert_equal @RM::Document.new, out
416  end
417
418  def test_ancestors_of
419    util_ancestors_store
420
421    assert_equal %w[X Mixin Object Foo], @driver.ancestors_of('Foo::Bar')
422  end
423
424  def test_classes
425    util_multi_store
426
427    expected = {
428      'Ambiguous' => [@store1, @store2],
429      'Bar'       => [@store2],
430      'Ext'       => [@store1],
431      'Foo'       => [@store1, @store2],
432      'Foo::Bar'  => [@store1],
433      'Foo::Baz'  => [@store1, @store2],
434      'Inc'       => [@store1],
435      'Qux'       => [@store1],
436    }
437
438    classes = @driver.classes
439
440    assert_equal expected.keys.sort, classes.keys.sort
441
442    expected.each do |klass, stores|
443      assert_equal stores, classes[klass].sort_by { |store| store.path },
444                   "mismatch for #{klass}"
445    end
446  end
447
448  def test_class_document
449    util_store
450
451    tl1 = @store1.add_file 'one.rb'
452    tl2 = @store1.add_file 'two.rb'
453
454    @cFoo.add_comment 'one', tl1
455    @cFoo.add_comment 'two', tl2
456
457    @store1.save_class @cFoo
458
459    found = [
460      [@store1, @store1.load_class(@cFoo.full_name)]
461    ]
462
463    extends  = [[[@cFooExt], @store1]]
464    includes = [[[@cFooInc], @store1]]
465
466    out = @driver.class_document @cFoo.full_name, found, [], includes, extends
467
468    expected = @RM::Document.new
469    @driver.add_class expected, 'Foo', []
470    @driver.add_includes expected, includes
471    @driver.add_extends  expected, extends
472    @driver.add_from expected, @store1
473    expected << @RM::Rule.new(1)
474
475    doc = @RM::Document.new(@RM::Paragraph.new('one'))
476    doc.file = 'one.rb'
477    expected.push doc
478    expected << @RM::BlankLine.new
479    doc = @RM::Document.new(@RM::Paragraph.new('two'))
480    doc.file = 'two.rb'
481    expected.push doc
482
483    expected << @RM::Rule.new(1)
484    expected << @RM::Heading.new(1, 'Instance methods:')
485    expected << @RM::BlankLine.new
486    expected << @RM::Verbatim.new('inherit')
487    expected << @RM::Verbatim.new('override')
488    expected << @RM::BlankLine.new
489
490    assert_equal expected, out
491  end
492
493  def test_complete
494    store = RDoc::RI::Store.new @home_ri
495    store.cache[:ancestors] = {
496      'Foo'      => %w[Object],
497      'Foo::Bar' => %w[Object],
498    }
499    store.cache[:class_methods] = {
500      'Foo' => %w[bar]
501    }
502    store.cache[:instance_methods] = {
503      'Foo' => %w[Bar]
504    }
505    store.cache[:modules] = %w[
506      Foo
507      Foo::Bar
508    ]
509
510    @driver.stores = [store]
511
512    assert_equal %w[Foo         ], @driver.complete('F')
513    assert_equal %w[    Foo::Bar], @driver.complete('Foo::B')
514
515    assert_equal %w[Foo#Bar],           @driver.complete('Foo#'),   'Foo#'
516    assert_equal %w[Foo#Bar  Foo::bar], @driver.complete('Foo.'),   'Foo.'
517    assert_equal %w[Foo::Bar Foo::bar], @driver.complete('Foo::'),  'Foo::'
518
519    assert_equal %w[         Foo::bar], @driver.complete('Foo::b'), 'Foo::b'
520  end
521
522  def test_complete_ancestor
523    util_ancestors_store
524
525    assert_equal %w[Foo::Bar#i_method], @driver.complete('Foo::Bar#')
526
527    assert_equal %w[Foo::Bar#i_method Foo::Bar::c_method Foo::Bar::new],
528                 @driver.complete('Foo::Bar.')
529  end
530
531  def test_complete_classes
532    util_store
533
534    assert_equal %w[                       ], @driver.complete('[')
535    assert_equal %w[                       ], @driver.complete('[::')
536    assert_equal %w[Foo                    ], @driver.complete('F')
537    assert_equal %w[Foo:: Foo::Bar Foo::Baz], @driver.complete('Foo::')
538    assert_equal %w[      Foo::Bar Foo::Baz], @driver.complete('Foo::B')
539  end
540
541  def test_complete_multistore
542    util_multi_store
543
544    assert_equal %w[Bar], @driver.complete('B')
545    assert_equal %w[Foo], @driver.complete('F')
546    assert_equal %w[Foo::Bar Foo::Baz], @driver.complete('Foo::B')
547  end
548
549  def test_display
550    doc = @RM::Document.new(
551            @RM::Paragraph.new('hi'))
552
553    out, = capture_io do
554      @driver.display doc
555    end
556
557    assert_equal "hi\n", out
558  end
559
560  def test_display_class
561    util_store
562
563    out, = capture_io do
564      @driver.display_class 'Foo::Bar'
565    end
566
567    assert_match %r%^= Foo::Bar%, out
568    assert_match %r%^\(from%, out
569
570    assert_match %r%^= Class methods:%, out
571    assert_match %r%^  new%, out
572    assert_match %r%^= Instance methods:%, out
573    assert_match %r%^  blah%, out
574    assert_match %r%^= Attributes:%, out
575    assert_match %r%^  attr_accessor attr%, out
576
577    assert_equal 1, out.scan(/-\n/).length
578
579    refute_match %r%Foo::Bar#blah%, out
580  end
581
582  def test_display_class_all
583    util_store
584
585    @driver.show_all = true
586
587    out, = capture_io do
588      @driver.display_class 'Foo::Bar'
589    end
590
591    assert_match %r%^= Foo::Bar%, out
592    assert_match %r%^\(from%, out
593
594    assert_match %r%^= Class methods:%, out
595    assert_match %r%^  new%, out
596    assert_match %r%^= Instance methods:%, out
597    assert_match %r%^  blah%, out
598    assert_match %r%^= Attributes:%, out
599    assert_match %r%^  attr_accessor attr%, out
600
601    assert_equal 6, out.scan(/-\n/).length
602
603    assert_match %r%Foo::Bar#blah%, out
604  end
605
606  def test_display_class_ambiguous
607    util_multi_store
608
609    out, = capture_io do
610      @driver.display_class 'Ambiguous'
611    end
612
613    assert_match %r%^= Ambiguous < Object$%, out
614  end
615
616  def test_display_class_multi_no_doc
617    util_multi_store
618
619    out, = capture_io do
620      @driver.display_class 'Foo::Baz'
621    end
622
623    assert_match %r%^= Foo::Baz%, out
624    assert_match %r%-\n%, out
625    assert_match %r%Also found in:%, out
626    assert_match %r%#{Regexp.escape @home_ri}%, out
627    assert_match %r%#{Regexp.escape @home_ri2}%, out
628  end
629
630  def test_display_class_superclass
631    util_multi_store
632
633    out, = capture_io do
634      @driver.display_class 'Bar'
635    end
636
637    assert_match %r%^= Bar < Foo%, out
638  end
639
640  def test_display_class_module
641    util_store
642
643    out, = capture_io do
644      @driver.display_class 'Inc'
645    end
646
647    assert_match %r%^= Inc$%, out
648  end
649
650  def test_display_class_page
651    out, = capture_io do
652      @driver.display_class 'ruby:README'
653    end
654
655    assert_empty out
656  end
657
658  def test_display_method
659    util_store
660
661    out, = capture_io do
662      @driver.display_method 'Foo::Bar#blah'
663    end
664
665    assert_match %r%Foo::Bar#blah%, out
666    assert_match %r%blah.5%,        out
667    assert_match %r%blah.6%,        out
668  end
669
670  def test_display_method_attribute
671    util_store
672
673    out, = capture_io do
674      @driver.display_method 'Foo::Bar#attr'
675    end
676
677    assert_match %r%Foo::Bar#attr%, out
678    refute_match %r%Implementation from%, out
679  end
680
681  def test_display_method_inherited
682    util_multi_store
683
684    out, = capture_io do
685      @driver.display_method 'Bar#inherit'
686    end
687
688    assert_match %r%^= Bar#inherit%, out
689    assert_match %r%^=== Implementation from Foo%, out
690  end
691
692  def test_display_method_overridden
693    util_multi_store
694
695    out, = capture_io do
696      @driver.display_method 'Bar#override'
697    end
698
699    refute_match %r%must not be displayed%, out
700  end
701
702  def test_display_name
703    util_store
704
705    out, = capture_io do
706      assert_equal true, @driver.display_name('home:README.rdoc')
707    end
708
709    expected = <<-EXPECTED
710= README
711This is a README
712    EXPECTED
713
714    assert_equal expected, out
715  end
716
717  def test_display_name_not_found_class
718    util_store
719
720    out, = capture_io do
721      assert_equal false, @driver.display_name('Foo::B')
722    end
723
724    expected = <<-EXPECTED
725Foo::B not found, maybe you meant:
726
727Foo::Bar
728Foo::Baz
729    EXPECTED
730
731    assert_equal expected, out
732  end
733
734  def test_display_name_not_found_method
735    util_store
736
737    out, = capture_io do
738      assert_equal false, @driver.display_name('Foo::Bar#b')
739    end
740
741    expected = <<-EXPECTED
742Foo::Bar#b not found, maybe you meant:
743
744Foo::Bar#blah
745Foo::Bar#bother
746    EXPECTED
747
748    assert_equal expected, out
749  end
750
751  def test_display_name_not_found_special
752    util_store
753
754    assert_raises RDoc::RI::Driver::NotFoundError do
755      assert_equal false, @driver.display_name('Set#[]')
756    end
757  end
758
759  def test_display_method_params
760    util_store
761
762    out, = capture_io do
763      @driver.display_method 'Foo::Bar#bother'
764    end
765
766    assert_match %r%things.*stuff%, out
767  end
768
769  def test_display_page
770    util_store
771
772    out, = capture_io do
773      @driver.display_page 'home:README.rdoc'
774    end
775
776    assert_match %r%= README%, out
777  end
778
779  def test_display_page_add_extension
780    util_store
781
782    out, = capture_io do
783      @driver.display_page 'home:README'
784    end
785
786    assert_match %r%= README%, out
787  end
788
789  def test_display_page_ambiguous
790    util_store
791
792    other = @store1.add_file 'README.md'
793    other.parser = RDoc::Parser::Simple
794    other.comment =
795      doc(
796        head(1, 'README.md'),
797        para('This is the other README'))
798
799    @store1.save_page other
800
801    out, = capture_io do
802      @driver.display_page 'home:README'
803    end
804
805    assert_match %r%= README pages in ~/\.rdoc%, out
806    assert_match %r%README\.rdoc%,               out
807    assert_match %r%README\.md%,                 out
808  end
809
810  def test_display_page_extension
811    util_store
812
813    other = @store1.add_file 'README.EXT'
814    other.parser = RDoc::Parser::Simple
815    other.comment =
816      doc(
817        head(1, 'README.EXT'),
818        para('This is the other README'))
819
820    @store1.save_page other
821
822    out, = capture_io do
823      @driver.display_page 'home:README.EXT'
824    end
825
826    assert_match 'other README', out
827  end
828
829  def test_display_page_ignore_directory
830    util_store
831
832    other = @store1.add_file 'doc/globals.rdoc'
833    other.parser = RDoc::Parser::Simple
834    other.comment =
835      doc(
836        head(1, 'globals.rdoc'),
837        para('Globals go here'))
838
839    @store1.save_page other
840
841    out, = capture_io do
842      @driver.display_page 'home:globals'
843    end
844
845    assert_match %r%= globals\.rdoc%, out
846  end
847
848  def test_display_page_missing
849    util_store
850
851    out, = capture_io do
852      @driver.display_page 'home:missing'
853    end
854
855    out, = capture_io do
856      @driver.display_page_list @store1
857    end
858
859    assert_match %r%= Pages in ~/\.rdoc%, out
860    assert_match %r%README\.rdoc%,        out
861  end
862
863  def test_display_page_list
864    util_store
865
866    other = @store1.add_file 'OTHER.rdoc'
867    other.parser = RDoc::Parser::Simple
868    other.comment =
869      doc(
870        head(1, 'OTHER'),
871        para('This is OTHER'))
872
873    @store1.save_page other
874
875    out, = capture_io do
876      @driver.display_page_list @store1
877    end
878
879    assert_match %r%= Pages in ~/\.rdoc%, out
880    assert_match %r%README\.rdoc%,        out
881    assert_match %r%OTHER\.rdoc%,         out
882  end
883
884  def test_expand_class
885    util_store
886
887    assert_equal 'Foo',       @driver.expand_class('F')
888    assert_equal 'Foo::Bar',  @driver.expand_class('F::Bar')
889
890    assert_raises RDoc::RI::Driver::NotFoundError do
891      @driver.expand_class 'F::B'
892    end
893  end
894
895  def test_expand_class_2
896    @store1 = RDoc::RI::Store.new @home_ri, :home
897
898    @top_level = @store1.add_file 'file.rb'
899
900    @cFoo = @top_level.add_class RDoc::NormalClass, 'Foo'
901    @mFox = @top_level.add_module RDoc::NormalModule, 'Fox'
902    @cFoo_Bar = @cFoo.add_class RDoc::NormalClass, 'Bar'
903    @store1.save
904
905    @driver.stores = [@store1]
906    assert_raises RDoc::RI::Driver::NotFoundError do
907      @driver.expand_class 'F'
908    end
909    assert_equal 'Foo::Bar',  @driver.expand_class('F::Bar')
910    assert_equal 'Foo::Bar',  @driver.expand_class('F::B')
911  end
912
913  def test_expand_class_3
914    @store1 = RDoc::RI::Store.new @home_ri, :home
915
916    @top_level = @store1.add_file 'file.rb'
917
918    @cFoo = @top_level.add_class RDoc::NormalClass, 'Foo'
919    @mFox = @top_level.add_module RDoc::NormalModule, 'FooBar'
920    @store1.save
921
922    @driver.stores = [@store1]
923
924    assert_equal 'Foo',  @driver.expand_class('Foo')
925  end
926
927  def test_expand_name
928    util_store
929
930    assert_equal '.b',        @driver.expand_name('b')
931    assert_equal 'Foo',       @driver.expand_name('F')
932    assert_equal 'Foo::Bar#', @driver.expand_name('F::Bar#')
933
934    e = assert_raises RDoc::RI::Driver::NotFoundError do
935      @driver.expand_name 'Z'
936    end
937
938    assert_equal 'Z', e.name
939
940    @driver.stores << RDoc::Store.new(nil, :system)
941
942    assert_equal 'ruby:README', @driver.expand_name('ruby:README')
943    assert_equal 'ruby:',       @driver.expand_name('ruby:')
944
945    e = assert_raises RDoc::RI::Driver::NotFoundError do
946      @driver.expand_name 'nonexistent_gem:'
947    end
948
949    assert_equal 'nonexistent_gem', e.name
950  end
951
952  def test_find_methods
953    util_store
954
955    items = []
956
957    @driver.find_methods 'Foo::Bar.' do |store, klass, ancestor, types, method|
958      items << [store, klass, ancestor, types, method]
959    end
960
961    expected = [
962      [@store1, 'Foo::Bar', 'Foo::Bar', :both, nil],
963    ]
964
965    assert_equal expected, items
966  end
967
968  def test_find_methods_method
969    util_store
970
971    items = []
972
973    @driver.find_methods '.blah' do |store, klass, ancestor, types, method|
974      items << [store, klass, ancestor, types, method]
975    end
976
977    expected = [
978      [@store1, 'Ambiguous', 'Ambiguous', :both, 'blah'],
979      [@store1, 'Ext',       'Ext',       :both, 'blah'],
980      [@store1, 'Foo',       'Foo',       :both, 'blah'],
981      [@store1, 'Foo::Bar',  'Foo::Bar',  :both, 'blah'],
982      [@store1, 'Foo::Baz',  'Foo::Baz',  :both, 'blah'],
983      [@store1, 'Inc',       'Inc',       :both, 'blah'],
984      [@store1, 'Qux',       'Qux',       :both, 'blah'],
985    ]
986
987    assert_equal expected, items
988  end
989
990  def test_filter_methods
991    util_multi_store
992
993    name = 'Bar#override'
994
995    found = @driver.load_methods_matching name
996
997    sorted = @driver.filter_methods found, name
998
999    expected = [[@store2, [@override]]]
1000
1001    assert_equal expected, sorted
1002  end
1003
1004  def test_filter_methods_not_found
1005    util_multi_store
1006
1007    name = 'Bar#inherit'
1008
1009    found = @driver.load_methods_matching name
1010
1011    sorted = @driver.filter_methods found, name
1012
1013    assert_equal found, sorted
1014  end
1015
1016  def test_find_store
1017    @driver.stores << RDoc::Store.new(nil,              :system)
1018    @driver.stores << RDoc::Store.new('doc/gem-1.0/ri', :gem)
1019
1020    assert_equal 'ruby',    @driver.find_store('ruby')
1021    assert_equal 'gem-1.0', @driver.find_store('gem-1.0')
1022    assert_equal 'gem-1.0', @driver.find_store('gem')
1023
1024    e = assert_raises RDoc::RI::Driver::NotFoundError do
1025      @driver.find_store 'nonexistent'
1026    end
1027
1028    assert_equal 'nonexistent', e.name
1029  end
1030
1031  def test_did_you_mean
1032    skip 'skip test with did_you_men' unless defined? DidYouMean::SpellChecker
1033
1034    util_ancestors_store
1035
1036    e = assert_raises RDoc::RI::Driver::NotFoundError do
1037      @driver.lookup_method 'Foo.i_methdo'
1038    end
1039    assert_equal "Nothing known about Foo.i_methdo\nDid you mean?  i_method", e.message
1040
1041    e = assert_raises RDoc::RI::Driver::NotFoundError do
1042      @driver.lookup_method 'Foo#i_methdo'
1043    end
1044    assert_equal "Nothing known about Foo#i_methdo\nDid you mean?  i_method", e.message
1045
1046    e = assert_raises RDoc::RI::Driver::NotFoundError do
1047      @driver.lookup_method 'Foo::i_methdo'
1048    end
1049    assert_equal "Nothing known about Foo::i_methdo\nDid you mean?  c_method", e.message
1050  end
1051
1052  def test_formatter
1053    tty = Object.new
1054    def tty.tty?() true; end
1055
1056    @options.delete :use_stdout
1057    @options.delete :formatter
1058
1059    driver = RDoc::RI::Driver.new @options
1060
1061    assert_instance_of @RM::ToAnsi, driver.formatter(tty)
1062
1063    assert_instance_of @RM::ToBs, driver.formatter(StringIO.new)
1064
1065    driver.instance_variable_set :@paging, true
1066
1067    assert_instance_of @RM::ToBs, driver.formatter(StringIO.new)
1068  end
1069
1070  def test_in_path_eh
1071    path = ENV['PATH']
1072
1073    test_path = File.expand_path '..', __FILE__
1074
1075    temp_dir do |dir|
1076      nonexistent = File.join dir, 'nonexistent'
1077      refute @driver.in_path?(nonexistent)
1078
1079      ENV['PATH'] = test_path
1080
1081      assert @driver.in_path?(File.basename(__FILE__))
1082    end
1083  ensure
1084    ENV['PATH'] = path
1085  end
1086
1087  def test_method_type
1088    assert_equal :both,     @driver.method_type(nil)
1089    assert_equal :both,     @driver.method_type('.')
1090    assert_equal :instance, @driver.method_type('#')
1091    assert_equal :class,    @driver.method_type('::')
1092  end
1093
1094  def test_name_regexp
1095    assert_equal %r%^RDoc::AnyMethod#new$%,
1096                 @driver.name_regexp('RDoc::AnyMethod#new')
1097
1098    assert_equal %r%^RDoc::AnyMethod::new$%,
1099                 @driver.name_regexp('RDoc::AnyMethod::new')
1100
1101    assert_equal %r%^RDoc::AnyMethod(#|::)new$%,
1102                 @driver.name_regexp('RDoc::AnyMethod.new')
1103
1104    assert_equal %r%^Hash(#|::)\[\]$%,
1105                 @driver.name_regexp('Hash.[]')
1106
1107    assert_equal %r%^Hash::\[\]$%,
1108                 @driver.name_regexp('Hash::[]')
1109  end
1110
1111  def test_list_known_classes
1112    util_store
1113
1114    out, = capture_io do
1115      @driver.list_known_classes
1116    end
1117
1118    assert_equal "Ambiguous\nExt\nFoo\nFoo::Bar\nFoo::Baz\nInc\nQux\n", out
1119  end
1120
1121  def test_list_known_classes_name
1122    util_store
1123
1124    out, = capture_io do
1125      @driver.list_known_classes %w[F I]
1126    end
1127
1128    assert_equal "Foo\nFoo::Bar\nFoo::Baz\nInc\n", out
1129  end
1130
1131  def test_list_methods_matching
1132    util_store
1133
1134    assert_equal %w[
1135        Foo::Bar#attr
1136        Foo::Bar#blah
1137        Foo::Bar#bother
1138        Foo::Bar::new
1139      ],
1140      @driver.list_methods_matching('Foo::Bar.').sort
1141  end
1142
1143  def test_list_methods_matching_inherit
1144    util_multi_store
1145
1146    assert_equal %w[
1147        Bar#baz
1148        Bar#inherit
1149        Bar#override
1150      ],
1151      @driver.list_methods_matching('Bar.').sort
1152  end
1153
1154  def test_list_methods_matching_regexp
1155    util_store
1156
1157    index = RDoc::AnyMethod.new nil, '[]'
1158    index.record_location @top_level
1159    @cFoo.add_method index
1160    @store1.save_method @cFoo, index
1161
1162    c_index = RDoc::AnyMethod.new nil, '[]'
1163    c_index.singleton = true
1164    c_index.record_location @top_level
1165    @cFoo.add_method c_index
1166    @store1.save_method @cFoo, c_index
1167
1168    @store1.save_cache
1169
1170    assert_equal %w[Foo#[]], @driver.list_methods_matching('Foo#[]')
1171    assert_equal %w[Foo::[]], @driver.list_methods_matching('Foo::[]')
1172  end
1173
1174  def test_load_method
1175    util_store
1176
1177    method = @driver.load_method(@store1, :instance_methods, 'Foo', '#',
1178                                 'inherit')
1179
1180    assert_equal @inherit, method
1181  end
1182
1183  def test_load_method_inherited
1184    util_multi_store
1185
1186    method = @driver.load_method(@store2, :instance_methods, 'Bar', '#',
1187                                 'inherit')
1188
1189    assert_nil method
1190  end
1191
1192  def test_load_methods_matching
1193    util_store
1194
1195    expected = [[@store1, [@inherit]]]
1196
1197    assert_equal expected, @driver.load_methods_matching('Foo#inherit')
1198
1199    expected = [[@store1, [@blah]]]
1200
1201    assert_equal expected, @driver.load_methods_matching('.blah')
1202
1203    assert_empty @driver.load_methods_matching('.b')
1204  end
1205
1206  def test_load_methods_matching_inherited
1207    util_multi_store
1208
1209    expected = [[@store1, [@inherit]]]
1210
1211    assert_equal expected, @driver.load_methods_matching('Bar#inherit')
1212  end
1213
1214  def test_load_method_missing
1215    util_store
1216
1217    FileUtils.rm @store1.method_file 'Foo', '#inherit'
1218
1219    method = @driver.load_method(@store1, :instance_methods, 'Foo', '#',
1220                                 'inherit')
1221
1222    assert_equal '(unknown)#inherit', method.full_name
1223  end
1224
1225  def _test_page # this test doesn't do anything anymore :(
1226    @driver.use_stdout = false
1227
1228    with_dummy_pager do
1229      @driver.page do |io|
1230        skip "couldn't find a standard pager" if io == $stdout
1231
1232        assert @driver.paging?
1233      end
1234    end
1235
1236    refute @driver.paging?
1237  end
1238
1239  # this test is too fragile. Perhaps using Process.spawn will make this
1240  # reliable
1241  def _test_page_in_presence_of_child_status
1242    skip 'this test hangs on travis-ci.org' if ENV['CI']
1243    @driver.use_stdout = false
1244
1245    with_dummy_pager do
1246      @driver.page do |io|
1247        refute_equal $stdout, io
1248        assert @driver.paging?
1249      end
1250    end
1251  end
1252
1253  def test_page_stdout
1254    @driver.use_stdout = true
1255
1256    @driver.page do |io|
1257      assert_equal $stdout, io
1258    end
1259
1260    refute @driver.paging?
1261  end
1262
1263  def test_parse_name_method
1264    klass, type, meth = @driver.parse_name 'foo'
1265
1266    assert_equal '',    klass, 'foo class'
1267    assert_equal '.',   type,  'foo type'
1268    assert_equal 'foo', meth,  'foo method'
1269
1270    klass, type, meth = @driver.parse_name '#foo'
1271
1272    assert_equal '',    klass, '#foo class'
1273    assert_equal '#',   type,  '#foo type'
1274    assert_equal 'foo', meth,  '#foo method'
1275
1276    klass, type, meth = @driver.parse_name '::foo'
1277
1278    assert_equal '',    klass, '::foo class'
1279    assert_equal '::',  type,  '::foo type'
1280    assert_equal 'foo', meth,  '::foo method'
1281  end
1282
1283  def test_parse_name_page
1284    klass, type, meth = @driver.parse_name 'ruby:README'
1285
1286    assert_equal 'ruby',   klass, 'ruby project'
1287    assert_equal ':',      type,  'ruby type'
1288    assert_equal 'README', meth,  'ruby page'
1289
1290    klass, type, meth = @driver.parse_name 'ruby:'
1291
1292    assert_equal 'ruby',   klass, 'ruby project'
1293    assert_equal ':',      type,  'ruby type'
1294    assert_nil             meth,  'ruby page'
1295  end
1296
1297  def test_parse_name_page_extenson
1298    klass, type, meth = @driver.parse_name 'ruby:README.EXT'
1299
1300    assert_equal 'ruby',      klass, 'ruby project'
1301    assert_equal ':',         type,  'ruby type'
1302    assert_equal 'README.EXT', meth,  'ruby page'
1303  end
1304
1305  def test_parse_name_single_class
1306    klass, type, meth = @driver.parse_name 'Foo'
1307
1308    assert_equal 'Foo', klass, 'Foo class'
1309    assert_nil          type,  'Foo type'
1310    assert_nil          meth,  'Foo method'
1311
1312    klass, type, meth = @driver.parse_name 'Foo#'
1313
1314    assert_equal 'Foo', klass, 'Foo# class'
1315    assert_equal '#',   type,  'Foo# type'
1316    assert_nil          meth,  'Foo# method'
1317
1318    klass, type, meth = @driver.parse_name 'Foo::'
1319
1320    assert_equal 'Foo', klass, 'Foo:: class'
1321    assert_equal '::',  type,  'Foo:: type'
1322    assert_nil          meth,  'Foo:: method'
1323
1324    klass, type, meth = @driver.parse_name 'Foo.'
1325
1326    assert_equal 'Foo', klass, 'Foo. class'
1327    assert_equal '.',   type,  'Foo. type'
1328    assert_nil          meth,  'Foo. method'
1329
1330    klass, type, meth = @driver.parse_name 'Foo#Bar'
1331
1332    assert_equal 'Foo', klass, 'Foo#Bar class'
1333    assert_equal '#',   type,  'Foo#Bar type'
1334    assert_equal 'Bar', meth,  'Foo#Bar method'
1335
1336    klass, type, meth = @driver.parse_name 'Foo.Bar'
1337
1338    assert_equal 'Foo', klass, 'Foo.Bar class'
1339    assert_equal '.',   type,  'Foo.Bar type'
1340    assert_equal 'Bar', meth,  'Foo.Bar method'
1341
1342    klass, type, meth = @driver.parse_name 'Foo::bar'
1343
1344    assert_equal 'Foo', klass, 'Foo::bar class'
1345    assert_equal '::',  type,  'Foo::bar type'
1346    assert_equal 'bar', meth,  'Foo::bar method'
1347  end
1348
1349  def test_parse_name_namespace
1350    klass, type, meth = @driver.parse_name 'Foo::Bar'
1351
1352    assert_equal 'Foo::Bar', klass, 'Foo::Bar class'
1353    assert_nil               type,  'Foo::Bar type'
1354    assert_nil               meth,  'Foo::Bar method'
1355
1356    klass, type, meth = @driver.parse_name 'Foo::Bar#'
1357
1358    assert_equal 'Foo::Bar', klass, 'Foo::Bar# class'
1359    assert_equal '#',        type,  'Foo::Bar# type'
1360    assert_nil               meth,  'Foo::Bar# method'
1361
1362    klass, type, meth = @driver.parse_name 'Foo::Bar#baz'
1363
1364    assert_equal 'Foo::Bar', klass, 'Foo::Bar#baz class'
1365    assert_equal '#',        type,  'Foo::Bar#baz type'
1366    assert_equal 'baz',      meth,  'Foo::Bar#baz method'
1367  end
1368
1369  def test_parse_name_special
1370    specials = %w[
1371      %
1372      &
1373      *
1374      +
1375      +@
1376      -
1377      -@
1378      /
1379      <
1380      <<
1381      <=
1382      <=>
1383      ==
1384      ===
1385      =>
1386      =~
1387      >
1388      >>
1389      []
1390      []=
1391      ^
1392      `
1393      |
1394      ~
1395      ~@
1396    ]
1397
1398    specials.each do |special|
1399      parsed = @driver.parse_name special
1400
1401      assert_equal ['', '.', special], parsed
1402    end
1403  end
1404
1405  def _test_setup_pager # this test doesn't do anything anymore :(
1406    @driver.use_stdout = false
1407
1408    pager = with_dummy_pager do @driver.setup_pager end
1409
1410    skip "couldn't find a standard pager" unless pager
1411
1412    assert @driver.paging?
1413  ensure
1414    pager.close if pager
1415  end
1416
1417  def util_ancestors_store
1418    store1 = RDoc::RI::Store.new @home_ri
1419    store1.cache[:ancestors] = {
1420      'Foo'      => %w[Object],
1421      'Foo::Bar' => %w[Foo],
1422    }
1423    store1.cache[:class_methods] = {
1424      'Foo'      => %w[c_method new],
1425      'Foo::Bar' => %w[new],
1426    }
1427    store1.cache[:instance_methods] = {
1428      'Foo' => %w[i_method],
1429    }
1430    store1.cache[:modules] = %w[
1431      Foo
1432      Foo::Bar
1433    ]
1434
1435    store2 = RDoc::RI::Store.new @home_ri
1436    store2.cache[:ancestors] = {
1437      'Foo'    => %w[Mixin Object],
1438      'Mixin'  => %w[],
1439      'Object' => %w[X Object],
1440      'X'      => %w[Object],
1441    }
1442    store2.cache[:class_methods] = {
1443      'Foo'    => %w[c_method new],
1444      'Mixin'  => %w[],
1445      'X'      => %w[],
1446      'Object' => %w[],
1447    }
1448    store2.cache[:instance_methods] = {
1449      'Foo'   => %w[i_method],
1450      'Mixin' => %w[],
1451    }
1452    store2.cache[:modules] = %w[
1453      Foo
1454      Mixin
1455      Object
1456      X
1457    ]
1458
1459    @driver.stores = store1, store2
1460  end
1461
1462  def util_multi_store
1463    util_store
1464
1465    @home_ri2 = "#{@home_ri}2"
1466    @store2 = RDoc::RI::Store.new @home_ri2
1467
1468    @top_level = @store2.add_file 'file.rb'
1469
1470    # as if seen in a namespace like class Ambiguous::Other
1471    @mAmbiguous = @top_level.add_module RDoc::NormalModule, 'Ambiguous'
1472
1473    @cFoo = @top_level.add_class RDoc::NormalClass, 'Foo'
1474
1475    @cBar = @top_level.add_class RDoc::NormalClass, 'Bar', 'Foo'
1476    @cFoo_Baz = @cFoo.add_class RDoc::NormalClass, 'Baz'
1477
1478    @baz = @cBar.add_method RDoc::AnyMethod.new(nil, 'baz')
1479    @baz.record_location @top_level
1480
1481    @override = @cBar.add_method RDoc::AnyMethod.new(nil, 'override')
1482    @override.comment = 'must be displayed'
1483    @override.record_location @top_level
1484
1485    @store2.save
1486
1487    @driver.stores = [@store1, @store2]
1488  end
1489
1490  def util_store
1491    @store1 = RDoc::RI::Store.new @home_ri, :home
1492
1493    @top_level = @store1.add_file 'file.rb'
1494
1495    @readme = @store1.add_file 'README.rdoc'
1496    @readme.parser = RDoc::Parser::Simple
1497    @readme.comment =
1498      doc(
1499        head(1, 'README'),
1500        para('This is a README'))
1501
1502    @cFoo = @top_level.add_class RDoc::NormalClass, 'Foo'
1503    @mExt = @top_level.add_module RDoc::NormalModule, 'Ext'
1504    @mInc = @top_level.add_module RDoc::NormalModule, 'Inc'
1505    @cAmbiguous = @top_level.add_class RDoc::NormalClass, 'Ambiguous'
1506
1507    doc = @RM::Document.new @RM::Paragraph.new('Extend thingy')
1508    @cFooExt = @cFoo.add_extend RDoc::Extend.new('Ext', doc)
1509    @cFooExt.record_location @top_level
1510    doc = @RM::Document.new @RM::Paragraph.new('Include thingy')
1511    @cFooInc = @cFoo.add_include RDoc::Include.new('Inc', doc)
1512    @cFooInc.record_location @top_level
1513
1514    @cFoo_Bar = @cFoo.add_class RDoc::NormalClass, 'Bar'
1515
1516    @blah = @cFoo_Bar.add_method RDoc::AnyMethod.new(nil, 'blah')
1517    @blah.call_seq = "blah(5) => 5\nblah(6) => 6\n"
1518    @blah.record_location @top_level
1519
1520    @bother = @cFoo_Bar.add_method RDoc::AnyMethod.new(nil, 'bother')
1521    @bother.block_params = "stuff"
1522    @bother.params = "(things)"
1523    @bother.record_location @top_level
1524
1525    @new = @cFoo_Bar.add_method RDoc::AnyMethod.new nil, 'new'
1526    @new.record_location @top_level
1527    @new.singleton = true
1528
1529    @attr = @cFoo_Bar.add_attribute RDoc::Attr.new nil, 'attr', 'RW', ''
1530    @attr.record_location @top_level
1531
1532    @cFoo_Baz = @cFoo.add_class RDoc::NormalClass, 'Baz'
1533    @cFoo_Baz.record_location @top_level
1534
1535    @inherit = @cFoo.add_method RDoc::AnyMethod.new(nil, 'inherit')
1536    @inherit.record_location @top_level
1537
1538    # overridden by Bar in multi_store
1539    @overridden = @cFoo.add_method RDoc::AnyMethod.new(nil, 'override')
1540    @overridden.comment = 'must not be displayed in Bar#override'
1541    @overridden.record_location @top_level
1542
1543    @cQux = @top_level.add_class RDoc::NormalClass, 'Qux'
1544
1545    @original = @cQux.add_method RDoc::AnyMethod.new(nil, 'original')
1546    @original.comment = 'original comment'
1547    @original.record_location @top_level
1548
1549    @aliased = @original.add_alias RDoc::Alias.new(nil, 'original', 'aliased', 'alias comment'), @cQux
1550    @aliased.record_location @top_level
1551
1552    @store1.save
1553
1554    @driver.stores = [@store1]
1555  end
1556
1557end
1558