RubyInlineWithoutZenTest 3.12.2
Sign up to get free protection for your applications and to get access to all the features.
- data/.gitignore +17 -0
- data/Gemfile +4 -0
- data/History.txt +470 -0
- data/LICENSE.txt +22 -0
- data/Manifest.txt +12 -0
- data/README.md +29 -0
- data/README.txt +138 -0
- data/Rakefile +17 -0
- data/demo/fastmath.rb +27 -0
- data/demo/hello.rb +13 -0
- data/example.rb +86 -0
- data/example2.rb +33 -0
- data/lib/inline/mapping.rb +119 -0
- data/lib/inline/version.rb +3 -0
- data/lib/inline.rb +902 -0
- data/rubyinlinewithoutzentest.gemspec +39 -0
- data/test/test_inline.rb +1059 -0
- data/tutorial/example1.rb +63 -0
- data/tutorial/example2.rb +96 -0
- metadata +127 -0
data/test/test_inline.rb
ADDED
@@ -0,0 +1,1059 @@
|
|
1
|
+
$TESTING = true
|
2
|
+
|
3
|
+
$0 = __FILE__ if $0 =~ /-e|\(eval\)|^$/
|
4
|
+
$0 = $0.sub(Dir.pwd, '.')
|
5
|
+
|
6
|
+
require 'inline'
|
7
|
+
require 'tempfile'
|
8
|
+
require 'tmpdir'
|
9
|
+
require 'fileutils' unless defined?(::FileUtils)
|
10
|
+
|
11
|
+
require 'minitest/autorun'
|
12
|
+
|
13
|
+
File.umask(0)
|
14
|
+
|
15
|
+
require 'pathname'
|
16
|
+
|
17
|
+
$inline_path = './lib/inline.rb'
|
18
|
+
$test_inline_path = './test/test_inline.rb'
|
19
|
+
|
20
|
+
class InlineTestCase < Minitest::Test
|
21
|
+
def setup
|
22
|
+
super
|
23
|
+
@rootdir = File.join(Dir.tmpdir, "test_inline.#{$$}")
|
24
|
+
Dir.mkdir @rootdir, 0700 unless test ?d, @rootdir
|
25
|
+
ENV['INLINEDIR'] = @rootdir
|
26
|
+
end
|
27
|
+
|
28
|
+
def teardown
|
29
|
+
unless $DEBUG then
|
30
|
+
FileUtils.rm_rf @rootdir
|
31
|
+
ENV.delete 'INLINEDIR'
|
32
|
+
end
|
33
|
+
end
|
34
|
+
end
|
35
|
+
|
36
|
+
class TestDir < InlineTestCase
|
37
|
+
def setup
|
38
|
+
super
|
39
|
+
@count = 1
|
40
|
+
end
|
41
|
+
|
42
|
+
def util_assert_secure(perms, should_pass)
|
43
|
+
path = File.join(@rootdir, @count.to_s)
|
44
|
+
@count += 1
|
45
|
+
Dir.mkdir path, perms unless perms.nil?
|
46
|
+
if should_pass then
|
47
|
+
Dir.assert_secure path
|
48
|
+
else
|
49
|
+
assert_raises(SecurityError) do
|
50
|
+
Dir.assert_secure path
|
51
|
+
end
|
52
|
+
end
|
53
|
+
end
|
54
|
+
|
55
|
+
def test_assert_secure
|
56
|
+
# existing/good
|
57
|
+
util_assert_secure 0700, true
|
58
|
+
# existing/bad
|
59
|
+
util_assert_secure 0707, false
|
60
|
+
util_assert_secure 0770, false
|
61
|
+
util_assert_secure 0777, false
|
62
|
+
# missing
|
63
|
+
util_assert_secure nil, true
|
64
|
+
end
|
65
|
+
end unless Inline::WINDOZE
|
66
|
+
|
67
|
+
class TestInline < InlineTestCase
|
68
|
+
|
69
|
+
def test_rootdir
|
70
|
+
assert_equal(@rootdir, Inline.rootdir)
|
71
|
+
end
|
72
|
+
|
73
|
+
def test_directory
|
74
|
+
version = "#{Gem.ruby_engine}-#{RbConfig::CONFIG['ruby_version']}"
|
75
|
+
inlinedir = File.join(@rootdir, ".ruby_inline", version)
|
76
|
+
assert_equal(inlinedir, Inline.directory)
|
77
|
+
end
|
78
|
+
|
79
|
+
end
|
80
|
+
|
81
|
+
class TestInline
|
82
|
+
class TestC < InlineTestCase
|
83
|
+
|
84
|
+
def setup
|
85
|
+
super
|
86
|
+
@builder = Inline::C.new(self.class)
|
87
|
+
end
|
88
|
+
|
89
|
+
|
90
|
+
# quick hack to make tests more readable,
|
91
|
+
# does nothing I wouldn't otherwise do...
|
92
|
+
def inline(lang=:C)
|
93
|
+
self.class.inline(lang, true) do |builder|
|
94
|
+
yield(builder)
|
95
|
+
end
|
96
|
+
end
|
97
|
+
|
98
|
+
def test_initialize
|
99
|
+
x = Inline::C.new(self.class)
|
100
|
+
assert_equal TestInline::TestC, x.mod
|
101
|
+
assert_equal [], x.src
|
102
|
+
assert_equal({}, x.sig)
|
103
|
+
assert_equal [], x.flags
|
104
|
+
assert_equal [], x.libs
|
105
|
+
end
|
106
|
+
|
107
|
+
def test_accessor
|
108
|
+
builder = Inline::C.new self.class
|
109
|
+
|
110
|
+
builder.struct_name = 'MyStruct'
|
111
|
+
builder.accessor 'method_name', 'int'
|
112
|
+
|
113
|
+
source = util_strip_lines builder.src
|
114
|
+
|
115
|
+
expected = []
|
116
|
+
expected << <<-READER
|
117
|
+
# line N "#{$inline_path}"
|
118
|
+
static VALUE method_name(VALUE self) {
|
119
|
+
|
120
|
+
MyStruct *pointer;
|
121
|
+
|
122
|
+
Data_Get_Struct(self, MyStruct, pointer);
|
123
|
+
|
124
|
+
return (INT2FIX(pointer->method_name));
|
125
|
+
}
|
126
|
+
READER
|
127
|
+
|
128
|
+
expected << <<-WRITER
|
129
|
+
# line N "#{$inline_path}"
|
130
|
+
static VALUE method_name_equals(VALUE self, VALUE _value) {
|
131
|
+
VALUE value = (_value);
|
132
|
+
|
133
|
+
MyStruct *pointer;
|
134
|
+
|
135
|
+
Data_Get_Struct(self, MyStruct, pointer);
|
136
|
+
|
137
|
+
pointer->method_name = FIX2INT(value);
|
138
|
+
|
139
|
+
return (value);
|
140
|
+
}
|
141
|
+
WRITER
|
142
|
+
|
143
|
+
assert_equal expected, source
|
144
|
+
end
|
145
|
+
|
146
|
+
def test_accessor_member_name
|
147
|
+
builder = Inline::C.new self.class
|
148
|
+
|
149
|
+
builder.struct_name = 'MyStruct'
|
150
|
+
builder.accessor 'method_name', 'int', 'member_name'
|
151
|
+
|
152
|
+
source = util_strip_lines builder.src
|
153
|
+
|
154
|
+
expected = []
|
155
|
+
expected << <<-READER
|
156
|
+
# line N "#{$inline_path}"
|
157
|
+
static VALUE method_name(VALUE self) {
|
158
|
+
|
159
|
+
MyStruct *pointer;
|
160
|
+
|
161
|
+
Data_Get_Struct(self, MyStruct, pointer);
|
162
|
+
|
163
|
+
return (INT2FIX(pointer->member_name));
|
164
|
+
}
|
165
|
+
READER
|
166
|
+
|
167
|
+
expected << <<-WRITER
|
168
|
+
# line N "#{$inline_path}"
|
169
|
+
static VALUE method_name_equals(VALUE self, VALUE _value) {
|
170
|
+
VALUE value = (_value);
|
171
|
+
|
172
|
+
MyStruct *pointer;
|
173
|
+
|
174
|
+
Data_Get_Struct(self, MyStruct, pointer);
|
175
|
+
|
176
|
+
pointer->member_name = FIX2INT(value);
|
177
|
+
|
178
|
+
return (value);
|
179
|
+
}
|
180
|
+
WRITER
|
181
|
+
|
182
|
+
assert_equal expected, source
|
183
|
+
end
|
184
|
+
|
185
|
+
def test_accessor_no_struct_name
|
186
|
+
builder = Inline::C.new self.class
|
187
|
+
|
188
|
+
e = assert_raises RuntimeError do
|
189
|
+
builder.accessor 'method_name', 'int'
|
190
|
+
end
|
191
|
+
|
192
|
+
assert_equal "struct name not set for reader method_name int", e.message
|
193
|
+
end
|
194
|
+
|
195
|
+
def test_add_type_converter
|
196
|
+
builder = Inline::C.new self.class
|
197
|
+
|
198
|
+
builder.add_type_converter 'my_type', 'ruby_type2my_type',
|
199
|
+
'my_type2ruby_type'
|
200
|
+
|
201
|
+
assert_equal 'my_type2ruby_type', builder.c2ruby('my_type')
|
202
|
+
assert_equal 'ruby_type2my_type', builder.ruby2c('my_type')
|
203
|
+
end
|
204
|
+
|
205
|
+
def test_alias_type_converter
|
206
|
+
builder = Inline::C.new self.class
|
207
|
+
|
208
|
+
builder.alias_type_converter 'long long', 'int64_t'
|
209
|
+
|
210
|
+
assert_equal 'LL2NUM', builder.c2ruby('int64_t')
|
211
|
+
assert_equal 'NUM2LL', builder.ruby2c('int64_t')
|
212
|
+
end
|
213
|
+
|
214
|
+
def test_reader
|
215
|
+
builder = Inline::C.new self.class
|
216
|
+
|
217
|
+
builder.struct_name = 'MyStruct'
|
218
|
+
builder.reader 'method_name', 'int'
|
219
|
+
|
220
|
+
source = util_strip_lines builder.src
|
221
|
+
|
222
|
+
expected = []
|
223
|
+
expected << <<-READER
|
224
|
+
# line N "#{$inline_path}"
|
225
|
+
static VALUE method_name(VALUE self) {
|
226
|
+
|
227
|
+
MyStruct *pointer;
|
228
|
+
|
229
|
+
Data_Get_Struct(self, MyStruct, pointer);
|
230
|
+
|
231
|
+
return (INT2FIX(pointer->method_name));
|
232
|
+
}
|
233
|
+
READER
|
234
|
+
|
235
|
+
assert_equal expected, source
|
236
|
+
end
|
237
|
+
|
238
|
+
def test_reader_member_name
|
239
|
+
builder = Inline::C.new self.class
|
240
|
+
|
241
|
+
builder.struct_name = 'MyStruct'
|
242
|
+
builder.reader 'method_name', 'int', 'member_name'
|
243
|
+
|
244
|
+
source = util_strip_lines builder.src
|
245
|
+
|
246
|
+
expected = []
|
247
|
+
expected << <<-READER
|
248
|
+
# line N "#{$inline_path}"
|
249
|
+
static VALUE method_name(VALUE self) {
|
250
|
+
|
251
|
+
MyStruct *pointer;
|
252
|
+
|
253
|
+
Data_Get_Struct(self, MyStruct, pointer);
|
254
|
+
|
255
|
+
return (INT2FIX(pointer->member_name));
|
256
|
+
}
|
257
|
+
READER
|
258
|
+
|
259
|
+
assert_equal expected, source
|
260
|
+
end
|
261
|
+
|
262
|
+
def test_reader_no_struct_name
|
263
|
+
builder = Inline::C.new self.class
|
264
|
+
|
265
|
+
e = assert_raises RuntimeError do
|
266
|
+
builder.reader 'method_name', 'int'
|
267
|
+
end
|
268
|
+
|
269
|
+
assert_equal "struct name not set for reader method_name int", e.message
|
270
|
+
end
|
271
|
+
|
272
|
+
def test_remove_type_converter
|
273
|
+
builder = Inline::C.new self.class
|
274
|
+
|
275
|
+
builder.remove_type_converter 'long'
|
276
|
+
|
277
|
+
assert_raises ArgumentError do
|
278
|
+
builder.c2ruby 'long'
|
279
|
+
end
|
280
|
+
end
|
281
|
+
|
282
|
+
def test_writer
|
283
|
+
builder = Inline::C.new self.class
|
284
|
+
|
285
|
+
builder.struct_name = 'MyStruct'
|
286
|
+
builder.writer 'method_name', 'int'
|
287
|
+
|
288
|
+
source = util_strip_lines builder.src
|
289
|
+
|
290
|
+
expected = []
|
291
|
+
expected << <<-WRITER
|
292
|
+
# line N "#{$inline_path}"
|
293
|
+
static VALUE method_name_equals(VALUE self, VALUE _value) {
|
294
|
+
VALUE value = (_value);
|
295
|
+
|
296
|
+
MyStruct *pointer;
|
297
|
+
|
298
|
+
Data_Get_Struct(self, MyStruct, pointer);
|
299
|
+
|
300
|
+
pointer->method_name = FIX2INT(value);
|
301
|
+
|
302
|
+
return (value);
|
303
|
+
}
|
304
|
+
WRITER
|
305
|
+
|
306
|
+
assert_equal expected, source
|
307
|
+
end
|
308
|
+
|
309
|
+
def test_writer_member_name
|
310
|
+
builder = Inline::C.new self.class
|
311
|
+
|
312
|
+
builder.struct_name = 'MyStruct'
|
313
|
+
builder.writer 'method_name', 'int', 'member_name'
|
314
|
+
|
315
|
+
source = util_strip_lines builder.src
|
316
|
+
|
317
|
+
expected = []
|
318
|
+
expected << <<-WRITER
|
319
|
+
# line N "#{$inline_path}"
|
320
|
+
static VALUE method_name_equals(VALUE self, VALUE _value) {
|
321
|
+
VALUE value = (_value);
|
322
|
+
|
323
|
+
MyStruct *pointer;
|
324
|
+
|
325
|
+
Data_Get_Struct(self, MyStruct, pointer);
|
326
|
+
|
327
|
+
pointer->member_name = FIX2INT(value);
|
328
|
+
|
329
|
+
return (value);
|
330
|
+
}
|
331
|
+
WRITER
|
332
|
+
|
333
|
+
assert_equal expected, source
|
334
|
+
end
|
335
|
+
|
336
|
+
def test_writer_no_struct_name
|
337
|
+
builder = Inline::C.new self.class
|
338
|
+
|
339
|
+
e = assert_raises RuntimeError do
|
340
|
+
builder.writer 'method_name', 'int'
|
341
|
+
end
|
342
|
+
|
343
|
+
assert_equal "struct name not set for writer method_name int", e.message
|
344
|
+
end
|
345
|
+
|
346
|
+
def test_ruby2c
|
347
|
+
x = Inline::C.new(self.class)
|
348
|
+
assert_equal 'NUM2CHR', x.ruby2c("char")
|
349
|
+
assert_equal 'StringValuePtr', x.ruby2c("char *")
|
350
|
+
|
351
|
+
assert_equal "FI\X2INT", x.ruby2c("int")
|
352
|
+
assert_equal 'NUM2UINT', x.ruby2c("unsigned")
|
353
|
+
assert_equal 'NUM2UINT', x.ruby2c("unsigned int")
|
354
|
+
|
355
|
+
assert_equal 'NUM2LONG', x.ruby2c("long")
|
356
|
+
assert_equal 'NUM2ULONG', x.ruby2c("unsigned long")
|
357
|
+
|
358
|
+
assert_equal 'NUM2LL', x.ruby2c("long long")
|
359
|
+
assert_equal 'NUM2ULL', x.ruby2c("unsigned long long")
|
360
|
+
|
361
|
+
assert_equal 'NUM2DBL', x.ruby2c("double")
|
362
|
+
|
363
|
+
assert_equal 'NUM2OFFT', x.ruby2c("off_t")
|
364
|
+
|
365
|
+
assert_equal '', x.ruby2c("VALUE")
|
366
|
+
|
367
|
+
assert_raises ArgumentError do
|
368
|
+
x.ruby2c('blah')
|
369
|
+
end
|
370
|
+
end
|
371
|
+
|
372
|
+
def test_c2ruby
|
373
|
+
x = Inline::C.new(self.class)
|
374
|
+
assert_equal 'CHR2FIX', x.c2ruby("char")
|
375
|
+
|
376
|
+
assert_equal 'rb_str_new2', x.c2ruby("char *")
|
377
|
+
|
378
|
+
assert_equal 'INT2FIX', x.c2ruby("int")
|
379
|
+
assert_equal 'UINT2NUM', x.c2ruby("unsigned int")
|
380
|
+
assert_equal 'UINT2NUM', x.c2ruby("unsigned")
|
381
|
+
|
382
|
+
assert_equal 'LONG2NUM', x.c2ruby("long")
|
383
|
+
assert_equal 'ULONG2NUM', x.c2ruby("unsigned long")
|
384
|
+
|
385
|
+
assert_equal 'LL2NUM', x.c2ruby("long long")
|
386
|
+
assert_equal 'ULL2NUM', x.c2ruby("unsigned long long")
|
387
|
+
|
388
|
+
assert_equal 'rb_float_new', x.c2ruby("double")
|
389
|
+
|
390
|
+
assert_equal 'OFFT2NUM', x.c2ruby("off_t")
|
391
|
+
|
392
|
+
assert_equal '', x.c2ruby("VALUE")
|
393
|
+
|
394
|
+
assert_raises ArgumentError do
|
395
|
+
x.c2ruby('blah')
|
396
|
+
end
|
397
|
+
end
|
398
|
+
|
399
|
+
def util_module_name(*signatures)
|
400
|
+
md5 = Digest::MD5.new
|
401
|
+
|
402
|
+
signatures.each do |signature|
|
403
|
+
@builder.sig[signature] = [nil, 0]
|
404
|
+
md5 << signature.to_s
|
405
|
+
end
|
406
|
+
|
407
|
+
assert_equal("Inline_TestInline__TestC_#{md5}", @builder.module_name)
|
408
|
+
end
|
409
|
+
|
410
|
+
def test_module_name_0_methods
|
411
|
+
util_module_name
|
412
|
+
end
|
413
|
+
|
414
|
+
def test_module_name_1_method
|
415
|
+
util_module_name :something1
|
416
|
+
end
|
417
|
+
|
418
|
+
def test_module_name_2_methods
|
419
|
+
util_module_name :something2, :something3
|
420
|
+
end
|
421
|
+
|
422
|
+
def test_module_name_2_other_methods
|
423
|
+
util_module_name :something4, :something5
|
424
|
+
end
|
425
|
+
|
426
|
+
def util_parse_signature(src, expected, t=nil, a=nil, b=nil)
|
427
|
+
result = nil
|
428
|
+
|
429
|
+
@builder.add_type_converter t, a, b unless t.nil?
|
430
|
+
result = @builder.parse_signature(src)
|
431
|
+
|
432
|
+
assert_equal(expected, result)
|
433
|
+
end
|
434
|
+
|
435
|
+
def test_parse_signature
|
436
|
+
src = "// stupid cpp comment
|
437
|
+
#include \"header.h\"
|
438
|
+
/* stupid c comment */
|
439
|
+
int
|
440
|
+
add(int x, int y) {
|
441
|
+
int result = x+y;
|
442
|
+
return result;
|
443
|
+
}
|
444
|
+
"
|
445
|
+
|
446
|
+
expected = {
|
447
|
+
'name' => 'add',
|
448
|
+
'return' => 'int',
|
449
|
+
'arity' => 2,
|
450
|
+
'args' => [
|
451
|
+
['x', 'int'],
|
452
|
+
['y', 'int']
|
453
|
+
]
|
454
|
+
}
|
455
|
+
|
456
|
+
util_parse_signature(src, expected)
|
457
|
+
end
|
458
|
+
|
459
|
+
def test_parse_signature_custom
|
460
|
+
|
461
|
+
src = "// stupid cpp comment
|
462
|
+
#include \"header.h\"
|
463
|
+
/* stupid c comment */
|
464
|
+
int
|
465
|
+
add(fooby x, int y) {
|
466
|
+
int result = x+y;
|
467
|
+
return result;
|
468
|
+
}
|
469
|
+
"
|
470
|
+
|
471
|
+
expected = {
|
472
|
+
'name' => 'add',
|
473
|
+
'return' => 'int',
|
474
|
+
'arity' => 2,
|
475
|
+
'args' => [
|
476
|
+
[ 'x', 'fooby' ],
|
477
|
+
['y', 'int']
|
478
|
+
]
|
479
|
+
}
|
480
|
+
|
481
|
+
util_parse_signature(src, expected,
|
482
|
+
"fooby", "r2c_fooby", "c2r_fooby")
|
483
|
+
end
|
484
|
+
|
485
|
+
def test_parse_signature_register
|
486
|
+
|
487
|
+
src = "// stupid cpp comment
|
488
|
+
#include \"header.h\"
|
489
|
+
/* stupid c comment */
|
490
|
+
int
|
491
|
+
add(register int x, int y) {
|
492
|
+
int result = x+y;
|
493
|
+
return result;
|
494
|
+
}
|
495
|
+
"
|
496
|
+
|
497
|
+
expected = {
|
498
|
+
'name' => 'add',
|
499
|
+
'return' => 'int',
|
500
|
+
'arity' => 2,
|
501
|
+
'args' => [
|
502
|
+
[ 'x', 'register int' ],
|
503
|
+
['y', 'int']
|
504
|
+
]
|
505
|
+
}
|
506
|
+
|
507
|
+
|
508
|
+
util_parse_signature(src, expected,
|
509
|
+
"register int", "FI\X2INT", "INT2FI\X")
|
510
|
+
end
|
511
|
+
|
512
|
+
def util_generate(src, expected, expand_types=true)
|
513
|
+
result = @builder.generate src, expand_types
|
514
|
+
result = util_strip_lines result
|
515
|
+
|
516
|
+
expected = "# line N \"#{$0}\"\n" + expected
|
517
|
+
assert_equal(expected, result)
|
518
|
+
end
|
519
|
+
|
520
|
+
def util_generate_raw(src, expected)
|
521
|
+
util_generate(src, expected, false)
|
522
|
+
end
|
523
|
+
|
524
|
+
def util_strip_lines(src)
|
525
|
+
case src
|
526
|
+
when String then
|
527
|
+
src.gsub(/\# line \d+/, '# line N').gsub(Dir.pwd, '.')
|
528
|
+
when Array then
|
529
|
+
src.map do |chunk|
|
530
|
+
util_strip_lines chunk
|
531
|
+
end
|
532
|
+
end
|
533
|
+
end
|
534
|
+
|
535
|
+
# Ruby Arity Rules, from the mouth of Matz:
|
536
|
+
# -2 = ruby array argv
|
537
|
+
# -1 = c array argv
|
538
|
+
# 0 = self
|
539
|
+
# 1 = self, value
|
540
|
+
# 2 = self, value, value
|
541
|
+
# ...
|
542
|
+
# 16 = self, value * 15
|
543
|
+
|
544
|
+
def test_generate_raw_arity_0
|
545
|
+
src = "VALUE y(VALUE self) {blah;}"
|
546
|
+
|
547
|
+
expected = "static VALUE y(VALUE self) {blah;}"
|
548
|
+
|
549
|
+
util_generate_raw(src, expected)
|
550
|
+
end
|
551
|
+
|
552
|
+
def test_generate_arity_0
|
553
|
+
src = "int y() { do_something; return 42; }"
|
554
|
+
|
555
|
+
expected = "static VALUE y(VALUE self) {\n do_something; return INT2FIX(42); }"
|
556
|
+
|
557
|
+
util_generate(src, expected)
|
558
|
+
end
|
559
|
+
|
560
|
+
def test_generate_arity_0_no_return
|
561
|
+
src = "void y() { do_something; }"
|
562
|
+
|
563
|
+
expected = "static VALUE y(VALUE self) {\n do_something;\nreturn Qnil;\n}"
|
564
|
+
|
565
|
+
util_generate(src, expected)
|
566
|
+
end
|
567
|
+
|
568
|
+
def test_generate_arity_0_void_return
|
569
|
+
src = "void y(void) {go_do_something_external;}"
|
570
|
+
|
571
|
+
expected = "static VALUE y(VALUE self) {
|
572
|
+
go_do_something_external;\nreturn Qnil;\n}"
|
573
|
+
|
574
|
+
util_generate(src, expected)
|
575
|
+
end
|
576
|
+
|
577
|
+
def test_generate_arity_0_int_return
|
578
|
+
src = "int x() {return 42}"
|
579
|
+
|
580
|
+
expected = "static VALUE x(VALUE self) {
|
581
|
+
return INT2FIX(42)}"
|
582
|
+
|
583
|
+
util_generate(src, expected)
|
584
|
+
end
|
585
|
+
|
586
|
+
def test_generate_raw_arity_1
|
587
|
+
src = "VALUE y(VALUE self, VALUE obj) {blah;}"
|
588
|
+
|
589
|
+
expected = "static VALUE y(VALUE self, VALUE obj) {blah;}"
|
590
|
+
|
591
|
+
util_generate_raw(src, expected)
|
592
|
+
end
|
593
|
+
|
594
|
+
def test_generate_arity_1
|
595
|
+
src = "int y(int x) {blah; return x+1;}"
|
596
|
+
|
597
|
+
expected = "static VALUE y(VALUE self, VALUE _x) {\n int x = FIX2INT(_x);\nblah; return INT2FIX(x+1);}"
|
598
|
+
|
599
|
+
util_generate(src, expected)
|
600
|
+
end
|
601
|
+
|
602
|
+
def test_generate_arity_1_no_return
|
603
|
+
src = "void y(int x) {blah;}"
|
604
|
+
|
605
|
+
expected = "static VALUE y(VALUE self, VALUE _x) {\n int x = FIX2INT(_x);\nblah;\nreturn Qnil;\n}"
|
606
|
+
|
607
|
+
util_generate(src, expected)
|
608
|
+
end
|
609
|
+
|
610
|
+
def test_generate_raw_arity_2
|
611
|
+
src = "VALUE func(VALUE self, VALUE obj1, VALUE obj2) {blah;}"
|
612
|
+
|
613
|
+
expected = "static VALUE func(VALUE self, VALUE obj1, VALUE obj2) {blah;}"
|
614
|
+
|
615
|
+
util_generate_raw(src, expected)
|
616
|
+
end
|
617
|
+
|
618
|
+
def test_generate_arity_2
|
619
|
+
src = "int func(int x, int y) {blah; return x+y;}"
|
620
|
+
|
621
|
+
expected = "static VALUE func(VALUE self, VALUE _x, VALUE _y) {\n int x = FIX2INT(_x);\n int y = FIX2INT(_y);\nblah; return INT2FIX(x+y);}"
|
622
|
+
|
623
|
+
util_generate(src, expected)
|
624
|
+
end
|
625
|
+
|
626
|
+
def test_generate_raw_arity_3
|
627
|
+
src = "VALUE func(VALUE self, VALUE obj1, VALUE obj2, VALUE obj3) {blah;}"
|
628
|
+
|
629
|
+
expected = "static VALUE func(VALUE self, VALUE obj1, VALUE obj2, VALUE obj3) {blah;}"
|
630
|
+
|
631
|
+
util_generate_raw(src, expected)
|
632
|
+
end
|
633
|
+
|
634
|
+
def test_generate_arity_too_many
|
635
|
+
src = "int too_many(int a1, int a2, int a3, int a4, int a5, int a6, int a7, int a8, int a9, int aA, int aB, int aC, int aD, int aE, int aF, int ugh) {
|
636
|
+
int q = v + w;
|
637
|
+
return q+x+y+z;
|
638
|
+
}"
|
639
|
+
|
640
|
+
assert_raises ArgumentError do
|
641
|
+
@builder.generate src, true
|
642
|
+
end
|
643
|
+
end
|
644
|
+
|
645
|
+
def test_generate_comments
|
646
|
+
src = "// stupid cpp comment
|
647
|
+
/* stupid c comment */
|
648
|
+
int
|
649
|
+
add(int x, int y) { // add two numbers
|
650
|
+
return x+y;
|
651
|
+
}
|
652
|
+
"
|
653
|
+
|
654
|
+
expected = "static VALUE add(VALUE self, VALUE _x, VALUE _y) {
|
655
|
+
int x = FIX2INT(_x);
|
656
|
+
int y = FIX2INT(_y);
|
657
|
+
|
658
|
+
return INT2FIX(x+y);
|
659
|
+
}
|
660
|
+
"
|
661
|
+
|
662
|
+
util_generate(src, expected)
|
663
|
+
end
|
664
|
+
|
665
|
+
def test_generate_local_header
|
666
|
+
src = "// stupid cpp comment
|
667
|
+
#include \"header\"
|
668
|
+
/* stupid c comment */
|
669
|
+
int
|
670
|
+
add(int x, int y) { // add two numbers
|
671
|
+
return x+y;
|
672
|
+
}
|
673
|
+
"
|
674
|
+
# FIX: should be 2 spaces before the return. Can't find problem.
|
675
|
+
expected = "#include \"header\"
|
676
|
+
static VALUE add(VALUE self, VALUE _x, VALUE _y) {
|
677
|
+
int x = FIX2INT(_x);
|
678
|
+
int y = FIX2INT(_y);
|
679
|
+
|
680
|
+
return INT2FIX(x+y);
|
681
|
+
}
|
682
|
+
"
|
683
|
+
util_generate(src, expected)
|
684
|
+
end
|
685
|
+
|
686
|
+
def test_generate_map_name
|
687
|
+
src = "VALUE y_equals(VALUE self) {blah;}"
|
688
|
+
|
689
|
+
expected = "static VALUE y_equals(VALUE self) {blah;}"
|
690
|
+
|
691
|
+
util_generate_raw(src, expected)
|
692
|
+
|
693
|
+
assert_equal [-1, nil, 'y='], @builder.sig['y_equals']
|
694
|
+
end
|
695
|
+
|
696
|
+
def test_generate_system_header
|
697
|
+
src = "// stupid cpp comment
|
698
|
+
#include <header>
|
699
|
+
/* stupid c comment */
|
700
|
+
int
|
701
|
+
add(int x, int y) { // add two numbers
|
702
|
+
return x+y;
|
703
|
+
}
|
704
|
+
"
|
705
|
+
expected = "#include <header>
|
706
|
+
static VALUE add(VALUE self, VALUE _x, VALUE _y) {
|
707
|
+
int x = FIX2INT(_x);
|
708
|
+
int y = FIX2INT(_y);
|
709
|
+
|
710
|
+
return INT2FIX(x+y);
|
711
|
+
}
|
712
|
+
"
|
713
|
+
util_generate(src, expected)
|
714
|
+
end
|
715
|
+
|
716
|
+
def test_generate_wonky_return
|
717
|
+
src = "unsigned\nlong z(void) {return 42}"
|
718
|
+
|
719
|
+
expected = "static VALUE z(VALUE self) {
|
720
|
+
return ULONG2NUM(42)}"
|
721
|
+
|
722
|
+
util_generate(src, expected)
|
723
|
+
end
|
724
|
+
|
725
|
+
def test_generate_compact
|
726
|
+
src = "int add(int x, int y) {return x+y}"
|
727
|
+
|
728
|
+
expected = "static VALUE add(VALUE self, VALUE _x, VALUE _y) {
|
729
|
+
int x = FIX2INT(_x);
|
730
|
+
int y = FIX2INT(_y);
|
731
|
+
return INT2FIX(x+y)}"
|
732
|
+
|
733
|
+
util_generate(src, expected)
|
734
|
+
end
|
735
|
+
|
736
|
+
def test_generate_char_star_normalize
|
737
|
+
src = "char\n\*\n blah( char*s) {puts(s); return s}"
|
738
|
+
|
739
|
+
expected = "static VALUE blah(VALUE self, VALUE _s) {
|
740
|
+
char * s = StringValuePtr(_s);
|
741
|
+
puts(s); return rb_str_new2(s)}"
|
742
|
+
|
743
|
+
util_generate(src, expected)
|
744
|
+
end
|
745
|
+
|
746
|
+
def test_generate_ext
|
747
|
+
@builder.c_singleton "VALUE allocate() { return Qnil; }"
|
748
|
+
|
749
|
+
@builder.c "VALUE my_method() { return Qnil; }"
|
750
|
+
|
751
|
+
@builder.prefix "static int x;"
|
752
|
+
|
753
|
+
windoze = "\n __declspec(dllexport)" if Inline::WINDOZE
|
754
|
+
|
755
|
+
expected = <<-EXT
|
756
|
+
#include "ruby.h"
|
757
|
+
|
758
|
+
static int x;
|
759
|
+
|
760
|
+
# line N "#{$test_inline_path}"
|
761
|
+
static VALUE allocate(VALUE self) {
|
762
|
+
return (Qnil); }
|
763
|
+
|
764
|
+
# line N "#{$test_inline_path}"
|
765
|
+
static VALUE my_method(VALUE self) {
|
766
|
+
return (Qnil); }
|
767
|
+
|
768
|
+
|
769
|
+
#ifdef __cplusplus
|
770
|
+
extern \"C\" {
|
771
|
+
#endif#{windoze}
|
772
|
+
void Init_Inline_TestInline__TestC_3ab8c09639e499394bb1f0a0194a839f() {
|
773
|
+
VALUE c = rb_cObject;
|
774
|
+
c = rb_const_get(c, rb_intern("TestInline"));
|
775
|
+
c = rb_const_get(c, rb_intern("TestC"));
|
776
|
+
|
777
|
+
rb_define_alloc_func(c, (VALUE(*)(VALUE))allocate);
|
778
|
+
rb_define_method(c, "my_method", (VALUE(*)(ANYARGS))my_method, 0);
|
779
|
+
|
780
|
+
}
|
781
|
+
#ifdef __cplusplus
|
782
|
+
}
|
783
|
+
#endif
|
784
|
+
EXT
|
785
|
+
|
786
|
+
assert_equal expected, util_strip_lines(@builder.generate_ext)
|
787
|
+
end
|
788
|
+
|
789
|
+
def test_generate_ext_bad_allocate
|
790
|
+
@builder.c_singleton "VALUE allocate(VALUE bad) { return Qnil; }"
|
791
|
+
|
792
|
+
e = assert_raises RuntimeError do
|
793
|
+
@builder.generate_ext
|
794
|
+
end
|
795
|
+
|
796
|
+
assert_equal 'TestInline::TestC::allocate must have an arity of zero',
|
797
|
+
e.message
|
798
|
+
end
|
799
|
+
|
800
|
+
def test_c
|
801
|
+
result = @builder.c "int add(int a, int b) { return a + b; }"
|
802
|
+
|
803
|
+
expected = "# line N \"#{$0}\"\nstatic VALUE add(VALUE self, VALUE _a, VALUE _b) {\n int a = FIX2INT(_a);\n int b = FIX2INT(_b);\n return INT2FIX(a + b); }"
|
804
|
+
|
805
|
+
result = util_strip_lines result
|
806
|
+
|
807
|
+
assert_equal expected, result
|
808
|
+
assert_equal [expected], util_strip_lines(@builder.src)
|
809
|
+
end
|
810
|
+
|
811
|
+
def test_c_raw
|
812
|
+
src = "static VALUE answer_raw(int argc, VALUE *argv, VALUE self) { return INT2NUM(42); }"
|
813
|
+
|
814
|
+
result = @builder.c_raw src.dup
|
815
|
+
result = util_strip_lines result
|
816
|
+
|
817
|
+
expected = "# line N \"#{$0}\"\n" + src
|
818
|
+
|
819
|
+
assert_equal expected, result
|
820
|
+
assert_equal [expected], util_strip_lines(@builder.src)
|
821
|
+
end
|
822
|
+
|
823
|
+
def test_map_c_const
|
824
|
+
@builder.map_c_const :C_NAME => :int
|
825
|
+
|
826
|
+
expected = [
|
827
|
+
" rb_define_const(c, \"C_NAME\", INT2FIX(C_NAME));"
|
828
|
+
]
|
829
|
+
|
830
|
+
assert_equal expected, @builder.init_extra
|
831
|
+
end
|
832
|
+
|
833
|
+
def test_map_c_const_named
|
834
|
+
@builder.map_c_const :C_NAME => [:int, :RUBY_NAME]
|
835
|
+
|
836
|
+
expected = [
|
837
|
+
" rb_define_const(c, \"RUBY_NAME\", INT2FIX(C_NAME));"
|
838
|
+
]
|
839
|
+
|
840
|
+
assert_equal expected, @builder.init_extra
|
841
|
+
end
|
842
|
+
|
843
|
+
# TODO: fix ?
|
844
|
+
def util_simple_code(klassname, c_src)
|
845
|
+
result = "
|
846
|
+
require 'inline'
|
847
|
+
|
848
|
+
class #{klassname}
|
849
|
+
inline do |builder|
|
850
|
+
builder.c <<-EOC
|
851
|
+
#{c_src}
|
852
|
+
EOC
|
853
|
+
end
|
854
|
+
end"
|
855
|
+
result
|
856
|
+
end
|
857
|
+
|
858
|
+
def util_test_build(src)
|
859
|
+
tempfile = Tempfile.new("util_test_build")
|
860
|
+
tempfile.write src
|
861
|
+
tempfile.close
|
862
|
+
rb_file = tempfile.path + ".rb"
|
863
|
+
File.rename tempfile.path, rb_file
|
864
|
+
begin
|
865
|
+
Kernel.module_eval { require rb_file }
|
866
|
+
yield if block_given?
|
867
|
+
ensure
|
868
|
+
File.unlink rb_file
|
869
|
+
end
|
870
|
+
end
|
871
|
+
|
872
|
+
def test_build_good
|
873
|
+
skip "https://github.com/MagLev/maglev/issues/231" if maglev?
|
874
|
+
|
875
|
+
code = util_simple_code(:DumbTest1, "long dumbpi() { return 314; }")
|
876
|
+
util_test_build(code) do
|
877
|
+
result = DumbTest1.new.dumbpi
|
878
|
+
assert_equal(314, result)
|
879
|
+
end
|
880
|
+
end
|
881
|
+
|
882
|
+
def test_build_bad
|
883
|
+
code = util_simple_code(:DumbTest2, "void should_puke() { 1+1 2+2 }")
|
884
|
+
assert_raises(CompilationError) do
|
885
|
+
util_test_build(code) do
|
886
|
+
flunk
|
887
|
+
end
|
888
|
+
end
|
889
|
+
end
|
890
|
+
|
891
|
+
def util_strip_comments(input)
|
892
|
+
expect = 'line 1
|
893
|
+
|
894
|
+
#if 0
|
895
|
+
line 2
|
896
|
+
#endif
|
897
|
+
line 3
|
898
|
+
|
899
|
+
'
|
900
|
+
|
901
|
+
assert_equal expect, @builder.strip_comments(input)
|
902
|
+
end
|
903
|
+
|
904
|
+
def test_strip_comments_cpp
|
905
|
+
input = 'line 1
|
906
|
+
|
907
|
+
#if 0
|
908
|
+
line 2
|
909
|
+
#endif
|
910
|
+
// 1 comment
|
911
|
+
// 2 comment
|
912
|
+
line 3 // trailing comment
|
913
|
+
|
914
|
+
'
|
915
|
+
util_strip_comments(input)
|
916
|
+
end
|
917
|
+
|
918
|
+
def test_strip_comments_c
|
919
|
+
input = 'line 1
|
920
|
+
|
921
|
+
#if 0
|
922
|
+
line 2
|
923
|
+
#endif
|
924
|
+
/*
|
925
|
+
* 1 comment
|
926
|
+
* 2 comment
|
927
|
+
*/
|
928
|
+
line 3 /* trailing comment */
|
929
|
+
|
930
|
+
'
|
931
|
+
util_strip_comments(input)
|
932
|
+
end
|
933
|
+
|
934
|
+
def test_load
|
935
|
+
# totally tested by test_build
|
936
|
+
end
|
937
|
+
|
938
|
+
end # class TestC
|
939
|
+
end # class TestInline
|
940
|
+
|
941
|
+
module Foo
|
942
|
+
class Bar
|
943
|
+
# inline stuff will go here...
|
944
|
+
end
|
945
|
+
end
|
946
|
+
|
947
|
+
$test_module_code = <<-EOR
|
948
|
+
module Foo
|
949
|
+
class Bar
|
950
|
+
inline do |builder|
|
951
|
+
builder.c <<-EOC
|
952
|
+
static int forty_two_instance() { return 42; }
|
953
|
+
EOC
|
954
|
+
builder.c_singleton <<-EOC
|
955
|
+
static int twenty_four_class() { return 24; }
|
956
|
+
EOC
|
957
|
+
end
|
958
|
+
end
|
959
|
+
end
|
960
|
+
EOR
|
961
|
+
|
962
|
+
$test_module_code2 = <<-EOR
|
963
|
+
require 'inline'
|
964
|
+
|
965
|
+
# Demonstrates native functions in nested classes and
|
966
|
+
# extending a class more than once from different ruby
|
967
|
+
# source files
|
968
|
+
module Foo
|
969
|
+
class Bar
|
970
|
+
inline do |builder|
|
971
|
+
builder.c <<-EOC
|
972
|
+
int twelve_instance() { return 12; }
|
973
|
+
EOC
|
974
|
+
builder.c_singleton <<-EOC
|
975
|
+
int twelve_class() { return 12; }
|
976
|
+
EOC
|
977
|
+
end
|
978
|
+
end
|
979
|
+
end
|
980
|
+
EOR
|
981
|
+
|
982
|
+
class TestModule < InlineTestCase
|
983
|
+
def test_nested
|
984
|
+
skip "https://github.com/MagLev/maglev/issues/231" if maglev?
|
985
|
+
|
986
|
+
Object.class_eval $test_module_code
|
987
|
+
fb = Foo::Bar.new
|
988
|
+
assert_equal(42, fb.forty_two_instance)
|
989
|
+
assert_equal(24, Foo::Bar.twenty_four_class)
|
990
|
+
|
991
|
+
tempfile = Tempfile.new("test_inline_nested")
|
992
|
+
tempfile.write($test_module_code2)
|
993
|
+
tempfile.flush
|
994
|
+
tempfile.rewind
|
995
|
+
|
996
|
+
FileUtils.cp tempfile.path, "#{tempfile.path}.rb"
|
997
|
+
|
998
|
+
require "#{tempfile.path}.rb"
|
999
|
+
assert_equal(12, fb.twelve_instance)
|
1000
|
+
assert_equal(12, Foo::Bar.twelve_class)
|
1001
|
+
|
1002
|
+
FileUtils.rm "#{tempfile.path}.rb"
|
1003
|
+
end
|
1004
|
+
|
1005
|
+
def test_argument_check_good
|
1006
|
+
skip "https://github.com/MagLev/maglev/issues/231" if maglev?
|
1007
|
+
|
1008
|
+
util_arity_check
|
1009
|
+
fb = Foo::Bar.new
|
1010
|
+
assert_equal 13, fb.arity6(1, 2, 3, 4, 5, "blah")
|
1011
|
+
end
|
1012
|
+
|
1013
|
+
def test_argument_check_fewer
|
1014
|
+
skip "https://github.com/MagLev/maglev/issues/231" if maglev?
|
1015
|
+
|
1016
|
+
util_arity_check
|
1017
|
+
fb = Foo::Bar.new
|
1018
|
+
|
1019
|
+
assert_raises ArgumentError do
|
1020
|
+
fb.arity6(1, 2, 3)
|
1021
|
+
end
|
1022
|
+
end
|
1023
|
+
|
1024
|
+
def test_argument_check_more
|
1025
|
+
skip "https://github.com/MagLev/maglev/issues/231" if maglev?
|
1026
|
+
|
1027
|
+
util_arity_check
|
1028
|
+
fb = Foo::Bar.new
|
1029
|
+
assert_raises ArgumentError do
|
1030
|
+
fb.arity6(1, 2, 3, 4, 5, "blah", :extra)
|
1031
|
+
end
|
1032
|
+
end
|
1033
|
+
|
1034
|
+
def test_inline
|
1035
|
+
skip "https://github.com/MagLev/maglev/issues/231" if maglev?
|
1036
|
+
|
1037
|
+
self.class.inline(:C) do |builder|
|
1038
|
+
builder.c "int add(int a, int b) { return a + b; }"
|
1039
|
+
end
|
1040
|
+
assert(test(?d, Inline.directory),
|
1041
|
+
"inline dir should have been created")
|
1042
|
+
matches = Dir[File.join(Inline.directory, "Inline_TestModule_*.c")]
|
1043
|
+
assert_equal(1, matches.length, "Source should have been created")
|
1044
|
+
library_file = matches.first.gsub(/\.c$/) { "." + RbConfig::CONFIG["DLEXT"] }
|
1045
|
+
assert(test(?f, library_file),
|
1046
|
+
"Library file should have been created")
|
1047
|
+
end
|
1048
|
+
|
1049
|
+
def util_arity_check
|
1050
|
+
methods = Foo::Bar.public_instance_methods.map { |s| s.to_s }
|
1051
|
+
|
1052
|
+
unless methods.include? "arity6" then
|
1053
|
+
Foo::Bar.inline do |builder|
|
1054
|
+
builder.include "<string.h>"
|
1055
|
+
builder.c "int arity6(int u, int v, int w, int x, int y, char * z) { return x + y + strlen(z); }"
|
1056
|
+
end
|
1057
|
+
end
|
1058
|
+
end
|
1059
|
+
end
|