TEST2::MANUAL::ANATOMY::EVENT(3perl)
π Test2::Manual::Anatomy::Event β the internals of events β¨
okay bestie, buckle up, we are going deep into the Test2 event guts. this is the Test2::Manual::Anatomy::Event page from the Perl Programmers Reference Guide and it's lowkey a whole personality β¨
πΈ NAME πΈ
Test2::Manual::Anatomy::Event β the internals of events π
π«Ά DESCRIPTION π«Ά
so like, events? they're the way tools mess with global state, and how they pass info along to the harness, or to the actual human running the tests. that's the whole job. that's the assignment. π
π HISTORY π
before we go any further you need to know some event lore, fr fr.
so originally there was an event API, and an event would implement that API to produce an effect. and that API? it proved to be lossy and inflexible. not slay. π
recently the 'facet' system got introduced, and it makes up for the shortcomings and the inflexibility of the old API. iconic behaviour honestly.
and here's the thing: all events must still implement the old API, but that can be largely automated if you use the facet system effectively. likewise, essential facets can often be deduced from events that only implement the old API β though their information maybe less complete. so like, you can be old-school, you'll just be a little vague about it π
π¦ THE EVENT OBJECT π¦
all event objects must subclass Test2::Event. no exceptions, that's the law π
here's the deal: if you inherit from this base class and implement the old API properly, facets will be generated for you for free. free!!! π
on the other hand, you can inherit from this and also import Test2::Util::Facets2Legacy β which will instead rely on your facet data, and deduce the old API from them. so it works in both directions, cute right?
all new events "MUST" implement both APIs, one way or the other. a common way to do this is to simply implement both APIs directly in your event.
here is a good template for a new event, no cap:
package Test2::Event::Mine;
use strict;
use warnings;
use parent 'Test2::Event';
use Test2::Util::Facets2Legacy ':ALL';
sub facet_data {
my $self = shift;
# Adds 'about', 'amnesty', and 'trace' facets
my $out = $self->common_facet_data;
# Add any additional facets to the $out hashref
...
return $out;
}
1;
β¨ THE FACET API β¨
the new API is literally just one single method: facet_data(). that's it, that's the whole thing, she's simple π
this method must return a hashref where each key is specific to a facet type, and the value is either a facet hashref, or an array of hashrefs. some facets "MUST" be lone hashrefs, others "MUST" be hashrefs inside an arrayref. do not mix those up, bestie, the vibes will be off π
the standard facet types are as follows:
assertβ{details => $name, pass => $bool, no_debug => $bool, number => $maybe_int}- documented in
Test2::EventFacet::Assert. an event may only have one. - the
'details'key is the name of the assertion. - the
'pass'key denotes a passing or failing assertion. - the
'no_debug'key tells any harness or formatter that diagnostics should not be added automatically to a failing assertion (used when there are custom diagnostics instead). - the
'number'key is for harness use, never set it yourself.
- documented in
aboutβ{details => $string, no_display => $bool, package => $pkg}- documented in
Test2::EventFacet::About. an event may only have one. 'details'is a human readable string describing the overall event.'no_display'means that a formatter/harness should hide the event.'package'is the package of the event the facet describes (IE:Test2::Event::Ok)
- documented in
amnestyβ[{details => $string, tag => $short_string, inherited => $bool}]- documented in
Test2::EventFacet::Amnesty. an event may have multiple. - this event is how things like
'todo'are implemented. amnesty prevents a failing assertion from causing a global test failure. 'details'is a human readable description of why the failure is being granted amnesty (IE The'todo'reason)'tag'is a short human readable string, or category for the amnesty. this is typically'TODO'or'SKIP'.'inherited'is true if the amnesty was applied in a parent context (true if this test is run in a subtest that is marked todo).
- documented in
controlβ{details => $string, global => $bool, terminate => $maybe_int, halt => $bool, has_callback => $bool, encoding => $enc}- documented in
Test2::EventFacet::Control. an event may have one. - this facet is used to apply extra behavior when the event is processed.
'details'is a human readable explanation for the behavior.'global'true if this event should be forwarded to, and processed by, all hubs everywhere. (bail-out uses this)'terminate'this should either be undef, or an integer. when defined this will cause the test to exit with the specific exit code.'halt'is used to signal any harness that no further test files should be run (bail-out uses this).'has_callback'is set to true if the event has a callback sub defined.'encoding'used to tell the formatter what encoding to use.
- documented in
errorsβ[{details => $string, tag => $short_string, fail => $bool}]- documented in
Test2::EventFacet::Error. an event may have multiple. 'details'is a human readable explanation of the error.'tag'is a short human readable category for the error.'fail'is true if the error should cause test failure. if this is false the error is simply informative, but not fatal.
- documented in
infoβ[{details => $string, tag => $short_string, debug => $bool, important => $bool}]- documented in
Test2::EventFacet::Info. an event may have multiple. - this is how
diagandnoteare implemented. 'details'human readable message.'tag'short category for the message, such as'diag'or'note'.'debug'is true if the message is diagnostics in nature, this is the main difference between a note and a diag.'important'is true if the message is not diagnostics, but is important to have it shown anyway. this is primarily used to communicate with a harness.
- documented in
parentβ{details => $string, hid => $hid, children => [...], buffered => 1}- documented in
Test2::EventFacet::Parent. an event may have one. - this is used by subtests.
'details'human readable name of the subtest.'hid'subtest hub id.'children'an arrayref containingfacet_datainstances from all child events.'buffered'true if it was a buffered subtest.
- documented in
planβ{details => $string, count => $int, skip => $bool, none => $bool}- documented in
Test2::EventFacet::Plan. an event may have one. 'details'is a human readable string describing the plan (for instance, why a test is skipped)'count'is the number of expected assertions (0for skip)'skip'is true if the plan is to skip the test.'none'used forTest::More's'no_plan'plan.
- documented in
traceβ{details => $string, frame => [$pkg, $file, $line, $sub], pid => $int, tid => $int, cid => $cid, hid => $hid, nested => $int, buffered => $bool}- documented in
Test2::EventFacet::Trace. an event may have one. - this is how debugging information is tracked. this is taken from the context object at event creation.
'details'human readable debug message (otherwise generated fromframe)'frame'first 4 fields returned bycaller:"[$package, $file, $line, $subname]".'pid'the process id in which the event was created.'tid'the thread is in which the event was created.'cid'the id of the context used to create the event.'hid'the id of the hub to which the event was sent.'nest'subtest nesting depth of the event.'buffered'is true if the event was generated inside a buffered subtest.
- documented in
π note that ALL facet types have a 'details' key that may have a string. this string should always be human readable, and should be an explanation for the facet. for an assertion this is the test name. for a plan this is the reason for the plan (such as skip reason). for info it is the human readable diagnostics message.
π§ CUSTOM FACETS π§
you can write custom facet types too, bestie! just add a new key to the hash and populate it. the general rule is that any code looking at the facets should ignore any it does not understand. (yes, that means your weird little custom facet won't break anything, go off π )
optionally you can also create a package to document your custom facet. the package should be proper object, and may have additional methods to help work with your facet.
package Test2::EventFacet::MyFacet;
use parent 'Test2::EventFacet';
sub facet_key { 'myfacet' }
sub is_list { 0 }
1;
your facet package should always be under the Test2::EventFacet:: namespace if you want any tools to automatically find it. the last part of the namespace should be the non-plural name of your facet with only the first word capitalized.
$string = $facet_class->facet_keyβ the key for your facet should be the same as the last section of the namespace, but all lowercase. you may append's'to the key if your facet is a list type.$bool = $facet_class->is_listβ true if an event should put these facets in a list:
{ myfacet => [{}, {}] }
false if an event may only have one of this type of facet at a time:
{ myfacet => {} }
π EXAMPLES π
the assert facet is not a list type, so its implementation would look like this:
package Test2::EventFacet::Assert;
sub facet_key { 'assert' }
sub is_list { 0 }
the amnesty facet is a list type, but amnesty does not need 's' appended to make it plural:
package Test2::EventFacet::Amnesty;
sub facet_key { 'amnesty' }
sub is_list { 1 }
the error facet is a list type, and appending 's' makes error plural as errors. this means the package name is '::Error', but the key is 'errors'.
package Test2::EventFacet::Error;
sub facet_key { 'errors' }
sub is_list { 1 }
Note2: in practice most tools completely ignore the facet packages, and work with the facet data directly in its raw structure. this is by design and recommended. the facet data is intended to be serialized frequently and passed around. when facets are concerned, data is important, classes and methods are not. π
π THE OLD API π
okay so before the fancy facet thing existed, this is what we had: a lil set of methods you just had to implement, periodt. no options, no vibes, just do the thing. it's giving "legacy but iconic" π
$bool = $e->causes_failβ returns true if this event should make the test fail. bestie, in general this should be false. don't be out here causing chaos for no reason π$bool = $e->increments_countβ should be true if this event should bump the test count up. that's it, that's the whole job β¨$e->callback($hub)β if your event needs to do extra lil effects on theTest2::Hub, you can override this method. lowkey powerful.
β οΈ this gets called BEFORE your event is passed to the formatter. before!! not after!! remember that π«Ά
$num = $e->nestedβ if this event is nested inside other events, this should be the depth of nesting. (it's mainly for subtests, iykyk πͺ)$bool = $e->globalβ set this to true if your event is global, meaning ALL threads and processes should see it no matter when or where it gets generated. that's a lot of eyes on you π this is not a common thing to want β it's used by bail-out and skip_all to end testing.$code = $e->terminateβ this gets called AFTER your event has been passed to the formatter. it should normally returnundef. only change that if your event should cause the test to exit immediately. π
if you want this event to make the test exit, you return the exit code right here. exit code of 0 means exit success, any other integer means exit with failure. so like. choose wisely.
this is used by Test2::Event::Plan to exit 0 when the plan is 'skip_all'. it's also used by Test2::Event::Bail to force the test to exit with a failure.
it's called after the event has been sent to the formatter so that the event is seen and understood. no ghosting the formatter πΈ
$msg = $e->summaryβ this is meant to be a human readable summary of the event. ideally just one line long, but you can use multiple lines if you truly have to. it's for human consumption β you do not need to make it easy for machines to understand. speak your truth, bestie π
the default is to simply return the event package name.
($count, $directive, $reason) = $e->sets_plan()β checks if this event sets the testing plan. it returns an empty list if it does not. if it does set the plan, it returns a list of 1 to 3 items, in order: Expected Test Count, Test Directive, Reason for directive. very structured of it π§$bool = $e->diagnosticsβ true if the event contains diagnostics info. this is useful because a non-verbose harness may choose to hide events that aren't in this category. some formatters may choose to send these toSTDERRinstead ofSTDOUTso they actually get seen. loud and proud, no cap π₯$bool = $e->no_displayβ false by default. this returns true on events that should not be displayed by formatters. invisible mode activated π
π SEE ALSO
Test2::Manualβ the primary index of the manual. your map, your guide, your bestie πΊοΈβ¨
π SOURCE
the source code repository for Test2-Manual can be found at https://github.com/Test-More/test-more/. go peek, it's not creepy it's called research π
π MAINTAINERS
Chad Granum <exodist@cpan.org>
π AUTHORS
Chad Granum <exodist@cpan.org>
β COPYRIGHT
Copyright Chad Granum <exodist@cpan.org>.
This program is free software; you can redistribute it and/or modify it under the same terms as Perl itself.
See http://dev.perl.org/licenses/
and that's the whole page, bestie π¦ this version is perl v5.44.0, dated 2026-10-17, and it lives under Test2:...::Event(3perl) β go forth and write events, you absolute main character πβ¨
π¦ where this page comes from
- arch rolling: perl 5.44.0-1 Β· Artistic-1.0-Perl AND GPL-1.0-or-later
- π girlified by deepseek-ai/DeepSeek-V4.1-Flash on : a language model's rewrite of the original, under its terms