summaryrefslogtreecommitdiff
path: root/cpan/Test-Simple/t/Test2
diff options
context:
space:
mode:
authorChad Granum <exodist7@gmail.com>2016-05-10 07:44:27 -0700
committerTony Cook <tony@develop-help.com>2016-05-12 09:32:16 +1000
commitb4514920cd5cabccad6add35edf1bef258070a11 (patch)
tree5b325fe0b70f3820ce5da1163326e555b4240aa1 /cpan/Test-Simple/t/Test2
parentb25b06cfba95499e3ff101909adcc2c23aea0d58 (diff)
downloadperl-b4514920cd5cabccad6add35edf1bef258070a11.tar.gz
Update to the latest Test-Simple cpan dist
Diffstat (limited to 'cpan/Test-Simple/t/Test2')
-rw-r--r--cpan/Test-Simple/t/Test2/acceptance/try_it_done_testing.t26
-rw-r--r--cpan/Test-Simple/t/Test2/acceptance/try_it_fork.t34
-rw-r--r--cpan/Test-Simple/t/Test2/acceptance/try_it_no_plan.t24
-rw-r--r--cpan/Test-Simple/t/Test2/acceptance/try_it_plan.t24
-rw-r--r--cpan/Test-Simple/t/Test2/acceptance/try_it_skip.t16
-rw-r--r--cpan/Test-Simple/t/Test2/acceptance/try_it_threads.t35
-rw-r--r--cpan/Test-Simple/t/Test2/acceptance/try_it_todo.t52
-rw-r--r--cpan/Test-Simple/t/Test2/behavior/Subtest_buffer_formatter.t90
-rw-r--r--cpan/Test-Simple/t/Test2/behavior/Subtest_events.t17
-rw-r--r--cpan/Test-Simple/t/Test2/behavior/Subtest_plan.t19
-rw-r--r--cpan/Test-Simple/t/Test2/behavior/Taint.t23
-rw-r--r--cpan/Test-Simple/t/Test2/behavior/err_var.t15
-rw-r--r--cpan/Test-Simple/t/Test2/behavior/init_croak.t28
-rw-r--r--cpan/Test-Simple/t/Test2/behavior/nested_context_exception.t111
-rw-r--r--cpan/Test-Simple/t/Test2/behavior/no_load_api.t49
-rw-r--r--cpan/Test-Simple/t/Test2/legacy/TAP.t182
-rw-r--r--cpan/Test-Simple/t/Test2/modules/API.t266
-rw-r--r--cpan/Test-Simple/t/Test2/modules/API/Breakage.t89
-rw-r--r--cpan/Test-Simple/t/Test2/modules/API/Context.t444
-rw-r--r--cpan/Test-Simple/t/Test2/modules/API/Instance.t466
-rw-r--r--cpan/Test-Simple/t/Test2/modules/API/Stack.t79
-rw-r--r--cpan/Test-Simple/t/Test2/modules/Event.t40
-rw-r--r--cpan/Test-Simple/t/Test2/modules/Event/Bail.t29
-rw-r--r--cpan/Test-Simple/t/Test2/modules/Event/Diag.t31
-rw-r--r--cpan/Test-Simple/t/Test2/modules/Event/Exception.t17
-rw-r--r--cpan/Test-Simple/t/Test2/modules/Event/Note.t30
-rw-r--r--cpan/Test-Simple/t/Test2/modules/Event/Ok.t102
-rw-r--r--cpan/Test-Simple/t/Test2/modules/Event/Plan.t107
-rw-r--r--cpan/Test-Simple/t/Test2/modules/Event/Skip.t24
-rw-r--r--cpan/Test-Simple/t/Test2/modules/Event/Subtest.t30
-rw-r--r--cpan/Test-Simple/t/Test2/modules/Event/Waiting.t16
-rw-r--r--cpan/Test-Simple/t/Test2/modules/Formatter/TAP.t464
-rw-r--r--cpan/Test-Simple/t/Test2/modules/Hub.t484
-rw-r--r--cpan/Test-Simple/t/Test2/modules/Hub/Interceptor.t15
-rw-r--r--cpan/Test-Simple/t/Test2/modules/Hub/Interceptor/Terminator.t9
-rw-r--r--cpan/Test-Simple/t/Test2/modules/Hub/Subtest.t124
-rw-r--r--cpan/Test-Simple/t/Test2/modules/IPC.t19
-rw-r--r--cpan/Test-Simple/t/Test2/modules/IPC/Driver.t57
-rw-r--r--cpan/Test-Simple/t/Test2/modules/IPC/Driver/Files.t282
-rw-r--r--cpan/Test-Simple/t/Test2/modules/Util.t37
-rw-r--r--cpan/Test-Simple/t/Test2/modules/Util/ExternalMeta.t70
-rw-r--r--cpan/Test-Simple/t/Test2/modules/Util/HashBase.t105
-rw-r--r--cpan/Test-Simple/t/Test2/modules/Util/Trace.t41
-rw-r--r--cpan/Test-Simple/t/Test2/regression/gh_16.t23
-rw-r--r--cpan/Test-Simple/t/Test2/regression/ipc_files_abort_exit.t62
45 files changed, 4307 insertions, 0 deletions
diff --git a/cpan/Test-Simple/t/Test2/acceptance/try_it_done_testing.t b/cpan/Test-Simple/t/Test2/acceptance/try_it_done_testing.t
new file mode 100644
index 0000000000..7badf3e6ee
--- /dev/null
+++ b/cpan/Test-Simple/t/Test2/acceptance/try_it_done_testing.t
@@ -0,0 +1,26 @@
+use strict;
+use warnings;
+
+use Test2::API qw/context/;
+
+sub done_testing {
+ my $ctx = context();
+
+ die "Test Already ended!" if $ctx->hub->ended;
+ $ctx->hub->finalize($ctx->trace, 1);
+ $ctx->release;
+}
+
+sub ok($;$) {
+ my ($bool, $name) = @_;
+ my $ctx = context();
+ $ctx->ok($bool, $name);
+ $ctx->release;
+}
+
+ok(1, "First");
+ok(1, "Second");
+
+done_testing;
+
+1;
diff --git a/cpan/Test-Simple/t/Test2/acceptance/try_it_fork.t b/cpan/Test-Simple/t/Test2/acceptance/try_it_fork.t
new file mode 100644
index 0000000000..f6d72f643e
--- /dev/null
+++ b/cpan/Test-Simple/t/Test2/acceptance/try_it_fork.t
@@ -0,0 +1,34 @@
+use strict;
+use warnings;
+
+use Test2::Util qw/CAN_FORK/;
+use Test2::IPC;
+use Test2::API qw/context/;
+
+sub plan {
+ my $ctx = context();
+ $ctx->plan(@_);
+ $ctx->release;
+}
+
+sub ok($;$) {
+ my ($bool, $name) = @_;
+ my $ctx = context();
+ $ctx->ok($bool, $name);
+ $ctx->release;
+}
+
+plan(0, skip_all => 'System cannot fork') unless CAN_FORK();
+
+plan(6);
+
+for (1 .. 3) {
+ my $pid = fork;
+ die "Failed to fork" unless defined $pid;
+ next if $pid;
+ ok(1, "test 1 in pid $$");
+ ok(1, "test 2 in pid $$");
+ last;
+}
+
+1;
diff --git a/cpan/Test-Simple/t/Test2/acceptance/try_it_no_plan.t b/cpan/Test-Simple/t/Test2/acceptance/try_it_no_plan.t
new file mode 100644
index 0000000000..32dde2cc45
--- /dev/null
+++ b/cpan/Test-Simple/t/Test2/acceptance/try_it_no_plan.t
@@ -0,0 +1,24 @@
+use strict;
+use warnings;
+
+use Test2::API qw/context/;
+
+sub plan {
+ my $ctx = context();
+ $ctx->plan(@_);
+ $ctx->release;
+}
+
+sub ok($;$) {
+ my ($bool, $name) = @_;
+ my $ctx = context();
+ $ctx->ok($bool, $name);
+ $ctx->release;
+}
+
+plan(0, 'no_plan');
+
+ok(1, "First");
+ok(1, "Second");
+
+1;
diff --git a/cpan/Test-Simple/t/Test2/acceptance/try_it_plan.t b/cpan/Test-Simple/t/Test2/acceptance/try_it_plan.t
new file mode 100644
index 0000000000..3656d85d12
--- /dev/null
+++ b/cpan/Test-Simple/t/Test2/acceptance/try_it_plan.t
@@ -0,0 +1,24 @@
+use strict;
+use warnings;
+
+use Test2::API qw/context/;
+
+sub plan {
+ my $ctx = context();
+ $ctx->plan(@_);
+ $ctx->release;
+}
+
+sub ok($;$) {
+ my ($bool, $name) = @_;
+ my $ctx = context();
+ $ctx->ok($bool, $name);
+ $ctx->release;
+}
+
+plan(2);
+
+ok(1, "First");
+ok(1, "Second");
+
+1;
diff --git a/cpan/Test-Simple/t/Test2/acceptance/try_it_skip.t b/cpan/Test-Simple/t/Test2/acceptance/try_it_skip.t
new file mode 100644
index 0000000000..3816eb035a
--- /dev/null
+++ b/cpan/Test-Simple/t/Test2/acceptance/try_it_skip.t
@@ -0,0 +1,16 @@
+use strict;
+use warnings;
+
+use Test2::API qw/context/;
+
+sub plan {
+ my $ctx = context();
+ $ctx->plan(@_);
+ $ctx->release;
+}
+
+plan(0, skip_all => 'testing skip all');
+
+die "Should not see this";
+
+1;
diff --git a/cpan/Test-Simple/t/Test2/acceptance/try_it_threads.t b/cpan/Test-Simple/t/Test2/acceptance/try_it_threads.t
new file mode 100644
index 0000000000..e3201585fd
--- /dev/null
+++ b/cpan/Test-Simple/t/Test2/acceptance/try_it_threads.t
@@ -0,0 +1,35 @@
+use strict;
+use warnings;
+
+use Test2::Util qw/CAN_THREAD/;
+use Test2::IPC;
+use Test2::API qw/context/;
+
+sub plan {
+ my $ctx = context();
+ $ctx->plan(@_);
+ $ctx->release;
+}
+
+sub ok($;$) {
+ my ($bool, $name) = @_;
+ my $ctx = context();
+ $ctx->ok($bool, $name);
+ $ctx->release;
+}
+
+plan(0, skip_all => 'System does not have threads') unless CAN_THREAD();
+
+plan(6);
+
+require threads;
+threads->import;
+
+for (1 .. 3) {
+ threads->create(sub {
+ ok(1, "test 1 in thread " . threads->tid());
+ ok(1, "test 2 in thread " . threads->tid());
+ });
+}
+
+1;
diff --git a/cpan/Test-Simple/t/Test2/acceptance/try_it_todo.t b/cpan/Test-Simple/t/Test2/acceptance/try_it_todo.t
new file mode 100644
index 0000000000..5c5f6945ec
--- /dev/null
+++ b/cpan/Test-Simple/t/Test2/acceptance/try_it_todo.t
@@ -0,0 +1,52 @@
+use strict;
+use warnings;
+
+use Test2::API qw/context test2_stack/;
+
+sub done_testing {
+ my $ctx = context();
+
+ die "Test Already ended!" if $ctx->hub->ended;
+ $ctx->hub->finalize($ctx->trace, 1);
+ $ctx->release;
+}
+
+sub ok($;$) {
+ my ($bool, $name) = @_;
+ my $ctx = context();
+ $ctx->ok($bool, $name);
+ $ctx->release;
+}
+
+sub diag {
+ my $ctx = context();
+ $ctx->diag( join '', @_ );
+ $ctx->release;
+}
+
+ok(1, "First");
+
+my $filter = test2_stack->top->filter(sub {
+ my ($hub, $event) = @_;
+
+ # Turn a diag into a note
+ return Test2::Event::Note->new(%$event) if ref($event) eq 'Test2::Event::Diag';
+
+ # Set todo on ok's
+ if ($event->isa('Test2::Event::Ok')) {
+ $event->set_todo('here be dragons');
+ $event->set_effective_pass(1);
+ }
+
+ return $event;
+});
+
+ok(0, "Second");
+diag "should be a note";
+
+test2_stack->top->unfilter($filter);
+
+ok(1, "Third");
+diag "should be a diag";
+
+done_testing;
diff --git a/cpan/Test-Simple/t/Test2/behavior/Subtest_buffer_formatter.t b/cpan/Test-Simple/t/Test2/behavior/Subtest_buffer_formatter.t
new file mode 100644
index 0000000000..6aa0ffb6b3
--- /dev/null
+++ b/cpan/Test-Simple/t/Test2/behavior/Subtest_buffer_formatter.t
@@ -0,0 +1,90 @@
+use strict;
+use warnings;
+
+BEGIN { require "t/tools.pl" };
+
+use Test2::API qw/run_subtest intercept test2_stack/;
+
+{
+ package Formatter::Hide;
+ sub write { }
+ sub hide_buffered { 1 };
+
+ package Formatter::Show;
+ sub write { }
+ sub hide_buffered { 0 };
+
+ package Formatter::NA;
+ sub write { }
+}
+
+my %HAS_FORMATTER;
+
+my $events = intercept {
+ my $code = sub {
+ my $hub = test2_stack->top;
+ $HAS_FORMATTER{unbuffered_none} = $hub->format ? 1 : 0;
+ };
+ run_subtest('unbuffered', $code);
+
+ $code = sub {
+ my $hub = test2_stack->top;
+ $HAS_FORMATTER{buffered_none} = $hub->format ? 1 : 0;
+ };
+ run_subtest('buffered', $code, 'BUFFERED');
+
+
+ #####################
+ test2_stack->top->format(bless {}, 'Formatter::Hide');
+ $code = sub {
+ my $hub = test2_stack->top;
+ $HAS_FORMATTER{unbuffered_hide} = $hub->format ? 1 : 0;
+ };
+ run_subtest('unbuffered', $code);
+
+ $code = sub {
+ my $hub = test2_stack->top;
+ $HAS_FORMATTER{buffered_hide} = $hub->format ? 1 : 0;
+ };
+ run_subtest('buffered', $code, 'BUFFERED');
+
+
+ #####################
+ test2_stack->top->format(bless {}, 'Formatter::Show');
+ $code = sub {
+ my $hub = test2_stack->top;
+ $HAS_FORMATTER{unbuffered_show} = $hub->format ? 1 : 0;
+ };
+ run_subtest('unbuffered', $code);
+
+ $code = sub {
+ my $hub = test2_stack->top;
+ $HAS_FORMATTER{buffered_show} = $hub->format ? 1 : 0;
+ };
+ run_subtest('buffered', $code, 'BUFFERED');
+
+
+ #####################
+ $code = sub {
+ my $hub = test2_stack->top;
+ $HAS_FORMATTER{unbuffered_na} = $hub->format ? 1 : 0;
+ };
+ run_subtest('unbuffered', $code);
+
+ test2_stack->top->format(bless {}, 'Formatter::NA');
+ $code = sub {
+ my $hub = test2_stack->top;
+ $HAS_FORMATTER{buffered_na} = $hub->format ? 1 : 0;
+ };
+ run_subtest('buffered', $code, 'BUFFERED');
+};
+
+ok(!$HAS_FORMATTER{unbuffered_none}, "Unbuffered with no parent formatter has no formatter");
+ok( $HAS_FORMATTER{unbuffered_show}, "Unbuffered where parent has 'show' formatter has formatter");
+ok( $HAS_FORMATTER{unbuffered_hide}, "Unbuffered where parent has 'hide' formatter has formatter");
+
+ok(!$HAS_FORMATTER{buffered_none}, "Buffered with no parent formatter has no formatter");
+ok( $HAS_FORMATTER{buffered_show}, "Buffered where parent has 'show' formatter has formatter");
+ok(!$HAS_FORMATTER{buffered_hide}, "Buffered where parent has 'hide' formatter has no formatter");
+
+done_testing;
diff --git a/cpan/Test-Simple/t/Test2/behavior/Subtest_events.t b/cpan/Test-Simple/t/Test2/behavior/Subtest_events.t
new file mode 100644
index 0000000000..8113dd9078
--- /dev/null
+++ b/cpan/Test-Simple/t/Test2/behavior/Subtest_events.t
@@ -0,0 +1,17 @@
+use strict;
+use warnings;
+
+BEGIN { require "t/tools.pl" };
+
+use Test2::API qw/run_subtest intercept/;
+
+my $events = intercept {
+ my $code = sub { ok(1) };
+ run_subtest('blah', $code, 'buffered');
+};
+
+ok(!$events->[0]->in_subtest, "main event is not inside a subtest");
+ok($events->[0]->subtest_id, "Got subtest id");
+ok($events->[0]->subevents->[0]->in_subtest, "nested events are in the subtest");
+
+done_testing;
diff --git a/cpan/Test-Simple/t/Test2/behavior/Subtest_plan.t b/cpan/Test-Simple/t/Test2/behavior/Subtest_plan.t
new file mode 100644
index 0000000000..f3dab3ca6a
--- /dev/null
+++ b/cpan/Test-Simple/t/Test2/behavior/Subtest_plan.t
@@ -0,0 +1,19 @@
+use strict;
+use warnings;
+
+BEGIN { require "t/tools.pl" };
+
+use Test2::API qw/run_subtest intercept/;
+
+my $events = intercept {
+ my $code = sub { plan 4; ok(1) };
+ run_subtest('bad_plan', $code, 'buffered');
+};
+
+is(
+ $events->[-1]->message,
+ "Bad subtest plan, expected 4 but ran 1",
+ "Helpful message if subtest has a bad plan",
+);
+
+done_testing;
diff --git a/cpan/Test-Simple/t/Test2/behavior/Taint.t b/cpan/Test-Simple/t/Test2/behavior/Taint.t
new file mode 100644
index 0000000000..5af9298683
--- /dev/null
+++ b/cpan/Test-Simple/t/Test2/behavior/Taint.t
@@ -0,0 +1,23 @@
+#!/usr/bin/env perl -T
+# HARNESS-NO-FORMATTER
+
+use Test2::API qw/context/;
+
+sub ok($;$@) {
+ my ($bool, $name) = @_;
+ my $ctx = context();
+ $ctx->ok($bool, $name);
+ $ctx->release;
+ return $bool ? 1 : 0;
+}
+
+sub done_testing {
+ my $ctx = context();
+ $ctx->hub->finalize($ctx->trace, 1);
+ $ctx->release;
+}
+
+ok(1);
+ok(1);
+
+done_testing;
diff --git a/cpan/Test-Simple/t/Test2/behavior/err_var.t b/cpan/Test-Simple/t/Test2/behavior/err_var.t
new file mode 100644
index 0000000000..7d8bc7c4ee
--- /dev/null
+++ b/cpan/Test-Simple/t/Test2/behavior/err_var.t
@@ -0,0 +1,15 @@
+use strict;
+use warnings;
+
+use Test2::IPC;
+
+BEGIN { require "t/tools.pl" };
+
+{
+ local $! = 100;
+
+ is(0 + $!, 100, 'set $!');
+ is(0 + $!, 100, 'preserved $!');
+}
+
+done_testing;
diff --git a/cpan/Test-Simple/t/Test2/behavior/init_croak.t b/cpan/Test-Simple/t/Test2/behavior/init_croak.t
new file mode 100644
index 0000000000..e8eb44b8ac
--- /dev/null
+++ b/cpan/Test-Simple/t/Test2/behavior/init_croak.t
@@ -0,0 +1,28 @@
+use strict;
+use warnings;
+BEGIN { require "t/tools.pl" };
+
+BEGIN {
+ package Foo::Bar;
+ use Test2::Util::HashBase qw/foo bar baz/;
+ use Carp qw/croak/;
+
+ sub init {
+ my $self = shift;
+ croak "'foo' is a required attribute"
+ unless $self->{+FOO};
+ }
+}
+
+$@ = "";
+my ($file, $line) = (__FILE__, __LINE__ + 1);
+eval { my $one = Foo::Bar->new };
+my $err = $@;
+
+like(
+ $err,
+ qr/^'foo' is a required attribute at \Q$file\E line $line/,
+ "Croak does not report to HashBase from init"
+);
+
+done_testing;
diff --git a/cpan/Test-Simple/t/Test2/behavior/nested_context_exception.t b/cpan/Test-Simple/t/Test2/behavior/nested_context_exception.t
new file mode 100644
index 0000000000..04cbdb8067
--- /dev/null
+++ b/cpan/Test-Simple/t/Test2/behavior/nested_context_exception.t
@@ -0,0 +1,111 @@
+use strict;
+use warnings;
+BEGIN { $Test2::API::DO_DEPTH_CHECK = 1 }
+BEGIN { require "t/tools.pl" };
+
+use Test2::API qw/context/;
+
+sub outer {
+ my $code = shift;
+ my $ctx = context();
+
+ $ctx->note("outer");
+
+ my $out = eval { $code->() };
+
+ $ctx->release;
+
+ return $out;
+}
+
+sub dies {
+ my $ctx = context();
+ $ctx->note("dies");
+ die "Foo";
+}
+
+sub bad_store {
+ my $ctx = context();
+ $ctx->note("bad store");
+ return $ctx; # Emulate storing it somewhere
+}
+
+sub bad_simple {
+ my $ctx = context();
+ $ctx->note("bad simple");
+ return;
+}
+
+my @warnings;
+{
+ local $SIG{__WARN__} = sub { push @warnings => @_ };
+ eval { dies() };
+}
+ok(!@warnings, "no warnings") || diag @warnings;
+
+@warnings = ();
+my $keep = bad_store();
+eval { my $x = 1 }; # Ensure an eval changing $@ does not meddle.
+{
+ local $SIG{__WARN__} = sub { push @warnings => @_ };
+ ok(1, "random event");
+}
+ok(@warnings, "got warnings");
+like(
+ $warnings[0],
+ qr/context\(\) was called to retrieve an existing context/,
+ "got expected warning"
+);
+$keep = undef;
+
+{
+ @warnings = ();
+ local $SIG{__WARN__} = sub { push @warnings => @_ };
+ bad_simple();
+}
+ok(@warnings, "got warnings");
+like(
+ $warnings[0],
+ qr/A context appears to have been destroyed without first calling release/,
+ "got expected warning"
+);
+
+@warnings = ();
+outer(\&dies);
+{
+ local $SIG{__WARN__} = sub { push @warnings => @_ };
+ ok(1, "random event");
+}
+ok(!@warnings, "no warnings") || diag @warnings;
+
+
+
+@warnings = ();
+{
+ local $SIG{__WARN__} = sub { push @warnings => @_ };
+ outer(\&bad_store);
+}
+ok(@warnings, "got warnings");
+like(
+ $warnings[0],
+ qr/A context appears to have been destroyed without first calling release/,
+ "got expected warning"
+);
+
+
+
+{
+ @warnings = ();
+ local $SIG{__WARN__} = sub { push @warnings => @_ };
+ outer(\&bad_simple);
+}
+ok(@warnings, "got warnings") || diag @warnings;
+like(
+ $warnings[0],
+ qr/A context appears to have been destroyed without first calling release/,
+ "got expected warning"
+);
+
+
+
+done_testing;
diff --git a/cpan/Test-Simple/t/Test2/behavior/no_load_api.t b/cpan/Test-Simple/t/Test2/behavior/no_load_api.t
new file mode 100644
index 0000000000..eab0da82ae
--- /dev/null
+++ b/cpan/Test-Simple/t/Test2/behavior/no_load_api.t
@@ -0,0 +1,49 @@
+use strict;
+use warnings;
+use Data::Dumper;
+
+###############################################################################
+# #
+# This test is to insure certain objects do not load Test2::API directly or #
+# indirectly when being required. It is ok for import() to load Test2::API if #
+# necessary, but simply requiring the modules should not. #
+# #
+###############################################################################
+
+require Test2::Formatter;
+require Test2::Formatter::TAP;
+
+require Test2::Event;
+require Test2::Event::Bail;
+require Test2::Event::Diag;
+require Test2::Event::Exception;
+require Test2::Event::Note;
+require Test2::Event::Ok;
+require Test2::Event::Plan;
+require Test2::Event::Skip;
+require Test2::Event::Subtest;
+require Test2::Event::Waiting;
+
+require Test2::Util;
+require Test2::Util::ExternalMeta;
+require Test2::Util::HashBase;
+require Test2::Util::Trace;
+
+require Test2::Hub;
+require Test2::Hub::Interceptor;
+require Test2::Hub::Subtest;
+require Test2::Hub::Interceptor::Terminator;
+
+my @loaded = grep { $INC{$_} } qw{
+ Test2/API.pm
+ Test2/API/Instance.pm
+ Test2/API/Context.pm
+ Test2/API/Stack.pm
+};
+
+require "t/tools.pl";
+
+ok(!@loaded, "Test2::API was not loaded")
+ || diag("Loaded: " . Dumper(\@loaded));
+
+done_testing();
diff --git a/cpan/Test-Simple/t/Test2/legacy/TAP.t b/cpan/Test-Simple/t/Test2/legacy/TAP.t
new file mode 100644
index 0000000000..d98d7445f8
--- /dev/null
+++ b/cpan/Test-Simple/t/Test2/legacy/TAP.t
@@ -0,0 +1,182 @@
+use strict;
+use warnings;
+# HARNESS-NO-FORMATTER
+
+BEGIN { require "t/tools.pl" };
+
+#########################
+#
+# This test us here to insure that Ok, Diag, and Note events render the way
+# Test::More renders them, trailing whitespace and all.
+#
+#########################
+
+use Test2::API qw/test2_stack/;
+
+sub capture(&) {
+ my $code = shift;
+
+ my ($err, $out) = ("", "");
+
+ my $handles = test2_stack->top->format->handles;
+ my ($ok, $e);
+ {
+ my ($out_fh, $err_fh);
+
+ ($ok, $e) = try {
+ open($out_fh, '>', \$out) or die "Failed to open a temporary STDOUT: $!";
+ open($err_fh, '>', \$err) or die "Failed to open a temporary STDERR: $!";
+
+ test2_stack->top->format->set_handles([$out_fh, $err_fh, $out_fh]);
+
+ $code->();
+ };
+ }
+ test2_stack->top->format->set_handles($handles);
+
+ die $e unless $ok;
+
+ $err =~ s/ $/_/mg;
+ $out =~ s/ $/_/mg;
+
+ return {
+ STDOUT => $out,
+ STDERR => $err,
+ };
+}
+
+# The tools in tools.pl have some intentional differences from the Test::More
+# versions, these behave more like Test::More which is important for
+# back-compat.
+sub tm_ok($;$) {
+ my ($bool, $name) = @_;
+ my $ctx = context;
+
+ $name && (
+ (index($name, "#" ) >= 0 && $name =~ s|#|\\#|g),
+ (index($name, "\n") >= 0 && $name =~ s{\n}{\n# }sg)
+ );
+
+ my $ok = bless {
+ pass => $bool,
+ name => $name,
+ effective_pass => 1,
+ trace => $ctx->trace->snapshot,
+ }, 'Test2::Event::Ok';
+ # Do not call init
+
+ $ctx->hub->send($ok);
+ $ctx->release;
+ return $bool;
+}
+
+# Test::More actually does a bit more, but for this test we just want to see
+# what happens when message is a specific string, or undef.
+sub tm_diag {
+ my $ctx = context();
+ $ctx->diag(@_);
+ $ctx->release;
+}
+
+sub tm_note {
+ my $ctx = context();
+ $ctx->note(@_);
+ $ctx->release;
+}
+
+# Ensure the top hub is generated
+test2_stack->top;
+
+my $temp_hub = test2_stack->new_hub();
+my $diag = capture {
+ tm_diag(undef);
+ tm_diag("");
+ tm_diag(" ");
+ tm_diag("A");
+ tm_diag("\n");
+ tm_diag("\nB");
+ tm_diag("C\n");
+ tm_diag("\nD\n");
+ tm_diag("E\n\n");
+};
+
+my $note = capture {
+ tm_note(undef);
+ tm_note("");
+ tm_note(" ");
+ tm_note("A");
+ tm_note("\n");
+ tm_note("\nB");
+ tm_note("C\n");
+ tm_note("\nD\n");
+ tm_note("E\n\n");
+};
+
+my $ok = capture {
+ tm_ok(1);
+ tm_ok(1, "");
+ tm_ok(1, " ");
+ tm_ok(1, "A");
+ tm_ok(1, "\n");
+ tm_ok(1, "\nB");
+ tm_ok(1, "C\n");
+ tm_ok(1, "\nD\n");
+ tm_ok(1, "E\n\n");
+};
+test2_stack->pop($temp_hub);
+
+is($diag->{STDOUT}, "", "STDOUT is empty for diag");
+is($diag->{STDERR}, <<EOT, "STDERR for diag looks right");
+# undef
+#_
+# _
+# A
+#_
+#_
+# B
+# C
+#_
+# D
+# E
+#_
+EOT
+
+
+is($note->{STDERR}, "", "STDERR for note is empty");
+is($note->{STDOUT}, <<EOT, "STDOUT looks right for note");
+# undef
+#_
+# _
+# A
+#_
+#_
+# B
+# C
+#_
+# D
+# E
+#_
+EOT
+
+
+is($ok->{STDERR}, "", "STDERR for ok is empty");
+is($ok->{STDOUT}, <<EOT, "STDOUT looks right for ok");
+ok 1
+ok 2 -_
+ok 3 - _
+ok 4 - A
+ok 5 -_
+#_
+ok 6 -_
+# B
+ok 7 - C
+#_
+ok 8 -_
+# D
+#_
+ok 9 - E
+#_
+#_
+EOT
+
+done_testing;
diff --git a/cpan/Test-Simple/t/Test2/modules/API.t b/cpan/Test-Simple/t/Test2/modules/API.t
new file mode 100644
index 0000000000..c916d2a159
--- /dev/null
+++ b/cpan/Test-Simple/t/Test2/modules/API.t
@@ -0,0 +1,266 @@
+use strict;
+use warnings;
+
+use Test2::API;
+
+my ($LOADED, $INIT);
+BEGIN {
+ $INIT = Test2::API::test2_init_done;
+ $LOADED = Test2::API::test2_load_done;
+};
+
+use Test2::IPC;
+BEGIN { require "t/tools.pl" };
+use Test2::Util qw/get_tid/;
+my $CLASS = 'Test2::API';
+
+# Ensure we do not break backcompat later by removing anything
+ok(Test2::API->can($_), "$_ method is present") for qw{
+ context_do
+ no_context
+
+ test2_init_done
+ test2_load_done
+
+ test2_pid
+ test2_tid
+ test2_stack
+ test2_no_wait
+
+ test2_add_callback_context_init
+ test2_add_callback_context_release
+ test2_add_callback_exit
+ test2_add_callback_post_load
+ test2_list_context_init_callbacks
+ test2_list_context_release_callbacks
+ test2_list_exit_callbacks
+ test2_list_post_load_callbacks
+
+ test2_ipc
+ test2_ipc_drivers
+ test2_ipc_add_driver
+ test2_ipc_polling
+ test2_ipc_disable_polling
+ test2_ipc_enable_polling
+
+ test2_formatter
+ test2_formatters
+ test2_formatter_add
+ test2_formatter_set
+};
+
+ok(!$LOADED, "Was not load_done right away");
+ok(!$INIT, "Init was not done right away");
+ok(Test2::API::test2_load_done, "We loaded it");
+
+# Note: This is a check that stuff happens in an END block.
+{
+ {
+ package FOLLOW;
+
+ sub DESTROY {
+ return if $_[0]->{fixed};
+ print "not ok - Did not run end ($_[0]->{name})!";
+ $? = 255;
+ exit 255;
+ }
+ }
+
+ our $kill1 = bless {fixed => 0, name => "Custom Hook"}, 'FOLLOW';
+ Test2::API::test2_add_callback_exit(
+ sub {
+ print "# Running END hook\n";
+ $kill1->{fixed} = 1;
+ }
+ );
+
+ our $kill2 = bless {fixed => 0, name => "set exit"}, 'FOLLOW';
+ my $old = Test2::API::Instance->can('set_exit');
+ no warnings 'redefine';
+ *Test2::API::Instance::set_exit = sub {
+ $kill2->{fixed} = 1;
+ print "# Running set_exit\n";
+ $old->(@_);
+ };
+}
+
+ok($CLASS->can('test2_init_done')->(), "init is done.");
+ok($CLASS->can('test2_load_done')->(), "Test2 is finished loading");
+
+is($CLASS->can('test2_pid')->(), $$, "got pid");
+is($CLASS->can('test2_tid')->(), get_tid(), "got tid");
+
+ok($CLASS->can('test2_stack')->(), 'got stack');
+is($CLASS->can('test2_stack')->(), $CLASS->can('test2_stack')->(), "always get the same stack");
+
+ok($CLASS->can('test2_ipc')->(), 'got ipc');
+is($CLASS->can('test2_ipc')->(), $CLASS->can('test2_ipc')->(), "always get the same IPC");
+
+is_deeply([$CLASS->can('test2_ipc_drivers')->()], [qw/Test2::IPC::Driver::Files/], "Got driver list");
+
+# Verify it reports to the correct file/line, there was some trouble with this...
+my $file = __FILE__;
+my $line = __LINE__ + 1;
+my $warnings = warnings { $CLASS->can('test2_ipc_add_driver')->('fake') };
+like(
+ $warnings->[0],
+ qr{^IPC driver fake loaded too late to be used as the global ipc driver at \Q$file\E line $line},
+ "got warning about adding driver too late"
+);
+
+is_deeply([$CLASS->can('test2_ipc_drivers')->()], [qw/fake Test2::IPC::Driver::Files/], "Got updated list");
+
+ok($CLASS->can('test2_ipc_polling')->(), "Polling is on");
+$CLASS->can('test2_ipc_disable_polling')->();
+ok(!$CLASS->can('test2_ipc_polling')->(), "Polling is off");
+$CLASS->can('test2_ipc_enable_polling')->();
+ok($CLASS->can('test2_ipc_polling')->(), "Polling is on");
+
+ok($CLASS->can('test2_formatter')->(), "Got a formatter");
+is($CLASS->can('test2_formatter')->(), $CLASS->can('test2_formatter')->(), "always get the same Formatter (class name)");
+
+my $ran = 0;
+$CLASS->can('test2_add_callback_post_load')->(sub { $ran++ });
+is($ran, 1, "ran the post-load");
+
+like(
+ exception { $CLASS->can('test2_formatter_set')->() },
+ qr/No formatter specified/,
+ "formatter_set requires an argument"
+);
+
+like(
+ exception { $CLASS->can('test2_formatter_set')->('fake') },
+ qr/Global Formatter already set/,
+ "formatter_set doesn't work after initialization",
+);
+
+ok(!$CLASS->can('test2_no_wait')->(), "no_wait is not set");
+$CLASS->can('test2_no_wait')->(1);
+ok($CLASS->can('test2_no_wait')->(), "no_wait is set");
+$CLASS->can('test2_no_wait')->(undef);
+ok(!$CLASS->can('test2_no_wait')->(), "no_wait is not set");
+
+my $pctx;
+sub tool_a($;$) {
+ Test2::API::context_do {
+ my $ctx = shift;
+ my ($bool, $name) = @_;
+ $pctx = wantarray;
+ die "xyz" unless $bool;
+ $ctx->ok($bool, $name);
+ return unless defined $pctx;
+ return (1, 2) if $pctx;
+ return 'a';
+ } @_;
+}
+
+$pctx = 'x';
+tool_a(1, "void context test");
+ok(!defined($pctx), "void context");
+
+my $x = tool_a(1, "scalar context test");
+ok(defined($pctx) && $pctx == 0, "scalar context");
+is($x, 'a', "got scalar return");
+
+my @x = tool_a(1, "array context test");
+ok($pctx, "array context");
+is_deeply(\@x, [1, 2], "Got array return");
+
+like(
+ exception { tool_a(0) },
+ qr/^xyz/,
+ "got exception"
+);
+
+sub {
+ my $outer = context();
+ sub {
+ my $middle = context();
+ is($outer->trace, $middle->trace, "got the same context before calling no_context");
+
+ Test2::API::no_context {
+ my $inner = context();
+ ok($inner->trace != $outer->trace, "Got a different context inside of no_context()");
+ $inner->release;
+ };
+
+ $middle->release;
+ }->();
+
+ $outer->release;
+}->();
+
+sub {
+ my $outer = context();
+ sub {
+ my $middle = context();
+ is($outer->trace, $middle->trace, "got the same context before calling no_context");
+
+ Test2::API::no_context {
+ my $inner = context();
+ ok($inner->trace != $outer->trace, "Got a different context inside of no_context({}, hid)");
+ $inner->release;
+ } $outer->hub->hid;
+
+ $middle->release;
+ }->();
+
+ $outer->release;
+}->();
+
+sub {
+ my @warnings;
+ my $outer = context();
+ sub {
+ my $middle = context();
+ is($outer->trace, $middle->trace, "got the same context before calling no_context");
+
+ local $SIG{__WARN__} = sub { push @warnings => @_ };
+ Test2::API::no_context {
+ my $inner = context();
+ ok($inner->trace != $outer->trace, "Got a different context inside of no_context({}, hid)");
+ } $outer->hub->hid;
+
+ $middle->release;
+ }->();
+
+ $outer->release;
+
+ is(@warnings, 1, "1 warning");
+ like(
+ $warnings[0],
+ qr/A context appears to have been destroyed without first calling release/,
+ "Got warning about unreleased context"
+ );
+}->();
+
+
+my $sub = sub { };
+
+Test2::API::test2_add_callback_context_acquire($sub);
+Test2::API::test2_add_callback_context_init($sub);
+Test2::API::test2_add_callback_context_release($sub);
+Test2::API::test2_add_callback_exit($sub);
+Test2::API::test2_add_callback_post_load($sub);
+
+is((grep { $_ == $sub } Test2::API::test2_list_context_acquire_callbacks()), 1, "got the one instance of the hook");
+is((grep { $_ == $sub } Test2::API::test2_list_context_init_callbacks()), 1, "got the one instance of the hook");
+is((grep { $_ == $sub } Test2::API::test2_list_context_release_callbacks()), 1, "got the one instance of the hook");
+is((grep { $_ == $sub } Test2::API::test2_list_exit_callbacks()), 1, "got the one instance of the hook");
+is((grep { $_ == $sub } Test2::API::test2_list_post_load_callbacks()), 1, "got the one instance of the hook");
+
+Test2::API::test2_add_callback_context_acquire($sub);
+Test2::API::test2_add_callback_context_init($sub);
+Test2::API::test2_add_callback_context_release($sub);
+Test2::API::test2_add_callback_exit($sub);
+Test2::API::test2_add_callback_post_load($sub);
+
+is((grep { $_ == $sub } Test2::API::test2_list_context_acquire_callbacks()), 2, "got the two instances of the hook");
+is((grep { $_ == $sub } Test2::API::test2_list_context_init_callbacks()), 2, "got the two instances of the hook");
+is((grep { $_ == $sub } Test2::API::test2_list_context_release_callbacks()), 2, "got the two instances of the hook");
+is((grep { $_ == $sub } Test2::API::test2_list_exit_callbacks()), 2, "got the two instances of the hook");
+is((grep { $_ == $sub } Test2::API::test2_list_post_load_callbacks()), 2, "got the two instances of the hook");
+
+done_testing;
+
diff --git a/cpan/Test-Simple/t/Test2/modules/API/Breakage.t b/cpan/Test-Simple/t/Test2/modules/API/Breakage.t
new file mode 100644
index 0000000000..2069c93626
--- /dev/null
+++ b/cpan/Test-Simple/t/Test2/modules/API/Breakage.t
@@ -0,0 +1,89 @@
+use strict;
+use warnings;
+
+use Test2::IPC;
+BEGIN { require "t/tools.pl" };
+use Test2::API::Breakage;
+my $CLASS = 'Test2::API::Breakage';
+
+for my $meth (qw/upgrade_suggested upgrade_required known_broken/) {
+ my @list = $CLASS->$meth;
+ ok(!(@list % 2), "Got even list ($meth)");
+ ok(!(grep {!defined($_)} @list), "No undefined items ($meth)");
+}
+
+{
+ no warnings 'redefine';
+ local *Test2::API::Breakage::upgrade_suggested = sub {
+ return ('T2Test::UG1' => '1.0', 'T2Test::UG2' => '0.5');
+ };
+
+ local *Test2::API::Breakage::upgrade_required = sub {
+ return ('T2Test::UR1' => '1.0', 'T2Test::UR2' => '0.5');
+ };
+
+ local *Test2::API::Breakage::known_broken = sub {
+ return ('T2Test::KB1' => '1.0', 'T2Test::KB2' => '0.5');
+ };
+ use warnings 'redefine';
+
+ ok(!$CLASS->report, "Nothing to report");
+ ok(!$CLASS->report(1), "Still nothing to report");
+
+ {
+ local %INC = (
+ %INC,
+ 'T2Test/UG1.pm' => 1,
+ 'T2Test/UG2.pm' => 1,
+ 'T2Test/UR1.pm' => 1,
+ 'T2Test/UR2.pm' => 1,
+ 'T2Test/KB1.pm' => 1,
+ 'T2Test/KB2.pm' => 1,
+ );
+ local $T2Test::UG1::VERSION = '0.9';
+ local $T2Test::UG2::VERSION = '0.9';
+ local $T2Test::UR1::VERSION = '0.9';
+ local $T2Test::UR2::VERSION = '0.9';
+ local $T2Test::KB1::VERSION = '0.9';
+ local $T2Test::KB2::VERSION = '0.9';
+
+ my @report = $CLASS->report;
+
+ is_deeply(
+ [sort @report],
+ [
+ sort
+ " * Module 'T2Test::UG1' is outdated, we recommed updating above 1.0.",
+ " * Module 'T2Test::UR1' is outdated and known to be broken, please update to 1.0 or higher.",
+ " * Module 'T2Test::KB1' is known to be broken in version 1.0 and below, newer versions have not been tested. You have: 0.9",
+ " * Module 'T2Test::KB2' is known to be broken in version 0.5 and below, newer versions have not been tested. You have: 0.9",
+ ],
+ "Got expected report items"
+ );
+ }
+
+ my %look;
+ unshift @INC => sub {
+ my ($this, $file) = @_;
+ $look{$file}++ if $file =~ m{T2Test};
+ return;
+ };
+ ok(!$CLASS->report, "Nothing to report");
+ is_deeply(\%look, {}, "Did not try to load anything");
+
+ ok(!$CLASS->report(1), "Nothing to report");
+ is_deeply(
+ \%look,
+ {
+ 'T2Test/UG1.pm' => 1,
+ 'T2Test/UG2.pm' => 1,
+ 'T2Test/UR1.pm' => 1,
+ 'T2Test/UR2.pm' => 1,
+ 'T2Test/KB1.pm' => 1,
+ 'T2Test/KB2.pm' => 1,
+ },
+ "Tried to load modules"
+ );
+}
+
+done_testing;
diff --git a/cpan/Test-Simple/t/Test2/modules/API/Context.t b/cpan/Test-Simple/t/Test2/modules/API/Context.t
new file mode 100644
index 0000000000..b4ca009f5e
--- /dev/null
+++ b/cpan/Test-Simple/t/Test2/modules/API/Context.t
@@ -0,0 +1,444 @@
+use strict;
+use warnings;
+
+BEGIN { $Test2::API::DO_DEPTH_CHECK = 1 }
+BEGIN { require "t/tools.pl" };
+
+use Test2::API qw{
+ context intercept
+ test2_stack
+ test2_add_callback_context_acquire
+ test2_add_callback_context_init
+ test2_add_callback_context_release
+};
+
+my $error = exception { context(); 1 };
+my $exception = "context() called, but return value is ignored at " . __FILE__ . ' line ' . (__LINE__ - 1);
+like($error, qr/^\Q$exception\E/, "Got the exception" );
+
+my $ref;
+my $frame;
+sub wrap(&) {
+ my $ctx = context();
+ my ($pkg, $file, $line, $sub) = caller(0);
+ $frame = [$pkg, $file, $line, $sub];
+
+ $_[0]->($ctx);
+
+ $ref = "$ctx";
+
+ $ctx->release;
+}
+
+wrap {
+ my $ctx = shift;
+ ok($ctx->hub, "got hub");
+ delete $ctx->trace->frame->[4];
+ is_deeply($ctx->trace->frame, $frame, "Found place to report errors");
+};
+
+wrap {
+ my $ctx = shift;
+ ok("$ctx" ne "$ref", "Got a new context");
+ my $new = context();
+ my @caller = caller(0);
+ is_deeply(
+ $new,
+ {%$ctx, _is_canon => undef, _is_spawn => [@caller[0,1,2,3]]},
+ "Additional call to context gets spawn"
+ );
+ delete $ctx->trace->frame->[4];
+ is_deeply($ctx->trace->frame, $frame, "Found place to report errors");
+ $new->release;
+};
+
+wrap {
+ my $ctx = shift;
+ my $snap = $ctx->snapshot;
+
+ is_deeply(
+ $snap,
+ {%$ctx, _is_canon => undef, _is_spawn => undef, _aborted => undef},
+ "snapshot is identical except for canon/spawn/aborted"
+ );
+ ok($ctx != $snap, "snapshot is a new instance");
+};
+
+my $end_ctx;
+{ # Simulate an END block...
+ local *END = sub { local *__ANON__ = 'END'; context() };
+ my $ctx = END(); $frame = [ __PACKAGE__, __FILE__, __LINE__, 'main::END' ];
+ $end_ctx = $ctx->snapshot;
+ $ctx->release;
+}
+delete $end_ctx->trace->frame->[4];
+is_deeply( $end_ctx->trace->frame, $frame, 'context is ok in an end block');
+
+# Test event generation
+{
+ package My::Formatter;
+
+ sub write {
+ my $self = shift;
+ my ($e) = @_;
+ push @$self => $e;
+ }
+}
+my $events = bless [], 'My::Formatter';
+my $hub = Test2::Hub->new(
+ formatter => $events,
+);
+my $trace = Test2::Util::Trace->new(
+ frame => [ 'Foo::Bar', 'foo_bar.t', 42, 'Foo::Bar::baz' ],
+);
+my $ctx = Test2::API::Context->new(
+ trace => $trace,
+ hub => $hub,
+);
+
+my $e = $ctx->build_event('Ok', pass => 1, name => 'foo');
+is($e->pass, 1, "Pass");
+is($e->name, 'foo', "got name");
+is_deeply($e->trace, $trace, "Got the trace info");
+ok(!@$events, "No events yet");
+
+$e = $ctx->send_event('Ok', pass => 1, name => 'foo');
+is($e->pass, 1, "Pass");
+is($e->name, 'foo', "got name");
+is_deeply($e->trace, $trace, "Got the trace info");
+is(@$events, 1, "1 event");
+is_deeply($events, [$e], "Hub saw the event");
+pop @$events;
+
+$e = $ctx->ok(1, 'foo');
+is($e->pass, 1, "Pass");
+is($e->name, 'foo', "got name");
+is_deeply($e->trace, $trace, "Got the trace info");
+is(@$events, 1, "1 event");
+is_deeply($events, [$e], "Hub saw the event");
+pop @$events;
+
+$e = $ctx->note('foo');
+is($e->message, 'foo', "got message");
+is_deeply($e->trace, $trace, "Got the trace info");
+is(@$events, 1, "1 event");
+is_deeply($events, [$e], "Hub saw the event");
+pop @$events;
+
+$e = $ctx->diag('foo');
+is($e->message, 'foo', "got message");
+is_deeply($e->trace, $trace, "Got the trace info");
+is(@$events, 1, "1 event");
+is_deeply($events, [$e], "Hub saw the event");
+pop @$events;
+
+$e = $ctx->plan(100);
+is($e->max, 100, "got max");
+is_deeply($e->trace, $trace, "Got the trace info");
+is(@$events, 1, "1 event");
+is_deeply($events, [$e], "Hub saw the event");
+pop @$events;
+
+$e = $ctx->skip('foo', 'because');
+is($e->name, 'foo', "got name");
+is($e->reason, 'because', "got reason");
+ok($e->pass, "skip events pass by default");
+is_deeply($e->trace, $trace, "Got the trace info");
+is(@$events, 1, "1 event");
+is_deeply($events, [$e], "Hub saw the event");
+pop @$events;
+
+$e = $ctx->skip('foo', 'because', pass => 0);
+ok(!$e->pass, "can override skip params");
+pop @$events;
+
+# Test hooks
+
+my @hooks;
+$hub = test2_stack()->top;
+my $ref1 = $hub->add_context_init(sub { die "Bad Arg" unless ref($_[0]) eq 'Test2::API::Context'; push @hooks => 'hub_init' });
+my $ref2 = $hub->add_context_release(sub { die "Bad Arg" unless ref($_[0]) eq 'Test2::API::Context'; push @hooks => 'hub_release' });
+test2_add_callback_context_init(sub { die "Bad Arg" unless ref($_[0]) eq 'Test2::API::Context'; push @hooks => 'global_init' });
+test2_add_callback_context_release(sub { die "Bad Arg" unless ref($_[0]) eq 'Test2::API::Context'; push @hooks => 'global_release' });
+
+my $ref3 = $hub->add_context_acquire(sub { die "Bad Arg" unless ref($_[0]) eq 'HASH'; push @hooks => 'hub_acquire' });
+test2_add_callback_context_acquire(sub { die "Bad Arg" unless ref($_[0]) eq 'HASH'; push @hooks => 'global_acquire' });
+
+sub {
+ push @hooks => 'start';
+ my $ctx = context(on_init => sub { push @hooks => 'ctx_init' }, on_release => sub { push @hooks => 'ctx_release' });
+ push @hooks => 'deep';
+ my $ctx2 = sub {
+ context(on_init => sub { push @hooks => 'ctx_init_deep' }, on_release => sub { push @hooks => 'ctx_release_deep' });
+ }->();
+ push @hooks => 'release_deep';
+ $ctx2->release;
+ push @hooks => 'release_parent';
+ $ctx->release;
+ push @hooks => 'released_all';
+
+ push @hooks => 'new';
+ $ctx = context(on_init => sub { push @hooks => 'ctx_init2' }, on_release => sub { push @hooks => 'ctx_release2' });
+ push @hooks => 'release_new';
+ $ctx->release;
+ push @hooks => 'done';
+}->();
+
+$hub->remove_context_init($ref1);
+$hub->remove_context_release($ref2);
+$hub->remove_context_acquire($ref3);
+@{Test2::API::_context_init_callbacks_ref()} = ();
+@{Test2::API::_context_release_callbacks_ref()} = ();
+@{Test2::API::_context_acquire_callbacks_ref()} = ();
+
+is_deeply(
+ \@hooks,
+ [qw{
+ start
+ global_acquire
+ hub_acquire
+ global_init
+ hub_init
+ ctx_init
+ deep
+ global_acquire
+ hub_acquire
+ release_deep
+ release_parent
+ ctx_release_deep
+ ctx_release
+ hub_release
+ global_release
+ released_all
+ new
+ global_acquire
+ hub_acquire
+ global_init
+ hub_init
+ ctx_init2
+ release_new
+ ctx_release2
+ hub_release
+ global_release
+ done
+ }],
+ "Got all hook in correct order"
+);
+
+{
+ my $ctx = context(level => -1);
+
+ my $one = Test2::API::Context->new(
+ trace => Test2::Util::Trace->new(frame => [__PACKAGE__, __FILE__, __LINE__, 'blah']),
+ hub => test2_stack()->top,
+ );
+ is($one->_depth, 0, "default depth");
+
+ my $ran = 0;
+ my $doit = sub {
+ is_deeply(\@_, [qw/foo bar/], "got args");
+ $ran++;
+ die "Make sure old context is restored";
+ };
+
+ eval { $one->do_in_context($doit, 'foo', 'bar') };
+
+ my $spawn = context(level => -1, wrapped => -2);
+ is($spawn->trace, $ctx->trace, "Old context restored");
+ $spawn->release;
+ $ctx->release;
+
+ ok(!exception { $one->do_in_context(sub {1}) }, "do_in_context works without an original")
+}
+
+{
+ like(exception { Test2::API::Context->new() }, qr/The 'trace' attribute is required/, "need to have trace");
+
+ my $trace = Test2::Util::Trace->new(frame => [__PACKAGE__, __FILE__, __LINE__, 'foo']);
+ like(exception { Test2::API::Context->new(trace => $trace) }, qr/The 'hub' attribute is required/, "need to have hub");
+
+ my $hub = test2_stack()->top;
+ my $ctx = Test2::API::Context->new(trace => $trace, hub => $hub);
+ is($ctx->{_depth}, 0, "depth set to 0 when not defined.");
+
+ $ctx = Test2::API::Context->new(trace => $trace, hub => $hub, _depth => 1);
+ is($ctx->{_depth}, 1, "Do not reset depth");
+
+ like(
+ exception { $ctx->release },
+ qr/release\(\) should not be called on context that is neither canon nor a child/,
+ "Non canonical context, do not release"
+ );
+}
+
+sub {
+ like(
+ exception { my $ctx = context(level => 20) },
+ qr/Could not find context at depth 21/,
+ "Level sanity"
+ );
+
+ ok(
+ !exception {
+ my $ctx = context(level => 20, fudge => 1);
+ $ctx->release;
+ },
+ "Was able to get context when fudging level"
+ );
+}->();
+
+sub {
+ my ($ctx1, $ctx2);
+ sub { $ctx1 = context() }->();
+
+ my @warnings;
+ {
+ local $SIG{__WARN__} = sub { push @warnings => @_ };
+ $ctx2 = context();
+ $ctx1 = undef;
+ }
+
+ $ctx2->release;
+
+ is(@warnings, 1, "1 warning");
+ like(
+ $warnings[0],
+ qr/^context\(\) was called to retrieve an existing context, however the existing/,
+ "Got expected warning"
+ );
+}->();
+
+sub {
+ my $ctx = context();
+ my $e = exception { $ctx->throw('xxx') };
+ like($e, qr/xxx/, "got exception");
+
+ $ctx = context();
+ my $warnings = warnings { $ctx->alert('xxx') };
+ like($warnings->[0], qr/xxx/, "got warning");
+ $ctx->release;
+}->();
+
+sub {
+ my $ctx = context;
+
+ is($ctx->_parse_event('Ok'), 'Test2::Event::Ok', "Got the Ok event class");
+ is($ctx->_parse_event('+Test2::Event::Ok'), 'Test2::Event::Ok', "Got the +Ok event class");
+
+ like(
+ exception { $ctx->_parse_event('+DFASGFSDFGSDGSD') },
+ qr/Could not load event module 'DFASGFSDFGSDGSD': Can't locate DFASGFSDFGSDGSD\.pm/,
+ "Bad event type"
+ );
+}->();
+
+{
+ my ($e1, $e2);
+ my $events = intercept {
+ my $ctx = context();
+ $e1 = $ctx->ok(0, 'foo', ['xxx']);
+ $e2 = $ctx->ok(0, 'foo');
+ $ctx->release;
+ };
+
+ ok($e1->isa('Test2::Event::Ok'), "returned ok event");
+ ok($e2->isa('Test2::Event::Ok'), "returned ok event");
+
+ is($events->[0], $e1, "got ok event 1");
+ is($events->[3], $e2, "got ok event 2");
+
+ is($events->[2]->message, 'xxx', "event 1 diag 2");
+}
+
+sub {
+ local $! = 100;
+ local $@ = 'foobarbaz';
+ local $? = 123;
+
+ my $ctx = context();
+
+ is($ctx->errno, 100, "saved errno");
+ is($ctx->eval_error, 'foobarbaz', "saved eval error");
+ is($ctx->child_error, 123, "saved child exit");
+
+ $! = 22;
+ $@ = 'xyz';
+ $? = 33;
+
+ is(0 + $!, 22, "altered \$! in tool");
+ is($@, 'xyz', "altered \$@ in tool");
+ is($?, 33, "altered \$? in tool");
+
+ sub {
+ my $ctx2 = context();
+
+ $! = 42;
+ $@ = 'app';
+ $? = 43;
+
+ is(0 + $!, 42, "altered \$! in tool (nested)");
+ is($@, 'app', "altered \$@ in tool (nested)");
+ is($?, 43, "altered \$? in tool (nested)");
+
+ $ctx2->release;
+
+ is(0 + $!, 22, "restored the nested \$! in tool");
+ is($@, 'xyz', "restored the nested \$@ in tool");
+ is($?, 33, "restored the nested \$? in tool");
+ }->();
+
+ sub {
+ my $ctx2 = context();
+
+ $! = 42;
+ $@ = 'app';
+ $? = 43;
+
+ is(0 + $!, 42, "altered \$! in tool (nested)");
+ is($@, 'app', "altered \$@ in tool (nested)");
+ is($?, 43, "altered \$? in tool (nested)");
+
+ # Will not warn since $@ is changed
+ $ctx2 = undef;
+
+ is(0 + $!, 42, 'Destroy does not reset $!');
+ is($@, 'app', 'Destroy does not reset $@');
+ is($?, 43, 'Destroy does not reset $?');
+ }->();
+
+ $ctx->release;
+
+ is($ctx->errno, 100, "restored errno");
+ is($ctx->eval_error, 'foobarbaz', "restored eval error");
+ is($ctx->child_error, 123, "restored child exit");
+}->();
+
+
+sub {
+ local $! = 100;
+ local $@ = 'foobarbaz';
+ local $? = 123;
+
+ my $ctx = context();
+
+ is($ctx->errno, 100, "saved errno");
+ is($ctx->eval_error, 'foobarbaz', "saved eval error");
+ is($ctx->child_error, 123, "saved child exit");
+
+ $! = 22;
+ $@ = 'xyz';
+ $? = 33;
+
+ is(0 + $!, 22, "altered \$! in tool");
+ is($@, 'xyz', "altered \$@ in tool");
+ is($?, 33, "altered \$? in tool");
+
+ # Will not warn since $@ is changed
+ $ctx = undef;
+
+ is(0 + $!, 22, "Destroy does not restore \$!");
+ is($@, 'xyz', "Destroy does not restore \$@");
+ is($?, 33, "Destroy does not restore \$?");
+}->();
+
+done_testing;
diff --git a/cpan/Test-Simple/t/Test2/modules/API/Instance.t b/cpan/Test-Simple/t/Test2/modules/API/Instance.t
new file mode 100644
index 0000000000..45e739fe6e
--- /dev/null
+++ b/cpan/Test-Simple/t/Test2/modules/API/Instance.t
@@ -0,0 +1,466 @@
+use strict;
+use warnings;
+
+use Test2::IPC;
+BEGIN { require "t/tools.pl" };
+use Test2::Util qw/CAN_THREAD CAN_REALLY_FORK USE_THREADS get_tid/;
+
+my $CLASS = 'Test2::API::Instance';
+
+my $one = $CLASS->new;
+is_deeply(
+ $one,
+ {
+ pid => $$,
+ tid => get_tid(),
+ contexts => {},
+
+ finalized => undef,
+ ipc => undef,
+ formatter => undef,
+
+ ipc_polling => undef,
+ ipc_drivers => [],
+
+ formatters => [],
+
+ no_wait => 0,
+ loaded => 0,
+
+ exit_callbacks => [],
+ post_load_callbacks => [],
+ context_acquire_callbacks => [],
+ context_init_callbacks => [],
+ context_release_callbacks => [],
+
+ stack => [],
+ },
+ "Got initial settings"
+);
+
+%$one = ();
+is_deeply($one, {}, "wiped object");
+
+$one->reset;
+is_deeply(
+ $one,
+ {
+ pid => $$,
+ tid => get_tid(),
+ contexts => {},
+
+ ipc_polling => undef,
+ ipc_drivers => [],
+
+ formatters => [],
+
+ finalized => undef,
+ ipc => undef,
+ formatter => undef,
+
+ no_wait => 0,
+ loaded => 0,
+
+ exit_callbacks => [],
+ post_load_callbacks => [],
+ context_acquire_callbacks => [],
+ context_init_callbacks => [],
+ context_release_callbacks => [],
+
+ stack => [],
+ },
+ "Reset Object"
+);
+
+ok(!$one->formatter_set, "no formatter set");
+$one->set_formatter('Foo');
+ok($one->formatter_set, "formatter set");
+$one->reset;
+
+my $ran = 0;
+my $callback = sub { $ran++ };
+$one->add_post_load_callback($callback);
+ok(!$ran, "did not run yet");
+is_deeply($one->post_load_callbacks, [$callback], "stored callback for later");
+
+ok(!$one->loaded, "not loaded");
+$one->load;
+ok($one->loaded, "loaded");
+is($ran, 1, "ran the callback");
+
+$one->load;
+is($ran, 1, "Did not run the callback again");
+
+$one->add_post_load_callback($callback);
+is($ran, 2, "ran the new callback");
+is_deeply($one->post_load_callbacks, [$callback, $callback], "stored callback for the record");
+
+like(
+ exception { $one->add_post_load_callback({}) },
+ qr/Post-load callbacks must be coderefs/,
+ "Post-load callbacks must be coderefs"
+);
+
+$one->reset;
+ok($one->ipc, 'got ipc');
+ok($one->finalized, "calling ipc finalized the object");
+
+$one->reset;
+ok($one->stack, 'got stack');
+ok(!$one->finalized, "calling stack did not finaliz the object");
+
+$one->reset;
+ok($one->formatter, 'Got formatter');
+ok($one->finalized, "calling format finalized the object");
+
+$one->reset;
+$one->set_formatter('Foo');
+is($one->formatter, 'Foo', "got specified formatter");
+ok($one->finalized, "calling format finalized the object");
+
+{
+ local $ENV{T2_FORMATTER} = 'TAP';
+ $one->reset;
+ is($one->formatter, 'Test2::Formatter::TAP', "got specified formatter");
+ ok($one->finalized, "calling format finalized the object");
+
+ local $ENV{T2_FORMATTER} = '+Test2::Formatter::TAP';
+ $one->reset;
+ is($one->formatter, 'Test2::Formatter::TAP', "got specified formatter");
+ ok($one->finalized, "calling format finalized the object");
+
+ local $ENV{T2_FORMATTER} = '+Fake';
+ $one->reset;
+ like(
+ exception { $one->formatter },
+ qr/COULD NOT LOAD FORMATTER 'Fake' \(set by the 'T2_FORMATTER' environment variable\)/,
+ "Bad formatter"
+ );
+}
+
+$ran = 0;
+$one->reset;
+$one->add_exit_callback($callback);
+is(@{$one->exit_callbacks}, 1, "added an exit callback");
+$one->add_exit_callback($callback);
+is(@{$one->exit_callbacks}, 2, "added another exit callback");
+
+like(
+ exception { $one->add_exit_callback({}) },
+ qr/End callbacks must be coderefs/,
+ "Exit callbacks must be coderefs"
+);
+
+if (CAN_REALLY_FORK) {
+ $one->reset;
+ my $pid = fork;
+ die "Failed to fork!" unless defined $pid;
+ unless($pid) { exit 0 }
+
+ is($one->_ipc_wait, 0, "No errors");
+
+ $pid = fork;
+ die "Failed to fork!" unless defined $pid;
+ unless($pid) { exit 255 }
+ my @warnings;
+ {
+ local $SIG{__WARN__} = sub { push @warnings => @_ };
+ is($one->_ipc_wait, 255, "Process exited badly");
+ }
+ like($warnings[0], qr/Process .* did not exit cleanly \(status: 255\)/, "Warn about exit");
+}
+
+if (CAN_THREAD && $] ge '5.010') {
+ require threads;
+ $one->reset;
+
+ threads->new(sub { 1 });
+ is($one->_ipc_wait, 0, "No errors");
+
+ if (threads->can('error')) {
+ threads->new(sub {
+ close(STDERR);
+ close(STDOUT);
+ die "xxx"
+ });
+ my @warnings;
+ {
+ local $SIG{__WARN__} = sub { push @warnings => @_ };
+ is($one->_ipc_wait, 255, "Thread exited badly");
+ }
+ like($warnings[0], qr/Thread .* did not end cleanly: xxx/, "Warn about exit");
+ }
+}
+
+{
+ $one->reset();
+ local $? = 0;
+ $one->set_exit;
+ is($?, 0, "no errors on exit");
+}
+
+{
+ $one->reset();
+ $one->set_tid(1);
+ local $? = 0;
+ $one->set_exit;
+ is($?, 0, "no errors on exit");
+}
+
+{
+ $one->reset();
+ $one->stack->top;
+ $one->no_wait(1);
+ local $? = 0;
+ $one->set_exit;
+ is($?, 0, "no errors on exit");
+}
+
+{
+ $one->reset();
+ $one->stack->top->set_no_ending(1);
+ local $? = 0;
+ $one->set_exit;
+ is($?, 0, "no errors on exit");
+}
+
+{
+ $one->reset();
+ $one->stack->top->set_failed(2);
+ local $? = 0;
+ $one->set_exit;
+ is($?, 2, "number of failures");
+}
+
+{
+ $one->reset();
+ local $? = 500;
+ $one->set_exit;
+ is($?, 255, "set exit code to a sane number");
+}
+
+{
+ local %INC = %INC;
+ delete $INC{'Test2/IPC.pm'};
+ $one->reset();
+ my @events;
+ $one->stack->top->filter(sub { push @events => $_[1]; undef});
+ $one->stack->new_hub;
+ local $? = 0;
+ $one->set_exit;
+ is($?, 255, "errors on exit");
+ like($events[0]->message, qr/Test ended with extra hubs on the stack!/, "got diag");
+}
+
+{
+ $one->reset;
+ my $stderr = "";
+ {
+ local $INC{'Test/Builder.pm'} = __FILE__;
+ local $Test2::API::VERSION = '0.002';
+ local $Test::Builder::VERSION = '0.001';
+ local *STDERR;
+ open(STDERR, '>', \$stderr) or print "Failed to open new STDERR";
+
+ $one->set_exit;
+ }
+
+ is($stderr, <<' EOT', "Got warning about version mismatch");
+
+********************************************************************************
+* *
+* Test::Builder -- Test2::API version mismatch detected *
+* *
+********************************************************************************
+ Test2::API Version: 0.002
+Test::Builder Version: 0.001
+
+This is not a supported configuration, you will have problems.
+
+ EOT
+}
+
+{
+ require Test2::API::Breakage;
+ no warnings qw/redefine once/;
+ my $ran = 0;
+ local *Test2::API::Breakage::report = sub { $ran++; return "foo" };
+ use warnings qw/redefine once/;
+ $one->reset();
+
+ my $stderr = "";
+ {
+ local *STDERR;
+ open(STDERR, '>', \$stderr) or print "Failed to open new STDERR";
+ local $? = 255;
+ $one->set_exit;
+ }
+
+ is($stderr, <<" EOT", "Reported bad modules");
+
+You have loaded versions of test modules known to have problems with Test2.
+This could explain some test failures.
+foo
+
+ EOT
+}
+
+
+{
+ $one->reset();
+ my @events;
+ $one->stack->top->filter(sub { push @events => $_[1]; undef});
+ $one->stack->new_hub;
+ ok($one->stack->top->ipc, "Have IPC");
+ $one->stack->new_hub;
+ ok($one->stack->top->ipc, "Have IPC");
+ $one->stack->top->set_ipc(undef);
+ ok(!$one->stack->top->ipc, "no IPC");
+ $one->stack->new_hub;
+ local $? = 0;
+ $one->set_exit;
+ is($?, 255, "errors on exit");
+ like($events[0]->message, qr/Test ended with extra hubs on the stack!/, "got diag");
+}
+
+if (CAN_REALLY_FORK) {
+ local $SIG{__WARN__} = sub { };
+ $one->reset();
+ my $pid = fork;
+ die "Failed to fork!" unless defined $pid;
+ unless ($pid) { exit 255 }
+ $one->_finalize;
+ $one->stack->top;
+
+ local $? = 0;
+ $one->set_exit;
+ is($?, 255, "errors on exit");
+
+ $one->reset();
+ $pid = fork;
+ die "Failed to fork!" unless defined $pid;
+ unless ($pid) { exit 255 }
+ $one->_finalize;
+ $one->stack->top;
+
+ local $? = 122;
+ $one->set_exit;
+ is($?, 122, "kept original exit");
+}
+
+{
+ my $ctx = bless {
+ trace => Test2::Util::Trace->new(frame => ['Foo::Bar', 'Foo/Bar.pm', 42, 'xxx']),
+ hub => Test2::Hub->new(),
+ }, 'Test2::API::Context';
+ $one->contexts->{1234} = $ctx;
+
+ local $? = 500;
+ my $warnings = warnings { $one->set_exit };
+ is($?, 255, "set exit code to a sane number");
+
+ is_deeply(
+ $warnings,
+ [
+ "context object was never released! This means a testing tool is behaving very badly at Foo/Bar.pm line 42.\n"
+ ],
+ "Warned about unfreed context"
+ );
+}
+
+{
+ local %INC = %INC;
+ delete $INC{'Test2/IPC.pm'};
+ delete $INC{'threads.pm'};
+ ok(!USE_THREADS, "Sanity Check");
+
+ $one->reset;
+ ok(!$one->ipc, 'IPC not loaded, no IPC object');
+ ok($one->finalized, "calling ipc finalized the object");
+ is($one->ipc_polling, undef, "no polling defined");
+ ok(!@{$one->ipc_drivers}, "no driver");
+
+ if (CAN_THREAD) {
+ local $INC{'threads.pm'} = 1;
+ no warnings 'once';
+ local *threads::tid = sub { 0 } unless threads->can('tid');
+ $one->reset;
+ ok($one->ipc, 'IPC loaded if threads are');
+ ok($one->finalized, "calling ipc finalized the object");
+ ok($one->ipc_polling, "polling on by default");
+ is($one->ipc_drivers->[0], 'Test2::IPC::Driver::Files', "default driver");
+ }
+
+ {
+ local $INC{'Test2/IPC.pm'} = 1;
+ $one->reset;
+ ok($one->ipc, 'IPC loaded if Test2::IPC is');
+ ok($one->finalized, "calling ipc finalized the object");
+ ok($one->ipc_polling, "polling on by default");
+ is($one->ipc_drivers->[0], 'Test2::IPC::Driver::Files', "default driver");
+ }
+
+ require Test2::IPC::Driver::Files;
+ $one->reset;
+ $one->add_ipc_driver('Test2::IPC::Driver::Files');
+ ok($one->ipc, 'IPC loaded if drivers have been added');
+ ok($one->finalized, "calling ipc finalized the object");
+ ok($one->ipc_polling, "polling on by default");
+
+ my $file = __FILE__;
+ my $line = __LINE__ + 1;
+ my $warnings = warnings { $one->add_ipc_driver('Test2::IPC::Driver::Files') };
+ like(
+ $warnings->[0],
+ qr{^IPC driver Test2::IPC::Driver::Files loaded too late to be used as the global ipc driver at \Q$file\E line $line},
+ "Got warning at correct frame"
+ );
+
+ $one->reset;
+ $one->add_ipc_driver('Fake::Fake::XXX');
+ is(
+ exception { $one->ipc },
+ "IPC has been requested, but no viable drivers were found. Aborting...\n",
+ "Failed without viable IPC driver"
+ );
+}
+
+{
+ $one->reset;
+ ok(!@{$one->context_init_callbacks}, "no callbacks");
+ is($one->ipc_polling, undef, "no polling, undef");
+
+ $one->disable_ipc_polling;
+ ok(!@{$one->context_init_callbacks}, "no callbacks");
+ is($one->ipc_polling, undef, "no polling, still undef");
+
+ my $cull = 0;
+ no warnings 'once';
+ local *Fake::Hub::cull = sub { $cull++ };
+ use warnings;
+
+ $one->enable_ipc_polling;
+ is(@{$one->context_init_callbacks}, 1, "added the callback");
+ is($one->ipc_polling, 1, "polling on");
+ $one->set_ipc_shm_last('abc1');
+ $one->context_init_callbacks->[0]->({'hub' => 'Fake::Hub'});
+ is($cull, 1, "called cull once");
+ $cull = 0;
+
+ $one->disable_ipc_polling;
+ is(@{$one->context_init_callbacks}, 1, "kept the callback");
+ is($one->ipc_polling, 0, "no polling, set to 0");
+ $one->set_ipc_shm_last('abc3');
+ $one->context_init_callbacks->[0]->({'hub' => 'Fake::Hub'});
+ is($cull, 0, "did not call cull");
+ $cull = 0;
+
+ $one->enable_ipc_polling;
+ is(@{$one->context_init_callbacks}, 1, "did not add the callback");
+ is($one->ipc_polling, 1, "polling on");
+ $one->set_ipc_shm_last('abc3');
+ $one->context_init_callbacks->[0]->({'hub' => 'Fake::Hub'});
+ is($cull, 1, "called cull once");
+}
+
+done_testing;
diff --git a/cpan/Test-Simple/t/Test2/modules/API/Stack.t b/cpan/Test-Simple/t/Test2/modules/API/Stack.t
new file mode 100644
index 0000000000..731022e8b0
--- /dev/null
+++ b/cpan/Test-Simple/t/Test2/modules/API/Stack.t
@@ -0,0 +1,79 @@
+use strict;
+use warnings;
+use Test2::IPC;
+BEGIN { require "t/tools.pl" };
+use Test2::API::Stack;
+use Test2::API qw/test2_ipc/;
+
+ok(my $stack = Test2::API::Stack->new, "Create a stack");
+
+ok(!@$stack, "Empty stack");
+ok(!$stack->peek, "Nothing to peek at");
+
+ok(!exception { $stack->cull }, "cull lives when stack is empty");
+ok(!exception { $stack->all }, "all lives when stack is empty");
+ok(!exception { $stack->clear }, "clear lives when stack is empty");
+
+like(
+ exception { $stack->pop(Test2::Hub->new) },
+ qr/No hubs on the stack/,
+ "No hub to pop"
+);
+
+my $hub = Test2::Hub->new;
+ok($stack->push($hub), "pushed a hub");
+
+like(
+ exception { $stack->pop($hub) },
+ qr/You cannot pop the root hub/,
+ "Root hub cannot be popped"
+);
+
+$stack->push($hub);
+like(
+ exception { $stack->pop(Test2::Hub->new) },
+ qr/Hub stack mismatch, attempted to pop incorrect hub/,
+ "Must specify correct hub to pop"
+);
+
+is_deeply(
+ [ $stack->all ],
+ [ $hub, $hub ],
+ "Got all hubs"
+);
+
+ok(!exception { $stack->pop($hub) }, "Popped the correct hub");
+
+is_deeply(
+ [ $stack->all ],
+ [ $hub ],
+ "Got all hubs"
+);
+
+is($stack->peek, $hub, "got the hub");
+is($stack->top, $hub, "got the hub");
+
+$stack->clear;
+
+is_deeply(
+ [ $stack->all ],
+ [ ],
+ "no hubs"
+);
+
+ok(my $top = $stack->top, "Generated a top hub");
+is($top->ipc, test2_ipc, "Used sync's ipc");
+ok($top->format, 'Got formatter');
+
+is($stack->top, $stack->top, "do not generate a new top if there is already a top");
+
+ok(my $new = $stack->new_hub(), "Add a new hub");
+is($stack->top, $new, "new one is on top");
+is($new->ipc, $top->ipc, "inherited ipc");
+is($new->format, $top->format, "inherited formatter");
+
+my $new2 = $stack->new_hub(formatter => undef, ipc => undef);
+ok(!$new2->ipc, "built with no ipc");
+ok(!$new2->format, "built with no formatter");
+
+done_testing;
diff --git a/cpan/Test-Simple/t/Test2/modules/Event.t b/cpan/Test-Simple/t/Test2/modules/Event.t
new file mode 100644
index 0000000000..323972aa27
--- /dev/null
+++ b/cpan/Test-Simple/t/Test2/modules/Event.t
@@ -0,0 +1,40 @@
+use strict;
+use warnings;
+BEGIN { require "t/tools.pl" };
+
+use Test2::Event();
+
+{
+ package My::MockEvent;
+
+ use base 'Test2::Event';
+ use Test2::Util::HashBase qw/foo bar baz/;
+}
+
+ok(My::MockEvent->can($_), "Added $_ accessor") for qw/foo bar baz/;
+
+my $one = My::MockEvent->new(trace => 'fake');
+
+ok(!$one->causes_fail, "Events do not cause failures by default");
+
+ok(!$one->$_, "$_ is false by default") for qw/increments_count terminate global/;
+
+ok(!$one->get_meta('xxx'), "no meta-data associated for key 'xxx'");
+
+$one->set_meta('xxx', '123');
+
+is($one->meta('xxx'), '123', "got meta-data");
+
+is($one->meta('xxx', '321'), '123', "did not use default");
+
+is($one->meta('yyy', '1221'), '1221', "got the default");
+
+is($one->meta('yyy'), '1221', "last call set the value to the default for future use");
+
+is($one->summary, 'My::MockEvent', "Default summary is event package");
+
+is($one->diagnostics, 0, "Not diagnostics by default");
+
+ok(!$one->in_subtest, "no subtest_id by default");
+
+done_testing;
diff --git a/cpan/Test-Simple/t/Test2/modules/Event/Bail.t b/cpan/Test-Simple/t/Test2/modules/Event/Bail.t
new file mode 100644
index 0000000000..0d69f43f6f
--- /dev/null
+++ b/cpan/Test-Simple/t/Test2/modules/Event/Bail.t
@@ -0,0 +1,29 @@
+use strict;
+use warnings;
+BEGIN { require "t/tools.pl" };
+use Test2::Event::Bail;
+
+my $bail = Test2::Event::Bail->new(
+ trace => 'fake',
+ reason => 'evil',
+);
+
+ok($bail->causes_fail, "bailout always causes fail.");
+
+is($bail->terminate, 255, "Bail will cause the test to exit.");
+is($bail->global, 1, "Bail is global, everything should bail");
+
+my $hub = Test2::Hub->new;
+ok($hub->is_passing, "passing");
+ok(!$hub->failed, "no failures");
+
+$bail->callback($hub);
+is($hub->bailed_out, $bail, "set bailed out");
+
+is($bail->summary, "Bail out! evil", "Summary includes reason");
+$bail->set_reason("");
+is($bail->summary, "Bail out!", "Summary has no reason");
+
+ok($bail->diagnostics, "Bail events are counted as diagnostics");
+
+done_testing;
diff --git a/cpan/Test-Simple/t/Test2/modules/Event/Diag.t b/cpan/Test-Simple/t/Test2/modules/Event/Diag.t
new file mode 100644
index 0000000000..eb38554988
--- /dev/null
+++ b/cpan/Test-Simple/t/Test2/modules/Event/Diag.t
@@ -0,0 +1,31 @@
+use strict;
+use warnings;
+BEGIN { require "t/tools.pl" };
+use Test2::Event::Diag;
+use Test2::Util::Trace;
+
+my $diag = Test2::Event::Diag->new(
+ trace => Test2::Util::Trace->new(frame => [__PACKAGE__, __FILE__, __LINE__]),
+ message => 'foo',
+);
+
+is($diag->summary, 'foo', "summary is just message");
+
+$diag = Test2::Event::Diag->new(
+ trace => Test2::Util::Trace->new(frame => [__PACKAGE__, __FILE__, __LINE__]),
+ message => undef,
+);
+
+is($diag->message, 'undef', "set undef message to undef");
+is($diag->summary, 'undef', "summary is just message even when undef");
+
+$diag = Test2::Event::Diag->new(
+ trace => Test2::Util::Trace->new(frame => [__PACKAGE__, __FILE__, __LINE__]),
+ message => {},
+);
+
+like($diag->message, qr/^HASH\(.*\)$/, "stringified the input value");
+
+ok($diag->diagnostics, "Diag events are counted as diagnostics");
+
+done_testing;
diff --git a/cpan/Test-Simple/t/Test2/modules/Event/Exception.t b/cpan/Test-Simple/t/Test2/modules/Event/Exception.t
new file mode 100644
index 0000000000..d9082ad795
--- /dev/null
+++ b/cpan/Test-Simple/t/Test2/modules/Event/Exception.t
@@ -0,0 +1,17 @@
+use strict;
+use warnings;
+BEGIN { require "t/tools.pl" };
+use Test2::Event::Exception;
+
+my $exception = Test2::Event::Exception->new(
+ trace => 'fake',
+ error => "evil at lake_of_fire.t line 6\n",
+);
+
+ok($exception->causes_fail, "Exception events always cause failure");
+
+is($exception->summary, "Exception: evil at lake_of_fire.t line 6", "Got summary");
+
+ok($exception->diagnostics, "Exception events are counted as diagnostics");
+
+done_testing;
diff --git a/cpan/Test-Simple/t/Test2/modules/Event/Note.t b/cpan/Test-Simple/t/Test2/modules/Event/Note.t
new file mode 100644
index 0000000000..cac7da9e7f
--- /dev/null
+++ b/cpan/Test-Simple/t/Test2/modules/Event/Note.t
@@ -0,0 +1,30 @@
+use strict;
+use warnings;
+
+BEGIN { require "t/tools.pl" };
+use Test2::Event::Note;
+use Test2::Util::Trace;
+
+my $note = Test2::Event::Note->new(
+ trace => Test2::Util::Trace->new(frame => [__PACKAGE__, __FILE__, __LINE__]),
+ message => 'foo',
+);
+
+is($note->summary, 'foo', "summary is just message");
+
+$note = Test2::Event::Note->new(
+ trace => Test2::Util::Trace->new(frame => [__PACKAGE__, __FILE__, __LINE__]),
+ message => undef,
+);
+
+is($note->message, 'undef', "set undef message to undef");
+is($note->summary, 'undef', "summary is just message even when undef");
+
+$note = Test2::Event::Note->new(
+ trace => Test2::Util::Trace->new(frame => [__PACKAGE__, __FILE__, __LINE__]),
+ message => {},
+);
+
+like($note->message, qr/^HASH\(.*\)$/, "stringified the input value");
+
+done_testing;
diff --git a/cpan/Test-Simple/t/Test2/modules/Event/Ok.t b/cpan/Test-Simple/t/Test2/modules/Event/Ok.t
new file mode 100644
index 0000000000..ea709f5b85
--- /dev/null
+++ b/cpan/Test-Simple/t/Test2/modules/Event/Ok.t
@@ -0,0 +1,102 @@
+use strict;
+use warnings;
+
+BEGIN { require "t/tools.pl" };
+use Test2::Util::Trace;
+use Test2::Event::Ok;
+use Test2::Event::Diag;
+
+use Test2::API qw/context/;
+
+my $trace;
+sub before_each {
+ # Make sure there is a fresh trace object for each group
+ $trace = Test2::Util::Trace->new(
+ frame => ['main_foo', 'foo.t', 42, 'main_foo::flubnarb'],
+ );
+}
+
+tests Passing => sub {
+ my $ok = Test2::Event::Ok->new(
+ trace => $trace,
+ pass => 1,
+ name => 'the_test',
+ );
+ ok($ok->increments_count, "Bumps the count");
+ ok(!$ok->causes_fail, "Passing 'OK' event does not cause failure");
+ is($ok->pass, 1, "got pass");
+ is($ok->name, 'the_test', "got name");
+ is($ok->effective_pass, 1, "effective pass");
+ is($ok->summary, "the_test", "Summary is just the name of the test");
+
+ $ok = Test2::Event::Ok->new(
+ trace => $trace,
+ pass => 1,
+ name => '',
+ );
+ is($ok->summary, "Nameless Assertion", "Nameless test");
+
+};
+
+tests Failing => sub {
+ local $ENV{HARNESS_ACTIVE} = 1;
+ local $ENV{HARNESS_IS_VERBOSE} = 1;
+ my $ok = Test2::Event::Ok->new(
+ trace => $trace,
+ pass => 0,
+ name => 'the_test',
+ );
+ ok($ok->increments_count, "Bumps the count");
+ ok($ok->causes_fail, "A failing test causes failures");
+ is($ok->pass, 0, "got pass");
+ is($ok->name, 'the_test', "got name");
+ is($ok->effective_pass, 0, "effective pass");
+ is($ok->summary, "the_test", "Summary is just the name of the test");
+};
+
+tests "Failing TODO" => sub {
+ local $ENV{HARNESS_ACTIVE} = 1;
+ local $ENV{HARNESS_IS_VERBOSE} = 1;
+ my $ok = Test2::Event::Ok->new(
+ trace => $trace,
+ pass => 0,
+ name => 'the_test',
+ todo => 'A Todo',
+ );
+ ok($ok->increments_count, "Bumps the count");
+ is($ok->pass, 0, "got pass");
+ is($ok->name, 'the_test', "got name");
+ is($ok->effective_pass, 1, "effective pass is true from todo");
+ is($ok->summary, "the_test (TODO: A Todo)", "Summary is just the name of the test + todo");
+
+ $ok = Test2::Event::Ok->new(
+ trace => $trace,
+ pass => 0,
+ name => 'the_test2',
+ todo => '',
+ );
+ ok($ok->effective_pass, "empty string todo is still a todo");
+ is($ok->summary, "the_test2 (TODO)", "Summary is just the name of the test + todo");
+};
+
+tests init => sub {
+ like(
+ exception { Test2::Event::Ok->new(trace => $trace, pass => 1, name => "foo#foo") },
+ qr/'foo#foo' is not a valid name, names must not contain '#' or newlines/,
+ "Some characters do not belong in a name"
+ );
+
+ like(
+ exception { Test2::Event::Ok->new(trace => $trace, pass => 1, name => "foo\nfoo") },
+ qr/'foo\nfoo' is not a valid name, names must not contain '#' or newlines/,
+ "Some characters do not belong in a name"
+ );
+
+ my $ok = Test2::Event::Ok->new(
+ trace => $trace,
+ pass => 1,
+ );
+ is($ok->effective_pass, 1, "set effective pass");
+};
+
+done_testing;
diff --git a/cpan/Test-Simple/t/Test2/modules/Event/Plan.t b/cpan/Test-Simple/t/Test2/modules/Event/Plan.t
new file mode 100644
index 0000000000..33a5950773
--- /dev/null
+++ b/cpan/Test-Simple/t/Test2/modules/Event/Plan.t
@@ -0,0 +1,107 @@
+use strict;
+use warnings;
+
+BEGIN { require "t/tools.pl" };
+use Test2::Event::Plan;
+use Test2::Util::Trace;
+
+my $plan = Test2::Event::Plan->new(
+ trace => Test2::Util::Trace->new(frame => [__PACKAGE__, __FILE__, __LINE__]),
+ max => 100,
+);
+
+is($plan->summary, "Plan is 100 assertions", "simple summary");
+is_deeply( [$plan->sets_plan], [100, '', undef], "Got plan details");
+
+ok(!$plan->global, "regular plan is not a global event");
+my $state = Test2::Hub->new;
+$plan->callback($state);
+is($state->plan, 100, "set plan in state");
+is($plan->terminate, undef, "No terminate for normal plan");
+
+$plan->set_max(0);
+$plan->set_directive('SKIP');
+$plan->set_reason('foo');
+$state = Test2::Hub->new;
+$plan->callback($state);
+is($state->plan, 'SKIP', "set plan in state");
+is($plan->terminate, 0, "Terminate 0 on skip_all");
+
+is($plan->summary, "Plan is 'SKIP', foo", "skip summary");
+is_deeply( [$plan->sets_plan], [0, 'SKIP', 'foo'], "Got skip details");
+
+$plan->set_max(0);
+$plan->set_directive('NO PLAN');
+$plan->set_reason(undef);
+is($plan->summary, "Plan is 'NO PLAN'", "NO PLAN summary");
+is_deeply( [$plan->sets_plan], [0, 'NO PLAN', undef], "Got 'NO PLAN' details");
+$state = Test2::Hub->new;
+$plan->callback($state);
+is($state->plan, 'NO PLAN', "set plan in state");
+is($plan->terminate, undef, "No terminate for no_plan");
+$plan->set_max(100);
+$plan->set_directive(undef);
+$plan->callback($state);
+is($state->plan, '100', "Update plan in state if it is 'NO PLAN'");
+
+$plan = Test2::Event::Plan->new(
+ trace => Test2::Util::Trace->new(frame => [__PACKAGE__, __FILE__, __LINE__]),
+ max => 0,
+ directive => 'skip_all',
+);
+is($plan->directive, 'SKIP', "Change skip_all to SKIP");
+
+$plan = Test2::Event::Plan->new(
+ trace => Test2::Util::Trace->new(frame => [__PACKAGE__, __FILE__, __LINE__]),
+ max => 0,
+ directive => 'no_plan',
+);
+is($plan->directive, 'NO PLAN', "Change no_plan to 'NO PLAN'");
+ok(!$plan->global, "NO PLAN is not global");
+
+like(
+ exception {
+ $plan = Test2::Event::Plan->new(
+ trace => Test2::Util::Trace->new(frame => [__PACKAGE__, __FILE__, __LINE__]),
+ max => 0,
+ directive => 'foo',
+ );
+ },
+ qr/'foo' is not a valid plan directive/,
+ "Invalid Directive"
+);
+
+like(
+ exception {
+ $plan = Test2::Event::Plan->new(
+ trace => Test2::Util::Trace->new(frame => [__PACKAGE__, __FILE__, __LINE__]),
+ max => 0,
+ reason => 'foo',
+ );
+ },
+ qr/Cannot have a reason without a directive!/,
+ "Reason without directive"
+);
+
+like(
+ exception {
+ $plan = Test2::Event::Plan->new(
+ trace => Test2::Util::Trace->new(frame => [__PACKAGE__, __FILE__, __LINE__]),
+ );
+ },
+ qr/No number of tests specified/,
+ "Nothing to do"
+);
+
+like(
+ exception {
+ $plan = Test2::Event::Plan->new(
+ trace => Test2::Util::Trace->new(frame => [__PACKAGE__, __FILE__, __LINE__]),
+ max => 'skip',
+ );
+ },
+ qr/Plan test count 'skip' does not appear to be a valid positive integer/,
+ "Max must be an integer"
+);
+
+done_testing;
diff --git a/cpan/Test-Simple/t/Test2/modules/Event/Skip.t b/cpan/Test-Simple/t/Test2/modules/Event/Skip.t
new file mode 100644
index 0000000000..a1580202db
--- /dev/null
+++ b/cpan/Test-Simple/t/Test2/modules/Event/Skip.t
@@ -0,0 +1,24 @@
+BEGIN { require "t/tools.pl" };
+use strict;
+use warnings;
+
+use Test2::Event::Skip;
+use Test2::Util::Trace;
+
+my $skip = Test2::Event::Skip->new(
+ trace => Test2::Util::Trace->new(frame => [__PACKAGE__, __FILE__, __LINE__]),
+ name => 'skip me',
+ reason => 'foo',
+);
+
+is($skip->name, 'skip me', "set name");
+is($skip->reason, 'foo', "got skip reason");
+ok(!$skip->pass, "no default for pass");
+ok($skip->effective_pass, "TODO always effectively passes");
+
+is($skip->summary, "skip me (SKIP: foo)", "summary with reason");
+
+$skip->set_reason('');
+is($skip->summary, "skip me (SKIP)", "summary without reason");
+
+done_testing;
diff --git a/cpan/Test-Simple/t/Test2/modules/Event/Subtest.t b/cpan/Test-Simple/t/Test2/modules/Event/Subtest.t
new file mode 100644
index 0000000000..d6bff4d3b6
--- /dev/null
+++ b/cpan/Test-Simple/t/Test2/modules/Event/Subtest.t
@@ -0,0 +1,30 @@
+use strict;
+use warnings;
+
+BEGIN { require "t/tools.pl" };
+use Test2::Event::Subtest;
+my $st = 'Test2::Event::Subtest';
+
+my $trace = Test2::Util::Trace->new(frame => [__PACKAGE__, __FILE__, __LINE__, 'xxx']);
+my $one = $st->new(
+ trace => $trace,
+ pass => 1,
+ buffered => 1,
+ name => 'foo',
+ subtest_id => "1-1-1",
+);
+
+ok($one->isa('Test2::Event::Ok'), "Inherit from Ok");
+is_deeply($one->subevents, [], "subevents is an arrayref");
+
+is($one->summary, "foo", "simple summary");
+$one->set_todo('');
+is($one->summary, "foo (TODO)", "simple summary + TODO");
+$one->set_todo('foo');
+is($one->summary, "foo (TODO: foo)", "simple summary + TODO + Reason");
+
+$one->set_todo(undef);
+$one->set_name('');
+is($one->summary, "Nameless Subtest", "unnamed summary");
+
+done_testing;
diff --git a/cpan/Test-Simple/t/Test2/modules/Event/Waiting.t b/cpan/Test-Simple/t/Test2/modules/Event/Waiting.t
new file mode 100644
index 0000000000..cc54895dae
--- /dev/null
+++ b/cpan/Test-Simple/t/Test2/modules/Event/Waiting.t
@@ -0,0 +1,16 @@
+use strict;
+use warnings;
+
+BEGIN { require "t/tools.pl" };
+use Test2::Event::Waiting;
+
+my $waiting = Test2::Event::Waiting->new(
+ trace => 'fake',
+);
+
+ok($waiting, "Created event");
+ok($waiting->global, "waiting is global");
+
+is($waiting->summary, "IPC is waiting for children to finish...", "Got summary");
+
+done_testing;
diff --git a/cpan/Test-Simple/t/Test2/modules/Formatter/TAP.t b/cpan/Test-Simple/t/Test2/modules/Formatter/TAP.t
new file mode 100644
index 0000000000..115e900a95
--- /dev/null
+++ b/cpan/Test-Simple/t/Test2/modules/Formatter/TAP.t
@@ -0,0 +1,464 @@
+use strict;
+use warnings;
+use Test2::Formatter::TAP;
+use Test2::API qw/context/;
+use PerlIO;
+
+BEGIN {
+ require "t/tools.pl";
+ *OUT_STD = Test2::Formatter::TAP->can('OUT_STD') or die;
+ *OUT_ERR = Test2::Formatter::TAP->can('OUT_ERR') or die;
+}
+
+use Test2::API;
+Test2::API::test2_add_callback_context_release(sub {
+ my $ctx = shift;
+ return if $ctx->hub->is_passing;
+ $ctx->throw("(Die On Fail)");
+});
+
+ok(my $one = Test2::Formatter::TAP->new, "Created a new instance");
+my $handles = $one->handles;
+is(@$handles, 2, "Got 2 handles");
+ok($handles->[0] != $handles->[1], "First and second handles are not the same");
+my $layers = { map {$_ => 1} PerlIO::get_layers($handles->[0]) };
+
+if (${^UNICODE} & 2) { # 2 means STDIN
+ ok($layers->{utf8}, "'S' is set in PERL_UNICODE, or in -C, honor it, utf8 should be on")
+}
+else {
+ ok(!$layers->{utf8}, "Not utf8 by default")
+}
+
+$one->encoding('utf8');
+is($one->encoding, 'utf8', "Got encoding");
+$handles = $one->handles;
+is(@$handles, 2, "Got 2 handles");
+$layers = { map {$_ => 1} PerlIO::get_layers($handles->[0]) };
+ok($layers->{utf8}, "Now utf8");
+
+my $two = Test2::Formatter::TAP->new(encoding => 'utf8');
+$handles = $two->handles;
+is(@$handles, 2, "Got 2 handles");
+$layers = { map {$_ => 1} PerlIO::get_layers($handles->[0]) };
+ok($layers->{utf8}, "Now utf8");
+
+
+{
+ package My::Event;
+
+ use base 'Test2::Event';
+ use Test2::Util::HashBase qw{pass name diag note};
+
+ Test2::Formatter::TAP->register_event(
+ __PACKAGE__,
+ sub {
+ my $self = shift;
+ my ($e, $num) = @_;
+ return (
+ [main::OUT_STD, "ok $num - " . $e->name . "\n"],
+ [main::OUT_ERR, "# " . $e->name . " " . $e->diag . "\n"],
+ [main::OUT_STD, "# " . $e->name . " " . $e->note . "\n"],
+ );
+ }
+ );
+}
+
+my ($std, $err);
+open( my $stdh, '>', \$std ) || die "Ooops";
+open( my $errh, '>', \$err ) || die "Ooops";
+
+my $it = Test2::Formatter::TAP->new(
+ handles => [$stdh, $errh, $stdh],
+);
+
+$it->write(
+ My::Event->new(
+ pass => 1,
+ name => 'foo',
+ diag => 'diag',
+ note => 'note',
+ trace => 'fake',
+ ),
+ 55,
+);
+
+$it->write(
+ My::Event->new(
+ pass => 1,
+ name => 'bar',
+ diag => 'diag',
+ note => 'note',
+ trace => 'fake',
+ nested => 1,
+ ),
+ 1,
+);
+
+is($std, <<EOT, "Got expected TAP output to std");
+ok 55 - foo
+# foo note
+ ok 1 - bar
+ # bar note
+EOT
+
+is($err, <<EOT, "Got expected TAP output to err");
+# foo diag
+ # bar diag
+EOT
+
+$it = undef;
+close($stdh);
+close($errh);
+
+my ($trace, $ok, $diag, $plan, $bail);
+
+my $fmt = Test2::Formatter::TAP->new;
+sub before_each {
+ # Make sure there is a fresh trace object for each group
+ $trace = Test2::Util::Trace->new(
+ frame => ['main_foo', 'foo.t', 42, 'main_foo::flubnarb'],
+ );
+}
+
+tests bail => sub {
+ my $bail = Test2::Event::Bail->new(
+ trace => $trace,
+ reason => 'evil',
+ );
+
+ is_deeply(
+ [$fmt->event_tap($bail, 1)],
+ [[OUT_STD, "Bail out! evil\n" ]],
+ "Got tap"
+ );
+};
+
+tests diag => sub {
+ my $diag = Test2::Event::Diag->new(
+ trace => $trace,
+ message => 'foo',
+ );
+
+ is_deeply(
+ [$fmt->event_tap($diag, 1)],
+ [[OUT_ERR, "# foo\n"]],
+ "Got tap"
+ );
+
+ $diag->set_message("foo\n");
+ is_deeply(
+ [$fmt->event_tap($diag, 1)],
+ [[OUT_ERR, "# foo\n"]],
+ "Only 1 newline"
+ );
+
+ $diag->set_message("foo\nbar\nbaz");
+ is_deeply(
+ [$fmt->event_tap($diag, 1)],
+ [[OUT_ERR, "# foo\n# bar\n# baz\n"]],
+ "All lines have proper prefix"
+ );
+};
+
+tests exception => sub {
+ my $exception = Test2::Event::Exception->new(
+ trace => $trace,
+ error => "evil at lake_of_fire.t line 6\n",
+ );
+
+ is_deeply(
+ [$fmt->event_tap($exception, 1)],
+ [[OUT_ERR, "evil at lake_of_fire.t line 6\n" ]],
+ "Got tap"
+ );
+};
+
+tests note => sub {
+ my $note = Test2::Event::Note->new(
+ trace => $trace,
+ message => 'foo',
+ );
+
+ is_deeply(
+ [$fmt->event_tap($note, 1)],
+ [[OUT_STD, "# foo\n"]],
+ "Got tap"
+ );
+
+ $note->set_message("foo\n");
+ is_deeply(
+ [$fmt->event_tap($note, 1)],
+ [[OUT_STD, "# foo\n"]],
+ "Only 1 newline"
+ );
+
+ $note->set_message("foo\nbar\nbaz");
+ is_deeply(
+ [$fmt->event_tap($note, 1)],
+ [[OUT_STD, "# foo\n# bar\n# baz\n"]],
+ "All lines have proper prefix"
+ );
+};
+
+for my $pass (1, 0) {
+ local $ENV{HARNESS_IS_VERBOSE} = 1;
+ tests name_and_number => sub {
+ my $ok = Test2::Event::Ok->new(trace => $trace, pass => $pass, name => 'foo');
+ my @tap = $fmt->event_tap($ok, 7);
+ is_deeply(
+ \@tap,
+ [
+ [OUT_STD, ($pass ? 'ok' : 'not ok') . " 7 - foo\n"],
+ ],
+ "Got expected output"
+ );
+ };
+
+ tests no_number => sub {
+ my $ok = Test2::Event::Ok->new(trace => $trace, pass => $pass, name => 'foo');
+ my @tap = $fmt->event_tap($ok, );
+ is_deeply(
+ \@tap,
+ [
+ [OUT_STD, ($pass ? 'ok' : 'not ok') . " - foo\n"],
+ ],
+ "Got expected output"
+ );
+ };
+
+ tests no_name => sub {
+ my $ok = Test2::Event::Ok->new(trace => $trace, pass => $pass);
+ my @tap = $fmt->event_tap($ok, 7);
+ is_deeply(
+ \@tap,
+ [
+ [OUT_STD, ($pass ? 'ok' : 'not ok') . " 7\n"],
+ ],
+ "Got expected output"
+ );
+ };
+
+ tests todo => sub {
+ my $ok = Test2::Event::Ok->new(trace => $trace, pass => $pass);
+ $ok->set_todo('b');
+ my @tap = $fmt->event_tap($ok, 7);
+ is_deeply(
+ \@tap,
+ [
+ [OUT_STD, ($pass ? 'ok' : 'not ok') . " 7 # TODO b\n"],
+ ],
+ "Got expected output"
+ );
+
+ $ok->set_todo("");
+
+ @tap = $fmt->event_tap($ok, 7);
+ is_deeply(
+ \@tap,
+ [
+ [OUT_STD, ($pass ? 'ok' : 'not ok') . " 7 # TODO\n"],
+ ],
+ "Got expected output"
+ );
+ };
+};
+
+tests plan => sub {
+ my $plan = Test2::Event::Plan->new(
+ trace => $trace,
+ max => 100,
+ );
+
+ is_deeply(
+ [$fmt->event_tap($plan, 1)],
+ [[OUT_STD, "1..100\n"]],
+ "Got tap"
+ );
+
+ $plan->set_max(0);
+ $plan->set_directive('SKIP');
+ $plan->set_reason('foo');
+ is_deeply(
+ [$fmt->event_tap($plan, 1)],
+ [[OUT_STD, "1..0 # SKIP foo\n"]],
+ "Got tap for skip_all"
+ );
+
+ $plan = Test2::Event::Plan->new(
+ trace => $trace,
+ max => 0,
+ directive => 'skip_all',
+ );
+ is_deeply(
+ [$fmt->event_tap($plan)],
+ [[OUT_STD, "1..0 # SKIP\n"]],
+ "SKIP without reason"
+ );
+
+ $plan = Test2::Event::Plan->new(
+ trace => $trace,
+ max => 0,
+ directive => 'no_plan',
+ );
+ is_deeply(
+ [$fmt->event_tap($plan)],
+ [],
+ "NO PLAN"
+ );
+
+ $plan = Test2::Event::Plan->new(
+ trace => $trace,
+ max => 0,
+ directive => 'skip_all',
+ reason => "Foo\nBar\nBaz",
+ );
+ is_deeply(
+ [$fmt->event_tap($plan)],
+ [
+ [OUT_STD, "1..0 # SKIP Foo\n# Bar\n# Baz\n"],
+ ],
+ "Multi-line reason for skip"
+ );
+};
+
+tests subtest => sub {
+ my $st = 'Test2::Event::Subtest';
+
+ my $one = $st->new(
+ trace => $trace,
+ pass => 1,
+ buffered => 1,
+ name => 'foo',
+ subtest_id => '1-1-1',
+ );
+
+ is_deeply(
+ [$fmt->event_tap($one, 5)],
+ [
+ [OUT_STD, "ok 5 - foo {\n"],
+ [OUT_STD, "}\n"],
+ ],
+ "Got Buffered TAP output"
+ );
+
+ $one->set_buffered(0);
+ is_deeply(
+ [$fmt->event_tap($one, 5)],
+ [
+ [OUT_STD, "ok 5 - foo\n"],
+ ],
+ "Got Unbuffered TAP output"
+ );
+
+ $one = $st->new(
+ trace => $trace,
+ pass => 0,
+ buffered => 1,
+ name => 'bar',
+ subtest_id => '1-1-1',
+ subevents => [
+ Test2::Event::Ok->new(trace => $trace, name => 'first', pass => 1),
+ Test2::Event::Ok->new(trace => $trace, name => 'second', pass => 0),
+ Test2::Event::Ok->new(trace => $trace, name => 'third', pass => 1),
+
+ Test2::Event::Diag->new(trace => $trace, message => 'blah blah'),
+
+ Test2::Event::Plan->new(trace => $trace, max => 3),
+ ],
+ );
+
+ {
+ local $ENV{HARNESS_IS_VERBOSE};
+ is_deeply(
+ [$fmt->event_tap($one, 5)],
+ [
+ [OUT_STD, "not ok 5 - bar {\n"],
+ [OUT_STD, " ok 1 - first\n"],
+ [OUT_STD, " not ok 2 - second\n"],
+ [OUT_STD, " ok 3 - third\n"],
+ [OUT_ERR, " # blah blah\n"],
+ [OUT_STD, " 1..3\n"],
+ [OUT_STD, "}\n"],
+ ],
+ "Got Buffered TAP output (non-verbose)"
+ );
+ }
+
+ {
+ local $ENV{HARNESS_IS_VERBOSE} = 1;
+ is_deeply(
+ [$fmt->event_tap($one, 5)],
+ [
+ [OUT_STD, "not ok 5 - bar {\n"],
+ [OUT_STD, " ok 1 - first\n"],
+ [OUT_STD, " not ok 2 - second\n"],
+ [OUT_STD, " ok 3 - third\n"],
+ [OUT_ERR, " # blah blah\n"],
+ [OUT_STD, " 1..3\n"],
+ [OUT_STD, "}\n"],
+ ],
+ "Got Buffered TAP output (verbose)"
+ );
+ }
+
+ {
+ local $ENV{HARNESS_IS_VERBOSE};
+ $one->set_buffered(0);
+ is_deeply(
+ [$fmt->event_tap($one, 5)],
+ [
+ # In unbuffered TAP the subevents are rendered outside of this.
+ [OUT_STD, "not ok 5 - bar\n"],
+ ],
+ "Got Unbuffered TAP output (non-verbose)"
+ );
+ }
+
+ {
+ local $ENV{HARNESS_IS_VERBOSE} = 1;
+ $one->set_buffered(0);
+ is_deeply(
+ [$fmt->event_tap($one, 5)],
+ [
+ # In unbuffered TAP the subevents are rendered outside of this.
+ [OUT_STD, "not ok 5 - bar\n"],
+ ],
+ "Got Unbuffered TAP output (verbose)"
+ );
+ }
+};
+
+tests skip => sub {
+ my $skip = Test2::Event::Skip->new(trace => $trace, pass => 1, name => 'foo', reason => 'xxx');
+ my @tap = $fmt->event_tap($skip, 7);
+ is_deeply(
+ \@tap,
+ [
+ [OUT_STD, "ok 7 - foo # skip xxx\n"],
+ ],
+ "Passing Skip"
+ );
+
+ $skip->set_pass(0);
+ @tap = $fmt->event_tap($skip, 7);
+ is_deeply(
+ \@tap,
+ [
+ [OUT_STD, "not ok 7 - foo # skip xxx\n"],
+ ],
+ "Failling Skip"
+ );
+
+ $skip->set_todo("xxx");
+ @tap = $fmt->event_tap($skip, 7);
+ is_deeply(
+ \@tap,
+ [
+ [OUT_STD, "not ok 7 - foo # TODO & SKIP xxx\n"],
+ ],
+ "Todo Skip"
+ );
+};
+
+done_testing;
diff --git a/cpan/Test-Simple/t/Test2/modules/Hub.t b/cpan/Test-Simple/t/Test2/modules/Hub.t
new file mode 100644
index 0000000000..4ed48bb61b
--- /dev/null
+++ b/cpan/Test-Simple/t/Test2/modules/Hub.t
@@ -0,0 +1,484 @@
+use strict;
+use warnings;
+
+use Test2::IPC;
+BEGIN { require "t/tools.pl" };
+use Test2::API qw/context test2_ipc_drivers/;
+use Test2::Util qw/CAN_FORK CAN_THREAD CAN_REALLY_FORK/;
+
+{
+ package My::Formatter;
+
+ sub new { bless [], shift };
+
+ my $check = 1;
+ sub write {
+ my $self = shift;
+ my ($e, $count) = @_;
+ push @$self => $e;
+ }
+}
+
+{
+ package My::Event;
+
+ use base 'Test2::Event';
+ use Test2::Util::HashBase qw{msg};
+}
+
+tests basic => sub {
+ my $hub = Test2::Hub->new(
+ formatter => My::Formatter->new,
+ );
+
+ my $send_event = sub {
+ my ($msg) = @_;
+ my $e = My::Event->new(msg => $msg, trace => 'fake');
+ $hub->send($e);
+ };
+
+ ok(my $e1 = $send_event->('foo'), "Created event");
+ ok(my $e2 = $send_event->('bar'), "Created event");
+ ok(my $e3 = $send_event->('baz'), "Created event");
+
+ my $old = $hub->format(My::Formatter->new);
+
+ ok($old->isa('My::Formatter'), "old formatter");
+ is_deeply(
+ $old,
+ [$e1, $e2, $e3],
+ "Formatter got all events"
+ );
+};
+
+tests follow_ups => sub {
+ my $hub = Test2::Hub->new;
+ $hub->set_count(1);
+
+ my $trace = Test2::Util::Trace->new(
+ frame => [__PACKAGE__, __FILE__, __LINE__],
+ );
+
+ my $ran = 0;
+ $hub->follow_up(sub {
+ my ($d, $h) = @_;
+ is_deeply($d, $trace, "Got trace");
+ is_deeply($h, $hub, "Got hub");
+ ok(!$hub->ended, "Hub state has not ended yet");
+ $ran++;
+ });
+
+ like(
+ exception { $hub->follow_up('xxx') },
+ qr/follow_up only takes coderefs for arguments, got 'xxx'/,
+ "follow_up takes a coderef"
+ );
+
+ $hub->finalize($trace);
+
+ is($ran, 1, "ran once");
+
+ is_deeply(
+ $hub->ended,
+ $trace->frame,
+ "Ended at the expected place."
+ );
+
+ eval { $hub->finalize($trace) };
+
+ is($ran, 1, "ran once");
+
+ $hub = undef;
+};
+
+tests IPC => sub {
+ my ($driver) = test2_ipc_drivers();
+ is($driver, 'Test2::IPC::Driver::Files', "Default Driver");
+ my $ipc = $driver->new;
+ my $hub = Test2::Hub->new(
+ formatter => My::Formatter->new,
+ ipc => $ipc,
+ );
+
+ my $build_event = sub {
+ my ($msg) = @_;
+ return My::Event->new(msg => $msg, trace => 'fake');
+ };
+
+ my $e1 = $build_event->('foo');
+ my $e2 = $build_event->('bar');
+ my $e3 = $build_event->('baz');
+
+ my $do_send = sub {
+ $hub->send($e1);
+ $hub->send($e2);
+ $hub->send($e3);
+ };
+
+ my $do_check = sub {
+ my $name = shift;
+
+ my $old = $hub->format(My::Formatter->new);
+
+ ok($old->isa('My::Formatter'), "old formatter");
+ is_deeply(
+ $old,
+ [$e1, $e2, $e3],
+ "Formatter got all events ($name)"
+ );
+ };
+
+ if (CAN_REALLY_FORK) {
+ my $pid = fork();
+ die "Could not fork!" unless defined $pid;
+
+ if ($pid) {
+ is(waitpid($pid, 0), $pid, "waited properly");
+ ok(!$?, "child exited with success");
+ $hub->cull();
+ $do_check->('Fork');
+ }
+ else {
+ $do_send->();
+ exit 0;
+ }
+ }
+
+ if (CAN_THREAD && $] ge '5.010') {
+ require threads;
+ my $thr = threads->new(sub { $do_send->() });
+ $thr->join;
+ $hub->cull();
+ $do_check->('Threads');
+ }
+
+ $do_send->();
+ $hub->cull();
+ $do_check->('no IPC');
+};
+
+tests listen => sub {
+ my $hub = Test2::Hub->new();
+
+ my @events;
+ my @counts;
+ my $it = $hub->listen(sub {
+ my ($h, $e, $count) = @_;
+ is_deeply($h, $hub, "got hub");
+ push @events => $e;
+ push @counts => $count;
+ });
+
+ my $second;
+ my $it2 = $hub->listen(sub { $second++ });
+
+ my $ok1 = Test2::Event::Ok->new(
+ pass => 1,
+ name => 'foo',
+ trace => Test2::Util::Trace->new(
+ frame => [ __PACKAGE__, __FILE__, __LINE__ ],
+ ),
+ );
+
+ my $ok2 = Test2::Event::Ok->new(
+ pass => 0,
+ name => 'bar',
+ trace => Test2::Util::Trace->new(
+ frame => [ __PACKAGE__, __FILE__, __LINE__ ],
+ ),
+ );
+
+ my $ok3 = Test2::Event::Ok->new(
+ pass => 1,
+ name => 'baz',
+ trace => Test2::Util::Trace->new(
+ frame => [ __PACKAGE__, __FILE__, __LINE__ ],
+ ),
+ );
+
+ $hub->send($ok1);
+ $hub->send($ok2);
+
+ $hub->unlisten($it);
+
+ $hub->send($ok3);
+
+ is_deeply(\@counts, [1, 2], "Got counts");
+ is_deeply(\@events, [$ok1, $ok2], "got events");
+ is($second, 3, "got all events in listener that was not removed");
+
+ like(
+ exception { $hub->listen('xxx') },
+ qr/listen only takes coderefs for arguments, got 'xxx'/,
+ "listen takes a coderef"
+ );
+};
+
+tests metadata => sub {
+ my $hub = Test2::Hub->new();
+
+ my $default = { foo => 1 };
+ my $meta = $hub->meta('Foo', $default);
+ is_deeply($meta, $default, "Set Meta");
+
+ $meta = $hub->meta('Foo', {});
+ is_deeply($meta, $default, "Same Meta");
+
+ $hub->delete_meta('Foo');
+ is($hub->meta('Foo'), undef, "No Meta");
+
+ $hub->meta('Foo', {})->{xxx} = 1;
+ is($hub->meta('Foo')->{xxx}, 1, "Vivified meta and set it");
+
+ like(
+ exception { $hub->meta(undef) },
+ qr/Invalid META key: undef, keys must be true, and may not be references/,
+ "Cannot use undef as a meta key"
+ );
+
+ like(
+ exception { $hub->meta(0) },
+ qr/Invalid META key: '0', keys must be true, and may not be references/,
+ "Cannot use 0 as a meta key"
+ );
+
+ like(
+ exception { $hub->delete_meta(undef) },
+ qr/Invalid META key: undef, keys must be true, and may not be references/,
+ "Cannot use undef as a meta key"
+ );
+
+ like(
+ exception { $hub->delete_meta(0) },
+ qr/Invalid META key: '0', keys must be true, and may not be references/,
+ "Cannot use 0 as a meta key"
+ );
+};
+
+tests filter => sub {
+ my $hub = Test2::Hub->new();
+
+ my @events;
+ my $it = $hub->filter(sub {
+ my ($h, $e) = @_;
+ is($h, $hub, "got hub");
+ push @events => $e;
+ return $e;
+ });
+
+ my $count;
+ my $it2 = $hub->filter(sub { $count++; $_[1] });
+
+ my $ok1 = Test2::Event::Ok->new(
+ pass => 1,
+ name => 'foo',
+ trace => Test2::Util::Trace->new(
+ frame => [ __PACKAGE__, __FILE__, __LINE__ ],
+ ),
+ );
+
+ my $ok2 = Test2::Event::Ok->new(
+ pass => 0,
+ name => 'bar',
+ trace => Test2::Util::Trace->new(
+ frame => [ __PACKAGE__, __FILE__, __LINE__ ],
+ ),
+ );
+
+ my $ok3 = Test2::Event::Ok->new(
+ pass => 1,
+ name => 'baz',
+ trace => Test2::Util::Trace->new(
+ frame => [ __PACKAGE__, __FILE__, __LINE__ ],
+ ),
+ );
+
+ $hub->send($ok1);
+ $hub->send($ok2);
+
+ $hub->unfilter($it);
+
+ $hub->send($ok3);
+
+ is_deeply(\@events, [$ok1, $ok2], "got events");
+ is($count, 3, "got all events, even after other filter was removed");
+
+ $hub = Test2::Hub->new();
+ @events = ();
+
+ $hub->filter(sub { undef });
+ $hub->listen(sub {
+ my ($hub, $e) = @_;
+ push @events => $e;
+ });
+
+ $hub->send($ok1);
+ $hub->send($ok2);
+ $hub->send($ok3);
+
+ ok(!@events, "Blocked events");
+
+ like(
+ exception { $hub->filter('xxx') },
+ qr/filter only takes coderefs for arguments, got 'xxx'/,
+ "filter takes a coderef"
+ );
+};
+
+tests pre_filter => sub {
+ my $hub = Test2::Hub->new();
+
+ my @events;
+ my $it = $hub->pre_filter(sub {
+ my ($h, $e) = @_;
+ is($h, $hub, "got hub");
+ push @events => $e;
+ return $e;
+ });
+
+ my $count;
+ my $it2 = $hub->pre_filter(sub { $count++; $_[1] });
+
+ my $ok1 = Test2::Event::Ok->new(
+ pass => 1,
+ name => 'foo',
+ trace => Test2::Util::Trace->new(
+ frame => [ __PACKAGE__, __FILE__, __LINE__ ],
+ ),
+ );
+
+ my $ok2 = Test2::Event::Ok->new(
+ pass => 0,
+ name => 'bar',
+ trace => Test2::Util::Trace->new(
+ frame => [ __PACKAGE__, __FILE__, __LINE__ ],
+ ),
+ );
+
+ my $ok3 = Test2::Event::Ok->new(
+ pass => 1,
+ name => 'baz',
+ trace => Test2::Util::Trace->new(
+ frame => [ __PACKAGE__, __FILE__, __LINE__ ],
+ ),
+ );
+
+ $hub->send($ok1);
+ $hub->send($ok2);
+
+ $hub->pre_unfilter($it);
+
+ $hub->send($ok3);
+
+ is_deeply(\@events, [$ok1, $ok2], "got events");
+ is($count, 3, "got all events, even after other pre_filter was removed");
+
+ $hub = Test2::Hub->new();
+ @events = ();
+
+ $hub->pre_filter(sub { undef });
+ $hub->listen(sub {
+ my ($hub, $e) = @_;
+ push @events => $e;
+ });
+
+ $hub->send($ok1);
+ $hub->send($ok2);
+ $hub->send($ok3);
+
+ ok(!@events, "Blocked events");
+
+ like(
+ exception { $hub->pre_filter('xxx') },
+ qr/pre_filter only takes coderefs for arguments, got 'xxx'/,
+ "pre_filter takes a coderef"
+ );
+};
+
+tests state => sub {
+ my $hub = Test2::Hub->new;
+
+ is($hub->count, 0, "count starts at 0");
+ is($hub->failed, 0, "failed starts at 0");
+ is($hub->is_passing, 1, "start off passing");
+ is($hub->plan, undef, "no plan yet");
+
+ $hub->is_passing(0);
+ is($hub->is_passing, 0, "Can Fail");
+
+ $hub->is_passing(1);
+ is($hub->is_passing, 1, "Passes again");
+
+ $hub->set_count(1);
+ is($hub->count, 1, "Added a passing result");
+ is($hub->failed, 0, "still no fails");
+ is($hub->is_passing, 1, "Still passing");
+
+ $hub->set_count(2);
+ $hub->set_failed(1);
+ is($hub->count, 2, "Added a result");
+ is($hub->failed, 1, "new failure");
+ is($hub->is_passing, 0, "Not passing");
+
+ $hub->is_passing(1);
+ is($hub->is_passing, 0, "is_passing always false after a failure");
+
+ $hub->set_failed(0);
+ $hub->is_passing(1);
+ is($hub->is_passing, 1, "Passes again");
+
+ $hub->set_failed(1);
+ is($hub->count, 2, "No new result");
+ is($hub->failed, 1, "new failure");
+ is($hub->is_passing, 0, "Not passing");
+
+ ok(!eval { $hub->plan('foo'); 1 }, "Could not set plan to 'foo'");
+ like($@, qr/'foo' is not a valid plan! Plan must be an integer greater than 0, 'NO PLAN', or 'SKIP'/, "Got expected error");
+
+ ok($hub->plan(5), "Can set plan to integer");
+ is($hub->plan, 5, "Set the plan to an integer");
+
+ $hub->set__plan(undef);
+ ok($hub->plan('NO PLAN'), "Can set plan to 'NO PLAN'");
+ is($hub->plan, 'NO PLAN', "Set the plan to 'NO PLAN'");
+
+ $hub->set__plan(undef);
+ ok($hub->plan('SKIP'), "Can set plan to 'SKIP'");
+ is($hub->plan, 'SKIP', "Set the plan to 'SKIP'");
+
+ ok(!eval { $hub->plan(5); 1 }, "Cannot change plan");
+ like($@, qr/You cannot change the plan/, "Got error");
+
+ my $trace = Test2::Util::Trace->new(frame => ['Foo::Bar', 'foo.t', 42, 'blah']);
+ $hub->finalize($trace);
+ my $ok = eval { $hub->finalize($trace) };
+ my $err = $@;
+ ok(!$ok, "died");
+
+ is($err, <<" EOT", "Got expected error");
+Test already ended!
+First End: foo.t line 42
+Second End: foo.t line 42
+ EOT
+
+ $hub = Test2::Hub->new;
+
+ $hub->plan(5);
+ $hub->set_count(5);
+ $hub->set_failed(1);
+ $hub->set_ended($trace);
+ $hub->set_bailed_out("foo");
+ $hub->set_skip_reason('xxx');
+ ok(!$hub->is_passing, "not passing");
+
+ $hub->reset_state;
+
+ ok(!$hub->plan, "no plan");
+ is($hub->count, 0, "count reset to 0");
+ is($hub->failed, 0, "reset failures");
+ ok(!$hub->ended, "not ended");
+ ok(!$hub->bailed_out, "did not bail out");
+ ok(!$hub->skip_reason, "no skip reason");
+};
+
+done_testing;
diff --git a/cpan/Test-Simple/t/Test2/modules/Hub/Interceptor.t b/cpan/Test-Simple/t/Test2/modules/Hub/Interceptor.t
new file mode 100644
index 0000000000..a1257210a1
--- /dev/null
+++ b/cpan/Test-Simple/t/Test2/modules/Hub/Interceptor.t
@@ -0,0 +1,15 @@
+use strict;
+use warnings;
+BEGIN { require "t/tools.pl" };
+
+use Test2::Hub::Interceptor;
+
+my $one = Test2::Hub::Interceptor->new();
+
+ok($one->isa('Test2::Hub'), "inheritence");;
+
+my $e = exception { $one->terminate(55) };
+ok($e->isa('Test2::Hub::Interceptor::Terminator'), "exception type");
+is($$e, 55, "Scalar reference value");
+
+done_testing;
diff --git a/cpan/Test-Simple/t/Test2/modules/Hub/Interceptor/Terminator.t b/cpan/Test-Simple/t/Test2/modules/Hub/Interceptor/Terminator.t
new file mode 100644
index 0000000000..9f25d4b317
--- /dev/null
+++ b/cpan/Test-Simple/t/Test2/modules/Hub/Interceptor/Terminator.t
@@ -0,0 +1,9 @@
+use strict;
+use warnings;
+BEGIN { require "t/tools.pl" };
+
+use Test2::Hub::Interceptor::Terminator;
+
+ok($INC{'Test2/Hub/Interceptor/Terminator.pm'}, "loaded");
+
+done_testing;
diff --git a/cpan/Test-Simple/t/Test2/modules/Hub/Subtest.t b/cpan/Test-Simple/t/Test2/modules/Hub/Subtest.t
new file mode 100644
index 0000000000..ca16785720
--- /dev/null
+++ b/cpan/Test-Simple/t/Test2/modules/Hub/Subtest.t
@@ -0,0 +1,124 @@
+use strict;
+use warnings;
+BEGIN { require "t/tools.pl" };
+
+use Test2::Hub::Subtest;
+use Test2::Util qw/get_tid/;
+use Carp qw/croak/;
+
+my %TODO;
+
+sub def {
+ my ($func, @args) = @_;
+
+ my @caller = caller(0);
+
+ $TODO{$caller[0]} ||= [];
+ push @{$TODO{$caller[0]}} => [$func, \@args, \@caller];
+}
+
+sub do_def {
+ my $for = caller;
+ my $tests = delete $TODO{$for} or croak "No tests to run!";
+
+ for my $test (@$tests) {
+ my ($func, $args, $caller) = @$test;
+
+ my ($pkg, $file, $line) = @$caller;
+
+# Note: The '&' below is to bypass the prototype, which is important here.
+ eval <<" EOT" or die $@;
+package $pkg;
+# line $line "(eval in DeferredTests) $file"
+\&$func(\@\$args);
+1;
+ EOT
+ }
+}
+
+my $ran = 0;
+my $event;
+
+my $one = Test2::Hub::Subtest->new(
+ nested => 3,
+);
+
+ok($one->isa('Test2::Hub'), "inheritence");
+
+{
+ no warnings 'redefine';
+ local *Test2::Hub::process = sub { $ran++; (undef, $event) = @_; 'P!' };
+ use warnings;
+
+ my $ok = Test2::Event::Ok->new(
+ pass => 1,
+ name => 'blah',
+ trace => Test2::Util::Trace->new(frame => [__PACKAGE__, __FILE__, __LINE__, 'xxx']),
+ );
+
+ def is => ($one->process($ok), 'P!', "processed");
+ def is => ($ran, 1, "ran the mocked process");
+ def is => ($event, $ok, "got our event");
+ def is => ($event->nested, 3, "nested was set");
+ def is => ($one->bailed_out, undef, "did not bail");
+
+ $ran = 0;
+ $event = undef;
+
+ my $bail = Test2::Event::Bail->new(
+ message => 'blah',
+ trace => Test2::Util::Trace->new(frame => [__PACKAGE__, __FILE__, __LINE__, 'xxx']),
+ );
+
+ def is => ($one->process($bail), 'P!', "processed");
+ def is => ($ran, 1, "ran the mocked process");
+ def is => ($event, $bail, "got our event");
+ def is => ($event->nested, 3, "nested was set");
+ def is => ($one->bailed_out, $event, "bailed");
+}
+
+do_def;
+
+my $skip = Test2::Event::Plan->new(
+ trace => Test2::Util::Trace->new(frame => [__PACKAGE__, __FILE__, __LINE__], pid => $$, tid => get_tid),
+ directive => 'SKIP',
+ reason => 'foo',
+);
+
+$ran = 0;
+T2_SUBTEST_WRAPPER: {
+ $ran++;
+ $one->terminate(100, $skip);
+ $ran++;
+}
+is($ran, 1, "did not get past the terminate");
+
+$ran = 0;
+T2_SUBTEST_WRAPPER: {
+ $ran++;
+ $one->send($skip);
+ $ran++;
+}
+is($ran, 1, "did not get past the terminate");
+
+$one->reset_state;
+$one->set_manual_skip_all(1);
+
+$ran = 0;
+T2_SUBTEST_WRAPPER: {
+ $ran++;
+ $one->terminate(100, $skip);
+ $ran++;
+}
+is($ran, 2, "did not automatically abort");
+
+$one->reset_state;
+$ran = 0;
+T2_SUBTEST_WRAPPER: {
+ $ran++;
+ $one->send($skip);
+ $ran++;
+}
+is($ran, 2, "did not automatically abort");
+
+done_testing;
diff --git a/cpan/Test-Simple/t/Test2/modules/IPC.t b/cpan/Test-Simple/t/Test2/modules/IPC.t
new file mode 100644
index 0000000000..0557925ca4
--- /dev/null
+++ b/cpan/Test-Simple/t/Test2/modules/IPC.t
@@ -0,0 +1,19 @@
+use strict;
+use warnings;
+
+use Test2::IPC qw/cull/;
+use Test2::API qw/context test2_ipc_drivers test2_ipc/;
+
+BEGIN { require "t/tools.pl" };
+
+test2_ipc();
+
+is_deeply(
+ [test2_ipc_drivers()],
+ ['Test2::IPC::Driver::Files'],
+ "Default driver"
+);
+
+ok(__PACKAGE__->can('cull'), "Imported cull");
+
+done_testing;
diff --git a/cpan/Test-Simple/t/Test2/modules/IPC/Driver.t b/cpan/Test-Simple/t/Test2/modules/IPC/Driver.t
new file mode 100644
index 0000000000..f7daf84dd7
--- /dev/null
+++ b/cpan/Test-Simple/t/Test2/modules/IPC/Driver.t
@@ -0,0 +1,57 @@
+use strict;
+use warnings;
+
+use Test2::IPC::Driver::Files;
+
+BEGIN { require "t/tools.pl" };
+use Test2::API qw/context test2_ipc_drivers/;
+
+Test2::IPC::Driver::Files->import();
+Test2::IPC::Driver::Files->import();
+Test2::IPC::Driver::Files->import();
+
+is_deeply(
+ [test2_ipc_drivers()],
+ ['Test2::IPC::Driver::Files'],
+ "Driver not added multiple times"
+);
+
+for my $meth (qw/send cull add_hub drop_hub waiting is_viable/) {
+ my $one = Test2::IPC::Driver->new;
+ like(
+ exception { $one->$meth },
+ qr/'\Q$one\E' did not define the required method '$meth'/,
+ "Require override of method $meth"
+ );
+}
+
+tests abort => sub {
+ my $one = Test2::IPC::Driver->new(no_fatal => 1);
+ my ($err, $out) = ("", "");
+
+ {
+ local *STDERR;
+ local *STDOUT;
+ open(STDERR, '>', \$err);
+ open(STDOUT, '>', \$out);
+ $one->abort('foo');
+ }
+
+ is($err, "IPC Fatal Error: foo\n", "Got error");
+ is($out, "not ok - IPC Fatal Error\n", "got 'not ok' on stdout");
+
+ ($err, $out) = ("", "");
+
+ {
+ local *STDERR;
+ local *STDOUT;
+ open(STDERR, '>', \$err);
+ open(STDOUT, '>', \$out);
+ $one->abort_trace('foo');
+ }
+
+ is($out, "not ok - IPC Fatal Error\n", "got 'not ok' on stdout");
+ like($err, qr/IPC Fatal Error: foo/, "Got error");
+};
+
+done_testing;
diff --git a/cpan/Test-Simple/t/Test2/modules/IPC/Driver/Files.t b/cpan/Test-Simple/t/Test2/modules/IPC/Driver/Files.t
new file mode 100644
index 0000000000..368bbf2363
--- /dev/null
+++ b/cpan/Test-Simple/t/Test2/modules/IPC/Driver/Files.t
@@ -0,0 +1,282 @@
+BEGIN { require "t/tools.pl" };
+use Test2::Util qw/get_tid USE_THREADS try/;
+use File::Temp qw/tempfile/;
+use strict;
+use warnings;
+
+sub capture(&) {
+ my $code = shift;
+
+ my ($err, $out) = ("", "");
+
+ my ($ok, $e);
+ {
+ local *STDOUT;
+ local *STDERR;
+
+ ($ok, $e) = try {
+ open(STDOUT, '>', \$out) or die "Failed to open a temporary STDOUT: $!";
+ open(STDERR, '>', \$err) or die "Failed to open a temporary STDERR: $!";
+
+ $code->();
+ };
+ }
+
+ die $e unless $ok;
+
+ return {
+ STDOUT => $out,
+ STDERR => $err,
+ };
+}
+
+require Test2::IPC::Driver::Files;
+ok(my $ipc = Test2::IPC::Driver::Files->new, "Created an IPC instance");
+ok($ipc->isa('Test2::IPC::Driver::Files'), "Correct type");
+ok($ipc->isa('Test2::IPC::Driver'), "inheritence");
+
+ok(-d $ipc->tempdir, "created temp dir");
+is($ipc->pid, $$, "stored pid");
+is($ipc->tid, get_tid(), "stored the tid");
+
+my $hid = '12345';
+
+$ipc->add_hub($hid);
+ok(-f $ipc->tempdir . '/HUB-' . $hid, "wrote hub file");
+if(ok(open(my $fh, '<', $ipc->tempdir . '/HUB-' . $hid), "opened hub file")) {
+ my @lines = <$fh>;
+ close($fh);
+ is_deeply(
+ \@lines,
+ [ "$$\n", get_tid() . "\n" ],
+ "Wrote pid and tid to hub file"
+ );
+}
+
+{
+ package Foo;
+ use base 'Test2::Event';
+}
+
+$ipc->send($hid, bless({ foo => 1 }, 'Foo'));
+$ipc->send($hid, bless({ bar => 1 }, 'Foo'));
+
+opendir(my $dh, $ipc->tempdir) || die "Could not open tempdir: !?";
+my @files = grep { $_ !~ m/^\.+$/ && $_ ne "HUB-$hid" } readdir($dh);
+closedir($dh);
+is(@files, 2, "2 files added to the IPC directory");
+
+my @events = $ipc->cull($hid);
+is_deeply(
+ \@events,
+ [{ foo => 1 }, { bar => 1 }],
+ "Culled both events"
+);
+
+opendir($dh, $ipc->tempdir) || die "Could not open tempdir: !?";
+@files = grep { $_ !~ m/^\.+$/ && $_ ne "HUB-$hid" } readdir($dh);
+closedir($dh);
+is(@files, 0, "All files collected");
+
+$ipc->drop_hub($hid);
+ok(!-f $ipc->tempdir . '/' . $hid, "removed hub file");
+
+$ipc->send($hid, bless({global => 1}, 'Foo'), 'GLOBAL');
+my @got = $ipc->cull($hid);
+ok(@got == 0, "did not get our own global event");
+
+my $tmpdir = $ipc->tempdir;
+ok(-d $tmpdir, "still have temp dir");
+$ipc = undef;
+ok(!-d $tmpdir, "cleaned up temp dir");
+
+{
+ my $ipc = Test2::IPC::Driver::Files->new();
+
+ my $tmpdir = $ipc->tempdir;
+
+ my $ipc_thread_clone = bless {%$ipc}, 'Test2::IPC::Driver::Files';
+ $ipc_thread_clone->set_tid(100);
+ $ipc_thread_clone = undef;
+ ok(-d $tmpdir, "Directory not removed (different thread)");
+
+ my $ipc_fork_clone = bless {%$ipc}, 'Test2::IPC::Driver::Files';
+ $ipc_fork_clone->set_pid($$ + 10);
+ $ipc_fork_clone = undef;
+ ok(-d $tmpdir, "Directory not removed (different proc)");
+
+
+ $ipc_thread_clone = bless {%$ipc}, 'Test2::IPC::Driver::Files';
+ $ipc_thread_clone->set_tid(undef);
+ $ipc_thread_clone = undef;
+ ok(-d $tmpdir, "Directory not removed (no thread)");
+
+ $ipc_fork_clone = bless {%$ipc}, 'Test2::IPC::Driver::Files';
+ $ipc_fork_clone->set_pid(undef);
+ $ipc_fork_clone = undef;
+ ok(-d $tmpdir, "Directory not removed (no proc)");
+
+ $ipc = undef;
+ ok(!-d $tmpdir, "Directory removed");
+}
+
+{
+ no warnings 'once';
+ local *Test2::IPC::Driver::Files::abort = sub {
+ my $self = shift;
+ local $self->{no_fatal} = 1;
+ $self->Test2::IPC::Driver::abort(@_);
+ die 255;
+ };
+
+ my $tmpdir;
+ my @lines;
+ my $file = __FILE__;
+
+ my $out = capture {
+ local $ENV{T2_KEEP_TEMPDIR} = 1;
+
+ my $ipc = Test2::IPC::Driver::Files->new();
+ $tmpdir = $ipc->tempdir;
+ $ipc->add_hub($hid);
+ eval { $ipc->add_hub($hid) }; push @lines => __LINE__;
+ $ipc->send($hid, bless({ foo => 1 }, 'Foo'));
+ $ipc->cull($hid);
+ $ipc->drop_hub($hid);
+ eval { $ipc->drop_hub($hid) }; push @lines => __LINE__;
+
+ # Make sure having a hub file sitting around does not throw things off
+ # in T2_KEEP_TEMPDIR
+ $ipc->add_hub($hid);
+ $ipc = undef;
+ 1;
+ };
+
+ is($out->{STDOUT}, "not ok - IPC Fatal Error\nnot ok - IPC Fatal Error\n", "printed ");
+
+ like($out->{STDERR}, qr/IPC Temp Dir: \Q$tmpdir\E/m, "Got temp dir path");
+ like($out->{STDERR}, qr/^# Not removing temp dir: \Q$tmpdir\E$/m, "Notice about not closing tempdir");
+
+ like($out->{STDERR}, qr/^IPC Fatal Error: File for hub '12345' already exists/m, "Got message for duplicate hub");
+ like($out->{STDERR}, qr/^IPC Fatal Error: File for hub '12345' does not exist/m, "Cannot remove hub twice");
+
+ $out = capture {
+ my $ipc = Test2::IPC::Driver::Files->new();
+ $ipc->add_hub($hid);
+ my $trace = Test2::Util::Trace->new(frame => [__PACKAGE__, __FILE__, __LINE__, 'foo']);
+ my $e = eval { $ipc->send($hid, bless({glob => \*ok, trace => $trace}, 'Foo')); 1 };
+ print STDERR $@ unless $e || $@ =~ m/^255/;
+ $ipc->drop_hub($hid);
+ };
+
+ like($out->{STDERR}, qr/IPC Fatal Error:/, "Got fatal error");
+ like($out->{STDERR}, qr/There was an error writing an event/, "Explanation");
+ like($out->{STDERR}, qr/Destination: 12345/, "Got dest");
+ like($out->{STDERR}, qr/Origin PID:\s+$$/, "Got pid");
+ like($out->{STDERR}, qr/Error: Can't store GLOB items/, "Got cause");
+
+ $out = capture {
+ my $ipc = Test2::IPC::Driver::Files->new();
+ local $@;
+ eval { $ipc->send($hid, bless({ foo => 1 }, 'Foo')) };
+ print STDERR $@ unless $@ =~ m/^255/;
+ $ipc = undef;
+ };
+ like($out->{STDERR}, qr/IPC Fatal Error: hub '12345' is not available, failed to send event!/, "Cannot send to missing hub");
+
+ $out = capture {
+ my $ipc = Test2::IPC::Driver::Files->new();
+ $ipc->add_hub($hid);
+ $ipc->send($hid, bless({ foo => 1 }, 'Foo'));
+ local $@;
+ eval { $ipc->drop_hub($hid) };
+ print STDERR $@ unless $@ =~ m/^255/;
+ };
+ like($out->{STDERR}, qr/IPC Fatal Error: Not all files from hub '12345' have been collected/, "Leftover files");
+ like($out->{STDERR}, qr/IPC Fatal Error: Leftover files in the directory \(.*\.ready\)/, "What file");
+
+ $out = capture {
+ my $ipc = Test2::IPC::Driver::Files->new();
+ $ipc->add_hub($hid);
+
+ eval { $ipc->send($hid, { foo => 1 }) };
+ print STDERR $@ unless $@ =~ m/^255/;
+
+ eval { $ipc->send($hid, bless({ foo => 1 }, 'xxx')) };
+ print STDERR $@ unless $@ =~ m/^255/;
+ };
+ like($out->{STDERR}, qr/IPC Fatal Error: 'HASH\(.*\)' is not a blessed object/, "Cannot send unblessed objects");
+ like($out->{STDERR}, qr/IPC Fatal Error: 'xxx=HASH\(.*\)' is not an event object!/, "Cannot send non-event objects");
+
+
+ $ipc = Test2::IPC::Driver::Files->new();
+
+ my ($fh, $fn) = tempfile();
+ print $fh "\n";
+ close($fh);
+
+ Storable::store({}, $fn);
+ $out = capture { eval { $ipc->read_event_file($fn) } };
+ like(
+ $out->{STDERR},
+ qr/IPC Fatal Error: Got an unblessed object: 'HASH\(.*\)'/,
+ "Events must actually be events (must be blessed)"
+ );
+
+ Storable::store(bless({}, 'Test2::Event::FakeEvent'), $fn);
+ $out = capture { eval { $ipc->read_event_file($fn) } };
+ like(
+ $out->{STDERR},
+ qr{IPC Fatal Error: Event has unknown type \(Test2::Event::FakeEvent\), tried to load 'Test2/Event/FakeEvent\.pm' but failed: Can't locate Test2/Event/FakeEvent\.pm},
+ "Events must actually be events (not a real module)"
+ );
+
+ Storable::store(bless({}, 'Test2::API'), $fn);
+ $out = capture { eval { $ipc->read_event_file($fn) } };
+ like(
+ $out->{STDERR},
+ qr{'Test2::API=HASH\(.*\)' is not a 'Test2::Event' object},
+ "Events must actually be events (not an event type)"
+ );
+
+ Storable::store(bless({}, 'Foo'), $fn);
+ $out = capture {
+ local @INC;
+ push @INC => ('t/lib', 'lib');
+ eval { $ipc->read_event_file($fn) };
+ };
+ ok(!$out->{STDERR}, "no problem", $out->{STDERR});
+ ok(!$out->{STDOUT}, "no problem", $out->{STDOUT});
+
+ unlink($fn);
+}
+
+{
+ my $ipc = Test2::IPC::Driver::Files->new();
+ $ipc->add_hub($hid);
+ $ipc->send($hid, bless({global => 1}, 'Foo'), 'GLOBAL');
+ $ipc->set_globals({});
+ my @events = $ipc->cull($hid);
+ is_deeply(
+ \@events,
+ [ {global => 1} ],
+ "Got global event"
+ );
+
+ @events = $ipc->cull($hid);
+ ok(!@events, "Did not grab it again");
+
+ $ipc->set_globals({});
+ @events = $ipc->cull($hid);
+ is_deeply(
+ \@events,
+ [ {global => 1} ],
+ "Still there"
+ );
+
+ $ipc->drop_hub($hid);
+ $ipc = undef;
+}
+
+done_testing;
+
diff --git a/cpan/Test-Simple/t/Test2/modules/Util.t b/cpan/Test-Simple/t/Test2/modules/Util.t
new file mode 100644
index 0000000000..1632a95d44
--- /dev/null
+++ b/cpan/Test-Simple/t/Test2/modules/Util.t
@@ -0,0 +1,37 @@
+use strict;
+use warnings;
+
+BEGIN { require "t/tools.pl" };
+use Test2::Util qw/
+ try
+
+ get_tid USE_THREADS
+
+ pkg_to_file
+
+ CAN_FORK
+ CAN_THREAD
+ CAN_REALLY_FORK
+/;
+
+{
+ for my $try (\&try, Test2::Util->can('_manual_try'), Test2::Util->can('_local_try')) {
+ my ($ok, $err) = $try->(sub { die "xxx" });
+ ok(!$ok, "cought exception");
+ like($err, qr/xxx/, "expected exception");
+
+ ($ok, $err) = $try->(sub { 0 });
+ ok($ok, "Success");
+ ok(!$err, "no error");
+ }
+}
+
+is(pkg_to_file('A::Package::Name'), 'A/Package/Name.pm', "Converted package to file");
+
+# Make sure running them does not die
+# We cannot really do much to test these.
+CAN_THREAD();
+CAN_FORK();
+CAN_REALLY_FORK();
+
+done_testing;
diff --git a/cpan/Test-Simple/t/Test2/modules/Util/ExternalMeta.t b/cpan/Test-Simple/t/Test2/modules/Util/ExternalMeta.t
new file mode 100644
index 0000000000..de6be9963a
--- /dev/null
+++ b/cpan/Test-Simple/t/Test2/modules/Util/ExternalMeta.t
@@ -0,0 +1,70 @@
+use strict;
+use warnings;
+BEGIN { require "t/tools.pl" };
+
+{
+ package Foo::Bar;
+
+ use Test2::Util::ExternalMeta;
+ use Test2::Util::HashBase qw/foo bar/;
+}
+
+ok(Foo::Bar->can($_), "Imported '$_'") for qw/meta get_meta set_meta delete_meta/;
+
+my $one = Foo::Bar->new(foo => 1, bar => 2);
+ok($one->isa('Foo::Bar'), "Got instance");
+
+is_deeply($one, {foo => 1, bar => 2}, "nothing fishy.. yet");
+
+is($one->get_meta('foo'), undef, "no meta-data for foo");
+is($one->get_meta('bar'), undef, "no meta-data for bar");
+is($one->get_meta('baz'), undef, "no meta-data for baz");
+
+is($one->meta('foo'), undef, "no meta-data for foo");
+is($one->meta('bar'), undef, "no meta-data for bar");
+is($one->meta('baz'), undef, "no meta-data for baz");
+
+is_deeply($one, {foo => 1, bar => 2}, "Still have not modified instance");
+
+$one->set_meta('foo' => 123);
+is($one->foo, 1, "did not change attribute");
+is($one->meta('foo'), 123, "get meta-data for foo");
+is($one->get_meta('foo'), 123, "get meta-data for foo again");
+
+$one->meta('foo', 345);
+is($one->foo, 1, "did not change attribute");
+is($one->meta('foo', 678), 123, "did not alter already set meta-attribute");
+is($one->get_meta('foo'), 123, "still did not alter already set meta-attribute");
+
+is($one->meta('bar', 789), 789, "used default for bar");
+is($one->bar, 2, "did not change attribute");
+
+is_deeply(
+ $one,
+ {
+ foo => 1,
+ bar => 2,
+ Test2::Util::ExternalMeta::META_KEY() => {
+ foo => 123,
+ bar => 789,
+ },
+ },
+ "Stored meta-data"
+);
+
+is($one->delete_meta('foo'), 123, "got old value on delete");
+is($one->meta('foo'), undef, "no more value");
+
+is_deeply(
+ $one,
+ {
+ foo => 1,
+ bar => 2,
+ Test2::Util::ExternalMeta::META_KEY() => {
+ bar => 789,
+ },
+ },
+ "Deleted the meta key"
+);
+
+done_testing;
diff --git a/cpan/Test-Simple/t/Test2/modules/Util/HashBase.t b/cpan/Test-Simple/t/Test2/modules/Util/HashBase.t
new file mode 100644
index 0000000000..0e81e9fcec
--- /dev/null
+++ b/cpan/Test-Simple/t/Test2/modules/Util/HashBase.t
@@ -0,0 +1,105 @@
+use strict;
+use warnings;
+BEGIN { require "t/tools.pl" };
+BEGIN {
+ $INC{'My/HBase.pm'} = __FILE__;
+
+ package My::HBase;
+ use Test2::Util::HashBase qw/foo bar baz/;
+
+ main::is(FOO, 'foo', "FOO CONSTANT");
+ main::is(BAR, 'bar', "BAR CONSTANT");
+ main::is(BAZ, 'baz', "BAZ CONSTANT");
+}
+
+BEGIN {
+ package My::HBaseSub;
+ use base 'My::HBase';
+ use Test2::Util::HashBase qw/apple pear/;
+
+ main::is(FOO, 'foo', "FOO CONSTANT");
+ main::is(BAR, 'bar', "BAR CONSTANT");
+ main::is(BAZ, 'baz', "BAZ CONSTANT");
+ main::is(APPLE, 'apple', "APPLE CONSTANT");
+ main::is(PEAR, 'pear', "PEAR CONSTANT");
+}
+
+my $one = My::HBase->new(foo => 'a', bar => 'b', baz => 'c');
+is($one->foo, 'a', "Accessor");
+is($one->bar, 'b', "Accessor");
+is($one->baz, 'c', "Accessor");
+$one->set_foo('x');
+is($one->foo, 'x', "Accessor set");
+$one->set_foo(undef);
+
+is_deeply(
+ $one,
+ {
+ foo => undef,
+ bar => 'b',
+ baz => 'c',
+ },
+ 'hash'
+);
+
+BEGIN {
+ package My::Const::Test;
+ use Test2::Util::HashBase qw/foo/;
+
+ sub do_it {
+ if (FOO()) {
+ return 'const';
+ }
+ return 'not const'
+ }
+}
+
+my $pkg = 'My::Const::Test';
+is($pkg->do_it, 'const', "worked as expected");
+{
+ local $SIG{__WARN__} = sub { };
+ *My::Const::Test::FOO = sub { 0 };
+}
+ok(!$pkg->FOO, "overrode const sub");
+is($pkg->do_it, 'const', "worked as expected, const was constant");
+
+BEGIN {
+ $INC{'My/HBase/Wrapped.pm'} = __FILE__;
+
+ package My::HBase::Wrapped;
+ use Test2::Util::HashBase qw/foo bar/;
+
+ my $foo = __PACKAGE__->can('foo');
+ no warnings 'redefine';
+ *foo = sub {
+ my $self = shift;
+ $self->set_bar(1);
+ $self->$foo(@_);
+ };
+}
+
+BEGIN {
+ $INC{'My/HBase/Wrapped/Inherit.pm'} = __FILE__;
+
+ package My::HBase::Wrapped::Inherit;
+ use base 'My::HBase::Wrapped';
+ use Test2::Util::HashBase;
+}
+
+my $o = My::HBase::Wrapped::Inherit->new(foo => 1);
+my $foo = $o->foo;
+is($o->bar, 1, 'parent attribute sub not overridden');
+
+{
+ package Foo;
+
+ sub new;
+
+ use Test2::Util::HashBase qw/foo bar baz/;
+
+ sub new { 'foo' };
+}
+
+is(Foo->new, 'foo', "Did not override existing 'new' method");
+
+done_testing;
diff --git a/cpan/Test-Simple/t/Test2/modules/Util/Trace.t b/cpan/Test-Simple/t/Test2/modules/Util/Trace.t
new file mode 100644
index 0000000000..846c04ff6c
--- /dev/null
+++ b/cpan/Test-Simple/t/Test2/modules/Util/Trace.t
@@ -0,0 +1,41 @@
+use strict;
+use warnings;
+BEGIN { require "t/tools.pl" };
+use Test2::Util::Trace;
+
+like(
+ exception { 'Test2::Util::Trace'->new() },
+ qr/The 'frame' attribute is required/,
+ "got error"
+);
+
+my $one = 'Test2::Util::Trace'->new(frame => ['Foo::Bar', 'foo.t', 5, 'Foo::Bar::foo']);
+is_deeply($one->frame, ['Foo::Bar', 'foo.t', 5, 'Foo::Bar::foo'], "Got frame");
+is_deeply([$one->call], ['Foo::Bar', 'foo.t', 5, 'Foo::Bar::foo'], "Got call");
+is($one->package, 'Foo::Bar', "Got package");
+is($one->file, 'foo.t', "Got file");
+is($one->line, 5, "Got line");
+is($one->subname, 'Foo::Bar::foo', "got subname");
+
+is($one->debug, "at foo.t line 5", "got trace");
+$one->set_detail("yo momma");
+is($one->debug, "yo momma", "got detail for trace");
+$one->set_detail(undef);
+
+is(
+ exception { $one->throw('I died') },
+ "I died at foo.t line 5.\n",
+ "got exception"
+);
+
+is_deeply(
+ warnings { $one->alert('I cried') },
+ [ "I cried at foo.t line 5.\n" ],
+ "alter() warns"
+);
+
+my $snap = $one->snapshot;
+is_deeply($snap, $one, "identical");
+ok($snap != $one, "Not the same instance");
+
+done_testing;
diff --git a/cpan/Test-Simple/t/Test2/regression/gh_16.t b/cpan/Test-Simple/t/Test2/regression/gh_16.t
new file mode 100644
index 0000000000..45e4cd7b76
--- /dev/null
+++ b/cpan/Test-Simple/t/Test2/regression/gh_16.t
@@ -0,0 +1,23 @@
+use strict;
+use warnings;
+
+# This test is for gh #16
+# Also see https://rt.perl.org/Public/Bug/Display.html?id=127774
+
+# Ceate this END before anything else so that $? gets set to 0
+END { $? = 0 }
+
+BEGIN {
+ print "\n1..1\n";
+ close(STDERR);
+ open(STDERR, '>&', STDOUT);
+}
+
+use Test2::API;
+
+eval(' sub { die "xxx" } ')->();
+END {
+ sub { my $ctx = Test2::API::context(); $ctx->release; }->();
+ print "ok 1 - Did not segv\n";
+ $? = 0;
+}
diff --git a/cpan/Test-Simple/t/Test2/regression/ipc_files_abort_exit.t b/cpan/Test-Simple/t/Test2/regression/ipc_files_abort_exit.t
new file mode 100644
index 0000000000..1bbc624e31
--- /dev/null
+++ b/cpan/Test-Simple/t/Test2/regression/ipc_files_abort_exit.t
@@ -0,0 +1,62 @@
+use strict;
+use warnings;
+use Test2::IPC;
+BEGIN { require "t/tools.pl" };
+use Test2::API qw/context test2_stack/;
+use Test2::Util qw/CAN_FORK/;
+
+BEGIN {
+ skip_all "System cannot fork" unless CAN_FORK;
+}
+
+plan(3);
+
+pipe(my ($read, $write));
+
+test2_stack()->top;
+my $hub = test2_stack()->new_hub();
+
+my $pid = fork();
+die "Failed to fork" unless defined $pid;
+
+if ($pid) {
+ close($read);
+ test2_stack()->pop($hub);
+ $hub = undef;
+ print $write "Go\n";
+ close($write);
+ waitpid($pid, 0);
+ my $err = $? >> 8;
+ is($err, 255, "Exit code was not masked");
+ ok($err != 100, "Did not hit the safety exit");
+}
+else {
+ close($write);
+ my $ignore = <$read>;
+ close($read);
+ close(STDERR);
+ close(STDOUT);
+ open(STDERR, '>', my $x);
+ my $ctx = context(hub => $hub, level => -1);
+ my $clone = $ctx->snapshot;
+ $ctx->release;
+ $clone->ok(0, "Should not see this");
+ print STDERR "\n\nSomething went wrong!!!!\n\n";
+ exit 100; # Safety exit
+};
+
+
+# The rest of this is to make sure nothing that happens when reading the event
+# messes with $?.
+
+pipe($read, $write);
+
+$pid = fork;
+die "Failed to fork" unless defined $pid;
+
+unless($pid) {
+ my $ignore = <$read>;
+ ok(1, "Test in forked process");
+}
+
+print $write "Go\n";