EUnit Testing gen_fsm – Part 3


This third part will add the deal closure API as well as refactoring the code, we will also see the inception of the Automated Test Generating machinery.

Sixth Iteration – Deal closing API and the beginning of the ATG machinery

Buyer and Seller can now insert their items, and wish to close the deal, for this we need some kind of “security”. To implement this, the buyer and seller must both agree on the item and the cash. They both query the tradepost for it’s contents, get the item name and the cash amount, then if they agree, they can send an okay to the tradepost with the same item name and the same cash and their Pid. If the tradepost contains an item with that name, and that cash, the tradepost sends the item to the buyer and the cash to the seller. The tradepost then terminates.

For this parallelised receive, the syntax of the symbolic language is extended with

{parallel,call,MODULE,FUNCTION,ARGLIST,REPLYMATCH}

Which gives the new code in eunit_fsm.erl as

%%% @author Gianfranco <zenon@zen.home>
%%% @copyright (C) 2010, Gianfranco
%%% Created : 19 Sep 2010 by Gianfranco <zenon@zen.home>
-module(eunit_fsm).
-export([translateCmd/2,get/2]).
-define(Expr(X),??X).
translateCmd(Id,{state,is,X}) ->
    case get(Id,"StateName") of
        X -> true;
        _V ->  .erlang:error({statename_match_failed,
                              [{module, ?MODULE},
                               {line, ?LINE},
                               {expected, X},
                               {value, _V}]})
    end;
translateCmd(_Id,{call,M,F,A,X}) ->
    case apply(M,F,A) of
        X -> ok;
        _V ->  .erlang:error({function_call_match_failed,
                              [{module, ?MODULE},
                               {line, ?LINE},
                               {expression, "apply("++atom_to_list(M)++","++atom_to_list(F)++","++
                                ?Expr(A)},
                               {expected, X},
                               {value, _V}]})
    end;
translateCmd(Id,{loopdata,is,X}) ->
    case tl(tuple_to_list(get(Id,"StateData"))) of
        X    -> true;
        _V ->    .erlang:error({loopdata_match_failed,
                                [{module, ?MODULE},
                                 {line, ?LINE},
                                 {expected, X},
                                 {value, _V}]})
    end;
translateCmd(Id,{parallel,call,M,F,A,X}) ->
    spawn_link(fun() -> translateCmd(Id,{call,M,F,A,X}) end).             

% StateName or StateData
get(Id,Which) ->
    {status,_Pid,_ModTpl, List} = sys:get_status(Id),
    AllData = lists:flatten([ X || {data,X} <- lists:last(List) ]),
    proplists:get_value(Which,AllData).

To pull this off, we need to extend the state with a holder for the agreed parts Pids, this will reveal a flaw with the current syntax that has not been so evident until now (bu surely nagging) that will need to be countered.

closedeal(Pid) ->
    ?fsm_test(Pid,
              "Seller Identifies, Buyer Identifies, Seller Inserts Item "
              ", Buyer Inserts Cash, Seller Gets Contents, Buyer Gets Contents "
              ", Buyer Agrees, Seller Agrees ",
              [{state,is,pending},
               {call,tradepost,seller_identify,[Pid,s],ok},
               {call,tradepost,buyer_identify,[Pid,b],ok},
               {call,tradepost,seller_insertitem,[Pid,ring,s],ok},
               {call,tradepost,buyer_insertcash,[Pid,100,b],ok},
               {call,tradepost,get_contents,[Pid,s],{ring,100}},
               {call,tradepost,get_contents,[Pid,b],{ring,100}},
               {parallel,call,tradepost,seller_deal,[Pid,s,{ring,100}],100},
               {parallel,call,tradepost,buyer_deal,[Pid,b,{ring,100}],ring}
              ]).

Writing the code and also modifying the state record, the result is

