File Coverage

blib/lib/Test/Alien.pm
Criterion Covered Total %
statement 362 435 83.2
branch 97 150 64.6
condition 36 67 53.7
subroutine 51 53 96.2
pod 7 8 87.5
total 553 713 77.5


line stmt bran cond sub pod time code
1             package Test::Alien;
2              
3 5     5   864978 use strict;
  5         49  
  5         141  
4 5     5   21 use warnings;
  5         11  
  5         113  
5 5     5   109 use 5.008004;
  5         17  
6 5     5   3325 use Env qw( @PATH );
  5         9659  
  5         27  
7 5     5   2550 use File::Which 1.10 qw( which );
  5         2958  
  5         290  
8 5     5   1949 use Capture::Tiny qw( capture capture_merged );
  5         77844  
  5         336  
9 5     5   1747 use Alien::Build::Temp;
  5         15  
  5         217  
10 5     5   2156 use File::Copy qw( move );
  5         8497  
  5         346  
11 5     5   1761 use Text::ParseWords qw( shellwords );
  5         4809  
  5         364  
12 5     5   34 use Test2::API qw( context run_subtest );
  5         11  
  5         278  
13 5     5   29 use Exporter qw( import );
  5         9  
  5         112  
14 5     5   23 use Path::Tiny qw( path );
  5         10  
  5         200  
15 5     5   1691 use Alien::Build::Util qw( _dump );
  5         13  
  5         289  
16 5     5   31 use Config;
  5         7  
  5         11714  
