1require 'rdoc/test_case'
2
3=begin
4  TODO: test call-seq parsing
5
6/*
7 *  call-seq:
8 *     ARGF.readlines(sep=$/)     -> array
9 *     ARGF.readlines(limit)      -> array
10 *     ARGF.readlines(sep, limit) -> array
11 *
12 *     ARGF.to_a(sep=$/)     -> array
13 *     ARGF.to_a(limit)      -> array
14 *     ARGF.to_a(sep, limit) -> array
15 *
16 *  Reads +ARGF+'s current file in its entirety, returning an +Array+ of its
17 *  lines, one line per element. Lines are assumed to be separated by _sep_.
18 *
19 *     lines = ARGF.readlines
20 *     lines[0]                #=> "This is line one\n"
21 */
22
23assert call-seq did not stop at first empty line
24
25/*
26 * call-seq:
27 *
28 *  flt ** other  ->  float
29 *
30 * Raises <code>float</code> the <code>other</code> power.
31 *
32 *    2.0**3      #=> 8.0
33 */
34
35assert call-seq correct (bug: was empty)
36
37/* call-seq: flt ** other  ->  float */
38
39assert call-seq correct
40
41=end
42
43class TestRDocParserC < RDoc::TestCase
44
45  def setup
46    super
47
48    @tempfile = Tempfile.new self.class.name
49    filename = @tempfile.path
50
51    @top_level = @store.add_file filename
52    @fn = filename
53    @options = RDoc::Options.new
54    @options.verbosity = 2
55    @stats = RDoc::Stats.new @store, 0
56  end
57
58  def teardown
59    super
60
61    @tempfile.close
62  end
63
64  def test_class_can_parse
65    c_parser = RDoc::Parser::C
66
67    temp_dir do
68      FileUtils.touch 'file.C'
69      assert_equal c_parser, c_parser.can_parse('file.C')
70
71      FileUtils.touch 'file.CC'
72      assert_equal c_parser, c_parser.can_parse('file.CC')
73
74      FileUtils.touch 'file.H'
75      assert_equal c_parser, c_parser.can_parse('file.H')
76
77      FileUtils.touch 'file.HH'
78      assert_equal c_parser, c_parser.can_parse('file.HH')
79
80      FileUtils.touch 'file.c'
81      assert_equal c_parser, c_parser.can_parse('file.c')
82
83      FileUtils.touch 'file.cc'
84      assert_equal c_parser, c_parser.can_parse('file.cc')
85
86      FileUtils.touch 'file.cpp'
87      assert_equal c_parser, c_parser.can_parse('file.cpp')
88
89      FileUtils.touch 'file.cxx'
90      assert_equal c_parser, c_parser.can_parse('file.cxx')
91
92      FileUtils.touch 'file.h'
93      assert_equal c_parser, c_parser.can_parse('file.h')
94
95      FileUtils.touch 'file.hh'
96      assert_equal c_parser, c_parser.can_parse('file.hh')
97
98      FileUtils.touch 'file.y'
99      assert_equal c_parser, c_parser.can_parse('file.y')
100    end
101  end
102
103  def test_initialize
104    some_ext        = @top_level.add_class RDoc::NormalClass, 'SomeExt'
105                      @top_level.add_class RDoc::SingleClass, 'SomeExtSingle'
106
107    @store.cache[:c_class_variables] = {
108      @fn => { 'cSomeExt' => 'SomeExt' }
109    }
110
111    @store.cache[:c_singleton_class_variables] = {
112      @fn => { 'cSomeExtSingle' => 'SomeExtSingle' }
113    }
114
115    parser = RDoc::Parser::C.new @top_level, @fn, '', @options, @stats
116
117    expected = { 'cSomeExt' => some_ext }
118    assert_equal expected, parser.classes
119
120    expected = { 'cSomeExtSingle' => 'SomeExtSingle' }
121    assert_equal expected, parser.singleton_classes
122
123    expected = {
124      'cSomeExt'       => 'SomeExt',
125      'cSomeExtSingle' => 'SomeExtSingle',
126    }
127    known_classes = parser.known_classes.delete_if do |key, _|
128      RDoc::KNOWN_CLASSES.keys.include? key
129    end
130
131    assert_equal expected, known_classes
132  end
133
134  def test_do_attr_rb_attr
135    content = <<-EOF
136void Init_Blah(void) {
137  cBlah = rb_define_class("Blah", rb_cObject);
138
139  /*
140   * This is an accessor
141   */
142  rb_attr(cBlah, rb_intern("accessor"), 1, 1, Qfalse);
143
144  /*
145   * This is a reader
146   */
147  rb_attr(cBlah, rb_intern("reader"), 1, 0, Qfalse);
148
149  /*
150   * This is a writer
151   */
152  rb_attr(cBlah, rb_intern("writer"), 0, 1, Qfalse);
153}
154    EOF
155
156    klass = util_get_class content, 'cBlah'
157
158    attrs = klass.attributes
159    assert_equal 3, attrs.length, attrs.inspect
160
161    accessor = attrs.shift
162    assert_equal 'accessor',            accessor.name
163    assert_equal 'RW',                  accessor.rw
164    assert_equal 'This is an accessor', accessor.comment.text
165    assert_equal @top_level,            accessor.file
166
167    reader = attrs.shift
168    assert_equal 'reader',           reader.name
169    assert_equal 'R',                reader.rw
170    assert_equal 'This is a reader', reader.comment.text
171
172    writer = attrs.shift
173    assert_equal 'writer',           writer.name
174    assert_equal 'W',                writer.rw
175    assert_equal 'This is a writer', writer.comment.text
176  end
177
178  def test_do_attr_rb_define_attr
179    content = <<-EOF
180void Init_Blah(void) {
181  cBlah = rb_define_class("Blah", rb_cObject);
182
183  /*
184   * This is an accessor
185   */
186  rb_define_attr(cBlah, "accessor", 1, 1);
187}
188    EOF
189
190    klass = util_get_class content, 'cBlah'
191
192    attrs = klass.attributes
193    assert_equal 1, attrs.length, attrs.inspect
194
195    accessor = attrs.shift
196    assert_equal 'accessor',            accessor.name
197    assert_equal 'RW',                  accessor.rw
198    assert_equal 'This is an accessor', accessor.comment.text
199    assert_equal @top_level,            accessor.file
200  end
201
202  def test_do_aliases
203    content = <<-EOF
204/*
205 * This should show up as an alias with documentation
206 */
207VALUE blah(VALUE klass, VALUE year) {
208}
209
210void Init_Blah(void) {
211  cDate = rb_define_class("Date", rb_cObject);
212
213  rb_define_method(cDate, "blah", blah, 1);
214
215  rb_define_alias(cDate, "bleh", "blah");
216}
217    EOF
218
219    klass = util_get_class content, 'cDate'
220
221    methods = klass.method_list
222    assert_equal 2,      methods.length
223    assert_equal 'bleh', methods.last.name
224    assert_equal 'blah', methods.last.is_alias_for.name
225
226    assert_equal @top_level, methods.last.is_alias_for.file
227    assert_equal @top_level, methods.last.file
228  end
229
230  def test_do_aliases_singleton
231    content = <<-EOF
232/*
233 * This should show up as a method with documentation
234 */
235VALUE blah(VALUE klass, VALUE year) {
236}
237
238void Init_Blah(void) {
239  cDate = rb_define_class("Date", rb_cObject);
240  sDate = rb_singleton_class(cDate);
241
242  rb_define_method(sDate, "blah", blah, 1);
243
244  /*
245   * This should show up as an alias
246   */
247  rb_define_alias(sDate, "bleh", "blah");
248}
249    EOF
250
251    klass = util_get_class content, 'cDate'
252
253    methods = klass.method_list
254
255    assert_equal 2,      methods.length
256    assert_equal 'bleh', methods.last.name
257    assert               methods.last.singleton
258    assert_equal 'blah', methods.last.is_alias_for.name
259    assert_equal 'This should show up as an alias', methods.last.comment.text
260  end
261
262  def test_do_classes_boot_class
263    content = <<-EOF
264/* Document-class: Foo
265 * this is the Foo boot class
266 */
267VALUE cFoo = boot_defclass("Foo", rb_cObject);
268    EOF
269
270    klass = util_get_class content, 'cFoo'
271    assert_equal "this is the Foo boot class", klass.comment.text
272    assert_equal 'Object', klass.superclass
273  end
274
275  def test_do_classes_boot_class_nil
276    content = <<-EOF
277/* Document-class: Foo
278 * this is the Foo boot class
279 */
280VALUE cFoo = boot_defclass("Foo", 0);
281    EOF
282
283    klass = util_get_class content, 'cFoo'
284    assert_equal "this is the Foo boot class", klass.comment.text
285    assert_equal nil, klass.superclass
286  end
287
288  def test_do_aliases_missing_class
289    content = <<-EOF
290void Init_Blah(void) {
291  rb_define_alias(cDate, "b", "a");
292}
293    EOF
294
295    _, err = verbose_capture_io do
296      refute util_get_class(content, 'cDate')
297    end
298
299    assert_equal "Enclosing class or module \"cDate\" for alias b a is not known\n",
300                 err
301  end
302
303  def test_do_classes_class
304    content = <<-EOF
305/* Document-class: Foo
306 * this is the Foo class
307 */
308VALUE cFoo = rb_define_class("Foo", rb_cObject);
309    EOF
310
311    klass = util_get_class content, 'cFoo'
312    assert_equal "this is the Foo class", klass.comment.text
313  end
314
315  def test_do_classes_struct
316    content = <<-EOF
317/* Document-class: Foo
318 * this is the Foo class
319 */
320VALUE cFoo = rb_struct_define_without_accessor(
321        "Foo", rb_cObject, foo_alloc,
322        "some", "various", "fields", NULL);
323    EOF
324
325    klass = util_get_class content, 'cFoo'
326    assert_equal "this is the Foo class", klass.comment.text
327  end
328
329  def test_do_classes_class_under
330    content = <<-EOF
331/* Document-class: Kernel::Foo
332 * this is the Foo class under Kernel
333 */
334VALUE cFoo = rb_define_class_under(rb_mKernel, "Foo", rb_cObject);
335    EOF
336
337    klass = util_get_class content, 'cFoo'
338    assert_equal 'Kernel::Foo', klass.full_name
339    assert_equal "this is the Foo class under Kernel", klass.comment.text
340  end
341
342  def test_do_classes_class_under_rb_path2class
343    content = <<-EOF
344/* Document-class: Kernel::Foo
345 * this is Kernel::Foo < A::B
346 */
347VALUE cFoo = rb_define_class_under(rb_mKernel, "Foo", rb_path2class("A::B"));
348    EOF
349
350    klass = util_get_class content, 'cFoo'
351
352    assert_equal 'Kernel::Foo', klass.full_name
353    assert_equal 'A::B', klass.superclass
354    assert_equal 'this is Kernel::Foo < A::B', klass.comment.text
355  end
356
357  def test_do_classes_singleton
358    content = <<-EOF
359VALUE cFoo = rb_define_class("Foo", rb_cObject);
360VALUE cFooS = rb_singleton_class(cFoo);
361    EOF
362
363    util_get_class content, 'cFooS'
364
365    assert_equal 'Foo', @parser.singleton_classes['cFooS']
366  end
367
368  def test_do_classes_module
369    content = <<-EOF
370/* Document-module: Foo
371 * this is the Foo module
372 */
373VALUE mFoo = rb_define_module("Foo");
374    EOF
375
376    klass = util_get_class content, 'mFoo'
377    assert_equal "this is the Foo module", klass.comment.text
378  end
379
380  def test_do_classes_module_under
381    content = <<-EOF
382/* Document-module: Kernel::Foo
383 * this is the Foo module under Kernel
384 */
385VALUE mFoo = rb_define_module_under(rb_mKernel, "Foo");
386    EOF
387
388    klass = util_get_class content, 'mFoo'
389    assert_equal "this is the Foo module under Kernel", klass.comment.text
390  end
391
392  def test_do_constants
393    content = <<-EOF
394#include <ruby.h>
395
396void Init_foo(){
397   VALUE cFoo = rb_define_class("Foo", rb_cObject);
398
399   /* 300: The highest possible score in bowling */
400   rb_define_const(cFoo, "PERFECT", INT2FIX(300));
401
402   /* Huzzah!: What you cheer when you roll a perfect game */
403   rb_define_const(cFoo, "CHEER", rb_str_new2("Huzzah!"));
404
405   /* TEST\:TEST: Checking to see if escaped colon works */
406   rb_define_const(cFoo, "TEST", rb_str_new2("TEST:TEST"));
407
408   /* \\: The file separator on MS Windows */
409   rb_define_const(cFoo, "MSEPARATOR", rb_str_new2("\\"));
410
411   /* /: The file separator on Unix */
412   rb_define_const(cFoo, "SEPARATOR", rb_str_new2("/"));
413
414   /* C:\\Program Files\\Stuff: A directory on MS Windows */
415   rb_define_const(cFoo, "STUFF", rb_str_new2("C:\\Program Files\\Stuff"));
416
417   /* Default definition */
418   rb_define_const(cFoo, "NOSEMI", INT2FIX(99));
419
420   rb_define_const(cFoo, "NOCOMMENT", rb_str_new2("No comment"));
421
422   /*
423    * Multiline comment goes here because this comment spans multiple lines.
424    * Multiline comment goes here because this comment spans multiple lines.
425    */
426   rb_define_const(cFoo, "MULTILINE", INT2FIX(1));
427
428   /*
429    * 1: Multiline comment goes here because this comment spans multiple lines.
430    * Multiline comment goes here because this comment spans multiple lines.
431    */
432   rb_define_const(cFoo, "MULTILINE_VALUE", INT2FIX(1));
433
434   /* Multiline comment goes here because this comment spans multiple lines.
435    * Multiline comment goes here because this comment spans multiple lines.
436    */
437   rb_define_const(cFoo, "MULTILINE_NOT_EMPTY", INT2FIX(1));
438
439   /*
440    * Multiline comment goes here because this comment spans multiple lines.
441    * 1: However, the value extraction should only happen for the first line
442    */
443   rb_define_const(cFoo, "MULTILINE_COLON_ON_SECOND_LINE", INT2FIX(1));
444
445}
446    EOF
447
448    @parser = util_parser content
449
450    @parser.do_classes
451    @parser.do_constants
452
453    klass = @parser.classes['cFoo']
454    assert klass
455
456    constants = klass.constants
457    assert !klass.constants.empty?
458
459    assert_equal @top_level, constants.first.file
460
461    constants = constants.map { |c| [c.name, c.value, c.comment.text] }
462
463    assert_equal ['PERFECT', '300', 'The highest possible score in bowling   '],
464                 constants.shift
465    assert_equal ['CHEER', 'Huzzah!',
466                  'What you cheer when you roll a perfect game   '],
467                 constants.shift
468    assert_equal ['TEST', 'TEST:TEST',
469                  'Checking to see if escaped colon works   '],
470                 constants.shift
471    assert_equal ['MSEPARATOR', '\\',
472                  'The file separator on MS Windows   '],
473                 constants.shift
474    assert_equal ['SEPARATOR', '/',
475                  'The file separator on Unix   '],
476                 constants.shift
477    assert_equal ['STUFF', 'C:\\Program Files\\Stuff',
478                  'A directory on MS Windows   '],
479                 constants.shift
480    assert_equal ['NOSEMI', 'INT2FIX(99)',
481                  'Default definition   '],
482                 constants.shift
483    assert_equal ['NOCOMMENT', 'rb_str_new2("No comment")', ''],
484                 constants.shift
485
486    comment = <<-EOF.chomp
487Multiline comment goes here because this comment spans multiple lines.
488Multiline comment goes here because this comment spans multiple lines.
489    EOF
490    assert_equal ['MULTILINE',           'INT2FIX(1)', comment], constants.shift
491    assert_equal ['MULTILINE_VALUE',     '1',          comment], constants.shift
492    assert_equal ['MULTILINE_NOT_EMPTY', 'INT2FIX(1)', comment], constants.shift
493
494    comment = <<-EOF.chomp
495Multiline comment goes here because this comment spans multiple lines.
4961: However, the value extraction should only happen for the first line
497    EOF
498    assert_equal ['MULTILINE_COLON_ON_SECOND_LINE', 'INT2FIX(1)', comment],
499                 constants.shift
500
501    assert constants.empty?, constants.inspect
502  end
503
504  def test_do_constants_curses
505    content = <<-EOF
506void Init_curses(){
507  mCurses = rb_define_module("Curses");
508
509  /*
510   * Document-const: Curses::COLOR_BLACK
511   *
512   * Value of the color black
513   */
514  rb_curses_define_const(COLOR_BLACK);
515}
516    EOF
517
518    @parser = util_parser content
519
520    @parser.do_modules
521    @parser.do_classes
522    @parser.do_constants
523
524    klass = @parser.classes['mCurses']
525
526    constants = klass.constants
527    refute_empty klass.constants
528
529    assert_equal 'COLOR_BLACK', constants.first.name
530    assert_equal 'UINT2NUM(COLOR_BLACK)', constants.first.value
531    assert_equal 'Value of the color black', constants.first.comment.text
532  end
533
534  def test_do_constants_file
535    content = <<-EOF
536void Init_File(void) {
537  rb_cFile = rb_define_class("File", rb_cIO);
538  rb_mFConst = rb_define_module_under(rb_cFile, "Constants");
539  rb_include_module(rb_cIO, rb_mFConst);
540
541  /*  Document-const: LOCK_SH
542   *
543   *  Shared lock
544   */
545  rb_file_const("LOCK_SH", INT2FIX(LOCK_SH));
546}
547    EOF
548
549    @parser = util_parser content
550
551    @parser.do_modules
552    @parser.do_classes
553    @parser.do_constants
554
555    klass = @parser.classes['rb_mFConst']
556
557    constants = klass.constants
558    refute_empty klass.constants
559
560    constant = constants.first
561
562    assert_equal 'LOCK_SH',          constant.name
563    assert_equal 'INT2FIX(LOCK_SH)', constant.value
564    assert_equal 'Shared lock',      constant.comment.text
565  end
566
567  def test_do_includes
568    content = <<-EOF
569Init_foo() {
570   VALUE cFoo = rb_define_class("Foo", rb_cObject);
571   VALUE mInc = rb_define_module("Inc");
572
573   rb_include_module(cFoo, mInc);
574}
575    EOF
576
577    klass = util_get_class content, 'cFoo'
578
579    incl = klass.includes.first
580    assert_equal 'Inc',      incl.name
581    assert_equal '',         incl.comment.text
582    assert_equal @top_level, incl.file
583  end
584
585  # HACK parsing warning instead of setting up in file
586  def test_do_methods_in_c
587    content = <<-EOF
588VALUE blah(VALUE klass, VALUE year) {
589}
590
591void Init_Blah(void) {
592  cDate = rb_define_class("Date", rb_cObject);
593
594  rb_define_method(cDate, "blah", blah, 1); /* in blah.c */
595}
596    EOF
597
598    klass = nil
599
600    _, err = verbose_capture_io do
601      klass = util_get_class content, 'cDate'
602    end
603
604    assert_match ' blah.c ', err
605  end
606
607  # HACK parsing warning instead of setting up in file
608  def test_do_methods_in_cpp
609    content = <<-EOF
610VALUE blah(VALUE klass, VALUE year) {
611}
612
613void Init_Blah(void) {
614  cDate = rb_define_class("Date", rb_cObject);
615
616  rb_define_method(cDate, "blah", blah, 1); /* in blah.cpp */
617}
618    EOF
619
620    klass = nil
621
622    _, err = verbose_capture_io do
623      klass = util_get_class content, 'cDate'
624    end
625
626    assert_match ' blah.cpp ', err
627  end
628
629  # HACK parsing warning instead of setting up in file
630  def test_do_methods_in_y
631    content = <<-EOF
632VALUE blah(VALUE klass, VALUE year) {
633}
634
635void Init_Blah(void) {
636  cDate = rb_define_class("Date", rb_cObject);
637
638  rb_define_method(cDate, "blah", blah, 1); /* in blah.y */
639}
640    EOF
641
642    klass = nil
643
644    _, err = verbose_capture_io do
645      klass = util_get_class content, 'cDate'
646    end
647
648    assert_match ' blah.y ', err
649  end
650
651  def test_do_methods_singleton_class
652    content = <<-EOF
653VALUE blah(VALUE klass, VALUE year) {
654}
655
656void Init_Blah(void) {
657  cDate = rb_define_class("Date", rb_cObject);
658  sDate = rb_singleton_class(cDate);
659
660  rb_define_method(sDate, "blah", blah, 1);
661}
662    EOF
663
664    klass = util_get_class content, 'cDate'
665
666    methods = klass.method_list
667    assert_equal 1,      methods.length
668    assert_equal 'blah', methods.first.name
669    assert               methods.first.singleton
670  end
671
672  def test_do_missing
673    parser = util_parser
674
675    klass_a = @top_level.add_class RDoc::ClassModule, 'A'
676    parser.classes['a'] = klass_a
677
678    parser.enclosure_dependencies['c'] << 'b'
679    parser.enclosure_dependencies['b'] << 'a'
680    parser.enclosure_dependencies['d'] << 'a'
681
682    parser.missing_dependencies['d'] = ['d', :class, 'D', 'Object', 'a']
683    parser.missing_dependencies['c'] = ['c', :class, 'C', 'Object', 'b']
684    parser.missing_dependencies['b'] = ['b', :class, 'B', 'Object', 'a']
685
686    parser.do_missing
687
688    assert_equal %w[A A::B A::B::C A::D],
689                 @store.all_classes_and_modules.map { |m| m.full_name }.sort
690  end
691
692  def test_do_missing_cycle
693    parser = util_parser
694
695    klass_a = @top_level.add_class RDoc::ClassModule, 'A'
696    parser.classes['a'] = klass_a
697
698    parser.enclosure_dependencies['c'] << 'b'
699    parser.enclosure_dependencies['b'] << 'a'
700
701    parser.missing_dependencies['c'] = ['c', :class, 'C', 'Object', 'b']
702    parser.missing_dependencies['b'] = ['b', :class, 'B', 'Object', 'a']
703
704    parser.enclosure_dependencies['y'] << 'z'
705    parser.enclosure_dependencies['z'] << 'y'
706
707    parser.missing_dependencies['y'] = ['y', :class, 'Y', 'Object', 'z']
708    parser.missing_dependencies['z'] = ['z', :class, 'Z', 'Object', 'y']
709
710    _, err = verbose_capture_io do
711      parser.do_missing
712    end
713
714    expected = 'Unable to create class Y (y), class Z (z) ' +
715               'due to a cyclic class or module creation'
716
717    assert_equal expected, err.chomp
718
719    assert_equal %w[A A::B A::B::C],
720                 @store.all_classes_and_modules.map { |m| m.full_name }.sort
721  end
722
723  def test_find_alias_comment
724    parser = util_parser
725
726    comment = parser.find_alias_comment 'C', '[]', 'index'
727
728    assert_equal '', comment.text
729
730    parser = util_parser <<-C
731/*
732 * comment
733 */
734
735rb_define_alias(C, "[]", "index");
736    C
737
738    comment = parser.find_alias_comment 'C', '[]', 'index'
739
740    assert_equal "/*\n * comment\n */\n\n", comment.text
741  end
742
743  def test_find_class_comment
744    @options.rdoc_include << File.dirname(__FILE__)
745
746    content = <<-EOF
747/*
748 * Comment 1
749 */
750foo = rb_define_class("MyClassName1", rb_cObject);
751
752/*
753 * Comment 2
754 */
755bar = rb_define_class("MyClassName2", rb_cObject);
756    EOF
757
758    util_get_class content
759
760    assert_equal "Comment 1", @parser.classes['foo'].comment.text
761    assert_equal "Comment 2", @parser.classes['bar'].comment.text
762  end
763
764  def test_find_class_comment_init
765    content = <<-EOF
766/*
767 * a comment for class Foo
768 */
769void
770Init_Foo(void) {
771  VALUE foo = rb_define_class("Foo", rb_cObject);
772}
773    EOF
774
775    klass = util_get_class content, 'foo'
776
777    assert_equal "a comment for class Foo", klass.comment.text
778  end
779
780  def test_find_class_comment_define_class
781    content = <<-EOF
782/*
783 * a comment for class Foo
784 */
785VALUE foo = rb_define_class("Foo", rb_cObject);
786    EOF
787
788    klass = util_get_class content, 'foo'
789
790    assert_equal "a comment for class Foo", klass.comment.text
791  end
792
793  def test_find_class_comment_define_class_Init_Foo
794    content = <<-EOF
795/*
796 * a comment for class Foo on Init
797 */
798void
799Init_Foo(void) {
800    /*
801     * a comment for class Foo on rb_define_class
802     */
803    VALUE foo = rb_define_class("Foo", rb_cObject);
804}
805    EOF
806
807    klass = util_get_class content, 'foo'
808
809    assert_equal "a comment for class Foo on Init", klass.comment.text
810  end
811
812  def test_find_class_comment_define_class_Init_Foo_no_void
813    content = <<-EOF
814/*
815 * a comment for class Foo on Init
816 */
817void
818Init_Foo() {
819    /*
820     * a comment for class Foo on rb_define_class
821     */
822    VALUE foo = rb_define_class("Foo", rb_cObject);
823}
824    EOF
825
826    klass = util_get_class content, 'foo'
827
828    assert_equal "a comment for class Foo on Init", klass.comment.text
829  end
830
831  def test_find_class_comment_define_class_bogus_comment
832    content = <<-EOF
833/*
834 * a comment for other_function
835 */
836void
837other_function() {
838}
839
840void
841Init_Foo(void) {
842    VALUE foo = rb_define_class("Foo", rb_cObject);
843}
844    EOF
845
846    klass = util_get_class content, 'foo'
847
848    assert_equal '', klass.comment.text
849  end
850
851  def test_find_class_comment_define_class_under
852    content = <<-EOF
853/*
854 * a comment for class Foo
855 */
856VALUE foo = rb_define_class_under(rb_cObject, "Foo", rb_cObject);
857    EOF
858
859    klass = util_get_class content, 'foo'
860
861    assert_equal "a comment for class Foo", klass.comment.text
862  end
863
864  def test_find_class_comment_define_class_under_Init
865    content = <<-EOF
866/*
867 * a comment for class Foo on Init
868 */
869void
870Init_Foo(void) {
871    /*
872     * a comment for class Foo on rb_define_class
873     */
874    VALUE foo = rb_define_class_under(rb_cObject, "Foo", rb_cObject);
875}
876    EOF
877
878    klass = util_get_class content, 'foo'
879
880    # the inner comment is used since Object::Foo is not necessarily the same
881    # thing as "Foo" for Init_
882    assert_equal "a comment for class Foo on rb_define_class",
883                 klass.comment.text
884  end
885
886  def test_find_const_comment_rb_define
887    content = <<-EOF
888/*
889 * A comment
890 */
891rb_define_const(cFoo, "CONST", value);
892    EOF
893
894    parser = util_parser content
895
896    comment = parser.find_const_comment 'const', 'CONST'
897
898    assert_equal "/*\n * A comment\n */\n", comment.text
899  end
900
901  def test_find_const_comment_document_const
902    content = <<-EOF
903/*
904 * Document-const: CONST
905 *
906 * A comment
907 */
908    EOF
909
910    parser = util_parser content
911
912    comment = parser.find_const_comment nil, 'CONST'
913
914    assert_equal "/*\n *\n * A comment\n */", comment.text
915  end
916
917  def test_find_const_comment_document_const_full_name
918    content = <<-EOF
919/*
920 * Document-const: Foo::CONST
921 *
922 * A comment
923 */
924    EOF
925
926    parser = util_parser content
927
928    comment = parser.find_const_comment nil, 'CONST', 'Foo'
929
930    assert_equal "/*\n *\n * A comment\n */", comment.text
931  end
932
933  def test_find_body
934    content = <<-EOF
935/*
936 * a comment for other_function
937 */
938VALUE
939other_function() {
940}
941
942void
943Init_Foo(void) {
944    VALUE foo = rb_define_class("Foo", rb_cObject);
945
946    rb_define_method(foo, "my_method", other_function, 0);
947}
948    EOF
949
950    klass = util_get_class content, 'foo'
951    other_function = klass.method_list.first
952
953    assert_equal 'my_method', other_function.name
954    assert_equal "a comment for other_function",
955                 other_function.comment.text
956    assert_equal '()', other_function.params
957
958    code = other_function.token_stream.first.text
959
960    assert_equal "VALUE\nother_function() {\n}", code
961  end
962
963  def test_find_body_2
964    content = <<-CONTENT
965/* Copyright (C) 2010  Sven Herzberg
966 *
967 * This file is free software; the author(s) gives unlimited
968 * permission to copy and/or distribute it, with or without
969 * modifications, as long as this notice is preserved.
970 */
971
972#include <ruby.h>
973
974static VALUE
975wrap_initialize (VALUE  self)
976{
977  return self;
978}
979
980/* */
981static VALUE
982wrap_shift (VALUE self,
983            VALUE arg)
984{
985  return self;
986}
987
988void
989init_gi_repository (void)
990{
991  VALUE mTest = rb_define_module ("Test");
992  VALUE cTest = rb_define_class_under (mTest, "Test", rb_cObject);
993
994  rb_define_method (cTest, "initialize", wrap_initialize, 0);
995  rb_define_method (cTest, "shift", wrap_shift, 0);
996}
997    CONTENT
998
999    klass = util_get_class content, 'cTest'
1000    assert_equal 2, klass.method_list.length
1001  end
1002
1003  def test_find_body_define
1004    content = <<-EOF
1005#define something something_else
1006
1007#define other_function rb_other_function
1008
1009/*
1010 * a comment for rb_other_function
1011 */
1012VALUE
1013rb_other_function() {
1014}
1015
1016void
1017Init_Foo(void) {
1018    VALUE foo = rb_define_class("Foo", rb_cObject);
1019
1020    rb_define_method(foo, "my_method", other_function, 0);
1021}
1022    EOF
1023
1024    klass = util_get_class content, 'foo'
1025    other_function = klass.method_list.first
1026
1027    assert_equal 'my_method', other_function.name
1028    assert_equal 'a comment for rb_other_function', other_function.comment.text
1029    assert_equal '()', other_function.params
1030    assert_equal 118, other_function.offset
1031    assert_equal 8, other_function.line
1032
1033    code = other_function.token_stream.first.text
1034
1035    assert_equal "VALUE\nrb_other_function() {\n}", code
1036  end
1037
1038  def test_find_body_define_comment
1039    content = <<-EOF
1040/*
1041 * a comment for other_function
1042 */
1043#define other_function rb_other_function
1044
1045/* */
1046VALUE
1047rb_other_function() {
1048}
1049
1050void
1051Init_Foo(void) {
1052    VALUE foo = rb_define_class("Foo", rb_cObject);
1053
1054    rb_define_method(foo, "my_method", other_function, 0);
1055}
1056    EOF
1057
1058    klass = util_get_class content, 'foo'
1059    other_function = klass.method_list.first
1060
1061    assert_equal 'my_method', other_function.name
1062    assert_equal 'a comment for other_function', other_function.comment.text
1063    assert_equal '()', other_function.params
1064    assert_equal 39, other_function.offset
1065    assert_equal 4, other_function.line
1066
1067    code = other_function.token_stream.first.text
1068
1069    assert_equal "#define other_function rb_other_function", code
1070  end
1071
1072  def test_find_body_document_method
1073    content = <<-EOF
1074/*
1075 * Document-method: bar
1076 * Document-method: baz
1077 *
1078 * a comment for bar
1079 */
1080VALUE
1081bar() {
1082}
1083
1084void
1085Init_Foo(void) {
1086    VALUE foo = rb_define_class("Foo", rb_cObject);
1087
1088    rb_define_method(foo, "bar", bar, 0);
1089    rb_define_method(foo, "baz", bar, 0);
1090}
1091    EOF
1092
1093    klass = util_get_class content, 'foo'
1094    assert_equal 2, klass.method_list.length
1095
1096    methods = klass.method_list.sort
1097
1098    bar = methods.first
1099    assert_equal 'Foo#bar', bar.full_name
1100    assert_equal "a comment for bar", bar.comment.text
1101
1102    baz = methods.last
1103    assert_equal 'Foo#baz', baz.full_name
1104    assert_equal "a comment for bar", baz.comment.text
1105  end
1106
1107  def test_find_body_document_method_equals
1108    content = <<-EOF
1109/*
1110 * Document-method: Zlib::GzipFile#mtime=
1111 *
1112 * A comment
1113 */
1114static VALUE
1115rb_gzfile_set_mtime(VALUE obj, VALUE mtime)
1116{
1117
1118void
1119Init_zlib() {
1120    mZlib = rb_define_module("Zlib");
1121    cGzipFile = rb_define_class_under(mZlib, "GzipFile", rb_cObject);
1122    cGzipWriter = rb_define_class_under(mZlib, "GzipWriter", cGzipFile);
1123    rb_define_method(cGzipWriter, "mtime=", rb_gzfile_set_mtime, 1);
1124}
1125    EOF
1126
1127    klass = util_get_class content, 'cGzipWriter'
1128    assert_equal 1, klass.method_list.length
1129
1130    methods = klass.method_list.sort
1131
1132    bar = methods.first
1133    assert_equal 'Zlib::GzipWriter#mtime=', bar.full_name
1134    assert_equal 'A comment', bar.comment.text
1135  end
1136
1137  def test_find_body_document_method_same
1138    content = <<-EOF
1139VALUE
1140s_bar() {
1141}
1142
1143VALUE
1144bar() {
1145}
1146
1147/*
1148 * Document-method: Foo::bar
1149 *
1150 * a comment for Foo::bar
1151 */
1152
1153/*
1154 * Document-method: Foo#bar
1155 *
1156 * a comment for Foo#bar
1157 */
1158
1159void
1160Init_Foo(void) {
1161    VALUE foo = rb_define_class("Foo", rb_cObject);
1162
1163    rb_define_singleton_method(foo, "bar", s_bar, 0);
1164    rb_define_method(foo, "bar", bar, 0);
1165}
1166    EOF
1167
1168    klass = util_get_class content, 'foo'
1169    assert_equal 2, klass.method_list.length
1170
1171    methods = klass.method_list.sort
1172
1173    s_bar = methods.first
1174    assert_equal 'Foo::bar', s_bar.full_name
1175    assert_equal "a comment for Foo::bar", s_bar.comment.text
1176
1177    bar = methods.last
1178    assert_equal 'Foo#bar', bar.full_name
1179    assert_equal "a comment for Foo#bar", bar.comment.text
1180  end
1181
1182  def test_find_modifiers_call_seq
1183    comment = RDoc::Comment.new <<-COMMENT
1184call-seq:
1185  commercial() -> Date <br />
1186
1187If no arguments are given:
1188
1189    COMMENT
1190
1191    parser = util_parser
1192    method_obj = RDoc::AnyMethod.new nil, 'blah'
1193
1194    parser.find_modifiers comment, method_obj
1195
1196    expected = <<-CALL_SEQ.chomp
1197commercial() -> Date <br />
1198
1199    CALL_SEQ
1200
1201    assert_equal expected, method_obj.call_seq
1202  end
1203
1204  def test_find_modifiers_nodoc
1205    comment = RDoc::Comment.new <<-COMMENT
1206/* :nodoc:
1207 *
1208 * Blah
1209 */
1210
1211    COMMENT
1212
1213    parser = util_parser
1214    method_obj = RDoc::AnyMethod.new nil, 'blah'
1215
1216    parser.find_modifiers comment, method_obj
1217
1218    assert_equal nil, method_obj.document_self
1219  end
1220
1221  def test_find_modifiers_yields
1222    comment = RDoc::Comment.new <<-COMMENT
1223/* :yields: a, b
1224 *
1225 * Blah
1226 */
1227
1228    COMMENT
1229
1230    parser = util_parser
1231    method_obj = RDoc::AnyMethod.new nil, 'blah'
1232
1233    parser.find_modifiers comment, method_obj
1234
1235    assert_equal 'a, b', method_obj.block_params
1236
1237    assert_equal "\n\nBlah", comment.text
1238  end
1239
1240  def test_handle_method_args_minus_1
1241    parser = util_parser "Document-method: Object#m\n blah */"
1242
1243    parser.content = <<-BODY
1244VALUE
1245rb_other(VALUE obj) {
1246  rb_funcall(obj, rb_intern("other"), 0);
1247  return rb_str_new2("blah, blah, blah");
1248}
1249
1250VALUE
1251rb_m(int argc, VALUE *argv, VALUE obj) {
1252  VALUE o1, o2;
1253  rb_scan_args(argc, argv, "1", &o1, &o2);
1254}
1255    BODY
1256
1257    parser.handle_method 'method', 'rb_cObject', 'm', 'rb_m', -1
1258
1259    m = @top_level.find_module_named('Object').method_list.first
1260
1261    assert_equal 'm', m.name
1262    assert_equal @top_level, m.file
1263    assert_equal 115, m.offset
1264    assert_equal 7, m.line
1265
1266    assert_equal '(p1)', m.params
1267  end
1268
1269  def test_handle_method_args_0
1270    parser = util_parser "Document-method: BasicObject#==\n blah */"
1271
1272    parser.handle_method 'method', 'rb_cBasicObject', '==', 'rb_obj_equal', 0
1273
1274    bo = @top_level.find_module_named 'BasicObject'
1275
1276    assert_equal 1, bo.method_list.length
1277
1278    equals2 = bo.method_list.first
1279
1280    assert_equal '()', equals2.params
1281  end
1282
1283  def test_handle_method_args_1
1284    parser = util_parser "Document-method: BasicObject#==\n blah */"
1285
1286    parser.handle_method 'method', 'rb_cBasicObject', '==', 'rb_obj_equal', 1
1287
1288    bo = @top_level.find_module_named 'BasicObject'
1289
1290    assert_equal 1, bo.method_list.length
1291
1292    equals2 = bo.method_list.first
1293
1294    assert_equal '(p1)', equals2.params
1295  end
1296
1297  def test_handle_method_args_2
1298    parser = util_parser "Document-method: BasicObject#==\n blah */"
1299
1300    parser.handle_method 'method', 'rb_cBasicObject', '==', 'rb_obj_equal', 2
1301
1302    bo = @top_level.find_module_named 'BasicObject'
1303
1304    assert_equal 1, bo.method_list.length
1305
1306    equals2 = bo.method_list.first
1307
1308    assert_equal '(p1, p2)', equals2.params
1309  end
1310
1311  # test_handle_args_minus_1 handled by test_handle_method
1312
1313  def test_handle_method_args_minus_2
1314    parser = util_parser "Document-method: BasicObject#==\n blah */"
1315
1316    parser.handle_method 'method', 'rb_cBasicObject', '==', 'rb_obj_equal', -2
1317
1318    bo = @top_level.find_module_named 'BasicObject'
1319
1320    assert_equal 1, bo.method_list.length
1321
1322    equals2 = bo.method_list.first
1323
1324    assert_equal '(*args)', equals2.params
1325  end
1326
1327  def test_handle_method_initialize
1328    parser = util_parser "Document-method: BasicObject::new\n blah */"
1329
1330    parser.handle_method('private_method', 'rb_cBasicObject',
1331                         'initialize', 'rb_obj_dummy', -1)
1332
1333    bo = @top_level.find_module_named 'BasicObject'
1334
1335    assert_equal 1, bo.method_list.length
1336
1337    new = bo.method_list.first
1338
1339    assert_equal 'new',   new.name
1340    assert_equal :public, new.visibility
1341  end
1342
1343  def test_handle_singleton
1344    parser = util_parser <<-SINGLE
1345void Init_Blah(void) {
1346  cDate = rb_define_class("Date", rb_cObject);
1347  sDate = rb_singleton_class(cDate);
1348}
1349    SINGLE
1350
1351    parser.scan
1352
1353    assert_equal 'Date', parser.known_classes['sDate']
1354    assert_equal 'Date', parser.singleton_classes['sDate']
1355  end
1356
1357  def test_look_for_directives_in
1358    parser = util_parser
1359
1360    comment = RDoc::Comment.new "# :other: not_handled\n"
1361
1362    parser.look_for_directives_in @top_level, comment
1363
1364    assert_equal "# :other: not_handled\n", comment.text
1365    assert_equal 'not_handled', @top_level.metadata['other']
1366  end
1367
1368  def test_load_variable_map
1369    some_ext = @top_level.add_class RDoc::NormalClass, 'SomeExt'
1370               @top_level.add_class RDoc::NormalClass, 'OtherExt'
1371
1372    @store.cache[:c_class_variables][@fn]       = { 'cSomeExt'  => 'SomeExt'  }
1373    @store.cache[:c_class_variables]['other.c'] = { 'cOtherExt' => 'OtherExt' }
1374
1375    parser = util_parser
1376
1377    map = parser.load_variable_map :c_class_variables
1378
1379    expected = { 'cSomeExt' => some_ext }
1380
1381    assert_equal expected, map
1382
1383    assert_equal 'SomeExt', parser.known_classes['cSomeExt']
1384    assert_nil              parser.known_classes['cOtherExt']
1385  end
1386
1387  def test_load_variable_map_empty
1388    parser = util_parser
1389
1390    map = parser.load_variable_map :c_class_variables
1391
1392    assert_empty map
1393  end
1394
1395  def test_load_variable_map_legacy
1396    @store.cache[:c_class_variables] = nil
1397
1398    parser = util_parser
1399
1400    map = parser.load_variable_map :c_class_variables
1401
1402    assert_empty map
1403  end
1404
1405  def test_load_variable_map_singleton
1406    @top_level.add_class RDoc::NormalClass, 'SomeExt'
1407    @top_level.add_class RDoc::NormalClass, 'OtherExt'
1408
1409    @store.cache[:c_singleton_class_variables][@fn] =
1410      { 'cSomeExt'  => 'SomeExt'  }
1411    @store.cache[:c_singleton_class_variables]['other.c'] =
1412      { 'cOtherExt' => 'OtherExt' }
1413
1414    parser = util_parser
1415
1416    map = parser.load_variable_map :c_singleton_class_variables
1417
1418    expected = { 'cSomeExt' => 'SomeExt' }
1419
1420    assert_equal expected, map
1421
1422    assert_equal 'SomeExt', parser.known_classes['cSomeExt']
1423    assert_nil              parser.known_classes['cOtherExt']
1424  end
1425
1426  def test_load_variable_map_trim
1427    a = @top_level.add_class RDoc::NormalClass, 'A'
1428
1429    @store.cache[:c_class_variables][@fn] = {
1430      'cA'  => 'A',
1431      'cB'  => 'B',
1432    }
1433
1434    parser = util_parser
1435
1436    map = parser.load_variable_map :c_class_variables
1437
1438    expected = { 'cA' => a }
1439
1440    assert_equal expected, map
1441  end
1442
1443  def test_define_method
1444    content = <<-EOF
1445/*Method Comment! */
1446static VALUE
1447rb_io_s_read(argc, argv, io)
1448    int argc;
1449    VALUE *argv;
1450    VALUE io;
1451{
1452}
1453
1454void
1455Init_IO(void) {
1456    /*
1457     * a comment for class Foo on rb_define_class
1458     */
1459    VALUE rb_cIO = rb_define_class("IO", rb_cObject);
1460    rb_define_singleton_method(rb_cIO, "read", rb_io_s_read, -1);
1461}
1462    EOF
1463
1464    klass = util_get_class content, 'rb_cIO'
1465    read_method = klass.method_list.first
1466    assert_equal "read", read_method.name
1467    assert_equal "Method Comment!   ", read_method.comment.text
1468    assert_equal "rb_io_s_read", read_method.c_function
1469    assert read_method.singleton
1470  end
1471
1472  def test_define_method_with_prototype
1473    content = <<-EOF
1474static VALUE rb_io_s_read(int, VALUE*, VALUE);
1475
1476/* Method Comment! */
1477static VALUE
1478rb_io_s_read(argc, argv, io)
1479    int argc;
1480    VALUE *argv;
1481    VALUE io;
1482{
1483}
1484
1485void
1486Init_IO(void) {
1487    /*
1488     * a comment for class Foo on rb_define_class
1489     */
1490    VALUE rb_cIO = rb_define_class("IO", rb_cObject);
1491    rb_define_singleton_method(rb_cIO, "read", rb_io_s_read, -1);
1492}
1493    EOF
1494
1495    klass = util_get_class content, 'rb_cIO'
1496    read_method = klass.method_list.first
1497    assert_equal "read", read_method.name
1498    assert_equal "Method Comment!   ", read_method.comment.text
1499    assert_equal "rb_io_s_read", read_method.c_function
1500    assert read_method.singleton
1501  end
1502
1503  def test_define_method_private
1504    content = <<-EOF
1505/*Method Comment! */
1506static VALUE
1507rb_io_s_read(argc, argv, io)
1508    int argc;
1509    VALUE *argv;
1510    VALUE io;
1511{
1512}
1513
1514void
1515Init_IO(void) {
1516    /*
1517     * a comment for class Foo on rb_define_class
1518     */
1519    VALUE rb_cIO = rb_define_class("IO", rb_cObject);
1520    rb_define_private_method(rb_cIO, "read", rb_io_s_read, -1);
1521}
1522    EOF
1523
1524    klass = util_get_class content, 'rb_cIO'
1525    read_method = klass.method_list.first
1526    assert_equal 'IO#read', read_method.full_name
1527    assert_equal :private, read_method.visibility
1528    assert_equal "Method Comment!   ", read_method.comment.text
1529  end
1530
1531  def test_define_method_private_singleton
1532    content = <<-EOF
1533/*Method Comment! */
1534static VALUE
1535rb_io_s_read(argc, argv, io)
1536    int argc;
1537    VALUE *argv;
1538    VALUE io;
1539{
1540}
1541
1542void
1543Init_IO(void) {
1544    /*
1545     * a comment for class Foo on rb_define_class
1546     */
1547    VALUE rb_cIO = rb_define_class("IO", rb_cObject);
1548    VALUE rb_cIO_s = rb_singleton_class(rb_cIO);
1549    rb_define_private_method(rb_cIO_s, "read", rb_io_s_read, -1);
1550}
1551    EOF
1552
1553    klass = util_get_class content, 'rb_cIO'
1554    read_method = klass.method_list.first
1555    assert_equal "read", read_method.name
1556    assert_equal "Method Comment!   ", read_method.comment.text
1557    assert_equal :private, read_method.visibility
1558    assert read_method.singleton
1559  end
1560
1561  def test_define_method_singleton
1562    content = <<-EOF
1563/*Method Comment! */
1564static VALUE
1565rb_io_s_read(argc, argv, io)
1566    int argc;
1567    VALUE *argv;
1568    VALUE io;
1569{
1570}
1571
1572void
1573Init_IO(void) {
1574    /*
1575     * a comment for class Foo on rb_define_class
1576     */
1577    VALUE rb_cIO = rb_define_class("IO", rb_cObject);
1578    VALUE rb_cIO_s = rb_singleton_class(rb_cIO);
1579    rb_define_method(rb_cIO_s, "read", rb_io_s_read, -1);
1580}
1581    EOF
1582
1583    klass = util_get_class content, 'rb_cIO'
1584    read_method = klass.method_list.first
1585    assert_equal "read", read_method.name
1586    assert_equal "Method Comment!   ", read_method.comment.text
1587    assert read_method.singleton
1588  end
1589
1590  def test_rb_scan_args
1591    parser = util_parser
1592
1593    assert_equal '(p1)',
1594                 parser.rb_scan_args('rb_scan_args(a, b, "1",)')
1595    assert_equal '(p1, p2)',
1596                 parser.rb_scan_args('rb_scan_args(a, b, "2",)')
1597
1598    assert_equal '(p1 = v1)',
1599                 parser.rb_scan_args('rb_scan_args(a, b, "01",)')
1600    assert_equal '(p1 = v1, p2 = v2)',
1601                 parser.rb_scan_args('rb_scan_args(a, b, "02",)')
1602
1603    assert_equal '(p1, p2 = v2)',
1604                 parser.rb_scan_args('rb_scan_args(a, b, "11",)')
1605
1606    assert_equal '(p1, *args)',
1607                 parser.rb_scan_args('rb_scan_args(a, b, "1*",)')
1608    assert_equal '(p1, p2 = {})',
1609                 parser.rb_scan_args('rb_scan_args(a, b, "1:",)')
1610    assert_equal '(p1, &block)',
1611                 parser.rb_scan_args('rb_scan_args(a, b, "1&",)')
1612
1613    assert_equal '(p1, p2)',
1614                 parser.rb_scan_args('rb_scan_args(a, b, "101",)')
1615
1616    assert_equal '(p1, p2 = v2, p3)',
1617                 parser.rb_scan_args('rb_scan_args(a, b, "111",)')
1618
1619    assert_equal '(p1, *args, p3)',
1620                 parser.rb_scan_args('rb_scan_args(a, b, "1*1",)')
1621
1622    assert_equal '(p1, p2 = v2, *args)',
1623                 parser.rb_scan_args('rb_scan_args(a, b, "11*",)')
1624    assert_equal '(p1, p2 = v2, p3 = {})',
1625                 parser.rb_scan_args('rb_scan_args(a, b, "11:",)')
1626    assert_equal '(p1, p2 = v2, &block)',
1627                 parser.rb_scan_args('rb_scan_args(a, b, "11&",)')
1628
1629    assert_equal '(p1, p2 = v2, *args, p4, p5 = {}, &block)',
1630                 parser.rb_scan_args('rb_scan_args(a, b, "11*1:&",)')
1631
1632    # The following aren't valid according to spec but are according to the
1633    # implementation.
1634    assert_equal '(*args)',
1635                 parser.rb_scan_args('rb_scan_args(a, b, "*",)')
1636    assert_equal '(p1 = {})',
1637                 parser.rb_scan_args('rb_scan_args(a, b, ":",)')
1638    assert_equal '(&block)',
1639                 parser.rb_scan_args('rb_scan_args(a, b, "&",)')
1640
1641    assert_equal '(*args, p2 = {})',
1642                 parser.rb_scan_args('rb_scan_args(a, b, "*:",)')
1643    assert_equal '(p1 = {}, &block)',
1644                 parser.rb_scan_args('rb_scan_args(a, b, ":&",)')
1645    assert_equal '(*args, p2 = {}, &block)',
1646                 parser.rb_scan_args('rb_scan_args(a, b, "*:&",)')
1647  end
1648
1649  def test_scan
1650    parser = util_parser <<-C
1651void Init(void) {
1652    mM = rb_define_module("M");
1653    cC = rb_define_class("C", rb_cObject);
1654    sC = rb_singleton_class(cC);
1655}
1656    C
1657
1658    parser.scan
1659
1660    expected = {
1661      @fn => {
1662        'mM' => 'M',
1663        'cC' => 'C', }}
1664    assert_equal expected, @store.c_class_variables
1665
1666    expected = {
1667      @fn => {
1668        'sC' => 'C' } }
1669    assert_equal expected, @store.c_singleton_class_variables
1670  end
1671
1672  def test_scan_order_dependent
1673    parser = util_parser <<-C
1674void a(void) {
1675    mA = rb_define_module("A");
1676}
1677
1678void b(void) {
1679    cB = rb_define_class_under(mA, "B", rb_cObject);
1680}
1681
1682void c(void) {
1683    mC = rb_define_module_under(cB, "C");
1684}
1685
1686void d(void) {
1687    mD = rb_define_class_under(mC, "D");
1688}
1689    C
1690
1691    parser.scan
1692
1693    assert_equal %w[A A::B A::B::C],
1694                 @store.all_classes_and_modules.map { |m| m.full_name }.sort
1695  end
1696
1697  def util_get_class content, name = nil
1698    @parser = util_parser content
1699    @parser.scan
1700
1701    @parser.classes[name] if name
1702  end
1703
1704  def util_parser content = ''
1705    RDoc::Parser::C.new @top_level, @fn, content, @options, @stats
1706  end
1707
1708end
1709
1710