Testing gen_fsm – Part 5 ATG Engine

The first post on the Automatest Test Generator for gen_fsm testing gave us a good idea on Rule basis, what we should brainstorm on now, is how the internal machinery should be for this to work as expected. By now this seems to take on a form outside of EUnit, and I shall therefore drop the EUnit prefix on the titles (for now). Without further ado, let us start writing some free-style mock code of our machine, firs off, a basic skeleton.

Second Iteration – Machinery Brainstorming

% Minimal for now, there is a lot of bells and whistles that can be added.
run(BaseSetOfRules, GenFsm_Setup) ->
    case (catch GenFsm_Setup()) of
        {'EXIT',Reason} -> report_error(setup,Reason);
        Result ->

% No more iterations to run, and no crashes, all is okay
run_until(_RulesSet, 0) -> report_success();
% An iteration: Find valid rules (== positive precondition) and run one of them
run_until(RuleSet,Iterations) when Iterations > 0 ->
    HitSet = [ X || X={Precond,_,_} <- RuleSet, Precond() ],
    {_,Program,PostCondition} = lists:nth(random:uniform(length(HitSet)),HitSet),
    case (catch Program()) of
        {'EXIT',Reason} -> report_error(program,Reason);
        Result ->
            % Test postcondition
            case (catch PostCondition()) of
                {'EXIT',Reason2} -> report_error(postcondition,Reason2);
                true -> run_until(RuleSet,Iterations-1);
                false-> report_error(postcondition,postcondition_false)

In the code seen above, the BaseSetOfRules would be the result of the previously written rule() function, a list of rules. The second argument GenFsm_Setup would correspond to some kind of setup function to run before starting (think in {setup,fun setup/0,fun cleanup/0} terms). This function call should give a result which can be used to bind with ‘$ID’.

A very important detail is that the predicates PostCondition and PreCondition must not change the State of the gen_fsm!

Given the code above, we kind of have an idea of the machinery and it’s syntax. The natural question is then, how do we put this together through parse_transforming? The special parts that need consideration is the binding of values  to

  • ‘$ID’
  • ‘$STATE’

And also the accessing of these values. Of course, another detail worth mentioning is the fact that the machinery seems to treat the Precondition, Program and Postcondition as they where all function objects. This brain-storm design implies that all Expressions given in the previous Rule post are encapsulated into a fun during the parse_transforming (aha!).

Let this discussion of value binding and accessing be the focus for the next iteration.

Third Iteration – Binding and Accessing

For the binding of values, the erlang process dictionary fits perfectly. But before going further, let me light up the usual warning lamps about using the process dictionary and tell you how bad it is and how you should never do it, and give you the link to the info about it. Now that we got that out of the way, here is the simple implementation the machinery needs to pull this of.

% Use the process dictionary of the machinery for these special values
bind_to_id(Value) -> erlang:put('$ID',Value).
bind_to_program_result(Value) -> erlang:put('$PROGRAM_RESULT',Value).

Great, but what about the ‘$STATE’ binding, do we need the process dictionary for it?The answer is: No. The gen_fsm state is a fresh info value that needs to be  requests each time the ‘$STATE’ atom is seen in the user (rule) code,  thus we would expect the parse_transform to exchange the  ‘$STATE’ atom with a special function call.

What about accessing the values bound to these atoms? Well, for the process dictionary bound ones, just let the parse_transform exchange all occurrences with process dictionary lookups. Looking at the first brainstorm syntax, and how it evolves:

 % Old brain-storm syntax
   { not reg(seller), do_reg(seller), reg(seller,generated_pwd)},

 % Updated brain-storm syntax
   { not reg(seller), do_reg(seller), reg(seller,'$PROGRAM_RESULT')},

 % Parse_transform ed
   { fun() -> not reg(seller) end ,
     fun() -> do_reg(seller) end,
     fun() -> reg(seller,erlang:get('$PROGRAM_RESULT') end },

As for the ‘$STATE’ atom, we would expect the parse_transform to replace it with something roughly similar to this “do not try this at home kids – I am a professional“.

% Old brain-storm syntax
 reg(seller) -> '$STATE'#state.seller =/= undefined;

% Parse_transform:d into this
 reg(seller) ->
   ((fun() ->
      {status,_,_,[_,_,_,_,Misc]} = sys:get_status(erlang:get('$ID')),
      AllDatas = lists:flatten([ X || {data,X} <- Misc]),
     end)())#state.seller =/= undefined;

Let me guide you through it in case you need it. According to our manual pages, the sys:get_status will return a nifty status tuple where the last element is a list which in turn has a last element which is a proplist. That proplist contains the state representation which can be accessed through the Key “StateData”. For our purposes, this is great. Please note: “… Callback modules for gen_server and gen_fsm can also customise the value of Misc by exporting a format_status/2 function that contributes module-specific information…“.

This state accessing might have to be done in several places with close proximity, thus I encase it into an instantly executed functional object (fun), which is parenthesised in order to make it usable by any type accessing method of choice.

Now we have a kind of good idea on how we would like the Rules to be written. Also we kind of know how we would like the machinery to work, thus, the next iteration should be the start of the  actual implementation.



Previous Post
Leave a comment

Leave a Reply

Fill in your details below or click an icon to log in:

WordPress.com Logo

You are commenting using your WordPress.com account. Log Out /  Change )

Google+ photo

You are commenting using your Google+ account. Log Out /  Change )

Twitter picture

You are commenting using your Twitter account. Log Out /  Change )

Facebook photo

You are commenting using your Facebook account. Log Out /  Change )


Connecting to %s

%d bloggers like this: