ruby/test/rdoc/test_rdoc_parser_c.rb

913 строки
21 KiB
Ruby

require 'stringio'
require 'tempfile'
require 'rubygems'
require 'minitest/autorun'
require 'rdoc/options'
require 'rdoc/parser/c'
=begin
TODO: test call-seq parsing
/*
* call-seq:
* ARGF.readlines(sep=$/) -> array
* ARGF.readlines(limit) -> array
* ARGF.readlines(sep, limit) -> array
*
* ARGF.to_a(sep=$/) -> array
* ARGF.to_a(limit) -> array
* ARGF.to_a(sep, limit) -> array
*
* Reads +ARGF+'s current file in its entirety, returning an +Array+ of its
* lines, one line per element. Lines are assumed to be separated by _sep_.
*
* lines = ARGF.readlines
* lines[0] #=> "This is line one\n"
*/
assert call-seq did not stop at first empty line
/*
* call-seq:
*
* flt ** other -> float
*
* Raises <code>float</code> the <code>other</code> power.
*
* 2.0**3 #=> 8.0
*/
assert call-seq correct (bug: was empty)
/* call-seq: flt ** other -> float */
assert call-seq correct
=end
class RDoc::Parser::C
attr_accessor :classes, :singleton_classes
public :do_classes, :do_constants
end
class TestRDocParserC < MiniTest::Unit::TestCase
def setup
@tempfile = Tempfile.new self.class.name
filename = @tempfile.path
@top_level = RDoc::TopLevel.new filename
@fn = filename
@options = RDoc::Options.new
@stats = RDoc::Stats.new 0
RDoc::Parser::C.reset
RDoc::TopLevel.reset
end
def teardown
@tempfile.close
end
def test_do_attr_rb_attr
content = <<-EOF
void Init_Blah(void) {
cBlah = rb_define_class("Blah", rb_cObject);
/*
* This is an accessor
*/
rb_attr(cBlah, rb_intern("accessor"), 1, 1, Qfalse);
/*
* This is a reader
*/
rb_attr(cBlah, rb_intern("reader"), 1, 0, Qfalse);
/*
* This is a writer
*/
rb_attr(cBlah, rb_intern("writer"), 0, 1, Qfalse);
}
EOF
klass = util_get_class content, 'cBlah'
attrs = klass.attributes
assert_equal 3, attrs.length, attrs.inspect
accessor = attrs.shift
assert_equal 'accessor', accessor.name
assert_equal 'RW', accessor.rw
assert_equal 'This is an accessor', accessor.comment
reader = attrs.shift
assert_equal 'reader', reader.name
assert_equal 'R', reader.rw
assert_equal 'This is a reader', reader.comment
writer = attrs.shift
assert_equal 'writer', writer.name
assert_equal 'W', writer.rw
assert_equal 'This is a writer', writer.comment
end
def test_do_attr_rb_define_attr
content = <<-EOF
void Init_Blah(void) {
cBlah = rb_define_class("Blah", rb_cObject);
/*
* This is an accessor
*/
rb_define_attr(cBlah, "accessor", 1, 1);
}
EOF
klass = util_get_class content, 'cBlah'
attrs = klass.attributes
assert_equal 1, attrs.length, attrs.inspect
accessor = attrs.shift
assert_equal 'accessor', accessor.name
assert_equal 'RW', accessor.rw
assert_equal 'This is an accessor', accessor.comment
end
def test_do_aliases
content = <<-EOF
/*
* This should show up as an alias with documentation
*/
VALUE blah(VALUE klass, VALUE year) {
}
void Init_Blah(void) {
cDate = rb_define_class("Date", rb_cObject);
rb_define_method(cDate, "blah", blah, 1);
rb_define_alias(cDate, "bleh", "blah");
}
EOF
klass = util_get_class content, 'cDate'
methods = klass.method_list
assert_equal 2, methods.length
assert_equal 'bleh', methods.last.name
assert_equal 'blah', methods.last.is_alias_for.name
end
def test_do_aliases_singleton
content = <<-EOF
/*
* This should show up as a method with documentation
*/
VALUE blah(VALUE klass, VALUE year) {
}
void Init_Blah(void) {
cDate = rb_define_class("Date", rb_cObject);
sDate = rb_singleton_class(cDate);
rb_define_method(sDate, "blah", blah, 1);
/*
* This should show up as an alias
*/
rb_define_alias(sDate, "bleh", "blah");
}
EOF
klass = util_get_class content, 'cDate'
methods = klass.method_list
assert_equal 2, methods.length
assert_equal 'bleh', methods.last.name
assert methods.last.singleton
assert_equal 'blah', methods.last.is_alias_for.name
assert_equal 'This should show up as an alias', methods.last.comment
end
def test_do_classes_boot_class
content = <<-EOF
/* Document-class: Foo
* this is the Foo boot class
*/
VALUE cFoo = boot_defclass("Foo", rb_cObject);
EOF
klass = util_get_class content, 'cFoo'
assert_equal "this is the Foo boot class", klass.comment
assert_equal 'Object', klass.superclass
end
def test_do_classes_boot_class_nil
content = <<-EOF
/* Document-class: Foo
* this is the Foo boot class
*/
VALUE cFoo = boot_defclass("Foo", 0);
EOF
klass = util_get_class content, 'cFoo'
assert_equal "this is the Foo boot class", klass.comment
assert_equal nil, klass.superclass
end
def test_do_classes_class
content = <<-EOF
/* Document-class: Foo
* this is the Foo class
*/
VALUE cFoo = rb_define_class("Foo", rb_cObject);
EOF
klass = util_get_class content, 'cFoo'
assert_equal "this is the Foo class", klass.comment
end
def test_do_classes_singleton
content = <<-EOF
VALUE cFoo = rb_define_class("Foo", rb_cObject);
VALUE cFooS = rb_singleton_class(cFoo);
EOF
util_get_class content, 'cFooS'
assert_equal 'Foo', @parser.singleton_classes['cFooS']
end
def test_do_classes_class_under
content = <<-EOF
/* Document-class: Kernel::Foo
* this is the Foo class under Kernel
*/
VALUE cFoo = rb_define_class_under(rb_mKernel, "Foo", rb_cObject);
EOF
klass = util_get_class content, 'cFoo'
assert_equal "this is the Foo class under Kernel", klass.comment
end
def test_do_classes_module
content = <<-EOF
/* Document-module: Foo
* this is the Foo module
*/
VALUE mFoo = rb_define_module("Foo");
EOF
klass = util_get_class content, 'mFoo'
assert_equal "this is the Foo module", klass.comment
end
def test_do_classes_module_under
content = <<-EOF
/* Document-module: Kernel::Foo
* this is the Foo module under Kernel
*/
VALUE mFoo = rb_define_module_under(rb_mKernel, "Foo");
EOF
klass = util_get_class content, 'mFoo'
assert_equal "this is the Foo module under Kernel", klass.comment
end
def test_do_constants
content = <<-EOF
#include <ruby.h>
void Init_foo(){
VALUE cFoo = rb_define_class("Foo", rb_cObject);
/* 300: The highest possible score in bowling */
rb_define_const(cFoo, "PERFECT", INT2FIX(300));
/* Huzzah!: What you cheer when you roll a perfect game */
rb_define_const(cFoo, "CHEER", rb_str_new2("Huzzah!"));
/* TEST\:TEST: Checking to see if escaped colon works */
rb_define_const(cFoo, "TEST", rb_str_new2("TEST:TEST"));
/* \\: The file separator on MS Windows */
rb_define_const(cFoo, "MSEPARATOR", rb_str_new2("\\"));
/* /: The file separator on Unix */
rb_define_const(cFoo, "SEPARATOR", rb_str_new2("/"));
/* C:\\Program Files\\Stuff: A directory on MS Windows */
rb_define_const(cFoo, "STUFF", rb_str_new2("C:\\Program Files\\Stuff"));
/* Default definition */
rb_define_const(cFoo, "NOSEMI", INT2FIX(99));
rb_define_const(cFoo, "NOCOMMENT", rb_str_new2("No comment"));
/*
* Multiline comment goes here because this comment spans multiple lines.
* Multiline comment goes here because this comment spans multiple lines.
*/
rb_define_const(cFoo, "MULTILINE", INT2FIX(1));
/*
* 1: Multiline comment goes here because this comment spans multiple lines.
* Multiline comment goes here because this comment spans multiple lines.
*/
rb_define_const(cFoo, "MULTILINE_VALUE", INT2FIX(1));
/* Multiline comment goes here because this comment spans multiple lines.
* Multiline comment goes here because this comment spans multiple lines.
*/
rb_define_const(cFoo, "MULTILINE_NOT_EMPTY", INT2FIX(1));
}
EOF
@parser = util_parser content
@parser.do_classes
@parser.do_constants
klass = @parser.classes['cFoo']
assert klass
constants = klass.constants
assert !klass.constants.empty?
constants = constants.map { |c| [c.name, c.value, c.comment] }
assert_equal ['PERFECT', '300', 'The highest possible score in bowling '],
constants.shift
assert_equal ['CHEER', 'Huzzah!',
'What you cheer when you roll a perfect game '],
constants.shift
assert_equal ['TEST', 'TEST:TEST',
'Checking to see if escaped colon works '],
constants.shift
assert_equal ['MSEPARATOR', '\\',
'The file separator on MS Windows '],
constants.shift
assert_equal ['SEPARATOR', '/',
'The file separator on Unix '],
constants.shift
assert_equal ['STUFF', 'C:\\Program Files\\Stuff',
'A directory on MS Windows '],
constants.shift
assert_equal ['NOSEMI', 'INT2FIX(99)',
'Default definition '],
constants.shift
assert_equal ['NOCOMMENT', 'rb_str_new2("No comment")', ''],
constants.shift
comment = <<-EOF.chomp
Multiline comment goes here because this comment spans multiple lines.
Multiline comment goes here because this comment spans multiple lines.
EOF
assert_equal ['MULTILINE', 'INT2FIX(1)', comment], constants.shift
assert_equal ['MULTILINE_VALUE', '1', comment], constants.shift
assert_equal ['MULTILINE_NOT_EMPTY', 'INT2FIX(1)', comment], constants.shift
assert constants.empty?, constants.inspect
end
def test_find_alias_comment
parser = util_parser ''
comment = parser.find_alias_comment 'C', '[]', 'index'
assert_equal '', comment
parser = util_parser <<-C
/*
* comment
*/
rb_define_alias(C, "[]", "index");
C
comment = parser.find_alias_comment 'C', '[]', 'index'
assert_equal "/*\n * comment\n */\n\n", comment
end
def test_find_class_comment_include
@options.rdoc_include << File.dirname(__FILE__)
content = <<-EOF
/*
* a comment for class Foo
*
* :include: test.txt
*/
void
Init_Foo(void) {
VALUE foo = rb_define_class("Foo", rb_cObject);
}
EOF
klass = util_get_class content, 'foo'
assert_equal "a comment for class Foo\n\ntest file", klass.comment
end
def test_find_class_comment_init
content = <<-EOF
/*
* a comment for class Foo
*/
void
Init_Foo(void) {
VALUE foo = rb_define_class("Foo", rb_cObject);
}
EOF
klass = util_get_class content, 'foo'
assert_equal "a comment for class Foo", klass.comment
end
def test_find_class_comment_define_class
content = <<-EOF
/*
* a comment for class Foo
*/
VALUE foo = rb_define_class("Foo", rb_cObject);
EOF
klass = util_get_class content, 'foo'
assert_equal "a comment for class Foo", klass.comment
end
def test_find_class_comment_define_class_Init_Foo
content = <<-EOF
/*
* a comment for class Foo on Init
*/
void
Init_Foo(void) {
/*
* a comment for class Foo on rb_define_class
*/
VALUE foo = rb_define_class("Foo", rb_cObject);
}
EOF
klass = util_get_class content, 'foo'
assert_equal "a comment for class Foo on Init", klass.comment
end
def test_find_class_comment_define_class_Init_Foo_no_void
content = <<-EOF
/*
* a comment for class Foo on Init
*/
void
Init_Foo() {
/*
* a comment for class Foo on rb_define_class
*/
VALUE foo = rb_define_class("Foo", rb_cObject);
}
EOF
klass = util_get_class content, 'foo'
assert_equal "a comment for class Foo on Init", klass.comment
end
def test_find_class_comment_define_class_bogus_comment
content = <<-EOF
/*
* a comment for other_function
*/
void
other_function() {
}
void
Init_Foo(void) {
VALUE foo = rb_define_class("Foo", rb_cObject);
}
EOF
klass = util_get_class content, 'foo'
assert_equal '', klass.comment
end
def test_find_body
content = <<-EOF
/*
* a comment for other_function
*/
VALUE
other_function() {
}
void
Init_Foo(void) {
VALUE foo = rb_define_class("Foo", rb_cObject);
rb_define_method(foo, "my_method", other_function, 0);
}
EOF
klass = util_get_class content, 'foo'
other_function = klass.method_list.first
assert_equal 'my_method', other_function.name
assert_equal "a comment for other_function",
other_function.comment
assert_equal '()', other_function.params
code = other_function.token_stream.first.text
assert_equal "VALUE\nother_function() ", code
end
def test_find_body_2
content = <<-CONTENT
/* Copyright (C) 2010 Sven Herzberg
*
* This file is free software; the author(s) gives unlimited
* permission to copy and/or distribute it, with or without
* modifications, as long as this notice is preserved.
*/
#include <ruby.h>
static VALUE
wrap_initialize (VALUE self)
{
return self;
}
/* */
static VALUE
wrap_shift (VALUE self,
VALUE arg)
{
return self;
}
void
init_gi_repository (void)
{
VALUE mTest = rb_define_module ("Test");
VALUE cTest = rb_define_class_under (mTest, "Test", rb_cObject);
rb_define_method (cTest, "initialize", wrap_initialize, 0);
rb_define_method (cTest, "shift", wrap_shift, 0);
}
CONTENT
klass = util_get_class content, 'cTest'
assert_equal 2, klass.method_list.length
end
def test_find_body_define
content = <<-EOF
/*
* a comment for other_function
*/
#define other_function rb_other_function
/* */
VALUE
rb_other_function() {
}
void
Init_Foo(void) {
VALUE foo = rb_define_class("Foo", rb_cObject);
rb_define_method(foo, "my_method", other_function, 0);
}
EOF
klass = util_get_class content, 'foo'
other_function = klass.method_list.first
assert_equal 'my_method', other_function.name
assert_equal "a comment for other_function",
other_function.comment
assert_equal '()', other_function.params
code = other_function.token_stream.first.text
assert_equal "#define other_function rb_other_function", code
end
def test_find_body_document_method
content = <<-EOF
/*
* Document-method: bar
* Document-method: baz
*
* a comment for bar
*/
VALUE
bar() {
}
void
Init_Foo(void) {
VALUE foo = rb_define_class("Foo", rb_cObject);
rb_define_method(foo, "bar", bar, 0);
rb_define_method(foo, "baz", bar, 0);
}
EOF
klass = util_get_class content, 'foo'
assert_equal 2, klass.method_list.length
methods = klass.method_list.sort
bar = methods.first
assert_equal 'Foo#bar', bar.full_name
assert_equal "a comment for bar", bar.comment
baz = methods.last
assert_equal 'Foo#baz', baz.full_name
assert_equal "a comment for bar", bar.comment
end
def test_find_modifiers_call_seq
comment = <<-COMMENT
/* call-seq:
* commercial() -> Date <br />
* commercial(cwyear, cweek=41, cwday=5, sg=nil) -> Date [ruby 1.8] <br />
* commercial(cwyear, cweek=1, cwday=1, sg=nil) -> Date [ruby 1.9]
*
* If no arguments are given:
* * ruby 1.8: returns a +Date+ for 1582-10-15 (the Day of Calendar Reform in
* Italy)
* * ruby 1.9: returns a +Date+ for julian day 0
*
* Otherwise, returns a +Date+ for the commercial week year, commercial week,
* and commercial week day given. Ignores the 4th argument.
*/
COMMENT
parser = util_parser ''
method_obj = RDoc::AnyMethod.new nil, 'blah'
parser.find_modifiers comment, method_obj
expected = <<-CALL_SEQ.chomp
commercial() -> Date <br />
commercial(cwyear, cweek=41, cwday=5, sg=nil) -> Date [ruby 1.8] <br />
commercial(cwyear, cweek=1, cwday=1, sg=nil) -> Date [ruby 1.9]
CALL_SEQ
assert_equal expected, method_obj.call_seq
end
def test_find_modifiers_call_seq_no_blank
comment = <<-COMMENT
/* call-seq:
* commercial() -> Date <br />
* commercial(cwyear, cweek=41, cwday=5, sg=nil) -> Date [ruby 1.8] <br />
* commercial(cwyear, cweek=1, cwday=1, sg=nil) -> Date [ruby 1.9]
*/
COMMENT
parser = util_parser ''
method_obj = RDoc::AnyMethod.new nil, 'blah'
parser.find_modifiers comment, method_obj
expected = <<-CALL_SEQ.chomp
commercial() -> Date <br />
commercial(cwyear, cweek=41, cwday=5, sg=nil) -> Date [ruby 1.8] <br />
commercial(cwyear, cweek=1, cwday=1, sg=nil) -> Date [ruby 1.9]
CALL_SEQ
assert_equal expected, method_obj.call_seq
end
def test_find_modifiers_nodoc
comment = <<-COMMENT
/* :nodoc:
*
* Blah
*/
COMMENT
parser = util_parser ''
method_obj = RDoc::AnyMethod.new nil, 'blah'
parser.find_modifiers comment, method_obj
assert_equal nil, method_obj.document_self
end
def test_find_modifiers_yields
comment = <<-COMMENT
/* :yields: a, b
*
* Blah
*/
COMMENT
parser = util_parser ''
method_obj = RDoc::AnyMethod.new nil, 'blah'
parser.find_modifiers comment, method_obj
assert_equal 'a, b', method_obj.block_params
expected = <<-EXPECTED
/*
*
* Blah
*/
EXPECTED
assert_equal expected, comment
end
def test_handle_method
parser = util_parser "Document-method: BasicObject#==\n blah */"
parser.handle_method 'method', 'rb_cBasicObject', '==', 'rb_obj_equal', 1
bo = @top_level.find_module_named 'BasicObject'
assert_equal 1, bo.method_list.length
equals2 = bo.method_list.first
assert_equal '==', equals2.name
end
def test_handle_method_initialize
parser = util_parser "Document-method: BasicObject::new\n blah */"
parser.handle_method('private_method', 'rb_cBasicObject',
'initialize', 'rb_obj_dummy', -1)
bo = @top_level.find_module_named 'BasicObject'
assert_equal 1, bo.method_list.length
new = bo.method_list.first
assert_equal 'new', new.name
assert_equal :public, new.visibility
end
def test_look_for_directives_in
parser = util_parser ''
comment = "# :markup: not_rdoc\n"
parser.look_for_directives_in @top_level, comment
assert_equal "# :markup: not_rdoc\n", comment
assert_equal 'not_rdoc', @top_level.metadata['markup']
end
def test_define_method
content = <<-EOF
/*Method Comment! */
static VALUE
rb_io_s_read(argc, argv, io)
int argc;
VALUE *argv;
VALUE io;
{
}
void
Init_IO(void) {
/*
* a comment for class Foo on rb_define_class
*/
VALUE rb_cIO = rb_define_class("IO", rb_cObject);
rb_define_singleton_method(rb_cIO, "read", rb_io_s_read, -1);
}
EOF
klass = util_get_class content, 'rb_cIO'
read_method = klass.method_list.first
assert_equal "read", read_method.name
assert_equal "Method Comment! ", read_method.comment
assert read_method.singleton
end
def test_define_method_private
content = <<-EOF
/*Method Comment! */
static VALUE
rb_io_s_read(argc, argv, io)
int argc;
VALUE *argv;
VALUE io;
{
}
void
Init_IO(void) {
/*
* a comment for class Foo on rb_define_class
*/
VALUE rb_cIO = rb_define_class("IO", rb_cObject);
rb_define_private_method(rb_cIO, "read", rb_io_s_read, -1);
}
EOF
klass = util_get_class content, 'rb_cIO'
read_method = klass.method_list.first
assert_equal 'IO#read', read_method.full_name
assert_equal :private, read_method.visibility
assert_equal "Method Comment! ", read_method.comment
end
def test_define_method_private_singleton
content = <<-EOF
/*Method Comment! */
static VALUE
rb_io_s_read(argc, argv, io)
int argc;
VALUE *argv;
VALUE io;
{
}
void
Init_IO(void) {
/*
* a comment for class Foo on rb_define_class
*/
VALUE rb_cIO = rb_define_class("IO", rb_cObject);
VALUE rb_cIO_s = rb_singleton_class(rb_cIO);
rb_define_private_method(rb_cIO_s, "read", rb_io_s_read, -1);
}
EOF
klass = util_get_class content, 'rb_cIO'
read_method = klass.method_list.first
assert_equal "read", read_method.name
assert_equal "Method Comment! ", read_method.comment
assert_equal :private, read_method.visibility
assert read_method.singleton
end
def test_define_method_singleton
content = <<-EOF
/*Method Comment! */
static VALUE
rb_io_s_read(argc, argv, io)
int argc;
VALUE *argv;
VALUE io;
{
}
void
Init_IO(void) {
/*
* a comment for class Foo on rb_define_class
*/
VALUE rb_cIO = rb_define_class("IO", rb_cObject);
VALUE rb_cIO_s = rb_singleton_class(rb_cIO);
rb_define_method(rb_cIO_s, "read", rb_io_s_read, -1);
}
EOF
klass = util_get_class content, 'rb_cIO'
read_method = klass.method_list.first
assert_equal "read", read_method.name
assert_equal "Method Comment! ", read_method.comment
assert read_method.singleton
end
def util_get_class(content, name)
@parser = util_parser content
@parser.scan
@parser.classes[name]
end
def util_parser(content)
RDoc::Parser::C.new @top_level, @fn, content, @options, @stats
end
end