Affix

 view release on metacpan or  search on metacpan

t/001_affix.t  view on Meta::CPAN

use v5.40;
use lib '../lib', 'lib';
use blib;
use Test2::Tools::Affix qw[:all];
use Affix               qw[:all];
#
$|++;
#
subtest import => sub {
    imported_ok qw[affix pin unpin wrap];
    imported_ok qw[libm libc];
    imported_ok qw[sizeof alignof offsetof];
    imported_ok qw[calloc free malloc realloc dump own
        memchr    memcmp     memcpy     memmove     memset
        strdup    strnlen
        ptr_add   ptr_diff
        address
        is_null
    ];
    imported_ok qw[load_library find_symbol ];
    imported_ok qw[typedef cast coerce];
    imported_ok qw[get_last_error_message];
    imported_ok qw[direct_affix direct_wrap];    # Secrets
};
subtest types => sub {
    imported_ok qw[
        Array     Bool    Callback Char   CodeRef Complex   Double Enum   File
        Float     Float32 Float64  Int    Int128  Int16     Int32  Int64  Int8    Long  LongDouble
        LongLong  M256    M256d    M512   M512d   M512i     Packed PerlIO Pointer SChar
        SInt128   SInt16  SInt32   SInt64 SInt8   SSize_t   SV
        Short     Size_t  String   Struct UChar   UInt      UInt128
        UInt16    UInt32  UInt64   UInt8  ULong   ULongLong UShort
        Union     VarArgs Vector   Void   WChar   WString    ];
    subtest abstract => sub {
        is Void,       'void',       'Void';
        is Bool,       'bool',       'Bool';
        is Char,       'char',       'Char';
        is UChar,      'uchar',      'UChar';
        is Short,      'short',      'Short';
        is UShort,     'ushort',     'UShort';
        is Int,        'int',        'Int';
        is UInt,       'uint',       'UInt';
        is Long,       'long',       'Long';
        is ULong,      'ulong',      'ULong';
        is LongLong,   'longlong',   'LongLong';
        is ULongLong,  'ulonglong',  'ULongLong';
        is Float,      'float',      'Float';
        is Double,     'double',     'Double';
        is LongDouble, 'longdouble', 'LongDouble';
        is SChar,      'char',       'SChar';
    };
    subtest explicit => sub {
        is SInt8,   'sint8',   'SInt8';
        is SInt16,  'sint16',  'SInt16';
        is SInt32,  'sint32',  'SInt32';
        is SInt64,  'sint64',  'SInt64';
        is SInt128, 'sint128', 'SInt128';
    };
    subtest SIMD => sub {
        is M256,  'm256',  'M256';
        is M256d, 'm256d', 'M256d';
        is M512,  'm512',  'M512';
        is M512d, 'm512d', 'M512d';
        is M512i, 'm512i', 'M512i';
    };
    subtest composite => sub {
        is Pointer [Void],             '*void',  'Pointer[Void]';
        is Pointer [Char],             '*char',  'Pointer[Char]';
        is Pointer [ Pointer [Void] ], '**void', 'Pointer[Pointer[Void]]';
        #
        is Struct [ name => Pointer [Char] ], '{name:*char}', 'Struct[ name => ... ]';
        is Struct [ name => Pointer [Char], dob => Struct [ y => Int, m => Int, d => Int ] ], '{name:*char,dob:{y:int,m:int,d:int}}',
            'Struct[ name => ..., dob => ...]';
        #
        is Union [ i => Int, f => Float ], '<i:int,f:float>', 'Union[...]';
    };
    subtest etc => sub {
        is SV,     '@SV',     'SV';
        is File,   '@File',   'File';
        is PerlIO, '@PerlIO', 'PerlIO';
    };
};

t/001_affix.t  view on Meta::CPAN

    return v;
}