1> ======================== EUnit ========================
module 'tradepost'
  module 'tradepost_tests'
    tradepost_tests: started_properly (Started Properly Test)...[0.001 s] ok
    tradepost_tests: identify_seller (Identify Seller Test)...ok
    tradepost_tests: insert_item (Insert Item Test)...ok
    tradepost_tests: withdraw_item (Withdraw Item Test)...ok
    tradepost_tests: identify_buyer (Identify Buyer Test)...ok
    tradepost_tests: insert_cash (Insert Cash Test)...ok
    tradepost_tests: withdraw_cash (Withdraw Cash Test)...ok
    tradepost_tests: interleaving1 (Seller Identifies, Inserts Item, then Buyer Identiefies)...ok
    tradepost_tests: interleaving2 (Buyer Identifies, Inserts Item, then Seller Identiefies)...ok
    tradepost_tests: interleaving3 (Buyer Identifies, Seller Identifies, buyer inserts cash)...ok
    tradepost_tests: interleaving4 (Seller Identifies, Buyer Identifies, Seller inserts item)...ok
    tradepost_tests: interleaving5 (Seller Identifies, Seller inserts item, Buyer Identifies,Buyer Inserts Cash)...ok
    tradepost_tests: interleaving6 (Seller Identifies, Seller inserts item, Buyer Identifies,Seller Withdraws Item, Buyer Inserts Cash, Seller Inserts ItemBuyer Withdraws Cash, Seller Withdraws Item)...ok
    tradepost_tests: closedeal (Seller Identifies, Buyer Identifies, Seller Inserts Item , Buyer Inserts Cash, Seller Gets Contents, Buyer Gets Contents , Buyer Agrees, Seller Agrees )...ok
    [done in 0.045 s]
  [done in 0.045 s]
=======================================================
  All 14 tests passed.

1>

Greatness, but, in order for this to work, some modification had to be done to all previous tests! We had to extend the state-record with new fields, and the result is that the loopdata assertion had to be changed in each test! What if we had 1000 previous tests? *pulls hair in desperation*. Also, after this success, I made some refactoring, something that _should_ be done after each tests + code + success. This is also a great thing, once you have a nice big set of tests, refactoring can always be tested to preserve the logic by running your tests *thumbs up*.

Anyway, here is the cut and paste version of  tradepost.erl

%%%-------------------------------------------------------------------
%%% @author Gianfranco <zenon@zen.home>
%%% @copyright (C) 2010, Gianfranco
%%% Created :  2 Sep 2010 by Gianfranco <zenon@zen.home>
%%%-------------------------------------------------------------------
-module(tradepost).
-behaviour(gen_fsm).
% ------------------------------------------------------------------------------
% API
-export([start_link/0,stop/1,seller_identify/2,seller_insert/3,seller_withdraw/2,
         buyer_identify/2,buyer_insert/3,buyer_withdraw/2,get_contents/2,
         seller_deal/3,buyer_deal/3]).
% ------------------------------------------------------------------------------
% States
-export([pending/3,item_received/3,cash_received/3]).
% ------------------------------------------------------------------------------
% gen_fsm callbacks
-export([init/1, handle_event/3, handle_sync_event/4, handle_info/3,terminate/3,
         code_change/4]).
% ------------------------------------------------------------------------------
% State representation
-record(state, {item,cash,seller,buyer,seller_accept,buyer_accept}).

% ------------------------------------------------------------------------------
% API
start_link() -> gen_fsm:start_link(?MODULE, [], []).
stop(Pid) -> gen_fsm:sync_send_all_state_event(Pid,stop).
seller_identify(TP,Pwd) ->
    gen_fsm:sync_send_all_state_event(TP,{identify,seller,Pwd}).
seller_insert(TP,Item,Pwd) ->
    gen_fsm:sync_send_event(TP,{insert,item,Item,Pwd}).
seller_withdraw(TP,Pwd) ->
    gen_fsm:sync_send_event(TP,{withdraw,item,Pwd}).
seller_deal(TP,Pwd,{Item,Cash}) ->
    gen_fsm:sync_send_all_state_event(TP,{accept,seller,Pwd,{Item,Cash}}).
buyer_deal(TP,Pwd,{Item,Cash}) ->
    gen_fsm:sync_send_all_state_event(TP,{accept,buyer,Pwd,{Item,Cash}}).
buyer_identify(TP,Pwd) ->
    gen_fsm:sync_send_all_state_event(TP,{identify,buyer,Pwd}).
buyer_insert(TP,Amount,Pwd) ->
    gen_fsm:sync_send_event(TP,{insert,cash,Amount,Pwd}).
buyer_withdraw(TP,Pwd) ->
    gen_fsm:sync_send_event(TP,{withdraw,cash,Pwd}).
get_contents(TP,Pwd) ->
    gen_fsm:sync_send_all_state_event(TP,{get_contents,Pwd}).
