gir_ffi 0.0.1
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- 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
|