417 lines
13 KiB
Perl
417 lines
13 KiB
Perl
package Test2::Manual::Anatomy::Event;
|
|
use strict;
|
|
use warnings;
|
|
|
|
our $VERSION = '0.000139';
|
|
|
|
1;
|
|
|
|
__END__
|
|
|
|
=head1 NAME
|
|
|
|
Test2::Manual::Anatomy::Event - The internals of events
|
|
|
|
=head1 DESCRIPTION
|
|
|
|
Events are how tools effect global state, and pass information along to the
|
|
harness, or the human running the tests.
|
|
|
|
=head1 HISTORY
|
|
|
|
Before proceeding it is important that you know some history of events.
|
|
Initially there was an event API, and an event would implement the API to
|
|
produce an effect. This API proved to be lossy and inflexible. Recently the
|
|
'facet' system was introduced, and makes up for the shortcoming and
|
|
inflexibility of the old API.
|
|
|
|
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.
|
|
|
|
=head1 THE EVENT OBJECT
|
|
|
|
All event objects must subclass L<Test2::Event>. If you inherit from this base
|
|
class, and implement the old API properly, facets will be generated for you for
|
|
free. On the other hand you can inherit from this, and also import
|
|
L<Test2::Util::Facets2Legacy> which will instead rely on your facet data, and
|
|
deduce the old API from them.
|
|
|
|
All new events C<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:
|
|
|
|
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;
|
|
|
|
=head1 THE FACET API
|
|
|
|
The new API is a single method: C<facet_data()>. 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 C<MUST> be lone hashrefs,
|
|
others C<MUST> be hashrefs inside an arrayref.
|
|
|
|
The I<standard> facet types are as follows:
|
|
|
|
=over 4
|
|
|
|
=item assert => {details => $name, pass => $bool, no_debug => $bool, number => $maybe_int}
|
|
|
|
Documented in L<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.
|
|
|
|
=item about => {details => $string, no_display => $bool, package => $pkg}
|
|
|
|
Documented in L<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: L<Test2::Event::Ok>)
|
|
|
|
=item amnesty => [{details => $string, tag => $short_string, inherited => $bool}]
|
|
|
|
Documented in L<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).
|
|
|
|
=item control => {details => $string, global => $bool, terminate => $maybe_int, halt => $bool, has_callback => $bool, encoding => $enc}
|
|
|
|
Documented in L<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.
|
|
|
|
=item errors => [{details => $string, tag => $short_string, fail => $bool}]
|
|
|
|
Documented in L<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.
|
|
|
|
=item info => [{details => $string, tag => $short_string, debug => $bool, important => $bool}]
|
|
|
|
Documented in L<Test2::EventFacet::Info>. An event may have multiple.
|
|
|
|
This is how diag and note are 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.
|
|
|
|
=item parent => {details => $string, hid => $hid, children => [...], buffered => 1}
|
|
|
|
Documented in L<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 containing facet_data instances from all child events.
|
|
|
|
'buffered' true if it was a buffered subtest.
|
|
|
|
=item plan => {details => $string, count => $int, skip => $bool, none => $bool}
|
|
|
|
Documented in L<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 (0 for skip)
|
|
|
|
'skip' is true if the plan is to skip the test.
|
|
|
|
'none' used for Test::More's 'no_plan' plan.
|
|
|
|
=item trace => {details => $string, frame => [$pkg, $file, $line, $sub], pid => $int, tid => $int, cid => $cid, hid => $hid, nested => $int, buffered => $bool}
|
|
|
|
Documented in L<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 from frame)
|
|
|
|
'frame' first 4 fields returned by caller:
|
|
C<[$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.
|
|
|
|
=back
|
|
|
|
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.
|
|
|
|
=head2 CUSTOM FACETS
|
|
|
|
You can write custom facet types as well, simply add a new key to the hash and
|
|
populated it. The general rule is that any code looking at the facets should
|
|
ignore any it does not understand.
|
|
|
|
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.
|
|
|
|
=over 4
|
|
|
|
=item $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 I<may> append 's' to the key if your
|
|
facet is a list type.
|
|
|
|
=item $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 => {} }
|
|
|
|
=back
|
|
|
|
=head3 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 }
|
|
|
|
B<Note> Do not worry too much about getting the key/pluralization wrong. Most
|
|
tools will use L<Module::Pluggable> to load all facet types and build a hash
|
|
linking keys to packages and so on, working backwards. This means, in general,
|
|
that even if you get it wrong any tool that NEEDS the package for the facet
|
|
will find it.
|
|
|
|
B<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.
|
|
|
|
=head1 THE OLD API
|
|
|
|
The old API was simply a set of methods you were required to implement:
|
|
|
|
=over 4
|
|
|
|
=item $bool = $e->causes_fail
|
|
|
|
Returns true if this event should result in a test failure. In general this
|
|
should be false.
|
|
|
|
=item $bool = $e->increments_count
|
|
|
|
Should be true if this event should result in a test count increment.
|
|
|
|
=item $e->callback($hub)
|
|
|
|
If your event needs to have extra effects on the L<Test2::Hub> you can override
|
|
this method.
|
|
|
|
This is called B<BEFORE> your event is passed to the formatter.
|
|
|
|
=item $num = $e->nested
|
|
|
|
If this event is nested inside of other events, this should be the depth of
|
|
nesting. (This is mainly for subtests)
|
|
|
|
=item $bool = $e->global
|
|
|
|
Set this to true if your event is global, that is ALL threads and processes
|
|
should see it no matter when or where it is generated. This is not a common
|
|
thing to want, it is used by bail-out and skip_all to end testing.
|
|
|
|
=item $code = $e->terminate
|
|
|
|
This is called B<AFTER> your event has been passed to the formatter. This
|
|
should normally return undef, only change this if your event should cause the
|
|
test to exit immediately.
|
|
|
|
If you want this event to cause the test to exit you should return the exit
|
|
code here. Exit code of 0 means exit success, any other integer means exit with
|
|
failure.
|
|
|
|
This is used by L<Test2::Event::Plan> to exit 0 when the plan is
|
|
'skip_all'. This is also used by L<Test2::Event:Bail> to force the test
|
|
to exit with a failure.
|
|
|
|
This is called after the event has been sent to the formatter in order to
|
|
ensure the event is seen and understood.
|
|
|
|
=item $msg = $e->summary
|
|
|
|
This is intended to be a human readable summary of the event. This should
|
|
ideally only be one line long, but you can use multiple lines if necessary. This
|
|
is intended for human consumption. You do not need to make it easy for machines
|
|
to understand.
|
|
|
|
The default is to simply return the event package name.
|
|
|
|
=item ($count, $directive, $reason) = $e->sets_plan()
|
|
|
|
Check if this event sets the testing plan. It will return an empty list if it
|
|
does not. If it does set the plan it will return a list of 1 to 3 items in
|
|
order: Expected Test Count, Test Directive, Reason for directive.
|
|
|
|
=item $bool = $e->diagnostics
|
|
|
|
True if the event contains diagnostics info. This is useful because a
|
|
non-verbose harness may choose to hide events that are not in this category.
|
|
Some formatters may choose to send these to STDERR instead of STDOUT to ensure
|
|
they are seen.
|
|
|
|
=item $bool = $e->no_display
|
|
|
|
False by default. This will return true on events that should not be displayed
|
|
by formatters.
|
|
|
|
=back
|
|
|
|
=head1 SEE ALSO
|
|
|
|
L<Test2::Manual> - Primary index of the manual.
|
|
|
|
=head1 SOURCE
|
|
|
|
The source code repository for Test2-Manual can be found at
|
|
F<https://github.com/Test-More/Test2-Suite/>.
|
|
|
|
=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
|