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   945884 use strict;
  5         36  
  5         167  
4 5     5   25 use warnings;
  5         10  
  5         114  
5 5     5   116 use 5.008004;
  5         17  
6 5     5   3630 use Env qw( @PATH );
  5         10690  
  5         34  
7 5     5   2418 use File::Which 1.10 qw( which );
  5         3121  
  5         308  
8 5     5   2110 use Capture::Tiny qw( capture capture_merged );
  5         85326  
  5         347  
9 5     5   1735 use Alien::Build::Temp;
  5         14  
  5         190  
10 5     5   2226 use File::Copy qw( move );
  5         11182  
  5         344  
11 5     5   1951 use Text::ParseWords qw( shellwords );
  5         5380  
  5         318  
12 5     5   37 use Test2::API qw( context run_subtest );
  5         8  
  5         287  
13 5     5   30 use Exporter qw( import );
  5         11  
  5         134  
14 5     5   22 use Path::Tiny qw( path );
  5         11  
  5         211  
15 5     5   1879 use Alien::Build::Util qw( _dump );
  5         12  
  5         266  
16 5     5   35 use Config;
  5         11  
  5         12761  
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.45'; # VERSION
22              
23              
24             our @aliens;
25              
26             sub alien_ok ($;$)
27             {
28 12     12 1 22626 my($alien, $message) = @_;
29              
30 12 100       69 my $name = ref $alien ? ref($alien) . '[instance]' : $alien;
31 12 100       51 $name = 'undef' unless defined $name;
32 12         74 my @methods = qw( cflags libs dynamic_libs bin_dir );
33 12   33     135 $message ||= "$name responds to: @methods";
34              
35 12         25 my $ok;
36             my @diag;
37              
38 12 100       47 if(defined $alien)
39             {
40 11         24 my @missing = grep { ! $alien->can($_) } @methods;
  44         215  
41              
42 11         26 $ok = !@missing;
43 11         28 push @diag, map { " missing method $_" } @missing;
  4         9  
44              
45 11 100       33 if($ok)
46             {
47 10         25 push @aliens, $alien;
48 10         57 unshift @PATH, $alien->bin_dir;
49             }
50             }
51             else
52             {
53 1         3 $ok = 0;
54 1         3 push @diag, " undefined alien";
55             }
56              
57 12         555 my $ctx = context();
58 12         7077 $ctx->ok($ok, $message);
59 12         2416 $ctx->diag($_) for @diag;
60 12         826 $ctx->release;
61              
62 12         330 $ok;
63             }
64              
65              
66             sub synthetic
67             {
68 12     12 1 125610 my($opt) = @_;
69 12   100     71 $opt ||= {};
70 12         61 my %alien = %$opt;
71 12         2438 require Test::Alien::Synthetic;
72 12         140 bless \%alien, 'Test::Alien::Synthetic',
73             }
74              
75              
76             sub run_ok
77             {
78 6     6 1 14047 my($command, $message) = @_;
79              
80 6 50       31 my(@command) = ref $command ? @$command : ($command);
81 6   66     55 $message ||= "run @command";
82              
83 6         1418 require Test::Alien::Run;
84 6         101 my $run = bless {
85             out => '',
86             err => '',
87             exit => 0,
88             sig => 0,
89             cmd => [@command],
90             }, 'Test::Alien::Run';
91              
92 6         36 my $ctx = context();
93 6         632 my $exe = which $command[0];
94 6 100       276 if(defined $exe)
95             {
96 5         13 shift @command;
97 5         34 $run->{cmd} = [$exe, @command];
98 5         14 my @diag;
99 5         9 my $ok = 1;
100 5         9 my($exit, $errno);
101 5     5   298 ($run->{out}, $run->{err}, $exit, $errno) = capture { system $exe, @command; ($?,$!); };
  5         12877  
  5         34093  
102              
103 5 100       7066 if($exit == -1)
    100          
104             {
105 1         10 $ok = 0;
106 1         7 $run->{fail} = "failed to execute: $errno";
107 1         5 push @diag, " failed to execute: $errno";
108             }
109             elsif($exit & 127)
110             {
111 1         12 $ok = 0;
112 1         9 push @diag, " killed with signal: @{[ $exit & 127 ]}";
  1         17  
113 1         13 $run->{sig} = $exit & 127;
114             }
115             else
116             {
117 3         20 $run->{exit} = $exit >> 8;
118             }
119              
120 5         103 $ctx->ok($ok, $message);
121 5 100       2235 $ok
122             ? $ctx->note(" using $exe")
123             : $ctx->diag(" using $exe");
124 5         1823 $ctx->diag(@diag) for @diag;
125              
126             }
127             else
128             {
129 1         13 $ctx->ok(0, $message);
130 1         414 $ctx->diag(" command not found");
131 1         183 $run->{fail} = 'command not found';
132             }
133              
134 6 100       441 unless(@aliens)
135             {
136 1         13 $ctx->diag("run_ok called without any aliens, you may want to call alien_ok");
137             }
138              
139 6         353 $ctx->release;
140              
141 6         261 $run;
142             }
143              
144              
145             sub _flags
146             {
147 23     23   192 my($class, $method) = @_;
148 23         163 my $static = "${method}_static";
149 23 50 33     795 $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 42143 my $cb;
157 14 100 66     167 $cb = pop if defined $_[-1] && ref $_[-1] eq 'CODE';
158 14         47 my($xs, $message) = @_;
159 14   100     114 $message ||= 'xs';
160              
161 14 100       66 $xs = { xs => $xs } unless ref $xs;
162             # make sure this is a copy because we may
163             # modify it.
164 14         34 $xs->{xs} = "@{[ $xs->{xs} ]}";
  14         86  
165 14   50     112 $xs->{pxs} ||= {};
166 14   50     107 $xs->{cbuilder_check} ||= 'have_compiler';
167 14   100     86 $xs->{cbuilder_config} ||= {};
168 14   100     71 $xs->{cbuilder_compile} ||= {};
169 14   50     103 $xs->{cbuilder_link} ||= {};
170              
171 14         143 require ExtUtils::CBuilder;
172 14         32 my $skip = do {
173 14         29 my $have_compiler = $xs->{cbuilder_check};
174 14         220 !ExtUtils::CBuilder->new( config => $xs->{cbuilder_config} )->$have_compiler;
175             };
176              
177 14 100       808483 if($skip)
178             {
179 4         123 my $ctx = context();
180 4         440 $ctx->skip($message, 'test requires a compiler');
181 4 100       1421 $ctx->skip("$message subtest", 'test requires a compiler') if $cb;
182 4         563 $ctx->release;
183 4         116 return;
184             }
185              
186 10 50 33     2163 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     386 $xs->{c_ext} ||= 'c';
194             }
195              
196 10   100     232 my $verbose = $xs->{verbose} || 0;
197 10         55 my $ok = 1;
198 10         41 my @diag;
199 10 50       783 my $dir = Alien::Build::Temp->newdir(
200             TEMPLATE => 'test-alien-XXXXXX',
201             CLEANUP => $^O =~ /^(MSWin32|cygwin|msys)$/ ? 0 : 1,
202             );
203 10         9083 my $xs_filename = path($dir)->child('test.xs')->stringify;
204 10         1627 my $c_filename = path($dir)->child("test.@{[ $xs->{c_ext} ]}")->stringify;
  10         746  