DLLEXPORT long long multi_arg_sum(
    long long a, long long b, long long c, long long d,
    long long e, long long f, long long g, long long h, long long i
) {
    return a + b + c + d + e + f + g + h + i;
}
END_C
#
my $lib_path = compile_ok($C_CODE);
ok( $lib_path && -e $lib_path, 'Compiled a test shared library successfully' );
subtest 'Forward Calls: Comprehensive Primitives' => sub {
    for my ( $type, $value )(
        bool  => false,                                       #
        int8  => -100,           uint8  => 100,               #
        int16 => -30000,         uint16 => 60000,             #
        int32 => -2_000_000_000, uint32 => 4_000_000_000,     #
        int64 => -5_000_000_000, uint64 => 10_000_000_000,    #
        float =>  1.23,          double => -4.56              #
    ) {
        my $name = "echo_$type";
        my $sig  = "($type)->$type";
        isa_ok my $fn = wrap( $lib_path, $name, $sig ), ['Affix'], $sig;
        is $fn->($value), $value == int $value ? $value : float( $value, tolerance => 0.01 ), "Correctly passed and returned type '$type'";
    }
    subtest 'Float16 (via pointer)' => sub {
        isa_ok my $fn = wrap( $lib_path, 'echo_float16_ptr', [ Pointer [Float16], Pointer [Float16] ], Void ), ['Affix'];
        my $in  = 3.14;
        my $out = 0.0;
        $fn->( \$out, \$in );
        is $out, float( $in, tolerance => 0.01 ), 'Float16 passed correctly via pointer';
    };
};
subtest 'Bitfields' => sub {

    # Syntax: name : type : width
    my $struct = Struct [
        a => UInt32,
        3,                 # a : uint32 : 3
        b => UInt32, 5,    # b : uint32 : 5
        c => UInt32, 8,    # c : uint32 : 8
        d => UInt32, 16    # d : uint32 : 16
    ];
    isa_ok my $sum = wrap( $lib_path, 'sum_bitfield', [$struct] => UInt32 ), ['Affix'];
    is $sum->( { a => 7, b => 31, c => 255, d => 65535 } ), 7 + 31 + 255 + 65535, 'sum_bitfield (max values)';
    is $sum->( { a => 1, b => 2,  c => 3,   d => 4 } ),     1 + 2 + 3 + 4,        'sum_bitfield (small values)';
    isa_ok my $make = wrap( $lib_path, 'make_bitfield', [ UInt32, UInt32, UInt32, UInt32 ] => $struct ), ['Affix'];
    my $res = $make->( 2, 4, 8, 16 );
    is $res, { a => 2, b => 4, c => 8, d => 16 }, 'make_bitfield returns correct hash';
};
subtest 'Forward Call with Many Arguments' => sub {
    note 'Testing a C function with more arguments than available registers.';
    my $sig = '(int64, int64, int64, int64, int64, int64, int64, int64, int64)->int64';
    isa_ok my $summer = wrap( $lib_path, 'multi_arg_sum', $sig ), ['Affix'];
    my $result = $summer->( 1, 10, 100, 1000, 10000, 100000, 1000000, 10000000, 100000000 );
    is $result, 111111111, 'Correctly passed 9 arguments to a C function';
};
subtest 'Parser Error Reporting' => sub {
    note 'Testing that malformed signatures produce helpful error messages.';
    like warning { Affix::wrap( $lib_path, 'add', '(int, ^, int)->int' ) }, qr[parse signature], 'wrap() warning on invalid signature';
    like warning { Affix::sizeof('{int, double') },                         qr[parse signature], 'sizeof() warning on unterminated aggregate';
};
subtest 'These are called under valgrind in 900_leak' => sub {
    subtest 'use Affix' => sub {
        use Affix qw[];
        pass 'loaded';
    };
    subtest 'affix($$$$)' => sub {
        no warnings 'redefine';
        ok affix( libm, 'pow', [ Double, Double ], Double ), 'affix pow( Double, Double )';
        is pow( 5, 2 ), 25, 'pow(5, 2)';
    };
    subtest 'wrap($$$$)' => sub {
        isa_ok my $pow = wrap( libm, 'pow', [ Double, Double ], Double ), ['Affix'], 'double pow(double, double)';
        is $pow->( 5, 2 ), 25, '$pow->(5, 2)';
    };
    subtest 'return pointer' => sub {
        my $lib = compile_ok(<<'');
#include "std.h"
// ext: .c
void * test( ) { void * ret = "Testing"; return ret; }

        ok my $fn         = wrap( $lib, 'test', [] => Pointer [Void] ), 'affix';
        ok my $string_ptr = $fn->(),                                    'call';

        # Casting a pointer to String should return the Value "Testing"
        is Affix::cast( $string_ptr, String ), 'Testing', 'cast($ptr, String) returns value';
    }
};
subtest 'affix/wrap function pointer' => sub {
    my $lib = compile_ok(<<~'');
    #include "std.h"
    //ext: .c
    DLLEXPORT int add(int a, int b) { return a + b; }


    # Get address via find_symbol (simulating getting it from vtable or dlsym)
    my $ptr = find_symbol( load_library($lib), 'add' );
    ok $ptr, 'Got function pointer';

    # Test wrap(undef, $ptr, ...)
    subtest 'wrap(undef, $ptr, ...)' => sub {
        my $fn = wrap( undef, $ptr, [ Int, Int ] => Int );
        is $fn->( 10, 20 ), 30, 'Wrapped raw function pointer works';
    };

    # Test affix(undef, [$ptr => 'name'], ...)
    subtest 'affix(undef, [$ptr => name], ...)' => sub {
        affix( undef, [ $ptr => 'my_add' ], [ Int, Int ] => Int );
        is my_add( 5, 5 ), 10, 'Affixed raw function pointer works';
    };

    # Test wrap with explicit raw integer (simulating cast)
    subtest 'wrap(undef, int_addr, ...)' => sub {
        my $addr = address($ptr);                               # Convert Pin to UV
        my $fn   = wrap( undef, $addr, [ Int, Int ] => Int );
        is $fn->( 3, 4 ), 7, 'Wrapped raw integer address works';
    };
};



( run in 0.515 second using v1.01-cache-2.11-cpan-39bf76dae61 )