gir_ffi 0.0.1
Sign up to get free protection for your applications and to get access to all the features.
- data/DESIGN.rdoc +54 -0
- data/History.txt +3 -0
- data/README.rdoc +59 -0
- data/Rakefile +21 -0
- data/TODO.rdoc +40 -0
- data/examples/01_empty_window.rb +15 -0
- data/examples/02_hello_world.rb +30 -0
- data/examples/03_upgraded_hello_world.rb +45 -0
- data/examples/demo_ffi_inherited_layout.rb +21 -0
- data/examples/demo_ffi_nested_struct.rb +17 -0
- data/examples/demo_ffi_safe_inherited_layout.rb +43 -0
- data/examples/hard_coded.rb +144 -0
- data/lib/gir_ffi.rb +47 -0
- data/lib/gir_ffi/allocation_helper.rb +12 -0
- data/lib/gir_ffi/arg_helper.rb +77 -0
- data/lib/gir_ffi/base.rb +23 -0
- data/lib/gir_ffi/builder.rb +159 -0
- data/lib/gir_ffi/builder_helper.rb +32 -0
- data/lib/gir_ffi/class_base.rb +11 -0
- data/lib/gir_ffi/class_builder.rb +116 -0
- data/lib/gir_ffi/constructor_definition_builder.rb +20 -0
- data/lib/gir_ffi/function_definition_builder.rb +148 -0
- data/lib/gir_ffi/g_error.rb +8 -0
- data/lib/gir_ffi/g_type.rb +14 -0
- data/lib/gir_ffi/i_arg_info.rb +16 -0
- data/lib/gir_ffi/i_base_info.rb +45 -0
- data/lib/gir_ffi/i_callable_info.rb +18 -0
- data/lib/gir_ffi/i_callback_info.rb +7 -0
- data/lib/gir_ffi/i_constant_info.rb +6 -0
- data/lib/gir_ffi/i_enum_info.rb +13 -0
- data/lib/gir_ffi/i_field_info.rb +10 -0
- data/lib/gir_ffi/i_flags_info.rb +5 -0
- data/lib/gir_ffi/i_function_info.rb +16 -0
- data/lib/gir_ffi/i_interface_info.rb +7 -0
- data/lib/gir_ffi/i_object_info.rb +50 -0
- data/lib/gir_ffi/i_property_info.rb +7 -0
- data/lib/gir_ffi/i_registered_type_info.rb +8 -0
- data/lib/gir_ffi/i_repository.rb +108 -0
- data/lib/gir_ffi/i_signal_info.rb +7 -0
- data/lib/gir_ffi/i_struct_info.rb +22 -0
- data/lib/gir_ffi/i_type_info.rb +25 -0
- data/lib/gir_ffi/i_union_info.rb +7 -0
- data/lib/gir_ffi/i_value_info.rb +8 -0
- data/lib/gir_ffi/i_vfunc_info.rb +7 -0
- data/lib/gir_ffi/lib.rb +174 -0
- data/lib/gir_ffi/lib_c.rb +11 -0
- data/lib/gir_ffi/method_missing_definition_builder.rb +62 -0
- data/lib/gir_ffi/module_builder.rb +66 -0
- data/lib/gir_ffi/overrides/gtk.rb +12 -0
- data/lib/gir_ffi/version.rb +4 -0
- data/tasks/bones.rake +87 -0
- data/tasks/notes.rake +134 -0
- data/tasks/post_load.rake +25 -0
- data/tasks/setup.rb +138 -0
- data/tasks/test.rake +22 -0
- data/test/arg_helper_test.rb +112 -0
- data/test/builder_test.rb +328 -0
- data/test/constructor_definition_builder_test.rb +19 -0
- data/test/function_definition_builder_test.rb +60 -0
- data/test/g_type_test.rb +22 -0
- data/test/girffi_test.rb +11 -0
- data/test/gtk_overrides_test.rb +22 -0
- data/test/i_repository_test.rb +54 -0
- data/test/test_helper.rb +39 -0
- metadata +174 -0
data/tasks/notes.rake
ADDED
@@ -0,0 +1,134 @@
|
|
1
|
+
# The following code is copied straight from Bones 2.5.1
|
2
|
+
#
|
3
|
+
|
4
|
+
begin
|
5
|
+
require 'facets/ansicode'
|
6
|
+
HAVE_COLOR = true
|
7
|
+
rescue LoadError
|
8
|
+
HAVE_COLOR = false
|
9
|
+
end
|
10
|
+
|
11
|
+
module Bones
|
12
|
+
|
13
|
+
# A helper class used to find and display any annotations in a collection of
|
14
|
+
# project files.
|
15
|
+
#
|
16
|
+
class AnnotationExtractor
|
17
|
+
|
18
|
+
class Annotation < Struct.new(:line, :tag, :text)
|
19
|
+
# Returns a string representation of the annotation. If the
|
20
|
+
# <tt>:tag</tt> parameter is given as +true+, then the annotation tag
|
21
|
+
# will be included in the string.
|
22
|
+
#
|
23
|
+
def to_s( opts = {} )
|
24
|
+
s = "[%3d] " % line
|
25
|
+
s << "[#{tag}] " if opts[:tag]
|
26
|
+
s << text
|
27
|
+
end
|
28
|
+
end
|
29
|
+
|
30
|
+
# Enumerate all the annoations for the given _project_ and _tag_. This
|
31
|
+
# will search for all athe annotations and display them on standard
|
32
|
+
# output.
|
33
|
+
#
|
34
|
+
def self.enumerate( project, tag, id = nil, opts = {} )
|
35
|
+
extractor = new(project, tag, id)
|
36
|
+
extractor.display(extractor.find, opts)
|
37
|
+
end
|
38
|
+
|
39
|
+
attr_reader :tag, :project, :id
|
40
|
+
|
41
|
+
# Creates a new annotation extractor configured to use the _project_ open
|
42
|
+
# strcut and to search for the given _tag_ (which can be more than one tag
|
43
|
+
# via a regular expression 'or' operation -- i.e. THIS|THAT|OTHER)
|
44
|
+
#
|
45
|
+
def initialize( project, tag, id)
|
46
|
+
@project = project
|
47
|
+
@tag = tag
|
48
|
+
@id = @id_rgxp = nil
|
49
|
+
|
50
|
+
unless id.nil? or id.empty?
|
51
|
+
@id = id
|
52
|
+
@id_rgxp = Regexp.new(Regexp.escape(id), Regexp::IGNORECASE)
|
53
|
+
end
|
54
|
+
end
|
55
|
+
|
56
|
+
# Iterate over all the files in the project and extract annotations from
|
57
|
+
# the those files. Returns the results as a hash for display.
|
58
|
+
#
|
59
|
+
def find
|
60
|
+
results = {}
|
61
|
+
rgxp = %r/(#{tag}):?\s*(.*?)(?:\s*(?:-?%>|\*+\/))?$/o
|
62
|
+
|
63
|
+
extensions = project.notes.extensions.dup
|
64
|
+
exclude = if project.notes.exclude.empty? then nil
|
65
|
+
else Regexp.new(project.notes.exclude.join('|')) end
|
66
|
+
|
67
|
+
manifest.each do |fn|
|
68
|
+
next if exclude && exclude =~ fn
|
69
|
+
next unless extensions.include? File.extname(fn)
|
70
|
+
results.update(extract_annotations_from(fn, rgxp))
|
71
|
+
end
|
72
|
+
|
73
|
+
results
|
74
|
+
end
|
75
|
+
|
76
|
+
# Extract any annotations from the given _file_ using the regular
|
77
|
+
# expression _pattern_ provided.
|
78
|
+
#
|
79
|
+
def extract_annotations_from( file, pattern )
|
80
|
+
lineno = 0
|
81
|
+
result = File.readlines(file).inject([]) do |list, line|
|
82
|
+
lineno += 1
|
83
|
+
next list unless m = pattern.match(line)
|
84
|
+
next list << Annotation.new(lineno, m[1], m[2]) unless id
|
85
|
+
|
86
|
+
text = m[2]
|
87
|
+
if text =~ @id_rgxp
|
88
|
+
text.gsub!(@id_rgxp) {|str| ANSICode.green(str)} if HAVE_COLOR
|
89
|
+
list << Annotation.new(lineno, m[1], text)
|
90
|
+
end
|
91
|
+
list
|
92
|
+
end
|
93
|
+
result.empty? ? {} : { file => result }
|
94
|
+
end
|
95
|
+
|
96
|
+
# Print the results of the annotation extraction to the screen. If the
|
97
|
+
# <tt>:tags</tt> option is set to +true+, then the annotation tag will be
|
98
|
+
# displayed.
|
99
|
+
#
|
100
|
+
def display( results, opts = {} )
|
101
|
+
results.keys.sort.each do |file|
|
102
|
+
puts "#{file}:"
|
103
|
+
results[file].each do |note|
|
104
|
+
puts " * #{note.to_s(opts)}"
|
105
|
+
end
|
106
|
+
puts
|
107
|
+
end
|
108
|
+
end
|
109
|
+
|
110
|
+
end # class AnnotationExtractor
|
111
|
+
end # module Bones
|
112
|
+
|
113
|
+
desc "Enumerate all annotations"
|
114
|
+
task :notes do |t|
|
115
|
+
id = if t.application.top_level_tasks.length > 1
|
116
|
+
t.application.top_level_tasks.slice!(1..-1).join(' ')
|
117
|
+
end
|
118
|
+
Bones::AnnotationExtractor.enumerate(
|
119
|
+
PROJ, PROJ.notes.tags.join('|'), id, :tag => true)
|
120
|
+
end
|
121
|
+
|
122
|
+
namespace :notes do
|
123
|
+
PROJ.notes.tags.each do |tag|
|
124
|
+
desc "Enumerate all #{tag} annotations"
|
125
|
+
task tag.downcase.to_sym do |t|
|
126
|
+
id = if t.application.top_level_tasks.length > 1
|
127
|
+
t.application.top_level_tasks.slice!(1..-1).join(' ')
|
128
|
+
end
|
129
|
+
Bones::AnnotationExtractor.enumerate(PROJ, tag, id)
|
130
|
+
end
|
131
|
+
end
|
132
|
+
end
|
133
|
+
|
134
|
+
# EOF
|
@@ -0,0 +1,25 @@
|
|
1
|
+
|
2
|
+
# This file does not define any rake tasks. It is used to load some project
|
3
|
+
# settings if they are not defined by the user.
|
4
|
+
|
5
|
+
PROJ.exclude << ["^#{Regexp.escape(PROJ.ignore_file)}$"]
|
6
|
+
|
7
|
+
flatten_arrays = lambda do |this,os|
|
8
|
+
os.instance_variable_get(:@table).each do |key,val|
|
9
|
+
next if key == :dependencies \
|
10
|
+
or key == :development_dependencies
|
11
|
+
case val
|
12
|
+
when Array; val.flatten!
|
13
|
+
when OpenStruct; this.call(this,val)
|
14
|
+
end
|
15
|
+
end
|
16
|
+
end
|
17
|
+
flatten_arrays.call(flatten_arrays,PROJ)
|
18
|
+
|
19
|
+
PROJ.changes ||= paragraphs_of(PROJ.history_file, 0..1).join("\n\n")
|
20
|
+
|
21
|
+
PROJ.description ||= paragraphs_of(PROJ.readme_file, 'description').join("\n\n")
|
22
|
+
|
23
|
+
PROJ.summary ||= PROJ.description.split('.').first
|
24
|
+
|
25
|
+
# EOF
|
data/tasks/setup.rb
ADDED
@@ -0,0 +1,138 @@
|
|
1
|
+
|
2
|
+
require 'rake'
|
3
|
+
require 'rake/clean'
|
4
|
+
require 'fileutils'
|
5
|
+
require 'ostruct'
|
6
|
+
require 'find'
|
7
|
+
|
8
|
+
# TODO: Clean up bones' task set to remove unwanted parts.
|
9
|
+
|
10
|
+
PROJ = OpenStruct.new(
|
11
|
+
# Project Defaults
|
12
|
+
:name => nil,
|
13
|
+
:summary => nil,
|
14
|
+
:description => nil,
|
15
|
+
:changes => nil,
|
16
|
+
:authors => nil,
|
17
|
+
:email => nil,
|
18
|
+
:url => "\000",
|
19
|
+
:version => ENV['VERSION'] || '0.0.0',
|
20
|
+
:exclude => %w(tmp$ bak$ ~$ CVS \.svn/ \.git/ ^pkg/),
|
21
|
+
:release_name => ENV['RELEASE'],
|
22
|
+
|
23
|
+
# System Defaults
|
24
|
+
:ruby_opts => %w(-w),
|
25
|
+
:libs => [],
|
26
|
+
:history_file => 'History.txt',
|
27
|
+
:readme_file => 'README.txt',
|
28
|
+
:ignore_file => '.bnsignore',
|
29
|
+
|
30
|
+
# File Annotations
|
31
|
+
:notes => OpenStruct.new(
|
32
|
+
:exclude => %w(^tasks/setup\.rb$),
|
33
|
+
:extensions => %w(.txt .rb .erb .rdoc) << '',
|
34
|
+
:tags => %w(FIXME OPTIMIZE TODO)
|
35
|
+
),
|
36
|
+
|
37
|
+
# Test::Unit
|
38
|
+
:test => OpenStruct.new(
|
39
|
+
:files => FileList['test/**/*_test.rb'],
|
40
|
+
:file => 'test/all.rb',
|
41
|
+
:opts => []
|
42
|
+
)
|
43
|
+
)
|
44
|
+
|
45
|
+
# Load the other rake files in the tasks folder
|
46
|
+
tasks_dir = File.expand_path(File.dirname(__FILE__))
|
47
|
+
post_load_fn = File.join(tasks_dir, 'post_load.rake')
|
48
|
+
rakefiles = Dir.glob(File.join(tasks_dir, '*.rake')).sort
|
49
|
+
rakefiles.unshift(rakefiles.delete(post_load_fn)).compact!
|
50
|
+
import(*rakefiles)
|
51
|
+
|
52
|
+
# Setup the project libraries
|
53
|
+
%w(lib ext).each {|dir| PROJ.libs << dir if test ?d, dir}
|
54
|
+
|
55
|
+
%w(facets/ansicode).each do |lib|
|
56
|
+
begin
|
57
|
+
require lib
|
58
|
+
Object.instance_eval {const_set "HAVE_#{lib.tr('/','_').upcase}", true}
|
59
|
+
rescue LoadError
|
60
|
+
Object.instance_eval {const_set "HAVE_#{lib.tr('/','_').upcase}", false}
|
61
|
+
end
|
62
|
+
end
|
63
|
+
|
64
|
+
# Reads a file at +path+ and spits out an array of the +paragraphs+
|
65
|
+
# specified.
|
66
|
+
#
|
67
|
+
# changes = paragraphs_of('History.txt', 0..1).join("\n\n")
|
68
|
+
# summary, *description = paragraphs_of('README.txt', 3, 3..8)
|
69
|
+
#
|
70
|
+
def paragraphs_of( path, *paragraphs )
|
71
|
+
title = String === paragraphs.first ? paragraphs.shift : nil
|
72
|
+
ary = File.read(path).delete("\r").split(/\n\n+/)
|
73
|
+
|
74
|
+
result = if title
|
75
|
+
tmp, matching = [], false
|
76
|
+
rgxp = %r/^=+\s*#{Regexp.escape(title)}/i
|
77
|
+
paragraphs << (0..-1) if paragraphs.empty?
|
78
|
+
|
79
|
+
ary.each do |val|
|
80
|
+
if val =~ rgxp
|
81
|
+
break if matching
|
82
|
+
matching = true
|
83
|
+
rgxp = %r/^=+/i
|
84
|
+
elsif matching
|
85
|
+
tmp << val
|
86
|
+
end
|
87
|
+
end
|
88
|
+
tmp
|
89
|
+
else ary end
|
90
|
+
|
91
|
+
result.values_at(*paragraphs)
|
92
|
+
end
|
93
|
+
|
94
|
+
# Adds the given arguments to the include path if they are not already there
|
95
|
+
#
|
96
|
+
def ensure_in_path( *args )
|
97
|
+
args.each do |path|
|
98
|
+
path = File.expand_path(path)
|
99
|
+
$:.unshift(path) if test(?d, path) and not $:.include?(path)
|
100
|
+
end
|
101
|
+
end
|
102
|
+
|
103
|
+
# Scans the current working directory and creates a list of files that are
|
104
|
+
# candidates to be in the manifest.
|
105
|
+
#
|
106
|
+
def manifest
|
107
|
+
files = []
|
108
|
+
exclude = PROJ.exclude.dup
|
109
|
+
comment = %r/^\s*#/
|
110
|
+
|
111
|
+
# process the ignore file and add the items there to the exclude list
|
112
|
+
if test(?f, PROJ.ignore_file)
|
113
|
+
ary = []
|
114
|
+
File.readlines(PROJ.ignore_file).each do |line|
|
115
|
+
next if line =~ comment
|
116
|
+
line.chomp!
|
117
|
+
line.strip!
|
118
|
+
next if line.nil? or line.empty?
|
119
|
+
|
120
|
+
glob = line =~ %r/\*\./ ? File.join('**', line) : line
|
121
|
+
Dir.glob(glob).each {|fn| ary << "^#{Regexp.escape(fn)}"}
|
122
|
+
end
|
123
|
+
exclude.concat ary
|
124
|
+
end
|
125
|
+
|
126
|
+
# generate a regular expression from the exclude list
|
127
|
+
exclude = Regexp.new(exclude.join('|'))
|
128
|
+
|
129
|
+
Find.find '.' do |path|
|
130
|
+
path.sub! %r/^(\.\/|\/)/o, ''
|
131
|
+
next unless test ?f, path
|
132
|
+
next if path =~ exclude
|
133
|
+
files << path
|
134
|
+
end
|
135
|
+
files.sort!
|
136
|
+
end
|
137
|
+
|
138
|
+
# EOF
|
data/tasks/test.rake
ADDED
@@ -0,0 +1,22 @@
|
|
1
|
+
|
2
|
+
if test(?e, PROJ.test.file) or not PROJ.test.files.to_a.empty?
|
3
|
+
require 'rake/testtask'
|
4
|
+
|
5
|
+
namespace :test do
|
6
|
+
|
7
|
+
Rake::TestTask.new(:run) do |t|
|
8
|
+
t.libs = PROJ.libs
|
9
|
+
t.test_files = if test(?f, PROJ.test.file) then [PROJ.test.file]
|
10
|
+
else PROJ.test.files end
|
11
|
+
t.ruby_opts += PROJ.ruby_opts
|
12
|
+
t.ruby_opts += PROJ.test.opts
|
13
|
+
end
|
14
|
+
|
15
|
+
end # namespace :test
|
16
|
+
|
17
|
+
desc 'Alias to test:run'
|
18
|
+
task :test => 'test:run'
|
19
|
+
|
20
|
+
end
|
21
|
+
|
22
|
+
# EOF
|
@@ -0,0 +1,112 @@
|
|
1
|
+
require File.expand_path('test_helper.rb', File.dirname(__FILE__))
|
2
|
+
require 'gir_ffi/arg_helper'
|
3
|
+
|
4
|
+
class ArgHelperTest < Test::Unit::TestCase
|
5
|
+
context "The int_to_inoutptr method's return value" do
|
6
|
+
setup do
|
7
|
+
@result = GirFFI::ArgHelper.int_to_inoutptr 24
|
8
|
+
end
|
9
|
+
|
10
|
+
should "be a FFI::Pointer" do
|
11
|
+
assert_equal "FFI::Pointer", @result.class.to_s
|
12
|
+
end
|
13
|
+
|
14
|
+
should "hold a pointer to the correct input value" do
|
15
|
+
assert_equal 24, @result.read_int
|
16
|
+
end
|
17
|
+
end
|
18
|
+
|
19
|
+
context "The string_array_to_inoutptr method" do
|
20
|
+
context "when called with an array of strings" do
|
21
|
+
setup do
|
22
|
+
@result = GirFFI::ArgHelper.string_array_to_inoutptr ["foo", "bar", "baz"]
|
23
|
+
end
|
24
|
+
|
25
|
+
should "return a FFI::Pointer" do
|
26
|
+
assert_equal "FFI::Pointer", @result.class.to_s
|
27
|
+
end
|
28
|
+
|
29
|
+
should "return a pointer to an array of pointers to strings" do
|
30
|
+
ptr = @result.read_pointer
|
31
|
+
ary = ptr.read_array_of_pointer(3)
|
32
|
+
assert_equal ["foo", "bar", "baz"], ary.map {|p| p.read_string}
|
33
|
+
end
|
34
|
+
end
|
35
|
+
context "when called with nil" do
|
36
|
+
should "return nil" do
|
37
|
+
assert_nil GirFFI::ArgHelper.string_array_to_inoutptr nil
|
38
|
+
end
|
39
|
+
end
|
40
|
+
end
|
41
|
+
|
42
|
+
context "The outptr_to_int method" do
|
43
|
+
setup do
|
44
|
+
@ptr = GirFFI::AllocationHelper.safe_malloc FFI.type_size(:int)
|
45
|
+
@ptr.write_int 342
|
46
|
+
end
|
47
|
+
|
48
|
+
should "retrieve the correct integer value" do
|
49
|
+
assert_equal 342, GirFFI::ArgHelper.outptr_to_int(@ptr)
|
50
|
+
end
|
51
|
+
end
|
52
|
+
|
53
|
+
context "The outptr_to_string_array method" do
|
54
|
+
context "when called with a valid pointer to a string array" do
|
55
|
+
setup do
|
56
|
+
p = GirFFI::AllocationHelper.safe_malloc FFI.type_size(:pointer) * 2
|
57
|
+
p.write_array_of_pointer ["one", "two"].map {|str|
|
58
|
+
len = str.bytesize
|
59
|
+
GirFFI::AllocationHelper.safe_malloc(len + 1).write_string(str).put_char(len, 0)
|
60
|
+
}
|
61
|
+
@ptr = GirFFI::AllocationHelper.safe_malloc FFI.type_size(:pointer)
|
62
|
+
@ptr.write_pointer p
|
63
|
+
end
|
64
|
+
|
65
|
+
should "return the string array" do
|
66
|
+
assert_equal ["one", "two"],
|
67
|
+
GirFFI::ArgHelper.outptr_to_string_array(@ptr, 2)
|
68
|
+
end
|
69
|
+
end
|
70
|
+
|
71
|
+
context "when called with a pointer to a string array containing a null pointer" do
|
72
|
+
setup do
|
73
|
+
ptrs = ["one", "two"].map {|str|
|
74
|
+
len = str.bytesize
|
75
|
+
GirFFI::AllocationHelper.safe_malloc(len + 1).write_string(str).put_char(len, 0)
|
76
|
+
}
|
77
|
+
ptrs << nil
|
78
|
+
p = GirFFI::AllocationHelper.safe_malloc FFI.type_size(:pointer) * 3
|
79
|
+
p.write_array_of_pointer ptrs
|
80
|
+
@ptr = GirFFI::AllocationHelper.safe_malloc FFI.type_size(:pointer)
|
81
|
+
@ptr.write_pointer p
|
82
|
+
end
|
83
|
+
|
84
|
+
should "return render the null pointer as nil" do
|
85
|
+
assert_equal ["one", "two", nil],
|
86
|
+
GirFFI::ArgHelper.outptr_to_string_array(@ptr, 3)
|
87
|
+
end
|
88
|
+
end
|
89
|
+
|
90
|
+
context "when called with nil" do
|
91
|
+
should "return nil" do
|
92
|
+
assert_nil GirFFI::ArgHelper.outptr_to_string_array(nil, 0)
|
93
|
+
end
|
94
|
+
end
|
95
|
+
end
|
96
|
+
|
97
|
+
context "The object_to_inptr method" do
|
98
|
+
context "when called with an object implementing to_ptr" do
|
99
|
+
should "return the result of to_ptr" do
|
100
|
+
obj = Object.new
|
101
|
+
def obj.to_ptr; :test_value; end
|
102
|
+
assert_equal :test_value, GirFFI::ArgHelper.object_to_inptr(obj)
|
103
|
+
end
|
104
|
+
end
|
105
|
+
|
106
|
+
context "when called with nil" do
|
107
|
+
should "return nil" do
|
108
|
+
assert_equal nil, GirFFI::ArgHelper.object_to_inptr(nil)
|
109
|
+
end
|
110
|
+
end
|
111
|
+
end
|
112
|
+
end
|
@@ -0,0 +1,328 @@
|
|
1
|
+
require File.expand_path('test_helper.rb', File.dirname(__FILE__))
|
2
|
+
require 'gir_ffi/builder'
|
3
|
+
|
4
|
+
class BuilderTest < Test::Unit::TestCase
|
5
|
+
context "The GirFFI::Builder module" do
|
6
|
+
# TODO: Use gir's sample Everything library for testing instead.
|
7
|
+
context "building GObject::Object" do
|
8
|
+
setup do
|
9
|
+
GirFFI::Builder.build_class 'GObject', 'Object', 'NS1'
|
10
|
+
end
|
11
|
+
|
12
|
+
should "create a method_missing method for the class" do
|
13
|
+
ms = NS1::GObject::Object.instance_methods(false).map(&:to_sym)
|
14
|
+
assert_contains ms, :method_missing
|
15
|
+
end
|
16
|
+
|
17
|
+
should "create a Lib module in the parent namespace ready to attach functions from gobject-2.0" do
|
18
|
+
gir = GirFFI::IRepository.default
|
19
|
+
expected = gir.shared_library 'GObject'
|
20
|
+
assert_same_elements [*expected], NS1::GObject::Lib.ffi_libraries.map(&:name)
|
21
|
+
end
|
22
|
+
|
23
|
+
should "create an array CALLBACKS inside the GObject::Lib module" do
|
24
|
+
assert_equal [], NS1::GObject::Lib::CALLBACKS
|
25
|
+
end
|
26
|
+
|
27
|
+
should "not replace existing classes" do
|
28
|
+
oldclass = NS1::GObject::Object
|
29
|
+
GirFFI::Builder.build_class 'GObject', 'Object', 'NS1'
|
30
|
+
assert_equal oldclass, NS1::GObject::Object
|
31
|
+
end
|
32
|
+
end
|
33
|
+
|
34
|
+
context "building Gtk::Window" do
|
35
|
+
setup do
|
36
|
+
GirFFI::Builder.build_class 'Gtk', 'Window', 'NS3'
|
37
|
+
end
|
38
|
+
|
39
|
+
should "build Gtk namespace" do
|
40
|
+
assert NS3::Gtk.const_defined? :Lib
|
41
|
+
assert NS3::Gtk.respond_to? :method_missing
|
42
|
+
end
|
43
|
+
|
44
|
+
should "build parent classes also" do
|
45
|
+
assert NS3::Gtk.const_defined? :Widget
|
46
|
+
assert NS3::Gtk.const_defined? :Object
|
47
|
+
assert NS3.const_defined? :GObject
|
48
|
+
assert NS3::GObject.const_defined? :InitiallyUnowned
|
49
|
+
assert NS3::GObject.const_defined? :Object
|
50
|
+
end
|
51
|
+
|
52
|
+
should "set up inheritence chain" do
|
53
|
+
assert_equal [
|
54
|
+
NS3::Gtk::Window,
|
55
|
+
NS3::Gtk::Bin,
|
56
|
+
NS3::Gtk::Container,
|
57
|
+
NS3::Gtk::Widget,
|
58
|
+
NS3::Gtk::Object,
|
59
|
+
NS3::GObject::InitiallyUnowned,
|
60
|
+
NS3::GObject::Object
|
61
|
+
], NS3::Gtk::Window.ancestors[0..6]
|
62
|
+
end
|
63
|
+
|
64
|
+
should "create a Gtk::Window#to_ptr method" do
|
65
|
+
assert_contains NS3::Gtk::Window.instance_methods.map(&:to_sym), :to_ptr
|
66
|
+
end
|
67
|
+
|
68
|
+
should "attach gtk_window_new to Gtk::Lib" do
|
69
|
+
assert NS3::Gtk::Lib.respond_to? :gtk_window_new
|
70
|
+
end
|
71
|
+
|
72
|
+
should "result in Gtk::Window.new to succeed" do
|
73
|
+
assert_nothing_raised {NS3::Gtk::Window.new(:toplevel)}
|
74
|
+
end
|
75
|
+
end
|
76
|
+
|
77
|
+
context "building Gtk" do
|
78
|
+
setup do
|
79
|
+
GirFFI::Builder.build_module 'Gtk', 'NS2'
|
80
|
+
end
|
81
|
+
|
82
|
+
should "create a Lib module ready to attach functions from gtk-x11-2.0" do
|
83
|
+
# The Gtk module has more than one library on my current machine.
|
84
|
+
gir = GirFFI::IRepository.default
|
85
|
+
expected = (gir.shared_library 'Gtk').split(',')
|
86
|
+
assert_same_elements expected, NS2::Gtk::Lib.ffi_libraries.map(&:name)
|
87
|
+
end
|
88
|
+
|
89
|
+
should "create an array CALLBACKS inside the Gtk::Lib module" do
|
90
|
+
assert_equal [], NS2::Gtk::Lib::CALLBACKS
|
91
|
+
end
|
92
|
+
|
93
|
+
should "not replace existing module" do
|
94
|
+
oldmodule = NS2::Gtk
|
95
|
+
GirFFI::Builder.build_module 'Gtk', 'NS2'
|
96
|
+
assert_equal oldmodule, NS2::Gtk
|
97
|
+
end
|
98
|
+
|
99
|
+
should "not replace existing Lib module" do
|
100
|
+
oldmodule = NS2::Gtk::Lib
|
101
|
+
GirFFI::Builder.build_module 'Gtk', 'NS2'
|
102
|
+
assert_equal oldmodule, NS2::Gtk::Lib
|
103
|
+
end
|
104
|
+
end
|
105
|
+
|
106
|
+
context "looking at Gtk.main" do
|
107
|
+
setup do
|
108
|
+
@go = get_function_introspection_data 'Gtk', 'main'
|
109
|
+
end
|
110
|
+
# TODO: function_introspection_data should not return introspection data if not a function.
|
111
|
+
should "have correct introspection data" do
|
112
|
+
gir = GirFFI::IRepository.default
|
113
|
+
gir.require "Gtk", nil
|
114
|
+
go2 = gir.find_by_name "Gtk", "main"
|
115
|
+
assert_equal go2, @go
|
116
|
+
end
|
117
|
+
|
118
|
+
should "build correct definition of Gtk.main" do
|
119
|
+
code = GirFFI::Builder.send :function_definition, @go, Lib
|
120
|
+
|
121
|
+
expected = "def main\nLib.gtk_main\nend"
|
122
|
+
|
123
|
+
assert_equal cws(expected), cws(code)
|
124
|
+
end
|
125
|
+
|
126
|
+
should "attach function to Whatever::Lib" do
|
127
|
+
mod = Module.new
|
128
|
+
mod.const_set :Lib, libmod = Module.new
|
129
|
+
libmod.module_eval do
|
130
|
+
extend FFI::Library
|
131
|
+
ffi_lib "gtk-x11-2.0"
|
132
|
+
end
|
133
|
+
|
134
|
+
GirFFI::Builder.send :attach_ffi_function, mod, libmod, @go, nil
|
135
|
+
assert_contains libmod.public_methods.map(&:to_sym), :gtk_main
|
136
|
+
end
|
137
|
+
end
|
138
|
+
|
139
|
+
context "looking at Gtk.init" do
|
140
|
+
setup do
|
141
|
+
GirFFI::Builder.build_module 'Gtk'
|
142
|
+
@go = get_function_introspection_data 'Gtk', 'init'
|
143
|
+
end
|
144
|
+
|
145
|
+
should "delegate definition to FunctionDefinitionBuilder" do
|
146
|
+
code = GirFFI::Builder.send :function_definition, @go, Lib
|
147
|
+
expected = GirFFI::FunctionDefinitionBuilder.new(@go, Lib).generate
|
148
|
+
assert_equal cws(expected), cws(code)
|
149
|
+
end
|
150
|
+
|
151
|
+
should "have :pointer, :pointer as types of the arguments for the attached function" do
|
152
|
+
# FIXME: Ideally, we attach the function and test that it requires
|
153
|
+
# the correct argument types.
|
154
|
+
assert_equal [:pointer, :pointer], GirFFI::Builder.send(:ffi_function_argument_types, Gtk, Gtk::Lib, @go, nil)
|
155
|
+
end
|
156
|
+
|
157
|
+
should "have :void as return type for the attached function" do
|
158
|
+
assert_equal :void, GirFFI::Builder.send(:ffi_function_return_type, Gtk, Gtk::Lib, @go, nil)
|
159
|
+
end
|
160
|
+
end
|
161
|
+
|
162
|
+
context "looking at Gtk::Widget#show" do
|
163
|
+
setup do
|
164
|
+
@go = get_method_introspection_data 'Gtk', 'Widget', 'show'
|
165
|
+
end
|
166
|
+
|
167
|
+
should "delegate definition to FunctionDefinitionBuilder" do
|
168
|
+
code = GirFFI::Builder.send :function_definition, @go, Lib
|
169
|
+
expected = GirFFI::FunctionDefinitionBuilder.new(@go, Lib).generate
|
170
|
+
assert_equal cws(expected), cws(code)
|
171
|
+
end
|
172
|
+
|
173
|
+
should "have :pointer as types of the arguments for the attached function" do
|
174
|
+
assert_equal [:pointer], GirFFI::Builder.send(:ffi_function_argument_types, Gtk, Gtk::Lib, @go, nil)
|
175
|
+
end
|
176
|
+
|
177
|
+
end
|
178
|
+
|
179
|
+
context "looking at GObject.signal_connect_data" do
|
180
|
+
setup do
|
181
|
+
GirFFI::Builder.build_module 'GObject', 'NS5'
|
182
|
+
@go = get_function_introspection_data 'GObject', 'signal_connect_data'
|
183
|
+
end
|
184
|
+
|
185
|
+
should "delegate definition to FunctionDefinitionBuilder" do
|
186
|
+
code = GirFFI::Builder.send :function_definition, @go, Lib
|
187
|
+
expected = GirFFI::FunctionDefinitionBuilder.new(@go, Lib).generate
|
188
|
+
assert_equal cws(expected), cws(code)
|
189
|
+
end
|
190
|
+
|
191
|
+
should "have the correct types of the arguments for the attached function" do
|
192
|
+
assert_equal [:pointer, :string, :Callback, :pointer, :ClosureNotify, NS5::GObject::ConnectFlags],
|
193
|
+
GirFFI::Builder.send(:ffi_function_argument_types, NS5::GObject, NS5::GObject::Lib, @go, 'NS5')
|
194
|
+
end
|
195
|
+
|
196
|
+
should "define ffi callback types :Callback and :ClosureNotify" do
|
197
|
+
GirFFI::Builder.setup_function 'GObject', NS5::GObject::Lib, NS5::GObject, 'signal_connect_data'
|
198
|
+
cb = NS5::GObject::Lib.find_type :Callback
|
199
|
+
cn = NS5::GObject::Lib.find_type :ClosureNotify
|
200
|
+
|
201
|
+
assert_equal FFI.find_type(:void), cb.result_type
|
202
|
+
assert_equal FFI.find_type(:void), cn.result_type
|
203
|
+
assert_equal [], cb.param_types
|
204
|
+
assert_equal [FFI.find_type(:pointer), FFI.find_type(:pointer)], cn.param_types
|
205
|
+
end
|
206
|
+
|
207
|
+
should "define ffi enum type ConnectFlags" do
|
208
|
+
assert_equal({:after => 1, :swapped => 2}, NS5::GObject::ConnectFlags.to_h)
|
209
|
+
end
|
210
|
+
end
|
211
|
+
|
212
|
+
context "building Everything::TestStructA" do
|
213
|
+
setup do
|
214
|
+
GirFFI::Builder.build_class 'Everything', 'TestStructA'
|
215
|
+
end
|
216
|
+
|
217
|
+
should "set up the correct struct members" do
|
218
|
+
assert_equal [:some_int, :some_int8, :some_double, :some_enum],
|
219
|
+
Everything::TestStructA::Struct.members
|
220
|
+
end
|
221
|
+
|
222
|
+
should "set up struct members with the correct offset" do
|
223
|
+
info = GirFFI::IRepository.default.find_by_name 'Everything', 'TestStructA'
|
224
|
+
assert_equal info.fields.map{|f| [f.name.to_sym, f.offset]},
|
225
|
+
Everything::TestStructA::Struct.offsets
|
226
|
+
end
|
227
|
+
|
228
|
+
should "set up struct members with the correct types" do
|
229
|
+
tags = [:int, :int8, :double, Everything::TestEnum]
|
230
|
+
assert_equal tags.map {|t| FFI.find_type t},
|
231
|
+
Everything::TestStructA::Struct.layout.fields.map(&:type)
|
232
|
+
end
|
233
|
+
end
|
234
|
+
|
235
|
+
context "building Everything::TestBoxed" do
|
236
|
+
setup do
|
237
|
+
GirFFI::Builder.build_class 'Everything', 'TestBoxed'
|
238
|
+
end
|
239
|
+
|
240
|
+
should "set up #_real_new as an alias to #new" do
|
241
|
+
assert Everything::TestBoxed.respond_to? "_real_new"
|
242
|
+
end
|
243
|
+
|
244
|
+
should "allow creation using #new" do
|
245
|
+
tb = Everything::TestBoxed.new
|
246
|
+
assert_instance_of Everything::TestBoxed, tb
|
247
|
+
end
|
248
|
+
|
249
|
+
should "allow creation using alternative constructors" do
|
250
|
+
tb = Everything::TestBoxed.new_alternative_constructor1 1
|
251
|
+
assert_instance_of Everything::TestBoxed, tb
|
252
|
+
assert_equal 1, tb[:some_int8]
|
253
|
+
|
254
|
+
tb = Everything::TestBoxed.new_alternative_constructor2 1, 2
|
255
|
+
assert_instance_of Everything::TestBoxed, tb
|
256
|
+
assert_equal 1 + 2, tb[:some_int8]
|
257
|
+
|
258
|
+
tb = Everything::TestBoxed.new_alternative_constructor3 "54"
|
259
|
+
assert_instance_of Everything::TestBoxed, tb
|
260
|
+
assert_equal 54, tb[:some_int8]
|
261
|
+
end
|
262
|
+
|
263
|
+
should "make the equals method work" do
|
264
|
+
tb = Everything::TestBoxed.new_alternative_constructor1 123
|
265
|
+
tb2 = Everything::TestBoxed.new_alternative_constructor2 120, 3
|
266
|
+
assert_equal true, tb.equals(tb2)
|
267
|
+
end
|
268
|
+
|
269
|
+
should "make the copy method work" do
|
270
|
+
tb = Everything::TestBoxed.new_alternative_constructor1 123
|
271
|
+
tb2 = tb.copy
|
272
|
+
assert_instance_of Everything::TestBoxed, tb2
|
273
|
+
assert_equal 123, tb2[:some_int8], "fields copied"
|
274
|
+
tb2[:some_int8] = 89
|
275
|
+
assert_equal 123, tb[:some_int8], "is a true copy"
|
276
|
+
end
|
277
|
+
end
|
278
|
+
|
279
|
+
context "building Everything::TestEnum" do
|
280
|
+
setup do
|
281
|
+
GirFFI::Builder.build_class 'Everything', 'TestEnum'
|
282
|
+
end
|
283
|
+
should "create an object of type FFI::Enum" do
|
284
|
+
assert_instance_of FFI::Enum, Everything::TestEnum
|
285
|
+
end
|
286
|
+
end
|
287
|
+
|
288
|
+
# TODO: Should not allow functions to be called as methods, etc.
|
289
|
+
|
290
|
+
context "looking at Everything's functions" do
|
291
|
+
setup do
|
292
|
+
GirFFI::Builder.build_module 'Everything'
|
293
|
+
end
|
294
|
+
|
295
|
+
should "correctly handle test_boolean" do
|
296
|
+
assert_equal false, Everything.test_boolean(false)
|
297
|
+
assert_equal true, Everything.test_boolean(true)
|
298
|
+
end
|
299
|
+
|
300
|
+
should "correctly handle test_callback_user_data" do
|
301
|
+
a = :foo
|
302
|
+
result = Everything.test_callback_user_data Proc.new {|u|
|
303
|
+
a = u
|
304
|
+
5
|
305
|
+
}, :bar
|
306
|
+
assert_equal :bar, a
|
307
|
+
assert_equal 5, result
|
308
|
+
end
|
309
|
+
end
|
310
|
+
|
311
|
+
context "building the Everything module" do
|
312
|
+
setup do
|
313
|
+
GirFFI::Builder.build_module 'Everything', 'NS4'
|
314
|
+
end
|
315
|
+
|
316
|
+
should "create a method_missing method for the module" do
|
317
|
+
ms = (NS4::Everything.public_methods - Module.public_methods).map(&:to_sym)
|
318
|
+
assert_contains ms, :method_missing
|
319
|
+
end
|
320
|
+
|
321
|
+
should "cause the TestObj class to be autocreated" do
|
322
|
+
assert !NS4::Everything.const_defined?(:TestObj)
|
323
|
+
assert_nothing_raised {NS4::Everything::TestObj}
|
324
|
+
assert NS4::Everything.const_defined? :TestObj
|
325
|
+
end
|
326
|
+
end
|
327
|
+
end
|
328
|
+
end
|