File: c_parser_spec.rb

package info (click to toggle)
yard 0.9.37-1
  • links: PTS, VCS
  • area: main
  • in suites: forky, sid, trixie
  • size: 5,720 kB
  • sloc: ruby: 31,354; javascript: 7,608; makefile: 21
file content (254 lines) | stat: -rw-r--r-- 8,025 bytes parent folder | download | duplicates (2)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
# frozen_string_literal: true

RSpec.describe YARD::Parser::C::CParser do
  describe "#parse" do
    def parse(contents)
      Registry.clear
      YARD.parse_string(contents, :c)
    end

    describe "Array class" do
      before(:all) do
        file = File.join(File.dirname(__FILE__), 'examples', 'array.c.txt')
        parse(File.read(file))
      end

      it "parses Array class" do
        obj = YARD::Registry.at('Array')
        expect(obj).not_to be nil
        expect(obj.docstring).not_to be_blank
      end

      it "parses method" do
        obj = YARD::Registry.at('Array#initialize')
        expect(obj.docstring).not_to be_blank
        expect(obj.tags(:overload).size).to be > 1
      end

      it "parses new_ary return type" do
        obj = YARD::Registry.at('Array#map')
        expect(obj.tags(:overload).count do |overload|
          overload.tag(:return) && overload.tag(:return).types == ['Enumerator']
        end).to eq 2
        expect(obj.tags(:overload).count do |overload|
          overload.tag(:return) && overload.tag(:return).types == ['Array']
        end).to eq 2
      end
    end

    describe "C++ namespace" do
      before(:all) do
        file = File.join(File.dirname(__FILE__), 'examples', 'namespace.cpp.txt')
        parse(File.read(file))
      end

      it "parses Rect class" do
        obj = YARD::Registry.at('Rect')
        expect(obj).not_to be nil
        expect(obj.docstring).not_to be_blank
      end

      it "parses method inside of namespace" do
        obj = YARD::Registry.at('Rect#inspect')
        expect(obj.docstring).not_to be_blank
      end

      it "parses method after namespace" do
        obj = YARD::Registry.at('Rect#hello_world')
        expect(obj.docstring).not_to be_blank
      end
    end

    describe "Source located in extra files" do
      before(:all) do
        @multifile = File.join(File.dirname(__FILE__), 'examples', 'multifile.c.txt')
        @extrafile = File.join(File.dirname(__FILE__), 'examples', 'extrafile.c.txt')
        @contents = File.read(@multifile)
      end

      it "looks for methods in extra files (if 'in' comment is found)" do
        extra_contents = File.read(@extrafile)
        expect(File).to receive(:read).with('extra.c').and_return(extra_contents)
        parse(@contents)
        expect(Registry.at('Multifile#extra').docstring).to eq 'foo'
      end

      it "stops searching for extra source file gracefully if file is not found" do
        expect(File).to receive(:read).with('extra.c').and_raise(Errno::ENOENT)
        expect(log).to receive(:warn).with("Missing source file `extra.c' when parsing Multifile#extra")
        parse(@contents)
        expect(Registry.at('Multifile#extra').docstring).to eq ''
      end

      it "differentiates between a struct and a pointer to a struct retval" do
        parse(@contents)
        expect(Registry.at('Multifile#hello_mars').docstring).to eq 'Hello Mars'
      end
    end

    describe "Foo class" do
      it "does not include comments in docstring source" do
        parse <<-eof
          /*
           * Hello world
           */
          VALUE foo(VALUE x) {
            int value = x;
          }

          void Init_Foo() {
            rb_define_method(rb_cFoo, "foo", foo, 1);
          }
        eof
        expect(Registry.at('Foo#foo').source.gsub(/\s\s+/, ' ')).to eq(
          "VALUE foo(VALUE x) { int value = x;\n}"
        )
      end
    end

    describe "Class inherited from core error class" do
      it "resolves correct name" do
        parse <<-eof
          void
          Init_Mask(void)
          {
              rb_cFoo = rb_define_class("Foo", rb_cObject);
              rb_cMyError = rb_define_class("MyError", rb_eArgError);
          }
        eof
        klass = Registry.at('MyError')
        expect(klass.name).to eq :MyError
        expect(klass.title).to eq 'MyError'
        expect(klass.superclass.name).to eq :ArgumentError
        expect(klass.superclass.title).to eq 'ArgumentError'
      end
    end

    describe "Constant" do
      it "does not truncate docstring" do
        parse <<-eof
          #define MSK_DEADBEEF 0xdeadbeef
          void
          Init_Mask(void)
          {
              rb_cMask  = rb_define_class("Mask", rb_cObject);
              /* 0xdeadbeef: This constant is frequently used to indicate a
               * software crash or deadlock in embedded systems. */
              rb_define_const(rb_cMask, "DEADBEEF", INT2FIX(MSK_DEADBEEF));
          }
        eof
        constant = Registry.at('Mask::DEADBEEF')
        expect(constant.value).to eq '0xdeadbeef'
        expect(constant.docstring).to eq "This constant is frequently used to indicate a\nsoftware crash or deadlock in embedded systems."
      end
    end

    describe "Macros" do
      it "handles param## inside of macros" do
        thr = Thread.new do
          parse <<-eof
          void
          Init_gobject_gparamspecs(void)
          {
              VALUE cParamSpec = GTYPE2CLASS(G_TYPE_PARAM);
              VALUE c;

          #define DEF_NUMERIC_PSPEC_METHODS(c, typename) \
            G_STMT_START {\
              rbg_define_method(c, "initialize", typename##_initialize, 7); \
              rbg_define_method(c, "minimum", typename##_minimum, 0); \
              rbg_define_method(c, "maximum", typename##_maximum, 0); \
              rbg_define_method(c, "range", typename##_range, 0); \
            } G_STMT_END

          #if 0
              rbg_define_method(c, "default_value", typename##_default_value, 0); \
              rb_define_alias(c, "default", "default_value"); \

          #endif

              c = G_DEF_CLASS(G_TYPE_PARAM_CHAR, "Char", cParamSpec);
              DEF_NUMERIC_PSPEC_METHODS(c, char);
          eof
        end
        thr.join(5)
        if thr.alive?
          thr.kill
          raise "Did not parse in time"
        end
      end
    end

    describe "C macros in declaration" do
      it "handles C macros in method declaration" do
        Registry.clear
        parse <<-eof
        // docstring
        FOOBAR VALUE func() { }

        void
        Init_mod(void)
        {
          rb_define_method(rb_cFoo, "func", func, 0); \
        }
        eof

        expect(Registry.at('Foo#func').docstring).to eq "docstring"
      end
    end

    describe "File singleton methods" do
      before(:all) do
        file = File.join(File.dirname(__FILE__), 'examples', 'file.c.txt')
        parse(File.read(file))
      end

      it "parses methods from define_filetest_function" do
        obj = YARD::Registry.at('File.exist?')
        expect(obj).not_to be nil
        expect(obj.docstring).not_to be_blank
      end
    end
  end

  describe "Override comments" do
    before(:all) do
      Registry.clear
      override_file = File.join(File.dirname(__FILE__), 'examples', 'override.c.txt')
      @override_parser = YARD.parse_string(File.read(override_file), :c)
    end

    it "parses GMP::Z class" do
      z = YARD::Registry.at('GMP::Z')
      expect(z).not_to be nil
      expect(z.docstring).not_to be_blank
    end

    it "parses GMP::Z methods w/ bodies" do
      add = YARD::Registry.at('GMP::Z#+')
      expect(add.docstring).not_to be_blank
      expect(add.source).not_to be nil
      expect(add.source).not_to be_empty

      add_self = YARD::Registry.at('GMP::Z#+')
      expect(add_self.docstring).not_to be_blank
      expect(add_self.source).not_to be nil
      expect(add_self.source).not_to be_empty

      sqrtrem = YARD::Registry.at('GMP::Z#+')
      expect(sqrtrem.docstring).not_to be_blank
      expect(sqrtrem.source).not_to be nil
      expect(sqrtrem.source).not_to be_empty
    end

    it "parses GMP::Z methods w/o bodies" do
      neg = YARD::Registry.at('GMP::Z#neg')
      expect(neg.docstring).not_to be_blank
      expect(neg.source).to be nil

      neg_self = YARD::Registry.at('GMP::Z#neg')
      expect(neg_self.docstring).not_to be_blank
      expect(neg_self.source).to be nil
    end
  end
end