File Coverage

blib/lib/Test2/Tools/FFI.pm
Criterion Covered Total %
statement 86 91 94.5
branch 6 10 60.0
condition 7 12 58.3
subroutine 23 23 100.0
pod 0 1 0.0
total 122 137 89.0


line stmt bran cond sub pod time code
1             package Test2::Tools::FFI;
2              
3 3     3   1804 use strict;
  3         7  
  3         100  
4 3     3   27 use warnings;
  3         6  
  3         67  
5 3     3   86 use 5.008001;
  3         11  
6 3     3   15 use base qw( Exporter );
  3         10  
  3         318  
7 3     3   21 use FFI::Platypus;
  3         6  
  3         83  
8 3     3   15 use FFI::CheckLib 0.11 ();
  3         65  
  3         74  
9 3     3   18 use File::Basename ();
  3         6  
  3         46  
10 3     3   13 use Cwd ();
  3         4  
  3         56  
11 3     3   15 use File::Glob ();
  3         5  
  3         66  
12 3     3   14 use Test2::API qw( context );
  3         13  
  3         147  
13 3     3   27 use Test2::EventFacet::Trace;
  3         5  
  3         2410  
14              
15             # ABSTRACT: Tools for testing FFI
16             our $VERSION = '0.05'; # VERSION
17              
18             our @EXPORT = qw( ffi );
19              
20              
21             {
22             my $singleton;
23              
24             sub ffi
25             {
26 11 100   11 0 105299 unless($singleton)
27             {
28 1         7 $singleton = bless {}, 'Test2::Tools::FFI::Single';
29             }
30              
31 11         66 $singleton;
32             }
33             }
34              
35             sub _pass
36             {
37 1     1   71 my($name, @location) = @_;
38 1         6 my $ctx = context();
39 1         86 $ctx->send_event(
40             'Pass',
41             name => $name,
42             # this seems to swallow some info, be good
43             # to know if we need it.
44             trace => Test2::EventFacet::Trace->new(
45             frame => [@location],
46             )
47             );
48 1         185 $ctx->release;
49             }
50              
51             sub _fail
52             {
53 1     1   41 my($name, @location) = @_;
54 1         5 my $ctx = context();
55 1         82 $ctx->send_event(
56             'Fail',
57             name => $name,
58             trace => Test2::EventFacet::Trace->new(
59             frame => [@location],
60             )
61             );
62 1         234 $ctx->release;
63             }
64              
65             sub _note
66             {
67 2     2   194 my($message, @location) = @_;
68 2         7 my $ctx = context();
69 2         179 $ctx->send_event(
70             'Note',
71             message => $message,
72             trace => Test2::EventFacet::Trace->new(
73             frame => [@location],
74             )
75             );
76 2         499 $ctx->release;
77             }
78              
79             sub _diag
80             {
81 2     2   83 my($message, @location) = @_;
82 2         7 my $ctx = context();
83 2         164 $ctx->send_event(
84             'Diag',
85             message => $message,
86             trace => Test2::EventFacet::Trace->new(
87             frame => [@location],
88             )
89             );
90 2         435 $ctx->release;
91             }
92              
93             {
94             local $ENV{FFI_PLATYPUS_DLERROR} = 1;
95             our $ffi = FFI::Platypus->new;
96             our @closures = map { $ffi->closure($_) } \&_note, \&_diag, \&_pass, \&_fail;
97             $ffi->package;
98             $ffi->type('(string,string,string,int,string)->void' => 'message_cb_t');
99             $ffi
100             ->function(t2t_simple_init => ['message_cb_t','message_cb_t','message_cb_t','message_cb_t'] => 'void')
101             ->call(@closures);
102             }
103              
104             package Test2::Tools::FFI::Single;
105              
106              
107             sub runtime
108             {
109 4     4   9 my($self) = @_;
110              
111             $self->{runtime} ||= (sub {
112 1     1   13 my $ffi = Test2::Tools::FFI::Platypus->new;
113              
114 1         167 my @dll = File::Glob::bsd_glob("blib/lib/auto/share/dist/*/lib/*");
115 1 50       6 if(@dll)
116             {
117 1         10 $ffi->lib(@dll);
118 1         32 return $ffi;
119             }
120              
121 0         0 @dll = File::Glob::bsd_glob("share/lib/*");
122 0 0       0 if(@dll)
123             {
124 0         0 $ffi->lib(@dll);
125 0         0 return $ffi;
126             }
127 0         0 $ffi;
128 4   66     27 })->();
129             }
130              
131              
132             sub test
133             {
134 6     6   15 my($self) = @_;
135              
136 6   66     35 $self->{test} ||= do {
137 1         10 my $ffi = Test2::Tools::FFI::Platypus->new;
138 1         19 my @lib = FFI::CheckLib::find_lib(
139             lib => '*',
140             libpath => 't/ffi/_build',
141             systempath => [],
142             );
143 1 50       327 Carp::croak("unable to find test lib in t/ffi/_build")
144             unless @lib;
145 1         6 $ffi->lib(@lib);
146 1         20 $ffi;
147             };
148             }
149              
150              
151             sub combined
152             {
153 3     3   11 my($self) = @_;
154              
155 3   66     20 $self->{combined} ||= do {
156 1         4 my $rt = $self->runtime;
157 1         4 my $t = $self->test;
158 1         10 my $ffi = Test2::Tools::FFI::Platypus->new;
159 1         21 $ffi->lib($rt->lib, $t->lib);
160 1         34 $ffi;
161             };
162             }
163              
164             package Test2::Tools::FFI::Platypus;
165              
166 3     3   23 use base qw( FFI::Platypus );
  3         5  
  3         312  
167 3     3   36 use Test2::API ();
  3         6  
  3         490  
168              
169             sub symbol_ok
170             {
171 5     5   17 my($self, $symbol_name, $test_name) = @_;
172              
173 5   33     36 $test_name ||= "Library has symbol: $symbol_name";
174 5         17 my $address = $self->find_symbol($symbol_name);
175              
176 5         150 my $ctx = Test2::API::context();
177 5 100       401 if($address)
178             {
179 4         21 $ctx->pass_and_release($test_name);
180             }
181             else
182             {
183 1         6 $ctx->fail_and_release($test_name, map { "looked in $_" } $self->lib);
  1         17  
184             }
185             }
186              
187             1;
188              
189             __END__