diff options
author | tduehr <tduehr@gmail.com> | 2014-01-21 15:50:23 -0600 |
---|---|---|
committer | tduehr <tduehr@gmail.com> | 2014-06-05 21:33:39 -0500 |
commit | c79e9f5d1a622751226b82e302e20f734c1a4291 (patch) | |
tree | 385eb2ad96a1f399b80b14e2ffa38cba00f3a23d | |
parent | 3223054a9400ba356bf38ffe606ac58210319888 (diff) | |
download | ffi-c79e9f5d1a622751226b82e302e20f734c1a4291.tar.gz |
merge specs w/ JRuby and correct syntax to use rspec
55 files changed, 2152 insertions, 98 deletions
@@ -10,6 +10,11 @@ end require 'date' require 'fileutils' require 'rbconfig' +require 'rspec/core/rake_task' + +RSpec::Core::RakeTask.new(:spec) do |config| + config.rspec_opts = YAML.load_file 'spec/spec.opts' +end LIBEXT = case RbConfig::CONFIG['host_os'].downcase @@ -100,7 +105,6 @@ end desc "Build all packages" task :package => 'gem:package' - CLOBBER.include 'build' CLOBBER.include FileList['lib/**/ffi_c.so'] CLOBBER.include FileList["lib/**/ffi_c.#{RbConfig::CONFIG['DLEXT']}"] @@ -120,7 +124,7 @@ desc "Build test helper lib" task :libtest => "build/libtest.#{LIBEXT}" desc "Test the extension" -task :test => [ :specs, :rbxspecs ] +task :test => [ :spec ] namespace :bench do diff --git a/spec/ffi/LICENSE.SPECS b/spec/ffi/LICENSE.SPECS new file mode 100644 index 0000000..561dd8c --- /dev/null +++ b/spec/ffi/LICENSE.SPECS @@ -0,0 +1,22 @@ +Copyright (c) 2008-2014 Ruby-FFI contributors + +Permission is hereby granted, free of charge, to any person +obtaining a copy of this software and associated documentation +files (the "Software"), to deal in the Software without +restriction, including without limitation the rights to use, +copy, modify, merge, publish, distribute, sublicense, and/or sell +copies of the Software, and to permit persons to whom the +Software is furnished to do so, subject to the following +conditions: + +The above copyright notice and this permission notice shall be +included in all copies or substantial portions of the Software. + +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, +EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES +OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND +NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT +HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, +WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING +FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR +OTHER DEALINGS IN THE SOFTWARE. diff --git a/spec/ffi/async_callback_spec.rb b/spec/ffi/async_callback_spec.rb index 8a11558..5ba16a5 100644 --- a/spec/ffi/async_callback_spec.rb +++ b/spec/ffi/async_callback_spec.rb @@ -3,7 +3,8 @@ # For licensing, see LICENSE.SPECS # -require File.expand_path(File.join(File.dirname(__FILE__), "spec_helper")) +require 'ffi' +require_relative 'spec_helper' describe "async callback" do module LibTest diff --git a/spec/ffi/bool_spec.rb b/spec/ffi/bool_spec.rb index 9124657..989d493 100644 --- a/spec/ffi/bool_spec.rb +++ b/spec/ffi/bool_spec.rb @@ -3,7 +3,9 @@ # For licensing, see LICENSE.SPECS # -require File.expand_path(File.join(File.dirname(__FILE__), "spec_helper")) +require 'ffi' +require_relative 'spec_helper' + describe "Function with primitive boolean arguments and return values" do module LibTest extend FFI::Library diff --git a/spec/ffi/buffer_spec.rb b/spec/ffi/buffer_spec.rb index 219641b..4039556 100644 --- a/spec/ffi/buffer_spec.rb +++ b/spec/ffi/buffer_spec.rb @@ -3,7 +3,8 @@ # For licensing, see LICENSE.SPECS # -require File.expand_path(File.join(File.dirname(__FILE__), "spec_helper")) +require 'ffi' +require_relative 'spec_helper' describe "Buffer#total" do [1,2,3].each do |i| diff --git a/spec/ffi/callback_spec.rb b/spec/ffi/callback_spec.rb index d3151b4..396f91f 100644 --- a/spec/ffi/callback_spec.rb +++ b/spec/ffi/callback_spec.rb @@ -3,7 +3,8 @@ # For licensing, see LICENSE.SPECS # -require File.expand_path(File.join(File.dirname(__FILE__), "spec_helper")) +require 'ffi' +require_relative 'spec_helper' describe "Callback" do # module LibC @@ -253,6 +254,9 @@ describe "Callback" do s2[:f32] = struct[:f32] s2[:s32] = struct[:s32] end + expect(s2[:s8]).to eql 0x12 + expect(s2[:s32]).to eql 0x1eefbeef + expect(s2[:f32]).to be_within(0.0000001).of 1.234567 end @@ -264,7 +268,7 @@ describe "Callback" do describe "When the callback is considered optional by the underlying library" do it "should handle receiving 'nil' in place of the closure" do - LibTest.testOptionalCallbackCrV(nil, 13) + LibTest.testOptionalCallbackCrV(nil, 13).should be_nil end end @@ -288,7 +292,7 @@ describe "Callback" do callback :cb_return_type_1, [ :short ], :short callback :cb_lookup_1, [ :short ], :cb_return_type_1 attach_function :testReturnsCallback_1, :testReturnsClosure, [ :cb_lookup_1, :short ], :cb_return_type_1 - end + end.should be_an_instance_of FFI::Function end it "should return a callback" do @@ -345,7 +349,7 @@ describe "Callback" do callback :cb_argument, [ :int ], :int callback :cb_with_cb_argument, [ :cb_argument, :int ], :int attach_function :testCallbackAsArgument_2, :testArgumentClosure, [ :cb_with_cb_argument, :int ], :int - end + end.should be_an_instance_of FFI::Function end it 'should be able to use the callback argument' do module LibTest @@ -644,7 +648,7 @@ describe "Callback with " do magic = LibTest::S8F32S32.new LibTest.testCallbackYrV(magic) { |i| v = i } v.class.should == magic.class - v.pointer == magic.pointer + v.pointer.should == magic.pointer end it "struct by reference argument with nil value" do diff --git a/spec/ffi/custom_param_type.rb b/spec/ffi/custom_param_type.rb index ddb7c95..55db32f 100644 --- a/spec/ffi/custom_param_type.rb +++ b/spec/ffi/custom_param_type.rb @@ -3,7 +3,8 @@ # For licensing, see LICENSE.SPECS # -require File.expand_path(File.join(File.dirname(__FILE__), "spec_helper")) +require 'ffi' +require_relative 'spec_helper' describe "functions with custom parameter types" do before :each do diff --git a/spec/ffi/custom_type_spec.rb b/spec/ffi/custom_type_spec.rb index 0f8d36b..d3a36dc 100644 --- a/spec/ffi/custom_type_spec.rb +++ b/spec/ffi/custom_type_spec.rb @@ -3,7 +3,8 @@ # For licensing, see LICENSE.SPECS # -require File.expand_path(File.join(File.dirname(__FILE__), "spec_helper")) +require 'ffi' +require_relative 'spec_helper' describe "functions with custom types" do class Custom_enum diff --git a/spec/ffi/dup_spec.rb b/spec/ffi/dup_spec.rb index 32baf2b..c04f24e 100644 --- a/spec/ffi/dup_spec.rb +++ b/spec/ffi/dup_spec.rb @@ -3,8 +3,8 @@ # For licensing, see LICENSE.SPECS # - -require File.expand_path(File.join(File.dirname(__FILE__), "spec_helper")) +require 'ffi' +require_relative 'spec_helper' describe "Pointer#dup" do it "clone should be independent" do diff --git a/spec/ffi/enum_spec.rb b/spec/ffi/enum_spec.rb index f9b3e64..b7abbbf 100644 --- a/spec/ffi/enum_spec.rb +++ b/spec/ffi/enum_spec.rb @@ -3,7 +3,8 @@ # For licensing, see LICENSE.SPECS # -require File.expand_path(File.join(File.dirname(__FILE__), "spec_helper")) +require 'ffi' +require_relative 'spec_helper' module TestEnum0 extend FFI::Library diff --git a/spec/ffi/errno_spec.rb b/spec/ffi/errno_spec.rb index 36c0992..99d1e13 100644 --- a/spec/ffi/errno_spec.rb +++ b/spec/ffi/errno_spec.rb @@ -3,7 +3,9 @@ # For licensing, see LICENSE.SPECS # -require File.expand_path(File.join(File.dirname(__FILE__), "spec_helper")) +require 'ffi' +require_relative 'spec_helper' + describe "FFI.errno" do module LibTest extend FFI::Library diff --git a/spec/ffi/ffi_spec.rb b/spec/ffi/ffi_spec.rb index 6ff01b6..b592b85 100644 --- a/spec/ffi/ffi_spec.rb +++ b/spec/ffi/ffi_spec.rb @@ -3,7 +3,8 @@ # For licensing, see LICENSE.SPECS # -require File.expand_path(File.join(File.dirname(__FILE__), "spec_helper")) +require 'ffi' +require_relative 'spec_helper' describe "FFI" do diff --git a/spec/ffi/fixtures/.gitignore b/spec/ffi/fixtures/.gitignore new file mode 100644 index 0000000..dfd9b3f --- /dev/null +++ b/spec/ffi/fixtures/.gitignore @@ -0,0 +1,10 @@ +# signature of implementation that +# last compiled an extension +*.sig + +# build artifacts +*.o +*.so +*.bundle +*.dylib +*.dll diff --git a/spec/ffi/fixtures/Benchmark.c b/spec/ffi/fixtures/Benchmark.c new file mode 100644 index 0000000..55a7380 --- /dev/null +++ b/spec/ffi/fixtures/Benchmark.c @@ -0,0 +1,52 @@ +/* + * Copyright (c) 2007 Wayne Meissner. All rights reserved. + * + * For licensing, see LICENSE.SPECS + */ +#include <sys/types.h> +#include <stdint.h> + +void returnVoid() { + +} + +void returnVoidI(int arg) { + +} +int returnInt() { + return 0; +} + +int returnIntI(int arg) { + return arg; +} + +typedef int8_t s8; +typedef uint8_t u8; +typedef int16_t s16; +typedef uint16_t u16; +typedef int32_t s32; +typedef uint32_t u32; +typedef int64_t s64; +typedef uint64_t u64; +typedef float f32; +typedef double f64; +typedef void v; +typedef char* S; +typedef void* P; + +#define B6(R, T1, T2, T3, T4, T5, T6) R bench_##T1##T2##T3##T4##T5##T6##_##R(T1 a1, T2 a2, T3 a3, T4 a4, T5 a5, T6 a6) {} +#define B5(R, T1, T2, T3, T4, T5) R bench_##T1##T2##T3##T4##T5##_##R(T1 a1, T2 a2, T3 a3, T4 a4, T5 a5) {} +#define B4(R, T1, T2, T3, T4) R bench_##T1##T2##T3##T4##_##R(T1 a1, T2 a2, T3 a3, T4 a4) {} +#define B3(R, T1, T2, T3) R bench_##T1##T2##T3##_##R(T1 a1, T2 a2, T3 a3) {} +#define B2(R, T1, T2) R bench_##T1##T2##_##R(T1 a1, T2 a2) {} +#define B1(R, T1) R bench_##T1##_##R(T1 a1) {} +#define BrV(T) B1(v, T); B2(v, T, T); B3(v, T, T, T); B4(v, T, T, T, T); B5(v, T, T, T, T, T); B6(v, T, T, T, T, T, T); +BrV(u32); +BrV(s32); +BrV(s64); +BrV(u64); +BrV(f32); +BrV(f64); +BrV(S); +BrV(P); diff --git a/spec/ffi/fixtures/BoolTest.c b/spec/ffi/fixtures/BoolTest.c new file mode 100644 index 0000000..04cb6c6 --- /dev/null +++ b/spec/ffi/fixtures/BoolTest.c @@ -0,0 +1,34 @@ +/* + * Copyright (c) 2007 Wayne Meissner. + * Copyright (c) 2009 Aman Gupta. + * + * All rights reserved. + * + * For licensing, see LICENSE.SPECS + */ + +#include <stdbool.h> + +bool +bool_return_true() +{ + return true; +} + +bool +bool_return_false() +{ + return false; +} + +bool +bool_return_val(bool value) +{ + return value; +} + +bool +bool_reverse_val(bool value) +{ + return value ? false : true; +} diff --git a/spec/ffi/fixtures/BufferTest.c b/spec/ffi/fixtures/BufferTest.c new file mode 100644 index 0000000..3e95ebc --- /dev/null +++ b/spec/ffi/fixtures/BufferTest.c @@ -0,0 +1,31 @@ +/* + * Copyright (c) 2007 Wayne Meissner. All rights reserved. + * + * For licensing, see LICENSE.SPECS + */ + + +#define MEMSET(buf, value, size) do { \ + int i; for (i = 0; i < size; ++i) buf[i] = value; \ +} while(0) +#define MEMCPY(dst, src, size) do { \ + int i; for (i = 0; i < size; ++i) dst[i] = src[i]; \ +} while(0) + +#define FILL(JTYPE, CTYPE) \ +void fill##JTYPE##Buffer(CTYPE* buf, CTYPE value, int size) { MEMSET(buf, value, size); } + +#define COPY(JTYPE, CTYPE) \ +void copy##JTYPE##Buffer(CTYPE* dst, CTYPE* src, int size) { MEMCPY(dst, src, size); } + +#define FUNC(JTYPE, CTYPE) \ + FILL(JTYPE, CTYPE); \ + COPY(JTYPE, CTYPE) + +FUNC(Byte, char); +FUNC(Short, short); +FUNC(Int, int); +FUNC(Long, long long); +FUNC(Float, float); +FUNC(Double, double); + diff --git a/spec/ffi/fixtures/ClosureTest.c b/spec/ffi/fixtures/ClosureTest.c new file mode 100644 index 0000000..64ea2b4 --- /dev/null +++ b/spec/ffi/fixtures/ClosureTest.c @@ -0,0 +1,190 @@ +/* + * Copyright (c) 2007 Wayne Meissner. All rights reserved. + * + * For licensing, see LICENSE.SPECS + */ + +#include <stdlib.h> +#include <stdbool.h> +#ifndef _WIN32 +# include <pthread.h> +#else +# include <windows.h> +# include <process.h> +#endif + +#define R(T, rtype) rtype testClosureVr##T(rtype (*closure)(void)) { \ + return closure != NULL ? (*closure)() : (rtype) 0; \ +} + +#define P(T, ptype) void testClosure##T##rV(void (*closure)(ptype), ptype a1) { \ + if (closure != NULL) (*closure)(a1); \ +} + +void testClosureVrV(void (*closure)(void)) +{ + (*closure)(); +} + +R(Z, bool); +R(B, char); +R(S, short); +R(I, int); +R(L, long); +R(J, long long); +R(LL, long long); +R(F, float); +R(D, double); +R(P, const void*); + + +P(Z, bool); +P(B, char); +P(S, short); +P(I, int); +P(L, long); +P(J, long long); +P(LL, long long); +P(F, float); +P(D, double); +P(P, const void*); +P(UL, unsigned long); + +void testOptionalClosureBrV(void (*closure)(char), char a1) +{ + if (closure) { + (*closure)(a1); + } +} + + +struct ThreadVrV { + void (*closure)(void); + int count; +}; + +static void * +threadVrV(void *arg) +{ + struct ThreadVrV* t = (struct ThreadVrV *) arg; + + int i; + for (i = 0; i < t->count; i++) { + (*t->closure)(); + } + + return NULL; +} + +void testThreadedClosureVrV(void (*closure)(void), int n) +{ + struct ThreadVrV arg = {closure, n}; +#ifndef _WIN32 + pthread_t t; + pthread_create(&t, NULL, threadVrV, &arg); + pthread_join(t, NULL); +#else + HANDLE hThread = (HANDLE) _beginthread((void (*)(void *))threadVrV, 0, &arg); + WaitForSingleObject(hThread, INFINITE); +#endif +} + +struct s8f32s32 { + char s8; + float f32; + int s32; +}; + +// Takes a struct argument +void testClosureTrV(void (*closure)(struct s8f32s32 s), struct s8f32s32* s) +{ + (*closure)(*s); +} + +// Returns a struct value +struct s8f32s32 testClosureVrT(struct s8f32s32 (*closure)()) +{ + return (*closure)(); +} + +typedef int (*returnTypeClosure_t)(int) ; +typedef returnTypeClosure_t (*lookupClosure_t)(); + +int testReturnsClosure(lookupClosure_t lookup, int val) +{ + returnTypeClosure_t func = lookup ? (*lookup)() : NULL; + return func ? (*func)(val) : 0; +} + +static int multiplyByTwo(int value) +{ + return value * 2; +} + +returnTypeClosure_t testReturnsFunctionPointer() +{ + return multiplyByTwo; +} + +typedef int (*argumentClosure_t)(int); +typedef int (*withArgumentClosure_t)(argumentClosure_t, int); + +int testArgumentClosure(withArgumentClosure_t closure_with, argumentClosure_t closure_arg, int val) +{ + return (*closure_with)(closure_arg, val); +} + + +// +// These macros produce functions of the form: +// testClosureBIrV(void (*closure)(char, int), char a1, int a2) {} +// +#define C2_(J1, J2, N1, N2) \ +void testClosure##J1##J2##rV(void (*closure)(N1, N2), N1 a1, N2 a2) \ +{ \ + if (closure != NULL) (*closure)(a1, a2); \ +} + +#define C2(J, N) \ + C2_(B, J, char, N) \ + C2_(S, J, short, N) \ + C2_(I, J, int, N) \ + C2_(LL, J, long long, N) \ + C2_(F, J, float, N) \ + C2_(D, J, double, N) \ + + +C2(B, char); +C2(S, short); +C2(I, int); +C2(LL, long long); +C2(F, float); +C2(D, double); + +#define C3_(J1, J2, J3, N1, N2, N3) \ +void testClosure##J1##J2##J3##rV(void (*closure)(N1, N2, N3), N1 a1, N2 a2, N3 a3) \ +{ \ + (*closure)(a1, a2, a3); \ +} + + +#define C3(J, N) \ + C3_(B, J, B, char, N, char) \ + C3_(S, J, S, short, N, short) \ + C3_(I, J, I, int, N, int) \ + C3_(LL, J, LL, long long, N, long long) \ + C3_(F, J, F, float, N, float) \ + C3_(D, J, D, double, N, double) \ + +C3(B, char); +C3(S, short); +C3(I, int); +C3(LL, long long); +C3(F, float); +C3(D, double); +C3_(B, S, I, char, short, int); +C3_(B, S, LL, char, short, long long); +C3_(LL, S, B, long long, short, char); +C3_(LL, B, S, long long, char, short); + + diff --git a/spec/ffi/fixtures/EnumTest.c b/spec/ffi/fixtures/EnumTest.c new file mode 100644 index 0000000..4c9dda9 --- /dev/null +++ b/spec/ffi/fixtures/EnumTest.c @@ -0,0 +1,34 @@ +/* + * Copyright (c) 2007 Wayne Meissner. All rights reserved. + * + * For licensing, see LICENSE.SPECS + */ + +int test_untagged_enum(int val) { + return val; +} + +int test_untagged_typedef_enum(int val) { + return val; +} + +typedef enum {c1, c2, c3, c4} enum_type1; +enum_type1 test_tagged_typedef_enum1(enum_type1 val) { + return val; +} + +typedef enum {c5 = 42, c6, c7, c8} enum_type2; +enum_type2 test_tagged_typedef_enum2(enum_type2 val) { + return val; +} + +typedef enum {c9 = 42, c10, c11 = 4242, c12} enum_type3; +enum_type3 test_tagged_typedef_enum3(enum_type3 val) { + return val; +} + +typedef enum {c13 = 42, c14 = 4242, c15 = 424242, c16 = 42424242} enum_type4; +enum_type4 test_tagged_typedef_enum4(enum_type4 val) { + return val; +} + diff --git a/spec/ffi/fixtures/FunctionTest.c b/spec/ffi/fixtures/FunctionTest.c new file mode 100644 index 0000000..b4d45bb --- /dev/null +++ b/spec/ffi/fixtures/FunctionTest.c @@ -0,0 +1,58 @@ +/* + * Copyright (c) 2007 Wayne Meissner. All rights reserved. + * + * For licensing, see LICENSE.SPECS + */ + +#ifdef _WIN32 +#include <windows.h> +#define sleep(x) Sleep(x) +#endif + +#ifndef _WIN32 +#include <unistd.h> +#include <pthread.h> +#endif + +int testAdd(int a, int b) +{ + return a + b; +}; + +int testFunctionAdd(int a, int b, int (*f)(int, int)) +{ + return f(a, b); +}; + +void testBlocking(int seconds) { + sleep(seconds); +}; + +struct async_data { + void (*fn)(int); + int value; +}; + +static void* asyncThreadCall(void *data) +{ + struct async_data* d = (struct async_data *) data; + if (d != NULL && d->fn != NULL) { + (*d->fn)(d->value); + } + + return NULL; +} + +void testAsyncCallback(void (*fn)(int), int value) +{ +#ifndef _WIN32 + pthread_t t; + struct async_data d; + d.fn = fn; + d.value = value; + pthread_create(&t, NULL, asyncThreadCall, &d); + pthread_join(t, NULL); +#else + (*fn)(value); +#endif +} diff --git a/spec/ffi/fixtures/GNUmakefile b/spec/ffi/fixtures/GNUmakefile new file mode 100644 index 0000000..d730419 --- /dev/null +++ b/spec/ffi/fixtures/GNUmakefile @@ -0,0 +1,149 @@ +# -*- makefile -*- + +ifeq ($(OS),) + BUILD_OS := $(shell uname -s | tr '[:upper:]' '[:lower:]') + OS := $(BUILD_OS) +endif + +ifeq ($(CPU),) + CPU := $(shell uname -m | sed -e 's/i[345678]86/i386/') +endif + +PLATFORM = $(CPU)-$(OS) + +ifeq ($(OS), sunos) + OS = solaris +endif + +SRC_DIR = . +BUILD_DIR ?= . +TEST_BUILD_DIR = . +# Set defaults to unix (linux/solaris/bsd) +PREFIX = lib +LIBEXT ?= so +LIBNAME = $(PREFIX)test.$(LIBEXT) + +export MACOSX_DEPLOYMENT_TARGET=10.4 + +CCACHE := $(strip $(realpath $(shell which ccache 2> /dev/null))) + +TEST_SRCS = $(wildcard $(SRC_DIR)/*.c) +TEST_OBJS := $(patsubst $(SRC_DIR)/%.c, $(TEST_BUILD_DIR)/%.o, $(TEST_SRCS)) + +# +# Compiler/linker flags from: +# http://weblogs.java.net/blog/kellyohair/archive/2006/01/compilation_of_1.html +JFLAGS = -fno-omit-frame-pointer -fno-strict-aliasing +OFLAGS = -O2 $(JFLAGS) +WFLAGS = -W -Wall -Wno-unused -Wno-parentheses +PICFLAGS = -fPIC +SOFLAGS = -shared +LDFLAGS += $(SOFLAGS) + +IFLAGS = -I"$(BUILD_DIR)" +CFLAGS = $(OFLAGS) $(WFLAGS) $(IFLAGS) $(PICFLAGS) -D_REENTRANT + +ifneq ($(strip $(findstring $(OS), win32, mingw, cygwin)),) + # For cygwin => win32-native builds, strip out cygwin deps + ifneq ($(findstring cygwin, $(BUILD_OS)),) + CC += -mno-cygwin -mwin32 + LDFLAGS += -mno-cygwin -Wl,--add-stdcall-alias + endif + PICFLAGS= + LIBEXT=dll + CC = gcc +endif + +ifeq ($(OS), darwin) + ifneq ($(findstring $(CPU),ppc),) + ARCHFLAGS += -arch ppc + endif + ifneq ($(findstring $(CPU),i386 x86_64),) + ARCHFLAGS += -arch i386 -arch x86_64 + endif + CFLAGS += $(ARCHFLAGS) -DTARGET_RT_MAC_CFM=0 + CFLAGS += -fno-common + LDFLAGS = $(ARCHFLAGS) -dynamiclib + # link against the universal libraries on ppc machines + LDFLAGS += -L$(MACSDK)/usr/lib + LIBEXT = dylib + FFI_CFLAGS += -isysroot $(MACSDK) + PICFLAGS = + SOFLAGS = +endif + +ifeq ($(OS), linux) + SOFLAGS += -Wl,-soname,$(LIBNAME) +endif + +ifeq ($(OS), solaris) + CC = /usr/sfw/bin/gcc -std=c99 + LD = /usr/ccs/bin/ld + SOFLAGS = -shared -static-libgcc +endif + +ifeq ($(OS), aix) + LIBEXT = a + SOFLAGS = -shared -static-libgcc + PICFLAGS += -pthread +endif + +ifneq ($(findstring bsd, $(OS)),) + SOFLAGS = -shared -static-libgcc + CFLAGS += -pthread + LDFLAGS += -pthread +endif + +ifeq ($(CPU), i386) + MODEL = 32 +endif + +ifeq ($(CPU), sparcv9) + MODEL = 64 +endif + +ifeq ($(CPU), amd64) + MODEL = 64 +endif + +ifeq ($(CPU), x86_64) + MODEL = 64 +endif + +ifeq ($(CPU), ppc64) + MODEL = 64 +endif + +ifeq ($(CPU), powerpc64) + MODEL = 64 +endif + +MODELFLAG = +ifneq ($(MODEL),) + MODELFLAG = -m$(MODEL) +endif + +# On platforms (linux, solaris) that support both 32bit and 64bit, force building for one or the other +ifneq ($(or $(findstring linux, $(OS)), $(findstring solaris, $(OS))),) + # Change the CC/LD instead of CFLAGS/LDFLAGS, incase other things in the flags + # makes the libffi build choke + CC += $(MODELFLAG) + LD += $(MODELFLAG) +endif + +LIBTEST = $(LIBNAME) + +all: $(LIBTEST) + +$(TEST_BUILD_DIR)/%.o : $(SRC_DIR)/%.c + @mkdir -p $(@D) + $(CCACHE) $(CC) $(CFLAGS) -c $< -o $@ + +$(LIBTEST): $(TEST_OBJS) + $(CC) -o $@ $(LDFLAGS) $(TEST_OBJS) -lm + +clean:: + # nothing to do - ant will delete the build dir + +debug:: + @echo "SRCS=$(TEST_SRCS)" diff --git a/spec/ffi/fixtures/GlobalVariable.c b/spec/ffi/fixtures/GlobalVariable.c new file mode 100644 index 0000000..39c12a2 --- /dev/null +++ b/spec/ffi/fixtures/GlobalVariable.c @@ -0,0 +1,62 @@ +/* + * Copyright (c) 2007 Wayne Meissner. All rights reserved. + * + * For licensing, see LICENSE.SPECS + */ + +#include <sys/types.h> +#include <stdint.h> + +typedef int8_t s8; +typedef uint8_t u8; +typedef int16_t s16; +typedef uint16_t u16; +typedef int32_t s32; +typedef uint32_t u32; +typedef int64_t s64; +typedef uint64_t u64; +typedef signed long sL; +typedef unsigned long uL; +typedef float f32; +typedef double f64; +#if !defined(__OpenBSD__) +typedef unsigned long ulong; +#endif +typedef void* pointer; +typedef void* P; + +#define GVAR(T) \ + extern T gvar_##T; \ + T gvar_##T = (T) -1; \ + T gvar_##T##_get() { return gvar_##T; }; \ + void gvar_##T##_set(T v) { gvar_##T = v; } + +GVAR(s8); +GVAR(u8); +GVAR(s16); +GVAR(u16); +GVAR(s32); +GVAR(u32); +GVAR(s64); +GVAR(u64); +GVAR(long); +GVAR(ulong); +GVAR(pointer); + +struct gstruct { + long data; +}; + +struct gstruct gvar_gstruct = { -1 }; + +struct gstruct* +gvar_gstruct_get(void) +{ + return &gvar_gstruct; +} + +void +gvar_gstruct_set(const struct gstruct* val) +{ + gvar_gstruct = *val; +} diff --git a/spec/ffi/fixtures/LastErrorTest.c b/spec/ffi/fixtures/LastErrorTest.c new file mode 100644 index 0000000..02ce4a8 --- /dev/null +++ b/spec/ffi/fixtures/LastErrorTest.c @@ -0,0 +1,21 @@ +/* + * Copyright (c) 2007 Wayne Meissner. All rights reserved. + * + * For licensing, see LICENSE.SPECS + */ + +#if defined(_WIN32) || defined(__WIN32__) +# include <windows.h> +#else +# include <errno.h> +#endif + +int setLastError(int error) { +#if defined(_WIN32) || defined(__WIN32__) + SetLastError(error); +#else + errno = error; +#endif + return -1; +} + diff --git a/spec/ffi/fixtures/NumberTest.c b/spec/ffi/fixtures/NumberTest.c new file mode 100644 index 0000000..3fa25a8 --- /dev/null +++ b/spec/ffi/fixtures/NumberTest.c @@ -0,0 +1,132 @@ +/* + * Copyright (c) 2007 Wayne Meissner. All rights reserved. + * + * For licensing, see LICENSE.SPECS + */ + +#include <sys/types.h> +#include <stdio.h> +#include <string.h> +#include <stdint.h> + +#if defined(__sparc) && defined(__sun__) + #define fix_mem_access __asm("ta 6") +#else + #define fix_mem_access +#endif + +typedef int8_t s8; +typedef uint8_t u8; +typedef int16_t s16; +typedef uint16_t u16; +typedef int32_t s32; +typedef uint32_t u32; +typedef int64_t s64; +typedef uint64_t u64; +typedef signed long sL; +typedef unsigned long uL; +typedef float f32; +typedef double f64; +typedef long double f128; +#if !defined(__OpenBSD__) +typedef unsigned long ulong; +#endif + +#define ADD(T) T add_##T(T arg1, T arg2) { return arg1 + arg2; } +#define SUB(T) T sub_##T(T arg1, T arg2) { return arg1 - arg2; } +#define MUL(T) T mul_##T(T arg1, T arg2) { return arg1 * arg2; } +#define DIV(T) T div_##T(T arg1, T arg2) { return arg1 / arg2; } +#define RET(T) T ret_##T(T arg1) { return arg1; } +#define SET(T) static T T##_;void set_##T(T arg1) { T##_ = arg1; } +#define GET(T) T get_##T() { return T##_; } +typedef char* ptr; +#define TEST(T) ADD(T) SUB(T) MUL(T) DIV(T) RET(T) SET(T) GET(T) +TEST(s8); +TEST(u8); +TEST(s16); +TEST(u16); +TEST(s32); +TEST(u32); +TEST(s64); +TEST(u64); +TEST(float); +TEST(double); +TEST(long); +TEST(ulong); +TEST(f128); + +#define ADD2(R, T1, T2) R add_##T1##T2##_##R(T1 arg1, T2 arg2) { return arg1 + arg2; } +#define SUB2(R, T1, T2) R sub_##T1##T2##_##R(T1 arg1, T2 arg2) { return arg1 - arg2; } +#define MUL2(R, T1, T2) R mul_##T1##T2##_##R(T1 arg1, T2 arg2) { return arg1 * arg2; } +#define DIV2(R, T1, T2) R div_##T1##T2##_##R(T1 arg1, T2 arg2) { return arg1 / arg2; } + +#define T2__(R, T1, T2) ADD2(R, T1, T2) SUB2(R, T1, T2) MUL2(R, T1, T2) DIV2(R, T1, T2) +#define T2_(R, T1) \ + T2__(R, T1, s8) T2__(R, T1, u8) \ + T2__(R, T1, s16) T2__(R, T1, u16) \ + T2__(R, T1, s32) T2__(R, T1, u32) \ + T2__(R, T1, sL) T2__(R, T1, uL) \ + T2__(R, T1, s64) T2__(R, T1, u64) \ + +#define TEST2(R) \ + T2_(R, s8) T2_(R, u8) T2_(R, s16) T2_(R, u16) T2_(R, s32) T2_(R, u32) \ + T2_(R, sL) T2_(R, uL) T2_(R, s64) T2_(R, u64) + +#ifdef notyet +TEST2(s32) +TEST2(u32) +TEST2(s64) +TEST2(u64) +#endif + +#define ADD3(R, T1, T2, T3) R add_##T1##T2##T3##_##R(T1 arg1, T2 arg2, T3 arg3) { return arg1 + arg2 + arg3; } +#define pack_f32(buf, v) do { float f = v; memcpy((buf), &f, sizeof(f)); } while(0) +#define pack_f64(buf, v) do { double f = v; memcpy((buf), &f, sizeof(f)); } while(0) +#define pack_int(buf, v) do { *(buf) = v; } while(0) +#define pack_s8 pack_int +#define pack_u8 pack_int +#define pack_s16 pack_int +#define pack_u16 pack_int +#define pack_s32 pack_int +#define pack_u32 pack_int +#define pack_s64 pack_int +#define pack_u64 pack_int +#define pack_sL pack_int +#define pack_uL pack_int + +#define PACK3(R, T1, T2, T3) void pack_##T1##T2##T3##_##R(T1 arg1, T2 arg2, T3 arg3, R* r) { \ + fix_mem_access; \ + pack_##T1(&r[0], arg1); \ + pack_##T2(&r[1], arg2); \ + pack_##T3(&r[2], arg3); \ +} + +#define T3___(R, T1, T2, T3) PACK3(R, T1, T2, T3) /* SUB2(R, T1, T2) MUL2(R, T1, T2) DIV2(R, T1, T2) */ +#define T3__(R, T1, T2) \ + T3___(R, T1, T2, s8) T3___(R, T1, T2, u8) \ + T3___(R, T1, T2, s16) T3___(R, T1, T2, u16) \ + T3___(R, T1, T2, s32) T3___(R, T1, T2, u32) \ + T3___(R, T1, T2, sL) T3___(R, T1, T2, uL) \ + T3___(R, T1, T2, s64) T3___(R, T1, T2, u64) \ + T3___(R, T1, T2, f32) T3___(R, T1, T2, f64) \ + +#define T3_(R, T1) \ + T3__(R, T1, s8) T3__(R, T1, u8) \ + T3__(R, T1, s16) T3__(R, T1, u16) \ + T3__(R, T1, s32) T3__(R, T1, u32) \ + T3__(R, T1, sL) T3__(R, T1, uL) \ + T3__(R, T1, s64) T3__(R, T1, u64) \ + T3__(R, T1, f32) T3__(R, T1, f64) \ + +#define TEST3(R) \ + T3_(R, s8) T3_(R, u8) T3_(R, s16) T3_(R, u16) T3_(R, s32) T3_(R, u32) \ + T3_(R, sL) T3_(R, uL) T3_(R, s64) T3_(R, u64) T3_(R, f32) T3_(R, f64) + +TEST3(s64) + +void +foo6(intptr_t i1, intptr_t i2, intptr_t i3, intptr_t i4, intptr_t i5, intptr_t i6) { } + +void +foo5(intptr_t i1, intptr_t i2, intptr_t i3, intptr_t i4, intptr_t i5) { } + diff --git a/spec/ffi/fixtures/PointerTest.c b/spec/ffi/fixtures/PointerTest.c new file mode 100644 index 0000000..7237ab2 --- /dev/null +++ b/spec/ffi/fixtures/PointerTest.c @@ -0,0 +1,63 @@ +/* + * Copyright (c) 2007 Wayne Meissner. All rights reserved. + * + * For licensing, see LICENSE.SPECS + */ + +#include <sys/types.h> +#include <sys/param.h> +#include <stdint.h> +#include <stdio.h> +#include <stdlib.h> +#include <string.h> +typedef void* ptr; +typedef void* pointer; +#ifdef _WIN32 +typedef char* caddr_t; +#endif + +#define RET(T) T ptr_ret_##T(void* arg1, int offset) { \ + T tmp; memcpy(&tmp, (caddr_t) arg1 + offset, sizeof(tmp)); return tmp; \ +} +#define SET(T) void ptr_set_##T(void* arg1, int offset, T value) { \ + memcpy((caddr_t) arg1 + offset, &value, sizeof(value)); \ +} +#define TEST(T) SET(T) RET(T) + +TEST(int8_t); +TEST(int16_t); +TEST(int32_t); +TEST(int64_t); +TEST(float); +TEST(double); +TEST(pointer); + +void* +ptr_return_array_element(void **ptrArray, int arrayIndex) +{ + return ptrArray[arrayIndex]; +} + +void +ptr_set_array_element(void **ptrArray, int arrayIndex, void *value) +{ + ptrArray[arrayIndex] = value; +} + +void* +ptr_malloc(int size) +{ + return calloc(1, size); +} +void +ptr_free(void* ptr) +{ + free(ptr); +} + +void* +ptr_from_address(uintptr_t addr) +{ + return (void *) addr; +} + diff --git a/spec/ffi/fixtures/ReferenceTest.c b/spec/ffi/fixtures/ReferenceTest.c new file mode 100644 index 0000000..d1dd88b --- /dev/null +++ b/spec/ffi/fixtures/ReferenceTest.c @@ -0,0 +1,23 @@ +/* + * Copyright (c) 2007 Wayne Meissner. All rights reserved. + * + * For licensing, see LICENSE.SPECS + */ + +#include <stdint.h> + +#define REF(T) void ref_##T(T arg, T* result) { *result = arg; } +#define ADD(T) void ref_add_##T(T arg1, T arg2, T* result) { *result = arg1 + arg2; } +#define SUB(T) void ref_sub_##T(T arg1, T arg2, T* result) { *result = arg1 - arg2; } +#define MUL(T) void ref_mul_##T(T arg1, T arg2, T* result) { *result = arg1 * arg2; } +#define DIV(T) void ref_div_##T(T arg1, T arg2, T* result) { *result = arg1 / arg2; } +#define TEST(T) ADD(T) SUB(T) MUL(T) DIV(T) REF(T) + +TEST(int8_t); +TEST(int16_t); +TEST(int32_t); +TEST(int64_t); +TEST(float); +TEST(double); + + diff --git a/spec/ffi/fixtures/StringTest.c b/spec/ffi/fixtures/StringTest.c new file mode 100644 index 0000000..292242b --- /dev/null +++ b/spec/ffi/fixtures/StringTest.c @@ -0,0 +1,34 @@ +/* + * Copyright (c) 2007 Wayne Meissner. All rights reserved. + * + * For licensing, see LICENSE.SPECS + */ + +#include <string.h> + +int +string_equals(const char* s1, const char* s2) +{ + return strcmp(s1, s2) == 0; +} + +void +string_set(char* s1, const char* s2) +{ + strcpy(s1, s2); +} +void +string_concat(char* dst, const char* src) +{ + strcat(dst, src); +} +void +string_dummy(char* dummy) +{ +} +const char* +string_null(void) +{ + return NULL; +} + diff --git a/spec/ffi/fixtures/StructTest.c b/spec/ffi/fixtures/StructTest.c new file mode 100644 index 0000000..25683d3 --- /dev/null +++ b/spec/ffi/fixtures/StructTest.c @@ -0,0 +1,243 @@ +/* + * Copyright (c) 2007 Wayne Meissner. + * Copyright (c) 2009 Andrea Fazzi <andrea.fazzi@alcacoop.it>. + * + * All rights reserved. + * + * For licensing, see LICENSE.SPECS + */ + +#include <stdio.h> +#include <stdlib.h> +#include <stdbool.h> +#include <stdint.h> +#include <string.h> +#include <stdarg.h> + +typedef char s8; +typedef short s16; +typedef int s32; +typedef long long s64; +typedef float f32; +typedef double f64; + +typedef struct bugged_struct { + unsigned char visible; + unsigned int x; + unsigned int y; + short rx; + short ry; + unsigned char order; + unsigned char size; +} bugged_struct_t; + +unsigned int +bugged_struct_size() { + return sizeof(bugged_struct_t); +} + +struct test1 { + char b; + short s; + int i; + long long j; + long l; + float f; + double d; + char string[32]; +}; + +struct struct_with_array { + char c; + int a[5]; +}; + +struct nested { + int i; +}; + +struct container { + char first; + struct nested s; +}; + +int +struct_align_nested_struct(struct container* a) { return a->s.i; } + +void* +struct_field_array(struct struct_with_array* s) { return &s->a; } + +struct container* +struct_make_container_struct(int i) +{ + static struct container cs; + memset(&cs, 0, sizeof(cs)); + cs.first = 1; + cs.s.i = i; + return &cs; +} + +#define T(x, type) \ + type struct_field_##type(struct test1* t) { return t->x; } \ + struct type##_align { char first; type value; }; \ + type struct_align_##type(struct type##_align* a) { return a->value; } + +T(b, s8); +T(s, s16); +T(i, s32); +T(j, s64); +T(f, f32); +T(d, f64); +T(l, long); + +void +struct_set_string(struct test1* t, char* s) +{ + strcpy(t->string, s); +} + +struct test1* +struct_make_struct(char b, short s, int i, long long ll, float f, double d) +{ + static struct test1 t; + memset(&t, 0, sizeof(t)); + t.b = b; + t.s = s; + t.i = i; + t.j = ll; + t.f = f; + t.d = d; + return &t; +} + +typedef int (*add_cb)(int a1, int a2); +typedef int (*sub_cb)(int a1, int a2); +struct test2 { + add_cb add_callback; + sub_cb sub_callback; +}; + +int +struct_call_add_cb(struct test2* t, int a1, int a2) +{ + return t->add_callback(a1, a2); +} + +int +struct_call_sub_cb(struct test2* t, int a1, int a2) +{ + return t->sub_callback(a1, a2); +} + + +struct struct_with_array* +struct_make_struct_with_array(int a_0, int a_1, int a_2, int a_3, int a_4) +{ + static struct struct_with_array s; + + memset(&s, 0, sizeof(s)); + + s.a[0] = a_0; + s.a[1] = a_1; + s.a[2] = a_2; + s.a[3] = a_3; + s.a[4] = a_4; + + return &s; + +} + +struct s8s32 { + char s8; + int s32; +}; + +struct s8s32 +struct_return_s8s32() +{ + struct s8s32 s; + s.s8 = 0x7f; + s.s32 = 0x12345678; + + return s; +} + +struct s8s32 +struct_s8s32_set(char s8, int s32) +{ + struct s8s32 s; + + s.s8 = s8; + s.s32 = s32; + + return s; +} + +int +struct_s8s32_get_s8(struct s8s32 s) +{ + return s.s8; +} + +int +struct_s8s32_get_s32(struct s8s32 s) +{ + return s.s32; +} + +struct s8s32 +struct_s8s32_ret_s8s32(struct s8s32 s) +{ + return s; +} + +// Pass a struct and an int arg, ensure the int arg is passed correctly +int +struct_s8s32_s32_ret_s32(struct s8s32 s, int s32) +{ + return s32; +} + +// Pass a struct and a long long arg, ensure the long long arg is passed correctly +long long +struct_s8s32_s64_ret_s64(struct s8s32 s, long long s64) +{ + return s64; +} + +// Pass a struct and a long long arg, ensure the long long arg is passed correctly +int +struct_s32_ptr_s32_s8s32_ret_s32(int s32a, void *ptr, int s32b, struct s8s32 s) +{ + if (ptr != NULL) *(struct s8s32 *) ptr = s; + return s.s32; +} + +// Pass a char *, copy into buffer length struct +struct struct_string { + char *bytes; + int len; +}; + +struct struct_string +struct_varargs_ret_struct_string(int len, ...) +{ + struct struct_string ss; + va_list vl; + char* cp = NULL; + + va_start(vl, len); + + ss.len = len; + ss.bytes = va_arg(vl, char *); + if (ss.bytes != NULL) { + cp = malloc(strlen(ss.bytes) + 1); + strcpy(cp, ss.bytes); + ss.bytes = cp; + } + + va_end(vl); + + return ss; +} + diff --git a/spec/ffi/fixtures/UnionTest.c b/spec/ffi/fixtures/UnionTest.c new file mode 100644 index 0000000..0929a31 --- /dev/null +++ b/spec/ffi/fixtures/UnionTest.c @@ -0,0 +1,43 @@ +/* + * Copyright (c) 2007 Wayne Meissner. All rights reserved. + * + * For licensing, see LICENSE.SPECS + */ + +#include <stdio.h> +#include <stdbool.h> +#include <stdint.h> +#include <string.h> +#include <stdlib.h> + +typedef char s8; +typedef short s16; +typedef int s32; +typedef long long s64; +typedef float f32; +typedef double f64; + +typedef union union_test { + char b; + short s; + int i; + long long j; + long l; + float f; + double d; + s8 a[10]; +} union_test_t; + +#define T(x, type) \ + type union_align_##type(union_test_t* u) { return u->x; } \ + union_test_t* union_make_union_with_##type(type value) { static union_test_t u; u.x = value; return &u; } + +T(b, s8); +T(s, s16); +T(i, s32); +T(j, s64); +T(f, f32); +T(d, f64); +T(l, long); + +unsigned int union_size() { return sizeof(union_test_t); } diff --git a/spec/ffi/fixtures/VariadicTest.c b/spec/ffi/fixtures/VariadicTest.c new file mode 100644 index 0000000..fea6c3b --- /dev/null +++ b/spec/ffi/fixtures/VariadicTest.c @@ -0,0 +1,62 @@ +/* + * Copyright (c) 2007 Wayne Meissner. All rights reserved. + * + * For licensing, see LICENSE.SPECS + */ + +#include <sys/types.h> +#include <stdio.h> +#include <stdint.h> +#include <string.h> +#include <stdarg.h> + +typedef int8_t s8; +typedef uint8_t u8; +typedef int16_t s16; +typedef uint16_t u16; +typedef int32_t s32; +typedef uint32_t u32; +typedef int64_t s64; +typedef uint64_t u64; +typedef signed long sL; +typedef unsigned long uL; +typedef float F; +typedef double D; + +void pack_varargs(s64* buf, const char* fmt, ...) +{ + va_list ap; + int c; + double d; + va_start(ap, fmt); + while ((c = *fmt++)) { + switch (c) { + case 'c': + case 's': + case 'i': + *buf++ = va_arg(ap, s32); + break; + case 'l': + *buf++ = va_arg(ap, long); + break; + case 'j': + *buf++ = va_arg(ap, s64); + break; + case 'f': + case 'd': + d = va_arg(ap, double); + memcpy(buf++, &d, sizeof(d)); + break; + case 'C': + case 'S': + case 'I': + *buf++ = va_arg(ap, u32); + break; + case 'L': + *buf++ = va_arg(ap, unsigned long); + break; + } + } + va_end(ap); +} + diff --git a/spec/ffi/fixtures/classes.rb b/spec/ffi/fixtures/classes.rb new file mode 100644 index 0000000..581768c --- /dev/null +++ b/spec/ffi/fixtures/classes.rb @@ -0,0 +1,438 @@ +module FFISpecs + # + # Callback fixtures + # + module LibTest + callback :cbVrS8, [ ], :char + callback :cbVrU8, [ ], :uchar + callback :cbVrS16, [ ], :short + callback :cbVrU16, [ ], :ushort + callback :cbVrS32, [ ], :int + callback :cbVrU32, [ ], :uint + callback :cbVrL, [ ], :long + callback :cbVrUL, [ ], :ulong + callback :cbVrS64, [ ], :long_long + callback :cbVrU64, [ ], :ulong_long + callback :cbVrP, [], :pointer + callback :cbCrV, [ :char ], :void + callback :cbSrV, [ :short ], :void + callback :cbIrV, [ :int ], :void + callback :cbLrV, [ :long ], :void + callback :cbULrV, [ :ulong ], :void + callback :cbLrV, [ :long_long ], :void + + attach_function :testCallbackVrS8, :testClosureVrB, [ :cbVrS8 ], :char + attach_function :testCallbackVrU8, :testClosureVrB, [ :cbVrU8 ], :uchar + attach_function :testCallbackVrS16, :testClosureVrS, [ :cbVrS16 ], :short + attach_function :testCallbackVrU16, :testClosureVrS, [ :cbVrU16 ], :ushort + attach_function :testCallbackVrS32, :testClosureVrI, [ :cbVrS32 ], :int + attach_function :testCallbackVrU32, :testClosureVrI, [ :cbVrU32 ], :uint + attach_function :testCallbackVrL, :testClosureVrL, [ :cbVrL ], :long + attach_function :testCallbackVrUL, :testClosureVrL, [ :cbVrUL ], :ulong + attach_function :testCallbackVrS64, :testClosureVrLL, [ :cbVrS64 ], :long_long + attach_function :testCallbackVrU64, :testClosureVrLL, [ :cbVrU64 ], :ulong_long + attach_function :testCallbackVrP, :testClosureVrP, [ :cbVrP ], :pointer + attach_function :testCallbackCrV, :testClosureBrV, [ :cbCrV, :char ], :void + attach_variable :cbVrS8, :gvar_pointer, :cbVrS8 + attach_variable :pVrS8, :gvar_pointer, :pointer + attach_function :testGVarCallbackVrS8, :testClosureVrB, [ :pointer ], :char + attach_function :testOptionalCallbackCrV, :testOptionalClosureBrV, [ :cbCrV, :char ], :void + + attach_function :testCallbackVrS8, :testClosureVrB, [ callback([ ], :char) ], :char + + callback :cb_return_type, [ :int ], :int + callback :cb_lookup, [ ], :cb_return_type + attach_function :testReturnsCallback, :testReturnsClosure, [ :cb_lookup, :int ], :int + + callback :funcptr, [ :int ], :int + attach_function :testReturnsFunctionPointer, [ ], :funcptr + + callback :cbS8rV, [ :char ], :void + callback :cbU8rV, [ :uchar ], :void + callback :cbS16rV, [ :short ], :void + callback :cbU16rV, [ :ushort ], :void + + callback :cbS32rV, [ :int ], :void + callback :cbU32rV, [ :uint ], :void + + callback :cbLrV, [ :long ], :void + callback :cbULrV, [ :ulong ], :void + + callback :cbS64rV, [ :long_long ], :void + attach_function :testCallbackCrV, :testClosureBrV, [ :cbS8rV, :char ], :void + attach_function :testCallbackU8rV, :testClosureBrV, [ :cbU8rV, :uchar ], :void + attach_function :testCallbackSrV, :testClosureSrV, [ :cbS16rV, :short ], :void + attach_function :testCallbackU16rV, :testClosureSrV, [ :cbU16rV, :ushort ], :void + attach_function :testCallbackIrV, :testClosureIrV, [ :cbS32rV, :int ], :void + attach_function :testCallbackU32rV, :testClosureIrV, [ :cbU32rV, :uint ], :void + + attach_function :testCallbackLrV, :testClosureLrV, [ :cbLrV, :long ], :void + attach_function :testCallbackULrV, :testClosureULrV, [ :cbULrV, :ulong ], :void + + attach_function :testCallbackLLrV, :testClosureLLrV, [ :cbS64rV, :long_long ], :void + end + + # + # Enum fixtures + # + module TestEnum0 + extend FFI::Library + end + + module TestEnum1 + extend FFI::Library + ffi_lib LIBRARY + + enum [:c1, :c2, :c3, :c4] + enum [:c5, 42, :c6, :c7, :c8] + enum [:c9, 42, :c10, :c11, 4242, :c12] + enum [:c13, 42, :c14, 4242, :c15, 424242, :c16, 42424242] + + attach_function :test_untagged_enum, [:int], :int + end + + module TestEnum3 + extend FFI::Library + ffi_lib LIBRARY + + enum :enum_type1, [:c1, :c2, :c3, :c4] + enum :enum_type2, [:c5, 42, :c6, :c7, :c8] + enum :enum_type3, [:c9, 42, :c10, :c11, 4242, :c12] + enum :enum_type4, [:c13, 42, :c14, 4242, :c15, 424242, :c16, 42424242] + + attach_function :test_tagged_typedef_enum1, [:enum_type1], :enum_type1 + attach_function :test_tagged_typedef_enum2, [:enum_type2], :enum_type2 + attach_function :test_tagged_typedef_enum3, [:enum_type3], :enum_type3 + attach_function :test_tagged_typedef_enum4, [:enum_type4], :enum_type4 + end + + # + # Errno fixtures + # + module LibTest + attach_function :setLastError, [ :int ], :void + end + + # + # ManagedStruct fixtures + # + module LibTest + attach_function :ptr_from_address, [ FFI::Platform::ADDRESS_SIZE == 32 ? :uint : :ulong_long ], :pointer + end + + class NoRelease < ManagedStruct + layout :i, :int + end + + class WhatClassAmI < ManagedStruct + layout :i, :int + def self.release; end + end + + class PleaseReleaseMe < ManagedStruct + layout :i, :int + @@count = 0 + def self.release + @@count += 1 + end + def self.wait_gc(count) + loop = 5 + while loop > 0 && @@count < count + loop -= 1 + if RUBY_PLATFORM =~ /java/ + require 'java' + java.lang.System.gc + else + GC.start + end + sleep 0.05 if @@count < count + end + end + end + + # + # Number fixtures + # + module LibTest + attach_function :ret_s8, [ :char ], :char + attach_function :ret_u8, [ :uchar ], :uchar + attach_function :ret_s16, [ :short ], :short + attach_function :ret_u16, [ :ushort ], :ushort + attach_function :ret_s32, [ :int ], :int + attach_function :ret_u32, [ :uint ], :uint + attach_function :ret_s64, [ :long_long ], :long_long + attach_function :ret_u64, [ :ulong_long ], :ulong_long + attach_function :ret_long, [ :long ], :long + attach_function :ret_ulong, [ :ulong ], :ulong + attach_function :set_s8, [ :char ], :void + attach_function :get_s8, [ ], :char + attach_function :set_float, [ :float ], :void + attach_function :get_float, [ ], :float + attach_function :set_double, [ :double ], :void + attach_function :get_double, [ ], :double + end + + PACK_VALUES = { + 's8' => [ 0x12 ], + 'u8' => [ 0x34 ], + 's16' => [ 0x5678 ], + 'u16' => [ 0x9abc ], + 's32' => [ 0x7654321f ], + 'u32' => [ 0xfee1babe ], + 'sL' => [ 0x1f2e3d4c ], + 'uL' => [ 0xf7e8d9ca ], + 's64' => [ 0x1eafdeadbeefa1b2 ], + #'f32' => [ 1.234567 ], # TODO: Why is this disabled? + 'f64' => [ 9.87654321 ] + } + + TYPE_MAP = { + 's8' => :char, 'u8' => :uchar, 's16' => :short, 'u16' => :ushort, + 's32' => :int, 'u32' => :uint, 's64' => :long_long, 'u64' => :ulong_long, + 'sL' => :long, 'uL' => :ulong, 'f32' => :float, 'f64' => :double + } + TYPES = TYPE_MAP.keys + + module LibTest + [ 's32', 'u32', 's64', 'u64' ].each do |rt| + TYPES.each do |t1| + TYPES.each do |t2| + TYPES.each do |t3| + begin + attach_function "pack_#{t1}#{t2}#{t3}_#{rt}", + [ TYPE_MAP[t1], TYPE_MAP[t2], TYPE_MAP[t3], :buffer_out ], :void + rescue FFI::NotFoundError + end + end + end + end + end + end + + # + # Pointer fixtures + # + module LibTest + attach_function :ptr_ret_int32_t, [ :pointer, :int ], :int + attach_function :ptr_from_address, [ FFI::Platform::ADDRESS_SIZE == 32 ? :uint : :ulong_long ], :pointer + attach_function :ptr_set_pointer, [ :pointer, :int, :pointer ], :void + end + + class ToPtrTest + def initialize(ptr) + @ptr = ptr + end + def to_ptr + @ptr + end + end + + require 'delegate' + class PointerDelegate < DelegateClass(FFI::Pointer) + def initialize(ptr) + super + @ptr = ptr + end + def to_ptr + @ptr + end + end + + class AutoPointerTestHelper + @@count = 0 + def self.release + @@count += 1 if @@count > 0 + end + def self.reset + @@count = 0 + end + def self.gc_everything(count) + loop = 5 + while @@count < count && loop > 0 + loop -= 1 + if RUBY_PLATFORM =~ /java/ + require "java" + java.lang.System.gc + else + GC.start + end + sleep 0.05 unless @@count == count + end + @@count = 0 + end + def self.finalizer + self.method(:release).to_proc + end + end + + # + # String fixtures + # + module LibTest + attach_function :ptr_ret_pointer, [ :pointer, :int], :string + attach_function :string_equals, [ :string, :string ], :int + attach_function :string_dummy, [ :string ], :void + end + + # + # Struct initialize fixtures + # + class StructWithInitialize < FFI::Struct + layout :string, :string + attr_accessor :magic + def initialize + super + self.magic = 42 + end + end + + # + # Struct fixtures + # + StructTypes = { + 's8' => :char, + 's16' => :short, + 's32' => :int, + 's64' => :long_long, + 'long' => :long, + 'f32' => :float, + 'f64' => :double + } + + module LibTest + attach_function :ptr_ret_pointer, [ :pointer, :int], :string + attach_function :ptr_ret_int32_t, [ :pointer, :int ], :int + attach_function :ptr_from_address, [ :ulong ], :pointer + attach_function :string_equals, [ :string, :string ], :int + [ 's8', 's16', 's32', 's64', 'f32', 'f64', 'long' ].each do |t| + attach_function "struct_align_#{t}", [ :pointer ], StructTypes[t] + end + end + + class PointerMember < FFI::Struct + layout :pointer, :pointer + end + + class StringMember < FFI::Struct + layout :string, :string + end + + module CallbackMember + extend FFI::Library + ffi_lib LIBRARY + callback :add, [ :int, :int ], :int + callback :sub, [ :int, :int ], :int + + class TestStruct < FFI::Struct + layout :add, :add, + :sub, :sub + end + + attach_function :struct_call_add_cb, [TestStruct, :int, :int], :int + attach_function :struct_call_sub_cb, [TestStruct, :int, :int], :int + end + + module LibTest + class NestedStruct < FFI::Struct + layout :i, :int + end + + class ContainerStruct < FFI::Struct + layout :first, :char, :ns, NestedStruct + end + + attach_function :struct_align_nested_struct, [ :pointer ], :int + attach_function :struct_make_container_struct, [ :int ], :pointer + + class StructWithArray < FFI::Struct + layout :first, :char, :a, [:int, 5] + end + + attach_function :struct_make_struct_with_array, [:int, :int, :int, :int, :int], :pointer + attach_function :struct_field_array, [:pointer], :pointer + + class BuggedStruct < FFI::Struct + layout :visible, :uchar, + :x, :uint, + :y, :uint, + :rx, :short, + :ry, :short, + :order, :uchar, + :size, :uchar + end + + attach_function :bugged_struct_size, [], :uint + end + + module StructCustomTypedef + extend FFI::Library + ffi_lib LIBRARY + typedef :uint, :fubar3_t + + class S < FFI::Struct + layout :a, :fubar3_t + end + end + + # + # Union fixtures + # + module LibTest + Types = { + 's8' => [:char, :c, 1], + 's16' => [:short, :s, 0xff0], + 's32' => [:int, :i, 0xff00], + 's64' => [:long_long, :j, 0xffff00], + 'long' => [:long, :l, 0xffff], + 'f32' => [:float, :f, 1.0001], + 'f64' => [:double, :d, 1.000000001] + } + + class TestUnion < FFI::Union + layout( :a, [:char, 10], + :i, :int, + :f, :float, + :d, :double, + :s, :short, + :l, :long, + :j, :long_long, + :c, :char ) + end + + Types.keys.each do |k| + attach_function "union_align_#{k}", [ :pointer ], Types[k][0] + attach_function "union_make_union_with_#{k}", [ Types[k][0] ], :pointer + end + + attach_function :union_size, [], :uint + end + + # + # Variadic fixtures + # + module LibTest + attach_function :pack_varargs, [ :buffer_out, :string, :varargs ], :void + end + + module Varargs + PACK_VALUES = { + 'c' => [ 0x12 ], + 'C' => [ 0x34 ], + 's' => [ 0x5678 ], + 'S' => [ 0x9abc ], + 'i' => [ 0x7654321f ], + 'I' => [ 0xfee1babe ], + 'l' => [ 0x1f2e3d4c ], + 'L' => [ 0xf7e8d9ca ], + 'j' => [ 0x1eafdeadbeefa1b2 ], + 'f' => [ 1.23456789 ], + 'd' => [ 9.87654321 ] + } + + TYPE_MAP = { + 'c' => :char, 'C' => :uchar, 's' => :short, 'S' => :ushort, + 'i' => :int, 'I' => :uint, 'j' => :long_long, 'J' => :ulong_long, + 'l' => :long, 'L' => :ulong, 'f' => :float, 'd' => :double + } + end +end diff --git a/spec/ffi/function_spec.rb b/spec/ffi/function_spec.rb index 21ad09c..c011787 100644 --- a/spec/ffi/function_spec.rb +++ b/spec/ffi/function_spec.rb @@ -3,7 +3,8 @@ # For licensing, see LICENSE.SPECS # -require File.expand_path(File.join(File.dirname(__FILE__), "spec_helper")) +require 'ffi' +require_relative 'spec_helper' describe FFI::Function do module LibTest @@ -16,13 +17,14 @@ describe FFI::Function do FFI::DynamicLibrary::RTLD_LAZY | FFI::DynamicLibrary::RTLD_GLOBAL) end it 'is initialized with a signature and a block' do - FFI::Function.new(:int, []) { } + fn = FFI::Function.new(:int, []) { 5 } + expect(fn.call).to eql 5 end it 'raises an error when passing a wrong signature' do lambda { FFI::Function.new([], :int).new { } }.should raise_error TypeError end it 'returns a native pointer' do - FFI::Function.new(:int, []) { }.kind_of? FFI::Pointer + expect(FFI::Function.new(:int, []) { }).to be_a_kind_of FFI::Pointer end it 'can be used as callback from C passing to it a block' do function_add = FFI::Function.new(:int, [:int, :int]) { |a, b| a + b } diff --git a/spec/ffi/io_spec.rb b/spec/ffi/io_spec.rb new file mode 100644 index 0000000..ccc8d19 --- /dev/null +++ b/spec/ffi/io_spec.rb @@ -0,0 +1,16 @@ +# +# This file is part of ruby-ffi. +# For licensing, see LICENSE.SPECS +# + +require 'ffi' + +if false # disabled for #390 + describe "FFI::IO.for_fd" do + it "produces an IO wrapping the specified file descriptor" do + expect do + FFI::IO.for_fd(2, "r") + end.to_not raise_error + end + end +end
\ No newline at end of file diff --git a/spec/ffi/library_spec.rb b/spec/ffi/library_spec.rb index d5831a0..5b182f7 100644 --- a/spec/ffi/library_spec.rb +++ b/spec/ffi/library_spec.rb @@ -3,7 +3,9 @@ # For licensing, see LICENSE.SPECS # -require File.expand_path(File.join(File.dirname(__FILE__), "spec_helper")) +require 'ffi' +require_relative 'spec_helper' + describe "Library" do describe "#ffi_convention" do it "defaults to :default" do diff --git a/spec/ffi/long_double.rb b/spec/ffi/long_double.rb index b76344a..0cc4105 100644 --- a/spec/ffi/long_double.rb +++ b/spec/ffi/long_double.rb @@ -3,7 +3,8 @@ # For licensing, see LICENSE.SPECS # -require File.expand_path(File.join(File.dirname(__FILE__), "spec_helper")) +require 'ffi' +require_relative 'spec_helper' require 'bigdecimal' describe ":long_double arguments and return values" do diff --git a/spec/ffi/managed_struct_spec.rb b/spec/ffi/managed_struct_spec.rb index 43902c1..df6a5e1 100644 --- a/spec/ffi/managed_struct_spec.rb +++ b/spec/ffi/managed_struct_spec.rb @@ -3,8 +3,8 @@ # For licensing, see LICENSE.SPECS # -require File.expand_path(File.join(File.dirname(__FILE__), "spec_helper")) -require 'java' if RUBY_PLATFORM =~ /java/ +require 'ffi' +require_relative 'spec_helper' describe "Managed Struct" do include FFI diff --git a/spec/ffi/memorypointer_spec.rb b/spec/ffi/memorypointer_spec.rb new file mode 100644 index 0000000..084c77e --- /dev/null +++ b/spec/ffi/memorypointer_spec.rb @@ -0,0 +1,71 @@ +# +# This file is part of ruby-ffi. +# For licensing, see LICENSE.SPECS +# + +require 'ffi' + +MemoryPointer = FFI::MemoryPointer + +describe "MemoryPointer#total" do + it "MemoryPointer.new(:char, 1).total == 1" do + expect(MemoryPointer.new(:char, 1).total).to eq 1 + end + it "MemoryPointer.new(:short, 1).total == 2" do + expect(MemoryPointer.new(:short, 1).total).to eq 2 + end + it "MemoryPointer.new(:int, 1).total == 4" do + expect(MemoryPointer.new(:int, 1).total).to eq 4 + end + it "MemoryPointer.new(:long_long, 1).total == 8" do + expect(MemoryPointer.new(:long_long, 1).total).to eq 8 + end + it "MemoryPointer.new(1024).total == 1024" do + expect(MemoryPointer.new(1024).total).to eq 1024 + end +end +describe "MemoryPointer#read_array_of_long" do + it "foo" do + ptr = MemoryPointer.new(:long, 1024) + ptr[0].write_long 1234 + ptr[1].write_long 5678 + l = ptr.read_array_of_long(2) + expect(l[0]).to eq 1234 + expect(l[1]).to eq 5678 + end +end +describe "MemoryPointer argument" do + module Ptr + extend FFI::Library + ffi_lib FFI::Platform::LIBC + attach_function :memset, [ :pointer, :int, :ulong ], :pointer + attach_function :memcpy, [ :pointer, :pointer, :ulong ], :pointer + end + it "Pointer passed correctly" do + p = MemoryPointer.new :int, 1 + ret = Ptr.memset(p, 0, p.total) + expect(ret).to eq p + end + it "Data passed to native function" do + p = MemoryPointer.new :int, 1 + p2 = MemoryPointer.new :int, 1 + p2.put_int(0, 0x5eadbeef) + Ptr.memcpy(p, p2, p.total) + expect(p.get_int(0)).to eq p2.get_int(0) + expect(p2.get_int(0)).not_to eql 0 + end +end +describe "MemoryPointer return value" do + module Stdio + extend FFI::Library + ffi_lib FFI::Platform::LIBC + attach_function :fopen, [ :string, :string ], :pointer + attach_function :fclose, [ :pointer ], :int + attach_function :fwrite, [ :pointer, :ulong, :ulong, :string ], :ulong + end + it "fopen returns non-nil" do + fp = Stdio.fopen("/dev/null", "w") + expect(fp).to_not be_nil + expect(Stdio.fclose(fp)).to eq 0 unless fp.nil? or fp.null? + end +end diff --git a/spec/ffi/number_spec.rb b/spec/ffi/number_spec.rb index 6087cce..0dbc3cb 100644 --- a/spec/ffi/number_spec.rb +++ b/spec/ffi/number_spec.rb @@ -3,7 +3,9 @@ # For licensing, see LICENSE.SPECS # -require File.expand_path(File.join(File.dirname(__FILE__), "spec_helper")) +require 'ffi' +require_relative 'spec_helper' + describe "Function with primitive integer arguments" do module LibTest extend FFI::Library diff --git a/spec/ffi/platform_spec.rb b/spec/ffi/platform_spec.rb new file mode 100644 index 0000000..67539df --- /dev/null +++ b/spec/ffi/platform_spec.rb @@ -0,0 +1,97 @@ +# +# This file is part of ruby-ffi. +# For licensing, see LICENSE.SPECS +# + +require 'ffi' +require_relative 'spec_helper' + +describe "FFI::Platform::LIBSUFFIX" do + it "returns 'so'", if: RbConfig::CONFIG['host_os'].match('linux') do + FFI::Platform::LIBSUFFIX.should == 'so' + end + + it "returns 'dll'", if: RbConfig::CONFIG['host_os'].match('windows') do + FFI::Platform::LIBSUFFIX.should == 'dll' + end + + it "returns 'dylib'", if: RbConfig::CONFIG['host_os'].match('darwin') do + FFI::Platform::LIBSUFFIX.should == 'dylib' + end +end + +describe "FFI::Platform::IS_WINDOWS" do + it "returns false", if: RbConfig::CONFIG['host_os'].match('linux') do + FFI::Platform::IS_WINDOWS.should == false + end + + it "returns true", if: RbConfig::CONFIG['host_os'].match('windows') do + FFI::Platform::IS_WINDOWS.should == true + end + + it "returns false", if: RbConfig::CONFIG['host_os'].match('darwin') do + FFI::Platform::IS_WINDOWS.should == false + end +end + +describe "FFI::Platform::ARCH" do + it "returns the architecture type" do + FFI::Platform::ARCH.should == RbConfig::CONFIG["target_cpu"] + end +end + +describe "FFI::Platform::OS" do + it "returns 'linux' as a string", if: RbConfig::CONFIG['host_os'].match('linux') do + FFI::Platform::OS.should == 'linux' + end + + it "returns 'windows' as a string", if: RbConfig::CONFIG['host_os'].match('windows') do + FFI::Platform::OS.should == 'windows' + end + + it "returns 'darwin' as a string", if: RbConfig::CONFIG['host_os'].match('darwin') do + FFI::Platform::OS.should == 'darwin' + end + + describe "FFI::Platform.windows?" do + it "returns false", if: RbConfig::CONFIG['host_os'].match('linux') do + FFI::Platform.windows?.should == false + end + + it "returns true", if: RbConfig::CONFIG['host_os'].match('windows') do + FFI::Platform.windows?.should == true + end + + it "returns false", if: RbConfig::CONFIG['host_os'].match('darwin') do + FFI::Platform.windows?.should == false + end + end + + describe "FFI::Platform.mac?" do + it "returns false", if: RbConfig::CONFIG['host_os'].match('linux') do + FFI::Platform.mac?.should == false + end + + it "returns false", if: RbConfig::CONFIG['host_os'].match('windows') do + FFI::Platform.mac?.should == false + end + + it "returns true", if: RbConfig::CONFIG['host_os'].match('darwin') do + FFI::Platform.mac?.should == true + end + end + + describe "FFI::Platform.unix?" do + it "returns true", if: RbConfig::CONFIG['host_os'].match('linux') do + FFI::Platform.unix?.should == true + end + + it "returns false", if: RbConfig::CONFIG['host_os'].match('windows') do + FFI::Platform.unix?.should == false + end + + it "returns true", if: RbConfig::CONFIG['host_os'].match('darwin') do + FFI::Platform.unix?.should == true + end + end +end diff --git a/spec/ffi/pointer_spec.rb b/spec/ffi/pointer_spec.rb index ee4068d..2ddec39 100644 --- a/spec/ffi/pointer_spec.rb +++ b/spec/ffi/pointer_spec.rb @@ -2,9 +2,10 @@ # This file is part of ruby-ffi. # For licensing, see LICENSE.SPECS # -require File.expand_path(File.join(File.dirname(__FILE__), "spec_helper")) + +require 'ffi' +require_relative 'spec_helper' require 'delegate' -require 'java' if RUBY_PLATFORM =~ /java/ module PointerTestLib extend FFI::Library diff --git a/spec/ffi/rbx/attach_function_spec.rb b/spec/ffi/rbx/attach_function_spec.rb index 488414e..9099509 100644 --- a/spec/ffi/rbx/attach_function_spec.rb +++ b/spec/ffi/rbx/attach_function_spec.rb @@ -1,4 +1,10 @@ -require File.expand_path(File.join(File.dirname(__FILE__), "spec_helper")) +# +# This file is part of ruby-ffi. +# For licensing, see LICENSE.SPECS +# + +require 'ffi' +require_relative 'spec_helper' class Timeval < FFI::Struct layout :tv_sec, :ulong, 0, :tv_usec, :ulong, 4 diff --git a/spec/ffi/rbx/memory_pointer_spec.rb b/spec/ffi/rbx/memory_pointer_spec.rb index 8162198..a868d3f 100644 --- a/spec/ffi/rbx/memory_pointer_spec.rb +++ b/spec/ffi/rbx/memory_pointer_spec.rb @@ -1,6 +1,10 @@ -# coding: utf-8 -require "rubygems" -require File.expand_path(File.join(File.dirname(__FILE__), "spec_helper")) +# +# This file is part of ruby-ffi. +# For licensing, see LICENSE.SPECS +# + +require 'ffi' +require_relative 'spec_helper' module CTest extend FFI::Library @@ -17,7 +21,7 @@ describe "MemoryPointer" do end it "does not make a pointer from non-strings" do - expect { FFI::MemoryPointer.from_string(nil) }.to raise_error(TypeError) + expect {FFI::MemoryPointer.from_string(nil)}.to raise_error(TypeError) end it "makes a pointer from a string with multibyte characters" do diff --git a/spec/ffi/rbx/spec_helper.rb b/spec/ffi/rbx/spec_helper.rb index c8b41a1..a05eda6 100644 --- a/spec/ffi/rbx/spec_helper.rb +++ b/spec/ffi/rbx/spec_helper.rb @@ -1 +1,7 @@ -require File.expand_path(File.join(File.dirname(__FILE__), "..", "spec_helper")) +# +# This file is part of ruby-ffi. +# For licensing, see LICENSE.SPECS +# + +require_relative File.join("..", "spec_helper") +require 'ffi'
\ No newline at end of file diff --git a/spec/ffi/rbx/struct_spec.rb b/spec/ffi/rbx/struct_spec.rb index 092a1a5..0e7cf3f 100644 --- a/spec/ffi/rbx/struct_spec.rb +++ b/spec/ffi/rbx/struct_spec.rb @@ -1,4 +1,10 @@ -require File.expand_path(File.join(File.dirname(__FILE__), "spec_helper")) +# +# This file is part of ruby-ffi. +# For licensing, see LICENSE.SPECS +# + +require 'ffi' +require_relative 'spec_helper' class Timeval < FFI::Struct layout :tv_sec, :ulong, 0, :tv_usec, :ulong, 4 @@ -10,4 +16,4 @@ describe FFI::Struct do t[:tv_sec] = 12 t[:tv_sec].should == 12 end -end
\ No newline at end of file +end diff --git a/spec/ffi/spec_helper.rb b/spec/ffi/spec_helper.rb index fb9d141..291157d 100644 --- a/spec/ffi/spec_helper.rb +++ b/spec/ffi/spec_helper.rb @@ -2,21 +2,81 @@ # This file is part of ruby-ffi. # For licensing, see LICENSE.SPECS # -require 'rubygems' + require 'rbconfig' +require 'ffi' + +CPU = case RbConfig::CONFIG['host_cpu'].downcase + when /i[3456]86/ + # Darwin always reports i686, even when running in 64bit mode + if RbConfig::CONFIG['host_os'] =~ /darwin/ && 0xfee1deadbeef.is_a?(Fixnum) + "x86_64" + else + "i386" + end + + when /amd64|x86_64/ + "x86_64" + + when /ppc64|powerpc64/ + "powerpc64" + + when /ppc|powerpc/ + "powerpc" + + when /^arm/ + "arm" + + else + RbConfig::CONFIG['host_cpu'] + end + +OS = case RbConfig::CONFIG['host_os'].downcase + when /linux/ + "linux" + when /darwin/ + "darwin" + when /freebsd/ + "freebsd" + when /openbsd/ + "openbsd" + when /sunos|solaris/ + "solaris" + when /mswin|mingw/ + "win32" + else + RbConfig::CONFIG['host_os'].downcase + end + +def compile_library(path, lib) + + dir = File.expand_path(path, File.dirname(__FILE__)) + lib = "#{dir}/#{lib}" + if !File.exists?(lib) + ldshared = RbConfig::CONFIG["LDSHARED"] + libs = RbConfig::CONFIG["LIBS"] + dldflags = RbConfig::CONFIG["DLDFLAGS"] + + puts Dir.pwd, dir, File.dirname(__FILE__) + + FileUtils.cd(dir) do + output = system(*%{#{system('which gmake >/dev/null') && 'gmake' || 'make'} CPU=#{CPU} OS=#{OS} }.tap{|x| puts x.inspect}) + end -if RUBY_PLATFORM =~/java/ - libdir = File.expand_path(File.join(File.dirname(__FILE__), "..", "..", "lib")) - $:.reject! { |p| p == libdir } -else - $:.unshift File.join(File.dirname(__FILE__), "..", "..", "lib"), - File.join(File.dirname(__FILE__), "..", "..", "build", "#{RbConfig::CONFIG['host_cpu''arch']}", "ffi_c", RUBY_VERSION) + if $?.exitstatus != 0 + puts "ERROR:\n#{output}" + raise "Unable to compile \"#{lib}\"" + end + end + + lib end -# puts "loadpath=#{$:.join(':')}" + require "ffi" module TestLibrary - PATH = "build/libtest.#{FFI::Platform::LIBSUFFIX}" + PATH = compile_library("fixtures", "libtest.#{FFI::Platform::LIBSUFFIX}") + def self.force_gc if RUBY_PLATFORM =~ /java/ java.lang.System.gc diff --git a/spec/ffi/string_spec.rb b/spec/ffi/string_spec.rb index c76bcba..05ae20d 100644 --- a/spec/ffi/string_spec.rb +++ b/spec/ffi/string_spec.rb @@ -3,7 +3,9 @@ # For licensing, see LICENSE.SPECS # -require File.expand_path(File.join(File.dirname(__FILE__), "spec_helper")) +require 'ffi' +require_relative 'spec_helper' + describe "String tests" do include FFI module StrLibTest @@ -43,7 +45,7 @@ describe "String tests" do end end if false it "casts nil as NULL pointer" do - StrLibTest.string_dummy(nil) + StrLibTest.string_dummy(nil).should == nil end it "return nil for NULL char*" do StrLibTest.string_null.should == nil diff --git a/spec/ffi/strptr_spec.rb b/spec/ffi/strptr_spec.rb index 1e6f474..b837613 100644 --- a/spec/ffi/strptr_spec.rb +++ b/spec/ffi/strptr_spec.rb @@ -3,7 +3,8 @@ # For licensing, see LICENSE.SPECS # -require File.expand_path(File.join(File.dirname(__FILE__), "spec_helper")) +require 'ffi' +require_relative 'spec_helper' describe "functions returning :strptr" do diff --git a/spec/ffi/struct_by_ref_spec.rb b/spec/ffi/struct_by_ref_spec.rb index 94dda98..1b73cad 100644 --- a/spec/ffi/struct_by_ref_spec.rb +++ b/spec/ffi/struct_by_ref_spec.rb @@ -3,7 +3,8 @@ # For licensing, see LICENSE.SPECS # -require File.expand_path(File.join(File.dirname(__FILE__), "spec_helper")) +require 'ffi' +require_relative 'spec_helper' describe FFI::Struct, ' by_ref' do before :all do @@ -11,7 +12,7 @@ describe FFI::Struct, ' by_ref' do layout :a, :pointer end - @api = Module.new do + @api = Module.new do extend FFI::Library ffi_lib TestLibrary::PATH fn = FFI::Type::POINTER.size == FFI::Type::LONG.size ? :ret_ulong : :ret_u64 @@ -23,7 +24,7 @@ describe FFI::Struct, ' by_ref' do s = @struct_class.new @api.struct_test(s).should == s.pointer end - + it "should accept nil" do @api.struct_test(nil).should == nil end diff --git a/spec/ffi/struct_callback_spec.rb b/spec/ffi/struct_callback_spec.rb index 8ba9773..5fc1b38 100644 --- a/spec/ffi/struct_callback_spec.rb +++ b/spec/ffi/struct_callback_spec.rb @@ -3,7 +3,8 @@ # For licensing, see LICENSE.SPECS # -require File.expand_path(File.join(File.dirname(__FILE__), "spec_helper")) +require 'ffi' +require_relative 'spec_helper' describe FFI::Struct, ' with inline callback functions' do it 'should be able to define inline callback field' do @@ -19,7 +20,7 @@ describe FFI::Struct, ' with inline callback functions' do end attach_function :struct_call_add_cb, [TestStruct, :int, :int], :int attach_function :struct_call_sub_cb, [TestStruct, :int, :int], :int - end + end.should be_an_instance_of FFI::Function end it 'should take methods as callbacks' do module CallbackMember2 diff --git a/spec/ffi/struct_initialize_spec.rb b/spec/ffi/struct_initialize_spec.rb index a340781..796a584 100644 --- a/spec/ffi/struct_initialize_spec.rb +++ b/spec/ffi/struct_initialize_spec.rb @@ -3,7 +3,8 @@ # For licensing, see LICENSE.SPECS # -require File.expand_path(File.join(File.dirname(__FILE__), "spec_helper")) +require 'ffi' +require_relative 'spec_helper' describe FFI::Struct, ' with an initialize function' do it "should call the initialize function" do diff --git a/spec/ffi/struct_packed_spec.rb b/spec/ffi/struct_packed_spec.rb index 2c35f0b..68979ad 100644 --- a/spec/ffi/struct_packed_spec.rb +++ b/spec/ffi/struct_packed_spec.rb @@ -3,7 +3,8 @@ # For licensing, see LICENSE.SPECS # -require File.expand_path(File.join(File.dirname(__FILE__), "spec_helper")) +require 'ffi' +require_relative 'spec_helper' describe FFI::Struct do it "packed :char followed by :int should have size of 5" do diff --git a/spec/ffi/struct_spec.rb b/spec/ffi/struct_spec.rb index 7cf24d8..6fe2378 100644 --- a/spec/ffi/struct_spec.rb +++ b/spec/ffi/struct_spec.rb @@ -3,7 +3,9 @@ # For licensing, see LICENSE.SPECS # -require File.expand_path(File.join(File.dirname(__FILE__), "spec_helper")) +require 'ffi' +require_relative 'spec_helper' + describe "Struct tests" do StructTypes = { 's8' => :char, @@ -61,7 +63,7 @@ describe "Struct tests" do it "Struct#[:pointer]=struct" do smp = FFI::MemoryPointer.new :pointer s = PointerMember.new smp - lambda { s[:pointer] = s }.should_not raise_error + lambda { s[:pointer] = s }.should_not raise_error Exception end it "Struct#[:pointer]=nil" do smp = FFI::MemoryPointer.new :pointer @@ -121,21 +123,19 @@ describe "Struct tests" do s[:b] = 0xfee1deadbeef mp.get_int64(4).should == 0xfee1deadbeef end - rb_maj, rb_min = RUBY_VERSION.split('.') - if rb_maj.to_i >= 1 && rb_min.to_i >= 9 || RUBY_PLATFORM =~ /java/ - it "Struct#layout withs with a hash of :name => type" do - class HashLayout < FFI::Struct - layout :a => :int, :b => :long_long - end - ll_off = (FFI::TYPE_UINT64.alignment == 4? 4 : 8) - HashLayout.size.should == (ll_off + 8) - mp = FFI::MemoryPointer.new(HashLayout.size) - s = HashLayout.new mp - s[:a] = 0x12345678 - mp.get_int(0).should == 0x12345678 - s[:b] = 0xfee1deadbeef - mp.get_int64(ll_off).should == 0xfee1deadbeef - end + + it "Struct#layout withs with a hash of :name => type" do + class HashLayout < FFI::Struct + layout :a => :int, :b => :long_long + end + ll_off = (FFI::TYPE_UINT64.alignment == 4? 4 : 8) + HashLayout.size.should == (ll_off + 8) + mp = FFI::MemoryPointer.new(HashLayout.size) + s = HashLayout.new mp + s[:a] = 0x12345678 + mp.get_int(0).should == 0x12345678 + s[:b] = 0xfee1deadbeef + mp.get_int64(ll_off).should == 0xfee1deadbeef end it "subclass overrides initialize without calling super" do @@ -163,7 +163,7 @@ describe "Struct tests" do layout :c, :char end attach_function :struct_field_s8, [ TestStruct.in ], :char - end + end.should be_an_instance_of FFI::Function end it "Can use Struct subclass as IN parameter type" do module StructParam2 @@ -173,7 +173,7 @@ describe "Struct tests" do layout :c, :char end attach_function :struct_field_s8, [ TestStruct.in ], :char - end + end.should be_an_instance_of FFI::Function end it "Can use Struct subclass as OUT parameter type" do module StructParam3 @@ -183,7 +183,7 @@ describe "Struct tests" do layout :c, :char end attach_function :struct_field_s8, [ TestStruct.out ], :char - end + end.should be_an_instance_of FFI::Function end it "can be passed directly as a :pointer parameter" do class TestStruct < FFI::Struct @@ -261,7 +261,7 @@ describe "Struct tests" do def test_num_field(type, v) klass = Class.new(FFI::Struct) klass.layout :v, type, :dummy, :long - + s = klass.new s[:v] = v s.pointer.send("get_#{type.to_s}", 0).should == v @@ -312,11 +312,11 @@ describe "Struct tests" do extend FFI::Library TestEnum = enum :test_enum, [:c1, 10, :c2, 20, :c3, 30, :c4, 40] class TestStruct < FFI::Struct - layout :a, :int, :c, :test_enum, + layout :a, :int, :c, :test_enum, :d, [ TestEnum, TestEnum.symbols.length ] end end - + it ":enum field r/w" do s = EnumFields::TestStruct.new s[:c] = :c3 @@ -324,22 +324,22 @@ describe "Struct tests" do s.pointer.get_uint(FFI::Type::INT32.size).should == 30 s[:c].should == :c3 end - + it "array of :enum field" do s = EnumFields::TestStruct.new EnumFields::TestEnum.symbols.each_with_index do |val, i| s[:d][i] = val end - + EnumFields::TestEnum.symbols.each_with_index do |val, i| s.pointer.get_uint(FFI::Type::INT32.size * (2 + i)).should == EnumFields::TestEnum[val] end - + s[:d].each_with_index do |val, i| val.should == EnumFields::TestEnum.symbols[i] end end - + module CallbackMember extend FFI::Library ffi_lib TestLibrary::PATH @@ -396,6 +396,53 @@ describe "Struct tests" do end end +describe FFI::Struct, ".layout" do + module FFISpecs + module LibTest + extend FFI::Library + ffi_lib TestLibrary::PATH + attach_function :ptr_ret_int32_t, [ :pointer, :int ], :int + end + end + + describe "when derived class is not assigned to any constant" do + it "resolves a built-in type" do + klass = Class.new FFI::Struct + klass.layout :number, :int + + instance = klass.new + instance[:number] = 0xA1 + FFISpecs::LibTest.ptr_ret_int32_t(instance, 0).should == 0xA1 + end + end + + describe "when derived class is assigned to a constant" do + it "resolves a built-in type" do + class FFISpecs::TestStruct < FFI::Struct + layout :number, :int + end + + instance = FFISpecs::TestStruct.new + instance[:number] = 0xA1 + FFISpecs::LibTest.ptr_ret_int32_t(instance, 0).should == 0xA1 + end + + it "resolves a type from the enclosing module" do + module FFISpecs::LibTest + typedef :uint, :custom_int + + class TestStruct < FFI::Struct + layout :number, :custom_int + end + end + + instance = FFISpecs::LibTest::TestStruct.new + instance[:number] = 0xA1 + FFISpecs::LibTest.ptr_ret_int32_t(instance, 0).should == 0xA1 + end + end +end + describe FFI::Struct, ' with a nested struct field' do module LibTest extend FFI::Library @@ -420,7 +467,7 @@ describe FFI::Struct, ' with a nested struct field' do LibTest::ContainerStruct.size.should == 8 end it 'should return a Struct object when the field is accessed' do - @cs[:ns].is_a?(FFI::Struct).should be_true + @cs[:ns].is_a?(FFI::Struct).should be_true end it 'should read a value from memory' do @cs = LibTest::ContainerStruct.new(LibTest.struct_make_container_struct(123)) @@ -432,7 +479,7 @@ describe FFI::Struct, ' with a nested struct field' do LibTest.struct_align_nested_struct(@cs.to_ptr).should == 456 end - it 'should be able to assign struct instance to nested field' do + it 'should be able to assign struct instance to nested field' do cs = LibTest::ContainerStruct.new(LibTest.struct_make_container_struct(123)) ns = LibTest::NestedStruct.new ns[:i] = 567 @@ -540,15 +587,18 @@ describe FFI::Struct, ' by value' do s = LibTest::S8S32.new s[:s8] = 0x12 s[:s32] = 0x34567890 - + LibTest.struct_s8s32_s32_ret_s32(s, 0x1eefdead).should == 0x1eefdead end - it 'parameter with following s64' do - s = LibTest::S8S32.new - s[:s8] = 0x12 - s[:s32] = 0x34567890 - end + # it 'parameter with following s64' do + # s = LibTest::S8S64.new + # s[:s8] = 0x12 + # s[:s64] = 0x34567890 + # + # + # LibTest.struct_s8s64_s64_ret_s64(s, 0x1eefdead1eefdead).should == 0x1eefdead1eefdead + # end it 'parameter with preceding s32,ptr,s32' do s = LibTest::S8S32.new @@ -615,7 +665,7 @@ describe FFI::Struct, ' with an array field' do end it 'should allow iteration through the array elements' do @s = LibTest::StructWithArray.new(LibTest.struct_make_struct_with_array(0, 1, 2, 3, 4)) - @s[:a].each_with_index { |elem, i| elem.should == i } + @s[:a].each_with_index { |elem, i| elem.should == i } end it 'should return the pointer to the array' do @s = LibTest::StructWithArray.new(LibTest.struct_make_struct_with_array(0, 1, 2, 3, 4)) @@ -652,7 +702,7 @@ describe 'BuggedStruct' do end it 'should return correct field/offset pairs' do LibTest::BuggedStruct.offsets.sort do |a, b| - a[1] <=> b[1] + a[1] <=> b[1] end.should == [[:visible, 0], [:x, 4], [:y, 8], [:rx, 12], [:ry, 14], [:order, 16], [:size, 17]] end end @@ -702,7 +752,7 @@ describe "Struct allocation" do end struct = c.new struct[:b] = ! struct[:b] - end.should_not raise_error + end.should_not raise_error Exception end end @@ -713,7 +763,7 @@ describe "variable-length arrays" do Class.new(FFI::Struct) do layout :count, :int, :data, [ :char, 0 ] end - }.should_not raise_error + }.should_not raise_error Exception end it "zero length array before last element should raise error" do diff --git a/spec/ffi/typedef_spec.rb b/spec/ffi/typedef_spec.rb index 7c3b569..412030b 100644 --- a/spec/ffi/typedef_spec.rb +++ b/spec/ffi/typedef_spec.rb @@ -3,7 +3,9 @@ # For licensing, see LICENSE.SPECS # -require File.expand_path(File.join(File.dirname(__FILE__), "spec_helper")) +require 'ffi' +require_relative 'spec_helper' + describe "Custom type definitions" do it "attach_function with custom typedef" do module CustomTypedef diff --git a/spec/ffi/union_spec.rb b/spec/ffi/union_spec.rb index 2b3cf44..4e52470 100644 --- a/spec/ffi/union_spec.rb +++ b/spec/ffi/union_spec.rb @@ -3,7 +3,8 @@ # For licensing, see LICENSE.SPECS # -require File.expand_path(File.join(File.dirname(__FILE__), "spec_helper")) +require 'ffi' +require_relative 'spec_helper' module LibTest Types = { diff --git a/spec/ffi/variadic_spec.rb b/spec/ffi/variadic_spec.rb index a19cf59..1d08e4d 100644 --- a/spec/ffi/variadic_spec.rb +++ b/spec/ffi/variadic_spec.rb @@ -3,7 +3,9 @@ # For licensing, see LICENSE.SPECS # -require File.expand_path(File.join(File.dirname(__FILE__), "spec_helper")) +require 'ffi' +require_relative 'spec_helper' + describe "Function with variadic arguments" do module LibTest extend FFI::Library diff --git a/spec/spec.opts b/spec/spec.opts index 81b5382..4cc9a8a 100644 --- a/spec/spec.opts +++ b/spec/spec.opts @@ -1,4 +1,3 @@ --color --format -specdoc - +documentation |