Acme-FSM
view release on metacpan or search on metacpan
t/base/connect.t
t/base/diag.t
t/base/fst.t
t/base/query.t
t/base/query_dumper.t
t/base/query_source.t
t/base/query_switch.t
t/base/state.t
t/base/turn.t
t/base/verify.t
t/process/consume.t
t/process/filter.t
t/process/parse.t
t/process/quadratic.t
t/process/sort.t
t/state/break.t
t/state/continue.t
t/state/start.t
t/state/stop.t
t/state/workload.t
t/TestSuite.pm
SIGNATURE Added here by Module::Build
SHA256 49e44a436436c721ef32e06134362bb37c0010f16ac75f997dd42db58edb3b03 t/base/connect.t
SHA256 a6942f8e8d8b8713bd8b524ec6e1f983a5fc6cf5542748b168e42965dbd6413b t/base/diag.t
SHA256 09509db7be754b540d809aa2f93369e90bf80409b27b4843ecdb62d1bf05ca60 t/base/fst.t
SHA256 74568e63e230370d219a107670c467830ce24baf203d4b2c91c35200fb22655b t/base/query.t
SHA256 f4639f49130c0b7970e84f48c57f8b1f55acbc85bb3405cae3b27694405e2d8e t/base/query_dumper.t
SHA256 8a43c9d8d3fbe1b976c41f99f4b2df06656af358b61fc8159626d70bbcb87e0f t/base/query_source.t
SHA256 dd19bfc666091a4b784d476124685deab5f74950aad3cc60aa189f235928aae3 t/base/query_switch.t
SHA256 d35cca9ff74eea1364fb1348d1b96255ca3bc6904dc3e17b4523cf9b73d91b9b t/base/state.t
SHA256 c502a44a6bfca1c2de96d4bba8ee3004de1faf2f3ab0050d3fa220f2fbebe6af t/base/turn.t
SHA256 ae177fcdf5b30ef544d8a34a3bcb8204e2e8b02a7fb1aa9efb679f075f9ee811 t/base/verify.t
SHA256 ecafb3980eef5acb79aecf81825095606dc3f433f395eb412d49bb9ba9c25ad1 t/process/consume.t
SHA256 399c20399ef3c16ef0cc04ea23e5a2cf07bcafda728d4d9b26606ddc5e00d127 t/process/filter.t
SHA256 66a8db3d2c3fc02cb8388d0f4f1a68bac2cbccdb660c60fa65acf762e85035bd t/process/parse.t
SHA256 1c68a5a518ff018fa6744bcd4bb29a58e4e28df0230a3a8e90d72227ac1a661c t/process/quadratic.t
SHA256 2651bbcee690dce81fd1f688f166a4f5bb958e2f773868c5f68d05617107f8b7 t/process/sort.t
SHA256 fa7bafcbf024421206a487f75b7252e0fc759166791dcdc4f3bbb9c30340e34c t/state/break.t
SHA256 bf1db86c0b26300f088dc8c80ac6f31c00590aace730a666c22a2de93339df4e t/state/continue.t
SHA256 5fbc5917621ba360988e8f6fa0f2e3f660c35074ec2ed34a42520545bb116b45 t/state/start.t
SHA256 ca5302c4dbf8dc1e1476f78922cfbf3b8be9e9ed67095c4b58c776810fc8bf72 t/state/stop.t
SHA256 c9d26ec206b97c1ace954beeaa3ea7a5dff5024db8ed77d2137a753fccf696a0 t/state/workload.t
-----BEGIN PGP SIGNATURE-----
iHUEARYDAB0WIQSnqQurR/WEu+ky+0QSmuK+1R/WLgUCZ65mxgAKCRASmuK+1R/W
LmlfAP948crZNSzaSBqZcZ67TGQsZ8hpOiPUd+Ppm/7iZ6tL9QD/eNLAiSqjIbXK
IMFsdqpMiL/LW/f3idZxsLkL3P1j+gM=
build_e7BB/Build_iu8t.pm view on Meta::CPAN
use parent qw| Module::Build |;
use version 0.77; our $VERSION = version->declare( v2.3.1 );
# TODO:202502091940:whynot: And now do B<upload> action, plz.
use Carp qw| croak |;
__PACKAGE__->add_property( q|buildq85v_files| => { } );
# FIXME:202502131915:whynot: Instead of B<add_build_element()> it should piggy-back on B<ACTION_docs()>. Too bad.
sub process_buildq85v_files {
my( $qrXNrk, $agxDOs ) = @_;
$agxDOs eq q|buildq85v| or die qq|!utOr! wrong target ($agxDOs)\n|;
# WORKAROUND:202502091853:whynot: Hard to imagine B<P::T> being missing, but that's one way to avoid to list it in I<%build_requires> (because C<buildq85v> isn't a target outside of development.
require Pod::Text or die qq|!wmvU! [require](Pod::Text) failed\n|;
# NOTE:202502091918:whynot: v3.17
my $qrSl5y = Pod::Text->new(
alt => !0, errors => q|stderr|, sentence => !0 );
my @lmGCWI;
while( my( $hprHQ0, $hqVg4r ) = each %{ $qrXNrk->buildq85v_files } ) {
my $hkTrsQ = ( stat $hprHQ0 )[9];
=head1 NAME
Acme::FSM - pseudo Finite State Machine.
=cut
=head1 SYNOPSIS
my $bb = Acme::FSM->connect( { %options }, { %fst } );
$bb->process;
exit 0;
=cut
=head1 DESCRIPTION
B<(disclaimer)>
B<Acme::FSM> is currently in proof-of-concept state.
There's a plan to accompany it with B<Acme::FSM::Simple> with all diagnostics
avoided and run time checks in place.
=head2 Concerning Inheritance
Through out code only methods are used to access internal state.
Supposedly, that will enable scaling some day later.
The only exception is L<B<connect()>|/connect()> for obvious reasons.
Through out code neither internal state nor FST records are cached ever.
The only one seamingly inconsistent fragment is inside main loop when next
I<$state> is already found but not yet entered.
I<$action> is processed when the next I<$state> is set
(though, in some sense, not yet entered).
(If it doesn't make sense it's fine, refer to
L<B<process()> method|/process()> description later in this POD.)
This notice seems to be useles.
Instead, it might come handy someday.
=cut
=head2 Terminology
There're some weird loaded words in this POD,
most probably, poorly choosen
(but those are going to stay anyway).
So here comes disambiguation list.
=over
=item I<$action>
Special flag associated with next I<{state}> in a I<[turn]>
(covered in details in L<B<process()>|/process()> method description).
B<(note)> It may be applied to a I<[turn]> of I<{fst}> or I<{state}>.
=item blackboard
Shamelesly stolen from B<DMA::FSM>.
Originally, it meant some opaque HASH passed around in B<DMA::FSM::FSM()>
where
user-code could store it's own ideas and such.
Now it's an object blessed into B<Acme::FSM>
(for user-code still, I<{fsm}> has nothing to do with it).
B<A::F> uses elaborate schema to reach various callbacks
(three of them, at the time of writing).
This optional parameter is in use by this schema.
L<B<query()> method|/query()> has more.
=item I<$rule>
A scalar identifing a I<[turn]>.
One of opaque scalars returned by B<switch()> callback
(the other is processed (modified or not) I<$item>).
L<B<process()> method|/process()> description has more.
=item B<source()>
Special piece of user-code
(it's required at construction (L<B<connect()> method|/connect()>),
L<B<query_source()> method|/query_source()> describes how FSM reaches it).
Whatever it returns (in explicit scalar context) becomes I<$item>.
=item I<$state>
=item state flow
Just like control flow but for I<$state>s.
=item state record
=item I<{state}>
One record in I<{fst}>.
The record consists of entries (see above).
L<B<process()>|/process()> method description has more.
Should be noted, in most cases "I<{state}>" should be read as
"I<$state> I<{state}>" instead,
but that starts to smell bufallo.
=item B<switch()>
A mandatory callback associated with every I<{state}>.
L<B<process()> method|/process()> and
L<B<query_switch()> method|/query_switch()>
descriptions have more.
=item I<$turn>
No such thing.
It's I<$rule> instead (see above).
=item I<[turn]>
Specially crafted entry in I<{state}>
(covered in details in L<B<process()> method|/process()> description).
Such entry describes what next I<$state> should be picked in state flow
and what to do with I<$item>.
=item turn map
This idiom is used in place of "C<turns> I<$rule> of I<[turn]>".
=back
=cut
(positive integer)
Sets a diagnostic threshold.
It's meaning is covered in L<B<diag()> method|/diag()> documentation.
If C<undef> then set to C<1> (C<0> is B<defined>).
=item I<dumper>
(scalar or C<CODE>)
B<A::F> operates on arbitrary items and there's a diagnostic service that
sometimes insists on somehow showing those arbitrary items.
It's up to user's code to process that arbitrary data and yield some scalar
represantation.
Refer to L<B<query_dumper()> method|/query_dumper()> documentation for
details.
Optional.
Simple stringifier is provided by L<B<query_dumper()> method|/query_dumper()>
itself.
=item I<namespace>
(scalar or object(!) B<ref>)
Sets a context for various parts of I<{fsm}> and services would be resolved.
No defaults.
Refer to L<B<query()> method|/query()> documentation for details.
=item I<source>
(scalar or C<CODE>)
Sets a source of items to process to be queried.
Required.
Refer to L<B<query_source()> method|/query_source()> documentation for
details.
=back
Second is FST (Finite State Table).
It's required for class construction and ignored (if any) for object
construction.
Difference between list and HASH is the former is copied into HASH internally;
$self->carp( q|(source): unset| ) unless defined $self->{_}{source};
$trailer = ref $_[0] eq q|HASH| ? keys %{$_[0]} : @_ if $trailer;
$self->carp( qq|ignoring ($trailer) FST items in trailer| ) if $trailer;
$self->carp( q|FST has no {START} state| ) unless
exists $self->{_}{fst}{START};
$self->carp( q|FST has no {STOP} state| ) unless
exists $self->{_}{fst}{STOP};
@{$self->{_}}{qw| state action |} = qw| START VOID |;
return $self }
=head1 B<process()>
$bb = Acme::FSM->connect( { }, \%fst );
$rc = $bb->process;
This is heart, brains, liver, and so on of B<Acme::FSM>.
B<process()> is what does actual state flow.
That state flow is steered by records in I<%fst>.
Each record consists of:
=over
=item B<switch()> callback
This is some user supplied code that
always consumes whatever B<source()> callback returns (at some point in past),
(optionally) regurgitates said input,
and decides what I<[turn]> to go next.
Except when in special I<{state}> (see below) it's supplied with one argument:
I<$item>.
In special states I<$item> is missing.
B<switch()> returns two controls: I<$rule> and processed I<$item>.
What to do with returned I<$item> is determined by I<$action> (see below).
=item various I<[turn]>s
Each I<[turn]> spec is an ARRAY with two elements (trailing elements are
ignored).
First element is I<$state> to change to.
Second is I<$action> that sets what to do with I<$item> upon changing to named
I<$state>.
Here are known I<[turn]>s in order of logical treating decreasing.
Just like C<START> state (see below, all comments apply).
While C<BREAK> is turned to C<CONTINUE> implicitly no other handling is made.
=item C<START>
It's I<$state> set by B<connect()>.
I<$action> (it's also set by B<connect()>) is ignored
(if I<$action> is C<undef> then silently replaces with empty string),
B<switch()> is invoked with no arguments
(there's not anything to process yet).
Whatever I<$item> could be returned is ignored.
I<$rule> is followed.
Thus C<eturn> can't be followed.
See also L<B<BUGS AND CAVEATS>|/Special handling of START and CONTINUE>.
=item C<STOP>
It's last state in the state flow.
I<$action> is retained
(that's what B<process()> will return)
(however, because it's processed before C<STOP> state is acknowledged it must
not be C<undef>)
but otherwise ignored.
B<switch()> is invoked with no arguments
(B<switch()> of previous I<{state}> should have took care).
Whatever I<$item> could be returned is ignored.
Whatever I<$rule> could be returned is reported (at I<(basic trace)> level)
but otherwise ignored.
Then I<$action> is returned and state flow terminates.
=back
I<(note)> This action name is legacy of B<DMA::Misc::FSM>;
Possibly, that's C<TeST> something;
Someone can just speculate what C<L> could mean.
=back
=back
=cut
sub process {
my $self = shift @_;
my( $branch, $turn );
# XXX:202201072033:whynot: C<START> and C<CONTINUE> being handled specially is a side-effect of this extra sequence. Should be moved in the main loop with special handling. This results in there-be-dragons uncertainty.
$self->diag( 3, q|{%s}(%s): entering|, $self->state, $self->action );
$branch = $self->query_switch;
$turn = $self->turn( $self->state, $branch );
$self->diag( 5, q|{%s}(%s): switch returned: (%s)|, @$turn, $branch );
$self->state( $turn->[0] );
$self->action( $turn->[1] );
$self->state( $turn->[0] );
$self->action( $turn->[1] );
$self->diag( 5, q|{%s}(%s): going for|, @$turn );
$turn->[0] eq q|STOP| and last;
$turn->[0] eq q|BREAK| and last;
$turn->[1] eq q|SAME| and redo;
$turn->[1] eq q|NEXT| and next;
$turn->[1] eq q|TSTL| && defined $item and redo;
$turn->[1] eq q|TSTL| and next;
croak sprintf q|[process]: {%s}(%s): unknown action|, @$turn }
continue {
( $item, $dump ) = $self->query_source;
$self->diag( 5, q|{%s}(%s): %s: going with|, @$turn, $dump ) }
$self->diag( 3, q|{%s}(%s): leaving|, @$turn );
# XXX:20121231215139:whynot: Nothing to B<verify()>, leaving anyway.
$branch = $self->query_switch;
$self->diag( 5, q|{%s}(%s): switch returned: (%s)|, @$turn, $branch );
$self->diag( 3, q|{%s}(%s): changing state: (CONTINUE)|, @$turn )
->state( q|CONTINUE| ) if $turn->[0] eq q|BREAK|;
(and neither I<tturn> nor I<fturn> is present).
B<(note)> In that case, B<turn()> checks for I<turns> is indeed a HASH,
nothing more
(however B<croaks> if that's not the case);
It may as well be empty;
Design legacy.
=item *
Returns C<HASH> for C<STOP> and C<BREAK> I<$state>s without any further
processing
(For those I<$state>s any I<$rule> is ignored and C<HASH> enables I<switch()>
callbacks to give more informative logs
(while that information is mangled anyway);
Probably bad idea).
=item *
C<undef> is returned if there's nothing to say --
neither I<tturn>, nor I<fturn>, nor turn map --
this record is kind of void.
({fst} might contain CODE, it's not clear how to copy it).
It's possible, one day list trailer variant of initialization may be put to
sleep.
See also L<linter below|/Linter>.
=item Linter
B<(missing feature)>
It might be hard to imagine,
but FST might get out of hands
(ie check F<t/process/quadratic.t>).
Indeed, some kind of (limited!) linter would be much desired.
It's missing.
=item Perl FALSE, C<undef>, and C<uturn>
B<(caveat)>
Back then B<DMA::FSM> treated C<undef> as any other Perl FALSE.
C<uturn> I<$rule> mech has made C<undef> special
(if B<switch()> returns C<undef> and C<uturn> I<{turn}> isn't present then
it's a B<croak>).
IRL, multiple sources are normal.
Implementation wise that leads to B<source()> that on the fly figures out
current I<$state> and then somehow branches (sometimes branches out).
Such B<source()>s look horrible because they are.
=item Special handling of C<START> and C<CONTINUE>
B<(bug)>
In contrary with C<STOP> and C<BREAK> special states,
special treatement of C<START> and C<CONTINUE> is a side effect of
L<B<process()> method|/process()> entry sequence.
That's why routinely enter C<START> or C<CONTINUE> by state flow is of
there-be-dragons type.
Totally should be rewritten.
=item B<switch()> and I<$item>
B<(bug)>
It might be surprising, but, from experience,
the state flow mostly doesn't care about I<$item>.
Mostly, I<{state}> entered with I<$action> C<NEXT> processes input
and just wonders ahead.
Still those pesky I<$item>s *must* be passed around (by design).
Still every B<switch()> *must* return I<$item> too
(even if it's just a placeholder).
Something must be done about it.
=item C<tturn>, C<fturn>, and B<switch()>
B<(misdesign)>
First, by design and legacy B<switch()> must report what turn the control flow
B<(warning)>, L<B<action()> method|/action()>.
Obvious.
None or one argument is supposed.
=item C<[connect]: (%s): unknow option, ignored>
B<(warning)>, L<B<connect()> method|/connect()>.
Each option that's not known to B<A::F> is ignored and B<carp>ed.
There're no means for child class to force B<A::F> (as a parent class) to
accept something.
Child classes should process their options by itself.
=item C<[connect]: clean init with (%i) items in FST>
B<(basic trace)>, L<B<connect()> method|/connect()>.
During class construction FST has been found.
That FST consists of I<%i> items.
Those items are number of I<$state>s and not I<$state>s plus I<{state}>s.
=item C<[connect]: FST has no {START} state>
=item C<[fst]: updating {%s} record>
B<(basic trace)>, L<B<fst()> method|/fst()>.
Named record (I<%s>) has been updated.
=item C<[fst]: updating {%s}{%s} entry>
B<(basic trace)>, L<B<fst()> method|/fst()>.
Named entry (I<%s>, the latter) in record I<%s> (the former) has been updated.
=item C<[process]: {%s}(%s): changing state: (CONTINUE)>
B<(basic trace)>, L<B<process()> method|/process()>.
Implicit change of state from C<BREAK> to C<CONTINUE> is about to happen.
=item C<[process]: {%s}(%s): entering>
B<(basic trace)>, L<B<process()> method|/process()>.
Entering state flow from I<$state> I<%s> (the former) with I<$action> I<%s>
(the latter).
=item C<[process]: {%s}(%s): going for>
B<(deep trace)>, L<B<process()> method|/process()>.
Trying to enter I<$state> I<%s> (the former) with I<$action> I<%s> (the
latter).
=item C<[process]: {%s}(%s): %s: going with>
B<(basic trace)>, L<B<process()> method|/process()>.
While in I<$state> I<%s> (1st) doing I<$action> I<%s> (2nd) the I<$item>
happens to be I<%s> (3rd).
C<undef> will look like this: C<((undef))>.
=item C<[process]: {%s}(%s): %s: turning with>
B<(basic trace)>, L<B<process()> method|/process()>.
B<switch()> (of I<$state> I<%s>, the 1st) has returned I<$rule> I<%s> (2nd)
and I<$item> I<%s>.
Now dealing with this.
=item C<[process]: {%s}(%s): leaving>
B<(basic trace)>, L<B<process()> method|/process()>.
Leaving state flow with I<$state> I<%s> (the former) and I<$action> I<%s> (the
latter).
=item C<[process]: {%s}(%s): switch returned: (%s)>
B<(basic trace)>, L<B<process()> method|/process()>.
B<switch()> (of I<$state> I<%s>, the 1st, with I<$action> I<%s>, the 2nd) has
been invoked and returned something that was interpreted as I<$rule> I<%s>,
the 3rd.
=item C<[process]: {%s}(%s): unknown action>
B<(croak)>, L<B<process()> method|/process()>.
Attempt to enter I<$state> I<%s> (the former) has been made.
However, it has failed because I<$action> I<%s> (the latter) isn't known.
=item C<< [query]: [$caller]: <%s> package can't [%s] subroutine >>
B<(croak)>, L<B<query()> method|/query()>.
I<$namespace> and I<$what> has been resolved as package I<%s> (the former)
and subroutine I<%s> (the latter).
However, the package can't do such subroutine.
switch => sub {
my $item = shift;
$item % 3 ? !0 : !1, $item
},
tturn => [ qw/ echo SAME / ],
fturn => [ qw/ bravo NEXT / ]
}
World of a difference.
Furthermore, if you dare, take a look at
F<t/process/quadratic.t> and F<t/process/sort.t>.
But better don't, those are horrible.
Now, illustration what I<$namespace> configuration parameter opens.
Classics:
my $bb = Acme::FSM->connect(
{ },
alpha => {
switch => sub {
t/TestSuite.pm view on Meta::CPAN
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;
t/TestSuite.pm view on Meta::CPAN
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;
t/TestSuite.pm view on Meta::CPAN
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;
t/TestSuite.pm view on Meta::CPAN
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;
t/base/connect.t view on Meta::CPAN
( fst => { },
state => q|START|,
action => q|VOID|,
diag_level => 10,
namespace => undef,
source => undef,
dumper => undef );
my $tag = q|class, empty {options},|;
AFSMTS_class_wrap { };
isa_ok $bb, q|Acme::FSM|, qq|$tag processed|;
is_deeply $bb->{_}, { %common, diag_level => 1 }, qq|$tag init done|;
like $stderr, qr<(?m)^\Q[connect]: FST has no {START} state>,
qq|$tag no {START} noted|;
like $stderr, qr<(?m)^\Q[connect]: FST has no {STOP} state>,
qq|$tag no {STOP} noted|;
$bback = $bb;
undef $bb;
$bb = eval { AFSMTS_object_wrap $bback; 1 };
ok !$bb && $@ =~ m<{options} HASH is required>,
AFSMTS_croakson q|object, no {options}|;
$tag = q|object, empty {options},|;
AFSMTS_object_wrap $bback, { };
isa_ok $bb, q|Acme::FSM|, qq|$tag processed|;
is_deeply $bb->{_}, { %common, diag_level => 1 }, qq|$tag init done|;
like $stderr, qr<(?m)^\Q[connect]: FST has no {START} state>,
qq|$tag no {START} noted|;
like $stderr, qr<(?m)^\Q[connect]: FST has no {STOP} state>,
qq|$tag no {STOP} noted|;
is_deeply
[ $bb->{_}{fst}, $bb->{_} ne $bback->{_} ], [ $bback->{_}{fst}, !0 ],
qq|$tag {fst} check|;
$tag = q|class, minimal FST explicitly in {@_},|;
AFSMTS_class_wrap { diag_level => 10 }, qw| START splat STOP tic_tac_toe |;
isa_ok $bb, q|Acme::FSM|, qq|$tag processed|;
is_deeply $bb->{_}, { %common, fst => {qw| START splat STOP tic_tac_toe |}},
qq|$tag init done|;
like $stderr, qr<(?m)^\Q[connect]: clean init with (2) >,
qq|$tag items in FST noted|;
$bback = $bb;
$tag = q|object, minimal FST explicity in {@_},|;
AFSMTS_object_wrap $bback, { }, qw| START hash_mark STOP pound_sign |;
isa_ok $bb, q|Acme::FSM|, qq|$tag processed|;
is_deeply $bb->{_}, { %common, fst => {qw| START splat STOP tic_tac_toe |}},
qq|$tag init done|;
is_deeply
[ $bb->{_}{fst}, $bb->{_} ne $bback->{_} ], [ $bback->{_}{fst}, !0 ],
qq|$tag {fst} check|;
like $stderr, qr<(?m)^\Q[connect]: stealing (2) >,
qq|$tag items in FST noted|;
like $stderr, qr<(?m)^\Q[connect]: ignoring (4) >,
qq|$tag items in trailer noted|;
$tag = q|class, minimal FST in HASH,|;
my $fsta = {qw| START flash STOP thump |};
AFSMTS_class_wrap { diag_level => 10 }, $fsta;
isa_ok $bb, q|Acme::FSM|, qq|$tag processed|;
is_deeply $bb->{_}, { %common, fst => { %$fsta }}, qq|$tag init done|;
is $bb->{_}{fst}, $fsta, qq|$tag {fst} isa prepared HASH|;
like $stderr, qr<(?m)^\Q[connect]: clean init with (2) >,
qq|$tag items in FST noted|;
$bback = $bb;
$tag = q|object, minimal FST in HASH,|;
my $fstb = {qw| START thud STOP sharp |};
AFSMTS_object_wrap $bback, { }, $fstb;
isa_ok $bb, q|Acme::FSM|, qq|$tag processed|;
is_deeply $bb->{_}, { %common, fst => { %$fsta }}, qq|$tag init done|;
is_deeply
[ $bb->{_}{fst}, $bb->{_} ne $bback->{_}, $bb->{_}{fst} ],
[ $bback->{_}{fst}, !0, $fsta ],
qq|$tag {fst} check|;
like $stderr, qr<(?m)^\Q[connect]: stealing (2) >,
qq|$tag items in FST noted|;
like $stderr, qr<(?m)^\Q[connect]: ignoring (2) >,
qq|$tag items in trailer noted|;
$tag = q|class, minimal FST in HASH, minimal trailer,|;
$fsta = {qw| START mesh STOP crosshatch |};
AFSMTS_class_wrap { diag_level => 10 }, $fsta, hex => q|octalthorpe|;
isa_ok $bb, q|Acme::FSM|, qq|$tag processed|;
is_deeply $bb->{_}, { %common, fst => { %$fsta }}, qq|$tag init done|;
is $bb->{_}{fst}, $fsta, qq|$tag {fst} isa prepared HASH|;
like $stderr, qr<(?m)^\Q[connect]: clean init with (2) >,
qq|$tag items in FST noted|;
like $stderr, qr<(?m)^\Q[connect]: ignoring (2) >,
qq|$tag items in trailer noted|;
$bback = $bb;
$tag = q|object, minimal FST in HASH, minimal trailer,|;
$fstb = {qw| START octothorn STOP crunch |};
AFSMTS_object_wrap $bback, { }, $fstb, noughts_and_crosses => q|widget_mark|;
isa_ok $bb, q|Acme::FSM|, qq|$tag processed|;
is_deeply $bb->{_}, { %common, fst => { %$fsta }}, qq|$tag init done|;
is $bb->{_}{fst}, $fsta, qq|$tag {fst} isa prepared HASH|;
like $stderr, qr<(?m)^\Q[connect]: stealing (2) >,
qq|$tag items in FST noted|;
like $stderr, qr<(?m)^\Q[connect]: ignoring (2) >,
qq|$tag items in traler noted|;
$t::TestSuite::class_cheat = q|t::TestSuite::FSM|;
$tag = q|just checking,|;
$fsta = {qw| START pig_pen STOP comment_sign |};
AFSMTS_class_wrap { }, $fsta;
isa_ok $bb, q|t::TestSuite::FSM|, qq|$tag processed|;
$bback = $bb;
$tag = q|object, inheritance,|;
AFSMTS_object_wrap $bback, { };
isa_ok $bb, q|t::TestSuite::FSM|, qq|$tag processed|;
undef $t::TestSuite::class_cheat;
$tag = q|class, unknown {options},|;
AFSMTS_class_wrap { diag_level => 10, noughts_and_crosses => q|octothorpe| };
isa_ok $bb, q|Acme::FSM|, qq|$tag processed|;
is_deeply $bb->{_}, { %common }, qq|$tag init done|;
like $stderr, qr<(?m)^\Q[connect]: (noughts_and_crosses): unknown option>,
qq|$tag noted|;
$bback = $bb;
$tag = q|object, unknown {options},|;
AFSMTS_object_wrap $bback, { hex => q|gate| };
isa_ok $bb, q|Acme::FSM|, qq|$tag processed|;
is_deeply $bb->{_}, { %common }, qq|$tag init done|;
like $stderr, qr<(?m)^\Q[connect]: (hex): unknown option>, qq|$tag noted|;
$tag = q|class, {options}{namespace},|;
AFSMTS_class_wrap { diag_level => 10, namespace => q|gate| };
isa_ok $bb, q|Acme::FSM|, qq|$tag processed|;
is_deeply $bb->{_}, { %common, namespace => q|gate| },
qq|$tag {namespace} accepted|;
$bback = $bb;
$tag = q|object, {options}{namespace}, get from source,|;
AFSMTS_object_wrap $bback, { };
isa_ok $bb, q|Acme::FSM|, qq|$tag processed|;
is_deeply $bb->{_}, { %common, namespace => q|gate| }, qq|$tag init done|;
$tag = q|object, {options}{namespace}, overwrite one from source,|;
AFSMTS_object_wrap $bback, { namespace => q|gridlet| };
isa_ok $bb, q|Acme::FSM|, qq|$tag processed|;
is_deeply $bb->{_}, { %common, namespace => q|gridlet| },
qq|$tag init done|;
$tag = q|object, {options}{namespace}, overwrite with (),|;
AFSMTS_object_wrap $bback, { namespace => '' };
isa_ok $bb, q|Acme::FSM|, qq|$tag processed|;
is_deeply $bb->{_}, { %common, namespace => '' },
qq|$tag init done|;
# vim: set filetype=perl
t/process/parse.t view on Meta::CPAN
$bb->{left} = eval qq|$bb->{left} $bb->{op} $bb->{right}| if
$bb->{op} && defined $bb->{right};
$bb->{left} = eval qq|$bb->{left}| if defined $bb->{left};
delete $bb->{right};
delete $bb->{op} }
while( my $input = shift @inbase ) {
@input = split m{}, $input->[0];
AFSMTS_wrap;
do_stuff;
until( $bb->state eq q|STOP| ) { AFSMTS_method_wrap q|process|; do_stuff }
is_deeply
[ $bb->action, @$bb{qw| fail left |} ], [ q|finish|, @$input[1 .. 2] ],
sprintf q|(%s) (%s) (%s)|,
$input->[0], $bb->{left} // q|(undef)|, $bb->{fail} // q|| }
# vim: set filetype=perl
t/state/continue.t view on Meta::CPAN
use Acme::FSM;
our( %st, $stderr, @inbase, @input );
our %opts = ( source => \&AFSMTS_shift, diag_level => -t STDOUT ? 10 : 1 );
sub toggle_now ( ) {
@inbase = $inbase[0] ? ( undef ) x 5 : qw| mannaro | x 5;
@input = ( ) }
my $method = q|process|;
sub combo_now ( ) { toggle_now; AFSMTS_wrap; AFSMTS_method_wrap $method }
my %common =
( state => q|CONTINUE|,
diag_level => $opts{diag_level},
namespace => undef,
source => $opts{source},
dumper => undef,
queue => [ undef ] );
( run in 0.372 second using v1.01-cache-2.11-cpan-8d75d55dd25 )