17              
18             our @EXPORT = qw( alien_ok run_ok xs_ok ffi_ok with_subtest synthetic helper_ok interpolate_template_is );
19              
20             # ABSTRACT: Testing tools for Alien modules
21             our $VERSION = '2.46'; # VERSION
22              
23              
24             our @aliens;
25              
26             sub alien_ok ($;$)
27             {
28 12     12 1 20588 my($alien, $message) = @_;
29              
30 12 100       56 my $name = ref $alien ? ref($alien) . '[instance]' : $alien;
31 12 100       36 $name = 'undef' unless defined $name;
32 12         40 my @methods = qw( cflags libs dynamic_libs bin_dir );
33 12   33     98 $message ||= "$name responds to: @methods";
34              
35 12         20 my $ok;
36             my @diag;
37              
38 12 100       30 if(defined $alien)
39             {
40 11         23 my @missing = grep { ! $alien->can($_) } @methods;
  44         183  
41              
42 11         21 $ok = !@missing;
43 11         29 push @diag, map { " missing method $_" } @missing;
  4         7  
44              
45 11 100       39 if($ok)
46             {
47 10         21 push @aliens, $alien;
48 10         40 unshift @PATH, $alien->bin_dir;
49             }
50             }
51             else
52             {
53 1         3 $ok = 0;
54 1         2 push @diag, " undefined alien";
55             }
56              
57 12         429 my $ctx = context();
58 12         5922 $ctx->ok($ok, $message);
59 12         1896 $ctx->diag($_) for @diag;
60 12         801 $ctx->release;
61              
62 12         304 $ok;
63             }
64              
65              
66             sub synthetic
67             {
68 12     12 1 117457 my($opt) = @_;
69 12   100     53 $opt ||= {};
70 12         45 my %alien = %$opt;
71 12         1945 require Test::Alien::Synthetic;
72 12         120 bless \%alien, 'Test::Alien::Synthetic',
73             }
74              
75              
76             sub run_ok
77             {
78 6     6 1 12177 my($command, $message) = @_;
79              
80 6 50       28 my(@command) = ref $command ? @$command : ($command);
81 6   66     44 $message ||= "run @command";
82              
83 6         1305 require Test::Alien::Run;
84 6         93 my $run = bless {
85             out => '',
86             err => '',
87             exit => 0,
88             sig => 0,
89             cmd => [@command],
90             }, 'Test::Alien::Run';
91              
92 6         20 my $ctx = context();
93 6         537 my $exe = which $command[0];
94 6 100       252 if(defined $exe)
95             {
96 5         11 shift @command;
97 5         36 $run->{cmd} = [$exe, @command];
98 5         10 my @diag;
99 5         9 my $ok = 1;
100 5         42 my($exit, $errno);
101 5     5   262 ($run->{out}, $run->{err}, $exit, $errno) = capture { system $exe, @command; ($?,$!); };
  5         12836  
  5         33563  
102              
103 5 100       6847 if($exit == -1)
    100          
104             {
105 1         3 $ok = 0;
106 1         4 $run->{fail} = "failed to execute: $errno";
107 1         5 push @diag, " failed to execute: $errno";
108             }
109             elsif($exit & 127)
110             {
111 1         14 $ok = 0;
112 1         8 push @diag, " killed with signal: @{[ $exit & 127 ]}";
  1         15  
113 1         10 $run->{sig} = $exit & 127;
114             }
115             else
116             {
117 3         30 $run->{exit} = $exit >> 8;
118             }
119              
120 5         99 $ctx->ok($ok, $message);
121 5 100       1941 $ok
122             ? $ctx->note(" using $exe")
123             : $ctx->diag(" using $exe");
124 5         1830 $ctx->diag(@diag) for @diag;
125              
126             }
127             else
128             {
129 1         4 $ctx->ok(0, $message);
130 1         288 $ctx->diag(" command not found");
131 1         151 $run->{fail} = 'command not found';
132             }
133              
134 6 100       364 unless(@aliens)
135             {
136 1         25 $ctx->diag("run_ok called without any aliens, you may want to call alien_ok");
137             }
138              
139 6         390 $ctx->release;
140              
141 6         253 $run;
142             }
143              
144              
145             sub _flags
146             {
147 23     23   128 my($class, $method) = @_;
148 23         104 my $static = "${method}_static";
149 23 50 33     721 $class->can($static) && $class->can('install_type') && $class->install_type eq 'share' && (!$class->can('xs_load'))
150             ? $class->$static
151             : $class->$method;
152             }
153              
154             sub xs_ok
155             {
156 14     14 1 33715 my $cb;
157 14 100 66     167 $cb = pop if defined $_[-1] && ref $_[-1] eq 'CODE';
158 14         45 my($xs, $message) = @_;
159 14   100     147 $message ||= 'xs';
160              
161 14 100       55 $xs = { xs => $xs } unless ref $xs;
162             # make sure this is a copy because we may
163             # modify it.
164 14         30 $xs->{xs} = "@{[ $xs->{xs} ]}";
  14         81  
165 14   50     103 $xs->{pxs} ||= {};
166 14   50     76 $xs->{cbuilder_check} ||= 'have_compiler';
167 14   100     70 $xs->{cbuilder_config} ||= {};
168 14   100     59 $xs->{cbuilder_compile} ||= {};
169 14   50     73 $xs->{cbuilder_link} ||= {};
170              
171 14         148 require ExtUtils::CBuilder;
172 14         31 my $skip = do {
173 14         35 my $have_compiler = $xs->{cbuilder_check};
174 14         172 !ExtUtils::CBuilder->new( config => $xs->{cbuilder_config} )->$have_compiler;
175             };
176              
177 14 100       736974 if($skip)
178             {
179 4         87 my $ctx = context();
180 4         337 $ctx->skip($message, 'test requires a compiler');
181 4 100       1092 $ctx->skip("$message subtest", 'test requires a compiler') if $cb;
182 4         441 $ctx->release;
183 4         97 return;
184             }
185              
186 10 50 33     2125 if($xs->{cpp} || $xs->{'C++'})
187             {
188 0         0 my $ctx = context();
189 0         0 $ctx->bail("The cpp and C++ options have been removed from xs_ok");
190             }
191             else
192             {
193 10   50     279 $xs->{c_ext} ||= 'c';
194             }
195              
196 10   100     168 my $verbose = $xs->{verbose} || 0;
197 10         41 my $ok = 1;
198 10         35 my @diag;
199 10 50       593 my $dir = Alien::Build::Temp->newdir(
200             TEMPLATE => 'test-alien-XXXXXX',
201             CLEANUP => $^O =~ /^(MSWin32|cygwin|msys)$/ ? 0 : 1,
202             );
203 10         7206 my $xs_filename = path($dir)->child('test.xs')->stringify;
204 10         1099 my $c_filename = path($dir)->child("test.@{[ $xs->{c_ext} ]}")->stringify;
  10         543  
205              
206 10         529 my $ctx = context();
207 10         3538 my $module;
208              
209 10 100       196 if($xs->{xs} =~ /\bTA_MODULE\b/)
210             {
211 6         34 our $count;
212 6 100       68 $count = 0 unless defined $count;
213 6         157 my $name = sprintf "Test::Alien::XS::Mod%s%s", $count, chr(65 + $count % 26 ) x 4;
214 6         29 $count++;
215 6         40 my $code = $xs->{xs};
216 6         182 $code =~ s{\bTA_MODULE\b}{$name}g;
217 6         49 $xs->{xs} = $code;
218             }
219              
220             # this regex copied shamefully from ExtUtils::ParseXS
221             # in part because we need the module name to do the bootstrap
222             # and also because if this regex doesn't match then ParseXS
223             # does an exit() which we don't want.
224 10 100       185 if($xs->{xs} =~ /^MODULE\s*=\s*([\w:]+)(?:\s+PACKAGE\s*=\s*([\w:]+))?(?:\s+PREFIX\s*=\s*(\S+))?\s*$/m)
225             {
226 8         46 $module = $1;
227 8 100       127 $ctx->note("detect module name $module") if $verbose;
228             }
229             else
230             {
231 2         118 $ok = 0;
232 2         22 push @diag, ' XS does not have a module decleration that we could find';
233             }
234              
235 10 100       2767 if($ok)
236             {
237 8         913 open my $fh, '>', $xs_filename;
238 8         280 print $fh $xs->{xs};
239 8         397 close $fh;
240              
241 8         1138 require ExtUtils::ParseXS;
242 8         19643 my $pxs = ExtUtils::ParseXS->new;
243              
244             my($out, $err) = capture_merged {
245 8     8   12742 eval {
246             $pxs->process_file(
247             filename => $xs_filename,
248             output => $c_filename,
249             versioncheck => 0,
250             prototypes => 0,
251 8         51 %{ $xs->{pxs} },
  8         135  
252             );
253             };
254 8         368897 $@;
255 8         856 };
256              
257 8 100       9813 $ctx->note("parse xs $xs_filename => $c_filename") if $verbose;
258 8 100       2534 $ctx->note($out) if $verbose;
259 8 50 66     890 $ctx->note("error: $err") if $verbose && $err;
260              
261 8 50       72 unless($pxs->report_error_count == 0)
262             {
263 0         0 $ok = 0;
264 0         0 push @diag, ' ExtUtils::ParseXS failed:';
265 0 0       0 push @diag, " $err" if $err;
266 0         0 push @diag, " $_" for split /\r?\n/, $out;
267             }
268             }
269              
270 10 100       1532 push @diag, "xs_ok called without any aliens, you may want to call alien_ok" unless @aliens;
271              
272 10 100       39 if($ok)
273             {
274             my $cb = ExtUtils::CBuilder->new(
275 8         27 config => do {
276 8         19 my %config = %{ $xs->{cbuilder_config} };
  8         10938  
277 8         406 my $lddlflags = join(' ', grep !/^-l/, shellwords map { _flags $_, 'libs' } @aliens) . " $Config{lddlflags}";
  8         86  
278 8 50       732 $config{lddlflags} = defined $config{lddlflags} ? "$lddlflags $config{lddlflags}" : $lddlflags;
279 8         219 \%config;
280             },
281             );
282              
283             my %compile_options = (
284             source => $c_filename,
285 8         54780 %{ $xs->{cbuilder_compile} },
  8         126  
286             );
287              
288 8 100 100     79 if(defined $compile_options{extra_compiler_flags} && ref($compile_options{extra_compiler_flags}) eq '')
289             {
290 1         17 $compile_options{extra_compiler_flags} = [ shellwords $compile_options{extra_compiler_flags} ];
291             }
292              
293 8         150 push @{ $compile_options{extra_compiler_flags} }, shellwords map { _flags $_, 'cflags' } @aliens;
  8         43  
  8         27  
294              
295             my($out, $obj, $err) = capture_merged {
296 8     8   12270 my $obj = eval {
297 8         111 $cb->compile(%compile_options);
298             };
299 8         1526745 ($obj, $@);
300 8         974 };
301              
302 8 100       12656 $ctx->note("compile $c_filename") if $verbose;
303 8 100       2996 $ctx->note($out) if $verbose;
304 8 50 66     1151 $ctx->note($err) if $verbose && $err;
305              
306 8 50       47 if($verbose > 1)
307             {
308 0         0 $ctx->note(_dump({ compile_options => \%compile_options }));
309             }
310              
311 8 100       51 unless($obj)
312             {
313 1         3 $ok = 0;
314 1         12 push @diag, ' ExtUtils::CBuilder->compile failed';
315 1 50       22 push @diag, " $err" if $err;
316 1         136 push @diag, " $_" for split /\r?\n/, $out;
317             }
318              
319 8 100       61 if($ok)
320             {
321              
322             my %link_options = (
323             objects => [$obj],
324             module_name => $module,
325 7         51 %{ $xs->{cbuilder_link} },
  7         487  
326             );
327              
328 7 50 33     92 if(defined $link_options{extra_linker_flags} && ref($link_options{extra_linker_flags}) eq '')
329             {
330 0         0 $link_options{extra_linker_flags} = [ shellwords $link_options{extra_linker_flags} ];
331             }
332              
333 7         55 unshift @{ $link_options{extra_linker_flags} }, grep /^-l/, shellwords map { _flags $_, 'libs' } @aliens;
  7         79  
  7         100  
334              
335             my($out, $lib, $err) = capture_merged {
336 7     7   11349 my $lib = eval {
337 7         117 $cb->link(%link_options);
338             };
339 7         227969 ($lib, $@);
340 7         1113 };
341              
342 7 100       11118 $ctx->note("link $obj") if $verbose;
343 7 100       2973 $ctx->note($out) if $verbose;
344 7 50 66     1138 $ctx->note($err) if $verbose && $err;
345              
346 7 50       54 if($verbose > 1)
347             {
348 0         0 $ctx->note(_dump({ link_options => \%link_options }));
349             }
350              
351 7 50 33     332 if($lib && -f $lib)
352             {
353 7 100       83 $ctx->note("created lib $lib") if $xs->{verbose};
354             }
355             else
356             {
357 0         0 $ok = 0;
358 0         0 push @diag, ' ExtUtils::CBuilder->link failed';
359 0 0       0 push @diag, " $err" if $err;
360 0         0 push @diag, " $_" for split /\r?\n/, $out;
361             }
362              
363 7 50       1010 if($ok)
364             {
365 7         181 my @modparts = split(/::/,$module);
366 7         355 my $dl_dlext = $Config{dlext};
367 7         54 my $modfname = $modparts[-1];
368              
369 7         142 my $libpath = path($dir)->child('auto', @modparts, "$modfname.$dl_dlext");
370 7         1474 $libpath->parent->mkpath;
371 7 50       6770 move($lib, "$libpath") || die "unable to copy $lib => $libpath $!";
372              
373 7         991 pop @modparts;
374 7         63 my $pmpath = path($dir)->child(@modparts, "$modfname.pm");
375 7         636 $pmpath->parent->mkpath;
376 7         2651 open my $fh, '>', "$pmpath";
377              
378 7         638 my($alien_with_xs_load, @rest) = grep { $_->can('xs_load') } @aliens;
  7         98  
379              
380 7 100       37 if($alien_with_xs_load)
381             {
382             {
383 5     5   49 no strict 'refs';
  5         16  
  5         7862  
  1         14  
384 1         11 @{join '::', $module, 'rest'} = @rest;
  1         64  
385 1         7 ${join '::', $module, 'alien_with_xs_load'} = $alien_with_xs_load;
  1         16  
386             }
387 1         26 print $fh '# line '. __LINE__ . ' "' . __FILE__ . qq("\n) . qq{
388             package $module;
389              
390 1     1   6 use strict;
  1         9  
  1         55  
391 1     1   11 use warnings;
  1         3  
  1         131  
392             our \$VERSION = '0.01';
393             our \@rest;
394             our \$alien_with_xs_load;
395              
396             \$alien_with_xs_load->xs_load('$module', \$VERSION, \@rest);
397              
398             1;
399             };
400             }
401             else
402             {
403 6         163 print $fh '# line '. __LINE__ . ' "' . __FILE__ . qq("\n) . qq{
404             package $module;
405              
406 1     1   8 use strict;
  1     1   7  
  1     1   162  
  1     1   15  
  1     1   6  
  1     1   55  
  1         8  
  1         5  
  1         57  
  1         13  
  1         9  
  1         56  
  1         16  
  1         10  
  1         61  
  1         15  
  1         8  
  1         71  
407 1     1   10 use warnings;
  1     1   2  
  1     1   166  
  1     1   8  
  1     1   2  
  1     1   126  
  1         14  
  1         6  
  1         138  
  1         13  
  1         6  
  1         141  
  1         10  
  1         8  
  1         142  
  1         9  
  1         3  
  1         153  
408             require XSLoader;
409             our \$VERSION = '0.01';
410             XSLoader::load('$module',\$VERSION);
411              
412             1;
413             };
414             }
415 7         307 close $fh;
416              
417             {
418 7         55 local @INC = @INC;
  7         220  
419 7         40 unshift @INC, "$dir";
420             ## no critic
421 7         1103 eval '# line '. __LINE__ . ' "' . __FILE__ . qq("\n) . qq{
422 1     1   206 use $module;
  1     1   6  
  1     1   25  
  1     1   166  
  1     1   9  
  1     1   24  
  1     1   199  
  1         3  
  1         26  
  1         177  
  1         6  
  1         22  
  1         188  
  1         8  
  1         33  
  1         196  
  1         9  
  1         35  
  1         196  
  1         9  
  1         32  
423             };
424             ## use critic
425             }
426              
427 7 50       324 if(my $error = $@)
428             {
429 0         0 $ok = 0;
430 0         0 push @diag, ' XSLoader failed';
431 0         0 push @diag, " $error";
432             }
433             }
434             }
435             }
436              
437 10         6894 $ctx->ok($ok, $message);
438 10         4672 $ctx->diag($_) for @diag;
439 10         10790 $ctx->release;
440              
441 10 100       463 if($cb)
442             {
443             $cb = sub {
444 1     1   1663 my $ctx = context();
445 1         111 $ctx->plan(0, 'SKIP', "subtest requires xs success");
446 0         0 $ctx->release;
447 6 100       49 } unless $ok;
448              
449 6         49 @_ = ("$message subtest", $cb, 1, $module);
450              
451 6         105 goto \&Test2::API::run_subtest;
452             }
453              
454 4         46 $ok;
455             }
456              
457             sub with_subtest (&)
458             {
459 6     6 0 21520 my($code) = @_;
460              
461             # it may be possible to catch a segmentation fault,
462             # but not with signal handlers apparently. See:
463             # https://feepingcreature.github.io/handling.html
464 6 50       37 return $code if $^O eq 'MSWin32';
465              
466             # try to catch a segmentation fault and bail out
467             # with a useful diagnostic. prove test to swallow
468             # the diagnostic on such failures.
469             sub {
470             local $SIG{SEGV} = sub {
471 1         502 my $ctx = context();
472 1         122 $ctx->bail("Segmentation fault");
473 6     6   13068 };
474 6         43 $code->(@_);
475             }
476 6         100 }
477              
478              
479             sub ffi_ok
480             {
481 0     0 1 0 my $cb;
482 0 0 0     0 $cb = pop if defined $_[-1] && ref $_[-1] eq 'CODE';
483 0         0 my($opt, $message) = @_;
484              
485 0   0     0 $message ||= 'ffi';
486              
487 0         0 my $ok = 1;
488 0         0 my $skip;
489             my $ffi;
490 0         0 my @diag;
491              
492             {
493 0         0 my $min = '0.12'; # the first CPAN release
  0         0  
494 0 0       0 $min = '0.15' if $opt->{ignore_not_found};
495 0 0       0 $min = '0.18' if $opt->{lang};
496 0 0 0     0 $min = '0.99' if defined $opt->{api} && $opt->{api} > 0;
497 0 0       0 unless(eval { require FFI::Platypus; FFI::Platypus->VERSION($min) })
  0         0  
  0         0  
498             {
499 0         0 $ok = 0;
500 0         0 $skip = "Test requires FFI::Platypus $min";
501             }
502             }
503              
504 0 0 0     0 if($ok && $opt->{lang})
505             {
506 0         0 my $class = "FFI::Platypus::Lang::@{[ $opt->{lang} ]}";
  0         0  
507             {
508 0         0 my $pm = "$class.pm";
  0         0  
509 0         0 $pm =~ s/::/\//g;
510 0         0 eval { require $pm };
  0         0  
511             }
512 0 0       0 if($@)
513             {
514 0         0 $ok = 0;
515 0         0 $skip = "Test requires FFI::Platypus::Lang::@{[ $opt->{lang} ]}";
  0         0  
516             }
517             }
518              
519 0 0       0 unless(@aliens)
520             {
521 0         0 push @diag, 'ffi_ok called without any aliens, you may want to call alien_ok';
522             }
523              
524 0 0       0 if($ok)
525             {
526             $ffi = FFI::Platypus->new(
527 0         0 do {
528             my @args = (
529 0         0 lib => [map { $_->dynamic_libs } @aliens],
530             ignore_not_found => $opt->{ignore_not_found},
531             lang => $opt->{lang},
532 0         0 );
533 0 0       0 push @args, api => $opt->{api} if defined $opt->{api};
534 0         0 @args;
535             }
536             );
537 0 0       0 foreach my $symbol (@{ $opt->{symbols} || [] })
  0         0  
538             {
539 0 0       0 unless($ffi->find_symbol($symbol))
540             {
541 0         0 $ok = 0;
542 0         0 push @diag, " $symbol not found"
543             }
544             }
545             }
546              
547 0         0 my $ctx = context();
548              
549 0 0       0 if($skip)
550             {
551 0         0 $ctx->skip($message, $skip);
552             }
553             else
554             {
555 0         0 $ctx->ok($ok, $message);
556             }
557 0         0 $ctx->diag($_) for @diag;
558              
559 0         0 $ctx->release;
560              
561 0 0       0 if($cb)
562             {
563             $cb = sub {
564 0     0   0 my $ctx = context();
565 0         0 $ctx->plan(0, 'SKIP', "subtest requires ffi success");
566 0         0 $ctx->release;
567 0 0       0 } unless $ok;
568              
569 0         0 @_ = ("$message subtest", $cb, 1, $ffi);
570              
571 0         0 goto \&Test2::API::run_subtest;
572             }
573              
574 0         0 $ok;
575             }
576              
577              
578             sub _interpolator
579             {
580 14     14   748 require Alien::Build::Interpolate::Default;
581 14         76 my $intr = Alien::Build::Interpolate::Default->new;
582              
583 14         32 foreach my $alien (@aliens)
584             {
585 12 50       55 if($alien->can('alien_helper'))
586             {
587 12         29 my $help = $alien->alien_helper;
588 12         35 foreach my $name (keys %$help)
589             {
590 24         28 my $code = $help->{$name};
591 24         46 $intr->replace_helper($name, $code);
592             }
593             }
594             }
595              
596 14         23 $intr;
597             }
598              
599             sub helper_ok
600             {
601 6     6 1 8829 my($name, $message) = @_;
602              
603 6   66     43 $message ||= "helper $name exists";
604              
605 6         19 my $intr = _interpolator;
606              
607 6         19 my $code = $intr->has_helper($name);
608              
609 6         19 my $ok = defined $code;
610              
611 6         17 my $ctx = context();
612 6         567 $ctx->ok($ok, $message);
613 6 100       1360 $ctx->diag("helper_ok called without any aliens, you may want to call alien_ok") unless @aliens;
614 6         222 $ctx->release;
615              
616 6         140 $ok;
617             }
618              
619              
620             sub interpolate_template_is
621             {
622 8     8 1 11848 my($template, $pattern, $message) = @_;
623              
624 8   100     45 $message ||= "template matches";
625              
626 8         16 my $intr = _interpolator;
627              
628 8         13 my $value = eval { $intr->interpolate($template) };
  8         23  
629 8         14 my $error = $@;
630 8         14 my @diag;
631             my $ok;
632              
633 8 100       22 if($error)
    100          
634             {
635 1         2 $ok = 0;
636 1         3 push @diag, "error in evaluation:";
637 1         3 push @diag, " $error";
638             }
639             elsif(ref($pattern) eq 'Regexp')
640             {
641 2         9 $ok = $value =~ $pattern;
642 2 100       7 push @diag, "value '$value' does not match $pattern'" unless $ok;
643             }
644             else
645             {
646 5         9 $ok = $value eq "$pattern";
647 5 100       13 push @diag, "value '$value' does not equal '$pattern'" unless $ok;
648             }
649              
650 8         21 my $ctx = context();
651 8         627 $ctx->ok($ok, $message, [@diag]);
652 8 100       1936 $ctx->diag('interpolate_template_is called without any aliens, you may want to call alien_ok') unless @aliens;
653 8         243 $ctx->release;
654              
655 8         183 $ok;
656             }
657              
658             1;
659              
660             __END__