205              
206 10         619 my $ctx = context();
207 10         4752 my $module;
208              
209 10 100       225 if($xs->{xs} =~ /\bTA_MODULE\b/)
210             {
211 6         46 our $count;
212 6 100       65 $count = 0 unless defined $count;
213 6         148 my $name = sprintf "Test::Alien::XS::Mod%s%s", $count, chr(65 + $count % 26 ) x 4;
214 6         35 $count++;
215 6         25 my $code = $xs->{xs};
216 6         214 $code =~ s{\bTA_MODULE\b}{$name}g;
217 6         39 $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       235 if($xs->{xs} =~ /^MODULE\s*=\s*([\w:]+)(?:\s+PACKAGE\s*=\s*([\w:]+))?(?:\s+PREFIX\s*=\s*(\S+))?\s*$/m)
225             {
226 8         108 $module = $1;
227 8 100       137 $ctx->note("detect module name $module") if $verbose;
228             }
229             else
230             {
231 2         8 $ok = 0;
232 2         28 push @diag, ' XS does not have a module decleration that we could find';
233             }
234              
235 10 100       3518 if($ok)
236             {
237 8         1163 open my $fh, '>', $xs_filename;
238 8         337 print $fh $xs->{xs};
239 8         483 close $fh;
240              
241 8         2013 require ExtUtils::ParseXS;
242 8         28794 my $pxs = ExtUtils::ParseXS->new;
243              
244             my($out, $err) = capture_merged {
245 8     8   14541 eval {
246             $pxs->process_file(
247             filename => $xs_filename,
248             output => $c_filename,
249             versioncheck => 0,
250             prototypes => 0,
251 8         56 %{ $xs->{pxs} },
  8         135  
252             );
253             };
254 8         395521 $@;
255 8         985 };
256              
257 8 100       10469 $ctx->note("parse xs $xs_filename => $c_filename") if $verbose;
258 8 100       2901 $ctx->note($out) if $verbose;
259 8 50 66     1023 $ctx->note("error: $err") if $verbose && $err;
260              
261 8 50       86 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       1676 push @diag, "xs_ok called without any aliens, you may want to call alien_ok" unless @aliens;
271              
272 10 100       43 if($ok)
273             {
274             my $cb = ExtUtils::CBuilder->new(
275 8         26 config => do {
276 8         20 my %config = %{ $xs->{cbuilder_config} };
  8         12208  
277 8         446 my $lddlflags = join(' ', grep !/^-l/, shellwords map { _flags $_, 'libs' } @aliens) . " $Config{lddlflags}";
  8         127  
278 8 50       724 $config{lddlflags} = defined $config{lddlflags} ? "$lddlflags $config{lddlflags}" : $lddlflags;
279 8         244 \%config;
280             },
281             );
282              
283             my %compile_options = (
284             source => $c_filename,
285 8         60972 %{ $xs->{cbuilder_compile} },
  8         119  
286             );
287              
288 8 100 100     89 if(defined $compile_options{extra_compiler_flags} && ref($compile_options{extra_compiler_flags}) eq '')
289             {
290 1         10 $compile_options{extra_compiler_flags} = [ shellwords $compile_options{extra_compiler_flags} ];
291             }
292              
293 8         111 push @{ $compile_options{extra_compiler_flags} }, shellwords map { _flags $_, 'cflags' } @aliens;
  8         51  
  8         27  
294              
295             my($out, $obj, $err) = capture_merged {
296 8     8   13215 my $obj = eval {
297 8         124 $cb->compile(%compile_options);
298             };
299 8         1672755 ($obj, $@);
300 8         1022 };
301              
302 8 100       14610 $ctx->note("compile $c_filename") if $verbose;
303 8 100       3302 $ctx->note($out) if $verbose;
304 8 50 66     1208 $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       55 unless($obj)
312             {
313 1         11 $ok = 0;
314 1         17 push @diag, ' ExtUtils::CBuilder->compile failed';
315 1 50       21 push @diag, " $err" if $err;
316 1         180 push @diag, " $_" for split /\r?\n/, $out;
317             }
318              
319 8 100       81 if($ok)
320             {
321              
322             my %link_options = (
323             objects => [$obj],
324             module_name => $module,
325 7         46 %{ $xs->{cbuilder_link} },
  7         179  
326             );
327              
328 7 50 33     98 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         52 unshift @{ $link_options{extra_linker_flags} }, grep /^-l/, shellwords map { _flags $_, 'libs' } @aliens;
  7         98  
  7         91  
334              
335             my($out, $lib, $err) = capture_merged {
336 7     7   12547 my $lib = eval {
337 7         102 $cb->link(%link_options);
338             };
339 7         245902 ($lib, $@);
340 7         1317 };
341              
342 7 100       11641 $ctx->note("link $obj") if $verbose;
343 7 100       3507 $ctx->note($out) if $verbose;
344 7 50 66     1232 $ctx->note($err) if $verbose && $err;
345              
346 7 50       93 if($verbose > 1)
347             {
348 0         0 $ctx->note(_dump({ link_options => \%link_options }));
349             }
350              
351 7 50 33     276 if($lib && -f $lib)
352             {
353 7 100       118 $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       1216 if($ok)
364             {
365 7         201 my @modparts = split(/::/,$module);
366 7         355 my $dl_dlext = $Config{dlext};
367 7         58 my $modfname = $modparts[-1];
368              
369 7         167 my $libpath = path($dir)->child('auto', @modparts, "$modfname.$dl_dlext");
370 7         2045 $libpath->parent->mkpath;
371 7 50       7109 move($lib, "$libpath") || die "unable to copy $lib => $libpath $!";
372              
373 7         1069 pop @modparts;
374 7         48 my $pmpath = path($dir)->child(@modparts, "$modfname.pm");
375 7         642 $pmpath->parent->mkpath;
376 7         3782 open my $fh, '>', "$pmpath";
377              
378 7         651 my($alien_with_xs_load, @rest) = grep { $_->can('xs_load') } @aliens;
  7         107  
379              
380 7 100       49 if($alien_with_xs_load)
381             {
382             {
383 5     5   49 no strict 'refs';
  5         13  
  5         8462  
  1         17  
384 1         13 @{join '::', $module, 'rest'} = @rest;
  1         71  
385 1         7 ${join '::', $module, 'alien_with_xs_load'} = $alien_with_xs_load;
  1         20  
386             }
387 1         21 print $fh '# line '. __LINE__ . ' "' . __FILE__ . qq("\n) . qq{
388             package $module;
389              
390 1     1   14 use strict;
  1         11  
  1         60  
391 1     1   7 use warnings;
  1         3  
  1         155  
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         177 print $fh '# line '. __LINE__ . ' "' . __FILE__ . qq("\n) . qq{
404             package $module;
405              
406 1     1   8 use strict;
  1     1   10  
  1     1   76  
  1     1   11  
  1     1   4  
  1     1   66  
  1         11  
  1         17  
  1         61  
  1         20  
  1         10  
  1         68  
  1         16  
  1         6  
  1         75  
  1         17  
  1         8  
  1         54  
407 1     1   12 use warnings;
  1     1   5  
  1     1   149  
  1     1   42  
  1     1   10  
  1     1   157  
  1         13  
  1         14  
  1         157  
  1         14  
  1         8  
  1         152  
  1         14  
  1         3  
  1         161  
  1         14  
  1         6  
  1         143  
408             require XSLoader;
409             our \$VERSION = '0.01';
410             XSLoader::load('$module',\$VERSION);
411              
412             1;
413             };
414             }
415 7         301 close $fh;
416              
417             {
418 7         27 local @INC = @INC;
  7         207  
419 7         48 unshift @INC, "$dir";
420             ## no critic
421 7         1268 eval '# line '. __LINE__ . ' "' . __FILE__ . qq("\n) . qq{
422 1     1   215 use $module;
  1     1   3  
  1     1   29  
  1     1   202  
  1     1   7  
  1     1   36  
  1     1   183  
  1         9  
  1         35  
  1         230  
  1         5  
  1         32  
  1         218  
  1         3  
  1         34  
  1         217  
  1         8  
  1         37  
  1         203  
  1         3  
  1         39  
423             };
424             ## use critic
425             }
426              
427 7 50       299 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         7795 $ctx->ok($ok, $message);
438 10         6014 $ctx->diag($_) for @diag;
439 10         13616 $ctx->release;
440              
441 10 100       565 if($cb)
442             {
443             $cb = sub {
444 1     1   2290 my $ctx = context();
445 1         993 $ctx->plan(0, 'SKIP', "subtest requires xs success");
446 0         0 $ctx->release;
447 6 100       63 } unless $ok;
448              
449 6         77 @_ = ("$message subtest", $cb, 1, $module);
450              
451 6         89 goto \&Test2::API::run_subtest;
452             }
453              
454 4         39 $ok;
455             }
456              
457             sub with_subtest (&)
458             {
459 6     6 0 22479 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       41 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         482 my $ctx = context();
472 1         122 $ctx->bail("Segmentation fault");
473 6     6   14302 };
474 6         52 $code->(@_);
475             }
476 6         76 }
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   847 require Alien::Build::Interpolate::Default;
581 14         97 my $intr = Alien::Build::Interpolate::Default->new;
582              
583 14         42 foreach my $alien (@aliens)
584             {
585 12 50       80 if($alien->can('alien_helper'))
586             {
587 12         45 my $help = $alien->alien_helper;
588 12         38 foreach my $name (keys %$help)
589             {
590 24         44 my $code = $help->{$name};
591 24         55 $intr->replace_helper($name, $code);
592             }
593             }
594             }
595              
596 14         29 $intr;
597             }
598              
599             sub helper_ok
600             {
601 6     6 1 9745 my($name, $message) = @_;
602              
603 6   66     53 $message ||= "helper $name exists";
604              
605 6         21 my $intr = _interpolator;
606              
607 6         25 my $code = $intr->has_helper($name);
608              
609 6         12 my $ok = defined $code;
610              
611 6         23 my $ctx = context();
612 6         709 $ctx->ok($ok, $message);
613 6 100       1352 $ctx->diag("helper_ok called without any aliens, you may want to call alien_ok") unless @aliens;
614 6         227 $ctx->release;
615              
616 6         153 $ok;
617             }
618              
619              
620             sub interpolate_template_is
621             {
622 8     8 1 13502 my($template, $pattern, $message) = @_;
623              
624 8   100     57 $message ||= "template matches";
625              
626 8         24 my $intr = _interpolator;
627              
628 8         15 my $value = eval { $intr->interpolate($template) };
  8         31  
629 8         17 my $error = $@;
630 8         16 my @diag;
631             my $ok;
632              
633 8 100       30 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         13 $ok = $value =~ $pattern;
642 2 100       9 push @diag, "value '$value' does not match $pattern'" unless $ok;
643             }
644             else
645             {
646 5         13 $ok = $value eq "$pattern";
647 5 100       17 push @diag, "value '$value' does not equal '$pattern'" unless $ok;
648             }
649              
650 8         26 my $ctx = context();
651 8         733 $ctx->ok($ok, $message, [@diag]);
652 8 100       2262 $ctx->diag('interpolate_template_is called without any aliens, you may want to call alien_ok') unless @aliens;
653 8         263 $ctx->release;
654              
655 8         182 $ok;
656             }
657              
658             1;
659              
660             __END__