Upgrade Test-Simple from version 1.302122 to 1.302133
authorTodd Rinaldo <toddr@cpan.org>
Sun, 18 Mar 2018 22:07:59 +0000 (17:07 -0500)
committerTodd Rinaldo <toddr@cpan.org>
Sun, 18 Mar 2018 22:07:59 +0000 (17:07 -0500)
[DELTA]

1.302133  2018-03-11 12:48:37-07:00 America/Los_Angeles

    - No changes since last trial

1.302132  2018-03-09 15:43:51-08:00 America/Los_Angeles (TRIAL RELEASE)

    - Add method to validate facet data
    - Add Test2::Event::V2 event class, and context helpers
    - Improve how events handle facets
    - Break out meta_facet_data
    - Document and fix Facets2Legacy
    - Fix nested and in_subtest to look at hub facets
    - Fix event->related and trace with uuid

1.302131  2018-03-07 09:36:16-08:00 America/Los_Angeles (TRIAL RELEASE)

    - Make sure event puts the uuid into the about facet

1.302130  2018-03-07 08:07:54-08:00 America/Los_Angeles

    - No changes since last trial

1.302129  2018-03-06 13:43:22-08:00 America/Los_Angeles (TRIAL RELEASE)

    - Make hubs tag events with a new facet

1.302128  2018-03-05 09:26:53-08:00 America/Los_Angeles

    - No changes since the trial

1.302127  2018-03-02 12:43:56-08:00 America/Los_Angeles (TRIAL RELEASE)

    - Fix missing UUID in Test::Builder subtests

1.302126  2018-03-01 23:15:52-08:00 America/Los_Angeles (TRIAL RELEASE)

    - Add optional UUID tagging

1.302125  2018-02-21 23:10:39-08:00 America/Los_Angeles

    - No changes since trial

1.302124  2018-02-13 22:02:48-08:00 America/Los_Angeles (TRIAL RELEASE)

    - Fix a test to skip without threads

1.302123  2018-02-13 21:39:31-08:00 America/Los_Angeles (TRIAL RELEASE)

    - Make it possible to disable IPC

77 files changed:
MANIFEST
Porting/Maintainers.pl
cpan/Test-Simple/lib/Test/Builder.pm
cpan/Test-Simple/lib/Test/Builder/Formatter.pm
cpan/Test-Simple/lib/Test/Builder/Module.pm
cpan/Test-Simple/lib/Test/Builder/Tester.pm
cpan/Test-Simple/lib/Test/Builder/Tester/Color.pm
cpan/Test-Simple/lib/Test/Builder/TodoDiag.pm
cpan/Test-Simple/lib/Test/More.pm
cpan/Test-Simple/lib/Test/Simple.pm
cpan/Test-Simple/lib/Test/Tester.pm
cpan/Test-Simple/lib/Test/Tester/Capture.pm
cpan/Test-Simple/lib/Test/Tester/CaptureRunner.pm
cpan/Test-Simple/lib/Test/Tester/Delegate.pm
cpan/Test-Simple/lib/Test/use/ok.pm
cpan/Test-Simple/lib/Test2.pm
cpan/Test-Simple/lib/Test2/API.pm
cpan/Test-Simple/lib/Test2/API/Breakage.pm
cpan/Test-Simple/lib/Test2/API/Context.pm
cpan/Test-Simple/lib/Test2/API/Instance.pm
cpan/Test-Simple/lib/Test2/API/Stack.pm
cpan/Test-Simple/lib/Test2/Event.pm
cpan/Test-Simple/lib/Test2/Event/Bail.pm
cpan/Test-Simple/lib/Test2/Event/Diag.pm
cpan/Test-Simple/lib/Test2/Event/Encoding.pm
cpan/Test-Simple/lib/Test2/Event/Exception.pm
cpan/Test-Simple/lib/Test2/Event/Fail.pm
cpan/Test-Simple/lib/Test2/Event/Generic.pm
cpan/Test-Simple/lib/Test2/Event/Note.pm
cpan/Test-Simple/lib/Test2/Event/Ok.pm
cpan/Test-Simple/lib/Test2/Event/Pass.pm
cpan/Test-Simple/lib/Test2/Event/Plan.pm
cpan/Test-Simple/lib/Test2/Event/Skip.pm
cpan/Test-Simple/lib/Test2/Event/Subtest.pm
cpan/Test-Simple/lib/Test2/Event/TAP/Version.pm
cpan/Test-Simple/lib/Test2/Event/V2.pm [new file with mode: 0644]
cpan/Test-Simple/lib/Test2/Event/Waiting.pm
cpan/Test-Simple/lib/Test2/EventFacet.pm
cpan/Test-Simple/lib/Test2/EventFacet/About.pm
cpan/Test-Simple/lib/Test2/EventFacet/Amnesty.pm
cpan/Test-Simple/lib/Test2/EventFacet/Assert.pm
cpan/Test-Simple/lib/Test2/EventFacet/Control.pm
cpan/Test-Simple/lib/Test2/EventFacet/Error.pm
cpan/Test-Simple/lib/Test2/EventFacet/Hub.pm [new file with mode: 0644]
cpan/Test-Simple/lib/Test2/EventFacet/Info.pm
cpan/Test-Simple/lib/Test2/EventFacet/Meta.pm
cpan/Test-Simple/lib/Test2/EventFacet/Parent.pm
cpan/Test-Simple/lib/Test2/EventFacet/Plan.pm
cpan/Test-Simple/lib/Test2/EventFacet/Render.pm
cpan/Test-Simple/lib/Test2/EventFacet/Trace.pm
cpan/Test-Simple/lib/Test2/Formatter.pm
cpan/Test-Simple/lib/Test2/Formatter/TAP.pm
cpan/Test-Simple/lib/Test2/Hub.pm
cpan/Test-Simple/lib/Test2/Hub/Interceptor.pm
cpan/Test-Simple/lib/Test2/Hub/Interceptor/Terminator.pm
cpan/Test-Simple/lib/Test2/Hub/Subtest.pm
cpan/Test-Simple/lib/Test2/IPC.pm
cpan/Test-Simple/lib/Test2/IPC/Driver.pm
cpan/Test-Simple/lib/Test2/IPC/Driver/Files.pm
cpan/Test-Simple/lib/Test2/Tools/Tiny.pm
cpan/Test-Simple/lib/Test2/Util.pm
cpan/Test-Simple/lib/Test2/Util/ExternalMeta.pm
cpan/Test-Simple/lib/Test2/Util/Facets2Legacy.pm
cpan/Test-Simple/lib/Test2/Util/HashBase.pm
cpan/Test-Simple/lib/Test2/Util/Trace.pm
cpan/Test-Simple/lib/ok.pm
cpan/Test-Simple/t/Test2/behavior/disable_ipc_a.t [new file with mode: 0644]
cpan/Test-Simple/t/Test2/behavior/disable_ipc_b.t [new file with mode: 0644]
cpan/Test-Simple/t/Test2/behavior/disable_ipc_c.t [new file with mode: 0644]
cpan/Test-Simple/t/Test2/behavior/disable_ipc_d.t [new file with mode: 0644]
cpan/Test-Simple/t/Test2/behavior/uuid.t [new file with mode: 0644]
cpan/Test-Simple/t/Test2/modules/API.t
cpan/Test-Simple/t/Test2/modules/API/Instance.t
cpan/Test-Simple/t/Test2/modules/Event.t
cpan/Test-Simple/t/Test2/modules/Event/Generic.t
cpan/Test-Simple/t/Test2/modules/Event/V2.t [new file with mode: 0644]
cpan/Test-Simple/t/Test2/modules/Hub.t

index c11c8f5..8493227 100644 (file)
--- a/MANIFEST
+++ b/MANIFEST
@@ -2286,6 +2286,7 @@ cpan/Test-Simple/lib/Test2/Event/Plan.pm
 cpan/Test-Simple/lib/Test2/Event/Skip.pm
 cpan/Test-Simple/lib/Test2/Event/Subtest.pm
 cpan/Test-Simple/lib/Test2/Event/TAP/Version.pm
+cpan/Test-Simple/lib/Test2/Event/V2.pm
 cpan/Test-Simple/lib/Test2/Event/Waiting.pm
 cpan/Test-Simple/lib/Test2/EventFacet.pm
 cpan/Test-Simple/lib/Test2/EventFacet/About.pm
@@ -2293,6 +2294,7 @@ cpan/Test-Simple/lib/Test2/EventFacet/Amnesty.pm
 cpan/Test-Simple/lib/Test2/EventFacet/Assert.pm
 cpan/Test-Simple/lib/Test2/EventFacet/Control.pm
 cpan/Test-Simple/lib/Test2/EventFacet/Error.pm
+cpan/Test-Simple/lib/Test2/EventFacet/Hub.pm
 cpan/Test-Simple/lib/Test2/EventFacet/Info.pm
 cpan/Test-Simple/lib/Test2/EventFacet/Meta.pm
 cpan/Test-Simple/lib/Test2/EventFacet/Parent.pm
@@ -2495,6 +2497,10 @@ cpan/Test-Simple/t/Test2/acceptance/try_it_plan.t
 cpan/Test-Simple/t/Test2/acceptance/try_it_skip.t
 cpan/Test-Simple/t/Test2/acceptance/try_it_threads.t
 cpan/Test-Simple/t/Test2/acceptance/try_it_todo.t
+cpan/Test-Simple/t/Test2/behavior/disable_ipc_a.t
+cpan/Test-Simple/t/Test2/behavior/disable_ipc_b.t
+cpan/Test-Simple/t/Test2/behavior/disable_ipc_c.t
+cpan/Test-Simple/t/Test2/behavior/disable_ipc_d.t
 cpan/Test-Simple/t/Test2/behavior/err_var.t
 cpan/Test-Simple/t/Test2/behavior/Formatter.t
 cpan/Test-Simple/t/Test2/behavior/init_croak.t
@@ -2512,6 +2518,7 @@ cpan/Test-Simple/t/Test2/behavior/Subtest_plan.t
 cpan/Test-Simple/t/Test2/behavior/Subtest_todo.t
 cpan/Test-Simple/t/Test2/behavior/Taint.t
 cpan/Test-Simple/t/Test2/behavior/trace_signature.t
+cpan/Test-Simple/t/Test2/behavior/uuid.t
 cpan/Test-Simple/t/Test2/legacy/TAP.t
 cpan/Test-Simple/t/Test2/modules/API.t
 cpan/Test-Simple/t/Test2/modules/API/Breakage.t
@@ -2532,6 +2539,7 @@ cpan/Test-Simple/t/Test2/modules/Event/Plan.t
 cpan/Test-Simple/t/Test2/modules/Event/Skip.t
 cpan/Test-Simple/t/Test2/modules/Event/Subtest.t
 cpan/Test-Simple/t/Test2/modules/Event/TAP/Version.t
+cpan/Test-Simple/t/Test2/modules/Event/V2.t
 cpan/Test-Simple/t/Test2/modules/Event/Waiting.t
 cpan/Test-Simple/t/Test2/modules/EventFacet.t
 cpan/Test-Simple/t/Test2/modules/EventFacet/About.t
