Acme-FSM
view release on metacpan or search on metacpan
t/TestSuite.pm view on Meta::CPAN
=over
=item I<$main::rc>
ARRAY of items FSM has just left behind (contents of I<$main::rc>);
=item I<\%blackboard>
A blackboard snapshot after FSM has been run;
That snapshotting means:
=over
=item *
all keys of I<$main::bb>, except special I<_> key, are copied;
=item *
all keys of I<$main::bb{_}>, exccept I<fst> key, are copied.
=back
That is, everything, except filtered goes in one HASH.
=back
If B<Test::More::is_deeply()> fails then a line in a test-unit where it
happened is hinted with B<AFSMTS_diag()>.
=cut
sub AFSMTS_deeply ( \@$ ) {
my( $expected, $descr ) = @_;
my $got = { };
$got->{$_} = $main::bb->{$_} foreach grep $_ ne q|_|, keys %$main::bb;
$got->{$_} = $main::bb->{_}{$_} foreach
grep $_ ne q|fst|, keys %{$main::bb->{_}};
unless( Test::More::is_deeply(
[ $main::rc, $got ], $expected, $descr )) {
AFSMTS_diag sprintf qq| at %s line %i.|, ( caller )[1,2];
AFSMTS_dump [ $main::rc ];
AFSMTS_dump [ $got ] }}
=item B<AFSMTS_wrap()>
use t::TestSuite qw/ :run /;
our( $rc, %st, $bb, %opts );
our( $stdout, $stderr );
AFSMTS_wrap;
AFSMTS_deeply @{[ ]}, 'again!';
TODO: {
local TODO = 'oops, not yet';
AFSMTS_wrap;
isnt $rc, "ALRM\n", 'success!';
}
Wraps B<connect()> and B<process()>.
Everything is got from I<main>.
Those are:
=over
=item I<$rc>
ARRAY;
storage for FSM return;
=item I<%st>
Status table;
=item I<$bb>
B<Acme::FSM> object;
An object is reZ<>B<connect>ed;
I<$bb{queue}> is created and set to empty ARRAY.
=item I<%opts>
A hash of options, those will be passed to constructor.
=back
I<STDOUT> and I<STDERR> are backed up in scalars;
those are saved in I<$main::stdout> and I<$main::stderr>.
I<STDERR> is output with B<AFSMTS_diag()> anyway.
However, it's trimmed to first 1024 bytes
(unless I<$t::TestSuite::NO_TRIM> is TRUE)
(it's not clear yet if those are 1024 bytes or characters).
Also, there's a timeout feature.
That timeout should be protected with TODO of B<Test::More>.
I<STDERR> is dumped too.
That timeout is implemented with B<alarm>ed B<eval>.
That B<eval> protects against B<die>s too.
=cut
sub AFSMTS_wrap ( ) {
open my $stdout_bak, q|>&|, \*STDOUT;
open my $stderr_bak, q|>&|, \*STDERR;
close STDOUT; open STDOUT, q|>|, \$main::stdout;
close STDERR; open STDERR, q|>|, \$main::stderr;
local $SIG{__DIE__} = sub {
alarm 0;
close STDOUT; open STDOUT, q|>&|, $stdout_bak;
close STDERR;
open STDERR, q|>&|, $stderr_bak };
do {
no warnings qw| once |;
$main::bb = Acme::FSM->connect( { %main::opts }, \%main::st ) };
$main::bb->{queue} = [ ];
my $rc = [ eval {
local $SIG{ALRM} = sub { die qq|ALRM\n| };
alarm 3;
$main::rc = [ $main::bb->process ];
alarm 0;
1 } ];
unless( @$rc ) {
# TODO:20121120224141:whynot: Make sure it's 1024 characters not bytes.
$main::stderr = substr $main::stderr || '', 0, 1024 unless $NO_TRIM;
$main::rc = [ $@ ] }
close STDERR; open STDERR, q|>&|, $stderr_bak;
close STDOUT; open STDOUT, q|>&|, $stdout_bak;
AFSMTS_diag $main::stderr }
=item B<AFSMTS_class_wrap()>
use t::Test::Suite qw/ :wraps /;
our( $rc, %st, $bb );
our( $stdout, $stderr );
AFSMTS_class_wrap @list;
Complete analogy of B<AFSMTS_wrap()> except B<process()> isn't called and
there's no timeout protection.
Also, there's I<$t::TestSuite::class_cheat>, what, if B<defined> is supposed
to be class name of B<A::F> descandant.
=cut
our $class_cheat;
sub AFSMTS_class_wrap ( @ ) {
open my $stdout_bak, q|>&|, \*STDOUT;
open my $stderr_bak, q|>&|, \*STDERR;
close STDOUT; open STDOUT, q|>|, \$main::stdout;
close STDERR; open STDERR, q|>|, \$main::stderr;
local $SIG{__DIE__} = sub {
close STDOUT; open STDOUT, q|>&|, $stdout_bak;
close STDERR; open STDERR, q|>&|, $stderr_bak;
AFSMTS_diag $main::stderr };
$main::bb = $class_cheat ?
eval qq|${class_cheat}->connect( \@_ )| :
Acme::FSM->connect( @_ );
close STDERR; open STDERR, q|>&|, $stderr_bak;
close STDOUT; open STDOUT, q|>&|, $stdout_bak;
AFSMTS_diag $main::stderr }
=item B<AFSMTS_object_wrap()>
use t::TestSuite qw/ :wraps /;
our( $rc, %st, $bb );
our( $stdout, $stderr );
AFSMTS_object_wrap $childof_A_F, @list;
Complete analogy of B<AFSMTS_wrap()> except B<process()> isn't called and
there's no timeout protection.
It's different from B<AFSMTS_class_wrap> that it goes with
object-construction.
That object goes as a first parameter, then comes list of items to process.
=cut
sub AFSMTS_object_wrap ( $@ ) {
my $obj = shift @_;
open my $stdout_bak, q|>&|, \*STDOUT;
open my $stderr_bak, q|>&|, \*STDERR;
close STDOUT; open STDOUT, q|>|, \$main::stdout;
close STDERR; open STDERR, q|>|, \$main::stderr;
local $SIG{__DIE__} = sub {
close STDOUT; open STDOUT, q|>&|, $stdout_bak;
close STDERR; open STDERR, q|>&|, $stderr_bak;
AFSMTS_diag $main::stderr };
$main::bb = $obj->connect( @_ );
close STDERR; open STDERR, q|>&|, $stderr_bak;
close STDOUT; open STDOUT, q|>&|, $stdout_bak;
AFSMTS_diag $main::stderr }
=item B<AFSMTS_method_wrap()>
use t::TestSuite qw/ :wraps /;
our( $rc, %st, $bb );
our( $stdout, $stderr );
AFSMTS_method_wrap 'some_method', @list;
Complete analogy of B<AFSMTS_wrap()> except instead of B<process()> some
requested I<$method> is B<can>ed first, than invoked with I<@list> over
I<$main::bb> in list context.
What is returned is placed in I<$main::rc> wrapped in ARRAY.
If I<$method> returned one element then ARRAY is replaced with scalar.
=cut
sub AFSMTS_method_wrap ( $@ ) {
open my $stdout_bak, q|>&|, \*STDOUT;
open my $stderr_bak, q|>&|, \*STDERR;
close STDOUT; open STDOUT, q|>|, \$main::stdout;
close STDERR; open STDERR, q|>|, \$main::stderr;
my $method = $main::bb->can( shift @_ );
my $rc = [ eval {
local $SIG{ALRM} = sub { die qq|ALRM\n| };
alarm 3;
$main::rc = [ $main::bb->$method( @_ ) ];
alarm 0;
1 } ];
alarm 0;
unless( @$rc ) {
$main::stderr = substr $main::stderr // '', 0, 1024 unless $NO_TRIM;
$main::rc = [ $@ ] }
$main::rc = $main::rc->[0] if 1 == @$main::rc;
close STDERR; open STDERR, q|>&|, $stderr_bak;
close STDOUT; open STDOUT, q|>&|, $stdout_bak;
AFSMTS_diag $main::stderr }
=item B<AFSMTS_croakson> 'actual description'
use t::TestSuite qw/ :diag /;
$rc = eval { die 'as expected'; 1 };
is !$rc, 0, AFSMTS_croakson 'surprise';
That will add I<$@> (with newlines replaced with spaces) to otherwise dumb
description, like this:
ok 1 - croaks on (surprise) (as expected at test-unit.t line 12 )
=cut
sub AFSMTS_croakson ( $ ) {
my $eval_msg = $@;
$eval_msg =~ tr{\n}{ };
return sprintf q|croaks on (%s) (%s)|, shift @_, $eval_msg }
=item B<AFSMTS_shift()>
our %opts;
our @inbase = ( qw/ a b c /, undef );
our @input = @inbase;
$opts{source} = \&AFSMTS_shift;
AFSMTS_wrap;
Quiet generic implementation of I<{source}> code.
Uses script globals:
=over
( run in 0.936 second using v1.01-cache-2.11-cpan-39bf76dae61 )