%%
%% %CopyrightBegin%
%%
%% Copyright Ericsson AB 1999-2010. All Rights Reserved.
%%
%% The contents of this file are subject to the Erlang Public License,
%% Version 1.1, (the "License"); you may not use this file except in
%% compliance with the License. You should have received a copy of the
%% Erlang Public License along with this software. If not, it can be
%% retrieved online at http://www.erlang.org/.
%%
%% Software distributed under the License is distributed on an "AS IS"
%% basis, WITHOUT WARRANTY OF ANY KIND, either express or implied. See
%% the License for the specific language governing rights and limitations
%% under the License.
%%
%% %CopyrightEnd%
%%
%%
-module(transactions_SUITE).
%%--------------- INCLUDES -----------------------------------
-include_lib("orber/include/corba.hrl").
-include_lib("orber/include/ifr_types.hrl").
%% Local
-include_lib("cosTransactions/src/ETraP_Common.hrl").
-include_lib("cosTransactions/include/CosTransactions.hrl").
-include("etrap_test_lib.hrl").
-include_lib("test_server/include/test_server.hrl").
-define(default_timeout, ?t:minutes(20)).
%%-----------------------------------------------------------------
%% External exports
%%-----------------------------------------------------------------
-export([all/0, suite/0,groups/0,init_per_group/2,end_per_group/2, cases/0,
init_per_suite/1, end_per_suite/1, resource_api/1, etrap_api/1,
init_per_testcase/2, end_per_testcase/2, app_test/1]).
%%-----------------------------------------------------------------
%% Func: all/1
%% Args:
%% Returns:
%%-----------------------------------------------------------------
suite() -> [{ct_hooks,[ts_install_cth]}].
all() ->
cases().
groups() ->
[].
init_per_group(_GroupName, Config) ->
Config.
end_per_group(_GroupName, Config) ->
Config.
cases() ->
[etrap_api, resource_api, app_test].
%%-----------------------------------------------------------------
%% Init and cleanup functions.
%%-----------------------------------------------------------------
init_per_testcase(_Case, Config) ->
Path = code:which(?MODULE),
code:add_pathz(filename:join(filename:dirname(Path), "idl_output")),
'oe_CosTransactions':'oe_register'(),
'oe_etrap_test':'oe_register'(),
?line Dog=test_server:timetrap(?default_timeout),
[{watchdog, Dog}|Config].
end_per_testcase(_Case, Config) ->
'oe_etrap_test':'oe_unregister'(),
'oe_CosTransactions':'oe_unregister'(),
Path = code:which(?MODULE),
code:del_path(filename:join(filename:dirname(Path), "idl_output")),
Dog = ?config(watchdog, Config),
test_server:timetrap_cancel(Dog),
ok.
init_per_suite(Config) ->
mnesia:delete_schema([node()]),
mnesia:create_schema([node()]),
orber:install([node()]),
application:start(mnesia),
application:start(orber),
if
is_list(Config) ->
Config;
true ->
exit("Config not a list")
end.
end_per_suite(Config) ->
application:stop(orber),
application:stop(mnesia),
mnesia:delete_schema([node()]),
Config.
%%-----------------------------------------------------------------
%% Tests app file
%%-----------------------------------------------------------------
app_test(doc) -> [];
app_test(suite) -> [];
app_test(_Config) ->
ok=test_server:app_test(cosTransactions),
ok.
%%-----------------------------------------------------------------
%% API tests
%%-----------------------------------------------------------------
etrap_api(doc) -> ["ETraP_Server tests", ""];
etrap_api(suite) -> [];
etrap_api(_Config) ->
?line ?match(ok, application:start(cosTransactions),
"Starting the cosTransactions application"),
?line TrFac = cosTransactions:start_factory(),
%% Start a new transaction:
%% RootCoord
%% / \
%% SubCoord1 SubCoord2
Control = 'CosTransactions_TransactionFactory':create(TrFac, 0),
Term = 'CosTransactions_Control':get_terminator(Control),
Coord = 'CosTransactions_Control':get_coordinator(Control),
SubCont1 = 'CosTransactions_Coordinator':create_subtransaction(Coord),
SubCont2 = 'CosTransactions_Coordinator':create_subtransaction(Coord),
SubCoord1 = 'CosTransactions_Control':get_coordinator(SubCont1),
SubCoord2 = 'CosTransactions_Control':get_coordinator(SubCont2),
%%------ Test CosTransactions::Coordinator ------
?line ?match(true,
'CosTransactions_Coordinator':is_same_transaction(Coord, Coord),
"'CosTransactions_Coordinator':is_same_transaction"),
?line ?match(false,
'CosTransactions_Coordinator':is_same_transaction(Coord, SubCoord1),
"'CosTransactions_Coordinator':is_same_transaction"),
?line ?match(true,
'CosTransactions_Coordinator':is_descendant_transaction(Coord, Coord),
"'CosTransactions_Coordinator':is_descendant_transaction"),
?line ?match(false,
'CosTransactions_Coordinator':is_descendant_transaction(Coord, SubCoord1),
"'CosTransactions_Coordinator':is_descendant_transaction"),
?line ?match(true,
'CosTransactions_Coordinator':is_descendant_transaction(SubCoord1, Coord),
"'CosTransactions_Coordinator':is_descendant_transaction"),
?line ?match(false,
'CosTransactions_Coordinator':is_descendant_transaction(SubCoord1, SubCoord2),
"'CosTransactions_Coordinator':is_descendant_transaction"),
?line ?match(true,
'CosTransactions_Coordinator':is_top_level_transaction(Coord),
"'CosTransactions_Coordinator':is_top_level_transaction"),
?line ?match(false,
'CosTransactions_Coordinator':is_top_level_transaction(SubCoord2),
"'CosTransactions_Coordinator':is_top_level_transaction"),
RootHash = 'CosTransactions_Coordinator':hash_transaction(Coord),
RepeatHash= 'CosTransactions_Coordinator':hash_transaction(Coord),
RootHash2 = 'CosTransactions_Coordinator':hash_top_level_tran(SubCoord1),
RootHash3 = 'CosTransactions_Coordinator':hash_top_level_tran(Coord),
_SubHash = 'CosTransactions_Coordinator':hash_transaction(SubCoord2),
?line ?match(RootHash, RepeatHash,
"'CosTransactions_Coordinator':hash_transaction"),
?line ?match(RootHash, RootHash2,
"'CosTransactions_Coordinator':hash_top_level_tran"),
?line ?match(RootHash, RootHash3,
"'CosTransactions_Coordinator':hash_top_level_tran"),
% ?line ?match_inverse(RootHash, SubHash,
% "'CosTransactions_Coordinator':hash_transaction"),
?line ?match('StatusActive',
'CosTransactions_Coordinator':get_status(Coord),
"'CosTransactions_Coordinator':get_status"),
?line ?match('StatusActive',
'CosTransactions_Coordinator':get_status(SubCoord1),
"'CosTransactions_Coordinator':get_status"),
?line ?match('StatusActive',
'CosTransactions_Coordinator':get_parent_status(Coord),
"'CosTransactions_Coordinator':get_parent_status"),
?line ?match('StatusActive',
'CosTransactions_Coordinator':get_parent_status(SubCoord1),
"'CosTransactions_Coordinator':get_parent_status"),
?line ?match('StatusActive',
'CosTransactions_Coordinator':get_top_level_status(Coord),
"'CosTransactions_Coordinator':get_top_level_status"),
?line ?match('StatusActive',
'CosTransactions_Coordinator':get_top_level_status(SubCoord1),
"'CosTransactions_Coordinator':get_top_level_status"),
%% Create a CosTransactions::Resource to experiments with.
%% Start a new transaction:
%% RootCoord
%% / \
%% SubCoord1 SubCoord2
%% /
%% Resource
N1 = 'ETraP_Common':create_name("test"),
O1 = etrap_test_server:oe_create(?nop, {global, N1}),
_RC1 = 'CosTransactions_Coordinator':register_resource(SubCoord1, O1),
% 'CosTransactions_Coordinator':register_synchronization(SubCoord1, O1),
?line ?match('VoteCommit',
'CosTransactions_Resource':prepare(SubCoord1),
"'CosTransactions_Coordinator':prepare"),
%% The Transaction are no longer in 'StatusActive' state. No new
%% "members" allowed.
?line ?match('StatusPrepared',
'CosTransactions_Coordinator':get_status(SubCoord1),
"'CosTransactions_Coordinator':get_status"),
% ?line ?match({'EXCEPTION', ?tr_inactive},
% 'CosTransactions_Coordinator':register_synchronization(SubCoord1, O1),
% "'CosTransactions_Coordinator':register_synchronization"),
?line ?match({'EXCEPTION', ?tr_inactive},
'CosTransactions_Coordinator':register_resource(SubCoord1, O1),
"'CosTransactions_Coordinator':register_resource"),
?line ?match({'EXCEPTION', ?tr_inactive},
'CosTransactions_Coordinator':create_subtransaction(SubCoord1),
"'CosTransactions_Coordinator':create_subtransaction"),
catch corba:dispose(SubCoord1),
catch corba:dispose(SubCoord2),
catch corba:dispose(SubCont1),
catch corba:dispose(SubCont2),
catch corba:dispose(Term),
catch corba:dispose(Control),
catch corba:dispose(Coord),
catch corba:dispose(O1),
?line cosTransactions:stop_factory(TrFac),
?line application:stop(cosTransactions),
ok.
%%-----------------------------------------------------------------
%% API tests
%%-----------------------------------------------------------------
resource_api(doc) -> ["cosTransactions API tests", ""];
resource_api(suite) -> [];
resource_api(_Config) ->
?line ?match(ok, application:start(cosTransactions),
"Starting the cosTransactions application"),
?line TrFac = cosTransactions:start_factory([{typecheck, true}]),
?line ?match({'EXCEPTION', #'TRANSACTION_ROLLEDBACK'{completion_status=?COMPLETED_YES}},
run(TrFac, 0, {?nop, ?nop, ?nop, ?prepare_rollback}),
"TESTCASE #1: Prepare rollback Resource 4"),
?line ?match({'EXCEPTION', ?tr_mixed},
run(TrFac, 0, {?nop, ?nop, ?commit_mix, ?nop}),
"TESTCASE #2: Heuristic Mixed exception Resource 3"),
?line ?match(ok,
run(TrFac, 0, {?nop, ?nop, ?nop, ?nop}),
"TESTCASE #3: Normal completion. No errors."),
?line ?match(ok,
run(TrFac, 0, {?nop, ?nop, ?nop, ?commit_cm}),
"TESTCASE #4: Heuristic Commit Exception Resource 4"),
?line ?match({'EXCEPTION', #'TRANSACTION_ROLLEDBACK'{completion_status=?COMPLETED_YES}},
run(TrFac, 0, {?nop, ?rollback_rb, ?nop, ?prepare_rollback}),
"TESTCASE #5: Heuristic Rollbac Resource 2, Resource 4 reply 'VoteRollback'"),
?line ?match({'EXCEPTION', #'TRANSACTION_ROLLEDBACK'{completion_status=?COMPLETED_YES}},
run(TrFac, 0, {?nop, ?nop, ?prepare_rollback, ?rollback_rb}),
"TESTCASE #6: Heuristic Rollbac Resource 4, Resource 3 reply 'VoteRollback'"),
?line ?match(ok,
run(TrFac, 0, {?nop, ?nop, ?commit_delay, ?nop}),
"TESTCASE #7: Resource 3 delay during commit. No timeout."),
?line ?match(ok,
run(TrFac, 0, {?nop, ?nop, ?prepare_delay, ?nop}),
"TESTCASE #8: Resource 3 delay during prepare. No timeout."),
?line ?match(ok,
run(TrFac, ?TIMEOUT, {?nop, ?commit_delay, ?nop, ?nop}),
"TESTCASE #9: Resource 3 delay during commit. Timeout."),
?line ?match({'EXCEPTION', #'TRANSACTION_ROLLEDBACK'{completion_status=?COMPLETED_YES}},
run(TrFac, ?TIMEOUT, {?nop, ?prepare_delay, ?nop, ?nop}),
"TESTCASE #10: Resource 3 delay during prepare. Timeout."),
case ?is_debug_compiled of
true ->
%% Testing the Coordinators (root and sub).
?line ?match(ok,
run(TrFac, 0, {?nop, ?nop, ?nop, ?nop, [?nop, ?nop,?crash_transient(commit), ?nop]}),
"TESTCASE #11: SubCoord 3 crash transient during commit."),
?line ?match({'EXCEPTION', #'TRANSACTION_ROLLEDBACK'{}},
run(TrFac, 0, {?nop, ?nop, ?nop, ?nop, [?nop, ?nop,?crash_transient(send_prepare), ?nop]}),
"TESTCASE #12: SubCoord 3 crash transient during send prepare."),
?line ?match({'EXCEPTION', ?tr_hazard},
run(TrFac, 0, {?nop, ?nop, ?nop, ?nop, [?nop, ?nop,?crash_permanent(commit), ?nop]}),
"TESTCASE #13: SubCoord 3 crash permanent during commit."),
?line ?match({'EXCEPTION', #'TRANSACTION_ROLLEDBACK'{}},
run(TrFac, 0, {?nop, ?nop, ?nop, ?nop, [?nop, ?nop,?crash_permanent(send_prepare), ?nop]}),
"TESTCASE #14: SubCoord 3 crash permanent during prepare."),
?line ?match({'EXCEPTION', #'TRANSACTION_ROLLEDBACK'{}},
run(TrFac, 0, {?nop, ?nop, ?nop, ?nop, [?nop, ?crash_transient(send_prepare), ?crash_transient(commit), ?nop]}),
"TESTCASE #15: SubCoord 2 crash transient during prepare. SubCoord 3 crash transient during commit"),
?line ?match({'EXCEPTION', #'TRANSACTION_ROLLEDBACK'{completion_status=?COMPLETED_YES}},
run(TrFac, 0, {?nop, ?nop, ?nop, ?nop, [?crash_transient(send_prepare), ?nop, ?nop, ?nop]}),
"TESTCASE #16: RootCoord crash transient during send prepare."),
?line ?match({'EXCEPTION', #'TRANSACTION_ROLLEDBACK'{}},
run(TrFac, 0, {?nop, ?nop, ?nop, ?nop, [?nop, ?crash_transient(prepare1), ?nop, ?nop]}),
"TESTCASE #17: SubCoord 1 crash transient during prepare1."),
?line ?match({'EXCEPTION', ?tr_mixed},
run(TrFac, 0, {?nop, ?prepare_mix, ?nop, ?nop, [?nop, ?nop, ?crash_transient(prepare2), ?nop]}),
"TESTCASE #18: SubCoord 3 crash transient during prepare2. Resource 2 raise Heuristic Mixed during prepare"),
?line ?match({'EXCEPTION', ?tr_mixed},
run(TrFac, 0, {?nop, ?commit_mix, ?nop, ?nop, [?nop, ?nop, ?crash_transient(commit2), ?nop]}),
"TESTCASE #19: Resource 2 raise Heurist mixed during commit. SubCoord crash transient commit2"),
?line ?match({'EXCEPTION', ?tr_mixed},
run(TrFac, 0, {?nop, ?rollback_cm, ?nop, ?prepare_rollback, [?nop, ?crash_transient(rollback2), ?nop, ?nop]}),
"TESTCASE #20: Resource 2 raise Heuristic Commit during rollback. Resource 4 'VoteRollback'. SubCoord 2 crash transient rollback2."),
?line ?match({'EXCEPTION', ?tr_mixed},
run(TrFac, 0, {?nop, ?nop, ?nop, ?commit_mix, [?nop, ?nop, ?crash_transient(send_forget1), ?nop]}),
"TESTCASE #21: Resource 4 raise Heuristic Mixed during commit. SubCoord 2 crash transient send_forget1."),
?line ?match({'EXCEPTION', ?tr_mixed},
run(TrFac, 0, {?nop, ?nop, ?nop, ?commit_mix, [?crash_transient(send_forget1), ?nop, ?nop, ?nop]}),
"TESTCASE #22: Resource 4 raise Heuristic Mixed during commit. Root Coord crash transient send_forget1."),
?line ?match({'EXCEPTION', ?tr_mixed},
run(TrFac, 0, {?nop, ?nop, ?nop, ?commit_mix, [?crash_transient(send_forget3), ?nop, ?crash_transient(send_forget1), ?nop]}),
"TESTCASE #23: Resource 4 raise Heuristic Mixed during commit. Root Coord crash transient send_forget3. SubCoord 3 crash transient send_forget1."),
?line ?match({'EXCEPTION', #'TRANSACTION_ROLLEDBACK'{completion_status=?COMPLETED_YES}},
run(TrFac, ?TIMEOUT, {?nop, ?nop, ?nop, ?nop, [?delay_transient(root_delay, ?TIMEOUT*2), ?nop, ?nop, ?nop]}),
"TESTCASE #24: Delay RootCoord. Timeout."),
%% Testing the Terminator.
?line ?match({'EXCEPTION', ?tr_mixed},
run(TrFac, ?TIMEOUT, {?nop, ?prepare_mix, ?nop, ?nop, [?nop, ?nop, ?nop, ?crash_transient(commit_heuristic1)]}),
"TESTCASE #25: Terminator crash transient after received and logged Heuristic mix."),
?line ?match(ok,
run(TrFac, ?TIMEOUT, {?nop, ?nop, ?nop, ?nop, [?nop, ?nop, ?nop, ?crash_transient(commit_ok2)]}),
"TESTCASE #26: Terminator crash transient after received and logged 'ok'.");
_ ->
ok
end,
?line cosTransactions:stop_factory(TrFac),
?line application:stop(cosTransactions),
ok.
%%-----------------------------------------------------------------
%% Internal functions
%%-----------------------------------------------------------------
run(TrFac, Time, Spec) ->
Control = 'CosTransactions_TransactionFactory':create(TrFac, Time),
Term = 'CosTransactions_Control':get_terminator(Control),
Coord = 'CosTransactions_Control':get_coordinator(Control),
SubCont1 = 'CosTransactions_Coordinator':create_subtransaction(Coord),
SubCont2 = 'CosTransactions_Coordinator':create_subtransaction(Coord),
SubCoord1 = 'CosTransactions_Control':get_coordinator(SubCont1),
SubCoord2 = 'CosTransactions_Control':get_coordinator(SubCont2),
%% Start resources/participants.
{O1, O2, O3, O4, Ctx} = start_resources(Spec),
%% Get generated names to use for debugging.
CoordN = 'CosTransactions_Coordinator':get_transaction_name(Coord),
SubC1N = 'CosTransactions_Coordinator':get_transaction_name(SubCoord1),
SubC2N = 'CosTransactions_Coordinator':get_transaction_name(SubCoord2),
?set_debug_context([CoordN, SubC1N, SubC2N, Term], Ctx),
%% Register the resources as participants.
_RC1 = 'CosTransactions_Coordinator':register_resource(SubCoord1, O1),
_RC2 = 'CosTransactions_Coordinator':register_resource(SubCoord1, O2),
_RC3 = 'CosTransactions_Coordinator':register_resource(SubCoord2, O3),
_RC4 = 'CosTransactions_Coordinator':register_resource(SubCoord2, O4),
'CosTransactions_Coordinator':register_subtran_aware(SubCoord1, O4),
% 'CosTransactions_Coordinator':register_synchronization(SubCoord1, O2),
% Reply = (catch 'CosTransactions_Terminator':commit(Term, true)),
Reply = (catch 'ETraP_Common':send_stubborn('CosTransactions_Terminator',
commit, [Term, true],
?tr_max_retries,
?tr_comm_failure_wait)),
catch corba:dispose(SubCoord1),
catch corba:dispose(SubCoord2),
catch corba:dispose(SubCont1),
catch corba:dispose(SubCont2),
catch corba:dispose(Term),
catch corba:dispose(Control),
catch corba:dispose(Coord),
catch corba:dispose(O1),
catch corba:dispose(O2),
catch corba:dispose(O3),
catch corba:dispose(O4),
Reply.
start_resources({A1, A2, A3, A4})->
start_resources({A1, A2, A3, A4, ?no_context});
start_resources({A1, A2, A3, A4, Ctx})->
N1 = 'ETraP_Common':create_name("test"),
N2 = 'ETraP_Common':create_name("test"),
N3 = 'ETraP_Common':create_name("test"),
N4 = 'ETraP_Common':create_name("test"),
{_,_,O1} = supervisor:start_child(?SUPERVISOR_NAME, ?SUP_TEST(A1, N1)),
{_,_,O2} = supervisor:start_child(?SUPERVISOR_NAME, ?SUP_TEST(A2, N2)),
% {_,_,O2} = supervisor:start_child(?SUPERVISOR_NAME, ?SUP_TEST([{sync,true}|A2], N2)),
{_,_,O3} = supervisor:start_child(?SUPERVISOR_NAME, ?SUP_TEST(A3, N3)),
{_,_,O4} = supervisor:start_child(?SUPERVISOR_NAME, ?SUP_TEST(A4, N4)),
{O1, O2, O3, O4, Ctx}.