spec/handlers/c/method_handler_spec.rb in yard-0.9.18 vs spec/handlers/c/method_handler_spec.rb in yard-0.9.19

- old
+ new

@@ -1,325 +1,327 @@ -# frozen_string_literal: true -require File.dirname(__FILE__) + "/spec_helper" - -RSpec.describe YARD::Handlers::C::MethodHandler do - it "registers methods" do - parse_init <<-eof - mFoo = rb_define_module("Foo"); - rb_define_method(mFoo, "bar", bar, 0); - eof - expect(Registry.at('Foo#bar')).not_to be nil - expect(Registry.at('Foo#bar').visibility).to eq :public - end - - it "registers private methods" do - parse_init <<-eof - mFoo = rb_define_module("Foo"); - rb_define_private_method(mFoo, "bar", bar, 0); - eof - expect(Registry.at('Foo#bar')).not_to be nil - expect(Registry.at('Foo#bar').visibility).to eq :private - end - - it "registers singleton methods" do - parse_init <<-eof - mFoo = rb_define_module("Foo"); - rb_define_singleton_method(mFoo, "bar", bar, 0); - eof - expect(Registry.at('Foo.bar')).not_to be nil - expect(Registry.at('Foo.bar').visibility).to eq :public - end - - it "registers module functions" do - parse <<-eof - /* DOCSTRING - * @return [String] foo! - */ - static VALUE bar(VALUE self) { x(); y(); z(); } - - void Init_Foo() { - mFoo = rb_define_module("Foo"); - rb_define_module_function(mFoo, "bar", bar, 0); - } - eof - bar_c = Registry.at('Foo.bar') - bar_i = Registry.at('Foo#bar') - expect(bar_c).to be_module_function - expect(bar_c.visibility).to eq :public - expect(bar_c.docstring).to eq "DOCSTRING" - expect(bar_c.tag(:return).object).to eq bar_c - expect(bar_c.source).to eq "static VALUE bar(VALUE self) { x(); y(); z(); }" - expect(bar_i).not_to be_module_function - expect(bar_i.visibility).to eq :private - expect(bar_i.docstring).to eq "DOCSTRING" - expect(bar_i.tag(:return).object).to eq bar_i - expect(bar_i.source).to eq bar_c.source - end - - it "registers global functions into Kernel" do - parse_init 'rb_define_global_function("bar", bar, 0);' - expect(Registry.at('Kernel#bar')).not_to be nil - end - - it "looks for symbol containing method source" do - parse <<-eof - static VALUE foo(VALUE self) { x(); y(); z(); } - VALUE bar() { a(); b(); c(); } - void Init_Foo() { - mFoo = rb_define_module("Foo"); - rb_define_method(mFoo, "foo", foo, 0); - rb_define_method(mFoo, "bar", bar, 0); - } - eof - foo = Registry.at('Foo#foo') - bar = Registry.at('Foo#bar') - expect(foo.source).to eq "static VALUE foo(VALUE self) { x(); y(); z(); }" - expect(foo.file).to eq '(stdin)' - expect(foo.line).to eq 1 - expect(bar.source).to eq "VALUE bar() { a(); b(); c(); }" - expect(bar.file).to eq '(stdin)' - expect(bar.line).to eq 2 - end - - it "finds docstrings attached to method symbols" do - parse <<-eof - /* DOCSTRING */ - static VALUE foo(VALUE self) { x(); y(); z(); } - void Init_Foo() { - mFoo = rb_define_module("Foo"); - rb_define_method(mFoo, "foo", foo, 0); - } - eof - foo = Registry.at('Foo#foo') - expect(foo.docstring).to eq 'DOCSTRING' - end - - it "uses declaration comments as docstring if there are no others" do - parse <<-eof - static VALUE foo(VALUE self) { x(); y(); z(); } - void Init_Foo() { - mFoo = rb_define_module("Foo"); - /* DOCSTRING */ - rb_define_method(mFoo, "foo", foo, 0); - // DOCSTRING! - rb_define_method(mFoo, "bar", bar, 0); - } - eof - foo = Registry.at('Foo#foo') - expect(foo.docstring).to eq 'DOCSTRING' - bar = Registry.at('Foo#bar') - expect(bar.docstring).to eq 'DOCSTRING!' - end - - it "looks for symbols in other file" do - other = <<-eof - /* DOCSTRING! */ - static VALUE foo() { x(); } - eof - expect(File).to receive(:read).with('other.c').and_return(other) - parse <<-eof - void Init_Foo() { - mFoo = rb_define_module("Foo"); - rb_define_method(mFoo, "foo", foo, 0); // in other.c - } - eof - foo = Registry.at('Foo#foo') - expect(foo.docstring).to eq 'DOCSTRING!' - expect(foo.file).to eq 'other.c' - expect(foo.line).to eq 2 - expect(foo.source).to eq 'static VALUE foo() { x(); }' - end - - it "allows extra file to include /'s and other filename characters" do - expect(File).to receive(:read).at_least(1).times.with('ext/a-file.c').and_return(<<-eof) - /* FOO */ - VALUE foo(VALUE x) { int value = x; } - - /* BAR */ - VALUE bar(VALUE x) { int value = x; } - eof - parse_init <<-eof - rb_define_method(rb_cFoo, "foo", foo, 1); /* in ext/a-file.c */ - rb_define_global_function("bar", bar, 1); /* in ext/a-file.c */ - eof - expect(Registry.at('Foo#foo').docstring).to eq 'FOO' - expect(Registry.at('Kernel#bar').docstring).to eq 'BAR' - end - - it "warns if other file can't be found" do - expect(log).to receive(:warn).with(/Missing source file `other.c' when parsing Foo#foo/) - parse <<-eof - void Init_Foo() { - mFoo = rb_define_module("Foo"); - rb_define_method(mFoo, "foo", foo, 0); // in other.c - } - eof - end - - it "looks at override comments for docstring" do - parse <<-eof - /* Document-method: Foo::foo - * Document-method: new - * Document-method: Foo::Bar#baz - * Foo bar! - */ - - // init comments - void Init_Foo() { - mFoo = rb_define_module("Foo"); - rb_define_method(mFoo, "foo", foo, 0); - rb_define_method(mFoo, "initialize", foo, 0); - mBar = rb_define_module_under(mFoo, "Bar"); - rb_define_method(mBar, "baz", foo, 0); - } - eof - expect(Registry.at('Foo#foo').docstring).to eq 'Foo bar!' - expect(Registry.at('Foo#initialize').docstring).to eq 'Foo bar!' - expect(Registry.at('Foo::Bar#baz').docstring).to eq 'Foo bar!' - end - - it "looks at overrides in other files" do - other = <<-eof - /* Document-method: Foo::foo - * Document-method: new - * Document-method: Foo::Bar#baz - * Foo bar! - */ - eof - expect(File).to receive(:read).with('foo/bar/other.c').and_return(other) - src = <<-eof - void Init_Foo() { - mFoo = rb_define_module("Foo"); - rb_define_method(mFoo, "foo", foo, 0); // in foo/bar/other.c - rb_define_method(mFoo, "initialize", foo, 0); // in foo/bar/other.c - mBar = rb_define_module_under(mFoo, "Bar"); // in foo/bar/other.c - rb_define_method(mBar, "baz", foo, 0); // in foo/bar/other.c - } - eof - parse(src, 'foo/bar/baz/init.c') - expect(Registry.at('Foo#foo').docstring).to eq 'Foo bar!' - expect(Registry.at('Foo#initialize').docstring).to eq 'Foo bar!' - expect(Registry.at('Foo::Bar#baz').docstring).to eq 'Foo bar!' - end - - it "adds return tag on methods ending in '?'" do - parse <<-eof - /* DOCSTRING */ - static VALUE foo(VALUE self) { x(); y(); z(); } - void Init_Foo() { - mFoo = rb_define_module("Foo"); - rb_define_method(mFoo, "foo?", foo, 0); - } - eof - foo = Registry.at('Foo#foo?') - expect(foo.docstring).to eq 'DOCSTRING' - expect(foo.tag(:return).types).to eq ['Boolean'] - end - - it "does not add return tag if return tags exist" do - parse <<-eof - // @return [String] foo - static VALUE foo(VALUE self) { x(); y(); z(); } - void Init_Foo() { - mFoo = rb_define_module("Foo"); - rb_define_method(mFoo, "foo?", foo, 0); - } - eof - foo = Registry.at('Foo#foo?') - expect(foo.tag(:return).types).to eq ['String'] - end - - it "handles casted method names" do - parse_init <<-eof - mFoo = rb_define_module("Foo"); - rb_define_method(mFoo, "bar", (METHOD)bar, 0); - rb_define_global_function("baz", (METHOD)baz, 0); - eof - expect(Registry.at('Foo#bar')).not_to be nil - expect(Registry.at('Kernel#baz')).not_to be nil - end - - it "extracts at regular method parameters from C function signatures" do - parse <<-eof - static VALUE noargs_func(VALUE self) { return Qnil; } - static VALUE twoargs_func(VALUE self, VALUE a, VALUE b) { return a; } - void Init_Foo() { - mFoo = rb_define_module("Foo"); - rb_define_method(mFoo, "noargs", noargs_func, 0); - rb_define_method(mFoo, "twoargs", twoargs_func, 2); - } - eof - expect(Registry.at('Foo#noargs').parameters).to be_empty - expect(Registry.at('Foo#twoargs').parameters).to eq [['a', nil], ['b', nil]] - end - - it "extracts at varargs method parameters from C function signatures" do - parse <<-eof - static VALUE varargs_func(int argc, VALUE *argv, VALUE self) { return self; } - /* let's see if parser is robust in the face of strange spacing */ - static VALUE varargs_func2( int argc , VALUE - * argv ,VALUE self ) - - {return self;} - void Init_Foo() { - mFoo = rb_define_module("Foo"); - rb_define_method(mFoo, "varargs", varargs_func, -1); - rb_define_method( mFoo ,"varargs2",varargs_func2 ,-1); - } - eof - expect(Registry.at('Foo#varargs').parameters).to eq [['*args', nil]] - expect(Registry.at('Foo#varargs2').parameters).to eq [['*args', nil]] - end - - it "is not too strict or too loose about matching override comments to methods" do - parse <<-eof - /* Document-method: Foo::foo - * Document-method: new - * Document-method: Foo::Bar#baz - * Foo bar! - */ - - void Init_Foo() { - mFoo = rb_define_module("Foo"); - mBar = rb_define_module_under(mFoo, "Bar"); - - rb_define_method(mFoo, "foo", foo, 0); - rb_define_singleton_method(mFoo, "foo", foo, 0); - rb_define_method(mBar, "foo", foo, 0); - rb_define_singleton_method(mBar, "foo", foo, 0); - - rb_define_method(mFoo, "initialize", foo, 0); - rb_define_method(mBar, "initialize", foo, 0); - - rb_define_method(mFoo, "baz", foo, 0); - rb_define_singleton_method(mFoo, "baz", foo, 0); - rb_define_method(mBar, "baz", foo, 0); - rb_define_singleton_method(mBar, "baz", foo, 0); - } - eof - expect(Registry.at('Foo#foo').docstring).to eq 'Foo bar!' - expect(Registry.at('Foo.foo').docstring).to eq 'Foo bar!' - expect(Registry.at('Foo::Bar#foo').docstring).to be_empty - expect(Registry.at('Foo::Bar.foo').docstring).to be_empty - expect(Registry.at('Foo#initialize').docstring).to eq 'Foo bar!' - expect(Registry.at('Foo::Bar#initialize').docstring).to eq 'Foo bar!' - expect(Registry.at('Foo#baz').docstring).to be_empty - expect(Registry.at('Foo.baz').docstring).to be_empty - expect(Registry.at('Foo::Bar#baz').docstring).to eq 'Foo bar!' - expect(Registry.at('Foo::Bar.baz').docstring).to be_empty - end - - it "recognizes core Ruby classes and modules provided by ruby.h" do - parse_init <<-eof - rb_define_method(rb_cFixnum, "popcount", fix_popcount, 0); - rb_define_private_method(rb_mKernel, "pp", obj_pp, 0); - rb_define_method(rb_mEnumerable, "to_hash", enum_to_hash, 0); - eof - expect(Registry.at('Fixnum').type).to eq :class - expect(Registry.at('Fixnum#popcount').type).to eq :method - expect(Registry.at('Object').type).to eq :class - # Methods defined on Kernel are treated as if they were defined on Object - expect(Registry.at('Object#pp').type).to eq :method - expect(Registry.at('Enumerable').type).to eq :module - expect(Registry.at('Enumerable#to_hash').type).to eq :method - end -end +# frozen_string_literal: true +require File.dirname(__FILE__) + "/spec_helper" + +RSpec.describe YARD::Handlers::C::MethodHandler do + it "registers methods" do + parse_init <<-eof + mFoo = rb_define_module("Foo"); + rb_define_method(mFoo, "bar", bar, 0); + eof + expect(Registry.at('Foo#bar')).not_to be nil + expect(Registry.at('Foo#bar').visibility).to eq :public + end + + it "registers private methods" do + parse_init <<-eof + mFoo = rb_define_module("Foo"); + rb_define_private_method(mFoo, "bar", bar, 0); + eof + expect(Registry.at('Foo#bar')).not_to be nil + expect(Registry.at('Foo#bar').visibility).to eq :private + end + + it "registers singleton methods" do + parse_init <<-eof + mFoo = rb_define_module("Foo"); + rb_define_singleton_method(mFoo, "bar", bar, 0); + eof + expect(Registry.at('Foo.bar')).not_to be nil + expect(Registry.at('Foo.bar').visibility).to eq :public + end + + it "registers module functions" do + parse <<-eof + /* DOCSTRING + * @return [String] foo! + */ + static VALUE bar(VALUE self) { x(); y(); z(); } + + void Init_Foo() { + mFoo = rb_define_module("Foo"); + rb_define_module_function(mFoo, "bar", bar, 0); + } + eof + bar_c = Registry.at('Foo.bar') + bar_i = Registry.at('Foo#bar') + expect(bar_c).to be_module_function + expect(bar_c.visibility).to eq :public + expect(bar_c.docstring).to eq "DOCSTRING" + expect(bar_c.tag(:return).object).to eq bar_c + expect(bar_c.source).to eq "static VALUE bar(VALUE self) { x(); y(); z(); }" + expect(bar_i).not_to be_module_function + expect(bar_i.visibility).to eq :private + expect(bar_i.docstring).to eq "DOCSTRING" + expect(bar_i.tag(:return).object).to eq bar_i + expect(bar_i.source).to eq bar_c.source + end + + it "registers global functions into Kernel" do + parse_init 'rb_define_global_function("bar", bar, 0);' + expect(Registry.at('Kernel#bar')).not_to be nil + end + + it "looks for symbol containing method source" do + parse <<-eof + static VALUE foo(VALUE self) { x(); y(); z(); } + VALUE bar() { a(); b(); c(); } + void Init_Foo() { + mFoo = rb_define_module("Foo"); + rb_define_method(mFoo, "foo", foo, 0); + rb_define_method(mFoo, "bar", bar, 0); + } + eof + foo = Registry.at('Foo#foo') + bar = Registry.at('Foo#bar') + expect(foo.source).to eq "static VALUE foo(VALUE self) { x(); y(); z(); }" + expect(foo.file).to eq '(stdin)' + expect(foo.line).to eq 1 + expect(bar.source).to eq "VALUE bar() { a(); b(); c(); }" + expect(bar.file).to eq '(stdin)' + expect(bar.line).to eq 2 + end + + it "finds docstrings attached to method symbols" do + parse <<-eof + /* DOCSTRING */ + static VALUE foo(VALUE self) { x(); y(); z(); } + void Init_Foo() { + mFoo = rb_define_module("Foo"); + rb_define_method(mFoo, "foo", foo, 0); + } + eof + foo = Registry.at('Foo#foo') + expect(foo.docstring).to eq 'DOCSTRING' + end + + it "uses declaration comments as docstring if there are no others" do + parse <<-eof + static VALUE foo(VALUE self) { x(); y(); z(); } + void Init_Foo() { + mFoo = rb_define_module("Foo"); + /* DOCSTRING */ + rb_define_method(mFoo, "foo", foo, 0); + // DOCSTRING! + rb_define_method(mFoo, "bar", bar, 0); + } + eof + foo = Registry.at('Foo#foo') + expect(foo.docstring).to eq 'DOCSTRING' + bar = Registry.at('Foo#bar') + expect(bar.docstring).to eq 'DOCSTRING!' + end + + it "looks for symbols in other file" do + other = <<-eof + /* DOCSTRING! */ + static VALUE foo() { x(); } + eof + expect(File).to receive(:read).with('other.c').and_return(other) + parse <<-eof + void Init_Foo() { + mFoo = rb_define_module("Foo"); + rb_define_method(mFoo, "foo", foo, 0); // in other.c + } + eof + foo = Registry.at('Foo#foo') + expect(foo.docstring).to eq 'DOCSTRING!' + expect(foo.file).to eq 'other.c' + expect(foo.line).to eq 2 + expect(foo.source).to eq 'static VALUE foo() { x(); }' + end + + it "allows extra file to include /'s and other filename characters" do + expect(File).to receive(:read).at_least(1).times.with('ext/a-file.c').and_return(<<-eof) + /* FOO */ + VALUE foo(VALUE x) { int value = x; } + + /* BAR */ + VALUE bar(VALUE x) { int value = x; } + eof + parse_init <<-eof + rb_define_method(rb_cFoo, "foo", foo, 1); /* in ext/a-file.c */ + rb_define_global_function("bar", bar, 1); /* in ext/a-file.c */ + eof + expect(Registry.at('Foo#foo').docstring).to eq 'FOO' + expect(Registry.at('Kernel#bar').docstring).to eq 'BAR' + end + + it "warns if other file can't be found" do + expect(log).to receive(:warn).with(/Missing source file `other.c' when parsing Foo#foo/) + parse <<-eof + void Init_Foo() { + mFoo = rb_define_module("Foo"); + rb_define_method(mFoo, "foo", foo, 0); // in other.c + } + eof + end + + it "looks at override comments for docstring" do + parse <<-eof + /* Document-method: Foo::foo + * Document-method: new + * Document-method: Foo::Bar#baz + * Foo bar! + */ + + // init comments + void Init_Foo() { + mFoo = rb_define_module("Foo"); + rb_define_method(mFoo, "foo", foo, 0); + rb_define_method(mFoo, "initialize", foo, 0); + mBar = rb_define_module_under(mFoo, "Bar"); + rb_define_method(mBar, "baz", foo, 0); + } + eof + expect(Registry.at('Foo#foo').docstring).to eq 'Foo bar!' + expect(Registry.at('Foo#initialize').docstring).to eq 'Foo bar!' + expect(Registry.at('Foo::Bar#baz').docstring).to eq 'Foo bar!' + end + + it "looks at overrides in other files" do + other = <<-eof + /* Document-method: Foo::foo + * Document-method: new + * Document-method: Foo::Bar#baz + * Foo bar! + */ + eof + expect(File).to receive(:read).with('foo/bar/other.c').and_return(other) + src = <<-eof + void Init_Foo() { + mFoo = rb_define_module("Foo"); + rb_define_method(mFoo, "foo", foo, 0); // in foo/bar/other.c + rb_define_method(mFoo, "initialize", foo, 0); // in foo/bar/other.c + mBar = rb_define_module_under(mFoo, "Bar"); // in foo/bar/other.c + rb_define_method(mBar, "baz", foo, 0); // in foo/bar/other.c + } + eof + parse(src, 'foo/bar/baz/init.c') + expect(Registry.at('Foo#foo').docstring).to eq 'Foo bar!' + expect(Registry.at('Foo#initialize').docstring).to eq 'Foo bar!' + expect(Registry.at('Foo::Bar#baz').docstring).to eq 'Foo bar!' + end + + it "adds return tag on methods ending in '?'" do + parse <<-eof + /* DOCSTRING */ + static VALUE foo(VALUE self) { x(); y(); z(); } + void Init_Foo() { + mFoo = rb_define_module("Foo"); + rb_define_method(mFoo, "foo?", foo, 0); + } + eof + foo = Registry.at('Foo#foo?') + expect(foo.docstring).to eq 'DOCSTRING' + expect(foo.tag(:return).types).to eq ['Boolean'] + expect(foo.tags(:return).size).to eq 1 + end + + it "does not add return tag if return tags exist" do + parse <<-eof + // @return [String] foo + static VALUE foo(VALUE self) { x(); y(); z(); } + void Init_Foo() { + mFoo = rb_define_module("Foo"); + rb_define_method(mFoo, "foo?", foo, 0); + } + eof + foo = Registry.at('Foo#foo?') + expect(foo.tag(:return).types).to eq ['String'] + expect(foo.tags(:return).size).to eq 1 + end + + it "handles casted method names" do + parse_init <<-eof + mFoo = rb_define_module("Foo"); + rb_define_method(mFoo, "bar", (METHOD)bar, 0); + rb_define_global_function("baz", (METHOD)baz, 0); + eof + expect(Registry.at('Foo#bar')).not_to be nil + expect(Registry.at('Kernel#baz')).not_to be nil + end + + it "extracts at regular method parameters from C function signatures" do + parse <<-eof + static VALUE noargs_func(VALUE self) { return Qnil; } + static VALUE twoargs_func(VALUE self, VALUE a, VALUE b) { return a; } + void Init_Foo() { + mFoo = rb_define_module("Foo"); + rb_define_method(mFoo, "noargs", noargs_func, 0); + rb_define_method(mFoo, "twoargs", twoargs_func, 2); + } + eof + expect(Registry.at('Foo#noargs').parameters).to be_empty + expect(Registry.at('Foo#twoargs').parameters).to eq [['a', nil], ['b', nil]] + end + + it "extracts at varargs method parameters from C function signatures" do + parse <<-eof + static VALUE varargs_func(int argc, VALUE *argv, VALUE self) { return self; } + /* let's see if parser is robust in the face of strange spacing */ + static VALUE varargs_func2( int argc , VALUE + * argv ,VALUE self ) + + {return self;} + void Init_Foo() { + mFoo = rb_define_module("Foo"); + rb_define_method(mFoo, "varargs", varargs_func, -1); + rb_define_method( mFoo ,"varargs2",varargs_func2 ,-1); + } + eof + expect(Registry.at('Foo#varargs').parameters).to eq [['*args', nil]] + expect(Registry.at('Foo#varargs2').parameters).to eq [['*args', nil]] + end + + it "is not too strict or too loose about matching override comments to methods" do + parse <<-eof + /* Document-method: Foo::foo + * Document-method: new + * Document-method: Foo::Bar#baz + * Foo bar! + */ + + void Init_Foo() { + mFoo = rb_define_module("Foo"); + mBar = rb_define_module_under(mFoo, "Bar"); + + rb_define_method(mFoo, "foo", foo, 0); + rb_define_singleton_method(mFoo, "foo", foo, 0); + rb_define_method(mBar, "foo", foo, 0); + rb_define_singleton_method(mBar, "foo", foo, 0); + + rb_define_method(mFoo, "initialize", foo, 0); + rb_define_method(mBar, "initialize", foo, 0); + + rb_define_method(mFoo, "baz", foo, 0); + rb_define_singleton_method(mFoo, "baz", foo, 0); + rb_define_method(mBar, "baz", foo, 0); + rb_define_singleton_method(mBar, "baz", foo, 0); + } + eof + expect(Registry.at('Foo#foo').docstring).to eq 'Foo bar!' + expect(Registry.at('Foo.foo').docstring).to eq 'Foo bar!' + expect(Registry.at('Foo::Bar#foo').docstring).to be_empty + expect(Registry.at('Foo::Bar.foo').docstring).to be_empty + expect(Registry.at('Foo#initialize').docstring).to eq 'Foo bar!' + expect(Registry.at('Foo::Bar#initialize').docstring).to eq 'Foo bar!' + expect(Registry.at('Foo#baz').docstring).to be_empty + expect(Registry.at('Foo.baz').docstring).to be_empty + expect(Registry.at('Foo::Bar#baz').docstring).to eq 'Foo bar!' + expect(Registry.at('Foo::Bar.baz').docstring).to be_empty + end + + it "recognizes core Ruby classes and modules provided by ruby.h" do + parse_init <<-eof + rb_define_method(rb_cFixnum, "popcount", fix_popcount, 0); + rb_define_private_method(rb_mKernel, "pp", obj_pp, 0); + rb_define_method(rb_mEnumerable, "to_hash", enum_to_hash, 0); + eof + expect(Registry.at('Fixnum').type).to eq :class + expect(Registry.at('Fixnum#popcount').type).to eq :method + expect(Registry.at('Object').type).to eq :class + # Methods defined on Kernel are treated as if they were defined on Object + expect(Registry.at('Object#pp').type).to eq :method + expect(Registry.at('Enumerable').type).to eq :module + expect(Registry.at('Enumerable#to_hash').type).to eq :method + end +end