% ------------------------------------------------------------------------------
% Internal States
pending({insert,item,Item,Pwd},_,LoopD) when Pwd == LoopD#state.seller,
                                             LoopD#state.seller =/= undefined ->
    {reply,ok,item_received,LoopD#state{item=Item}};
pending({insert,cash,Amount,Pwd},_,LoopD) when Pwd == LoopD#state.buyer;
                                             LoopD#state.buyer =/= undefined ->
    {reply,ok,cash_received,LoopD#state{cash=Amount}};
pending(_E,_From,LoopD) -> {reply,error,pending,LoopD}.

item_received({withdraw,What,Pwd},_,LoopD) ->
    withdraw(What,Pwd,LoopD,item_received);
item_received({insert,cash,Amount,Pwd},_,LoopD = #state{buyer=Pwd})->
    {reply,ok,cash_received,LoopD#state{cash=Amount}};
item_received({insert,item,Item,Pwd},_,LoopD = #state{seller=Pwd})->
    {reply,ok,item_received,LoopD#state{item=Item}};
item_received(_E,_From,LoopD) -> {reply,error,item_received,LoopD}.

cash_received({withdraw,What,Pwd},_,LoopD) ->
    withdraw(What,Pwd,LoopD,cash_received);
cash_received({insert,cash,Amount,Pwd},_,LoopD = #state{buyer=Pwd})->
    {reply,ok,cash_received,LoopD#state{cash=Amount}};
cash_received({insert,item,Item,Pwd},_Frm, LoopD = #state{seller=Pwd})->
    {reply,ok,item_received,LoopD#state{item=Item}};
cash_received(_E,_From,LoopD) -> {reply,error,cash_received,LoopD}.
% ------------------------------------------------------------------------------
% Callbacks with Logic
handle_sync_event({identify,Who,Pwd},_From,StateName,LoopD) ->
    {Reply,NextState} =
        case {Who , LoopD#state.seller, LoopD#state.buyer } of
            {seller, undefined, _ } -> {ok,LoopD#state{seller=Pwd}};
            {buyer, _ , undefined } -> {ok,LoopD#state{buyer=Pwd}};
            _ -> {error,LoopD}
        end,
    {reply,Reply,StateName,NextState};
handle_sync_event(stop,_From,_,LoopData) -> {stop,normal,ok,LoopData};
handle_sync_event({get_contents,Pwd},_Frm,StateName,LoopData) ->
    Reply =
        case {LoopData#state.seller,LoopData#state.buyer} of
            {Pwd,_} -> {LoopData#state.item,LoopData#state.cash};
            {_,Pwd} -> {LoopData#state.item,LoopData#state.cash}
        end,
    {reply,Reply,StateName,LoopData};
handle_sync_event({accept,seller,Pwd,{Item,Cash}},From,StateName,LoopD)
  when Pwd == LoopD#state.seller; LoopD#state.seller =/= undefined ->
    accept(LoopD#state.buyer_accept, {Item,Cash,From}, LoopD#state.item,
           LoopD#state.cash, seller, StateName, LoopD);
handle_sync_event({accept,buyer,Pwd,{Item,Cash}},From,StateName,LoopD)
  when Pwd == LoopD#state.buyer; LoopD#state.buyer =/= undefined ->
    accept(LoopD#state.seller_accept, {Item,Cash,From}, LoopD#state.item,
           LoopD#state.cash, buyer, StateName, LoopD);
handle_sync_event(_E,_From,StateName,LoopData) ->
    {reply,error,StateName,LoopData}.
% ------------------------------------------------------------------------------
% Obligatory others
init([]) -> {ok, pending, #state{}}.
handle_event(_Event, StateName, State) ->{next_state, StateName, State}.
handle_info(_Info, StateName, State) -> {next_state, StateName, State}.
terminate(_Reason, _StateName, _State) -> ok.
code_change(_OldVsn, StateName, State, _Extra) -> {ok, StateName, State}.

% ------------------------------------------------------------------------------
% Withdraw cash xor item - grossly compressed  :(
-spec withdraw(item | cash, any(), #state{}, atom()) ->
    {reply,ok|error,atom(),#state{}}.
withdraw(What,Pwd,LoopD=#state{seller=S,buyer=B,item = I,cash=C},State) ->
  {Reply,NextState,NewLoopData} =
   case { State, What, S, B, I, C} of
    {item_received, item, Pwd, _ , _, undefined } -> {ok, pending, undef(item,LoopD)};
    {item_received, item, Pwd, _ , _, _V } -> {ok, cash_received, undef(item,LoopD)};
    {item_received, cash, _ , Pwd, _, _} -> {ok, item_received, undef(cash,LoopD)};
    {cash_received, item, Pwd, _, _, undefined} -> {ok, cash_received, undef(item,LoopD)};
    {cash_received, cash, _, Pwd, undefined, _ } -> {ok, pending, undef(cash,LoopD)};
    {cash_received, cash, _ , Pwd, _V, _} -> {ok, item_received, undef(cash,LoopD)};
     _ -> {error,State,LoopD}
  end,
    {reply,Reply,NextState,NextLoopData}.
% ------------------------------------------------------------------------------
% Accept a deal
-spec accept({atom(), integer(), {reference(),pid()}},
             {atom(), integer(), {reference(),pid()}},
             atom(), integer(), seller | buyer, atom(),#state{}) ->
    {reply, atom() | integer | {error,not_same},  atom(), #state{}} |
    {next_state, atom(), #state{}}.
accept(AgreeOther, {ItemSelf,CashSelf,Self}, TradePostItem, TradePostCash,
       Who,StateName,LoopD) ->
    % Check if Other and Self have now agreed on the same thing
    AgreeThis  = {ItemSelf, CashSelf, Self},
    Contains   = {TradePostItem, TradePostCash},
    case { AgreeOther , AgreeThis,  Contains} of
        % Same Object And Cash, send cash to buyer And item to seller
        { {Item, Cash, Other}, {Item, Cash, Self}, {Item, Cash} } ->
            {SelfEnd,OtherEnd} =
                case Who of
                    seller -> {Cash,Item};
                    buyer -> {Item,Cash}
                end,
            gen_fsm:reply(Other,OtherEnd),
            {reply,SelfEnd,StateName,LoopD};
        % Not agreed on same
        { {_A,_B,Other}, {_C,_D,Self}, _ } ->
            gen_fsm:reply(Other,{error,not_same}),
            {reply,{error,not_same},StateName,LoopD};
        % Only self has agreed so far
        { undefined, _ , _ } ->
            NewLoopD =
            case Who of
                seller -> LoopD#state{seller_accept={ItemSelf,CashSelf,Self}};
                buyer -> LoopD#state{buyer_accept={ItemSelf,CashSelf,Self}}
            end,
            {next_state,StateName,NewLoopD}
    end.

-spec undef(item | cash, #state{}) -> #state{}.
undef(item,LoopData) -> LoopData#state{item=undefined};
undef(cash,LoopData) -> LoopData#state{cash=undefined}.

And the modified test module tradepost_tests.erl

% @author Gianfranco <zenon@zen.home>
% @copyright (C) 2010, Gianfranco
% Created :  6 Sep 2010 by Gianfranco <zenon@zen.home>
-module(tradepost_tests).
-include_lib("eunit/include/eunit.hrl").
-include("include/eunit_fsm.hrl").

% This is the main point of "entry" for my EUnit testing.
% A generator which forces setup and cleanup for each test in the testset
main_test_() ->
    {foreach,
     fun setup/0,
     fun cleanup/1,
     % Note that this must be a List of TestSet or Instantiator
     [
      % First Iteration
      fun started_properly/1,
      % Second Iteration
      fun identify_seller/1,
      fun insert_item/1,
      fun withdraw_item/1,
      % Fourth iteration
      fun identify_buyer/1,
      fun insert_cash/1,
      fun withdraw_cash/1,
      % Fifth iteration
      fun interleaving1/1,
      fun interleaving2/1,
      fun interleaving3/1,
      fun interleaving4/1,
      fun interleaving5/1,
      fun interleaving6/1,
      % Sixth Iteration
      fun closedeal/1
     ]}.

% Setup and Cleanup
setup()      -> {ok,Pid} = tradepost:start_link(), Pid.
cleanup(Pid) -> tradepost:stop(Pid).

% Pure tests below
% ------------------------------------------------------------------------------
% Let's start simple, I want it to start and check that it is okay.
% I will use the introspective function for this
started_properly(Pid) ->
    ?fsm_test(Pid,"Started Properly Test",
              [{state,is,pending},
               {loopdata,is,[undefined,undefined,undefined,undefined,
                            undefined,undefined]}
              ]).

% Now, we are adding the Seller API tests
identify_seller(Pid) ->
    ?fsm_test(Pid,"Identify Seller Test",
              [{state,is,pending},
               {call,tradepost,seller_identify,[Pid,seller_password],ok},
               {state,is,pending},
               {loopdata,is,[undefined,undefined,seller_password,undefined,
                             undefined,undefined]}
              ]).

insert_item(Pid) ->
    ?fsm_test(Pid,"Insert Item Test",
              [{state,is,pending},
               {call,tradepost,seller_identify,[Pid,seller_password],ok},
               {call,tradepost,seller_insert,[Pid,playstation,seller_password],ok},
               {state,is,item_received},
               {loopdata,is,[playstation,undefined,seller_password,undefined,
                             undefined,undefined]}
              ]).

withdraw_item(Pid) ->
    ?fsm_test(Pid,"Withdraw Item Test",
              [{state,is,pending},
               {call,tradepost,seller_identify,[Pid,seller_password],ok},
               {call,tradepost,seller_insert,[Pid,button,seller_password],ok},
               {state,is,item_received},
               {call,tradepost,seller_withdraw,[Pid,seller_password],ok},
               {state,is,pending},
               {loopdata,is,[undefined,undefined,seller_password,undefined,
                             undefined,undefined]}
              ]).

identify_buyer(Pid) ->
    ?fsm_test(Pid,"Identify Buyer Test",
              [{state,is,pending},
               {call,tradepost,buyer_identify,[Pid,buyer_password],ok},
               {state,is,pending},
               {loopdata,is,[undefined,undefined,undefined,buyer_password,
                            undefined,undefined]}
              ]).

insert_cash(Pid) ->
    ?fsm_test(Pid,"Insert Cash Test",
              [{state,is,pending},
               {call,tradepost,buyer_identify,[Pid,buyer_password],ok},
               {call,tradepost,buyer_insert,[Pid,100,buyer_password],ok},
               {state,is,cash_received},
               {loopdata,is,[undefined,100,undefined,buyer_password,
                            undefined,undefined]}
              ]).

withdraw_cash(Pid) ->
    ?fsm_test(Pid,"Withdraw Cash Test",
              [{state,is,pending},
               {call,tradepost,buyer_identify,[Pid,buyer_password],ok},
               {call,tradepost,buyer_insert,[Pid,100,buyer_password],ok},
               {call,tradepost,buyer_withdraw,[Pid,buyer_password],ok},
               {loopdata,is,[undefined,undefined,undefined,buyer_password,
                            undefined,undefined]}
              ]).

interleaving1(Pid) ->
    ?fsm_test(Pid,"Seller Identifies, Inserts Item, then Buyer Identiefies",
              [{state,is,pending},
               {call,tradepost,seller_identify,[Pid,s],ok},
               {call,tradepost,seller_insert,[Pid,ring,s],ok},
               {call,tradepost,buyer_identify,[Pid,b],ok},
               {loopdata,is,[ring,undefined,s,b,undefined,undefined]}
              ]).

interleaving2(Pid) ->
    ?fsm_test(Pid,"Buyer Identifies, Inserts Item, then Seller Identiefies",
              [{state,is,pending},
               {call,tradepost,buyer_identify,[Pid,b],ok},
               {call,tradepost,buyer_insert,[Pid,100,b],ok},
               {call,tradepost,seller_identify,[Pid,s],ok},
               {loopdata,is,[undefined,100,s,b,undefined,undefined]}
              ]).

interleaving3(Pid) ->
    ?fsm_test(Pid,"Buyer Identifies, Seller Identifies, buyer inserts cash",
              [{state,is,pending},
               {call,tradepost,buyer_identify,[Pid,b],ok},
               {call,tradepost,seller_identify,[Pid,s],ok},
               {call,tradepost,buyer_insert,[Pid,100,b],ok},
               {loopdata,is,[undefined,100,s,b,undefined,undefined]}
              ]).

interleaving4(Pid) ->
    ?fsm_test(Pid,"Seller Identifies, Buyer Identifies, Seller inserts item",
              [{state,is,pending},
               {call,tradepost,seller_identify,[Pid,s],ok},
               {call,tradepost,buyer_identify,[Pid,b],ok},
               {call,tradepost,seller_insert,[Pid,ring,s],ok},
               {loopdata,is,[ring,undefined,s,b,undefined,undefined]}
              ]).

interleaving5(Pid) ->
    ?fsm_test(Pid,"Seller Identifies, Seller inserts item, Buyer Identifies,"
              "Buyer Inserts Cash",
              [{state,is,pending},
               {call,tradepost,seller_identify,[Pid,s],ok},
               {call,tradepost,seller_insert,[Pid,ring,s],ok},
               {call,tradepost,buyer_identify,[Pid,b],ok},
               {call,tradepost,buyer_insert,[Pid,100,b],ok},
               {loopdata,is,[ring,100,s,b,undefined,undefined]}
              ]).

interleaving6(Pid) ->
    ?fsm_test(Pid,"Seller Identifies, Seller inserts item, Buyer Identifies,"
              "Seller Withdraws Item, Buyer Inserts Cash, Seller Inserts Item"
              "Buyer Withdraws Cash, Seller Withdraws Item",
              [{state,is,pending},
               {call,tradepost,seller_identify,[Pid,s],ok},
               {call,tradepost,seller_insert,[Pid,ring,s],ok},
               {call,tradepost,buyer_identify,[Pid,b],ok},
               {call,tradepost,seller_withdraw,[Pid,s],ok},
               {call,tradepost,buyer_insert,[Pid,100,b],ok},
               {call,tradepost,seller_insert,[Pid,ring,s],ok},
               {call,tradepost,buyer_withdraw,[Pid,b],ok},
               {call,tradepost,seller_withdraw,[Pid,s],ok},
               {loopdata,is,[undefined,undefined,s,b,undefined,undefined]}
              ]).

closedeal(Pid) ->
    ?fsm_test(Pid,
              "Seller Identifies, Buyer Identifies, Seller Inserts Item "
              ", Buyer Inserts Cash, Seller Gets Contents, Buyer Gets Contents "
              ", Buyer Agrees, Seller Agrees ",
              [{state,is,pending},
               {call,tradepost,seller_identify,[Pid,s],ok},
               {call,tradepost,buyer_identify,[Pid,b],ok},
               {call,tradepost,seller_insert,[Pid,ring,s],ok},
               {call,tradepost,buyer_insert,[Pid,100,b],ok},
               {call,tradepost,get_contents,[Pid,s],{ring,100}},
               {call,tradepost,get_contents,[Pid,b],{ring,100}},
               {parallel,call,tradepost,seller_deal,[Pid,s,{ring,100}],100},
               {parallel,call,tradepost,buyer_deal,[Pid,b,{ring,100}],ring}
              ]).

With this success, it's time to hunt the real cornercase-bugs using Automated Test Generation, a very strong piece of machinery, that requieres some setup. Oh, and just in case, if you forgot how it all fits together...

zen:EUnitFSM zenon$ tree .
.
├── ebin
│   ├── eunit_fsm.beam
│   ├── tradepost.beam
│   └── tradepost_tests.beam
├── include
│   └── eunit_fsm.hrl
├── src
│   └── tradepost.erl
└── test
    ├── eunit_fsm.erl
    └── tradepost_tests.erl

4 directories, 7 files
zen:EUnitFSM zenon$ 

Automated Test Generation - inception

As we wish to generate complex (almost the same as long) test sequences, and do not wish to enter them by hand, we want to have a piece of machinery which makes this for us. An immediate question that comes to mind is then, how does the machinery know which sequences are actually valid? Well, that is for us to know and express. Here we can draw inspiration from from Hoare Logic and use that notion for our automated test generator. A good candidate for this would be a syntax similar to something as

Rule = { PreCondPred, Program, PostCondPred }

PreCondPred = fun FSM-State x SetupResult -> Boolean

Program = [Action]

Action = {call,M,F,A,ExpectedResult} |
        {parallel, call, M, F, A, ResultExpectedResult}

PostCondPred = fun FSM-State x SetupResult -> Boolean

We would thus want the machinery to behave as follows. Given a current gen_fsm state, and the list of all rules, return a list of all rules for which the PreCondPred holds. From this list of rules, choose one randomly, and apply execute Program. After execution, check if PostCondPred holds true. If not, record failure. On Success, repeat process with random selection of valid rule and application, etc.

For this to work, we need to write our own generator, for this, we can peek at the EUnit User's Guide section on Lazy Generators. However, this should be discussed in the next part of this series. For this purpose, the iterations have ended.

Ps: I apologize for the long delay before this post came, but I had a lot to do at the sidelines.

About these ads
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 )

Twitter picture

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

Facebook photo

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

Google+ photo

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

Connecting to %s

Follow

Get every new post delivered to your Inbox.

%d bloggers like this: