summaryrefslogtreecommitdiff
path: root/cpan/Test-Simple/lib/Test/Stream/Tester.pm
diff options
context:
space:
mode:
authorChad Granum <chad.granum@dreamhost.com>2014-11-11 08:28:24 -0800
committerFather Chrysostomos <sprout@cpan.org>2014-11-11 13:02:04 -0800
commit518760d966a945c1dd7f5c552bdcf8fa73433ee3 (patch)
tree64e379bcd59a27a23ec038e8ae6e687ae51d8f06 /cpan/Test-Simple/lib/Test/Stream/Tester.pm
parent3246b00e7b4d4d350fa31848e41d8de6f2a53735 (diff)
downloadperl-518760d966a945c1dd7f5c552bdcf8fa73433ee3.tar.gz
Revert "Update Test-Simple to CPAN version 1.001009"
This reverts commit 3709f1d4bd0179938a418d9337449fdf20a783bc. We are using the alphas in blead currently, not stable, this update squashed that.
Diffstat (limited to 'cpan/Test-Simple/lib/Test/Stream/Tester.pm')
-rw-r--r--cpan/Test-Simple/lib/Test/Stream/Tester.pm725
1 files changed, 725 insertions, 0 deletions
diff --git a/cpan/Test-Simple/lib/Test/Stream/Tester.pm b/cpan/Test-Simple/lib/Test/Stream/Tester.pm
new file mode 100644
index 0000000000..80e45bd0d5
--- /dev/null
+++ b/cpan/Test-Simple/lib/Test/Stream/Tester.pm
@@ -0,0 +1,725 @@
+package Test::Stream::Tester;
+use strict;
+use warnings;
+
+use Test::Builder 1.301001;
+use Test::Stream;
+use Test::Stream::Util qw/try/;
+
+use B;
+
+use Scalar::Util qw/blessed reftype/;
+use Test::Stream::Carp qw/croak carp/;
+
+use Test::Stream::Tester::Checks;
+use Test::Stream::Tester::Checks::Event;
+use Test::Stream::Tester::Events;
+use Test::Stream::Tester::Events::Event;
+
+use Test::Stream::Toolset;
+use Test::Stream::Exporter;
+default_exports qw{
+ intercept grab
+
+ events_are
+ check event directive
+};
+
+default_export dir => \&directive;
+Test::Stream::Exporter->cleanup;
+
+sub grab {
+ require Test::Stream::Tester::Grab;
+ return Test::Stream::Tester::Grab->new;
+}
+
+our $EVENTS;
+sub check(&) {
+ my ($code) = @_;
+
+ my $o = B::svref_2object($code);
+ my $st = $o->START;
+ my $file = $st->file;
+ my $line = $st->line;
+
+ local $EVENTS = Test::Stream::Tester::Checks->new($file, $line);
+
+ my @out = $code->($EVENTS);
+
+ if (@out) {
+ if ($EVENTS->populated) {
+ carp "sub used in check(&) returned values, did you forget to prefix an event with 'event'?"
+ }
+ else {
+ croak "No events were produced by sub in check(&), but the sub returned some values, did you forget to prefix an event with 'event'?";
+ }
+ }
+
+ return $EVENTS;
+}
+
+sub event($$) {
+ my ($type, $data) = @_;
+
+ croak "event() cannot be used outside of a check { ... } block"
+ unless $EVENTS;
+
+ my $etypes = Test::Stream::Context->events;
+ croak "'$type' is not a valid event type!"
+ unless $etypes->{$type};
+
+ my $props;
+
+ croak "event() takes a type, followed by a hashref"
+ unless ref $data && reftype $data eq 'HASH';
+
+ # Make a copy
+ $props = { %{$data} };
+
+ my @call = caller(0);
+ $props->{debug_package} = $call[0];
+ $props->{debug_file} = $call[1];
+ $props->{debug_line} = $call[2];
+
+ $EVENTS->add_event($type, $props);
+ return ();
+}
+
+sub directive($;$) {
+ my ($directive, @args) = @_;
+
+ croak "directive() cannot be used outside of a check { ... } block"
+ unless $EVENTS;
+
+ croak "No directive specified"
+ unless $directive;
+
+ if (!ref $directive) {
+ croak "Directive '$directive' requires exactly 1 argument"
+ unless (@args && @args == 1) || $directive eq 'end';
+ }
+ else {
+ croak "directives must be a predefined name, or a sub ref"
+ unless reftype($directive) eq 'CODE';
+ }
+
+ $EVENTS->add_directive(@_);
+ return ();
+}
+
+sub intercept(&) {
+ my ($code) = @_;
+
+ my @events;
+
+ my ($ok, $error) = try {
+ Test::Stream->intercept(
+ sub {
+ my $stream = shift;
+ $stream->listen(
+ sub {
+ shift; # Stream
+ push @events => @_;
+ }
+ );
+ $code->();
+ }
+ );
+ };
+
+ die $error unless $ok || (blessed($error) && $error->isa('Test::Stream::Event'));
+
+ return \@events;
+}
+
+sub events_are {
+ my ($events, $checks, $name) = @_;
+
+ croak "Did not get any events"
+ unless $events;
+
+ croak "Did not get any checks"
+ unless $checks;
+
+ croak "checks must be an instance of Test::Stream::Tester::Checks"
+ unless blessed($checks)
+ && $checks->isa('Test::Stream::Tester::Checks');
+
+ my $ctx = context();
+
+ # use $_[0] directly so that the variable used in the method call can be undef'd
+ $events = $_[0]->finish
+ if blessed($events)
+ && $events->isa('Test::Stream::Tester::Grab');
+
+ $events = Test::Stream::Tester::Events->new(@$events)
+ if ref($events)
+ && reftype($events) eq 'ARRAY';
+
+ croak "'$events' is not a valid set of events."
+ unless $events
+ && blessed($events)
+ && $events->isa('Test::Stream::Tester::Events');
+
+ my ($ok, @diag) = $checks->run($events);
+
+ $ctx->ok($ok, $name, \@diag);
+ return $ok;
+}
+
+1;
+
+__END__
+
+=head1 NAME
+
+Test::Stream::Tester - Tools for validating the events produced by your testing
+tools.
+
+=head1 DESCRIPTION
+
+There are tools to validate your code. This library provides tools to validate
+your tools!
+
+=head1 SYNOPSIS
+
+ use Test::More;
+ use Test::Stream::Tester;
+
+ events_are(
+ # Capture all the events within the block
+ intercept {
+ ok(1, "pass");
+ ok(0, "fail");
+ diag("xxx");
+ },
+
+ # Describe what we expect to see
+ check {
+ event ok => {bool => 1, name => 'pass'};
+ event ok => {
+ bool => 0,
+ name => 'fail',
+
+ # Ignores any fields in the result we don't list
+ # real_bool, line, file, tool_package, tool_name, etc...
+
+ # Diagnostics generated by a test are typically linked to those
+ # results (new and updated tools only) They can be validated.
+ diag => qr/^Failed test /,
+ };
+ event diag => {message => 'xxx'};
+ directive 'end'; # enforce that there are no more results
+ },
+
+ "This is the name of our test"
+ );
+
+ done_testing;
+
+=head2 GRAB WITH NO ADDED STACK
+
+ use Test::More;
+ use Test::Stream::Tester;
+
+ # Start capturing events. We use grab() instead of intercept {} to avoid
+ # adding stack frames.
+ my $grab = grab();
+
+ # Generate some events.
+ ok(1, "pass");
+ ok(0, "fail");
+ diag("xxx");
+
+ # Stop capturing events, and validate the ones recieved.
+ events_are(
+ $grab,
+ check {
+ event ok => { bool => 1, name => 'pass' };
+ event ok => { bool => 0, name => 'fail' };
+ event diag => { message => 'xxx' };
+ directive 'end';
+ },
+ 'Validate our Grab results';
+ );
+
+ # $grab is now undef, it no longer exists.
+ is($grab, undef, '$grab was destroyed for us.');
+
+ ok(!$success, "Eval did not succeed, BAIL_OUT killed the test");
+
+ # Make sure we got the event as an exception
+ isa_ok($error, 'Test::Stream::Event::Bail');
+
+ done_testing
+
+=head1 EXPORTS
+
+=over 4
+
+=item $events = intercept { ... }
+
+=item $events = intercept(sub { ... })
+
+Capture the L<Test::Builder::Event> objects generated by tests inside the block.
+
+=item events_are(\@events, $check)
+
+=item events_are(\@events, $check, $name)
+
+=item events_are($events, $check)
+
+=item events_are($events, $check, $name)
+
+=item events_are($grab, $check)
+
+=item events_are($grab, $check, $name)
+
+The first argument may be either an arrayref of L<Test::Stream::Event> objects,
+an L<Test::Stream::Tester::Grab> object, or an L<Test::Stream::Tester::Events>
+object. C<intercept { ... }> can be used to capture events within a block of
+code, including plans such as C<skip_all>, and things that normally kill the
+test like C<BAIL_OUT()>.
+
+The second argument must be an L<Test::Stream::Tester::Checks> object.
+Typically these are generated using C<check { ... }>.
+
+The third argument is the name of the test, it is optional, but highly
+recommended.
+
+=item $checks = check { ... };
+
+Produce an array of expected events for use in events_are.
+
+ my $check = check {
+ event ok => { ... };
+ event diag => { ... };
+ directive 'end';
+ };
+
+If the block passed to check returns anything at all it will warn you as this
+usually means you forgot to use the C<event> and/or C<diag> functions. If it
+returns something AND has no events it will be fatal.
+
+C<event()> and C<directive()> both return nothing, this means that if you use
+them alone your codeblock will return nothing.
+
+=item event TYPE => { ... };
+
+Define an event and push it onto the list that will be returned by the
+enclosing C<check { ... }> block. Will fail if run outside a check block. This
+will fail if you give it an invalid event type.
+
+If you wish to acknowledge the event, but not check anything you may simply
+give it an empty hashref.
+
+The line number where the event was generated is recorded for helpful debugging
+in event of a failure.
+
+B<CAVEAT> The line number is inexact because of the way perl records it. The
+line number is taken from C<caller>.
+
+=item dir 'DIRECTIVE';
+
+=item dir DIRECTIVE => 'ARG';
+
+=item dir sub { ... };
+
+=item dir sub { ... }, $arg;
+
+=item directive 'DIRECTIVE';
+
+=item directive DIRECTIVE => 'ARG';
+
+=item directive sub { ... };
+
+=item directive sub { ... }, $arg;
+
+Define a directive and push it onto the list that will be returned by the
+enclosing C<check { ... }> block. This will fail if run outside of a check
+block.
+
+The first argument must be either a codeblock, or one of the name of a
+predefined directive I<See the directives section>.
+
+Coderefs will be given 3 arguments:
+
+ sub {
+ my ($checks, $events, $arg) = @_;
+ ...
+ }
+
+C<$checks> is the L<Test::Stream::Tester::Checks> object. C<$events> is the
+L<Test::Stream::Tester::Events> object. C<$arg> is whatever argument you passed
+via the C<directive()> call.
+
+Most directives will act on the C<$events> object to remove or alter events.
+
+=back
+
+=head1 INTERCEPTING EVENTS
+
+ my $events = intercept {
+ ok(1, "pass");
+ ok(0, "fail");
+ diag("xxx");
+ };
+
+Any events generated within the block will be intercepted and placed inside
+the C<$events> array reference.
+
+=head2 EVENT TYPES
+
+All events will be subclasses of L<Test::Builder::Event>
+
+=over 4
+
+=item L<Test::Builder::Event::Ok>
+
+=item L<Test::Builder::Event::Note>
+
+=item L<Test::Builder::Event::Diag>
+
+=item L<Test::Builder::Event::Plan>
+
+=item L<Test::Builder::Event::Finish>
+
+=item L<Test::Builder::Event::Bail>
+
+=item L<Test::Builder::Event::Subtest>
+
+=back
+
+=head1 VALIDATING EVENTS
+
+You can validate events by hand using traditional test tools such as
+C<is_deeply()> against the $events array returned from C<intercept()>. However
+it is easier to use C<events_are()> paried with C<checks> objects build using
+C<checks { ... }>.
+
+ events_are(
+ intercept {
+ ok(1, "pass");
+ ok(0, "fail");
+ diag("xxx");
+ },
+
+ check {
+ event ok => { bool => 1, name => 'pass' };
+ event ok => { bool => 0, name => 'fail' };
+ event diag => {message => 'xxx'};
+ directive 'end';
+ },
+
+ "This is the name of our test"
+ );
+
+=head2 WHAT DOES THIS BUY ME?
+
+C<checks { ... }>, C<event()>, and C<directive()>, work together to produce a
+nested set of objects to represent what you want to see. This was chosen over a
+hash/list system for 2 reasons:
+
+=over 4
+
+=item Better Diagnostics
+
+Whenever you use C<checks { ... }>, C<events()>, and C<directive()> it records
+the filename and line number where they are called. When a test fails the
+diagnostics will include this information so that you know where the error
+occured. In a hash/list based system this information is not available.
+
+A hash based system is not practical as you may generate several events of the
+same type, and in a hash duplicated keys are squashed (last one wins).
+
+A list based system works, but then a failure reports the index of the failure,
+this requires you to manually count events to find the correct one. Originally
+I tried letting you specify an ID for the events, but this proved annoying.
+
+Ultimately I am very happy with the diagnostics this allows. It is very nice to
+see what is essentially a simple trace showing where the event and check were
+generated. It also shows you the items leading to the failure in the event of
+nested checks.
+
+=item Loops and other constructs
+
+In a list based system you are limited in what you can produce. You can
+generate the list in advance, then pass it in, but this is hard to debug.
+Alternatively you can use C<map> to produce repeated events, but this is
+equally hard to debug.
+
+This system lets you call C<event()> and C<directive()> in loops directly. It
+also lets you write functions that produce them based on input for reusable
+test code.
+
+=back
+
+=head2 VALIDATING FIELDS
+
+The hashref against which events are checked is composed of keys, and values.
+The values may be regular values, which are checked for equality with the
+corresponding property of the event object. Alternatively you can provide a
+regex to match against, or an arrayref of regexes (each one must match).
+
+=over 4
+
+=item field => 'exact_value',
+
+The specified field must exactly match the given value, be it number or string.
+
+=item field => qr/.../,
+
+The specified field must match the regular expression.
+
+=item field => [qr/.../, qr/.../, ...],
+
+The value of the field must match ALL the regexes.
+
+=item field => sub { ... }
+
+Specify a sub that will validate the value of the field.
+
+ foo => sub {
+ my ($key, $val) = @_;
+
+ ...
+
+ # Return true (valid) or false, and any desired diagnostics messages.
+ return($bool, @diag);
+ },
+
+=back
+
+=head2 WHAT FIELDS ARE AVAILABLE?
+
+This is specific to the event type. All events inherit from
+L<Test::Builder::Event> which provides a C<summary()> method. The C<summary()>
+method returns a list of key/value pairs I<(not a reference!)> with all fields
+that are for public consumption.
+
+For each of the following modules see the B<SUMMARY FIELDS> section for a list
+of fields made available. These fields are inherited when events are
+subclassed, and all events have the summary fields present in
+L<Test::Builder::Event>.
+
+=over 4
+
+=item L<Test::Builder::Event/"SUMMARY FIELDS">
+
+=item L<Test::Builder::Event::Ok/"SUMMARY FIELDS">
+
+=item L<Test::Builder::Event::Note/"SUMMARY FIELDS">
+
+=item L<Test::Builder::Event::Diag/"SUMMARY FIELDS">
+
+=item L<Test::Builder::Event::Plan/"SUMMARY FIELDS">
+
+=item L<Test::Builder::Event::Finish/"SUMMARY FIELDS">
+
+=item L<Test::Builder::Event::Bail/"SUMMARY FIELDS">
+
+=item L<Test::Builder::Event::Subtest/"SUMMARY FIELDS">
+
+=back
+
+=head2 DIRECTIVES
+
+Directives give you a chance to alter the list of events part-way through the
+check, or to make the check skip/ignore events based on conditions.
+
+=head3 skip
+
+Skip will skip a specific number of events at that point in the check.
+
+=over 4
+
+=item directive skip => $num;
+
+ my $events = intercept {
+ ok(1, "foo");
+ diag("XXX");
+
+ ok(1, "bar");
+ diag("YYY");
+
+ ok(1, "baz");
+ diag("ZZZ");
+ };
+
+ events_are(
+ $events,
+ ok => { name => "foo" },
+
+ skip => 1, # Skips the diag 'XXX'
+
+ ok => { name => "bar" },
+
+ skip => 2, # Skips the diag 'YYY' and the ok 'baz'
+
+ diag => { message => 'ZZZ' },
+ );
+
+=back
+
+=head3 seek
+
+When turned on (true), any unexpected events will be skipped. You can turn
+this on and off any time by using it again with a false argument.
+
+=over 4
+
+=item directive seek => $BOOL;
+
+ my $events = intercept {
+ ok(1, "foo");
+
+ diag("XXX");
+ diag("YYY");
+
+ ok(1, "bar");
+ diag("ZZZ");
+
+ ok(1, "baz");
+ };
+
+ events_are(
+ $events,
+
+ seek => 1,
+ ok => { name => "foo" },
+ # The diags are ignored, it will seek to the next 'ok'
+ ok => { name => "bar" },
+
+ seek => 0,
+
+ # This will fail because the diag is not ignored anymore.
+ ok => { name => "baz" },
+ );
+
+=back
+
+=head3 end
+
+Used to say that there should not be any more events. Without this any events
+after your last check are simply ignored. This will generate a failure if any
+unchecked events remain.
+
+=over 4
+
+=item directive 'end';
+
+=back
+
+=head1 SEE ALSO
+
+=over 4
+
+=item L<Test::Tester> *Deprecated*
+
+A nice, but very limited tool for testing 'ok' results.
+
+=item L<Test::Builder::Tester> *Deprecated*
+
+The original test tester, checks TAP output as giant strings.
+
+=back
+
+=encoding utf8
+
+=head1 SOURCE
+
+The source code repository for Test::More can be found at
+F<http://github.com/Test-More/test-more/>.
+
+=head1 MAINTAINER
+
+=over 4
+
+=item Chad Granum E<lt>exodist@cpan.orgE<gt>
+
+=back
+
+=head1 AUTHORS
+
+The following people have all contributed to the Test-More dist (sorted using
+VIM's sort function).
+
+=over 4
+
+=item Chad Granum E<lt>exodist@cpan.orgE<gt>
+
+=item Fergal Daly E<lt>fergal@esatclear.ie>E<gt>
+
+=item Mark Fowler E<lt>mark@twoshortplanks.comE<gt>
+
+=item Michael G Schwern E<lt>schwern@pobox.comE<gt>
+
+=item 唐鳳
+
+=back
+
+=head1 COPYRIGHT
+
+There has been a lot of code migration between modules,
+here are all the original copyrights together:
+
+=over 4
+
+=item Test::Stream
+
+=item Test::Stream::Tester
+
+Copyright 2014 Chad Granum E<lt>exodist7@gmail.comE<gt>.
+
+This program is free software; you can redistribute it and/or
+modify it under the same terms as Perl itself.
+
+See F<http://www.perl.com/perl/misc/Artistic.html>
+
+=item Test::Simple
+
+=item Test::More
+
+=item Test::Builder
+
+Originally authored by Michael G Schwern E<lt>schwern@pobox.comE<gt> with much
+inspiration from Joshua Pritikin's Test module and lots of help from Barrie
+Slaymaker, Tony Bowden, blackstar.co.uk, chromatic, Fergal Daly and the perl-qa
+gang.
+
+Idea by Tony Bowden and Paul Johnson, code by Michael G Schwern
+E<lt>schwern@pobox.comE<gt>, wardrobe by Calvin Klein.
+
+Copyright 2001-2008 by Michael G Schwern E<lt>schwern@pobox.comE<gt>.
+
+This program is free software; you can redistribute it and/or
+modify it under the same terms as Perl itself.
+
+See F<http://www.perl.com/perl/misc/Artistic.html>
+
+=item Test::use::ok
+
+To the extent possible under law, 唐鳳 has waived all copyright and related
+or neighboring rights to L<Test-use-ok>.
+
+This work is published from Taiwan.
+
+L<http://creativecommons.org/publicdomain/zero/1.0>
+
+=item Test::Tester
+
+This module is copyright 2005 Fergal Daly <fergal@esatclear.ie>, some parts
+are based on other people's work.
+
+Under the same license as Perl itself
+
+See http://www.perl.com/perl/misc/Artistic.html
+
+=item Test::Builder::Tester
+
+Copyright Mark Fowler E<lt>mark@twoshortplanks.comE<gt> 2002, 2004.
+
+This program is free software; you can redistribute it
+and/or modify it under the same terms as Perl itself.
+
+=back