This is a live mirror of the Perl 5 development currently hosted at https://github.com/perl/perl5
5acee794f5d39c0f87830c2427f95b031b4e06fd
[perl5.git] / cpan / Test-Simple / lib / Test2 / Hub.pm
1 package Test2::Hub;
2 use strict;
3 use warnings;
4
5 our $VERSION = '1.302067';
6
7
8 use Carp qw/carp croak confess/;
9 use Test2::Util qw/get_tid ipc_separator/;
10
11 use Scalar::Util qw/weaken/;
12
13 use Test2::Util::ExternalMeta qw/meta get_meta set_meta delete_meta/;
14 use Test2::Util::HashBase qw{
15     pid tid hid ipc
16     no_ending
17     _filters
18     _pre_filters
19     _listeners
20     _follow_ups
21     _formatter
22     _context_acquire
23     _context_init
24     _context_release
25
26     active
27     count
28     failed
29     ended
30     bailed_out
31     _passing
32     _plan
33     skip_reason
34 };
35
36 my $ID_POSTFIX = 1;
37 sub init {
38     my $self = shift;
39
40     $self->{+PID} = $$;
41     $self->{+TID} = get_tid();
42     $self->{+HID} = join ipc_separator, $self->{+PID}, $self->{+TID}, $ID_POSTFIX++;
43
44     $self->{+COUNT}    = 0;
45     $self->{+FAILED}   = 0;
46     $self->{+_PASSING} = 1;
47
48     if (my $formatter = delete $self->{formatter}) {
49         $self->format($formatter);
50     }
51
52     if (my $ipc = $self->{+IPC}) {
53         $ipc->add_hub($self->{+HID});
54     }
55 }
56
57 sub is_subtest { 0 }
58
59 sub reset_state {
60     my $self = shift;
61
62     $self->{+COUNT} = 0;
63     $self->{+FAILED} = 0;
64     $self->{+_PASSING} = 1;
65
66     delete $self->{+_PLAN};
67     delete $self->{+ENDED};
68     delete $self->{+BAILED_OUT};
69     delete $self->{+SKIP_REASON};
70 }
71
72 sub inherit {
73     my $self = shift;
74     my ($from, %params) = @_;
75
76     $self->{+_FORMATTER} = $from->{+_FORMATTER}
77         unless $self->{+_FORMATTER} || exists($params{formatter});
78
79     if ($from->{+IPC} && !$self->{+IPC} && !exists($params{ipc})) {
80         my $ipc = $from->{+IPC};
81         $self->{+IPC} = $ipc;
82         $ipc->add_hub($self->{+HID});
83     }
84
85     if (my $ls = $from->{+_LISTENERS}) {
86         push @{$self->{+_LISTENERS}} => grep { $_->{inherit} } @$ls;
87     }
88
89     if (my $pfs = $from->{+_PRE_FILTERS}) {
90         push @{$self->{+_PRE_FILTERS}} => grep { $_->{inherit} } @$pfs;
91     }
92
93     if (my $fs = $from->{+_FILTERS}) {
94         push @{$self->{+_FILTERS}} => grep { $_->{inherit} } @$fs;
95     }
96 }
97
98 sub format {
99     my $self = shift;
100
101     my $old = $self->{+_FORMATTER};
102     ($self->{+_FORMATTER}) = @_ if @_;
103
104     return $old;
105 }
106
107 sub is_local {
108     my $self = shift;
109     return $$ == $self->{+PID}
110         && get_tid() == $self->{+TID};
111 }
112
113 sub listen {
114     my $self = shift;
115     my ($sub, %params) = @_;
116
117     carp "Useless addition of a listener in a child process or thread!"
118         if $$ != $self->{+PID} || get_tid() != $self->{+TID};
119
120     croak "listen only takes coderefs for arguments, got '$sub'"
121         unless ref $sub && ref $sub eq 'CODE';
122
123     push @{$self->{+_LISTENERS}} => { %params, code => $sub };
124
125     $sub; # Intentional return.
126 }
127
128 sub unlisten {
129     my $self = shift;
130
131     carp "Useless removal of a listener in a child process or thread!"
132         if $$ != $self->{+PID} || get_tid() != $self->{+TID};
133
134     my %subs = map {$_ => $_} @_;
135
136     @{$self->{+_LISTENERS}} = grep { !$subs{$_->{code}} } @{$self->{+_LISTENERS}};
137 }
138
139 sub filter {
140     my $self = shift;
141     my ($sub, %params) = @_;
142
143     carp "Useless addition of a filter in a child process or thread!"
144         if $$ != $self->{+PID} || get_tid() != $self->{+TID};
145
146     croak "filter only takes coderefs for arguments, got '$sub'"
147         unless ref $sub && ref $sub eq 'CODE';
148
149     push @{$self->{+_FILTERS}} => { %params, code => $sub };
150
151     $sub; # Intentional Return
152 }
153
154 sub unfilter {
155     my $self = shift;
156     carp "Useless removal of a filter in a child process or thread!"
157         if $$ != $self->{+PID} || get_tid() != $self->{+TID};
158     my %subs = map {$_ => $_} @_;
159     @{$self->{+_FILTERS}} = grep { !$subs{$_->{code}} } @{$self->{+_FILTERS}};
160 }
161
162 sub pre_filter {
163     my $self = shift;
164     my ($sub, %params) = @_;
165
166     croak "pre_filter only takes coderefs for arguments, got '$sub'"
167         unless ref $sub && ref $sub eq 'CODE';
168
169     push @{$self->{+_PRE_FILTERS}} => { %params, code => $sub };
170
171     $sub; # Intentional Return
172 }
173
174 sub pre_unfilter {
175     my $self = shift;
176     my %subs = map {$_ => $_} @_;
177     @{$self->{+_PRE_FILTERS}} = grep { !$subs{$_->{code}} } @{$self->{+_PRE_FILTERS}};
178 }
179
180 sub follow_up {
181     my $self = shift;
182     my ($sub) = @_;
183
184     carp "Useless addition of a follow-up in a child process or thread!"
185         if $$ != $self->{+PID} || get_tid() != $self->{+TID};
186
187     croak "follow_up only takes coderefs for arguments, got '$sub'"
188         unless ref $sub && ref $sub eq 'CODE';
189
190     push @{$self->{+_FOLLOW_UPS}} => $sub;
191 }
192
193 *add_context_aquire = \&add_context_acquire;
194 sub add_context_acquire {
195     my $self = shift;
196     my ($sub) = @_;
197
198     croak "add_context_acquire only takes coderefs for arguments, got '$sub'"
199         unless ref $sub && ref $sub eq 'CODE';
200
201     push @{$self->{+_CONTEXT_ACQUIRE}} => $sub;
202
203     $sub; # Intentional return.
204 }
205
206 *remove_context_aquire = \&remove_context_acquire;
207 sub remove_context_acquire {
208     my $self = shift;
209     my %subs = map {$_ => $_} @_;
210     @{$self->{+_CONTEXT_ACQUIRE}} = grep { !$subs{$_} == $_ } @{$self->{+_CONTEXT_ACQUIRE}};
211 }
212
213 sub add_context_init {
214     my $self = shift;
215     my ($sub) = @_;
216
217     croak "add_context_init only takes coderefs for arguments, got '$sub'"
218         unless ref $sub && ref $sub eq 'CODE';
219
220     push @{$self->{+_CONTEXT_INIT}} => $sub;
221
222     $sub; # Intentional return.
223 }
224
225 sub remove_context_init {
226     my $self = shift;
227     my %subs = map {$_ => $_} @_;
228     @{$self->{+_CONTEXT_INIT}} = grep { !$subs{$_} == $_ } @{$self->{+_CONTEXT_INIT}};
229 }
230
231 sub add_context_release {
232     my $self = shift;
233     my ($sub) = @_;
234
235     croak "add_context_release only takes coderefs for arguments, got '$sub'"
236         unless ref $sub && ref $sub eq 'CODE';
237
238     push @{$self->{+_CONTEXT_RELEASE}} => $sub;
239
240     $sub; # Intentional return.
241 }
242
243 sub remove_context_release {
244     my $self = shift;
245     my %subs = map {$_ => $_} @_;
246     @{$self->{+_CONTEXT_RELEASE}} = grep { !$subs{$_} == $_ } @{$self->{+_CONTEXT_RELEASE}};
247 }
248
249 sub send {
250     my $self = shift;
251     my ($e) = @_;
252
253     if ($self->{+_PRE_FILTERS}) {
254         for (@{$self->{+_PRE_FILTERS}}) {
255             $e = $_->{code}->($self, $e);
256             return unless $e;
257         }
258     }
259
260     my $ipc = $self->{+IPC} || return $self->process($e);
261
262     if($e->global) {
263         $ipc->send($self->{+HID}, $e, 'GLOBAL');
264         return $self->process($e);
265     }
266
267     return $ipc->send($self->{+HID}, $e)
268         if $$ != $self->{+PID} || get_tid() != $self->{+TID};
269
270     $self->process($e);
271 }
272
273 sub process {
274     my $self = shift;
275     my ($e) = @_;
276
277     if ($self->{+_FILTERS}) {
278         for (@{$self->{+_FILTERS}}) {
279             $e = $_->{code}->($self, $e);
280             return unless $e;
281         }
282     }
283
284     my $type = ref($e);
285     my $is_ok = $type eq 'Test2::Event::Ok';
286     my $no_fail = $type eq 'Test2::Event::Diag' || $type eq 'Test2::Event::Note';
287     my $causes_fail = $is_ok ? !$e->{effective_pass} : $no_fail ? 0 : $e->causes_fail;
288     my $counted = $is_ok || (!$no_fail && $e->increments_count);
289
290     $self->{+COUNT}++      if $counted;
291     $self->{+FAILED}++     if $causes_fail && $counted;
292     $self->{+_PASSING} = 0 if $causes_fail;
293
294     my $callback = $e->callback($self) unless $is_ok || $no_fail;
295
296     my $count = $self->{+COUNT};
297
298     $self->{+_FORMATTER}->write($e, $count) if $self->{+_FORMATTER};
299
300     if ($self->{+_LISTENERS}) {
301         $_->{code}->($self, $e, $count) for @{$self->{+_LISTENERS}};
302     }
303
304     return $e if $is_ok || $no_fail;
305
306     my $code = $e->terminate;
307     if (defined $code) {
308         $self->{+_FORMATTER}->terminate($e) if $self->{+_FORMATTER};
309         $self->terminate($code, $e);
310     }
311
312     return $e;
313 }
314
315 sub terminate {
316     my $self = shift;
317     my ($code) = @_;
318     exit($code);
319 }
320
321 sub cull {
322     my $self = shift;
323
324     my $ipc = $self->{+IPC} || return;
325     return if $self->{+PID} != $$ || $self->{+TID} != get_tid();
326
327     # No need to do IPC checks on culled events
328     $self->process($_) for $ipc->cull($self->{+HID});
329 }
330
331 sub finalize {
332     my $self = shift;
333     my ($trace, $do_plan) = @_;
334
335     $self->cull();
336
337     my $plan   = $self->{+_PLAN};
338     my $count  = $self->{+COUNT};
339     my $failed = $self->{+FAILED};
340     my $active = $self->{+ACTIVE};
341
342         # return if NOTHING was done.
343         unless ($active || $do_plan || defined($plan) || $count || $failed) {
344                 $self->{+_FORMATTER}->finalize($plan, $count, $failed, 0, $self->is_subtest) if $self->{+_FORMATTER};
345                 return;
346         }
347
348     unless ($self->{+ENDED}) {
349         if ($self->{+_FOLLOW_UPS}) {
350             $_->($trace, $self) for reverse @{$self->{+_FOLLOW_UPS}};
351         }
352
353         # These need to be refreshed now
354         $plan   = $self->{+_PLAN};
355         $count  = $self->{+COUNT};
356         $failed = $self->{+FAILED};
357
358         if (($plan && $plan eq 'NO PLAN') || ($do_plan && !$plan)) {
359             $self->send(
360                 Test2::Event::Plan->new(
361                     trace => $trace,
362                     max => $count,
363                 )
364             );
365         }
366         $plan = $self->{+_PLAN};
367     }
368
369     my $frame = $trace->frame;
370     if($self->{+ENDED}) {
371         my (undef, $ffile, $fline) = @{$self->{+ENDED}};
372         my (undef, $sfile, $sline) = @$frame;
373
374         die <<"        EOT"
375 Test already ended!
376 First End:  $ffile line $fline
377 Second End: $sfile line $sline
378         EOT
379     }
380
381     $self->{+ENDED} = $frame;
382     my $pass = $self->is_passing(); # Generate the final boolean.
383
384         $self->{+_FORMATTER}->finalize($plan, $count, $failed, $pass, $self->is_subtest) if $self->{+_FORMATTER};
385
386     return $pass;
387 }
388
389 sub is_passing {
390     my $self = shift;
391
392     ($self->{+_PASSING}) = @_ if @_;
393
394     # If we already failed just return 0.
395     my $pass = $self->{+_PASSING} or return 0;
396     return $self->{+_PASSING} = 0 if $self->{+FAILED};
397
398     my $count = $self->{+COUNT};
399     my $ended = $self->{+ENDED};
400     my $plan = $self->{+_PLAN};
401
402     return $pass if !$count && $plan && $plan =~ m/^SKIP$/;
403
404     return $self->{+_PASSING} = 0
405         if $ended && (!$count || !$plan);
406
407     return $pass unless $plan && $plan =~ m/^\d+$/;
408
409     if ($ended) {
410         return $self->{+_PASSING} = 0 if $count != $plan;
411     }
412     else {
413         return $self->{+_PASSING} = 0 if $count > $plan;
414     }
415
416     return $pass;
417 }
418
419 sub plan {
420     my $self = shift;
421
422     return $self->{+_PLAN} unless @_;
423
424     my ($plan) = @_;
425
426     confess "You cannot unset the plan"
427         unless defined $plan;
428
429     confess "You cannot change the plan"
430         if $self->{+_PLAN} && $self->{+_PLAN} !~ m/^NO PLAN$/;
431
432     confess "'$plan' is not a valid plan! Plan must be an integer greater than 0, 'NO PLAN', or 'SKIP'"
433         unless $plan =~ m/^(\d+|NO PLAN|SKIP)$/;
434
435     $self->{+_PLAN} = $plan;
436 }
437
438 sub check_plan {
439     my $self = shift;
440
441     return undef unless $self->{+ENDED};
442     my $plan = $self->{+_PLAN} || return undef;
443
444     return 1 if $plan !~ m/^\d+$/;
445
446     return 1 if $plan == $self->{+COUNT};
447     return 0;
448 }
449
450 sub DESTROY {
451     my $self = shift;
452     my $ipc = $self->{+IPC} || return;
453     return unless $$ == $self->{+PID};
454     return unless get_tid() == $self->{+TID};
455
456     $ipc->drop_hub($self->{+HID});
457 }
458
459 1;
460
461 __END__
462
463 =pod
464
465 =encoding UTF-8
466
467 =head1 NAME
468
469 Test2::Hub - The conduit through which all events flow.
470
471 =head1 SYNOPSIS
472
473     use Test2::Hub;
474
475     my $hub = Test2::Hub->new();
476     $hub->send(...);
477
478 =head1 DESCRIPTION
479
480 The hub is the place where all events get processed and handed off to the
481 formatter. The hub also tracks test state, and provides several hooks into the
482 event pipeline.
483
484 =head1 COMMON TASKS
485
486 =head2 SENDING EVENTS
487
488     $hub->send($event)
489
490 The C<send()> method is used to issue an event to the hub. This method will
491 handle thread/fork sync, filters, listeners, TAP output, etc.
492
493 =head2 ALTERING OR REMOVING EVENTS
494
495 You can use either C<filter()> or C<pre_filter()>, depending on your
496 needs. Both have identical syntax, so only C<filter()> is shown here.
497
498     $hub->filter(sub {
499         my ($hub, $event) = @_;
500
501         my $action = get_action($event);
502
503         # No action should be taken
504         return $event if $action eq 'none';
505
506         # You want your filter to remove the event
507         return undef if $action eq 'delete';
508
509         if ($action eq 'do_it') {
510             my $new_event = copy_event($event);
511             ... Change your copy of the event ...
512             return $new_event;
513         }
514
515         die "Should not happen";
516     });
517
518 By default, filters are not inherited by child hubs. That means if you start a
519 subtest, the subtest will not inherit the filter. You can change this behavior
520 with the C<inherit> parameter:
521
522     $hub->filter(sub { ... }, inherit => 1);
523
524 =head2 LISTENING FOR EVENTS
525
526     $hub->listen(sub {
527         my ($hub, $event, $number) = @_;
528
529         ... do whatever you want with the event ...
530
531         # return is ignored
532     });
533
534 By default listeners are not inherited by child hubs. That means if you start a
535 subtest, the subtest will not inherit the listener. You can change this behavior
536 with the C<inherit> parameter:
537
538     $hub->listen(sub { ... }, inherit => 1);
539
540
541 =head2 POST-TEST BEHAVIORS
542
543     $hub->follow_up(sub {
544         my ($trace, $hub) = @_;
545
546         ... do whatever you need to ...
547
548         # Return is ignored
549     });
550
551 follow_up subs are called only once, either when done_testing is called, or in
552 an END block.
553
554 =head2 SETTING THE FORMATTER
555
556 By default an instance of L<Test2::Formatter::TAP> is created and used.
557
558     my $old = $hub->format(My::Formatter->new);
559
560 Setting the formatter will REPLACE any existing formatter. You may set the
561 formatter to undef to prevent output. The old formatter will be returned if one
562 was already set. Only one formatter is allowed at a time.
563
564 =head1 METHODS
565
566 =over 4
567
568 =item $hub->send($event)
569
570 This is where all events enter the hub for processing.
571
572 =item $hub->process($event)
573
574 This is called by send after it does any IPC handling. You can use this to
575 bypass the IPC process, but in general you should avoid using this.
576
577 =item $old = $hub->format($formatter)
578
579 Replace the existing formatter instance with a new one. Formatters must be
580 objects that implement a C<< $formatter->write($event) >> method.
581
582 =item $sub = $hub->listen(sub { ... }, %optional_params)
583
584 You can use this to record all events AFTER they have been sent to the
585 formatter. No changes made here will be meaningful, except possibly to other
586 listeners.
587
588     $hub->listen(sub {
589         my ($hub, $event, $number) = @_;
590
591         ... do whatever you want with the event ...
592
593         # return is ignored
594     });
595
596 Normally listeners are not inherited by child hubs such as subtests. You can
597 add the C<< inherit => 1 >> parameter to allow a listener to be inherited.
598
599 =item $hub->unlisten($sub)
600
601 You can use this to remove a listen callback. You must pass in the coderef
602 returned by the C<listen()> method.
603
604 =item $sub = $hub->filter(sub { ... }, %optional_params)
605
606 =item $sub = $hub->pre_filter(sub { ... }, %optional_params)
607
608 These can be used to add filters. Filters can modify, replace, or remove events
609 before anything else can see them.
610
611     $hub->filter(
612         sub {
613             my ($hub, $event) = @_;
614
615             return $event;    # No Changes
616             return;           # Remove the event
617
618             # Or you can modify an event before returning it.
619             $event->modify;
620             return $event;
621         }
622     );
623
624 If you are not using threads, forking, or IPC then the only difference between
625 a C<filter> and a C<pre_filter> is that C<pre_filter> subs run first. When you
626 are using threads, forking, or IPC, pre_filters happen to events before they
627 are sent to their destination proc/thread, ordinary filters happen only in the
628 destination hub/thread.
629
630 You cannot add a regular filter to a hub if the hub was created in another
631 process or thread. You can always add a pre_filter.
632
633 =item $hub->unfilter($sub)
634
635 =item $hub->pre_unfilter($sub)
636
637 These can be used to remove filters and pre_filters. The C<$sub> argument is
638 the reference returned by C<filter()> or C<pre_filter()>.
639
640 =item $hub->follow_op(sub { ... })
641
642 Use this to add behaviors that are called just before the hub is finalized. The
643 only argument to your codeblock will be a L<Test2::Util::Trace> instance.
644
645     $hub->follow_up(sub {
646         my ($trace, $hub) = @_;
647
648         ... do whatever you need to ...
649
650         # Return is ignored
651     });
652
653 follow_up subs are called only once, ether when done_testing is called, or in
654 an END block.
655
656 =item $sub = $hub->add_context_acquire(sub { ... });
657
658 Add a callback that will be called every time someone tries to acquire a
659 context. It gets a single argument, a reference of the hash of parameters
660 being used the construct the context. This is your chance to change the
661 parameters by directly altering the hash.
662
663     test2_add_callback_context_acquire(sub {
664         my $params = shift;
665         $params->{level}++;
666     });
667
668 This is a very scary API function. Please do not use this unless you need to.
669 This is here for L<Test::Builder> and backwards compatibility. This has you
670 directly manipulate the hash instead of returning a new one for performance
671 reasons.
672
673 B<Note> Using this hook could have a huge performance impact.
674
675 The coderef you provide is returned and can be used to remove the hook later.
676
677 =item $hub->remove_context_acquire($sub);
678
679 This can be used to remove a context acquire hook.
680
681 =item $sub = $hub->add_context_init(sub { ... });
682
683 This allows you to add callbacks that will trigger every time a new context is
684 created for the hub. The only argument to the sub will be the
685 L<Test2::API::Context> instance that was created.
686
687 B<Note> Using this hook could have a huge performance impact.
688
689 The coderef you provide is returned and can be used to remove the hook later.
690
691 =item $hub->remove_context_init($sub);
692
693 This can be used to remove a context init hook.
694
695 =item $sub = $hub->add_context_release(sub { ... });
696
697 This allows you to add callbacks that will trigger every time a context for
698 this hub is released. The only argument to the sub will be the
699 L<Test2::API::Context> instance that was released. These will run in reverse
700 order.
701
702 B<Note> Using this hook could have a huge performance impact.
703
704 The coderef you provide is returned and can be used to remove the hook later.
705
706 =item $hub->remove_context_release($sub);
707
708 This can be used to remove a context release hook.
709
710 =item $hub->cull()
711
712 Cull any IPC events (and process them).
713
714 =item $pid = $hub->pid()
715
716 Get the process id under which the hub was created.
717
718 =item $tid = $hub->tid()
719
720 Get the thread id under which the hub was created.
721
722 =item $hud = $hub->hid()
723
724 Get the identifier string of the hub.
725
726 =item $ipc = $hub->ipc()
727
728 Get the IPC object used by the hub.
729
730 =item $hub->set_no_ending($bool)
731
732 =item $bool = $hub->no_ending
733
734 This can be used to disable auto-ending behavior for a hub. The auto-ending
735 behavior is triggered by an end block and is used to cull IPC events, and
736 output the final plan if the plan was 'no_plan'.
737
738 =item $bool = $hub->active
739
740 =item $hub->set_active($bool)
741
742 These are used to get/set the 'active' attribute. When true this attribute will
743 force C<< hub->finalize() >> to take action even if there is no plan, and no
744 tests have been run. This flag is useful for plugins that add follow-up
745 behaviors that need to run even if no events are seen.
746
747 =back
748
749 =head2 STATE METHODS
750
751 =over 4
752
753 =item $hub->reset_state()
754
755 Reset all state to the start. This sets the test count to 0, clears the plan,
756 removes the failures, etc.
757
758 =item $num = $hub->count
759
760 Get the number of tests that have been run.
761
762 =item $num = $hub->failed
763
764 Get the number of failures (Not all failures come from a test fail, so this
765 number can be larger than the count).
766
767 =item $bool = $hub->ended
768
769 True if the testing has ended. This MAY return the stack frame of the tool that
770 ended the test, but that is not guaranteed.
771
772 =item $bool = $hub->is_passing
773
774 =item $hub->is_passing($bool)
775
776 Check if the overall test run is a failure. Can also be used to set the
777 pass/fail status.
778
779 =item $hub->plan($plan)
780
781 =item $plan = $hub->plan
782
783 Get or set the plan. The plan must be an integer larger than 0, the string
784 'no_plan', or the string 'skip_all'.
785
786 =item $bool = $hub->check_plan
787
788 Check if the plan and counts match, but only if the tests have ended. If tests
789 have not ended this will return undef, otherwise it will be a true/false.
790
791 =back
792
793 =head1 THIRD PARTY META-DATA
794
795 This object consumes L<Test2::Util::ExternalMeta> which provides a consistent
796 way for you to attach meta-data to instances of this class. This is useful for
797 tools, plugins, and other extensions.
798
799 =head1 SOURCE
800
801 The source code repository for Test2 can be found at
802 F<http://github.com/Test-More/test-more/>.
803
804 =head1 MAINTAINERS
805
806 =over 4
807
808 =item Chad Granum E<lt>exodist@cpan.orgE<gt>
809
810 =back
811
812 =head1 AUTHORS
813
814 =over 4
815
816 =item Chad Granum E<lt>exodist@cpan.orgE<gt>
817
818 =back
819
820 =head1 COPYRIGHT
821
822 Copyright 2016 Chad Granum E<lt>exodist@cpan.orgE<gt>.
823
824 This program is free software; you can redistribute it and/or
825 modify it under the same terms as Perl itself.
826
827 See F<http://dev.perl.org/licenses/>
828
829 =cut