Initial Commit
This commit is contained in:
78
database/perl/vendor/lib/Test2/Manual/Anatomy/API.pm
vendored
Normal file
78
database/perl/vendor/lib/Test2/Manual/Anatomy/API.pm
vendored
Normal file
@@ -0,0 +1,78 @@
|
||||
package Test2::Manual::Anatomy::API;
|
||||
use strict;
|
||||
use warnings;
|
||||
|
||||
our $VERSION = '0.000139';
|
||||
|
||||
1;
|
||||
|
||||
__END__
|
||||
|
||||
=head1 NAME
|
||||
|
||||
Test2::Manual::Anatomy::API - Internals documentation for the API.
|
||||
|
||||
=head1 DESCRIPTION
|
||||
|
||||
This document covers some of the internals of L<Test2::API>.
|
||||
|
||||
=head1 IMPLEMENTATION DETAILS
|
||||
|
||||
=head2 Test2::API
|
||||
|
||||
L<Test2::API> provides a functional interface to any test2 global state. This
|
||||
API should be preserved regardless of internal details of how and where the
|
||||
global state is stored.
|
||||
|
||||
This module itself does not store any state (with a few minor exceptions) but
|
||||
instead relies on L<Test2::API::Instance> to store state. This module is really
|
||||
intended to be the layer between the consumer and the implementation details.
|
||||
Ideally the implementation details can change any way they like, and this
|
||||
module can be updated to use the new details without breaking anything.
|
||||
|
||||
=head2 Test2::API::Instance
|
||||
|
||||
L<Test2::API::Instance> is where the global state is actually managed. This is
|
||||
an implementation detail, and should not be relied upon. It is entirely
|
||||
possible that L<Test2::API::Instance> could be removed completely, or changed
|
||||
in incompatible ways. Really these details are free to change so long as
|
||||
L<Test2::API> is not broken.
|
||||
|
||||
L<Test2::API::Instance> is fairly well documented, so no additionally
|
||||
documentation is needed for this manual page.
|
||||
|
||||
=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
|
||||
114
database/perl/vendor/lib/Test2/Manual/Anatomy/Context.pm
vendored
Normal file
114
database/perl/vendor/lib/Test2/Manual/Anatomy/Context.pm
vendored
Normal file
@@ -0,0 +1,114 @@
|
||||
package Test2::Manual::Anatomy::Context;
|
||||
use strict;
|
||||
use warnings;
|
||||
|
||||
our $VERSION = '0.000139';
|
||||
|
||||
1;
|
||||
|
||||
__END__
|
||||
|
||||
=head1 NAME
|
||||
|
||||
Test2::Manual::Anatomy::Context - Internals documentation for the Context
|
||||
objects.
|
||||
|
||||
=head1 DESCRIPTION
|
||||
|
||||
This document explains how the L<Test2::API::Context> object works.
|
||||
|
||||
=head1 WHAT IS THE CONTEXT OBJECT?
|
||||
|
||||
The context object is one of the key components of Test2, and makes many
|
||||
features possible that would otherwise be impossible. Every test tool starts by
|
||||
getting a context, and ends by releasing the context. A test tool does all its
|
||||
work between getting and releasing the context. The context instance is the
|
||||
primary interface for sending events to the Test2 stack. Finally the context
|
||||
system is responsible for tracking what file and line number a tool operates
|
||||
on, which is critical for debugging.
|
||||
|
||||
=head2 PRIMARY INTERFACE FOR TEST TOOLS
|
||||
|
||||
Nearly every Test2 based tool should start by calling C<$ctx =
|
||||
Test2::API::context()> in order to get a context object, and should end by
|
||||
calling C<< $ctx->release() >>. Once a tool has its context object it can call
|
||||
methods on the object to send events or have other effects. Nearly everything a
|
||||
test tool needs to do should be done through the context object.
|
||||
|
||||
=head2 TRACK FILE AND LINE NUMBERS FOR ERROR REPORTING
|
||||
|
||||
When you call C<Test2::API::Context> a new context object will be returned. If
|
||||
there is already a context object in effect (from a different point in the
|
||||
stack) you will get a clone of the existing one. If there is not already a
|
||||
current context then a completely new one will be generated. When a new context
|
||||
is generated Test2 will determine the file name and line number for your test
|
||||
code, these will be used when reporting any failures.
|
||||
|
||||
Typically the file and line number will be determined using C<caller()> to look
|
||||
at your tools caller. The C<$Test::Builder::Level> will be respected if
|
||||
detected, but is discouraged in favor of just using context objects at every
|
||||
level.
|
||||
|
||||
When calling C<Test2::API::Context()> you can specify the
|
||||
C<< level => $count >> arguments if you need to look at a deeper caller.
|
||||
|
||||
=head2 PRESERVE $?, $!, $^E AND $@
|
||||
|
||||
When you call C<Test2::API::context()> the current values of C<$?>, C<$!>,
|
||||
C<$^E>, and C<$@> are stored in the context object itself. Whenever the context
|
||||
is released the original values of these variables will be restored. This
|
||||
protects the variables from any side effects caused by testing tools.
|
||||
|
||||
=head2 FINALIZE THE API STATE
|
||||
|
||||
L<Test2::API> works via a hidden singleton instance of L<Test2::API::Instance>.
|
||||
The singleton has some state that is not set in stone until the last possible
|
||||
minute. The last possible minute happens to be the first time a context is
|
||||
acquired. State includes IPC instance, Formatter class, Root PID, etc.
|
||||
|
||||
=head2 FIND/CREATE THE CURRENT/ROOT HUB
|
||||
|
||||
L<Test2> has a stack of hubs, the stack can be accessed via
|
||||
L<Test2::API::test2_stack>. When you get a context it will find the current
|
||||
hub, if there is no current hub then the root one will be initialized.
|
||||
|
||||
=head2 PROVIDE HOOKS
|
||||
|
||||
There are hooks that run when contexts are created, found, and released. See
|
||||
L<Test2::API> for details on these hooks and how to use them.
|
||||
|
||||
=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
|
||||
376
database/perl/vendor/lib/Test2/Manual/Anatomy/EndToEnd.pm
vendored
Normal file
376
database/perl/vendor/lib/Test2/Manual/Anatomy/EndToEnd.pm
vendored
Normal file
@@ -0,0 +1,376 @@
|
||||
package Test2::Manual::Anatomy::EndToEnd;
|
||||
use strict;
|
||||
use warnings;
|
||||
|
||||
our $VERSION = '0.000139';
|
||||
|
||||
1;
|
||||
|
||||
__END__
|
||||
|
||||
=head1 NAME
|
||||
|
||||
Test2::Manual::EndToEnd - Overview of Test2 from load to finish.
|
||||
|
||||
=head1 DESCRIPTION
|
||||
|
||||
This is a high level overview of everything from loading Test2 through the end
|
||||
of a test script.
|
||||
|
||||
=head1 WHAT HAPPENS WHEN I LOAD THE API?
|
||||
|
||||
use Test2::API qw/context/;
|
||||
|
||||
=over 4
|
||||
|
||||
=item A singleton instance of Test2::API::Instance is created.
|
||||
|
||||
You have no access to this, it is an implementation detail.
|
||||
|
||||
=item Several API functions are defined that use the singleton instance.
|
||||
|
||||
You can import these functions, or use them directly.
|
||||
|
||||
=item Then what?
|
||||
|
||||
It waits...
|
||||
|
||||
The API intentionally does as little as possible. At this point something can
|
||||
still change the formatter, load L<Test2::IPC>, or have other global effects
|
||||
that need to be done before the first L<Test2::API::Context> is created. Once
|
||||
the first L<Test2::API::Context> is created the API will finish initialization.
|
||||
|
||||
See L</"WHAT HAPPENS WHEN I ACQUIRE A CONTEXT?"> for more information.
|
||||
|
||||
=back
|
||||
|
||||
=head1 WHAT HAPPENS WHEN I USE A TOOL?
|
||||
|
||||
This section covers the basic workflow all tools such as C<ok()> must follow.
|
||||
|
||||
sub ok($$) {
|
||||
my ($bool, $name) = @_;
|
||||
|
||||
my $ctx = context();
|
||||
|
||||
my $event = $ctx->send_event('Ok', pass => $bool, name => $name);
|
||||
|
||||
...
|
||||
|
||||
$ctx->release;
|
||||
return $bool;
|
||||
}
|
||||
|
||||
ok(1, "1 is true");
|
||||
|
||||
=over 4
|
||||
|
||||
=item A tool function is run.
|
||||
|
||||
ok(1, "1 is true");
|
||||
|
||||
=item The tool acquires a context object.
|
||||
|
||||
my $ctx = context();
|
||||
|
||||
See L</"WHAT HAPPENS WHEN I ACQUIRE A CONTEXT?"> for more information.
|
||||
|
||||
=item The tool uses the context object to create, send, and return events.
|
||||
|
||||
See L</"WHAT HAPPENS WHEN I SEND AN EVENT?"> for more information.
|
||||
|
||||
my $event = $ctx->send_event('Ok', pass => $bool, name => $name);
|
||||
|
||||
=item When done the tool MUST release the context.
|
||||
|
||||
See L</"WHAT HAPPENS WHEN I RELEASE A CONTEXT?"> for more information.
|
||||
|
||||
$ctx->release();
|
||||
|
||||
=item The tool returns.
|
||||
|
||||
return $bool;
|
||||
|
||||
=back
|
||||
|
||||
=head1 WHAT HAPPENS WHEN I ACQUIRE A CONTEXT?
|
||||
|
||||
my $ctx = context();
|
||||
|
||||
These actions may not happen exactly in this order, but that is an
|
||||
implementation detail. For the purposes of this document this order is used to
|
||||
help the reader understand the flow.
|
||||
|
||||
=over 4
|
||||
|
||||
=item $!, $@, $? and $^E are captured and preserved.
|
||||
|
||||
Test2 makes a point to preserve the values of $!, $@, $? and $^E such that the test
|
||||
tools do not modify these variables unexpectedly. They are captured first thing
|
||||
so that they can be restored later.
|
||||
|
||||
=item The API state is changed to 'loaded'.
|
||||
|
||||
The 'loaded' state means that test tools have already started running. This is
|
||||
important as some plugins need to take effect before any tests are run. This
|
||||
state change only happens the first time a context is acquired, and may trigger
|
||||
some hooks defined by plugins to run.
|
||||
|
||||
=item The current hub is found.
|
||||
|
||||
A context attaches itself to the current L<Test2::Hub>. If there is no current
|
||||
hub then the root hub will be initialized. This will also initialize the hub
|
||||
stack if necessary.
|
||||
|
||||
=item Context acquire hooks fire.
|
||||
|
||||
It is possible to create global, or hub-specific hooks that fire whenever a
|
||||
context is acquired, these hooks will fire now. These hooks fire even if there
|
||||
is an existing context.
|
||||
|
||||
=item Any existing context is found.
|
||||
|
||||
If the current hub already has a context then a clone of it will be used
|
||||
instead of a completely new context. This is important because it allows nested
|
||||
tools to inherit the context used by parent tools.
|
||||
|
||||
=item Stack depth is measured.
|
||||
|
||||
Test2 makes a point to catch mistakes in how the context is used. The stack
|
||||
depth is used to accomplish this. If there is an existing context the depth
|
||||
will be checked against the one found here. If the old context has the same
|
||||
stack depth, or a shallower one, it means a tool is misbehaving and did not
|
||||
clean up the context when it was done, in which case the old context will be
|
||||
cleaned up, and a warning issued.
|
||||
|
||||
=item A new context is created (if no existing context was found)
|
||||
|
||||
If there is no existing context, a new one will be created using the data
|
||||
collected so far.
|
||||
|
||||
=item Context init hooks fire (if no existing context was found)
|
||||
|
||||
If a new context was created, context-creation hooks will fire.
|
||||
|
||||
=item $!, $@, $?, and $^E are restored.
|
||||
|
||||
We make sure $!, $@, $?, and $^E are unchanged at this point so that changes we
|
||||
made will not effect anything else. This is done in case something inside the
|
||||
context construction accidentally changed these vars.
|
||||
|
||||
=item The context is returned.
|
||||
|
||||
You have a shiney new context object, or a clone of the existing context.
|
||||
|
||||
=back
|
||||
|
||||
=head1 WHAT HAPPENS WHEN I SEND AN EVENT?
|
||||
|
||||
my $event = $ctx->send_event('Ok', pass => $bool, name => $name);
|
||||
|
||||
=over 4
|
||||
|
||||
=item The Test2::Event::Ok module is loaded.
|
||||
|
||||
The C<send_event()> method will automatically load any Event package necessary.
|
||||
Normally C<send_event()> will assume the first argument is an event class
|
||||
without the C<Test2::Event::> prefix, which it will add for you. If you want to
|
||||
use an event class that is in a different namespace you can prefix the class
|
||||
name with a C<+> to tell the tool that you are giving a fully qualified class
|
||||
name:
|
||||
|
||||
my $event = $ctx->send_event('+Fully::Qualified::Event', pass => $bool, name => $name);
|
||||
|
||||
=item A new instance of Test2::Event::Ok is created.
|
||||
|
||||
The event object is instantiated using the provided parameters.
|
||||
|
||||
=item The event object is sent to the hub.
|
||||
|
||||
The hub takes over from here.
|
||||
|
||||
=item The hub runs the event through any filters.
|
||||
|
||||
Filters are able to modify or remove events. Filters are run first, before the
|
||||
event can modify global test state.
|
||||
|
||||
=item The global test state is updated to reflect the event.
|
||||
|
||||
If the event effects test count then the count will be incremented. If the
|
||||
event causes failure then the failure count will be incremented. There are a
|
||||
couple other ways the global state can be effected as well.
|
||||
|
||||
=item The event is sent to the formatter
|
||||
|
||||
After the state is changed the hub will send the event to the formatter for
|
||||
rendering. This is where TAP is normally produced.
|
||||
|
||||
=item The event is sent to all listeners.
|
||||
|
||||
There can be any number of listeners that take action when events are
|
||||
processed, this happens now.
|
||||
|
||||
=back
|
||||
|
||||
=head1 WHAT HAPPENS WHEN I RELEASE A CONTEXT?
|
||||
|
||||
$ctx->release;
|
||||
|
||||
=over 4
|
||||
|
||||
=item The current context clone is released.
|
||||
|
||||
If your tool is nested inside another, then releasing will simply destroy the
|
||||
copy of the context, nothing else will happen.
|
||||
|
||||
=item If this was the canonical context, it will actually release
|
||||
|
||||
When a context is created it is considered 'canon'. Any context obtained by a
|
||||
nested tool will be considered a child context linked to the canonical one.
|
||||
Releasing child contexts does not do anything of note (but is still required).
|
||||
|
||||
=item Release hooks are called
|
||||
|
||||
Release hooks are the main motivation behind making the C<release()> method,
|
||||
and making it a required action on the part of test tools. These are hooks that
|
||||
we can have called when a tool is complete. This is how plugins like
|
||||
L<Test2::Plugin::DieOnFail> are implemented. If we simply had a destructor call
|
||||
the hooks then we would be unable to write this plugin as a C<die> inside of a
|
||||
destructor is useless.
|
||||
|
||||
=item The context is cleared
|
||||
|
||||
The main context data is cleared allowing the next tool to create a new
|
||||
context. This is important as the next tool very likely has a new line number.
|
||||
|
||||
=item $!, $@, $?, and $^E are restored
|
||||
|
||||
When a Test2 tool is complete it will restore $@, $!, $? and $^E to avoid action at
|
||||
a distance.
|
||||
|
||||
=back
|
||||
|
||||
=head1 WHAT HAPPENS WHEN I USE done_testing()?
|
||||
|
||||
done_testing();
|
||||
|
||||
=over 4
|
||||
|
||||
=item Any pending IPC events will be culled.
|
||||
|
||||
If IPC is turned on, a final culling will take place.
|
||||
|
||||
=item Follow-up hooks are run
|
||||
|
||||
The follow-up hooks are a way to run actions when a hub is complete. This is
|
||||
useful for adding cleanup tasks, or final tests to the end of a test.
|
||||
|
||||
=item The final plan event is generated and processed.
|
||||
|
||||
The final plan event will be produced using the current test count as the
|
||||
number of tests planned.
|
||||
|
||||
=item The current hub is finalized.
|
||||
|
||||
This will mark the hub is complete, and will not allow new events to be
|
||||
processed.
|
||||
|
||||
=back
|
||||
|
||||
=head1 WHAT HAPPENS WHEN A TEST SCRIPT IS DONE?
|
||||
|
||||
Test2 has some behaviors it runs in an C<END { ... }> block after tests are
|
||||
done running. This end block does some final checks to warn you if something
|
||||
went wrong. This end block also sets the exit value of the script.
|
||||
|
||||
=over 4
|
||||
|
||||
=item API Versions are checked.
|
||||
|
||||
A warning will be produced if L<Test::Builder> is loaded, but has a different
|
||||
version compared to L<Test2::API>. This situation can happen if you downgrade
|
||||
to an older Test-Simple distribution, and is a bad situation.
|
||||
|
||||
=item Any remaining context objects are cleaned up.
|
||||
|
||||
If there are leftover context objects they will need to be cleaned up. A
|
||||
leftover context is never a good thing, and usually requires a warning. A
|
||||
leftover context could also be the result of an exception being thrown which
|
||||
terminates the script, L<Test2> is fairly good at noticing this and not warning
|
||||
in these cases as the warning would simply be noise.
|
||||
|
||||
=item Child processes are sent a 'waiting' event.
|
||||
|
||||
If IPC is active, a waiting event is sent to all child processes.
|
||||
|
||||
=item The script will wait for all child processes and/or threads to complete.
|
||||
|
||||
This happens only when IPC is loaded, but Test::Builder is not. This behavior
|
||||
is useful, but would break compatibility for legacy tests.
|
||||
|
||||
=item The hub stack is cleaned up.
|
||||
|
||||
All hubs are finalized starting from the top. Leftover hubs are usually a bad
|
||||
thing, so a warning is produced if any are found.
|
||||
|
||||
=item The root hub is finalized.
|
||||
|
||||
This step is a no-op if C<done_testing()> was used. If needed this will mark
|
||||
the root hub as finished.
|
||||
|
||||
=item Exit callbacks are called.
|
||||
|
||||
This is a chance for plugins to modify the final exit value of the script.
|
||||
|
||||
=item The scripts exit value ($?) is set.
|
||||
|
||||
If the test encountered any failures this will be set to a non-zero value. If
|
||||
possible this will be set to the number of failures, or 255 if the number is
|
||||
larger than 255 (the max value allowed).
|
||||
|
||||
=item Broken module diagnostics
|
||||
|
||||
Test2 is aware of many modules which were broken by Test2's release. At this
|
||||
point the script will check if any known-broken modules were loaded, and warn
|
||||
you if they were.
|
||||
|
||||
B<Note:> This only happens if there were test failures. No broken module
|
||||
warnings are produced on a success.
|
||||
|
||||
=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
|
||||
416
database/perl/vendor/lib/Test2/Manual/Anatomy/Event.pm
vendored
Normal file
416
database/perl/vendor/lib/Test2/Manual/Anatomy/Event.pm
vendored
Normal file
@@ -0,0 +1,416 @@
|
||||
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
|
||||
120
database/perl/vendor/lib/Test2/Manual/Anatomy/Hubs.pm
vendored
Normal file
120
database/perl/vendor/lib/Test2/Manual/Anatomy/Hubs.pm
vendored
Normal file
@@ -0,0 +1,120 @@
|
||||
package Test2::Manual::Anatomy::Hubs;
|
||||
use strict;
|
||||
use warnings;
|
||||
|
||||
our $VERSION = '0.000139';
|
||||
|
||||
1;
|
||||
|
||||
__END__
|
||||
|
||||
=head1 NAME
|
||||
|
||||
Test2::Manual::Anatomy::Hubs - Internals documentation for the hub stack, and
|
||||
hubs.
|
||||
|
||||
=head1 DESCRIPTION
|
||||
|
||||
This document describes the hub stack, and the hubs it contains. It explains
|
||||
why we have a stack, and when to add/remove hubs from it.
|
||||
|
||||
=head1 WHAT IS A HUB?
|
||||
|
||||
Test2 is an event system, tools generate events, those events are then
|
||||
processed to modify the testing state (number of tests, number of failures,
|
||||
etc). The hub is responsible for receiving and processing events to record the
|
||||
change in state. All events should eventually reach a destination hub.
|
||||
|
||||
The base hub is L<Test2::Hub>. All hub classes should inherit from the base hub
|
||||
class. The base hub class provides several hooks that allow you to monitor or
|
||||
modify events. Hubs are also responsible for forwarding events to the output
|
||||
formatter.
|
||||
|
||||
=head1 WHY DO WE HAVE A HUB STACK?
|
||||
|
||||
There are cases where it makes sense to have more than one hub:
|
||||
|
||||
=over 4
|
||||
|
||||
=item subtests
|
||||
|
||||
In Test2 subtests are implemented using the hub stack. When you start a subtest
|
||||
a new L<Test2::Hub::Subtest> instance is created and pushed to the stack. Once
|
||||
this is done all calls to C<Test2::API::context> will find the new hub and send
|
||||
all events to it. When the subtest tool is complete it will remove the new hub,
|
||||
and send a final subtest event to the parent hub.
|
||||
|
||||
=item testing your test tools
|
||||
|
||||
C<Test2::API::intercept()> is implemented using the hub stack. The
|
||||
C<Test2::API::intercept()> function will add an L<Test2::Hub::Interceptor>
|
||||
instance to the stack, any calls to L<Test2::API::context()> will find the new
|
||||
hub, and send it all events. The intercept hub is special in that is has no
|
||||
connection to the parent hub, and usually does not have a formatter.
|
||||
|
||||
=back
|
||||
|
||||
=head1 WHEN SHOULD I ADD A HUB TO THE STACK?
|
||||
|
||||
Any time you want to intercept or block events from effecting the test state.
|
||||
Adding a new hub is essentially a way to create a sandbox where you have
|
||||
absolute control over what events do. Adding a new hub insures that the main
|
||||
test state will not be effected.
|
||||
|
||||
=head1 WHERE IS THE STACK?
|
||||
|
||||
The stack is an instance of L<Test2::API::Stack>. You can access the global hub
|
||||
stack using C<Test2::API::test2_stack>.
|
||||
|
||||
=head1 WHAT ABOUT THE ROOT HUB?
|
||||
|
||||
The root hub is created automatically as needed. A call to
|
||||
C<< Test2::API::test2_stack->top() >> will create the root hub if it does not
|
||||
already exist.
|
||||
|
||||
=head1 HOW DO HUBS HANDLE IPC?
|
||||
|
||||
If the IPC system (L<Test2::IPC>) was not loaded, then IPC is not handled at
|
||||
all. Forking or creating new threads without the IPC system can cause
|
||||
unexpected problems.
|
||||
|
||||
All hubs track the PID and Thread ID that was current when they were created.
|
||||
If an event is sent to a hub in a new process/thread the hub will detect this
|
||||
and try to forward the event along to the correct process/thread. This is
|
||||
accomplished using the IPC system.
|
||||
|
||||
=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
|
||||
90
database/perl/vendor/lib/Test2/Manual/Anatomy/IPC.pm
vendored
Normal file
90
database/perl/vendor/lib/Test2/Manual/Anatomy/IPC.pm
vendored
Normal file
@@ -0,0 +1,90 @@
|
||||
package Test2::Manual::Anatomy::IPC;
|
||||
use strict;
|
||||
use warnings;
|
||||
|
||||
our $VERSION = '0.000139';
|
||||
|
||||
1;
|
||||
|
||||
__END__
|
||||
|
||||
=head1 NAME
|
||||
|
||||
Test2::Manual::Anatomy::IPC - Manual for the IPC system.
|
||||
|
||||
=head1 DESCRIPTION
|
||||
|
||||
This document describes the IPC system.
|
||||
|
||||
=head1 WHAT IS THE IPC SYSTEM
|
||||
|
||||
The IPC system is activated by loading L<Test2::IPC>. This makes hubs
|
||||
process/thread aware, and makes them forward events along to the parent
|
||||
process/thread as necessary.
|
||||
|
||||
=head1 HOW DOES THE IPC SYSTEM EFFECT EVERYTHING?
|
||||
|
||||
L<Test2::API> and L<Test2::API::Instance> have some behaviors that trigger if
|
||||
L<Test2::IPC> is loaded before the global state is initialized. Mainly an IPC
|
||||
driver will be initiated and stored in the global state.
|
||||
|
||||
If an IPC driver is initialized then all hubs will be initialized with a
|
||||
reference to the driver instance. If a hub has an IPC driver instance it will
|
||||
use it to forward events to parent processes and threads.
|
||||
|
||||
=head1 WHAT DOES AN IPC DRIVER DO?
|
||||
|
||||
An L<Test2::IPC::Driver> provides a way to send event data to a destination
|
||||
process+thread+hub (or to all globally). The driver must also provide a way for
|
||||
a process/thread/hub to read in any pending events that have been sent to it.
|
||||
|
||||
=head1 HOW DOES THE DEFAULT IPC DRIVER WORK?
|
||||
|
||||
The default IPC driver is L<Test2::API::Driver::Files>. This default driver,
|
||||
when initialized, starts by creating a temporary directory. Any time an event
|
||||
needs to be sent to another process/thread/hub, the event will be written to a
|
||||
file using L<Storable>. The file is written with the destination process,
|
||||
thread, and hub as part of the filename. All hubs will regularly check for
|
||||
pending IPC events and will process them.
|
||||
|
||||
This driver is further optimized using a small chunk of SHM. Any time a new
|
||||
event is sent via IPC the shm is updated to have a new value. Hubs will not
|
||||
bother checking for new IPC events unless the shm value has changed since their
|
||||
last poll. A result of this is that the IPC system is surprisingly fast, and
|
||||
does not waste time polling the hard drive when there are no pending events.
|
||||
|
||||
=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
|
||||
76
database/perl/vendor/lib/Test2/Manual/Anatomy/Utilities.pm
vendored
Normal file
76
database/perl/vendor/lib/Test2/Manual/Anatomy/Utilities.pm
vendored
Normal file
@@ -0,0 +1,76 @@
|
||||
package Test2::Manual::Anatomy::Utilities;
|
||||
use strict;
|
||||
use warnings;
|
||||
|
||||
our $VERSION = '0.000139';
|
||||
|
||||
1;
|
||||
|
||||
__END__
|
||||
|
||||
=head1 NAME
|
||||
|
||||
Test2::Manual::Anatomy::Utilities - Overview of utilities for Test2.
|
||||
|
||||
=head1 DESCRIPTION
|
||||
|
||||
This is a brief overview of the utilities provided by Test2.
|
||||
|
||||
=head1 Test2::Util
|
||||
|
||||
L<Test2::Util> provides functions to help you find out about the current
|
||||
system, or to run generic tasks that tend to be Test2 specific.
|
||||
|
||||
This utility provides things like an internal C<try {...}> implementation, and
|
||||
constants for things like threading and forking support.
|
||||
|
||||
=head1 Test2::Util::ExternalMeta
|
||||
|
||||
L<Test2::Util::ExternalMeta> allows you to quickly and easily attach meta-data
|
||||
to an object class.
|
||||
|
||||
=head1 Test2::Util::Facets2Legacy
|
||||
|
||||
L<Test2::Util::Facets2Legacy> is a set of functions you can import into a more
|
||||
recent event class to provide the classic event API.
|
||||
|
||||
=head1 Test2::Util::HashBase
|
||||
|
||||
L<Test2::Util::HashBase> is a local copy of L<Object::HashBase>. All object
|
||||
classes provided by L<Test2> use this to generate methods and accessors.
|
||||
|
||||
=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
|
||||
Reference in New Issue
Block a user