1require File.expand_path '../xref_test_case', __FILE__
2
3class TestRDocStore < XrefTestCase
4
5  OBJECT_ANCESTORS = defined?(::BasicObject) ? %w[BasicObject] : []
6
7  def setup
8    super
9
10    @tmpdir = File.join Dir.tmpdir, "test_rdoc_ri_store_#{$$}"
11    @s = RDoc::RI::Store.new @tmpdir
12
13    @top_level = @s.add_file 'file.rb'
14
15    @page = @s.add_file 'README.txt'
16    @page.parser = RDoc::Parser::Simple
17    @page.comment = RDoc::Comment.new 'This is a page', @page
18
19    @klass = @top_level.add_class RDoc::NormalClass, 'Object'
20    @klass.add_comment 'original', @top_level
21    @klass.record_location @top_level
22
23    @cmeth = RDoc::AnyMethod.new nil, 'cmethod'
24    @cmeth.singleton = true
25    @cmeth.record_location @top_level
26
27    @meth_comment = RDoc::Comment.new 'method comment'
28    @meth_comment.location = @top_level
29
30    @meth = RDoc::AnyMethod.new nil, 'method'
31    @meth.record_location @top_level
32    @meth.comment = @meth_comment
33
34    @meth_bang = RDoc::AnyMethod.new nil, 'method!'
35    @meth_bang.record_location @top_level
36
37    @meth_bang_alias = RDoc::Alias.new nil, 'method!', 'method_bang', ''
38    @meth_bang_alias.record_location @top_level
39
40    @meth_bang.add_alias @meth_bang_alias, @klass
41
42    @attr_comment = RDoc::Comment.new 'attribute comment'
43    @attr_comment.location = @top_level
44
45    @attr = RDoc::Attr.new nil, 'attr', 'RW', ''
46    @attr.record_location @top_level
47    @attr.comment = @attr_comment
48
49    @klass.add_method @cmeth
50    @klass.add_method @meth
51    @klass.add_method @meth_bang
52    @klass.add_attribute @attr
53
54    @nest_klass = @klass.add_class RDoc::NormalClass, 'SubClass'
55    @nest_meth = RDoc::AnyMethod.new nil, 'method'
56    @nest_meth.record_location @top_level
57
58    @nest_incl = RDoc::Include.new 'Incl', ''
59    @nest_incl.record_location @top_level
60
61    @nest_klass.add_method @nest_meth
62    @nest_klass.add_include @nest_incl
63
64    @mod = @top_level.add_module RDoc::NormalModule, 'Mod'
65    @mod.record_location @top_level
66  end
67
68  def teardown
69    super
70
71    FileUtils.rm_rf @tmpdir
72  end
73
74  def mu_pp obj
75    s = ''
76    s = PP.pp obj, s
77    s.force_encoding Encoding.default_external if defined? Encoding
78    s.chomp
79  end
80
81  def assert_cache imethods, cmethods, attrs, modules,
82                   ancestors = {}, pages = [], main = nil, title = nil
83    imethods ||= { 'Object' => %w[method method! method_bang] }
84    cmethods ||= { 'Object' => %w[cmethod] }
85    attrs    ||= { 'Object' => ['attr_accessor attr'] }
86
87    # this is sort-of a hack
88    @s.clean_cache_collection ancestors
89
90    expected = {
91      :ancestors                   => ancestors,
92      :attributes                  => attrs,
93      :class_methods               => cmethods,
94      :c_class_variables           => {},
95      :c_singleton_class_variables => {},
96      :encoding                    => nil,
97      :instance_methods            => imethods,
98      :modules                     => modules,
99      :pages                       => pages,
100      :main                        => main,
101      :title                       => title,
102    }
103
104    @s.save_cache
105
106    assert_equal expected, @s.cache
107  end
108
109  def assert_directory path
110    assert File.directory?(path), "#{path} is not a directory"
111  end
112
113  def assert_file path
114    assert File.file?(path), "#{path} is not a file"
115  end
116
117  def refute_file path
118    refute File.exist?(path), "#{path} exists"
119  end
120
121  def test_add_c_enclosure
122    @s.add_c_enclosure 'cC1', @c1
123
124    expected = { 'cC1' => @c1 }
125
126    assert_equal expected, @s.c_enclosure_classes
127  end
128
129  def test_add_c_variables
130    options = RDoc::Options.new
131
132    c_file = @s.add_file 'ext.c'
133
134    some_ext   = c_file.add_class RDoc::NormalClass, 'SomeExt'
135                 c_file.add_class RDoc::SingleClass, 'SomeExtSingle'
136
137    c_parser = RDoc::Parser::C.new c_file, 'ext.c', '', options, nil
138
139    c_parser.classes['cSomeExt']             = some_ext
140    c_parser.singleton_classes['s_cSomeExt'] = 'SomeExtSingle'
141
142    @s.add_c_variables c_parser
143
144    expected = { 'ext.c' => { 'cSomeExt' => 'SomeExt' } }
145
146    assert_equal expected, @s.c_class_variables
147
148    expected = { 'ext.c' => { 's_cSomeExt' => 'SomeExtSingle' } }
149
150    assert_equal expected, @s.c_singleton_class_variables
151  end
152
153  def test_add_file
154    top_level = @store.add_file 'file.rb'
155
156    assert_kind_of RDoc::TopLevel, top_level
157    assert_equal @store, top_level.store
158    assert_equal 'file.rb', top_level.name
159    assert_includes @store.all_files, top_level
160
161    assert_same top_level, @store.add_file('file.rb')
162    refute_same top_level, @store.add_file('other.rb')
163  end
164
165  def test_add_file_relative
166    top_level = @store.add_file 'path/file.rb', 'file.rb'
167
168    assert_kind_of RDoc::TopLevel, top_level
169    assert_equal @store, top_level.store
170
171    assert_equal 'path/file.rb', top_level.absolute_name
172    assert_equal 'file.rb',      top_level.relative_name
173
174    assert_includes @store.all_files, top_level
175
176    assert_same top_level, @store.add_file('file.rb')
177    refute_same top_level, @store.add_file('other.rb')
178  end
179
180  def test_all_classes_and_modules
181    expected = %w[
182      C1 C2 C2::C3 C2::C3::H1 C3 C3::H1 C3::H2 C4 C4::C4 C5 C5::C1
183      Child
184      M1 M1::M2
185      Parent
186    ]
187
188    assert_equal expected,
189                 @store.all_classes_and_modules.map { |m| m.full_name }.sort
190  end
191
192  def test_all_files
193    assert_equal %w[xref_data.rb],
194                 @store.all_files.map { |m| m.full_name }.sort
195  end
196
197  def test_all_modules
198    assert_equal %w[M1 M1::M2],
199                 @store.all_modules.map { |m| m.full_name }.sort
200  end
201
202  def test_attributes
203    @s.cache[:attributes]['Object'] = %w[attr]
204
205    expected = { 'Object' => %w[attr] }
206
207    assert_equal expected, @s.attributes
208  end
209
210  def test_class_file
211    assert_equal File.join(@tmpdir, 'Object', 'cdesc-Object.ri'),
212                 @s.class_file('Object')
213    assert_equal File.join(@tmpdir, 'Object', 'SubClass', 'cdesc-SubClass.ri'),
214                 @s.class_file('Object::SubClass')
215  end
216
217  def test_class_methods
218    @s.cache[:class_methods]['Object'] = %w[method]
219
220    expected = { 'Object' => %w[method] }
221
222    assert_equal expected, @s.class_methods
223  end
224
225  def test_class_path
226    assert_equal File.join(@tmpdir, 'Object'), @s.class_path('Object')
227    assert_equal File.join(@tmpdir, 'Object', 'SubClass'),
228                 @s.class_path('Object::SubClass')
229  end
230
231  def test_classes
232    expected = %w[
233      C1 C2 C2::C3 C2::C3::H1 C3 C3::H1 C3::H2 C4 C4::C4 C5 C5::C1
234      Child
235      Parent
236    ]
237
238    assert_equal expected, @store.all_classes.map { |m| m.full_name }.sort
239  end
240
241  def test_complete
242    @c2.add_module_alias @c2_c3, 'A1', @top_level
243
244    @store.complete :public
245
246    a1 = @xref_data.find_class_or_module 'C2::A1'
247
248    assert_equal 'C2::A1', a1.full_name
249    refute_empty a1.aliases
250  end
251
252  def test_find_c_enclosure
253    assert_nil @s.find_c_enclosure 'cC1'
254
255    @s.add_c_enclosure 'cC1', @c1
256
257    assert_equal @c1, @s.find_c_enclosure('cC1')
258  end
259
260  def test_find_c_enclosure_from_cache
261    @s.save_class @klass
262    @s.classes_hash.clear
263
264    assert_nil @s.find_c_enclosure 'cObject'
265
266    @s.c_enclosure_names['cObject'] = 'Object'
267
268    klass = @s.find_c_enclosure('cObject')
269    assert_equal @klass, klass
270
271    assert_empty klass.comment_location
272    assert_equal @top_level, klass.parent
273
274    assert_includes @s.c_enclosure_classes, 'cObject'
275  end
276
277  def test_find_c_enclosure_from_cache_legacy
278    @klass.in_files.clear
279    @s.save_class @klass
280    @s.classes_hash.clear
281
282    assert_nil @s.find_c_enclosure 'cObject'
283
284    @s.c_enclosure_names['cObject'] = 'Object'
285
286    assert_nil @s.find_c_enclosure('cObject')
287  end
288
289  def test_find_class_named
290    assert_equal @c1, @store.find_class_named('C1')
291
292    assert_equal @c2_c3, @store.find_class_named('C2::C3')
293  end
294
295  def test_find_class_named_from
296    assert_equal @c5_c1, @store.find_class_named_from('C1', 'C5')
297
298    assert_equal @c1,    @store.find_class_named_from('C1', 'C4')
299  end
300
301  def test_find_class_or_module
302    assert_equal @m1, @store.find_class_or_module('M1')
303    assert_equal @c1, @store.find_class_or_module('C1')
304
305    assert_equal @m1, @store.find_class_or_module('::M1')
306    assert_equal @c1, @store.find_class_or_module('::C1')
307  end
308
309  def test_find_file_named
310    assert_equal @xref_data, @store.find_file_named(@file_name)
311  end
312
313  def test_find_module_named
314    assert_equal @m1,    @store.find_module_named('M1')
315    assert_equal @m1_m2, @store.find_module_named('M1::M2')
316  end
317
318  def test_find_text_page
319    page = @store.add_file 'PAGE.txt'
320    page.parser = RDoc::Parser::Simple
321
322    assert_nil @store.find_text_page 'no such page'
323
324    assert_equal page, @store.find_text_page('PAGE.txt')
325  end
326
327  def test_friendly_path
328    @s.path = @tmpdir
329    @s.type = nil
330    assert_equal @s.path, @s.friendly_path
331
332    @s.type = :extra
333    assert_equal @s.path, @s.friendly_path
334
335    @s.type = :system
336    assert_equal "ruby core", @s.friendly_path
337
338    @s.type = :site
339    assert_equal "ruby site", @s.friendly_path
340
341    @s.type = :home
342    assert_equal "~/.rdoc", @s.friendly_path
343
344    @s.type = :gem
345    @s.path = "#{@tmpdir}/gem_repository/doc/gem_name-1.0/ri"
346    assert_equal "gem gem_name-1.0", @s.friendly_path
347  end
348
349  def test_dry_run
350    refute @s.dry_run
351
352    @s.dry_run = true
353
354    assert @s.dry_run
355  end
356
357  def test_instance_methods
358    @s.cache[:instance_methods]['Object'] = %w[method]
359
360    expected = { 'Object' => %w[method] }
361
362    assert_equal expected, @s.instance_methods
363  end
364
365  def test_load_all
366    FileUtils.mkdir_p @tmpdir
367
368    @s.save
369
370    s = RDoc::Store.new @tmpdir
371
372    s.load_all
373
374    assert_equal [@klass, @nest_klass], s.all_classes.sort
375    assert_equal [@mod],                s.all_modules.sort
376    assert_equal [@page, @top_level],   s.all_files.sort
377
378    methods = s.all_classes_and_modules.map do |mod|
379      mod.method_list
380    end.flatten.sort
381
382    _meth_bang_alias = RDoc::AnyMethod.new nil, 'method_bang'
383    _meth_bang_alias.parent = @klass
384
385    assert_equal [@meth, @meth_bang, _meth_bang_alias, @nest_meth, @cmeth],
386                 methods.sort_by { |m| m.full_name }
387
388    method = methods.find { |m| m == @meth }
389    assert_equal @meth_comment.parse, method.comment
390
391    assert_equal @klass, methods.last.parent
392
393    attributes = s.all_classes_and_modules.map do |mod|
394      mod.attributes
395    end.flatten.sort
396
397    assert_equal [@attr], attributes
398
399    assert_equal @attr_comment.parse, attributes.first.comment
400  end
401
402  def test_load_cache
403    cache = {
404      :c_class_variables           =>
405        { 'file.c' => { 'cKlass' => 'Klass' } },
406      :c_singleton_class_variables =>
407        { 'file.c' => { 'sKlass' => 'KlassSingle' } },
408      :encoding                    => :encoding_value,
409      :methods                     => { "Object" => %w[Object#method] },
410      :main                        => @page.full_name,
411      :modules                     => %w[Object],
412      :pages                       => [],
413    }
414
415    Dir.mkdir @tmpdir
416
417    open File.join(@tmpdir, 'cache.ri'), 'wb' do |io|
418      Marshal.dump cache, io
419    end
420
421    @s.load_cache
422
423    assert_equal cache, @s.cache
424
425    assert_equal :encoding_value, @s.encoding
426    assert_equal 'README.txt',    @s.main
427
428    expected = { 'file.c' => { 'cKlass' => 'Klass' } }
429    assert_equal expected, @s.cache[:c_class_variables]
430
431    expected = { 'file.c' => { 'sKlass' => 'KlassSingle' } }
432    assert_equal expected, @s.cache[:c_singleton_class_variables]
433
434    expected = { 'cKlass' => 'Klass' }
435    assert_equal expected, @s.c_enclosure_names
436  end
437
438  def test_load_cache_encoding_differs
439    skip "Encoding not implemented" unless Object.const_defined? :Encoding
440
441    cache = {
442      :c_class_variables           => {},
443      :c_singleton_class_variables => {},
444      :encoding                    => Encoding::ISO_8859_1,
445      :main                        => nil,
446      :methods                     => { "Object" => %w[Object#method] },
447      :modules                     => %w[Object],
448      :pages                       => [],
449    }
450
451    Dir.mkdir @tmpdir
452
453    open File.join(@tmpdir, 'cache.ri'), 'wb' do |io|
454      Marshal.dump cache, io
455    end
456
457    @s.encoding = Encoding::UTF_8
458
459    @s.load_cache
460
461    assert_equal cache, @s.cache
462
463    assert_equal Encoding::UTF_8, @s.encoding
464  end
465
466  def test_load_cache_no_cache
467    cache = {
468      :ancestors                   => {},
469      :attributes                  => {},
470      :class_methods               => {},
471      :c_class_variables           => {},
472      :c_singleton_class_variables => {},
473      :encoding                    => nil,
474      :instance_methods            => {},
475      :main                        => nil,
476      :modules                     => [],
477      :pages                       => [],
478      :title                       => nil,
479    }
480
481    @s.load_cache
482
483    assert_equal cache, @s.cache
484  end
485
486  def test_load_cache_legacy
487    cache = {
488      :ancestors        => {},
489      :attributes       => {},
490      :class_methods    => {},
491      :encoding         => :encoding_value,
492      :instance_methods => { "Object" => %w[Object#method] },
493      :modules          => %w[Object],
494      # no :pages
495      # no :main
496      # no :c_class_variables
497      # no :c_singleton_class_variables
498    }
499
500    Dir.mkdir @tmpdir
501
502    open File.join(@tmpdir, 'cache.ri'), 'wb' do |io|
503      Marshal.dump cache, io
504    end
505
506    @s.load_cache
507
508    expected = {
509      :ancestors                   => {},
510      :attributes                  => {},
511      :class_methods               => {},
512      :c_class_variables           => {},
513      :c_singleton_class_variables => {},
514      :encoding                    => :encoding_value,
515      :instance_methods            => { "Object" => %w[Object#method] },
516      :main                        => nil,
517      :modules                     => %w[Object],
518      :pages                       => [],
519    }
520
521    assert_equal expected, @s.cache
522
523    assert_equal :encoding_value, @s.encoding
524    assert_nil                    @s.main
525  end
526
527  def test_load_class
528    @s.save_class @klass
529    @s.classes_hash.clear
530
531    assert_equal @klass, @s.load_class('Object')
532
533    assert_includes @s.classes_hash, 'Object'
534  end
535
536  def test_load_method
537    @s.save_method @klass, @meth_bang
538
539    meth = @s.load_method('Object', '#method!')
540    assert_equal @meth_bang, meth
541    assert_equal @klass, meth.parent
542    assert_equal @s, meth.store
543  end
544
545  def test_load_method_legacy
546    @s.save_method @klass, @meth
547
548    file = @s.method_file @klass.full_name, @meth.full_name
549
550    open file, 'wb' do |io|
551      io.write "\x04\bU:\x14RDoc::AnyMethod[\x0Fi\x00I" +
552               "\"\vmethod\x06:\x06EF\"\x11Klass#method0:\vpublic" +
553               "o:\eRDoc::Markup::Document\x06:\v@parts[\x06" +
554               "o:\x1CRDoc::Markup::Paragraph\x06;\t[\x06I" +
555               "\"\x16this is a comment\x06;\x06FI" +
556               "\"\rcall_seq\x06;\x06FI\"\x0Fsome_block\x06;\x06F" +
557               "[\x06[\aI\"\faliased\x06;\x06Fo;\b\x06;\t[\x06" +
558               "o;\n\x06;\t[\x06I\"\x12alias comment\x06;\x06FI" +
559               "\"\nparam\x06;\x06F"
560    end
561
562    meth = @s.load_method('Object', '#method')
563    assert_equal 'Klass#method', meth.full_name
564    assert_equal @klass,         meth.parent
565    assert_equal @s,             meth.store
566  end
567
568  def test_load_page
569    @s.save_page @page
570
571    assert_equal @page, @s.load_page('README.txt')
572  end
573
574  def test_main
575    assert_equal nil, @s.main
576
577    @s.main = 'README.txt'
578
579    assert_equal 'README.txt', @s.main
580  end
581
582  def test_method_file
583    assert_equal File.join(@tmpdir, 'Object', 'method-i.ri'),
584                 @s.method_file('Object', 'Object#method')
585
586    assert_equal File.join(@tmpdir, 'Object', 'method%21-i.ri'),
587                 @s.method_file('Object', 'Object#method!')
588
589    assert_equal File.join(@tmpdir, 'Object', 'SubClass', 'method%21-i.ri'),
590                 @s.method_file('Object::SubClass', 'Object::SubClass#method!')
591
592    assert_equal File.join(@tmpdir, 'Object', 'method-c.ri'),
593                 @s.method_file('Object', 'Object::method')
594  end
595
596  def test_module_names
597    @s.save_class @klass
598
599    assert_equal %w[Object], @s.module_names
600  end
601
602  def test_page
603    page = @store.add_file 'PAGE.txt'
604    page.parser = RDoc::Parser::Simple
605
606    assert_nil @store.page 'no such page'
607
608    assert_equal page, @store.page('PAGE')
609  end
610
611  def test_save
612    FileUtils.mkdir_p @tmpdir
613
614    @s.save
615
616    assert_directory File.join(@tmpdir, 'Object')
617
618    assert_file File.join(@tmpdir, 'Object', 'cdesc-Object.ri')
619    assert_file File.join(@tmpdir, 'Object', 'method-i.ri')
620    assert_file File.join(@tmpdir, 'page-README_txt.ri')
621
622    assert_file File.join(@tmpdir, 'cache.ri')
623
624    expected = {
625      :ancestors => {
626        'Object::SubClass' => %w[Incl Object],
627      },
628      :attributes => { 'Object' => ['attr_accessor attr'] },
629      :class_methods => { 'Object' => %w[cmethod] },
630      :c_class_variables => {},
631      :c_singleton_class_variables => {},
632      :instance_methods => {
633        'Object' => %w[attr method method! method_bang],
634        'Object::SubClass' => %w[method],
635      },
636      :main => nil,
637      :modules => %w[Mod Object Object::SubClass],
638      :encoding => nil,
639      :pages => %w[README.txt],
640      :title => nil,
641    }
642
643    expected[:ancestors]['Object'] = %w[BasicObject] if defined?(::BasicObject)
644
645    open File.join(@tmpdir, 'cache.ri'), 'rb' do |io|
646      cache = Marshal.load io.read
647
648      assert_equal expected, cache
649    end
650  end
651
652  def test_save_cache
653    @s.save_class @klass
654    @s.save_method @klass, @meth
655    @s.save_method @klass, @cmeth
656    @s.save_class @nest_klass
657    @s.save_page @page
658    @s.encoding = :encoding_value
659    @s.main     = @page.full_name
660    @s.title    = 'title'
661
662    options = RDoc::Options.new
663
664    c_file = @s.add_file 'ext.c'
665
666    some_ext   = c_file.add_class RDoc::NormalClass, 'SomeExt'
667                 c_file.add_class RDoc::SingleClass, 'SomeExtSingle'
668
669    c_parser = RDoc::Parser::C.new c_file, 'ext.c', '', options, nil
670
671    c_parser.classes['cSomeExt']             = some_ext
672    c_parser.singleton_classes['s_cSomeExt'] = 'SomeExtSingle'
673
674    @s.add_c_variables c_parser
675
676    @s.save_cache
677
678    assert_file File.join(@tmpdir, 'cache.ri')
679
680    c_class_variables = {
681      'ext.c' => {
682        'cSomeExt' => 'SomeExt'
683      }
684    }
685
686    c_singleton_class_variables = {
687      'ext.c' => {
688        's_cSomeExt' => 'SomeExtSingle'
689      }
690    }
691
692    expected = {
693      :ancestors => {
694        'Object::SubClass' => %w[Incl Object],
695      },
696      :attributes => { 'Object' => ['attr_accessor attr'] },
697      :class_methods => { 'Object' => %w[cmethod] },
698      :c_class_variables => c_class_variables,
699      :c_singleton_class_variables => c_singleton_class_variables,
700      :instance_methods => {
701        'Object' => %w[method method! method_bang],
702        'Object::SubClass' => %w[method],
703      },
704      :main => @page.full_name,
705      :modules => %w[Object Object::SubClass],
706      :encoding => :encoding_value,
707      :pages => %w[README.txt],
708      :title => 'title',
709    }
710
711    expected[:ancestors]['Object'] = %w[BasicObject] if defined?(::BasicObject)
712
713    open File.join(@tmpdir, 'cache.ri'), 'rb' do |io|
714      cache = Marshal.load io.read
715
716      assert_equal expected, cache
717    end
718  end
719
720  def test_save_cache_dry_run
721    @s.dry_run = true
722
723    @s.save_class @klass
724    @s.save_method @klass, @meth
725    @s.save_method @klass, @cmeth
726    @s.save_class @nest_klass
727
728    @s.save_cache
729
730    refute_file File.join(@tmpdir, 'cache.ri')
731  end
732
733  def test_save_cache_duplicate_methods
734    @s.save_method @klass, @meth
735    @s.save_method @klass, @meth
736
737    @s.save_cache
738
739    assert_cache({ 'Object' => %w[method] }, {}, {}, [])
740  end
741
742  def test_save_cache_duplicate_pages
743    @s.save_page @page
744    @s.save_page @page
745
746    @s.save_cache
747
748    assert_cache({}, {}, {}, [], {}, %w[README.txt])
749  end
750
751  def test_save_class
752    @s.save_class @klass
753
754    assert_directory File.join(@tmpdir, 'Object')
755    assert_file File.join(@tmpdir, 'Object', 'cdesc-Object.ri')
756
757    assert_cache nil, nil, nil, %w[Object], 'Object' => OBJECT_ANCESTORS
758
759    assert_equal @klass, @s.load_class('Object')
760  end
761
762  def test_save_class_basic_object
763    @klass.instance_variable_set :@superclass, nil
764
765    @s.save_class @klass
766
767    assert_directory File.join(@tmpdir, 'Object')
768    assert_file File.join(@tmpdir, 'Object', 'cdesc-Object.ri')
769
770    assert_cache(nil, nil, nil, %w[Object])
771
772    assert_equal @klass, @s.load_class('Object')
773  end
774
775  def test_save_class_delete
776    # save original
777    @s.save_class @klass
778    @s.save_method @klass, @meth
779    @s.save_method @klass, @meth_bang
780    @s.save_method @klass, @cmeth
781    @s.save_method @klass, @attr
782    @s.save_cache
783
784    klass = RDoc::NormalClass.new 'Object'
785
786    meth = klass.add_method RDoc::AnyMethod.new(nil, 'replace')
787    meth.record_location @top_level
788
789    # load original, save newly updated class
790    @s = RDoc::RI::Store.new @tmpdir
791    @s.load_cache
792    @s.save_class klass
793    @s.save_cache
794
795    # load from disk again
796    @s = RDoc::RI::Store.new @tmpdir
797    @s.load_cache
798
799    @s.load_class 'Object'
800
801    assert_cache({ 'Object' => %w[replace] }, {},
802                 { 'Object' => %w[attr_accessor\ attr] }, %w[Object],
803                   'Object' => OBJECT_ANCESTORS)
804
805    # assert these files were deleted
806    refute_file @s.method_file(@klass.full_name, @meth.full_name)
807    refute_file @s.method_file(@klass.full_name, @meth_bang.full_name)
808    refute_file @s.method_file(@klass.full_name, @cmeth.full_name)
809
810    # assert these files were not deleted
811    assert_file @s.method_file(@klass.full_name, @attr.full_name)
812  end
813
814  def test_save_class_dry_run
815    @s.dry_run = true
816
817    @s.save_class @klass
818
819    refute_file File.join(@tmpdir, 'Object')
820    refute_file File.join(@tmpdir, 'Object', 'cdesc-Object.ri')
821  end
822
823  def test_save_class_loaded
824    @s.save
825
826    assert_directory File.join(@tmpdir, 'Object')
827    assert_file      File.join(@tmpdir, 'Object', 'cdesc-Object.ri')
828
829    assert_file @s.method_file(@klass.full_name, @attr.full_name)
830    assert_file @s.method_file(@klass.full_name, @cmeth.full_name)
831    assert_file @s.method_file(@klass.full_name, @meth.full_name)
832    assert_file @s.method_file(@klass.full_name, @meth_bang.full_name)
833
834    s = RDoc::Store.new @s.path
835    s.load_cache
836
837    loaded = s.load_class 'Object'
838
839    assert_equal @klass, loaded
840
841    s.save_class loaded
842
843    s = RDoc::Store.new @s.path
844    s.load_cache
845
846    reloaded = s.load_class 'Object'
847
848    assert_equal @klass, reloaded
849
850    # assert these files were not deleted.  Bug #171
851    assert_file s.method_file(@klass.full_name, @attr.full_name)
852    assert_file s.method_file(@klass.full_name, @cmeth.full_name)
853    assert_file s.method_file(@klass.full_name, @meth.full_name)
854    assert_file s.method_file(@klass.full_name, @meth_bang.full_name)
855  end
856
857  def test_save_class_merge
858    @s.save_class @klass
859
860    klass = RDoc::NormalClass.new 'Object'
861    klass.add_comment 'new comment', @top_level
862
863    s = RDoc::RI::Store.new @tmpdir
864    s.save_class klass
865
866    s = RDoc::RI::Store.new @tmpdir
867
868    inner = @RM::Document.new @RM::Paragraph.new 'new comment'
869    inner.file = @top_level
870
871    document = @RM::Document.new inner
872
873    assert_equal document, s.load_class('Object').comment_location
874  end
875
876  # This is a functional test
877  def test_save_class_merge_constant
878    store = RDoc::Store.new
879    tl = store.add_file 'file.rb'
880
881    klass = tl.add_class RDoc::NormalClass, 'C'
882    klass.add_comment 'comment', tl
883
884    const = klass.add_constant RDoc::Constant.new('CONST', nil, nil)
885    const.record_location tl
886
887    @s.save_class klass
888
889    # separate parse run, independent store
890    store = RDoc::Store.new
891    tl = store.add_file 'file.rb'
892    klass2 = tl.add_class RDoc::NormalClass, 'C'
893    klass2.record_location tl
894
895    s = RDoc::RI::Store.new @tmpdir
896    s.save_class klass2
897
898    # separate `ri` run, independent store
899    s = RDoc::RI::Store.new @tmpdir
900
901    result = s.load_class 'C'
902
903    assert_empty result.constants
904  end
905
906  def test_save_class_methods
907    @s.save_class @klass
908
909    assert_directory File.join(@tmpdir, 'Object')
910    assert_file File.join(@tmpdir, 'Object', 'cdesc-Object.ri')
911
912    assert_cache nil, nil, nil, %w[Object], 'Object' => OBJECT_ANCESTORS
913
914    assert_equal @klass, @s.load_class('Object')
915  end
916
917  def test_save_class_nested
918    @s.save_class @nest_klass
919
920    assert_directory File.join(@tmpdir, 'Object', 'SubClass')
921    assert_file File.join(@tmpdir, 'Object', 'SubClass', 'cdesc-SubClass.ri')
922
923    assert_cache({ 'Object::SubClass' => %w[method] }, {}, {},
924                 %w[Object::SubClass], 'Object::SubClass' => %w[Incl Object])
925  end
926
927  def test_save_method
928    @s.save_method @klass, @meth
929
930    assert_directory File.join(@tmpdir, 'Object')
931    assert_file File.join(@tmpdir, 'Object', 'method-i.ri')
932
933    assert_cache({ 'Object' => %w[method] }, {}, {}, [])
934
935    assert_equal @meth, @s.load_method('Object', '#method')
936  end
937
938  def test_save_method_dry_run
939    @s.dry_run = true
940
941    @s.save_method @klass, @meth
942
943    refute_file File.join(@tmpdir, 'Object')
944    refute_file File.join(@tmpdir, 'Object', 'method-i.ri')
945  end
946
947  def test_save_method_nested
948    @s.save_method @nest_klass, @nest_meth
949
950    assert_directory File.join(@tmpdir, 'Object', 'SubClass')
951    assert_file File.join(@tmpdir, 'Object', 'SubClass', 'method-i.ri')
952
953    assert_cache({ 'Object::SubClass' => %w[method] }, {}, {}, [])
954  end
955
956  def test_save_page
957    @s.save_page @page
958
959    assert_file File.join(@tmpdir, 'page-README_txt.ri')
960
961    assert_cache({}, {}, {}, [], {}, %w[README.txt])
962  end
963
964  def test_save_page_file
965    @s.save_page @top_level
966
967    refute_file File.join(@tmpdir, 'page-file_rb.ri')
968  end
969
970  def test_source
971    @s.path = @tmpdir
972    @s.type = nil
973    assert_equal @s.path, @s.source
974
975    @s.type = :extra
976    assert_equal @s.path, @s.source
977
978    @s.type = :system
979    assert_equal "ruby", @s.source
980
981    @s.type = :site
982    assert_equal "site", @s.source
983
984    @s.type = :home
985    assert_equal "home", @s.source
986
987    @s.type = :gem
988    @s.path = "#{@tmpdir}/gem_repository/doc/gem_name-1.0/ri"
989    assert_equal "gem_name-1.0", @s.source
990  end
991
992  def test_title
993    assert_equal nil, @s.title
994
995    @s.title = 'rdoc'
996
997    assert_equal 'rdoc', @s.title
998  end
999
1000end
1001
1002