Hello community, here is the log from the commit of package perl-indirect for openSUSE:Factory checked in at 2015-07-20 11:22:26 ++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++ Comparing /work/SRC/openSUSE:Factory/perl-indirect (Old) and /work/SRC/openSUSE:Factory/.perl-indirect.new (New) ++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
Package is "perl-indirect" Changes: -------- --- /work/SRC/openSUSE:Factory/perl-indirect/perl-indirect.changes 2015-04-15 16:22:36.000000000 +0200 +++ /work/SRC/openSUSE:Factory/.perl-indirect.new/perl-indirect.changes 2015-07-20 11:22:39.000000000 +0200 @@ -1,0 +2,12 @@ +Sat Jul 18 09:20:09 UTC 2015 - co...@suse.com + +- updated to 0.36 + see /usr/share/doc/packages/perl-indirect/Changes + + 0.36 2015-07-17 22:15 UTC + + Fix : [RT #104312] : fatal hides perl errors in modules + no indirect 'fatal' will no longer hide compilation errors + occurring before indirect constructs. + Thanks Lukas Mai for reporting. + +------------------------------------------------------------------- Old: ---- indirect-0.35.tar.gz New: ---- cpanspec.yml indirect-0.36.tar.gz ++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++ Other differences: ------------------ ++++++ perl-indirect.spec ++++++ --- /var/tmp/diff_new_pack.Spicoh/_old 2015-07-20 11:22:41.000000000 +0200 +++ /var/tmp/diff_new_pack.Spicoh/_new 2015-07-20 11:22:41.000000000 +0200 @@ -17,14 +17,15 @@ Name: perl-indirect -Version: 0.35 +Version: 0.36 Release: 0 %define cpan_name indirect -Summary: Lexically warn about using the indirect method call syntax. +Summary: Lexically warn about using the indirect method call syntax License: Artistic-1.0 or GPL-1.0+ Group: Development/Libraries/Perl Url: http://search.cpan.org/dist/indirect/ -Source: http://www.cpan.org/authors/id/V/VP/VPIT/%{cpan_name}-%{version}.tar.gz +Source0: http://www.cpan.org/authors/id/V/VP/VPIT/%{cpan_name}-%{version}.tar.gz +Source1: cpanspec.yml BuildRoot: %{_tmppath}/%{name}-%{version}-build BuildRequires: perl BuildRequires: perl-macros @@ -64,6 +65,6 @@ %files -f %{name}.files %defattr(-,root,root,755) -%doc Changes README +%doc Changes README samples %changelog ++++++ cpanspec.yml ++++++ --- #description_paragraphs: 3 #no_testing: broken upstream #sources: # - source1 # - source2 #patches: # foo.patch: -p1 # bar.patch: #preamble: |- # BuildRequires: gcc-c++ #post_prep: |- # hunspell=`pkg-config --libs hunspell | sed -e 's,-l,,; s, *,,g'` # sed -i -e "s,hunspell-X,$hunspell," t/00-prereq.t Makefile.PL #post_install: |- # sed on %{name}.files #license: SUSE-NonFree #skip_noarch: 1 #custom_build: |- #./Build build flags=%{?_smp_mflags} --myflag #custom_test: |- #startserver && make test #ignore_requires: Bizarre::Module ++++++ indirect-0.35.tar.gz -> indirect-0.36.tar.gz ++++++ diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' '--exclude=.svnignore' old/indirect-0.35/Changes new/indirect-0.36/Changes --- old/indirect-0.35/Changes 2015-04-06 23:56:15.000000000 +0200 +++ new/indirect-0.36/Changes 2015-07-17 23:49:32.000000000 +0200 @@ -1,5 +1,11 @@ Revision history for indirect +0.36 2015-07-17 22:15 UTC + + Fix : [RT #104312] : fatal hides perl errors in modules + no indirect 'fatal' will no longer hide compilation errors + occurring before indirect constructs. + Thanks Lukas Mai for reporting. + 0.35 2015-04-06 22:20 UTC + Fix : The module could end being disabled in one thread if it was first loaded in another thread and that thread was immediately diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' '--exclude=.svnignore' old/indirect-0.35/MANIFEST new/indirect-0.36/MANIFEST --- old/indirect-0.35/MANIFEST 2015-03-24 16:34:52.000000000 +0100 +++ new/indirect-0.36/MANIFEST 2015-07-17 23:45:44.000000000 +0200 @@ -20,6 +20,7 @@ t/30-scope.t t/31-hints.t t/32-global.t +t/33-compilation-errors.t t/40-threads.t t/41-threads-teardown.t t/42-threads-global.t @@ -38,6 +39,7 @@ t/lib/indirect/Test3.pm t/lib/indirect/Test4.pm t/lib/indirect/Test5.pm +t/lib/indirect/TestCompilationError.pm t/lib/indirect/TestRequired1.pm t/lib/indirect/TestRequired2.pm t/lib/indirect/TestRequired3X.pm diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' '--exclude=.svnignore' old/indirect-0.35/META.json new/indirect-0.36/META.json --- old/indirect-0.35/META.json 2015-04-06 23:57:29.000000000 +0200 +++ new/indirect-0.36/META.json 2015-07-17 23:51:54.000000000 +0200 @@ -4,7 +4,7 @@ "Vincent Pit <p...@profvince.com>" ], "dynamic_config" : 1, - "generated_by" : "ExtUtils::MakeMaker version 7.04, CPAN::Meta::Converter version 2.150001", + "generated_by" : "ExtUtils::MakeMaker version 7.0401, CPAN::Meta::Converter version 2.150005", "license" : [ "perl_5" ], @@ -25,9 +25,14 @@ "Carp" : "0", "Config" : "0", "ExtUtils::MakeMaker" : "0", + "IO::Handle" : "0", + "IO::Select" : "0", + "IPC::Open3" : "0", "POSIX" : "0", + "Socket" : "0", "Test::More" : "0", - "XSLoader" : "0" + "XSLoader" : "0", + "lib" : "0" } }, "configure" : { @@ -57,5 +62,6 @@ "url" : "http://git.profvince.com/?p=perl%2Fmodules%2Findirect.git" } }, - "version" : "0.35" + "version" : "0.36", + "x_serialization_backend" : "JSON::PP version 2.27300" } diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' '--exclude=.svnignore' old/indirect-0.35/META.yml new/indirect-0.36/META.yml --- old/indirect-0.35/META.yml 2015-04-06 23:57:29.000000000 +0200 +++ new/indirect-0.36/META.yml 2015-07-17 23:51:54.000000000 +0200 @@ -6,14 +6,19 @@ Carp: '0' Config: '0' ExtUtils::MakeMaker: '0' + IO::Handle: '0' + IO::Select: '0' + IPC::Open3: '0' POSIX: '0' + Socket: '0' Test::More: '0' XSLoader: '0' + lib: '0' configure_requires: Config: '0' ExtUtils::MakeMaker: '0' dynamic_config: 1 -generated_by: 'ExtUtils::MakeMaker version 7.04, CPAN::Meta::Converter version 2.150001' +generated_by: 'ExtUtils::MakeMaker version 7.0401, CPAN::Meta::Converter version 2.150005' license: perl meta-spec: url: http://module-build.sourceforge.net/META-spec-v1.4.html @@ -32,4 +37,5 @@ homepage: http://search.cpan.org/dist/indirect/ license: http://dev.perl.org/licenses/ repository: http://git.profvince.com/?p=perl%2Fmodules%2Findirect.git -version: '0.35' +version: '0.36' +x_serialization_backend: 'CPAN::Meta::YAML version 0.016' diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' '--exclude=.svnignore' old/indirect-0.35/Makefile.PL new/indirect-0.36/Makefile.PL --- old/indirect-0.35/Makefile.PL 2015-03-31 14:35:27.000000000 +0200 +++ new/indirect-0.36/Makefile.PL 2015-07-17 20:53:37.000000000 +0200 @@ -65,8 +65,13 @@ my %BUILD_REQUIRES =( 'Config' => 0, 'ExtUtils::MakeMaker' => 0, + 'IO::Handle' => 0, + 'IO::Select' => 0, + 'IPC::Open3' => 0, 'POSIX' => 0, + 'Socket' => 0, 'Test::More' => 0, + 'lib' => 0, %PREREQ_PM, ); diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' '--exclude=.svnignore' old/indirect-0.35/README new/indirect-0.36/README --- old/indirect-0.35/README 2015-04-06 23:57:29.000000000 +0200 +++ new/indirect-0.36/README 2015-07-17 23:51:54.000000000 +0200 @@ -2,7 +2,7 @@ indirect - Lexically warn about using the indirect method call syntax. VERSION - Version 0.35 + Version 0.36 SYNOPSIS In a script : @@ -169,10 +169,6 @@ is due to a shortcoming in the way perl handles the hints hash, which is addressed in perl 5.10. - Indirect constructs that appear in code "eval"'d during the global - destruction phase of a spawned thread or pseudo-fork (the processes used - internally for the "fork" emulation on Windows) are not reported. - The search for indirect method calls happens before constant folding. Hence "my $x = new Class if 0" will be caught. diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' '--exclude=.svnignore' old/indirect-0.35/indirect.xs new/indirect-0.36/indirect.xs --- old/indirect-0.35/indirect.xs 2015-04-06 23:57:05.000000000 +0200 +++ new/indirect-0.36/indirect.xs 2015-07-17 19:22:33.000000000 +0200 @@ -219,7 +219,8 @@ #if I_THREADSAFE #define PTABLE_NAME ptable_loaded -#define PTABLE_VAL_FREE(V) NOOP +#define PTABLE_NEED_DELETE 1 +#define PTABLE_NEED_WALK 0 #include "ptable.h" @@ -320,6 +321,8 @@ #define PTABLE_NAME ptable_hints #define PTABLE_VAL_FREE(V) I_HINT_FREE(V) +#define PTABLE_NEED_DELETE 0 +#define PTABLE_NEED_WALK 1 #define pPTBL pTHX #define pPTBL_ pTHX_ @@ -346,6 +349,8 @@ #define PTABLE_NAME ptable #define PTABLE_VAL_FREE(V) if (V) { Safefree(((indirect_op_info_t *) (V))->buf); Safefree(V); } +#define PTABLE_NEED_DELETE 1 +#define PTABLE_NEED_WALK 0 #define pPTBL pTHX #define pPTBL_ pTHX_ @@ -421,49 +426,10 @@ ptable_hints_store(ud->tbl, ent->key, h2); } -static void indirect_thread_cleanup(pTHX_ void *ud) { - int global_teardown; - dMY_CXT; - - global_teardown = indirect_clear_loaded_locked(&MY_CXT); - assert(!global_teardown); - - SvREFCNT_dec(MY_CXT.global_code); - MY_CXT.global_code = NULL; - - ptable_free(MY_CXT.map); - MY_CXT.map = NULL; - - ptable_hints_free(MY_CXT.tbl); - MY_CXT.tbl = NULL; -} - -static int indirect_endav_free(pTHX_ SV *sv, MAGIC *mg) { - SAVEDESTRUCTOR_X(indirect_thread_cleanup, NULL); - - return 0; -} - -static MGVTBL indirect_endav_vtbl = { - 0, - 0, - 0, - 0, - indirect_endav_free -#if MGf_COPY - , 0 -#endif -#if MGf_DUP - , 0 -#endif -#if MGf_LOCAL - , 0 -#endif -}; - #endif /* I_THREADSAFE */ #if I_WORKAROUND_REQUIRE_PROPAGATION + static IV indirect_require_tag(pTHX) { #define indirect_require_tag() indirect_require_tag(aTHX) const CV *cv, *outside; @@ -507,6 +473,7 @@ return PTR2IV(cv); } + #endif /* I_WORKAROUND_REQUIRE_PROPAGATION */ static SV *indirect_tag(pTHX_ SV *value) { @@ -671,6 +638,68 @@ ptable_delete(MY_CXT.map, o); } +/* --- Safe version of call_sv() ------------------------------------------- */ + +static I32 indirect_call_sv(pTHX_ SV *sv, I32 flags) { +#define indirect_call_sv(S, F) indirect_call_sv(aTHX_ (S), (F)) + I32 ret, cxix; + PERL_CONTEXT saved_cx; + SV *saved_errsv = NULL; + + if (SvTRUE(ERRSV)) { + if (IN_PERL_COMPILETIME && PL_errors) + sv_catsv(PL_errors, ERRSV); + else + saved_errsv = newSVsv(ERRSV); + SvCUR_set(ERRSV, 0); + } + + cxix = (cxstack_ix < cxstack_max) ? (cxstack_ix + 1) : Perl_cxinc(aTHX); + /* The last popped context will be reused by call_sv(), but our callers may + * still need its previous value. Back it up so that it isn't clobbered. */ + saved_cx = cxstack[cxix]; + + ret = call_sv(sv, flags | G_EVAL); + + cxstack[cxix] = saved_cx; + + if (SvTRUE(ERRSV)) { + /* Discard the old ERRSV, and reuse the variable to temporarily store the + * new one. */ + if (saved_errsv) + sv_setsv(saved_errsv, ERRSV); + else + saved_errsv = newSVsv(ERRSV); + SvCUR_set(ERRSV, 0); + /* Immediately flush all errors. */ + if (IN_PERL_COMPILETIME) { +#if I_HAS_PERL(5, 10, 0) || defined(PL_parser) + if (PL_parser) + ++PL_parser->error_count; +#elif defined(PL_error_count) + ++PL_error_count; +#else + ++PL_Ierror_count; +#endif + if (PL_errors) { + sv_setsv(ERRSV, PL_errors); + SvCUR_set(PL_errors, 0); + } + } + sv_catsv(ERRSV, saved_errsv); + SvREFCNT_dec(saved_errsv); + croak(NULL); + } else if (saved_errsv) { + /* If IN_PERL_COMPILETIME && PL_errors, then the old ERRSV has already been + * added to PL_errors. Otherwise, just restore it to ERRSV, as if no eval + * block has ever been executed. */ + sv_setsv(ERRSV, saved_errsv); + SvREFCNT_dec(saved_errsv); + } + + return ret; +} + /* --- Check functions ----------------------------------------------------- */ static int indirect_find(pTHX_ SV *name_sv, const char *line_bufptr, STRLEN *name_pos) { @@ -1028,7 +1057,7 @@ mPUSHu(moi->line); PUTBACK; - call_sv(code, G_VOID); + indirect_call_sv(code, G_VOID); PUTBACK; @@ -1046,11 +1075,6 @@ static void indirect_teardown(pTHX_ void *interp) { dMY_CXT; -#if I_MULTIPLICITY - if (aTHX != interp) - return; -#endif - I_LOADED_LOCK; if (indirect_clear_loaded_locked(&MY_CXT)) { @@ -1122,11 +1146,7 @@ MY_CXT.global_code = NULL; } -#if I_MULTIPLICITY - call_atexit(indirect_teardown, aTHX); -#else call_atexit(indirect_teardown, NULL); -#endif return; } @@ -1150,7 +1170,6 @@ PREINIT: ptable *t; SV *global_code_dup; - GV *gv; PPCODE: { indirect_ptable_clone_ud ud; @@ -1175,26 +1194,9 @@ I_LOADED_UNLOCK; } } - gv = gv_fetchpv(__PACKAGE__ "::_THREAD_CLEANUP", 0, SVt_PVCV); - if (gv) { - CV *cv = GvCV(gv); - if (!PL_endav) - PL_endav = newAV(); - SvREFCNT_inc(cv); - if (!av_store(PL_endav, av_len(PL_endav) + 1, (SV *) cv)) - SvREFCNT_dec(cv); - sv_magicext((SV *) PL_endav, NULL, PERL_MAGIC_ext, &indirect_endav_vtbl, NULL, 0); - } - XSRETURN(0); - -void -_THREAD_CLEANUP(...) -PROTOTYPE: DISABLE -PPCODE: - indirect_thread_cleanup(aTHX_ NULL); XSRETURN(0); -#endif +#endif /* I_THREADSAFE */ SV * _tag(SV *value) diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' '--exclude=.svnignore' old/indirect-0.35/lib/indirect.pm new/indirect-0.36/lib/indirect.pm --- old/indirect-0.35/lib/indirect.pm 2015-04-06 23:52:42.000000000 +0200 +++ new/indirect-0.36/lib/indirect.pm 2015-07-17 22:44:10.000000000 +0200 @@ -11,13 +11,13 @@ =head1 VERSION -Version 0.35 +Version 0.36 =cut our $VERSION; BEGIN { - $VERSION = '0.35'; + $VERSION = '0.36'; } =head1 SYNOPSIS @@ -250,8 +250,6 @@ With 5.8 perls, the pragma does not propagate into C<eval STRING>. This is due to a shortcoming in the way perl handles the hints hash, which is addressed in perl 5.10. -Indirect constructs that appear in code C<eval>'d during the global destruction phase of a spawned thread or pseudo-fork (the processes used internally for the C<fork> emulation on Windows) are not reported. - The search for indirect method calls happens before constant folding. Hence C<my $x = new Class if 0> will be caught. diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' '--exclude=.svnignore' old/indirect-0.35/ptable.h new/indirect-0.36/ptable.h --- old/indirect-0.35/ptable.h 2015-02-26 14:40:44.000000000 +0100 +++ new/indirect-0.36/ptable.h 2015-05-14 18:24:16.000000000 +0200 @@ -52,10 +52,6 @@ # define PTABLE_NAME ptable #endif -#ifndef PTABLE_VAL_FREE -# define PTABLE_VAL_FREE(V) -#endif - #ifndef PTABLE_JOIN # define PTABLE_PASTE(A, B) A ## B # define PTABLE_JOIN(A, B) PTABLE_PASTE(A, B) @@ -65,6 +61,14 @@ # define PTABLE_PREFIX(X) PTABLE_JOIN(PTABLE_NAME, X) #endif +#ifndef PTABLE_NEED_DELETE +# define PTABLE_NEED_DELETE 1 +#endif + +#ifndef PTABLE_NEED_WALK +# define PTABLE_NEED_WALK 1 +#endif + #ifndef ptable_ent typedef struct ptable_ent { struct ptable_ent *next; @@ -84,7 +88,7 @@ #endif /* !ptable */ #ifndef ptable_new -STATIC ptable *ptable_new(pPTBLMS) { +static ptable *ptable_new(pPTBLMS) { #define ptable_new() ptable_new(aPTBLMS) ptable *t = VOID2(ptable *, PerlMemShared_malloc(sizeof *t)); t->max = 15; @@ -101,7 +105,7 @@ #endif #ifndef ptable_find -STATIC ptable_ent *ptable_find(const ptable * const t, const void * const key) { +static ptable_ent *ptable_find(const ptable * const t, const void * const key) { #define ptable_find ptable_find ptable_ent *ent; const UV hash = PTABLE_HASH(key); @@ -117,7 +121,7 @@ #endif /* !ptable_find */ #ifndef ptable_fetch -STATIC void *ptable_fetch(const ptable * const t, const void * const key) { +static void *ptable_fetch(const ptable * const t, const void * const key) { #define ptable_fetch ptable_fetch const ptable_ent *const ent = ptable_find(t, key); @@ -126,7 +130,7 @@ #endif /* !ptable_fetch */ #ifndef ptable_split -STATIC void ptable_split(pPTBLMS_ ptable * const t) { +static void ptable_split(pPTBLMS_ ptable * const t) { #define ptable_split(T) ptable_split(aPTBLMS_ (T)) ptable_ent **ary = t->ary; const size_t oldsize = t->max + 1; @@ -156,12 +160,14 @@ } #endif /* !ptable_split */ -STATIC void PTABLE_PREFIX(_store)(pPTBL_ ptable * const t, const void * const key, void * const val) { +static void PTABLE_PREFIX(_store)(pPTBL_ ptable * const t, const void * const key, void * const val) { ptable_ent *ent = ptable_find(t, key); if (ent) { +#ifdef PTABLE_VAL_FREE void *oldval = ent->val; PTABLE_VAL_FREE(oldval); +#endif ent->val = val; } else if (val) { const size_t i = PTABLE_HASH(key) & t->max; @@ -176,7 +182,9 @@ } } -STATIC void PTABLE_PREFIX(_delete)(pPTBL_ ptable * const t, const void * const key) { +#if PTABLE_NEED_DELETE + +static void PTABLE_PREFIX(_delete)(pPTBL_ ptable * const t, const void * const key) { ptable_ent *prev, *ent; const size_t i = PTABLE_HASH(key) & t->max; @@ -192,13 +200,18 @@ prev->next = ent->next; else t->ary[i] = ent->next; +#ifdef PTABLE_VAL_FREE PTABLE_VAL_FREE(ent->val); +#endif PerlMemShared_free(ent); } } -#ifndef ptable_walk -STATIC void ptable_walk(pTHX_ ptable * const t, void (*cb)(pTHX_ ptable_ent *ent, void *userdata), void *userdata) { +#endif /* PTABLE_NEED_DELETE */ + +#if PTABLE_NEED_WALK && !defined(ptable_walk) + +static void ptable_walk(pTHX_ ptable * const t, void (*cb)(pTHX_ ptable_ent *ent, void *userdata), void *userdata) { #define ptable_walk(T, CB, UD) ptable_walk(aTHX_ (T), (CB), (UD)) if (t && t->items) { register ptable_ent ** const array = t->ary; @@ -211,9 +224,10 @@ } while (i--); } } -#endif /* !ptable_walk */ -STATIC void PTABLE_PREFIX(_clear)(pPTBL_ ptable * const t) { +#endif /* PTABLE_NEED_WALK && !defined(ptable_walk) */ + +static void PTABLE_PREFIX(_clear)(pPTBL_ ptable * const t) { if (t && t->items) { register ptable_ent ** const array = t->ary; size_t i = t->max; @@ -221,11 +235,12 @@ do { ptable_ent *entry = array[i]; while (entry) { - ptable_ent * const oentry = entry; - void *val = oentry->val; - entry = entry->next; - PTABLE_VAL_FREE(val); - PerlMemShared_free(oentry); + ptable_ent * const nentry = entry->next; +#ifdef PTABLE_VAL_FREE + PTABLE_VAL_FREE(entry->val); +#endif + PerlMemShared_free(entry); + entry = nentry; } array[i] = NULL; } while (i--); @@ -234,7 +249,7 @@ } } -STATIC void PTABLE_PREFIX(_free)(pPTBL_ ptable * const t) { +static void PTABLE_PREFIX(_free)(pPTBL_ ptable * const t) { if (!t) return; PTABLE_PREFIX(_clear)(aPTBL_ t); @@ -249,3 +264,6 @@ #undef PTABLE_NAME #undef PTABLE_VAL_FREE + +#undef PTABLE_NEED_DELETE +#undef PTABLE_NEED_WALK diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' '--exclude=.svnignore' old/indirect-0.35/t/09-load-threads.t new/indirect-0.36/t/09-load-threads.t --- old/indirect-0.35/t/09-load-threads.t 2015-04-06 19:10:14.000000000 +0200 +++ new/indirect-0.36/t/09-load-threads.t 2015-05-14 18:04:30.000000000 +0200 @@ -3,9 +3,6 @@ use strict; use warnings; -use lib 't/lib'; -use VPIT::TestHelpers; - BEGIN { delete $ENV{PERL_INDIRECT_PM_DISABLE} } my ($module, $thread_safe_var); @@ -32,29 +29,8 @@ # Keep the rest of the file untouched -BEGIN { - my $is_threadsafe; - - if (defined $thread_safe_var) { - my $stat = run_perl "require POSIX; require $module; exit($thread_safe_var ? POSIX::EXIT_SUCCESS() : POSIX::EXIT_FAILURE())"; - if (defined $stat) { - require POSIX; - my $res = $stat >> 8; - if ($res == POSIX::EXIT_SUCCESS()) { - $is_threadsafe = 1; - } elsif ($res == POSIX::EXIT_FAILURE()) { - $is_threadsafe = !1; - } - } - if (not defined $is_threadsafe) { - skip_all "Could not detect if $module is thread safe or not"; - } - } - - VPIT::TestHelpers->import( - threads => [ $module => $is_threadsafe ], - ) -} +use lib 't/lib'; +use VPIT::TestHelpers threads => [ $module, $thread_safe_var ]; my $could_not_create_thread = 'Could not create thread'; diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' '--exclude=.svnignore' old/indirect-0.35/t/33-compilation-errors.t new/indirect-0.36/t/33-compilation-errors.t --- old/indirect-0.35/t/33-compilation-errors.t 1970-01-01 01:00:00.000000000 +0100 +++ new/indirect-0.36/t/33-compilation-errors.t 2015-07-17 22:54:30.000000000 +0200 @@ -0,0 +1,68 @@ +#!perl + +use strict; +use warnings; + +use Test::More tests => 5; + +use lib 't/lib'; +use VPIT::TestHelpers 'capture'; + +BEGIN { delete $ENV{PERL_INDIRECT_PM_DISABLE} } + +sub compile_err_code { + my ($fatal) = @_; + + if ($fatal) { + $fatal = 'no indirect q[fatal]; sub foo { \$bar }'; + } else { + $fatal = 'no indirect;'; + } + + return "use strict; use warnings; $fatal; baz \$_; sub qux { \$ook }"; +} + +my $indirect_msg = qr/Indirect call of method "baz" on object "\$_"/; +my $core_err1 = qr/Global symbol "\$bar"/; +my $core_err2 = qr/Global symbol "\$ook"/; +my $aborted = qr/Execution of -e aborted due to compilation errors\./; +my $failed_req = qr/Compilation failed in require/; +my $line_end = qr/[^\n]*\n/; +my $compile_err_warn_exp = qr/$indirect_msg$line_end$core_err2$line_end/o; +my $compile_err_fatal_exp = qr/$core_err1$line_end$indirect_msg$line_end/o; + +SKIP: { + my ($stat, $out, $err) = capture_perl compile_err_code(0); + skip CAPTURE_PERL_FAILED($out) => 1 unless defined $stat; + like $err, qr/\A$compile_err_warn_exp$aborted$line_end\z/o, + 'no indirect warn does not hide compilation errors outside of eval'; +} + +SKIP: { + my $code = compile_err_code(0); + my ($stat, $out, $err) = capture_perl "eval q[$code]; die \$@ if \$@"; + skip CAPTURE_PERL_FAILED($out) => 1 unless defined $stat; + like $err, qr/\A$compile_err_warn_exp\z/o, + 'no indirect warn does not hide compilation errors inside of eval'; +} + +SKIP: { + my ($stat, $out, $err) = capture_perl compile_err_code(1); + skip CAPTURE_PERL_FAILED($out) => 1 unless defined $stat; + like $err, qr/\A$compile_err_fatal_exp\z/o, + 'no indirect fatal does not hide compilation errors outside of eval'; +} + +{ + local $@; + eval compile_err_code(1); + like $@, qr/\A$compile_err_fatal_exp\z/o, + 'no indirect fatal does not hide compilation errors inside of eval'; +} + +{ + local $@; + eval { require indirect::TestCompilationError }; + like $@, qr/\A$compile_err_fatal_exp$failed_req$line_end\z/o, + 'no indirect fatal does not hide compilation errors inside of require'; +} diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' '--exclude=.svnignore' old/indirect-0.35/t/40-threads.t new/indirect-0.36/t/40-threads.t --- old/indirect-0.35/t/40-threads.t 2015-03-24 16:10:15.000000000 +0100 +++ new/indirect-0.36/t/40-threads.t 2015-04-20 17:49:38.000000000 +0200 @@ -1,14 +1,10 @@ -#!perl -T +#!perl use strict; use warnings; -BEGIN { require indirect; } - use lib 't/lib'; -use VPIT::TestHelpers ( - threads => [ 'indirect' => indirect::I_THREADSAFE ], -); +use VPIT::TestHelpers threads => [ 'indirect' => 'indirect::I_THREADSAFE()' ]; use Test::Leaner; diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' '--exclude=.svnignore' old/indirect-0.35/t/41-threads-teardown.t new/indirect-0.36/t/41-threads-teardown.t --- old/indirect-0.35/t/41-threads-teardown.t 2015-03-24 16:19:23.000000000 +0100 +++ new/indirect-0.36/t/41-threads-teardown.t 2015-05-14 18:12:41.000000000 +0200 @@ -3,17 +3,14 @@ use strict; use warnings; -BEGIN { require indirect; } - use lib 't/lib'; use VPIT::TestHelpers ( - threads => [ 'indirect' => indirect::I_THREADSAFE ], + threads => [ 'indirect' => 'indirect::I_THREADSAFE()' ], + 'run_perl', ); use Test::Leaner tests => 3; -my $run_perl_failed = 'Could not execute perl subprocess'; - SKIP: { skip 'Fails on 5.8.2 and lower' => 1 if "$]" <= 5.008_002; @@ -33,7 +30,7 @@ eval q{return; no indirect hook => \&cb; new Z;}; exit $code; RUN - skip $run_perl_failed => 1 unless defined $status; + skip RUN_PERL_FAILED() => 1 unless defined $status; is $status, 0, 'loading the pragma in a thread and using it outside doesn\'t segfault'; } @@ -42,15 +39,15 @@ my $status = run_perl <<' RUN'; use threads; BEGIN { require indirect; } - sub X::DESTROY { eval 'no indirect; 1'; exit 1 if $@ } + sub X2::DESTROY { eval 'no indirect; 1'; exit 1 if $@ } threads->create(sub { - my $x = bless { }, 'X'; + my $x = bless { }, 'X2'; $x->{self} = $x; return; })->join; exit $code; RUN - skip $run_perl_failed => 1 unless defined $status; + skip RUN_PERL_FAILED() => 1 unless defined $status; is $status, 0, 'indirect can be loaded in eval STRING during global destruction at the end of a thread'; } @@ -60,15 +57,16 @@ use threads::shared; my $code : shared; $code = 0; - no indirect cb => sub { lock $code; ++$code }; - sub X::DESTROY { eval $_[0]->{code} } + no indirect hook => sub { lock $code; ++$code }; + sub X3::DESTROY { eval $_[0]->{code} } threads->create(sub { - my $x = bless { code => 'new Z' }, 'X'; + my $x = bless { code => 'new Z3' }, 'X3'; $x->{self} = $x; return; })->join; exit $code; RUN - skip $run_perl_failed => 1 unless defined $status; - is $status, 0, 'indirect does not check eval STRING during global destruction at the end of a thread'; + skip RUN_PERL_FAILED() => 1 unless defined $status; + my $code = $status >> 8; + is $code, 1, 'indirect checks eval STRING during global destruction at the end of a cloned thread'; } diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' '--exclude=.svnignore' old/indirect-0.35/t/42-threads-global.t new/indirect-0.36/t/42-threads-global.t --- old/indirect-0.35/t/42-threads-global.t 2015-04-06 15:40:17.000000000 +0200 +++ new/indirect-0.36/t/42-threads-global.t 2015-04-20 17:52:25.000000000 +0200 @@ -1,14 +1,10 @@ -#!perl -T +#!perl use strict; use warnings; -BEGIN { require indirect; } - use lib 't/lib'; -use VPIT::TestHelpers ( - threads => [ 'indirect' => indirect::I_THREADSAFE ], -); +use VPIT::TestHelpers threads => [ 'indirect' => 'indirect::I_THREADSAFE()' ]; use Test::Leaner; diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' '--exclude=.svnignore' old/indirect-0.35/t/50-external.t new/indirect-0.36/t/50-external.t --- old/indirect-0.35/t/50-external.t 2015-03-24 16:22:07.000000000 +0100 +++ new/indirect-0.36/t/50-external.t 2015-04-20 17:56:18.000000000 +0200 @@ -8,15 +8,13 @@ use Test::More tests => 6; use lib 't/lib'; -use VPIT::TestHelpers; +use VPIT::TestHelpers 'run_perl'; BEGIN { delete $ENV{PERL_INDIRECT_PM_DISABLE} } -my $run_perl_failed = 'Could not execute perl subprocess'; - SKIP: { my $status = run_perl 'no indirect; qq{a\x{100}b} =~ /\A[\x00-\x7f]*\z/;'; - skip $run_perl_failed => 1 unless defined $status; + skip RUN_PERL_FAILED() => 1 unless defined $status; is $status, 0, 'RT #47866'; } @@ -24,7 +22,7 @@ skip 'Fixed in core only since 5.12' => 1 unless "$]" >= 5.012; my $status = run_perl 'no indirect hook => sub { exit 2 }; new X'; - skip $run_perl_failed => 1 unless defined $status; + skip RUN_PERL_FAILED() => 1 unless defined $status; is $status, 2 << 8, 'no semicolon at the end of -e'; } @@ -32,7 +30,7 @@ load_or_skip('Devel::CallParser', undef, undef, 1); my $status = run_perl "use Devel::CallParser (); no indirect; sub ok { } ok 1"; - skip $run_perl_failed => 1 unless defined $status; + skip RUN_PERL_FAILED() => 1 unless defined $status; is $status, 0, 'indirect is not getting upset by Devel::CallParser'; } @@ -45,7 +43,7 @@ unless $has_package_empty; my $status = run_perl 'no indirect hook => sub { }; exit 0; package; new X;'; - skip $run_perl_failed => 1 unless defined $status; + skip RUN_PERL_FAILED() => 1 unless defined $status; is $status, 0, 'indirect does not croak while package empty is in use'; } @@ -62,10 +60,10 @@ => $tests unless $fork_status == 0; my $status = run_perl 'require indirect; END { eval q[1] } my $pid = fork; exit 0 unless defined $pid; if ($pid) { waitpid $pid, 0; my $status = $?; exit(($status >> 8) || $status) } else { exit 0 }'; - skip $run_perl_failed => $tests unless defined $status; + skip RUN_PERL_FAILED() => $tests unless defined $status; is $status, 0, 'indirect and global END blocks executed at the end of a forked process (RT #99083)'; $status = run_perl 'require indirect; my $pid = fork; exit 0 unless defined $pid; if ($pid) { waitpid $pid, 0; my $status = $?; exit(($status >> 8) || $status) } else { eval q[END { eval q(1) }]; exit 0 }'; - skip $run_perl_failed => ($tests - 1) unless defined $status; + skip RUN_PERL_FAILED() => ($tests - 1) unless defined $status; is $status, 0, 'indirect and local END blocks executed at the end of a forked process'; } diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' '--exclude=.svnignore' old/indirect-0.35/t/lib/VPIT/TestHelpers.pm new/indirect-0.36/t/lib/VPIT/TestHelpers.pm --- old/indirect-0.35/t/lib/VPIT/TestHelpers.pm 2015-03-24 16:06:42.000000000 +0100 +++ new/indirect-0.36/t/lib/VPIT/TestHelpers.pm 2015-07-17 20:49:08.000000000 +0200 @@ -5,6 +5,19 @@ use Config (); +=head1 NAME + +VPIT::TestHelpers + +=head1 SYNTAX + + use VPIT::TestHelpers ( + feature1 => \@feature1_args, + feature2 => \@feature2_args, + ); + +=cut + sub export_to_pkg { my ($subs, $pkg) = @_; @@ -16,16 +29,31 @@ return 1; } +sub sanitize_prefix { + my $prefix = shift; + + if (defined $prefix) { + if (length $prefix and $prefix !~ /_$/) { + $prefix .= '_'; + } + } else { + $prefix = ''; + } + + return $prefix; +} + my %default_exports = ( load_or_skip => \&load_or_skip, load_or_skip_all => \&load_or_skip_all, - run_perl => \&run_perl, skip_all => \&skip_all, ); my %features = ( - threads => \&init_threads, - usleep => \&init_usleep, + threads => \&init_threads, + usleep => \&init_usleep, + run_perl => \&init_run_perl, + capture => \&init_capture, ); sub import { @@ -141,12 +169,54 @@ return $loaded; } -sub run_perl { - my $code = shift; +=head1 FEATURES - if ($code =~ /"/) { - die 'Double quotes in evaluated code are not portable'; - } +=head2 C<run_perl> + +=over 4 + +=item * + +Import : + + use VPIT::TestHelpers run_perl => [ $p ] + +where : + +=over 8 + +=item - + +C<$p> is prefixed to the constants exported by this feature (defaults to C<''>). + +=back + +=item * + +Dependencies : none + +=item * + +Exports : + +=over 8 + +=item - + +C<run_perl $code> + +=item - + +C<RUN_PERL_FAILED> (possibly prefixed by C<$p>) + +=back + +=back + +=cut + +sub fresh_perl_env (&) { + my $handler = shift; my ($SystemRoot, $PATH) = @ENV{qw<SystemRoot PATH>}; my $ld_name = $Config::Config{ldlibpthname}; @@ -165,55 +235,423 @@ } } - system { $perl } $perl, '-T', map("-I$_", @INC), '-e', $code; + return $handler->($perl, '-T', map("-I$_", @INC)); } +sub init_run_perl { + my $p = sanitize_prefix(shift); + + return ( + run_perl => \&run_perl, + "${p}RUN_PERL_FAILED" => sub () { 'Could not execute perl subprocess' }, + ); +} + +sub run_perl { + my $code = shift; + + if ($code =~ /"/) { + die 'Double quotes in evaluated code are not portable'; + } + + fresh_perl_env { + my ($perl, @perl_args) = @_; + system { $perl } $perl, @perl_args, '-e', $code; + }; +} + +=head2 C<capture> + +=over 4 + +=item * + +Import : + + use VPIT::TestHelpers capture => [ $p ]; + +where : + +=over 8 + +=item - + +C<$p> is prefixed to the constants exported by this feature (defaults to C<''>). + +=back + +=item * + +Dependencies : + +=over 8 + +=item - + +Neither VMS nor OS/2 + +=item - + +L<IO::Handle> + +=item - + +L<IO::Select> + +=item - + +L<IPC::Open3> + +=item - + +On MSWin32 : L<Socket> + +=back + +=item * + +Exports : + +=over 8 + +=item - + +C<capture @command> + +=item - + +C<CAPTURE_FAILED $details> (possibly prefixed by C<$p>) + +=item - + +C<capture_perl $code> + +=item - + +C<CAPTURE_PERL_FAILED $details> (possibly prefixed by C<$p>) + +=back + +=back + +=cut + +sub init_capture { + my $p = sanitize_prefix(shift); + + skip_all 'Cannot capture output on VMS' if $^O eq 'VMS'; + skip_all 'Cannot capture output on OS/2' if $^O eq 'os2'; + + load_or_skip_all 'IO::Handle', '0', [ ]; + load_or_skip_all 'IO::Select', '0', [ ]; + load_or_skip_all 'IPC::Open3', '0', [ ]; + if ($^O eq 'MSWin32') { + load_or_skip_all 'Socket', '0', [ ]; + } + + return ( + capture => \&capture, + "${p}CAPTURE_FAILED" => \&capture_failed_msg, + capture_perl => \&capture_perl, + "${p}CAPTURE_PERL_FAILED" => \&capture_perl_failed_msg, + ); +} + +# Inspired from IPC::Cmd + +sub capture { + my @cmd = @_; + + my $want = wantarray; + + my $fail = sub { + my $err = $!; + my $ext_err = $^O eq 'MSWin32' ? $^E : undef; + + my $syscall = shift; + my $args = join ', ', @_; + + my $msg = "$syscall($args) failed: "; + + if (defined $err) { + no warnings 'numeric'; + my ($err_code, $err_str) = (int $err, "$err"); + $msg .= "$err_str ($err_code)"; + } + + if (defined $ext_err) { + no warnings 'numeric'; + my ($ext_err_code, $ext_err_str) = (int $ext_err, "$ext_err"); + $msg .= ", $ext_err_str ($ext_err_code)"; + } + + die "$msg\n"; + }; + + my ($status, $content_out, $content_err); + + local $@; + my $ok = eval { + my ($pid, $out, $err); + + if ($^O eq 'MSWin32') { + my $pipe = sub { + socketpair $_[0], $_[1], + &Socket::AF_UNIX, &Socket::SOCK_STREAM, &Socket::PF_UNSPEC + or $fail->(qw<socketpair reader writer>); + shutdown $_[0], 1 or $fail->(qw<shutdown reader>); + shutdown $_[1], 0 or $fail->(qw<shutdown writer>); + return 1; + }; + local (*IN_R, *IN_W); + local (*OUT_R, *OUT_W); + local (*ERR_R, *ERR_W); + $pipe->(*IN_R, *IN_W); + $pipe->(*OUT_R, *OUT_W); + $pipe->(*ERR_R, *ERR_W); + + $pid = IPC::Open3::open3('>&IN_R', '<&OUT_W', '<&ERR_W', @cmd); + + close *IN_W or $fail->(qw<close input>); + $out = *OUT_R; + $err = *ERR_R; + } else { + my $in = IO::Handle->new; + $out = IO::Handle->new; + $out->autoflush(1); + $err = IO::Handle->new; + $err->autoflush(1); + + $pid = IPC::Open3::open3($in, $out, $err, @cmd); + + close $in; + } + + # Forward signals to the child (except SIGKILL) + my %sig_handlers; + foreach my $s (keys %SIG) { + $sig_handlers{$s} = sub { + kill "$s" => $pid; + $SIG{$s} = $sig_handlers{$s}; + }; + } + local $SIG{$_} = $sig_handlers{$_} for keys %SIG; + + unless ($want) { + close $out or $fail->(qw<close output>); + close $err or $fail->(qw<close error>); + waitpid $pid, 0; + $status = $?; + return 1; + } + + my $sel = IO::Select->new(); + $sel->add($out, $err); + + my $fd_out = fileno $out; + my $fd_err = fileno $err; + + my %contents; + $contents{$fd_out} = ''; + $contents{$fd_err} = ''; + + while (my @ready = $sel->can_read) { + for my $fh (@ready) { + my $buf; + my $bytes_read = sysread $fh, $buf, 4096; + if (not defined $bytes_read) { + $fail->('sysread', 'fd(' . fileno($fh) . ')'); + } elsif ($bytes_read) { + $contents{fileno($fh)} .= $buf; + } else { + $sel->remove($fh); + close $fh or $fail->('close', 'fd(' . fileno($fh) . ')'); + last unless $sel->count; + } + } + } + + waitpid $pid, 0; + $status = $?; + + if ($^O eq 'MSWin32') { + # Manual CRLF translation that couldn't be done with sysread. + s/\x0D\x0A/\n/g for values %contents; + } + + $content_out = $contents{$fd_out}; + $content_err = $contents{$fd_err}; + + 1; + }; + + if ("$]" < 5.014 and $ok and ($status >> 8) == 255 and defined $content_err + and $content_err =~ /^open3/) { + # Before perl commit 8960aa87 (between 5.12 and 5.14), exceptions in open3 + # could be reported to STDERR instead of being propagated, so work around + # this. + $ok = 0; + $@ = $content_err; + } + + if ($ok) { + return ($status, $content_out, $content_err); + } else { + my $err = $@; + chomp $err; + return (undef, $err); + } +} + +sub capture_failed_msg { + my $details = shift; + + my $msg = 'Could not capture command output'; + $msg .= " ($details)" if defined $details; + + return $msg; +} + +sub capture_perl { + my $code = shift; + + if ($code =~ /"/) { + die 'Double quotes in evaluated code are not portable'; + } + + fresh_perl_env { + my @perl = @_; + capture @perl, '-e', $code; + }; +} + +sub capture_perl_failed_msg { + my $details = shift; + + my $msg = 'Could not capture perl output'; + $msg .= " ($details)" if defined $details; + + return $msg; +} + +=head2 C<threads> + +=over 4 + +=item * + +Import : + + use VPIT::TestHelpers threads => [ + $pkg, $threadsafe_var, $force_var + ]; + +where : + +=over 8 + +=item - + +C<$pkg> is the target package name that will be exercised by this test ; + +=item - + +C<$threadsafe_var> is the name of an optional variable in C<$pkg> that evaluates to true if and only if the module claims to be thread safe (not checked if either C<$threadsafe_var> or C<$pkg> is C<undef>) ; + +=item - + +C<$force_var> is the name of the environment variable that can be used to force the thread tests (defaults to C<PERL_FORCE_TEST_THREADS>). + +=back + +=item * + +Dependencies : + +=over 8 + +=item - + +C<perl> 5.13.4 + +=item - + +L<POSIX> + +=item - + +L<threads> 1.67 + +=item - + +L<threads::shared> 1.14 + +=back + +=item * + +Exports : + +=over 8 + +=item - + +C<spawn $coderef> + +=back + +=back + +=cut + sub init_threads { - my ($pkg, $threadsafe, $force_var) = @_; + my ($pkg, $threadsafe_var, $force_var) = @_; skip_all 'This perl wasn\'t built to support threads' unless $Config::Config{useithreads}; - $pkg = 'package' unless defined $pkg; - skip_all "This $pkg isn't thread safe" if defined $threadsafe and !$threadsafe; + if (defined $pkg and defined $threadsafe_var) { + my $threadsafe; + my $stat = run_perl("require POSIX; require $pkg; exit($threadsafe_var ? POSIX::EXIT_SUCCESS() : POSIX::EXIT_FAILURE())"); + if (defined $stat) { + require POSIX; + my $res = $stat >> 8; + if ($res == POSIX::EXIT_SUCCESS()) { + $threadsafe = 1; + } elsif ($res == POSIX::EXIT_FAILURE()) { + $threadsafe = !1; + } + } + if (not defined $threadsafe) { + skip_all "Could not detect if $pkg is thread safe or not"; + } elsif (not $threadsafe) { + skip_all "This $pkg is not thread safe"; + } + } $force_var = 'PERL_FORCE_TEST_THREADS' unless defined $force_var; my $force = $ENV{$force_var} ? 1 : !1; skip_all 'perl 5.13.4 required to test thread safety' unless $force or "$]" >= 5.013_004; - if (($INC{'Test/More.pm'} || $INC{'Test/Leaner.pm'}) && !$INC{'threads.pm'}) { - die 'Test::More/Test::Leaner was loaded too soon'; + unless ($INC{'threads.pm'}) { + my $test_module; + if ($INC{'Test/Leaner.pm'}) { + $test_module = 'Test::Leaner'; + } elsif ($INC{'Test/More.pm'}) { + $test_module = 'Test::More'; + } + die "$test_module was loaded too soon" if defined $test_module; } load_or_skip_all 'threads', $force ? '0' : '1.67', [ ]; load_or_skip_all 'threads::shared', $force ? '0' : '1.14', [ ]; - require Test::Leaner; - diag "Threads testing forced by \$ENV{$force_var}" if $force; return spawn => \&spawn; } -sub init_usleep { - my $usleep; - - if (do { local $@; eval { require Time::HiRes; 1 } }) { - defined and diag "Using usleep() from Time::HiRes $_" - for $Time::HiRes::VERSION; - $usleep = \&Time::HiRes::usleep; - } else { - diag 'Using fallback usleep()'; - $usleep = sub { - my $s = int($_[0] / 2.5e5); - sleep $s if $s; - }; - } - - return usleep => $usleep; -} - sub spawn { local $@; my @diag; @@ -226,6 +664,118 @@ return $thread ? $thread : (); } +=head2 C<usleep> + +=over 4 + +=item * + +Import : + + use VPIT::TestHelpers 'usleep' => [ @impls ]; + +where : + +=over 8 + +=item - + +C<@impls> is the list of desired implementations (which may be C<'Time::HiRes'>, C<'select'> or C<'sleep'>), in the order they should be checked. +When the list is empty, it defaults to all of them. + +=back + +=item * + +Dependencies : none + +=item * + +Exports : + +=over 8 + +=item - + +C<usleep $microseconds> + +=back + +=back + +=cut + +sub init_usleep { + my (@impls) = @_; + + my %impls = ( + 'Time::HiRes' => sub { + if (do { local $@; eval { require Time::HiRes; 1 } }) { + defined and diag "Using usleep() from Time::HiRes $_" + for $Time::HiRes::VERSION; + return \&Time::HiRes::usleep; + } else { + return undef; + } + }, + 'select' => sub { + if ($Config::Config{d_select}) { + diag 'Using select()-based fallback usleep()'; + return sub ($) { + my $s = $_[0]; + my $r = 0; + while ($s > 0) { + my ($found, $t) = select(undef, undef, undef, $s / 1e6); + last unless defined $t; + $t = int($t * 1e6); + $s -= $t; + $r += $t; + } + return $r; + }; + } else { + return undef; + } + }, + 'sleep' => sub { + diag 'Using sleep()-based fallback usleep()'; + return sub ($) { + my $ms = int $_[0]; + my $s = int($ms / 1e6) + ($ms % 1e6 == 0 ? 0 : 1); + my $t = sleep $s; + return $t * 1e6; + }; + }, + ); + + @impls = qw<Time::HiRes select sleep> unless @impls; + + my $usleep; + for my $impl (@impls) { + next unless defined $impl and $impls{$impl}; + $usleep = $impls{$impl}->(); + last if defined $usleep; + } + + skip_all "Could not find a suitable usleep() implementation among: @impls" + unless $usleep; + + return usleep => $usleep; +} + +=head1 CLASSES + +=head2 C<VPIT::TestHelpers::Guard> + +Syntax : + + { + my $guard = VPIT::TestHelpers::Guard->new($coderef); + ... + } # $codref called here + +=cut + package VPIT::TestHelpers::Guard; sub new { @@ -236,4 +786,16 @@ sub DESTROY { $_[0]->{code}->() } +=head1 AUTHOR + +Vincent Pit, C<< <perl at profvince.com> >>, L<http://www.profvince.com>. + +=head1 COPYRIGHT & LICENSE + +Copyright 2012,2013,2014,2015 Vincent Pit, all rights reserved. + +This program is free software; you can redistribute it and/or modify it under the same terms as Perl itself. + +=cut + 1; diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' '--exclude=.svnignore' old/indirect-0.35/t/lib/indirect/TestCompilationError.pm new/indirect-0.36/t/lib/indirect/TestCompilationError.pm --- old/indirect-0.35/t/lib/indirect/TestCompilationError.pm 1970-01-01 01:00:00.000000000 +0100 +++ new/indirect-0.36/t/lib/indirect/TestCompilationError.pm 2015-07-17 22:50:36.000000000 +0200 @@ -0,0 +1,8 @@ +package indirect::TestCompilationError; +use strict; +use warnings; +no indirect 'fatal'; +sub foo { $bar } +baz $_; +sub qux { $ook } +1