index 2f985fc..8898378 100755 (executable)
@@ -1082,7 +1082,7 @@ use File::Glob qw(:case);
     },
 
     'Test::Simple' => {
-        'DISTRIBUTION' => 'EXODIST/Test-Simple-1.302122.tar.gz',
+        'DISTRIBUTION' => 'EXODIST/Test-Simple-1.302133.tar.gz',
         'FILES'        => q[cpan/Test-Simple],
         'EXCLUDED'     => [
             qr{^examples/},
index 24bd85a..46f93cc 100644 (file)
@@ -4,7 +4,7 @@ use 5.006;
 use strict;
 use warnings;
 
-our $VERSION = '1.302122';
+our $VERSION = '1.302133';
 
 BEGIN {
     if( $] < 5.008 ) {
@@ -21,7 +21,7 @@ BEGIN {
     warn "Test::Builder was loaded after Test2 initialization, this is not recommended."
         if Test2::API::test2_init_done() || Test2::API::test2_load_done();
 
-    if (USE_THREADS) {
+    if (USE_THREADS && ! Test2::API::test2_ipc_disabled()) {
         require Test2::IPC;
         require Test2::IPC::Driver::Files;
         Test2::IPC::Driver::Files->import;
@@ -120,7 +120,7 @@ sub new {
 
         Test2::API::test2_add_callback_exit(sub { $Test->_ending(@_) });
 
-        Test2::API::test2_ipc()->set_no_fatal(1) if USE_THREADS;
+        Test2::API::test2_ipc()->set_no_fatal(1) if Test2::API::test2_has_ipc();
     }
     return $Test;
 }
@@ -221,6 +221,7 @@ sub child {
     $meta->{Test_Results} = [];
     $meta->{subevents} = $subevents;
     $meta->{subtest_id} = $hub->id;
+    $meta->{subtest_uuid} = $hub->uuid;
     $meta->{subtest_buffered} = $parent->format ? 0 : 1;
 
     $self->_add_ts_hooks;
@@ -301,6 +302,7 @@ FAIL
         else {
             $parent->{subevents}  = $meta->{subevents};
             $parent->{subtest_id} = $meta->{subtest_id};
+            $parent->{subtest_uuid} = $meta->{subtest_uuid};
             $parent->{subtest_buffered} = $meta->{subtest_buffered};
             $parent->ok( $chub->is_passing, $meta->{Name} );
         }
@@ -671,11 +673,12 @@ sub ok {
     my @attrs;
     my $subevents  = delete $self->{subevents};
     my $subtest_id = delete $self->{subtest_id};
+    my $subtest_uuid = delete $self->{subtest_uuid};
     my $subtest_buffered = delete $self->{subtest_buffered};
     my $epkg = 'Test2::Event::Ok';
     if ($subevents) {
         $epkg = 'Test2::Event::Subtest';
-        push @attrs => (subevents => $subevents, subtest_id => $subtest_id, buffered => $subtest_buffered);
+        push @attrs => (subevents => $subevents, subtest_id => $subtest_id, subtest_uuid => $subtest_uuid, buffered => $subtest_buffered);
     }
 
     my $e = bless {
@@ -2541,6 +2544,18 @@ bugs to support.
 Test::Builder is only thread-aware if threads.pm is loaded I<before>
 Test::Builder.
 
+You can directly disable thread support with one of the following:
+
+    $ENV{T2_NO_IPC} = 1
+
+or
+
+    no Test2::IPC;
+
+or
+
+    Test2::API::test2_ipc_disable()
+
 =head1 MEMORY
 
 An informative hash, accessible via C<details()>, is stored for each
index 2e6dde3..82a8eea 100644 (file)
@@ -2,7 +2,7 @@ package Test::Builder::Formatter;
 use strict;
 use warnings;
 
-our $VERSION = '1.302122';
+our $VERSION = '1.302133';
 
 BEGIN { require Test2::Formatter::TAP; our @ISA = qw(Test2::Formatter::TAP) }
 
index ca09081..be66239 100644 (file)
@@ -7,7 +7,7 @@ use Test::Builder;
 require Exporter;
 our @ISA = qw(Exporter);
 
-our $VERSION = '1.302122';
+our $VERSION = '1.302133';
 
 
 =head1 NAME
index c5e29fd..25ad7c5 100644 (file)
@@ -1,7 +1,7 @@
 package Test::Builder::Tester;
 
 use strict;
-our $VERSION = '1.302122';
+our $VERSION = '1.302133';
 
 use Test::Builder;
 use Symbol;
index 513422c..5cb0e34 100644 (file)
@@ -1,7 +1,7 @@
 package Test::Builder::Tester::Color;
 
 use strict;
-our $VERSION = '1.302122';
+our $VERSION = '1.302133';
 
 require Test::Builder::Tester;
 
index 4baf799..b972a49 100644 (file)
@@ -2,7 +2,7 @@ package Test::Builder::TodoDiag;
 use strict;
 use warnings;
 
-our $VERSION = '1.302122';
+our $VERSION = '1.302133';
 
 BEGIN { require Test2::Event::Diag; our @ISA = qw(Test2::Event::Diag) }
 
index b2a7c78..9184c62 100644 (file)
@@ -17,7 +17,7 @@ sub _carp {
     return warn @_, " at $file line $line\n";
 }
 
-our $VERSION = '1.302122';
+our $VERSION = '1.302133';
 
 use Test::Builder::Module;
 our @ISA    = qw(Test::Builder::Module);
index f7ee0aa..5622021 100644 (file)
@@ -4,7 +4,7 @@ use 5.006;
 
 use strict;
 
-our $VERSION = '1.302122';
+our $VERSION = '1.302133';
 
 use Test::Builder::Module;
 our @ISA    = qw(Test::Builder::Module);
index 040233f..758a497 100644 (file)
@@ -18,7 +18,7 @@ require Exporter;
 
 use vars qw( @ISA @EXPORT );
 
-our $VERSION = '1.302122';
+our $VERSION = '1.302133';
 
 @EXPORT = qw( run_tests check_tests check_test cmp_results show_space );
 @ISA = qw( Exporter );
index 01d9788..d3e3425 100644 (file)
@@ -2,7 +2,7 @@ use strict;
 
 package Test::Tester::Capture;
 
-our $VERSION = '1.302122';
+our $VERSION = '1.302133';
 
 
 use Test::Builder;
index e5b7da2..57f7357 100644 (file)
@@ -3,7 +3,7 @@ use strict;
 
 package Test::Tester::CaptureRunner;
 
-our $VERSION = '1.302122';
+our $VERSION = '1.302133';
 
 
 use Test::Tester::Capture;
index 1c5c4d6..a1ea620 100644 (file)
@@ -3,7 +3,7 @@ use warnings;
 
 package Test::Tester::Delegate;
 
-our $VERSION = '1.302122';
+our $VERSION = '1.302133';
 
 use Scalar::Util();
 
index e5b26a8..1405824 100644 (file)
@@ -1,7 +1,7 @@
 package Test::use::ok;
 use 5.005;
 
-our $VERSION = '1.302122';
+our $VERSION = '1.302133';
 
 
 __END__
index 1db69af..b3b6d3c 100644 (file)
@@ -2,7 +2,7 @@ package Test2;
 use strict;
 use warnings;
 
-our $VERSION = '1.302122';
+our $VERSION = '1.302133';
 
 
 1;
index f0d4b43..f5caaa6 100644 (file)
@@ -9,7 +9,7 @@ BEGIN {
     $ENV{TEST2_ACTIVE} = 1;
 }
 
-our $VERSION = '1.302122';
+our $VERSION = '1.302133';
 
 
 my $INST;
@@ -97,6 +97,8 @@ our @EXPORT_OK = qw{
     test2_ipc_wait_disable
     test2_ipc_wait_enabled
 
+    test2_add_uuid_via
+
     test2_add_callback_context_aquire
     test2_add_callback_context_acquire
     test2_add_callback_context_init
@@ -114,6 +116,8 @@ our @EXPORT_OK = qw{
 
     test2_ipc
     test2_has_ipc
+    test2_ipc_disable
+    test2_ipc_disabled
     test2_ipc_drivers
     test2_ipc_add_driver
     test2_ipc_polling
@@ -191,8 +195,15 @@ sub test2_list_exit_callbacks            { @{$INST->exit_callbacks} }
 sub test2_list_post_load_callbacks       { @{$INST->post_load_callbacks} }
 sub test2_list_pre_subtest_callbacks     { @{$INST->pre_subtest_callbacks} }
 
+sub test2_add_uuid_via {
+    $INST->set_add_uuid_via(@_) if @_;
+    $INST->add_uuid_via();
+}
+
 sub test2_ipc                 { $INST->ipc }
 sub test2_has_ipc             { $INST->has_ipc }
+sub test2_ipc_disable         { $INST->ipc_disable }
+sub test2_ipc_disabled        { $INST->ipc_disabled }
 sub test2_ipc_add_driver      { $INST->add_ipc_driver(@_) }
 sub test2_ipc_drivers         { @{$INST->ipc_drivers} }
 sub test2_ipc_polling         { $INST->ipc_polling }
@@ -229,6 +240,7 @@ sub _contexts_ref                  { $INST->contexts }
 sub _context_acquire_callbacks_ref { $INST->context_acquire_callbacks }
 sub _context_init_callbacks_ref    { $INST->context_init_callbacks }
 sub _context_release_callbacks_ref { $INST->context_release_callbacks }
+sub _add_uuid_via_ref              { \($INST->{Test2::API::Instance::ADD_UUID_VIA()}) }
 
 # Private, for use in Test2::IPC
 sub _set_ipc { $INST->set_ipc(@_) }
@@ -276,6 +288,7 @@ sub no_context(&;$) {
     return;
 };
 
+my $UUID_VIA = _add_uuid_via_ref();
 my $CID = 1;
 sub context {
     # We need to grab these before anything else to ensure they are not
@@ -353,13 +366,18 @@ sub context {
     # hit with how often this needs to be called.
     my $trace = bless(
         {
-            frame    => [$pkg, $file, $line, $sub],
-            pid      => $$,
-            tid      => get_tid(),
-            cid      => 'C' . $CID++,
-            hid      => $hid,
-            nested   => $hub->{nested},
+            frame  => [$pkg, $file, $line, $sub],
+            pid    => $$,
+            tid    => get_tid(),
+            cid    => 'C' . $CID++,
+            hid    => $hid,
+            nested => $hub->{nested},
             buffered => $hub->{buffered},
+
+            $$UUID_VIA ? (
+                huuid => $hub->{uuid},
+                uuid  => ${$UUID_VIA}->('context'),
+            ) : (),
         },
         'Test2::EventFacet::Trace'
     );
@@ -620,7 +638,7 @@ sub run_subtest {
         }
     }
 
-    $hub->finalize($trace->snapshot(hid => $hub->hid, nested => $hub->nested, buffered => $buffered), 1)
+    $hub->finalize($trace->snapshot(huuid => $hub->uuid, hid => $hub->hid, nested => $hub->nested, buffered => $buffered), 1)
         if $ok
         && !$hub->no_ending
         && !$hub->ended;
@@ -628,11 +646,12 @@ sub run_subtest {
     my $pass = $ok && $hub->is_passing;
     my $e = $ctx->build_event(
         'Subtest',
-        pass       => $pass,
-        name       => $name,
-        subtest_id => $hub->id,
-        buffered   => $buffered,
-        subevents  => \@events,
+        pass         => $pass,
+        name         => $name,
+        subtest_id   => $hub->id,
+        subtest_uuid => $hub->uuid,
+        buffered     => $buffered,
+        subevents    => \@events,
     );
 
     my $plan_ok = $hub->check_plan;
@@ -1222,6 +1241,14 @@ Check if Test2 believes it is the END phase.
 This will return the global L<Test2::API::Stack> instance. If this has not
 yet been initialized it will be initialized now.
 
+=item test2_ipc_disable
+
+Disable IPC.
+
+=item $bool = test2_ipc_diabled
+
+Check if IPC is disabled.
+
 =item test2_ipc_wait_enable()
 
 =item test2_ipc_wait_disable()
@@ -1357,6 +1384,22 @@ Returns all the post load callback references.
 
 Returns all the pre-subtest callback references.
 
+=item test2_add_uuid_via(sub { ... })
+
+=item $sub = test2_add_uuid_via()
+
+This allows you to provide a UUID generator. If provided UUIDs will be attached
+to all events, hubs, and contexts. This is useful for storing, tracking, and
+linking these objects.
+
+The sub you provide should always return a unique identifier. Most things will
+expect a proper UUID string, however nothing in Test2::API enforces this.
+
+The sub will receive exactly 1 argument, the type of thing being tagged
+'context', 'hub', or 'event'. In the future additional things may be tagged, in
+which case new strings will be passed in. These are purely informative, you can
+(and usually should) ignore them.
+
 =back
 
 =head2 IPC AND CONCURRENCY
index dfa5af0..08aee9d 100644 (file)
@@ -2,7 +2,7 @@ package Test2::API::Breakage;
 use strict;
 use warnings;
 
-our $VERSION = '1.302122';
+our $VERSION = '1.302133';
 
 
 use Test2::Util qw/pkg_to_file/;
index eedab27..6141a43 100644 (file)
@@ -2,7 +2,7 @@ package Test2::API::Context;
 use strict;
 use warnings;
 
-our $VERSION = '1.302122';
+our $VERSION = '1.302133';
 
 
 use Carp qw/confess croak/;
@@ -19,7 +19,7 @@ my %LOADED = (
         my $file = "Test2/Event/$_.pm";
         require $file unless $INC{$file};
         ( $pkg => $pkg, $_ => $pkg )
-    } qw/Ok Diag Note Plan Bail Exception Waiting Skip Subtest Pass Fail/
+    } qw/Ok Diag Note Plan Bail Exception Waiting Skip Subtest Pass Fail V2/
 );
 
 use Test2::Util::ExternalMeta qw/meta get_meta set_meta delete_meta/;
@@ -200,6 +200,42 @@ sub alert {
     $self->trace->alert($msg);
 }
 
+sub send_ev2_and_release {
+    my $self = shift;
+    my $out  = $self->send_ev2(@_);
+    $self->release;
+    return $out;
+}
+
+sub send_ev2 {
+    my $self = shift;
+
+    my $e;
+    {
+        local $Carp::CarpLevel = $Carp::CarpLevel + 1;
+        $e = Test2::Event::V2->new(
+            trace => $self->{+TRACE}->snapshot,
+            @_,
+        );
+    }
+
+    if ($self->{+_ABORTED}) {
+        my $f = $e->facet_data;
+        ${$self->{+_ABORTED}}++ if $f->{control}->{halt} || defined($f->{control}->{terminate}) || defined($e->terminate);
+    }
+    $self->{+HUB}->send($e);
+}
+
+sub build_ev2 {
+    my $self = shift;
+
+    local $Carp::CarpLevel = $Carp::CarpLevel + 1;
+    Test2::Event::V2->new(
+        trace => $self->{+TRACE}->snapshot,
+        @_,
+    );
+}
+
 sub send_event_and_release {
     my $self = shift;
     my $out = $self->send_event(@_);
@@ -614,6 +650,21 @@ The value of C<$@> when the context was created.
 
 =head2 EVENT PRODUCTION METHODS
 
+B<Which one do I use?>
+
+The C<pass*> and C<fail*> are optimal if they meet your situation, using one of
+them will always be the most optimal. That said they are optimal by eliminating
+many features.
+
+Method such as C<ok>, and C<note> are shortcuts for generating common 1-task
+events based on the old API, however they are forward compatible, and easy to
+use. If these meet your needs then go ahead and use them, but please check back
+often for alternatives that may be added.
+
+If you want to generate new style events, events that do many things at once,
+then you want the C<*ev2*> methods. These let you directly specify which facets
+you wish to use.
+
 =over 4
 
 =item $event = $ctx->pass()
@@ -737,8 +788,45 @@ Send an L<Test2::Event::Skip> event.
 This sends an L<Test2::Event::Bail> event. This event will completely
 terminate all testing.
 
+=item $event = $ctx->send_ev2(%facets)
+
+This lets you build and send a V2 event directly from facets. The event is
+returned after it is sent.
+
+This example sends a single assertion, a note (comment for stdout in
+Test::Builder talk) and sets the plan to 1.
+
+    my $event = $ctx->send_event(
+        plan   => {count => 1},
+        assert => {pass  => 1, details => "A passing assert"},
+        info => [{tag => 'NOTE', details => "This is a note"}],
+    );
+
+=item $event = $ctx->build_e2(%facets)
+
+This is the same as C<send_ev2()>, except it builds and returns the event
+without sending it.
+
+=item $event = $ctx->send_ev2_and_release($Type, %parameters)
+
+This is a combination of C<send_ev2()> and C<release()>.
+
+    sub shorthand {
+        my $ctx = context();
+        return $ctx->send_ev2_and_release(assert => {pass => 1, details => 'foo'});
+    }
+
+    sub longform {
+        my $ctx = context();
+        my $event = $ctx->send_ev2(assert => {pass => 1, details => 'foo'});
+        $ctx->release;
+        return $event;
+    }
+
 =item $event = $ctx->send_event($Type, %parameters)
 
+B<It is better to use send_ev2() in new code.>
+
 This lets you build and send an event of any type. The C<$Type> argument should
 be the event package name with C<Test2::Event::> left off, or a fully
 qualified package name prefixed with a '+'. The event is returned after it is
@@ -752,11 +840,15 @@ or
 
 =item $event = $ctx->build_event($Type, %parameters)
 
+B<It is better to use build_ev2() in new code.>
+
 This is the same as C<send_event()>, except it builds and returns the event
 without sending it.
 
 =item $event = $ctx->send_event_and_release($Type, %parameters)
 
+B<It is better to use send_ev2_and_release() in new code.>
+
 This is a combination of C<send_event()> and C<release()>.
 
     sub shorthand {
index 2334893..0522dd7 100644 (file)
@@ -2,7 +2,7 @@ package Test2::API::Instance;
 use strict;
 use warnings;
 
-our $VERSION = '1.302122';
+our $VERSION = '1.302133';
 
 
 our @CARP_NOT = qw/Test2::API Test2::API::Instance Test2::IPC::Driver Test2::Formatter/;
@@ -21,8 +21,11 @@ use Test2::Util::HashBase qw{
     ipc stack formatter
     contexts
 
+    add_uuid_via
+
     -preload
 
+    ipc_disabled
     ipc_shm_size
     ipc_shm_last
     ipc_shm_id
@@ -97,12 +100,15 @@ sub post_preload_reset {
     delete $self->{+_PID};
     delete $self->{+_TID};
 
+    $self->{+ADD_UUID_VIA} = undef unless exists $self->{+ADD_UUID_VIA};
+
     $self->{+CONTEXTS} = {};
 
     $self->{+FORMATTERS} = [];
 
     $self->{+FINALIZED} = undef;
     $self->{+IPC}       = undef;
+    $self->{+IPC_DISABLED} = $ENV{T2_NO_IPC} ? 1 : 0;
 
     $self->{+IPC_TIMEOUT} = DEFAULT_IPC_TIMEOUT() unless defined $self->{+IPC_TIMEOUT};
 
@@ -117,7 +123,9 @@ sub reset {
     delete $self->{+_PID};
     delete $self->{+_TID};
 
-    $self->{+CONTEXTS}    = {};
+    $self->{+ADD_UUID_VIA} = undef;
+
+    $self->{+CONTEXTS} = {};
 
     $self->{+IPC_DRIVERS} = [];
     $self->{+IPC_POLLING} = undef;
@@ -125,8 +133,9 @@ sub reset {
     $self->{+FORMATTERS} = [];
     $self->{+FORMATTER}  = undef;
 
-    $self->{+FINALIZED} = undef;
-    $self->{+IPC}       = undef;
+    $self->{+FINALIZED}    = undef;
+    $self->{+IPC}          = undef;
+    $self->{+IPC_DISABLED} = $ENV{T2_NO_IPC} ? 1 : 0;
 
     $self->{+IPC_TIMEOUT} = DEFAULT_IPC_TIMEOUT() unless defined $self->{+IPC_TIMEOUT};
 
@@ -192,6 +201,7 @@ sub _finalize {
 
     # Turn on IPC if threads are on, drivers are registered, or the Test2::IPC
     # module is loaded.
+    return if $self->{+IPC_DISABLED};
     return unless USE_THREADS || $INC{'Test2/IPC.pm'} || @{$self->{+IPC_DRIVERS}};
 
     # Turn on polling by default, people expect it.
@@ -322,6 +332,15 @@ sub add_exit_callback {
     push @{$self->{+EXIT_CALLBACKS}} => $code;
 }
 
+sub ipc_disable {
+    my $self = shift;
+
+    confess "Attempt to disable IPC after it has been initialized"
+        if $self->{+IPC};
+
+    $self->{+IPC_DISABLED} = 1;
+}
+
 sub add_ipc_driver {
     my $self = shift;
     my ($driver) = @_;
@@ -814,6 +833,14 @@ L<Test2>).
 
 Get the one true IPC instance.
 
+=item $obj->ipc_disable
+
+Turn IPC off
+
+=item $bool = $obj->ipc_disabled
+
+Check if IPC is disabled
+
 =item $stack = $obj->stack
 
 Get the one true hub stack.
@@ -838,6 +865,22 @@ a warning will be generated:
 
     "Formatter $formatter loaded too late to be used as the global formatter"
 
+=item $obj->set_add_uuid_via(sub { ... })
+
+=item $sub = $obj->add_uuid_via()
+
+This allows you to provide a UUID generator. If provided UUIDs will be attached
+to all events, hubs, and contexts. This is useful for storing, tracking, and
+linking these objects.
+
+The sub you provide should always return a unique identifier. Most things will
+expect a proper UUID string, however nothing in Test2::API enforces this.
+
+The sub will receive exactly 1 argument, the type of thing being tagged
+'context', 'hub', or 'event'. In the future additional things may be tagged, in
+which case new strings will be passed in. These are purely informative, you can
+(and usually should) ignore them.
+
 =back
 
 =head1 SOURCE
index 8d2cb71..8007e1a 100644 (file)
@@ -2,7 +2,7 @@ package Test2::API::Stack;
 use strict;
 use warnings;
 
-our $VERSION = '1.302122';
+our $VERSION = '1.302133';
 
 
 use Test2::Hub();
index 4e877d3..307d49b 100644 (file)
@@ -2,9 +2,12 @@ package Test2::Event;
 use strict;
 use warnings;
 
-our $VERSION = '1.302122';
+our $VERSION = '1.302133';
 
-use Test2::Util::HashBase qw/trace -amnesty/;
+use Scalar::Util qw/blessed reftype/;
+use Carp qw/croak/;
+
+use Test2::Util::HashBase qw/trace -amnesty uuid -hubs/;
 use Test2::Util::ExternalMeta qw/meta get_meta set_meta delete_meta/;
 use Test2::Util qw(pkg_to_file);
 
@@ -18,25 +21,53 @@ use Test2::EventFacet::Meta();
 use Test2::EventFacet::Parent();
 use Test2::EventFacet::Plan();
 use Test2::EventFacet::Trace();
-
-my @FACET_TYPES = qw{
-    Test2::EventFacet::About
-    Test2::EventFacet::Amnesty
-    Test2::EventFacet::Assert
-    Test2::EventFacet::Control
-    Test2::EventFacet::Error
-    Test2::EventFacet::Info
-    Test2::EventFacet::Meta
-    Test2::EventFacet::Parent
-    Test2::EventFacet::Plan
-    Test2::EventFacet::Trace
-};
-
-sub FACET_TYPES() { @FACET_TYPES }
+use Test2::EventFacet::Hub();
 
 # Legacy tools will expect this to be loaded now
 require Test2::Util::Trace;
 
+my %LOADED_FACETS = (
+    'about'   => 'Test2::EventFacet::About',
+    'amnesty' => 'Test2::EventFacet::Amnesty',
+    'assert'  => 'Test2::EventFacet::Assert',
+    'control' => 'Test2::EventFacet::Control',
+    'errors'  => 'Test2::EventFacet::Error',
+    'info'    => 'Test2::EventFacet::Info',
+    'meta'    => 'Test2::EventFacet::Meta',
+    'parent'  => 'Test2::EventFacet::Parent',
+    'plan'    => 'Test2::EventFacet::Plan',
+    'trace'   => 'Test2::EventFacet::Trace',
+    'hubs'    => 'Test2::EventFacet::Hub',
+);
+
+sub FACET_TYPES { sort values %LOADED_FACETS }
+
+sub load_facet {
+    my $class = shift;
+    my ($facet) = @_;
+
+    return $LOADED_FACETS{$facet} if exists $LOADED_FACETS{$facet};
+
+    my @check = ($facet);
+    if ('s' eq substr($facet, -1, 1)) {
+        push @check => substr($facet, 0, -1);
+    }
+    else {
+        push @check => $facet . 's';
+    }
+
+    my $found;
+    for my $check (@check) {
+        my $mod  = "Test2::EventFacet::" . ucfirst($facet);
+        my $file = pkg_to_file($mod);
+        next unless eval { require $file; 1 };
+        $found = $mod;
+        last;
+    }
+
+    return undef unless $found;
+    $LOADED_FACETS{$facet} = $found;
+}
 
 sub causes_fail      { 0 }
 sub increments_count { 0 }
@@ -59,6 +90,13 @@ sub related {
     my $tracea = $self->trace  or return undef;
     my $traceb = $event->trace or return undef;
 
+    my $uuida = $tracea->uuid;
+    my $uuidb = $traceb->uuid;
+    if ($uuida && $uuidb) {
+        return 1 if $uuida eq $uuidb;
+        return 0;
+    }
+
     my $siga = $tracea->signature or return undef;
     my $sigb = $traceb->signature or return undef;
 
@@ -66,6 +104,11 @@ sub related {
     return 0;
 }
 
+sub add_hub {
+    my $self = shift;
+    unshift @{$self->{+HUBS}} => @_;
+}
+
 sub add_amnesty {
     my $self = shift;
 
@@ -83,22 +126,37 @@ sub common_facet_data {
     my %out;
 
     $out{about} = {package => ref($self) || undef};
+    if (my $uuid = $self->uuid) {
+        $out{about}->{uuid} = $uuid;
+    }
 
     if (my $trace = $self->trace) {
         $out{trace} = { %$trace };
     }
 
+    if (my $hubs = $self->hubs) {
+        $out{hubs} = $hubs;
+    }
+
     $out{amnesty} = [map {{ %{$_} }} @{$self->{+AMNESTY}}]
         if $self->{+AMNESTY};
 
-    my $key = Test2::Util::ExternalMeta::META_KEY();
-    if (my $hash = $self->{$key}) {
-        $out{meta} = {%$hash};
+    if (my $meta = $self->meta_facet_data) {
+        $out{meta} = $meta;
     }
 
     return \%out;
 }
 
+sub meta_facet_data {
+    my $self = shift;
+
+    my $key = Test2::Util::ExternalMeta::META_KEY();
+
+    my $hash = $self->{$key} or return undef;
+    return {%$hash};
+}
+
 sub facet_data {
     my $self = shift;
 
@@ -165,39 +223,98 @@ sub facet_data {
 
 sub facets {
     my $self = shift;
-    my $data = $self->facet_data;
     my %out;
 
-    for my $type (FACET_TYPES()) {
-        my $key = $type->facet_key;
-        next unless $data->{$key};
+    my $data = $self->facet_data;
+    my @errors = $self->validate_facet_data($data);
+    die join "\n" => @errors if @errors;
+
+    for my $facet (keys %$data) {
+        my $class = $self->load_facet($facet);
+        my $val = $data->{$facet};
 
-        if ($type->is_list) {
-            $out{$key} = [map { $type->new($_) } @{$data->{$key}}];
+        unless($class) {
+            $out{$facet} = $val;
+            next;
+        }
+
+        my $is_list = reftype($val) eq 'ARRAY' ? 1 : 0;
+        if ($is_list) {
+            $out{$facet} = [map { $class->new($_) } @$val];
         }
         else {
-            $out{$key} = $type->new($data->{$key});
+            $out{$facet} = $class->new($val);
         }
     }
 
     return \%out;
 }
 
+sub validate_facet_data {
+    my $class_or_self = shift;
+    my ($f, %params);
+
+    $f = shift if @_ && (reftype($_[0]) || '') eq 'HASH';
+    %params = @_;
+
+    $f ||= $class_or_self->facet_data if blessed($class_or_self);
+    croak "No facet data" unless $f;
+
+    my @errors;
+
+    for my $k (sort keys %$f) {
+        my $fclass = $class_or_self->load_facet($k);
+
+        push @errors => "Could not find a facet class for facet '$k'"
+            if $params{require_facet_class} && !$fclass;
+
+        next unless $fclass;
+
+        my $v = $f->{$k};
+        next unless defined($v); # undef is always fine
+
+        my $is_list = $fclass->is_list();
+        my $got_list = reftype($v) eq 'ARRAY' ? 1 : 0;
+
+        push @errors => "Facet '$k' should be a list, but got a single item ($v)"
+            if $is_list && !$got_list;
+
+        push @errors => "Facet '$k' should not be a list, but got a a list ($v)"
+            if $got_list && !$is_list;
+    }
+
+    return @errors;
+}
+
 sub nested {
+    my $self = shift;
+
     Carp::cluck("Use of Test2::Event->nested() is deprecated, use Test2::Event->trace->nested instead")
         if $ENV{AUTHOR_TESTING};
 
-    $_[0]->{+TRACE}->{nested};
+    if (my $hubs = $self->{+HUBS}) {
+        return $hubs->[0]->{nested} if @$hubs;
+    }
+
+    my $trace = $self->{+TRACE} or return undef;
+    return $trace->{nested};
 }
 
 sub in_subtest {
+    my $self = shift;
+
     Carp::cluck("Use of Test2::Event->in_subtest() is deprecated, use Test2::Event->trace->hid instead")
         if $ENV{AUTHOR_TESTING};
 
-    # Return undef if we are not nested, Legacy did not return the hid if nestign was 0.
-    return undef unless $_[0]->{+TRACE}->{nested};
+    my $hubs = $self->{+HUBS};
+    if ($hubs && @$hubs) {
+        return undef unless $hubs->[0]->{nested};
+        return $hubs->[0]->{hid}
+    }
 
-    $_[0]->{+TRACE}->{hid};
+    my $trace = $self->{+TRACE} or return undef;
+    return undef unless $trace->{nested};
+    return $trace->{hid};
 }
 
 1;
@@ -303,6 +420,37 @@ In other words it marks a failure as expected and allowed.
 B<Note:> This is how 'TODO' is implemented under the hood. TODO is essentially
 amnesty with the 'TODO' tag. The details are the reason for the TODO.
 
+=item $uuid = $e->uuid
+
+If UUID tagging is enabled (See L<Test::API>) then any event that has made its
+way through a hub will be tagged with a UUID. A newly created event will not
+yet be tagged in most cases.
+
+=item $class = $e->load_facet($name)
+
+This method is used to load a facet by name (or key). It will attempt to load
+the facet class, if it succeeds it will return the class it loaded. If it fails
+it will return C<undef>. This caches the result at the class level so that
+future calls will be faster.
+
+The C<$name> variable should be the key used to access the facet in a facets
+hashref. For instance the assertion facet has the key 'assert', the information
+facet has the 'info' key, and the error facet has the key 'errors'. You may
+include or omit the 's' at the end of the name, the method is smart enough to
+try both the 's' and no-'s' forms, it will check what you provided first, and
+if that is not found it will add or strip the 's and try again.
+
+=item @classes = $e->FACET_TYPES()
+
+=item @classes = Test2::Event->FACET_TYPES()
+
+This returns a list of all facets that have been loaded using the
+C<load_facet()> method. This will not return any classes that have not been
+loaded, or have been loaded directly without a call to C<load_facet()>.
+
+B<Note:> The core facet types are automatically loaded and populated in this
+list.
+
 =back
 
 =head2 NEW API
@@ -325,7 +473,42 @@ write out explicit facet data.
 =item $hashref = $e->facets()
 
 This takes the hashref from C<facet_data()> and blesses each facet into the
-proper C<Test2::EventFacet::*> subclass.
+proper C<Test2::EventFacet::*> subclass. If no class can be found for any given
+facet it will be passed along unchanged.
+
+=item @errors = $e->validate_facet_data();
+
+=item @errors = $e->validate_facet_data(%params);
+
+=item @errors = $e->validate_facet_data(\%facets, %params);
+
+=item @errors = Test2::Event->validate_facet_data(%params);
+
+=item @errors = Test2::Event->validate_facet_data(\%facets, %params);
+
+This method will validate facet data and return a list of errors. If no errors
+are found this will return an empty list.
+
+This can be called as an object method with no arguments, in which case the
+C<facet_data()> method will be called to get the facet data to be validated.
+
+When used as an object method the C<\%facet_data> argument may be omitted.
+
+When used as a class method the C<\%facet_data> argument is required.
+
+Remaining arguments will be slurped into a C<%params> hash.
+
+Currently only 1 parameter is defined:
+
+=over 4
+
+=item require_facet_class => $BOOL
+
+When set to true (default is false) this will reject any facets where a facet
+class cannot be found. Normally facets without classes are assumed to be custom
+and are ignored.
+
+=back
 
 =back
 
index 010b0ff..bfd99ee 100644 (file)
@@ -2,7 +2,7 @@ package Test2::Event::Bail;
 use strict;
 use warnings;
 
-our $VERSION = '1.302122';
+our $VERSION = '1.302133';
 
 
 BEGIN { require Test2::Event; our @ISA = qw(Test2::Event) }
index 933a667..f5d3a6a 100644 (file)
@@ -2,7 +2,7 @@ package Test2::Event::Diag;
 use strict;
 use warnings;
 
-our $VERSION = '1.302122';
+our $VERSION = '1.302133';
 
 
 BEGIN { require Test2::Event; our @ISA = qw(Test2::Event) }
index 036803e..cd4b28d 100644 (file)
@@ -2,7 +2,7 @@ package Test2::Event::Encoding;
 use strict;
 use warnings;
 
-our $VERSION = '1.302122';
+our $VERSION = '1.302133';
 
 use Carp qw/croak/;
 
index cf79168..4ec36c5 100644 (file)
@@ -2,7 +2,7 @@ package Test2::Event::Exception;
 use strict;
 use warnings;
 
-our $VERSION = '1.302122';
+our $VERSION = '1.302133';
 
 
 BEGIN { require Test2::Event; our @ISA = qw(Test2::Event) }
index 0871d7e..f243918 100644 (file)
@@ -2,7 +2,7 @@ package Test2::Event::Fail;
 use strict;
 use warnings;
 
-our $VERSION = '1.302122';
+our $VERSION = '1.302133';
 
 use Test2::EventFacet::Info;
 
index 8019958..b5bf934 100644 (file)
@@ -5,7 +5,7 @@ use warnings;
 use Carp qw/croak/;
 use Scalar::Util qw/reftype/;
 
-our $VERSION = '1.302122';
+our $VERSION = '1.302133';
 
 BEGIN { require Test2::Event; our @ISA = qw(Test2::Event) }
 use Test2::Util::HashBase;
index d065a1b..2fb1d0d 100644 (file)
@@ -2,7 +2,7 @@ package Test2::Event::Note;
 use strict;
 use warnings;
 
-our $VERSION = '1.302122';
+our $VERSION = '1.302133';
 
 
 BEGIN { require Test2::Event; our @ISA = qw(Test2::Event) }
index bf86c62..fb9a8db 100644 (file)
@@ -2,7 +2,7 @@ package Test2::Event::Ok;
 use strict;
 use warnings;
 
-our $VERSION = '1.302122';
+our $VERSION = '1.302133';
 
 
 BEGIN { require Test2::Event; our @ISA = qw(Test2::Event) }
index 05f1e8d..78fe1b2 100644 (file)
@@ -2,7 +2,7 @@ package Test2::Event::Pass;
 use strict;
 use warnings;
 
-our $VERSION = '1.302122';
+our $VERSION = '1.302133';
 
 use Test2::EventFacet::Info;
 
index 34ca8e0..a75e14d 100644 (file)
@@ -2,7 +2,7 @@ package Test2::Event::Plan;
 use strict;
 use warnings;
 
-our $VERSION = '1.302122';
+our $VERSION = '1.302133';
 
 
 BEGIN { require Test2::Event; our @ISA = qw(Test2::Event) }
index 91aae52..7a2e660 100644 (file)
@@ -2,7 +2,7 @@ package Test2::Event::Skip;
 use strict;
 use warnings;
 
-our $VERSION = '1.302122';
+our $VERSION = '1.302133';
 
 
 BEGIN { require Test2::Event::Ok; our @ISA = qw(Test2::Event::Ok) }
index dc65c86..3feb0f1 100644 (file)
@@ -2,10 +2,10 @@ package Test2::Event::Subtest;
 use strict;
 use warnings;
 
-our $VERSION = '1.302122';
+our $VERSION = '1.302133';
 
 BEGIN { require Test2::Event::Ok; our @ISA = qw(Test2::Event::Ok) }
-use Test2::Util::HashBase qw{subevents buffered subtest_id};
+use Test2::Util::HashBase qw{subevents buffered subtest_id subtest_uuid};
 
 sub init {
     my $self = shift;
index 50710d5..03f2621 100644 (file)
@@ -2,7 +2,7 @@ package Test2::Event::TAP::Version;
 use strict;
 use warnings;
 
-our $VERSION = '1.302122';
+our $VERSION = '1.302133';
 
 use Carp qw/croak/;
 
diff --git a/cpan/Test-Simple/lib/Test2/Event/V2.pm b/cpan/Test-Simple/lib/Test2/Event/V2.pm
new file mode 100644 (file)
index 0000000..86a44d4
--- /dev/null
@@ -0,0 +1,238 @@
+package Test2::Event::V2;
+use strict;
+use warnings;
+
+our $VERSION = '1.302133';
+
+use Scalar::Util qw/reftype/;
+use Carp qw/croak/;
+
+BEGIN { require Test2::Event; our @ISA = qw(Test2::Event) }
+
+use Test2::Util::Facets2Legacy qw{
+    causes_fail diagnostics global increments_count no_display sets_plan
+    subtest_id summary terminate
+};
+
+use Test2::Util::HashBase qw/-about/;
+
+sub non_facet_keys {
+    return (
+        +UUID,
+        Test2::Util::ExternalMeta::META_KEY(),
+    );
+}
+
+sub init {
+    my $self = shift;
+
+    my $uuid;
+    if ($uuid = $self->{+UUID}) {
+        croak "uuid '$uuid' passed to constructor, but uuid '$self->{+ABOUT}->{uuid}' is already set in the 'about' facet"
+            if $self->{+ABOUT}->{uuid} && $self->{+ABOUT}->{uuid} ne $uuid;
+
+        $self->{+ABOUT}->{uuid} = $uuid;
+    }
+    elsif ($uuid = $self->{+ABOUT}->{uuid}) {
+        $self->SUPER::set_uuid($uuid);
+    }
+
+    # Clone the trace, make sure it is blessed
+    if (my $trace = $self->{+TRACE}) {
+        $self->{+TRACE} = Test2::EventFacet::Trace->new(%$trace);
+    }
+}
+
+sub set_uuid {
+    my $self = shift;
+    my ($uuid) = @_;
+    $self->{+ABOUT}->{uuid} = $uuid;
+    $self->SUPER::set_uuid($uuid);
+}
+
+sub facet_data {
+    my $self = shift;
+    my $f = { %{$self} };
+
+    delete $f->{$_} for $self->non_facet_keys;
+
+    my %out;
+    for my $k (keys %$f) {
+        next if substr($k, 0, 1) eq '_';
+
+        my $data = $f->{$k};
+        my $is_list = reftype($data) eq 'ARRAY';
+        $out{$k} = $is_list ? [ map { {%{$_}} } @$data ] : {%$data};
+    }
+
+    if (my $meta = $self->meta_facet_data) {
+        $out{meta} = {%$meta, %{$out{meta} || {}}};
+    }
+
+    return \%out;
+}
+
+1;
+
+__END__
+
+=pod
+
+=encoding UTF-8
+
+=head1 NAME
+
+Test2::Event::V2 - Second generation event.
+
+=head1 DESCRIPTION
+
+This is the event type that should be used instead of L<Test2::Event> or its
+legacy subclasses.
+
+=head1 SYNOPSIS
+
+=head2 USING A CONTEXT
+
+    use Test2::API qw/context/;
+
+    sub my_tool {
+        my $ctx = context();
+
+        my $event = $ctx->send_ev2(info => [{tag => 'NOTE', details => "This is a note"}]);
+
+        $ctx->release;
+
+        return $event;
+    }
+
+=head2 USING THE CONSTRUCTOR
+
+    use Test2::Event::V2;
+
+    my $e = Test2::Event::V2->new(
+        trace => {frame => [$PKG, $FILE, $LINE, $SUBNAME]},
+        info  => [{tag => 'NOTE', details => "This is a note"}],
+    );
+
+=head1 METHODS
+
+This class inherits from L<Test2::Event>.
+
+=over 4
+
+=item $fd = $e->facet_data()
+
+This will return a hashref of facet data. Each facet hash will be a shallow
+copy of the original.
+
+=item $about = $e->about()
+
+This will return the 'about' facet hashref.
+
+B<NOTE:> This will return the internal hashref, not a copy.
+
+=item $trace = $e->trace()
+
+This will return the 'trace' facet, normally blessed (but this is not enforced
+when the trace is set using C<set_trace()>.
+
+B<NOTE:> This will return the internal trace, not a copy.
+
+=back
+
+=head2 MUTATION
+
+=over 4
+
+=item $e->add_amnesty({...})
+
+Inherited from L<Test2::Event>. This can be used to add 'amnesty' facets to an
+existing event. Each new item is added to the B<END> of the list.
+
+B<NOTE:> Items B<ARE> blessed when added.
+
+=item $e->add_hub({...})
+
+Inherited from L<Test2::Event>. This is used by hubs to stamp events as they
+pass through. New items are added to the B<START> of the list.
+
+B<NOTE:> Items B<ARE NOT> blessed when added.
+
+=item $e->set_uuid($UUID)
+
+Inherited from L<Test2::Event>, overridden to also vivify/mutate the 'about'
+facet.
+
+=item $e->set_trace($trace)
+
+Inherited from L<Test2::Event> which allows you to change the trace.
+
+B<Note:> This method does not bless/clone the trace for you. Many things will
+expect the trace to be blessed, so you should probably do that.
+
+=back
+
+=head2 LEGACY SUPPORT METHODS
+
+These are all imported from L<Test2::Util::Facets2Legacy>, see that module or
+L<Test2::Event> for documentation on what they do.
+
+=over 4
+
+=item causes_fail
+
+=item diagnostics
+
+=item global
+
+=item increments_count
+
+=item no_display
+
+=item sets_plan
+
+=item subtest_id
+
+=item summary
+
+=item terminate
+
+=back
+
+=head1 THIRD PARTY META-DATA
+
+This object consumes L<Test2::Util::ExternalMeta> which provides a consistent
+way for you to attach meta-data to instances of this class. This is useful for
+tools, plugins, and other extensions.
+
+=head1 SOURCE
+
+The source code repository for Test2 can be found at
+F<http://github.com/Test-More/test-more/>.
+
+=head1 MAINTAINERS
+
+=over 4
+
+=item Chad Granum E<lt>exodist@cpan.orgE<gt>
+
+=back
+
+=head1 AUTHORS
+
+=over 4
+
+=item Chad Granum E<lt>exodist@cpan.orgE<gt>
+
+=back
+
+=head1 COPYRIGHT
+
+Copyright 2018 Chad Granum E<lt>exodist@cpan.orgE<gt>.
+
+This program is free software; you can redistribute it and/or
+modify it under the same terms as Perl itself.
+
+See F<http://dev.perl.org/licenses/>
+
+=cut
index c1a2f86..58b583f 100644 (file)
@@ -2,7 +2,7 @@ package Test2::Event::Waiting;
 use strict;
 use warnings;
 
-our $VERSION = '1.302122';
+our $VERSION = '1.302133';
 
 
 BEGIN { require Test2::Event; our @ISA = qw(Test2::Event) }
index 79087ff..619d369 100644 (file)
@@ -2,7 +2,7 @@ package Test2::EventFacet;
 use strict;
 use warnings;
 
-our $VERSION = '1.302122';
+our $VERSION = '1.302133';
 
 use Test2::Util::HashBase qw/-details/;
 use Carp qw/croak/;
index 9b8bf2c..fe1d30e 100644 (file)
@@ -2,10 +2,10 @@ package Test2::EventFacet::About;
 use strict;
 use warnings;
 
-our $VERSION = '1.302122';
+our $VERSION = '1.302133';
 
 BEGIN { require Test2::EventFacet; our @ISA = qw(Test2::EventFacet) }
-use Test2::Util::HashBase qw{ -package -no_display };
+use Test2::Util::HashBase qw{ -package -no_display -uuid };
 
 1;
 
@@ -45,6 +45,12 @@ Event package name.
 
 True if the event should be skipped by formatters.
 
+=item $uuid = $about->{uuid}
+
+=item $uuid = $about->uuid()
+
+Will be set to a uuid if uuid tagging was enabled.
+
 =back
 
 =head1 SOURCE
index 29de90f..9ef227f 100644 (file)
@@ -2,7 +2,7 @@ package Test2::EventFacet::Amnesty;
 use strict;
 use warnings;
 
-our $VERSION = '1.302122';
+our $VERSION = '1.302133';
 
 sub is_list { 1 }
 
index 772093d..090860a 100644 (file)
@@ -2,7 +2,7 @@ package Test2::EventFacet::Assert;
 use strict;
 use warnings;
 
-our $VERSION = '1.302122';
+our $VERSION = '1.302133';
 
 BEGIN { require Test2::EventFacet; our @ISA = qw(Test2::EventFacet) }
 use Test2::Util::HashBase qw{ -pass -no_debug -number };
index 41a5cbf..b52718d 100644 (file)
@@ -2,7 +2,7 @@ package Test2::EventFacet::Control;
 use strict;
 use warnings;
 
-our $VERSION = '1.302122';
+our $VERSION = '1.302133';
 
 BEGIN { require Test2::EventFacet; our @ISA = qw(Test2::EventFacet) }
 use Test2::Util::HashBase qw{ -global -terminate -halt -has_callback -encoding };
index 71c51e5..306dc31 100644 (file)
@@ -2,7 +2,7 @@ package Test2::EventFacet::Error;
 use strict;
 use warnings;
 
-our $VERSION = '1.302122';
+our $VERSION = '1.302133';
 
 sub facet_key { 'errors' }
 sub is_list { 1 }
diff --git a/cpan/Test-Simple/lib/Test2/EventFacet/Hub.pm b/cpan/Test-Simple/lib/Test2/EventFacet/Hub.pm
new file mode 100644 (file)
index 0000000..03c5553
--- /dev/null
@@ -0,0 +1,109 @@
+package Test2::EventFacet::Hub;
+use strict;
+use warnings;
+
+our $VERSION = '1.302133';
+
+sub is_list { 1 }
+sub facet_key { 'hubs' }
+
+BEGIN { require Test2::EventFacet; our @ISA = qw(Test2::EventFacet) }
+use Test2::Util::HashBase qw{-pid -tid -hid -nested -buffered -uuid -ipc};
+
+1;
+
+__END__
+
+=pod
+
+=encoding UTF-8
+
+=head1 NAME
+
+Test2::EventFacet::Hub
+
+=head1 DESCRIPTION
+
+These are a record of the hubs an event passes through. Most recent hub is the
+first one in the list.
+
+=head1 FACET FIELDS
+
+=over 4
+
+=item $string = $trace->{details}
+
+=item $string = $trace->details()
+
+The hub class or subclass
+
+=item $int = $trace->{pid}
+
+=item $int = $trace->pid()
+
+PID of the hub this event was sent to.
+
+=item $int = $trace->{tid}
+
+=item $int = $trace->tid()
+
+The thread ID of the hub the event was sent to.
+
+=item $hid = $trace->{hid}
+
+=item $hid = $trace->hid()
+
+The ID of the hub that the event was send to.
+
+=item $huuid = $trace->{huuid}
+
+=item $huuid = $trace->huuid()
+
+The UUID of the hub that the event was sent to.
+
+=item $int = $trace->{nested}
+
+=item $int = $trace->nested()
+
+How deeply nested the hub was.
+
+=item $bool = $trace->{buffered}
+
+=item $bool = $trace->buffered()
+
+True if the event was buffered and not sent to the formatter independent of a
+parent (This should never be set when nested is C<0> or C<undef>).
+
+=back
+
+=head1 SOURCE
+
+The source code repository for Test2 can be found at
+F<http://github.com/Test-More/test-more/>.
+
+=head1 MAINTAINERS
+
+=over 4
+
+=item Chad Granum E<lt>exodist@cpan.orgE<gt>
+
+=back
+
+=head1 AUTHORS
+
+=over 4
+
+=item Chad Granum E<lt>exodist@cpan.orgE<gt>
+
+=back
+
+=head1 COPYRIGHT
+
+Copyright 2018 Chad Granum E<lt>exodist@cpan.orgE<gt>.
+
+This program is free software; you can redistribute it and/or
+modify it under the same terms as Perl itself.
+
+See F<http://dev.perl.org/licenses/>
+
+=cut
index f032d78..805cab1 100644 (file)
@@ -2,7 +2,7 @@ package Test2::EventFacet::Info;
 use strict;
 use warnings;
 
-our $VERSION = '1.302122';
+our $VERSION = '1.302133';
 
 sub is_list { 1 }
 
index b1fffdc..31a4e0c 100644 (file)
@@ -2,7 +2,7 @@ package Test2::EventFacet::Meta;
 use strict;
 use warnings;
 
-our $VERSION = '1.302122';
+our $VERSION = '1.302133';
 
 BEGIN { require Test2::EventFacet; our @ISA = qw(Test2::EventFacet) }
 use vars qw/$AUTOLOAD/;
index 142d24a..baf66e9 100644 (file)
@@ -2,7 +2,7 @@ package Test2::EventFacet::Parent;
 use strict;
 use warnings;
 
-our $VERSION = '1.302122';
+our $VERSION = '1.302133';
 
 use Carp qw/confess/;
 
index 3d55d99..3789bcb 100644 (file)
@@ -2,7 +2,7 @@ package Test2::EventFacet::Plan;
 use strict;
 use warnings;
 
-our $VERSION = '1.302122';
+our $VERSION = '1.302133';
 
 BEGIN { require Test2::EventFacet; our @ISA = qw(Test2::EventFacet) }
 use Test2::Util::HashBase qw{ -count -skip -none };
index b5255bc..709a9f6 100644 (file)
@@ -2,7 +2,7 @@ package Test2::EventFacet::Render;
 use strict;
 use warnings;
 
-our $VERSION = '1.302122';
+our $VERSION = '1.302133';
 
 sub is_list { 1 }
 
index 9791c37..280258c 100644 (file)
@@ -2,14 +2,14 @@ package Test2::EventFacet::Trace;
 use strict;
 use warnings;
 
-our $VERSION = '1.302122';
+our $VERSION = '1.302133';
 
 BEGIN { require Test2::EventFacet; our @ISA = qw(Test2::EventFacet) }
 
 use Test2::Util qw/get_tid pkg_to_file/;
 use Carp qw/confess/;
 
-use Test2::Util::HashBase qw{^frame ^pid ^tid ^cid -hid -nested details -buffered};
+use Test2::Util::HashBase qw{^frame ^pid ^tid ^cid -hid -nested details -buffered -uuid -huuid};
 
 {
     no warnings 'once';
@@ -134,12 +134,41 @@ The thread ID in which the event was generated.
 
 The ID of the context that was used to create the event.
 
+=item $uuid = $trace->{uuid}
+
+=item $uuid = $trace->uuid()
+
+The UUID of the context that was used to create the event. (If uuid tagging was
+enabled)
+
+=back
+
+=head2 DISCOURAGED HUB RELATED FIELDS
+
+These fields were not always set properly by tools. These are B<MOSTLY>
+deprecated by the L<Test2::EventFacet::Hub> facets. These fields are not
+required, and may only reflect the hub that was current when the event was
+created, which is not necessarily the same as the hub the event was sent
+through.
+
+Some tools did do a good job setting these to the correct hub, but you cannot
+always rely on that. Use the 'hubs' facet list instead.
+
+=over 4
+
 =item $hid = $trace->{hid}
 
 =item $hid = $trace->hid()
 
 The ID of the hub that was current when the event was created.
 
+=item $huuid = $trace->{huuid}
+
+=item $huuid = $trace->huuid()
+
+The UUID of the hub that was current when the event was created. (If uuid
+tagging was enabled).
+
 =item $int = $trace->{nested}
 
 =item $int = $trace->nested()
@@ -210,9 +239,11 @@ Get the debug-info subroutine name.
 =item $sig = trace->signature
 
 Get a signature string that identifies this trace. This is used to check if
-multiple events are related. The Trace includes pid, tid, file, line number,
-and the cid which is C<'C\d+'> for traces created by a context, or C<'T\d+'>
-for traces created by C<new()>.
+multiple events are related.
+
+If UUID's are enabled then a uuid is returned. Otherwise the signature includes
+pid, tid, file, line number, and the cid which is C<'C\d+'> for traces created
+by a context, or C<'T\d+'> for traces created by C<new()>.
 
 =back
 
index 74e4bba..0f179e5 100644 (file)
@@ -2,7 +2,7 @@ package Test2::Formatter;
 use strict;
 use warnings;
 
-our $VERSION = '1.302122';
+our $VERSION = '1.302133';
 
 
 my %ADDED;
index bed1be1..2082de7 100644 (file)
@@ -2,7 +2,7 @@ package Test2::Formatter::TAP;
 use strict;
 use warnings;
 
-our $VERSION = '1.302122';
+our $VERSION = '1.302133';
 
 use Test2::Util qw/clone_io/;
 
index 04c0c5e..6be1c8d 100644 (file)
@@ -2,7 +2,7 @@ package Test2::Hub;
 use strict;
 use warnings;
 
-our $VERSION = '1.302122';
+our $VERSION = '1.302133';
 
 
 use Carp qw/carp croak confess/;
@@ -25,6 +25,7 @@ use Test2::Util::HashBase qw{
     _context_init
     _context_release
 
+    uuid
     active
     count
     failed
@@ -35,6 +36,8 @@ use Test2::Util::HashBase qw{
     skip_reason
 };
 
+my $UUID_VIA;
+
 my $ID_POSTFIX = 1;
 sub init {
     my $self = shift;
@@ -43,6 +46,9 @@ sub init {
     $self->{+TID} = get_tid();
     $self->{+HID} = join ipc_separator, $self->{+PID}, $self->{+TID}, $ID_POSTFIX++;
 
+    $UUID_VIA ||= Test2::API::_add_uuid_via_ref();
+    $self->{+UUID} = ${$UUID_VIA}->('hub') if $$UUID_VIA;
+
     $self->{+NESTED}   = 0 unless defined $self->{+NESTED};
     $self->{+BUFFERED} = 0 unless defined $self->{+BUFFERED};
 
@@ -272,6 +278,23 @@ sub send {
     my $self = shift;
     my ($e) = @_;
 
+    $e->add_hub(
+        {
+            details => ref($self),
+
+            buffered => $self->{+BUFFERED},
+            hid      => $self->{+HID},
+            nested   => $self->{+NESTED},
+            pid      => $self->{+PID},
+            tid      => $self->{+TID},
+            uuid     => $self->{+UUID},
+
+            ipc => $self->{+IPC} ? 1 : 0,
+        }
+    );
+
+    $e->set_uuid(${$UUID_VIA}->('event')) if $$UUID_VIA;
+
     if ($self->{+_PRE_FILTERS}) {
         for (@{$self->{+_PRE_FILTERS}}) {
             $e = $_->{code}->($self, $e);
@@ -775,6 +798,10 @@ Get the thread id under which the hub was created.
 
 Get the identifier string of the hub.
 
+=item $uuid = $hub->uuid()
+
+If UUID tagging is enabled (see L<Test2::API>) then the hub will have a UUID.
+
 =item $ipc = $hub->ipc()
 
 Get the IPC object used by the hub.
index c708ca8..b7af4cf 100644 (file)
@@ -2,7 +2,7 @@ package Test2::Hub::Interceptor;
 use strict;
 use warnings;
 
-our $VERSION = '1.302122';
+our $VERSION = '1.302133';
 
 
 use Test2::Hub::Interceptor::Terminator();
@@ -12,7 +12,7 @@ use Test2::Util::HashBase;
 
 sub init {
     my $self = shift;
-    $self->SUPER::init;
+    $self->SUPER::init();
     $self->{+NESTED} = 0;
 }
 
index 378fada..76ad94a 100644 (file)
@@ -2,7 +2,7 @@ package Test2::Hub::Interceptor::Terminator;
 use strict;
 use warnings;
 
-our $VERSION = '1.302122';
+our $VERSION = '1.302133';
 
 
 1;
index 8be5f64..c36a19e 100644 (file)
@@ -2,7 +2,7 @@ package Test2::Hub::Subtest;
 use strict;
 use warnings;
 
-our $VERSION = '1.302122';
+our $VERSION = '1.302133';
 
 BEGIN { require Test2::Hub; our @ISA = qw(Test2::Hub) }
 use Test2::Util::HashBase qw/nested exit_code manual_skip_all/;
index fa98b35..cfee461 100644 (file)
@@ -2,7 +2,7 @@ package Test2::IPC;
 use strict;
 use warnings;
 
-our $VERSION = '1.302122';
+our $VERSION = '1.302133';
 
 
 use Test2::API::Instance;
@@ -23,9 +23,12 @@ use Carp qw/confess/;
 our @EXPORT_OK = qw/cull/;
 BEGIN { require Exporter; our @ISA = qw(Exporter) }
 
+sub unimport { Test2::API::test2_ipc_disable() }
+
 sub import {
     goto &Exporter::import if test2_has_ipc || !test2_init_done();
 
+    confess "IPC is disabled" if Test2::API::test2_ipc_disabled();
     confess "Cannot add IPC in a child process (" . test2_pid() . " vs $$)" if test2_pid() != $$;
     confess "Cannot add IPC in a child thread (" . test2_tid() . " vs " . get_tid() . ")"  if test2_tid() != get_tid();
 
@@ -96,6 +99,11 @@ You should C<use Test2::IPC;> as early as possible in your test file. If you
 import this module after API initialization it will attempt to retrofit IPC
 onto the existing hubs.
 
+=head2 DISABLING IT
+
+You can use C<no Test2::IPC;> to disable IPC for good. You can also use the
+T2_NO_IPC env var.
+
 =head1 EXPORTS
 
 All exports are optional.
index a19f711..a4d7255 100644 (file)
@@ -2,7 +2,7 @@ package Test2::IPC::Driver;
 use strict;
 use warnings;
 
-our $VERSION = '1.302122';
+our $VERSION = '1.302133';
 
 
 use Carp qw/confess/;
index b2e5cf8..1d37a83 100644 (file)
@@ -2,7 +2,7 @@ package Test2::IPC::Driver::Files;
 use strict;
 use warnings;
 
-our $VERSION = '1.302122';
+our $VERSION = '1.302133';
 
 
 BEGIN { require Test2::IPC::Driver; our @ISA = qw(Test2::IPC::Driver) }
index 7560607..93c3df0 100644 (file)
@@ -16,7 +16,7 @@ use Test2::API qw/context run_subtest test2_stack/;
 use Test2::Hub::Interceptor();
 use Test2::Hub::Interceptor::Terminator();
 
-our $VERSION = '1.302122';
+our $VERSION = '1.302133';
 
 BEGIN { require Exporter; our @ISA = qw(Exporter) }
 our @EXPORT = qw{
index d06ca4a..6de008b 100644 (file)
@@ -2,7 +2,7 @@ package Test2::Util;
 use strict;
 use warnings;
 
-our $VERSION = '1.302122';
+our $VERSION = '1.302133';
 
 use POSIX();
 use Config qw/%Config/;
index 835fbd0..f03422c 100644 (file)
@@ -2,7 +2,7 @@ package Test2::Util::ExternalMeta;
 use strict;
 use warnings;
 
-our $VERSION = '1.302122';
+our $VERSION = '1.302133';
 
 
 use Carp qw/croak/;
index ee84209..db74510 100644 (file)
@@ -2,7 +2,7 @@ package Test2::Util::Facets2Legacy;
 use strict;
 use warnings;
 
-our $VERSION = '1.302122';
+our $VERSION = '1.302133';
 
 use Carp qw/croak confess/;
 use Scalar::Util qw/blessed/;
@@ -18,6 +18,7 @@ our @EXPORT_OK = qw{
     subtest_id
     summary
     terminate
+    uuid
 };
 our %EXPORT_TAGS = ( ALL => \@EXPORT_OK );
 
@@ -111,4 +112,188 @@ sub terminate {
     return $facet_data->{control}->{terminate};
 }
 
+sub uuid {
+    my $in = shift;
+
+    if ($CYCLE_DETECT) {
+        if (blessed($in) && $in->isa('Test2::Event')) {
+            my $meth = $in->can('uuid');
+            $meth = $in->can('SUPER::uuid') if $meth == \&uuid;
+            my $uuid = $in->$meth if $meth && $meth != \&uuid;
+            return $uuid if $uuid;
+        }
+
+        return undef;
+    }
+
+    my $facet_data = _get_facet_data($in);
+    return $facet_data->{about}->{uuid} if $facet_data->{about} && $facet_data->{about}->{uuid};
+
+    return undef;
+}
+
 1;
+
+=pod
+
+=encoding UTF-8
+
+=head1 NAME
+
+Test2::Util::Facets2Legacy - Convert facet data to the legacy event API.
+
+=head1 DESCRIPTION
+
+This module exports several subroutines from the older event API (see
+L<Test2::Event>). These subroutines can be used as methods on any object that
+provides a custom C<facet_data()> method. These subroutines can also be used as
+functions that take a facet data hashref as arguments.
+
+=head1 SYNOPSIS
+
+=head2 AS METHODS
+
+    package My::Event;
+
+    use Test2::Util::Facets2Legacy ':ALL';
+
+    sub facet_data { return { ... } }
+
+Then to use it:
+
+    my $e = My::Event->new(...);
+
+    my $causes_fail = $e->causes_fail;
+    my $summary     = $e->summary;
+    ....
+
+=head2 AS FUNCTIONS
+
+    use Test2::Util::Facets2Legacy ':ALL';
+
+    my $f = {
+        assert => { ... },
+        info => [{...}, ...],
+        control => {...},
+        ...
+    };
+
+    my $causes_fail = causes_fail($f);
+    my $summary     = summary($f);
+
+=head1 NOTE ON CYCLES
+
+When used as methods, all these subroutines call C<< $e->facet_data() >>. The
+default C<facet_data()> method in L<Test2::Event> relies on the legacy methods
+this module emulates in order to work. As a result of this it is very easy to
+create infinite recursion bugs.
+
+These methods have cycle detection and will throw an exception early if a cycle
+is detected. C<uuid()> is currently the only subroutine in this library that
+has a fallback behavior when cycles are detected.
+
+=head1 EXPORTS
+
+Nothing is exported by default. You must specify which methods to import, or
+use the ':ALL' tag.
+
+=over 4
+
+=item $bool = $e->causes_fail()
+
+=item $bool = causes_fail($f)
+
+Check if the event or facets result in a failing state.
+
+=item $bool = $e->diagnostics()
+
+=item $bool = diagnostics($f)
+
+Check if the event or facets contain any diagnostics information.
+
+=item $bool = $e->global()
+
+=item $bool = global($f)
+
+Check if the event or facets need to be globally processed.
+
+=item $bool = $e->increments_count()
+
+=item $bool = increments_count($f)
+
+Check if the event or facets make an assertion.
+
+=item $bool = $e->no_display()
+
+=item $bool = no_display($f)
+
+Check if the event or facets should be rendered or hidden.
+
+=item ($max, $directive, $reason) = $e->sets_plan()
+
+=item ($max, $directive, $reason) = sets_plan($f)
+
+Check if the event or facets set a plan, and return the plan details.
+
+=item $id = $e->subtest_id()
+
+=item $id = subtest_id($f)
+
+Get the subtest id, if any.
+
+=item $string = $e->summary()
+
+=item $string = summary($f)
+
+Get the summary of the event or facets hash, if any.
+
+=item $undef_or_int = $e->terminate()
+
+=item $undef_or_int = terminate($f)
+
+Check if the event or facets should result in process termination, if so the
+exit code is returned (which could be 0). undef is returned if no termination
+is requested.
+
+=item $uuid = $e->uuid()
+
+=item $uuid = uuid($f)
+
+Get the UUID of the facets or event.
+
+B<Note:> This will fall back to C<< $e->SUPER::uuid() >> if a cycle is
+detected and an event is used as the argument.
+
+=back
+
+=head1 SOURCE
+
+The source code repository for Test2 can be found at
+F<http://github.com/Test-More/test-more/>.
+
+=head1 MAINTAINERS
+
+=over 4
+
+=item Chad Granum E<lt>exodist@cpan.orgE<gt>
+
+=back
+
+=head1 AUTHORS
+
+=over 4
+
+=item Chad Granum E<lt>exodist@cpan.orgE<gt>
+
+=back
+
+=head1 COPYRIGHT
+
+Copyright 2018 Chad Granum E<lt>exodist@cpan.orgE<gt>.
+
+This program is free software; you can redistribute it and/or
+modify it under the same terms as Perl itself.
+
+See F<http://dev.perl.org/licenses/>
+
+=cut
index 09bf8bf..8e73af4 100644 (file)
@@ -2,7 +2,7 @@ package Test2::Util::HashBase;
 use strict;
 use warnings;
 
-our $VERSION = '1.302122';
+our $VERSION = '1.302133';
 
 #################################################################
 #                                                               #
index ef1b345..1e5351f 100644 (file)
@@ -2,7 +2,7 @@ package Test2::Util::Trace;
 require Test2::EventFacet::Trace;
 @ISA = ('Test2::EventFacet::Trace');
 
-our $VERSION = '1.302122';
+our $VERSION = '1.302133';
 
 1;
 
index 30033be..19f0f08 100644 (file)
@@ -1,5 +1,5 @@
 package ok;
-our $VERSION = '1.302122';
+our $VERSION = '1.302133';
 
 use strict;
 use Test::More ();
diff --git a/cpan/Test-Simple/t/Test2/behavior/disable_ipc_a.t b/cpan/Test-Simple/t/Test2/behavior/disable_ipc_a.t
new file mode 100644 (file)
index 0000000..de8bf83
--- /dev/null
@@ -0,0 +1,11 @@
+use strict;
+use warnings;
+
+no Test2::IPC;
+use Test2::Tools::Tiny;
+use Test2::IPC::Driver::Files;
+
+ok(Test2::API::test2_ipc_disabled, "disabled IPC");
+ok(!Test2::API::test2_ipc, "No IPC");
+
+done_testing;
diff --git a/cpan/Test-Simple/t/Test2/behavior/disable_ipc_b.t b/cpan/Test-Simple/t/Test2/behavior/disable_ipc_b.t
new file mode 100644 (file)
index 0000000..e0605ba
--- /dev/null
@@ -0,0 +1,11 @@
+use strict;
+use warnings;
+
+BEGIN { $ENV{T2_NO_IPC} = 1 };
+use Test2::Tools::Tiny;
+use Test2::IPC::Driver::Files;
+
+ok(Test2::API::test2_ipc_disabled, "disabled IPC");
+ok(!Test2::API::test2_ipc, "No IPC");
+
+done_testing;
diff --git a/cpan/Test-Simple/t/Test2/behavior/disable_ipc_c.t b/cpan/Test-Simple/t/Test2/behavior/disable_ipc_c.t
new file mode 100644 (file)
index 0000000..859b025
--- /dev/null
@@ -0,0 +1,12 @@
+use strict;
+use warnings;
+
+use Test2::Tools::Tiny;
+use Test2::API qw/test2_ipc_disable/;
+BEGIN { test2_ipc_disable() }
+use Test2::IPC::Driver::Files;
+
+ok(Test2::API::test2_ipc_disabled, "disabled IPC");
+ok(!Test2::API::test2_ipc, "No IPC");
+
+done_testing;
diff --git a/cpan/Test-Simple/t/Test2/behavior/disable_ipc_d.t b/cpan/Test-Simple/t/Test2/behavior/disable_ipc_d.t
new file mode 100644 (file)
index 0000000..b9698f2
--- /dev/null
@@ -0,0 +1,27 @@
+use strict;
+use warnings;
+
+use Test2::Util qw/CAN_THREAD/;
+use Test2::API qw/context/;
+
+BEGIN {
+    sub plan {
+        my $ctx = context();
+        $ctx->plan(@_);
+        $ctx->release;
+    }
+
+    unless (CAN_THREAD()) {
+        plan(0, skip_all => 'System does not have threads');
+        exit 0;
+    }
+}
+
+use threads;
+no Test2::IPC;
+use Test::More;
+
+ok(Test2::API::test2_ipc_disabled, "disabled IPC");
+ok(!Test2::API::test2_ipc, "No IPC");
+
+done_testing;
diff --git a/cpan/Test-Simple/t/Test2/behavior/uuid.t b/cpan/Test-Simple/t/Test2/behavior/uuid.t
new file mode 100644 (file)
index 0000000..c8fabf8
--- /dev/null
@@ -0,0 +1,78 @@
+use Test2::Tools::Tiny;
+use Test2::API qw/test2_add_uuid_via context intercept/;
+
+my %CNT;
+test2_add_uuid_via(sub {
+    my $type = shift;
+    $CNT{$type} ||= 1;
+    $type . '-' . $CNT{$type}++;
+});
+
+my $events = intercept {
+    ok(1, "pass");
+
+    sub {
+        my $ctx = context();
+        ok(1, "pass");
+        ok(0, "fail");
+        $ctx->release;
+    }->();
+
+    tests foo => sub {
+        ok(1, "pass");
+    };
+
+    warnings {
+        require Test::More;
+        *subtest = \&Test::More::subtest;
+    };
+
+    subtest(foo => sub {
+        ok(1, "pass");
+    });
+};
+
+my $hub = Test2::API::test2_stack->top;
+is($hub->uuid, 'hub-1', "First hub got a uuid");
+
+is($events->[0]->uuid, 'event-1', "First event gets first uuid");
+is($events->[0]->trace->uuid, 'context-2', "First event has correct context");
+is($events->[0]->trace->huuid, 'hub-2', "First event has correct hub");
+
+is($events->[0]->facet_data->{about}->{uuid}, "event-1", "The UUID makes it to facet data");
+
+is($events->[1]->uuid, 'event-2', "Second event gets correct uuid");
+is($events->[1]->trace->uuid, 'context-3', "Second event has correct context");
+is($events->[1]->trace->huuid, 'hub-2', "Second event has correct hub");
+
+is($events->[2]->uuid, 'event-3', "Third event gets correct uuid");
+is($events->[2]->trace->uuid, $events->[1]->trace->uuid, "Third event shares context with event 2");
+is($events->[2]->trace->huuid, 'hub-2', "Third event has correct hub");
+
+is($events->[3]->uuid, 'event-6', "subtest event gets correct uuid (not next)");
+is($events->[3]->subtest_uuid, 'hub-3', "subtest event gets correct subtest-uuid (next hub uuid)");
+is($events->[3]->trace->uuid, 'context-4', "subtest gets next sequential context");
+is($events->[3]->trace->huuid, 'hub-2', "subtest event has correct hub");
+
+is($events->[3]->subevents->[0]->uuid, 'event-4', "First subevent gets next event uuid");
+is($events->[3]->subevents->[0]->trace->uuid, 'context-5', "First subevent has correct context");
+is($events->[3]->subevents->[0]->trace->huuid, 'hub-3', "First subevent has correct hub uuid (subtest hub uuid)");
+
+is($events->[3]->subevents->[1]->uuid, 'event-5', "Second subevent gets next event uuid");
+is($events->[3]->subevents->[1]->trace->uuid, $events->[3]->trace->uuid, "Second subevent has same context as subtest itself");
+is($events->[3]->subevents->[1]->trace->huuid, 'hub-3', "Second subevent has correct hub uuid (subtest hub uuid)");
+
+is($events->[5]->uuid, 'event-10', "subtest event gets correct uuid (not next)");
+is($events->[5]->subtest_uuid, 'hub-4', "subtest event gets correct subtest-uuid (next hub uuid)");
+is($events->[5]->trace->uuid, 'context-8', "subtest gets next sequential context");
+is($events->[5]->trace->huuid, 'hub-2', "subtest event has correct hub");
+
+is($events->[5]->subevents->[0]->uuid, 'event-8', "First subevent gets next event uuid");
+is($events->[5]->subevents->[0]->trace->uuid, 'context-10', "First subevent has correct context");
+is($events->[5]->subevents->[0]->trace->huuid, 'hub-4', "First subevent has correct hub uuid (subtest hub uuid)");
+
+is($events->[5]->subevents->[1]->uuid, 'event-9', "Second subevent gets next event uuid");
+is($events->[5]->subevents->[1]->trace->uuid, $events->[5]->trace->uuid, "Second subevent has same context as subtest itself");
+is($events->[5]->subevents->[1]->trace->huuid, 'hub-2', "Second subevent has correct hub uuid (subtest hub uuid)");
+
+done_testing;
index d7a8953..a804cac 100644 (file)
@@ -37,6 +37,8 @@ ok(Test2::API->can($_), "$_ method is present") for qw{
     test2_list_post_load_callbacks
 
     test2_ipc
+    test2_ipc_disable
+    test2_ipc_disabled
     test2_ipc_drivers
     test2_ipc_add_driver
     test2_ipc_polling
index 8e7e9a8..4238b1d 100644 (file)
@@ -7,6 +7,8 @@ use Test2::Util qw/CAN_THREAD CAN_REALLY_FORK USE_THREADS get_tid/;
 
 ok(1, "Just to get things initialized.");
 
+# We need to control this env var for this test
+$ENV{T2_NO_IPC} = 0;
 # This test relies on TAP being the default formatter for non-canon instances
 $ENV{T2_FORMATTER} = 'TAP';
 
@@ -22,9 +24,12 @@ is_deeply(
         ipc       => undef,
         formatter => undef,
 
-        ipc_polling => undef,
-        ipc_drivers => [],
-        ipc_timeout => 30,
+        add_uuid_via => undef,
+
+        ipc_polling    => undef,
+        ipc_drivers    => [],
+        ipc_timeout    => 30,
+        ipc_disabled   => 0,
 
         formatters => [],
 
@@ -52,9 +57,12 @@ is_deeply(
     {
         contexts => {},
 
-        ipc_polling => undef,
-        ipc_drivers => [],
-        ipc_timeout => 30,
+        ipc_polling  => undef,
+        ipc_drivers  => [],
+        ipc_timeout  => 30,
+        ipc_disabled => 0,
+
+        add_uuid_via => undef,
 
         formatters => [],
 
@@ -501,4 +509,29 @@ if (CAN_REALLY_FORK) {
     is($cull, 1, "called cull once");
 }
 
+{
+    require Test2::IPC::Driver::Files;
+
+    local $ENV{T2_NO_IPC} = 1;
+    $one->reset;
+    $one->add_ipc_driver('Test2::IPC::Driver::Files');
+    ok($one->ipc_disabled, "IPC is disabled by env var");
+    ok(!$one->ipc, 'IPC not loaded');
+
+    local $ENV{T2_NO_IPC} = 0;
+    $one->reset;
+    ok(!$one->ipc_disabled, "IPC is not disabled by env var");
+    ok($one->ipc, 'IPC loaded');
+    like(
+        exception { $one->ipc_disable },
+        qr/Attempt to disable IPC after it has been initialized/,
+        "Cannot diable IPC once it is initialized"
+    );
+
+    $one->reset;
+    ok(!$one->ipc_disabled, "IPC is not disabled by env var");
+    $one->ipc_disable;
+    ok($one->ipc_disabled, "IPC is disabled directly");
+}
+
 done_testing;
index 8eef5fc..6a414f7 100644 (file)
@@ -5,6 +5,8 @@ use Test2::Tools::Tiny;
 use Test2::Event();
 use Test2::EventFacet::Trace();
 use Test2::Event::Generic;
+
+use Test2::API qw/context/;
 use Scalar::Util qw/reftype/;
 
 tests old_api => sub {
@@ -636,4 +638,77 @@ tests common_facet_data => sub {
     );
 };
 
+tests related => sub {
+    my $ctx = context();
+    my $ev_a = $ctx->build_ev2(about => {});
+    my $ev_b = $ctx->build_ev2(about => {});
+    $ctx->release;
+
+    $ctx = context();
+    my $ev_c = $ctx->build_ev2(about => {});
+    $ctx->release;
+
+    delete $ev_a->{trace}->{uuid};
+    delete $ev_b->{trace}->{uuid};
+    delete $ev_c->{trace}->{uuid};
+
+    ok($ev_a->related($ev_b), "Related as they were created with the same context (no uuid)");
+    ok(!$ev_a->related($ev_c), "Not related as they were created with a different context (no uuid)");
+
+    $ev_a->{trace}->{uuid} = 'xxx'; # Yes I know it is not valid.
+    $ev_b->{trace}->{uuid} = 'yyy'; # Yes I know it is not valid.
+    $ev_c->{trace}->{uuid} = 'xxx'; # Yes I know it is not valid.
+
+    ok(!$ev_a->related($ev_b), "Not related, traces have different UUID's");
+    ok($ev_a->related($ev_c), "Related, traces have the same UUID's");
+};
+
+tests verify_facet_data => sub {
+    my $ev1 = Test2::Event::V2->new(
+        assert => { pass => 1 },
+        info => [{tag => 'NOTE', details => 'oops' }],
+        'a custom one' => {},
+    );
+
+    is_deeply(
+        [$ev1->validate_facet_data],
+        [],
+        "No errors"
+    );
+
+    my $ev2 = Test2::Event::V2->new(
+        assert => [{ pass => 1 }],
+        info => {tag => 'NOTE', details => 'oops' },
+        'a custom one' => {},
+    );
+
+    my @errors = $ev2->validate_facet_data;
+    is(@errors, 2, "Got 2 errors");
+    like($errors[0], qr/^Facet 'assert' should not be a list, but got a a list/, "Got a list for a non-list type");
+    like($errors[1], qr/^Facet 'info' should be a list, but got a single item/, "Got a single item when a list is needed");
+
+    @errors = $ev2->validate_facet_data(require_facet_class => 1);
+    is(@errors, 3, "Got 3 errors");
+    is($errors[0], "Could not find a facet class for facet 'a custom one'", "Classes required");
+    like($errors[1], qr/^Facet 'assert' should not be a list, but got a a list/, "Got a list for a non-list type");
+    like($errors[2], qr/^Facet 'info' should be a list, but got a single item/, "Got a single item when a list is needed");
+
+    is_deeply(
+        [Test2::Event->validate_facet_data($ev1->facet_data)],
+        [],
+        "No errors"
+    );
+
+    @errors = Test2::Event->validate_facet_data($ev2->facet_data);
+    is(@errors, 2, "Got 2 errors");
+    like($errors[0], qr/^Facet 'assert' should not be a list, but got a a list/, "Got a list for a non-list type");
+    like($errors[1], qr/^Facet 'info' should be a list, but got a single item/, "Got a single item when a list is needed");
+
+    @errors = Test2::Event->validate_facet_data($ev2->facet_data, require_facet_class => 1);
+    is(@errors, 3, "Got 3 errors");
+    is($errors[0], "Could not find a facet class for facet 'a custom one'", "Classes required");
+    like($errors[1], qr/^Facet 'assert' should not be a list, but got a a list/, "Got a list for a non-list type");
+    like($errors[2], qr/^Facet 'info' should be a list, but got a single item/, "Got a single item when a list is needed");
+};
+
 done_testing;
index a5ba4cb..10d57ec 100644 (file)
@@ -27,6 +27,18 @@ is_deeply(
         increments_count => 0,
         diagnostics      => 0,
         no_display       => 0,
+        hubs             => [
+            {
+                'buffered' => 0,
+                'details'  => 'Test2::Hub::Interceptor',
+                'hid'      => "$$~0~2",
+                'ipc'      => 0,
+                'nested'   => 0,
+                'pid'      => $$,
+                'tid'      => 0,
+                'uuid'     => undef
+            }
+        ],
     },
     "Defaults"
 );
diff --git a/cpan/Test-Simple/t/Test2/modules/Event/V2.t b/cpan/Test-Simple/t/Test2/modules/Event/V2.t
new file mode 100644 (file)
index 0000000..5214baf
--- /dev/null
@@ -0,0 +1,101 @@
+use strict;
+use warnings;
+use Test2::Tools::Tiny;
+
+use Test2::API qw/context intercept/;
+
+use Test2::Event::V2();
+
+my $CLASS = 'Test2::Event::V2';
+
+ok($CLASS->isa('Test2::Event'), "Subclass of Test2::Event");
+
+is_deeply(
+    [Test2::Event::V2->non_facet_keys],
+    ['uuid', '_meta'],
+    "Got non-facet keys"
+);
+
+ok($CLASS->can($_), "has method $_") for qw{
+    causes_fail diagnostics global increments_count no_display sets_plan
+    subtest_id summary terminate
+    uuid set_uuid
+    meta
+    facet_data
+    about
+};
+
+ok(!exception { $CLASS->new(uuid => 2, about => {uuid => 2}) }, "Can have matching uuids");
+
+like(
+    exception { $CLASS->new(uuid => 1, about => {uuid => 2}) },
+    qr/uuid '1' passed to constructor, but uuid '2' is already set in the 'about' facet/,
+    "Cannot have a uuid mismatch"
+);
+
+my $one = $CLASS->new(uuid => 123);
+is($one->about->{uuid}, 123, "Set uuid in about facet");
+
+$one = $CLASS->new(about => { uuid => 123 });
+is($one->uuid, 123, "set uuid attribute");
+
+my $trace = {frame => ['main', 'file.t', 42, 'foo'], tid => 0, pid => $$};
+$one = $CLASS->new(trace => $trace);
+ok($trace != $one->trace, "Did not keep or modify the original trace ref");
+ok($one->trace->isa('Test2::EventFacet::Trace'), "Blessed the trace");
+is_deeply($one->trace, $trace, "Trace has all data");
+
+$one = $CLASS->new;
+ok(!$one->uuid, "no uuid attribute");
+ok(!$one->about->{uuid}, "no uuid in about facet");
+$one->set_uuid(123);
+is($one->about->{uuid}, 123, "Set uuid in about facet");
+is($one->uuid, 123, "set uuid attribute");
+
+
+$one = $CLASS->new(
+    uuid => '123',
+    trace => $trace,
+    assert => {pass => 1, details => 'pass'},
+    info => [{tag => 'NOTE', details => 'a note'}],
+);
+
+$one->set_meta('foo' => {'xyz' => 1});
+
+$one->{_custom_sttr} = 'xxx';
+
+is_deeply(
+    $one->facet_data,
+    {
+        trace  => $trace,
+        assert => {pass => 1, details => 'pass'},
+        info   => [{tag => 'NOTE', details => 'a note'}],
+        meta  => {foo  => {'xyz' => 1}},
+        about => {uuid => 123},
+    },
+    "Facet data has everything we want, and nothing we do not"
+);
+
+sub my_tool {
+    my $ctx = context();
+
+    my $event = $ctx->send_ev2(info => [{tag => 'NOTE', details => "This is a note"}]);
+
+    $ctx->release;
+
+    return $event;
+}
+
+my $events = intercept {
+    my_tool();
+};
+
+is(@$events, 1, "Got 1 event");
+ok($events->[0]->isa($CLASS), "Created the right type of event");
+is_deeply(
+    $events->[0]->facet_data->{info},
+    [{tag => 'NOTE', details => "This is a note"}],
+    "Got the specified info facet"
+);
+
+done_testing;
index 46bbd54..50e1497 100644 (file)
@@ -121,11 +121,8 @@ tests IPC => sub {
         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)"
-        );
+        is(@$old, 3, "Formatter got all events ($name)");
+        ok($_->{hubs}, "Set the hubs") for @$old;
     };
 
     if (CAN_REALLY_FORK) {