diff options
159 files changed, 9057 insertions, 110 deletions
diff --git a/.gitignore b/.gitignore index b35a6c1c31..592ac6668b 100644 --- a/.gitignore +++ b/.gitignore @@ -366,5 +366,6 @@ make/win32/ /lib/xmerl/src/xmerl_sax_parser_*.erl /lib/xmerl/src/xmerl_b64Bin.erl /lib/xmerl/src/xmerl_xpath_parse.erl +/lib/xmerl/test/xmerl_test.erl /lib/erl_interface/config.h.in /lib/configure.in diff --git a/lib/mnesia/doc/src/Mnesia_chap7.xmlsrc b/lib/mnesia/doc/src/Mnesia_chap7.xmlsrc index 7078499fbf..21174340d1 100644 --- a/lib/mnesia/doc/src/Mnesia_chap7.xmlsrc +++ b/lib/mnesia/doc/src/Mnesia_chap7.xmlsrc @@ -473,6 +473,13 @@ dets:close(N). </pre> <c>mnesia:table_info(Tab, master_nodes)</c> may be used to obtain information about the potential master nodes. </p> + <p>Determining which data to keep after communication failure is outside + the scope of Mnesia. One approach would be to determine which "island" + contains a majority of the nodes. Using the <c>{majority,true}</c> option + for critical tables can be a way of ensuring that nodes that are not part + of a "majority island" are not able to update those tables. Note that this + constitutes a reduction in service on the minority nodes. This would be + a tradeoff in favour of higher consistency guarantees.</p> <p>The function <c>mnesia:force_load_table(Tab)</c> may be used to force load the table regardless of which table load mechanism is activated. diff --git a/lib/mnesia/doc/src/mnesia.xml b/lib/mnesia/doc/src/mnesia.xml index 16e78ea0af..2a2c7d3a9f 100644 --- a/lib/mnesia/doc/src/mnesia.xml +++ b/lib/mnesia/doc/src/mnesia.xml @@ -161,6 +161,14 @@ If a new item is inserted with the same key as </p> </item> <item> + <p><c>majority</c> This attribute can be either <c>true</c> or + <c>false</c> (default is <c>false</c>). When <c>true</c>, a majority + of the table replicas must be available for an update to succeed. + Majority checking can be enabled on tables with mission-critical data, + where it is vital to avoid inconsistencies due to network splits. + </p> + </item> + <item> <p><c>snmp</c> Each (set based) Mnesia table can be automatically turned into an SNMP ordered table as well. This property specifies the types of the SNMP keys. @@ -650,6 +658,17 @@ mnesia:change_table_copy_type(person, node(), disc_copies) </desc> </func> <func> + <name>change_table_majority(Tab, Majority) -> {aborted, R} | {atomic, ok}</name> + <fsummary>Change the majority check setting for the table.</fsummary> + <desc> + <p><c>Majority</c> must be a boolean; the default is <c>false</c>. + When <c>true</c>, a majority of the table's replicas must be available + for an update to succeed. When used on fragmented tables, <c>Tab</c> + must be the name base table. Directly changing the majority setting on + individual fragments is not allowed.</p> + </desc> + </func> + <func> <name>clear_table(Tab) -> {aborted, R} | {atomic, ok}</name> <fsummary>Deletes all entries in a table.</fsummary> <desc> @@ -753,6 +772,14 @@ mnesia:change_table_copy_type(person, node(), disc_copies) priority will be loaded first at startup. </p> </item> + <item> + <p><c>{majority, Flag}</c>, where <c>Flag</c> must be a boolean. + If <c>true</c>, any (non-dirty) update to the table will abort unless + a majority of the table's replicas are available for the commit. + When used on a fragmented table, all fragments will be given + the same majority setting. + </p> + </item> <item> <p><c>{ram_copies, Nodelist}</c>, where <c>Nodelist</c> is a list of the nodes where this table @@ -1737,7 +1764,10 @@ mnesia:create_table(person, <c>write</c> and <c>sticky_write</c> are supported. </p> <p>If the user wants to update the record it is more efficient to - use <c>write/sticky_write</c> as the LockKind. + use <c>write/sticky_write</c> as the LockKind. If majority checking + is active on the table, it will be checked as soon as a write lock is + attempted. This can be used to quickly abort if the majority condition + isn't met. </p> </desc> </func> diff --git a/lib/mnesia/src/mnesia.erl b/lib/mnesia/src/mnesia.erl index 025b32f506..980a9c6213 100644 --- a/lib/mnesia/src/mnesia.erl +++ b/lib/mnesia/src/mnesia.erl @@ -39,6 +39,7 @@ %% Access within an activity - Lock acquisition lock/2, lock/4, + lock_table/2, read_lock_table/1, write_lock_table/1, @@ -92,7 +93,7 @@ add_table_copy/3, del_table_copy/2, move_table_copy/3, add_table_index/2, del_table_index/2, transform_table/3, transform_table/4, - change_table_copy_type/3, + change_table_copy_type/3, change_table_majority/2, read_table_property/2, write_table_property/2, delete_table_property/2, change_table_frag/2, clear_table/1, clear_table/4, @@ -415,6 +416,9 @@ lock(LockItem, LockKind) -> abort(no_transaction) end. +lock_table(Tab, LockKind) -> + lock({table, Tab}, LockKind). + lock(Tid, Ts, LockItem, LockKind) -> case element(1, Tid) of tid -> @@ -467,6 +471,8 @@ lock_table(Tid, Ts, Tab, LockKind) when is_atom(Tab) -> mnesia_locker:rlock_table(Tid, Store, Tab); write -> mnesia_locker:wlock_table(Tid, Store, Tab); + load -> + mnesia_locker:load_lock_table(Tid, Store, Tab); sticky_write -> mnesia_locker:sticky_wlock_table(Tid, Store, Tab); none -> @@ -2455,6 +2461,9 @@ change_table_access_mode(T, Access) -> change_table_load_order(T, O) -> mnesia_schema:change_table_load_order(T, O). +change_table_majority(T, M) -> + mnesia_schema:change_table_majority(T, M). + set_master_nodes(Nodes) when is_list(Nodes) -> UseDir = system_info(use_dir), IsRunning = system_info(is_running), diff --git a/lib/mnesia/src/mnesia.hrl b/lib/mnesia/src/mnesia.hrl index d488d9364a..26537815a3 100644 --- a/lib/mnesia/src/mnesia.hrl +++ b/lib/mnesia/src/mnesia.hrl @@ -62,6 +62,7 @@ disc_only_copies = [], % [Node] load_order = 0, % Integer access_mode = read_write, % read_write | read_only + majority = false, % true | false index = [], % [Integer] snmp = [], % Snmp Ustruct local_content = false, % true | false diff --git a/lib/mnesia/src/mnesia_controller.erl b/lib/mnesia/src/mnesia_controller.erl index 0254769758..d4b2c7b5cc 100644 --- a/lib/mnesia/src/mnesia_controller.erl +++ b/lib/mnesia/src/mnesia_controller.erl @@ -72,6 +72,7 @@ add_active_replica/4, update/1, change_table_access_mode/1, + change_table_majority/1, del_active_replica/2, wait_for_tables/2, get_network_copy/2, @@ -690,7 +691,8 @@ handle_call({update_where_to_write, [add, Tab, AddNode], _From}, _Dummy, State) case lists:member(AddNode, Current) and (State#state.schema_is_merged == true) of true -> - mnesia_lib:add_lsort({Tab, where_to_write}, AddNode); + mnesia_lib:add_lsort({Tab, where_to_write}, AddNode), + update_where_to_wlock(Tab); false -> ignore end, @@ -1690,6 +1692,8 @@ add_active_replica(Tab, Node, Storage, AccessMode) -> set(Var, mark_blocked_tab(Blocked, Del)), mnesia_lib:del({Tab, where_to_write}, Node) end, + + update_where_to_wlock(Tab), add({Tab, active_replicas}, Node). del_active_replica(Tab, Node) -> @@ -1699,7 +1703,8 @@ del_active_replica(Tab, Node) -> New = lists:sort(Del), set(Var, mark_blocked_tab(Blocked, New)), % where_to_commit mnesia_lib:del({Tab, active_replicas}, Node), - mnesia_lib:del({Tab, where_to_write}, Node). + mnesia_lib:del({Tab, where_to_write}, Node), + update_where_to_wlock(Tab). change_table_access_mode(Cs) -> W = fun() -> @@ -1708,7 +1713,22 @@ change_table_access_mode(Cs) -> val({Tab, active_replicas})) end, update(W). - + +change_table_majority(Cs) -> + W = fun() -> + Tab = Cs#cstruct.name, + set({Tab, majority}, Cs#cstruct.majority), + update_where_to_wlock(Tab) + end, + update(W). + +update_where_to_wlock(Tab) -> + WNodes = val({Tab, where_to_write}), + Majority = case catch val({Tab, majority}) of + true -> true; + _ -> false + end, + set({Tab, where_to_wlock}, {WNodes, Majority}). %% node To now has tab loaded, but this must be undone %% This code is rpc:call'ed from the tab_copier process diff --git a/lib/mnesia/src/mnesia_dumper.erl b/lib/mnesia/src/mnesia_dumper.erl index 55b9946ae9..92fd9dfade 100644 --- a/lib/mnesia/src/mnesia_dumper.erl +++ b/lib/mnesia/src/mnesia_dumper.erl @@ -900,6 +900,14 @@ insert_op(Tid, _, {op, change_table_access_mode,TabDef, _OldAccess, _Access}, In end, insert_cstruct(Tid, Cs, true, InPlace, InitBy); +insert_op(Tid, _, {op, change_table_majority,TabDef, _OldAccess, _Access}, InPlace, InitBy) -> + Cs = mnesia_schema:list2cs(TabDef), + case InitBy of + startup -> ignore; + _ -> mnesia_controller:change_table_majority(Cs) + end, + insert_cstruct(Tid, Cs, true, InPlace, InitBy); + insert_op(Tid, _, {op, change_table_load_order, TabDef, _OldLevel, _Level}, InPlace, InitBy) -> Cs = mnesia_schema:list2cs(TabDef), insert_cstruct(Tid, Cs, true, InPlace, InitBy); diff --git a/lib/mnesia/src/mnesia_lib.erl b/lib/mnesia/src/mnesia_lib.erl index 36bcfe8de9..7e926a6258 100644 --- a/lib/mnesia/src/mnesia_lib.erl +++ b/lib/mnesia/src/mnesia_lib.erl @@ -96,6 +96,8 @@ exists/1, fatal/2, get_node_number/0, + have_majority/2, + have_majority/3, fix_error/1, important/2, incr_counter/1, @@ -660,6 +662,14 @@ proc_info(_) -> false. get_node_number() -> {node(), self()}. +have_majority(Tab, HaveNodes) -> + have_majority(Tab, val({Tab, all_nodes}), HaveNodes). + +have_majority(_Tab, AllNodes, HaveNodes) -> + Missing = AllNodes -- HaveNodes, + Present = AllNodes -- Missing, + length(Present) > length(Missing). + read_log_files() -> [{F, catch file:read_file(F)} || F <- mnesia_log:log_files()]. diff --git a/lib/mnesia/src/mnesia_loader.erl b/lib/mnesia/src/mnesia_loader.erl index 3de329503e..e785b795d1 100644 --- a/lib/mnesia/src/mnesia_loader.erl +++ b/lib/mnesia/src/mnesia_loader.erl @@ -702,7 +702,7 @@ send_table(Pid, Tab, RemoteS) -> prepare_copy(Pid, Tab, Storage) -> Trans = fun() -> - mnesia:write_lock_table(Tab), + mnesia:lock_table(Tab, load), mnesia_subscr:subscribe(Pid, {table, Tab}), update_where_to_write(Tab, node(Pid)), mnesia_lib:db_fixtable(Storage, Tab, true), diff --git a/lib/mnesia/src/mnesia_locker.erl b/lib/mnesia/src/mnesia_locker.erl index ca0cc79c45..0492d794f3 100644 --- a/lib/mnesia/src/mnesia_locker.erl +++ b/lib/mnesia/src/mnesia_locker.erl @@ -40,7 +40,8 @@ sticky_wlock_table/3, wlock/3, wlock_no_exist/4, - wlock_table/3 + wlock_table/3, + load_lock_table/3 ]). %% sys callback functions @@ -656,16 +657,17 @@ rwlock(Tid, Store, Oid) -> Lock = write, case need_lock(Store, Tab, Key, Lock) of yes -> - Ns = w_nodes(Tab), + {Ns, Majority} = w_nodes(Tab), + check_majority(Majority, Tab, Ns), Res = get_rwlocks_on_nodes(Ns, rwlock, Node, Store, Tid, Oid), ?ets_insert(Store, {{locks, Tab, Key}, Lock}), Res; no -> if Key == ?ALL -> - w_nodes(Tab); + element(2, w_nodes(Tab)); Tab == ?GLOBAL -> - w_nodes(Tab); + element(2, w_nodes(Tab)); true -> dirty_rpc(Node, Tab, Key, Lock) end @@ -677,12 +679,34 @@ rwlock(Tid, Store, Oid) -> %% in the local store under the key == nodes w_nodes(Tab) -> - Nodes = ?catch_val({Tab, where_to_write}), - case Nodes of - [_ | _] -> Nodes; + case ?catch_val({Tab, where_to_wlock}) of + {[_ | _], _} = Where -> Where; _ -> mnesia:abort({no_exists, Tab}) end. +%% If the table has the 'majority' flag set, we can +%% only take a write lock if we see a majority of the +%% nodes. + + +check_majority(true, Tab, HaveNs) -> + check_majority(Tab, HaveNs); +check_majority(false, _, _) -> + ok. + +check_majority(Tab, HaveNs) -> + case ?catch_val({Tab, majority}) of + true -> + case mnesia_lib:have_majority(Tab, HaveNs) of + true -> + ok; + false -> + mnesia:abort({no_majority, Tab}) + end; + _ -> + ok + end. + %% aquire a sticky wlock, a sticky lock is a lock %% which remains at this node after the termination of the %% transaction. @@ -708,12 +732,14 @@ sticky_lock(Tid, Store, {Tab, Key} = Oid, Lock) -> end. do_sticky_lock(Tid, Store, {Tab, Key} = Oid, Lock) -> + {WNodes, Majority} = w_nodes(Tab), + sticky_check_majority(Lock, Tab, Majority, WNodes), ?MODULE ! {self(), {test_set_sticky, Tid, Oid, Lock}}, N = node(), receive {?MODULE, N, granted} -> ?ets_insert(Store, {{locks, Tab, Key}, write}), - [?ets_insert(Store, {nodes, Node}) || Node <- w_nodes(Tab)], + [?ets_insert(Store, {nodes, Node}) || Node <- WNodes], granted; {?MODULE, N, {granted, Val}} -> %% for rwlocks case opt_lookup_in_client(Val, Oid, write) of @@ -721,7 +747,7 @@ do_sticky_lock(Tid, Store, {Tab, Key} = Oid, Lock) -> exit({aborted, C}); Val2 -> ?ets_insert(Store, {{locks, Tab, Key}, write}), - [?ets_insert(Store, {nodes, Node}) || Node <- w_nodes(Tab)], + [?ets_insert(Store, {nodes, Node}) || Node <- WNodes], Val2 end; {?MODULE, N, {not_granted, Reason}} -> @@ -737,6 +763,16 @@ do_sticky_lock(Tid, Store, {Tab, Key} = Oid, Lock) -> dirty_sticky_lock(Tab, Key, [N], Lock) end. +sticky_check_majority(W, Tab, true, WNodes) when W==write; W==read_write -> + case mnesia_lib:have_majority(Tab, WNodes) of + true -> + ok; + false -> + mnesia:abort({no_majority, Tab}) + end; +sticky_check_majority(_, _, _, _) -> + ok. + not_stuck(Tid, Store, Tab, _Key, Oid, _Lock, N) -> rlock(Tid, Store, {Tab, ?ALL}), %% needed? wlock(Tid, Store, Oid), %% perfect sync @@ -773,22 +809,33 @@ sticky_wlock_table(Tid, Store, Tab) -> %% local store when we have aquired the lock. %% wlock(Tid, Store, Oid) -> + wlock(Tid, Store, Oid, _CheckMajority = true). + +wlock(Tid, Store, Oid, CheckMajority) -> {Tab, Key} = Oid, case need_lock(Store, Tab, Key, write) of yes -> - Ns = w_nodes(Tab), + {Ns, Majority} = w_nodes(Tab), + if CheckMajority -> + check_majority(Majority, Tab, Ns); + true -> + ignore + end, Op = {self(), {write, Tid, Oid}}, ?ets_insert(Store, {{locks, Tab, Key}, write}), get_wlocks_on_nodes(Ns, Ns, Store, Op, Oid); no when Key /= ?ALL, Tab /= ?GLOBAL -> []; no -> - w_nodes(Tab) + element(2, w_nodes(Tab)) end. wlock_table(Tid, Store, Tab) -> wlock(Tid, Store, {Tab, ?ALL}). +load_lock_table(Tid, Store, Tab) -> + wlock(Tid, Store, {Tab, ?ALL}, _CheckMajority = false). + %% Write lock even if the table does not exist wlock_no_exist(Tid, Store, Tab, Ns) -> diff --git a/lib/mnesia/src/mnesia_schema.erl b/lib/mnesia/src/mnesia_schema.erl index f33a6c7a84..fef72ad39c 100644 --- a/lib/mnesia/src/mnesia_schema.erl +++ b/lib/mnesia/src/mnesia_schema.erl @@ -37,6 +37,7 @@ change_table_copy_type/3, change_table_access_mode/2, change_table_load_order/2, + change_table_majority/2, change_table_frag/2, clear_table/1, create_table/1, @@ -178,6 +179,8 @@ do_set_schema(Tab, Cs) -> set({Tab, disc_only_copies}, Cs#cstruct.disc_only_copies), set({Tab, load_order}, Cs#cstruct.load_order), set({Tab, access_mode}, Cs#cstruct.access_mode), + set({Tab, majority}, Cs#cstruct.majority), + set({Tab, all_nodes}, mnesia_lib:cs_to_nodes(Cs)), set({Tab, snmp}, Cs#cstruct.snmp), set({Tab, user_properties}, Cs#cstruct.user_properties), [set({Tab, user_property, element(1, P)}, P) || P <- Cs#cstruct.user_properties], @@ -651,6 +654,7 @@ list2cs(List) when is_list(List) -> Snmp = pick(Name, snmp, List, []), LoadOrder = pick(Name, load_order, List, 0), AccessMode = pick(Name, access_mode, List, read_write), + Majority = pick(Name, majority, List, false), UserProps = pick(Name, user_properties, List, []), verify({alt, [nil, list]}, mnesia_lib:etype(UserProps), {bad_type, Name, {user_properties, UserProps}}), @@ -676,6 +680,7 @@ list2cs(List) when is_list(List) -> snmp = Snmp, load_order = LoadOrder, access_mode = AccessMode, + majority = Majority, local_content = LC, record_name = RecName, attributes = Attrs, @@ -809,7 +814,16 @@ verify_cstruct(Cs) when is_record(Cs, cstruct) -> Access = Cs#cstruct.access_mode, verify({alt, [read_write, read_only]}, Access, {bad_type, Tab, {access_mode, Access}}), - + Majority = Cs#cstruct.majority, + verify({alt, [true, false]}, Majority, + {bad_type, Tab, {majority, Majority}}), + case Majority of + true -> + verify(false, LC, + {combine_error, Tab, [{local_content,true},{majority,true}]}); + false -> + ok + end, Snmp = Cs#cstruct.snmp, verify(true, mnesia_snmp_hook:check_ustruct(Snmp), {badarg, Tab, {snmp, Snmp}}), @@ -1495,6 +1509,43 @@ make_change_table_load_order(Tab, LoadOrder) -> %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% +change_table_majority(Tab, Majority) when is_boolean(Majority) -> + schema_transaction(fun() -> do_change_table_majority(Tab, Majority) end). + +do_change_table_majority(schema, _Majority) -> + mnesia:abort({bad_type, schema}); +do_change_table_majority(Tab, Majority) -> + TidTs = get_tid_ts_and_lock(schema, write), + get_tid_ts_and_lock(Tab, none), + insert_schema_ops(TidTs, make_change_table_majority(Tab, Majority)). + +make_change_table_majority(Tab, Majority) -> + ensure_writable(schema), + Cs = incr_version(val({Tab, cstruct})), + ensure_active(Cs), + OldMajority = Cs#cstruct.majority, + Cs2 = Cs#cstruct{majority = Majority}, + FragOps = case lists:keyfind(base_table, 1, Cs#cstruct.frag_properties) of + {_, Tab} -> + FragNames = mnesia_frag:frag_names(Tab) -- [Tab], + lists:map( + fun(T) -> + get_tid_ts_and_lock(Tab, none), + CsT = incr_version(val({T, cstruct})), + ensure_active(CsT), + CsT2 = CsT#cstruct{majority = Majority}, + verify_cstruct(CsT2), + {op, change_table_majority, cs2list(CsT2), + OldMajority, Majority} + end, FragNames); + false -> []; + {_, _} -> mnesia:abort({bad_type, Tab}) + end, + verify_cstruct(Cs2), + [{op, change_table_majority, cs2list(Cs2), OldMajority, Majority} | FragOps]. + +%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% + write_table_property(Tab, Prop) when is_tuple(Prop), size(Prop) >= 1 -> schema_transaction(fun() -> do_write_table_property(Tab, Prop) end); write_table_property(Tab, Prop) -> @@ -2974,6 +3025,7 @@ merge_versions(AnythingNew, Cs, RemoteCs, Force) -> Cs#cstruct.index == RemoteCs#cstruct.index, Cs#cstruct.snmp == RemoteCs#cstruct.snmp, Cs#cstruct.access_mode == RemoteCs#cstruct.access_mode, + Cs#cstruct.majority == RemoteCs#cstruct.majority, Cs#cstruct.load_order == RemoteCs#cstruct.load_order, Cs#cstruct.user_properties == RemoteCs#cstruct.user_properties -> do_merge_versions(AnythingNew, Cs, RemoteCs); diff --git a/lib/mnesia/src/mnesia_tm.erl b/lib/mnesia/src/mnesia_tm.erl index bb8e788b40..f62f7cb7c8 100644 --- a/lib/mnesia/src/mnesia_tm.erl +++ b/lib/mnesia/src/mnesia_tm.erl @@ -64,7 +64,8 @@ prev_tab = [], % initiate to a non valid table name prev_types, prev_snmp, - types + types, + majority = [] }). -record(participant, {tid, pid, commit, disc_nodes = [], @@ -1100,9 +1101,12 @@ t_commit(Type) -> case arrange(Tid, Store, Type) of {N, Prep} when N > 0 -> multi_commit(Prep#prep.protocol, + majority_attr(Prep), Tid, Prep#prep.records, Store); {0, Prep} -> - multi_commit(read_only, Tid, Prep#prep.records, Store) + multi_commit(read_only, + majority_attr(Prep), + Tid, Prep#prep.records, Store) end; true -> %% nested commit @@ -1117,6 +1121,10 @@ t_commit(Type) -> do_commit_nested end. +majority_attr(#prep{majority = M}) -> + M. + + %% This function arranges for all objects we shall write in S to be %% in a list of {Node, CommitRecord} %% Important function for the performance of mnesia. @@ -1222,11 +1230,13 @@ prepare_items(Tid, Tab, Key, Items, Prep) -> {blocked, _} -> unblocked = req({unblock_me, Tab}), prepare_items(Tid, Tab, Key, Items, Prep); - _ -> + _ -> + Majority = needs_majority(Tab, Prep), Snmp = val({Tab, snmp}), Recs2 = do_prepare_items(Tid, Tab, Key, Types, Snmp, Items, Prep#prep.records), Prep2 = Prep#prep{records = Recs2, prev_tab = Tab, + majority = Majority, prev_types = Types, prev_snmp = Snmp}, check_prep(Prep2, Types) end. @@ -1235,6 +1245,33 @@ do_prepare_items(Tid, Tab, Key, Types, Snmp, Items, Recs) -> Recs2 = prepare_snmp(Tid, Tab, Key, Types, Snmp, Items, Recs), % May exit prepare_nodes(Tid, Types, Items, Recs2, normal). + +needs_majority(Tab, #prep{majority = M}) -> + case lists:keymember(Tab, 1, M) of + true -> + M; + false -> + case ?catch_val({Tab, majority}) of + {'EXIT', _} -> + M; + false -> + M; + true -> + CopyHolders = val({Tab, all_nodes}), + [{Tab, CopyHolders} | M] + end + end. + +have_majority([], _) -> + ok; +have_majority([{Tab, AllNodes} | Rest], Nodes) -> + case mnesia_lib:have_majority(Tab, AllNodes, Nodes) of + true -> + have_majority(Rest, Nodes); + false -> + {error, Tab} + end. + prepare_snmp(Tab, Key, Items) -> case val({Tab, snmp}) of [] -> @@ -1261,10 +1298,15 @@ prepare_snmp(Tid, Tab, Key, Types, Us, Items, Recs) -> prepare_nodes(Tid, Types, [{clear_table, Tab}], Recs, snmp) end. -check_prep(Prep, Types) when Prep#prep.types == Types -> +check_prep(#prep{majority = [], types = Types} = Prep, Types) -> Prep; -check_prep(Prep, Types) when Prep#prep.types == undefined -> - Prep#prep{types = Types}; +check_prep(#prep{majority = M, types = undefined} = Prep, Types) -> + Protocol = if M == [] -> + Prep#prep.protocol; + true -> + asym_trans + end, + Prep#prep{protocol = Protocol, types = Types}; check_prep(Prep, _Types) -> Prep#prep{protocol = asym_trans}. @@ -1311,7 +1353,7 @@ prepare_node(_Node, _Storage, [], Rec, _Kind) -> %% multi_commit((Protocol, Tid, CommitRecords, Store) %% Local work is always performed in users process -multi_commit(read_only, Tid, CR, _Store) -> +multi_commit(read_only, _Maj = [], Tid, CR, _Store) -> %% This featherweight commit protocol is used when no %% updates has been performed in the transaction. @@ -1324,7 +1366,7 @@ multi_commit(read_only, Tid, CR, _Store) -> ?MODULE ! {delete_transaction, Tid}, do_commit; -multi_commit(sym_trans, Tid, CR, Store) -> +multi_commit(sym_trans, _Maj = [], Tid, CR, Store) -> %% This lightweight commit protocol is used when all %% the involved tables are replicated symetrically. %% Their storage types must match on each node. @@ -1376,7 +1418,7 @@ multi_commit(sym_trans, Tid, CR, Store) -> [{tid, Tid}, {outcome, Outcome}]), Outcome; -multi_commit(sync_sym_trans, Tid, CR, Store) -> +multi_commit(sync_sym_trans, _Maj = [], Tid, CR, Store) -> %% This protocol is the same as sym_trans except that it %% uses syncronized calls to disk_log and syncronized commits %% when several nodes are involved. @@ -1408,7 +1450,7 @@ multi_commit(sync_sym_trans, Tid, CR, Store) -> [{tid, Tid}, {outcome, Outcome}]), Outcome; -multi_commit(asym_trans, Tid, CR, Store) -> +multi_commit(asym_trans, Majority, Tid, CR, Store) -> %% This more expensive commit protocol is used when %% table definitions are changed (schema transactions). %% It is also used when the involved tables are @@ -1469,6 +1511,10 @@ multi_commit(asym_trans, Tid, CR, Store) -> {D2, CR2} = commit_decision(D, CR, [], []), DiscNs = D2#decision.disc_nodes, RamNs = D2#decision.ram_nodes, + case have_majority(Majority, DiscNs ++ RamNs) of + ok -> ok; + {error, Tab} -> mnesia:abort({no_majority, Tab}) + end, Pending = mnesia_checkpoint:tm_enter_pending(Tid, DiscNs, RamNs), ?ets_insert(Store, Pending), {WaitFor, Local} = ask_commit(asym_trans, Tid, CR2, DiscNs, RamNs), diff --git a/lib/mnesia/test/Makefile b/lib/mnesia/test/Makefile index b165924ef2..ae4c9626c7 100644 --- a/lib/mnesia/test/Makefile +++ b/lib/mnesia/test/Makefile @@ -42,6 +42,7 @@ MODULES= \ mnesia_dirty_access_test \ mnesia_atomicity_test \ mnesia_consistency_test \ + mnesia_majority_test \ mnesia_isolation_test \ mnesia_durability_test \ mnesia_recovery_test \ diff --git a/lib/mnesia/test/mnesia_SUITE.erl b/lib/mnesia/test/mnesia_SUITE.erl index 8ba8427213..dc8f216c1c 100644 --- a/lib/mnesia/test/mnesia_SUITE.erl +++ b/lib/mnesia/test/mnesia_SUITE.erl @@ -142,6 +142,105 @@ silly() -> mnesia_install_test:silly(). %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% +light(doc) -> + ["The 'light' test suite runs a selected set of test suites and is", + "intended to be the smallest test suite that is meaningful", + "to run. It starts with an installation test (which in essence is the", + "'silly' test case) and then it covers all functions in the API in", + "various depths. All configuration parameters and examples are also", + "covered."]; +light(suite) -> + [ + install, + nice, + evil, + {mnesia_frag_test, light}, + qlc, + registry, + config, + examples + ]. + +install(suite) -> + [{mnesia_install_test, all}]. + +nice(suite) -> + [{mnesia_nice_coverage_test, all}]. + +evil(suite) -> + [{mnesia_evil_coverage_test, all}]. + +qlc(suite) -> + [{mnesia_qlc_test, all}]. + +registry(suite) -> + [{mnesia_registry_test, all}]. + +config(suite) -> + [{mnesia_config_test, all}]. + +examples(suite) -> + [{mnesia_examples_test, all}]. + +%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% +medium(doc) -> + ["The 'medium' test suite verfies the ACID (atomicity, consistency", + "isolation and durability) properties and various recovery scenarios", + "These tests may take quite while to run."]; +medium(suite) -> + [ + install, + atomicity, + isolation, + durability, + recovery, + consistency, + majority, + {mnesia_frag_test, medium} + ]. + +atomicity(suite) -> + [{mnesia_atomicity_test, all}]. + +isolation(suite) -> + [{mnesia_isolation_test, all}]. + +durability(suite) -> + [{mnesia_durability_test, all}]. + +recovery(suite) -> + [{mnesia_recovery_test, all}]. + +consistency(suite) -> + [{mnesia_consistency_test, all}]. + +majority(suite) -> + [{mnesia_majority_test, all}]. + +%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% +heavy(doc) -> + ["The 'heavy' test suite runs some resource consuming tests and", + "benchmarks"]; +heavy(suite) -> + [measure]. + +measure(suite) -> + [{mnesia_measure_test, all}]. + +prediction(suite) -> + [{mnesia_measure_test, prediction}]. + +fairness(suite) -> + [{mnesia_measure_test, fairness}]. + +benchmarks(suite) -> + [{mnesia_measure_test, benchmarks}]. + +consumption(suite) -> + [{mnesia_measure_test, consumption}]. + +scalability(suite) -> + [{mnesia_measure_test, scalability}]. clean_up_suite(doc) -> ["Not a test case only kills mnesia and nodes, that where" "started during the tests"]; diff --git a/lib/mnesia/test/mnesia_majority_test.erl b/lib/mnesia/test/mnesia_majority_test.erl new file mode 100644 index 0000000000..17d1d8bcdd --- /dev/null +++ b/lib/mnesia/test/mnesia_majority_test.erl @@ -0,0 +1,188 @@ +%% +%% %CopyrightBegin% +%% +%% Copyright Ericsson AB 1996-2009. 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(mnesia_majority_test). +-author('[email protected]'). +-compile(export_all). +-include("mnesia_test_lib.hrl"). + +init_per_testcase(Func, Conf) -> + mnesia_test_lib:init_per_testcase(Func, Conf). + +fin_per_testcase(Func, Conf) -> + mnesia_test_lib:fin_per_testcase(Func, Conf). + +%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% +all(doc) -> + ["Verify that majority checking works"]; +all(suite) -> + [ + write + , wread + , delete + , clear_table + , frag + , change_majority + , frag_change_majority + ]. + +write(suite) -> []; +write(Config) when is_list(Config) -> + [N1, N2, N3] = Nodes = ?acquire_nodes(3, Config), + Tab = t, + Schema = [{name, Tab}, {ram_copies, [N1,N2,N3]}, {majority,true}], + ?match({atomic, ok}, mnesia:create_table(Schema)), + ?match({[ok,ok,ok],[]}, + rpc:multicall([N1,N2,N3], mnesia, wait_for_tables, [[Tab], 3000])), + ?match({atomic,ok}, + mnesia:transaction(fun() -> mnesia:write({t,1,a}) end)), + mnesia_test_lib:kill_mnesia([N3]), + ?match({atomic,ok}, + mnesia:transaction(fun() -> mnesia:write({t,1,a}) end)), + mnesia_test_lib:kill_mnesia([N2]), + ?match({aborted,{no_majority,Tab}}, + mnesia:transaction(fun() -> mnesia:write({t,1,a}) end)). + +wread(suite) -> []; +wread(Config) when is_list(Config) -> + [N1, N2] = Nodes = ?acquire_nodes(2, Config), + Tab = t, + Schema = [{name, Tab}, {ram_copies, [N1,N2]}, {majority,true}], + ?match({atomic, ok}, mnesia:create_table(Schema)), + ?match({[ok,ok],[]}, + rpc:multicall([N1,N2], mnesia, wait_for_tables, [[Tab], 3000])), + ?match({atomic,[]}, + mnesia:transaction(fun() -> mnesia:read(t,1,write) end)), + mnesia_test_lib:kill_mnesia([N2]), + ?match({aborted,{no_majority,Tab}}, + mnesia:transaction(fun() -> mnesia:read(t,1,write) end)). + +delete(suite) -> []; +delete(Config) when is_list(Config) -> + [N1, N2] = Nodes = ?acquire_nodes(2, Config), + Tab = t, + Schema = [{name, Tab}, {ram_copies, [N1,N2]}, {majority,true}], + ?match({atomic, ok}, mnesia:create_table(Schema)), + ?match({[ok,ok],[]}, + rpc:multicall([N1,N2], mnesia, wait_for_tables, [[Tab], 3000])), + %% works as expected with majority of nodes present + ?match({atomic,ok}, + mnesia:transaction(fun() -> mnesia:write({t,1,a}) end)), + ?match({atomic,ok}, + mnesia:transaction(fun() -> mnesia:delete({t,1}) end)), + ?match({atomic,[]}, + mnesia:transaction(fun() -> mnesia:read({t,1}) end)), + %% put the record back + ?match({atomic,ok}, + mnesia:transaction(fun() -> mnesia:write({t,1,a}) end)), + ?match({atomic,[{t,1,a}]}, + mnesia:transaction(fun() -> mnesia:read({t,1}) end)), + mnesia_test_lib:kill_mnesia([N2]), + ?match({aborted,{no_majority,Tab}}, + mnesia:transaction(fun() -> mnesia:delete({t,1}) end)). + +clear_table(suite) -> []; +clear_table(Config) when is_list(Config) -> + [N1, N2] = Nodes = ?acquire_nodes(2, Config), + Tab = t, + Schema = [{name, Tab}, {ram_copies, [N1,N2]}, {majority,true}], + ?match({atomic, ok}, mnesia:create_table(Schema)), + ?match({[ok,ok],[]}, + rpc:multicall([N1,N2], mnesia, wait_for_tables, [[Tab], 3000])), + %% works as expected with majority of nodes present + ?match({atomic,ok}, + mnesia:transaction(fun() -> mnesia:write({t,1,a}) end)), + ?match({atomic,ok}, mnesia:clear_table(t)), + ?match({atomic,[]}, + mnesia:transaction(fun() -> mnesia:read({t,1}) end)), + %% put the record back + ?match({atomic,ok}, + mnesia:transaction(fun() -> mnesia:write({t,1,a}) end)), + ?match({atomic,[{t,1,a}]}, + mnesia:transaction(fun() -> mnesia:read({t,1}) end)), + mnesia_test_lib:kill_mnesia([N2]), + ?match({aborted,{no_majority,Tab}}, mnesia:clear_table(t)). + +frag(suite) -> []; +frag(Config) when is_list(Config) -> + [N1] = Nodes = ?acquire_nodes(1, Config), + Tab = t, + Schema = [ + {name, Tab}, {ram_copies, [N1]}, + {majority,true}, + {frag_properties, [{n_fragments, 2}]} + ], + ?match({atomic, ok}, mnesia:create_table(Schema)), + ?match(true, mnesia:table_info(t, majority)), + ?match(true, mnesia:table_info(t_frag2, majority)). + +change_majority(suite) -> []; +change_majority(Config) when is_list(Config) -> + [N1,N2] = Nodes = ?acquire_nodes(2, Config), + Tab = t, + Schema = [ + {name, Tab}, {ram_copies, [N1,N2]}, + {majority,false} + ], + ?match({atomic, ok}, mnesia:create_table(Schema)), + ?match(false, mnesia:table_info(t, majority)), + ?match({atomic, ok}, + mnesia:change_table_majority(t, true)), + ?match(true, mnesia:table_info(t, majority)), + ?match(ok, + mnesia:activity(transaction, fun() -> + mnesia:write({t,1,a}) + end)), + mnesia_test_lib:kill_mnesia([N2]), + ?match({'EXIT',{aborted,{no_majority,_}}}, + mnesia:activity(transaction, fun() -> + mnesia:write({t,1,a}) + end)). + +frag_change_majority(suite) -> []; +frag_change_majority(Config) when is_list(Config) -> + [N1,N2] = Nodes = ?acquire_nodes(2, Config), + Tab = t, + Schema = [ + {name, Tab}, {ram_copies, [N1,N2]}, + {majority,false}, + {frag_properties, + [{n_fragments, 2}, + {n_ram_copies, 2}, + {node_pool, [N1,N2]}]} + ], + ?match({atomic, ok}, mnesia:create_table(Schema)), + ?match(false, mnesia:table_info(t, majority)), + ?match(false, mnesia:table_info(t_frag2, majority)), + ?match({aborted,{bad_type,t_frag2}}, + mnesia:change_table_majority(t_frag2, true)), + ?match({atomic, ok}, + mnesia:change_table_majority(t, true)), + ?match(true, mnesia:table_info(t, majority)), + ?match(true, mnesia:table_info(t_frag2, majority)), + ?match(ok, + mnesia:activity(transaction, fun() -> + mnesia:write({t,1,a}) + end, mnesia_frag)), + mnesia_test_lib:kill_mnesia([N2]), + ?match({'EXIT',{aborted,{no_majority,_}}}, + mnesia:activity(transaction, fun() -> + mnesia:write({t,1,a}) + end, mnesia_frag)). diff --git a/lib/reltool/src/reltool_server.erl b/lib/reltool/src/reltool_server.erl index 9743289ca6..692baea0a4 100644 --- a/lib/reltool/src/reltool_server.erl +++ b/lib/reltool/src/reltool_server.erl @@ -465,16 +465,16 @@ analyse(#state{common = C, [MissingApp2 | Apps2] end, app_propagate_is_used_by(C, Apps3), - Apps4 = read_apps(C, Sys, Apps3, []), + {Apps4,Status4} = app_recap_dependencies(C, Sys, Apps3, [], Status3), %% io:format("Missing app: ~p\n", %% [lists:keysearch(?MISSING_APP_NAME, #app.name, Apps4)]), Sys2 = Sys#sys{apps = Apps4}, - case verify_config(RelApps2, Sys2, Status3) of - {ok, _Warnings} = Status4 -> - {S#state{sys = Sys2}, Status4}; - {error, _} = Status4 -> - {S, Status4} + case verify_config(RelApps2, Sys2, Status4) of + {ok, _Warnings} = Status5 -> + {S#state{sys = Sys2}, Status5}; + {error, _} = Status5 -> + {S, Status5} end. apps_in_rels(Rels, Apps, Status) -> @@ -548,21 +548,24 @@ app_init_is_included(C, {derived, [_ | _]} -> % App is included in at least one rel {true, undefined, true, Status} end, - A2 = A#app{is_pre_included = IsPreIncl, + {Mods2,Status3} = lists:mapfoldl(fun(Mod,Acc) -> + mod_init_is_included(C, + Mod, + ModCond, + AppCond, + Default, + Acc) + end, + Status2, + Mods), + A2 = A#app{mods = Mods2, + is_pre_included = IsPreIncl, is_included = IsIncl, rels = Rels}, ets:insert(C#common.app_tab, A2), - lists:foreach(fun(Mod) -> - mod_init_is_included(C, - Mod, - ModCond, - AppCond, - Default) - end, - Mods), - {A2, Status2}. - -mod_init_is_included(C, M, ModCond, AppCond, Default) -> + {A2, Status3}. + +mod_init_is_included(C, M, ModCond, AppCond, Default, Status) -> %% print(M#mod.name, hipe, "incl_cond -> ~p\n", [AppCond]), IsIncl = case AppCond of @@ -595,9 +598,52 @@ mod_init_is_included(C, M, ModCond, AppCond, Default) -> Default end end, + M2 = M#mod{is_pre_included = IsIncl, is_included = IsIncl}, + + Status2 = + case ets:lookup(C#common.mod_tab,M#mod.name) of + [Existing] -> + case {Existing#mod.is_included,IsIncl} of + {false,_} -> + Warning = + lists:concat( + ["Module ",M#mod.name, + " exists in applications ", Existing#mod.app_name, + " and ", M#mod.app_name, + ". Using module from application ", + M#mod.app_name, "."]), + ets:insert(C#common.mod_tab, M2), + reltool_utils:add_warning(Status,Warning); + {_,false} -> + Warning = + lists:concat( + ["Module ",M#mod.name, + " exists in applications ", Existing#mod.app_name, + " and ", M#mod.app_name, + ". Using module from application ", + Existing#mod.app_name, "."]), + + %% Don't insert in mod_tab - using Existing + reltool_utils:add_warning(Status,Warning); + {_,_} -> + Error = + lists:concat( + ["Module ",M#mod.name, + " potentially included by ", + "two different applications: ", + Existing#mod.app_name, " and ", + M#mod.app_name, "."]), + %% Don't insert in mod_tab - using Existing + reltool_utils:return_first_error(Status,Error) + end; + [] -> + ets:insert(C#common.mod_tab, M2), + Status + end, + %% print(M#mod.name, hipe, "~p -> ~p\n", [M2, IsIncl]), - ets:insert(C#common.mod_tab, M2). + {M2,Status2}. false_to_undefined(Bool) -> case Bool of @@ -612,21 +658,27 @@ app_propagate_is_included(_C, _Sys, [], Acc) -> Acc. mod_propagate_is_included(C, Sys, A, [#mod{name = ModName} | Mods], Acc) -> - [M2] = ets:lookup(C#common.mod_tab, ModName), - %% print(ModName, file, "Maybe Prop ~p -> ~p\n", - %% [M2, M2#mod.is_included]), - %% print(ModName, filename, "Maybe Prop ~p -> ~p\n", - %% [M2, M2#mod.is_included]), Acc2 = - case M2#mod.is_included of - true -> - %% Propagate include mark - mod_mark_is_included(C, Sys, ModName, M2#mod.uses_mods, Acc); - false -> - Acc; - undefined -> - Acc - end, + case ets:lookup(C#common.mod_tab, ModName) of + [M2] when M2#mod.app_name=:=A#app.name -> + %% print(ModName, file, "Maybe Prop ~p -> ~p\n", + %% [M2, M2#mod.is_included]), + %% print(ModName, filename, "Maybe Prop ~p -> ~p\n", + %% [M2, M2#mod.is_included]), + case M2#mod.is_included of + true -> + %% Propagate include mark + mod_mark_is_included(C,Sys,ModName,M2#mod.uses_mods,Acc); + false -> + Acc; + undefined -> + Acc + end; + [_] -> + %% This module is currently used from a different application + %% Ignore + Acc + end, mod_propagate_is_included(C, Sys, A, Mods, Acc2); mod_propagate_is_included(_C, _Sys, _A, [], Acc) -> Acc. @@ -740,9 +792,10 @@ mod_propagate_is_used_by(C, [#mod{name = ModName} | Mods]) -> mod_propagate_is_used_by(_C, []) -> ok. -read_apps(C, Sys, [#app{mods = Mods, is_included = IsIncl} = A | Apps], Acc) -> - {Mods2, IsIncl2} = read_apps(C, Sys, A, Mods, [], IsIncl), - Status = +app_recap_dependencies(C, Sys, [#app{mods = Mods, is_included = IsIncl} = A | Apps], Acc, Status) -> + {Mods2, IsIncl2, Status2} = + mod_recap_dependencies(C, Sys, A, Mods, [], IsIncl, Status), + AppStatus = case lists:keymember(missing, #mod.status, Mods2) of true -> missing; false -> ok @@ -759,34 +812,52 @@ read_apps(C, Sys, [#app{mods = Mods, is_included = IsIncl} = A | Apps], Acc) -> UsedByApps2 = lists:usort(UsedByApps), A2 = A#app{mods = Mods2, - status = Status, + status = AppStatus, uses_mods = UsesMods2, used_by_mods = UsedByMods2, uses_apps = UsesApps2, used_by_apps = UsedByApps2, is_included = IsIncl2}, - read_apps(C, Sys, Apps, [A2 | Acc]); -read_apps(_C, _Sys, [], Acc) -> - lists:reverse(Acc). - -read_apps(C, Sys, A, [#mod{name = ModName} | Mods], Acc, IsIncl) -> - [M2] = ets:lookup(C#common.mod_tab, ModName), - Status = do_get_status(M2), - %% print(M2#mod.name, hipe, "status -> ~p\n", [Status]), - {IsIncl2, M3} = - case M2#mod.is_included of - true -> - UsedByMods = - [N || {_, N} <- ets:lookup(C#common.mod_used_by_tab, - ModName)], - {true, M2#mod{status = Status, used_by_mods = UsedByMods}}; - _ -> - {IsIncl, M2#mod{status = Status, used_by_mods = []}} - end, - ets:insert(C#common.mod_tab, M3), - read_apps(C, Sys, A, Mods, [M3 | Acc], IsIncl2); -read_apps(_C, _Sys, _A, [], Acc, IsIncl) -> - {lists:reverse(Acc), IsIncl}. + ets:insert(C#common.app_tab,A2), + app_recap_dependencies(C, Sys, Apps, [A2 | Acc], Status2); +app_recap_dependencies(_C, _Sys, [], Acc, Status) -> + {lists:reverse(Acc), Status}. + +mod_recap_dependencies(C, Sys, A, [#mod{name = ModName}=M1 | Mods], Acc, IsIncl, Status) -> + case ets:lookup(C#common.mod_tab, ModName) of + [M2] when M2#mod.app_name=:=A#app.name -> + ModStatus = do_get_status(M2), + %% print(M2#mod.name, hipe, "status -> ~p\n", [ModStatus]), + {IsIncl2, M3} = + case M2#mod.is_included of + true -> + UsedByMods = + [N || {_, N} <- ets:lookup(C#common.mod_used_by_tab, + ModName)], + {true, M2#mod{status = ModStatus, used_by_mods = UsedByMods}}; + _ -> + {IsIncl, M2#mod{status = ModStatus, used_by_mods = []}} + end, + ets:insert(C#common.mod_tab, M3), + mod_recap_dependencies(C, Sys, A, Mods, [M3 | Acc], IsIncl2, Status); + [_] when A#app.is_included==false; M1#mod.incl_cond==exclude -> + %% App is explicitely excluded so it is ok that the module + %% record does not exist for this module in this + %% application. + mod_recap_dependencies(C, Sys, A, Mods, [M1 | Acc], IsIncl, Status); + [M2] -> + %% A module is potensially included by multiple + %% applications. This is not allowed! + Error = + lists:concat( + ["Module ",ModName, + " potentially included by two different applications: ", + A#app.name, " and ", M2#mod.app_name, "."]), + Status2 = reltool_utils:return_first_error(Status,Error), + mod_recap_dependencies(C, Sys, A, Mods, [M1 | Acc], IsIncl, Status2) + end; +mod_recap_dependencies(_C, _Sys, _A, [], Acc, IsIncl, Status) -> + {lists:reverse(Acc), IsIncl, Status}. do_get_status(M) -> if diff --git a/lib/reltool/src/reltool_sys_win.erl b/lib/reltool/src/reltool_sys_win.erl index dbb8e32aa2..76c064f1e7 100644 --- a/lib/reltool/src/reltool_sys_win.erl +++ b/lib/reltool/src/reltool_sys_win.erl @@ -1,7 +1,7 @@ %% %% %CopyrightBegin% %% -%% Copyright Ericsson AB 2009-2010. All Rights Reserved. +%% Copyright Ericsson AB 2009-2011. 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 @@ -1092,17 +1092,23 @@ move_app(S, {_ItemNo, AppBase}, Action) -> OldApp#app{incl_cond = AppCond}. do_set_app(#state{server_pid = ServerPid, app_wins = AppWins} = S, NewApp) -> - {ok, AnalysedApp, Warnings} = reltool_server:set_app(ServerPid, NewApp), + Result = reltool_server:set_app(ServerPid, NewApp), [ok = reltool_app_win:refresh(AW#app_win.pid) || AW <- AppWins], S2 = redraw_apps(S), - case Warnings of - [] -> - ignore; - _ -> - Msg = lists:flatten([[W, $\n] || W <- Warnings]), - display_message(Msg, ?wxICON_WARNING) - end, - {ok, AnalysedApp, S2}. + ReturnApp = + case Result of + {ok, AnalysedApp, []} -> + AnalysedApp; + {ok, AnalysedApp, Warnings} -> + Msg = lists:flatten([[W, $\n] || W <- Warnings]), + display_message(Msg, ?wxICON_WARNING), + AnalysedApp; + {error, Reason} -> + display_message(Reason, ?wxICON_ERROR), + {ok,OldApp} = reltool_server:get_app(ServerPid, NewApp#app.name), + OldApp + end, + {ok, ReturnApp, S2}. redraw_apps(#state{server_pid = ServerPid, source = SourceCtrl, diff --git a/lib/reltool/test/Makefile b/lib/reltool/test/Makefile index 62fe05238b..767454b66a 100644 --- a/lib/reltool/test/Makefile +++ b/lib/reltool/test/Makefile @@ -76,8 +76,8 @@ release_tests_spec: opt $(INSTALL_DATA) reltool.spec reltool.cover $(ERL_FILES) $(HRL_FILES) $(RELSYSDIR) $(INSTALL_SCRIPT) rtt $(INSTALL_PROGS) $(RELSYSDIR) $(INSTALL_DATA) $(INSTALL_PROGS) $(RELSYSDIR) -# chmod -R u+w $(RELSYSDIR) -# @tar cf - *_SUITE_data | (cd $(RELSYSDIR); tar xf -) + chmod -R u+w $(RELSYSDIR) + @tar cf - *_SUITE_data | (cd $(RELSYSDIR); tar xf -) release_docs_spec: diff --git a/lib/reltool/test/reltool_server_SUITE.erl b/lib/reltool/test/reltool_server_SUITE.erl index b77560db94..9ed79e8c95 100644 --- a/lib/reltool/test/reltool_server_SUITE.erl +++ b/lib/reltool/test/reltool_server_SUITE.erl @@ -25,6 +25,7 @@ -compile(export_all). -include("reltool_test_lib.hrl"). +-include_lib("common_test/include/ct.hrl"). -define(NODE_NAME, '__RELTOOL__TEMPORARY_TEST__NODE__'). -define(WORK_DIR, "reltool_work_dir"). @@ -53,7 +54,7 @@ all() -> [start_server, set_config, create_release, create_script, create_target, create_embedded, create_standalone, create_old_target, - otp_9135]. + otp_9135, otp_9229_exclude_app, otp_9229_exclude_mod]. groups() -> []. @@ -361,6 +362,114 @@ create_old_target(_Config) -> ok. %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% +%% OTP-9229 - handle duplicated module names, i.e. same module name +%% exists in two applications. + +%% Include on app, exclude the other +otp_9229_exclude_app(Config) -> + DataDir = ?config(data_dir,Config), + LibDir = filename:join(DataDir,"otp_9229"), + + %% Configure the server + ExclApp = + {sys, + [ + {root_dir, code:root_dir()}, + {lib_dirs, [LibDir]}, + {incl_cond,exclude}, + {app,x,[{incl_cond,include}]}, + {app,y,[{incl_cond,exclude}]}, + {app,kernel,[{incl_cond,include}]}, + {app,stdlib,[{incl_cond,include}]}, + {app,sasl,[{incl_cond,include}]} + ]}, + + %% Generate target file + TargetDir = filename:join([?WORK_DIR, "target_dupl_mod_excl_app"]), + ?m(ok, reltool_utils:recursive_delete(TargetDir)), + ?m(ok, file:make_dir(TargetDir)), + ?log("SPEC: ~p\n", [reltool:get_target_spec([{config, ExclApp}])]), + {ok,["Module mylib exists in applications x and y. Using module from application x."]} = reltool:get_status([{config, ExclApp}]), + ?m(ok, reltool:create_target([{config, ExclApp}], TargetDir)), + + Erl = filename:join([TargetDir, "bin", "erl"]), + {ok, Node} = ?msym({ok, _}, start_node(?NODE_NAME, Erl)), + + AbsTargetDir = filename:absname(TargetDir), + XArchive = "x-1.0.ez", + AbsXArchive = filename:join([AbsTargetDir,lib,XArchive]), + XEbin = ["ebin","x-1.0",XArchive], + YArchive = "y-1.0.ez", + AbsYArchive = filename:join([AbsTargetDir,lib,YArchive]), + + ?m(true, filelib:is_file(AbsXArchive)), + ?m(XEbin, mod_path(Node,x)), + ?m(XEbin, mod_path(Node,mylib)), + ?m(false, filelib:is_file(AbsYArchive)), + ?m(non_existing, mod_path(Node,y)), + + ?msym(ok, stop_node(Node)), + + ok. + +%% Include both apps, but exclude common module from one app +otp_9229_exclude_mod(Config) -> + DataDir = ?config(data_dir,Config), + LibDir = filename:join(DataDir,"otp_9229"), + + %% Configure the server + ExclMod = + {sys, + [ + {root_dir, code:root_dir()}, + {lib_dirs, [LibDir]}, + {incl_cond,exclude}, + {app,x,[{incl_cond,include}]}, + {app,y,[{incl_cond,include},{mod, mylib,[{incl_cond,exclude}]}]}, + {app,kernel,[{incl_cond,include}]}, + {app,stdlib,[{incl_cond,include}]}, + {app,sasl,[{incl_cond,include}]} + ]}, + + %% Generate target file + TargetDir = filename:join([?WORK_DIR, "target_dupl_mod_excl_mod"]), + ?m(ok, reltool_utils:recursive_delete(TargetDir)), + ?m(ok, file:make_dir(TargetDir)), + ?log("SPEC: ~p\n", [reltool:get_target_spec([{config, ExclMod}])]), + {ok,["Module mylib exists in applications x and y. Using module from application x."]} = reltool:get_status([{config, ExclMod}]), + ?m(ok, reltool:create_target([{config, ExclMod}], TargetDir)), + + Erl = filename:join([TargetDir, "bin", "erl"]), + {ok, Node} = ?msym({ok, _}, start_node(?NODE_NAME, Erl)), + + AbsTargetDir = filename:absname(TargetDir), + XArchive = "x-1.0.ez", + AbsXArchive = filename:join([AbsTargetDir,lib,XArchive]), + XEbin = ["ebin","x-1.0",XArchive], + YArchive = "y-1.0.ez", + AbsYArchive = filename:join([AbsTargetDir,lib,YArchive]), + YEbin = ["ebin","y-1.0",YArchive], + + ?m(true, filelib:is_file(AbsXArchive)), + ?m(XEbin, mod_path(Node,x)), + ?m(XEbin, mod_path(Node,mylib)), + ?m(true, filelib:is_file(AbsYArchive)), + ?m(YEbin, mod_path(Node,y)), + + %% Remove path to XEbin and check that mylib is not located in YEbin + Mylib = rpc:call(Node,code,which,[mylib]), + rpc:call(Node,code,del_path,[filename:dirname(Mylib)]), + ?m(non_existing, mod_path(Node,mylib)), + + ?msym(ok, stop_node(Node)), + + ok. + + + + + +%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% %% Library functions erl_libs() -> @@ -407,6 +516,20 @@ os_cmd(Cmd) when is_list(Cmd) -> end end. +%% Returns the location (directory) of the given module. Split, +%% reverted and relative to the lib dir. +mod_path(Node,Mod) -> + case rpc:call(Node,code,which,[Mod]) of + Path when is_list(Path) -> + lists:takewhile( + fun("lib") -> false; + (_) -> true + end, + lists:reverse(filename:split(filename:dirname(Path)))); + Other -> + Other + end. + %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% %% Node handling diff --git a/lib/reltool/test/reltool_server_SUITE_data/Makefile.src b/lib/reltool/test/reltool_server_SUITE_data/Makefile.src new file mode 100644 index 0000000000..049e8dd6cc --- /dev/null +++ b/lib/reltool/test/reltool_server_SUITE_data/Makefile.src @@ -0,0 +1,19 @@ +EFLAGS=+debug_info + +OTP9229= \ + otp_9229/x-1.0/ebin/x.@EMULATOR@ \ + otp_9229/x-1.0/ebin/mylib.@EMULATOR@ \ + otp_9229/y-1.0/ebin/y.@EMULATOR@ \ + otp_9229/y-1.0/ebin/mylib.@EMULATOR@ + + +all: $(OTP9229) + +otp_9229/x-1.0/ebin/x.@EMULATOR@: otp_9229/x-1.0/src/x.erl + erlc $(EFLAGS) -ootp_9229/x-1.0/ebin otp_9229/x-1.0/src/x.erl +otp_9229/x-1.0/ebin/mylib.@EMULATOR@: otp_9229/x-1.0/src/mylib.erl + erlc $(EFLAGS) -ootp_9229/x-1.0/ebin otp_9229/x-1.0/src/mylib.erl +otp_9229/y-1.0/ebin/y.@EMULATOR@: otp_9229/y-1.0/src/y.erl + erlc $(EFLAGS) -ootp_9229/y-1.0/ebin otp_9229/y-1.0/src/y.erl +otp_9229/y-1.0/ebin/mylib.@EMULATOR@: otp_9229/y-1.0/src/mylib.erl + erlc $(EFLAGS) -ootp_9229/y-1.0/ebin otp_9229/y-1.0/src/mylib.erl diff --git a/lib/reltool/test/reltool_server_SUITE_data/otp_9229/x-1.0/ebin/x.app b/lib/reltool/test/reltool_server_SUITE_data/otp_9229/x-1.0/ebin/x.app new file mode 100644 index 0000000000..e597704b19 --- /dev/null +++ b/lib/reltool/test/reltool_server_SUITE_data/otp_9229/x-1.0/ebin/x.app @@ -0,0 +1,7 @@ +%% -*- erlang -*- +{application, x, + [{description, "X CXC 138 11"}, + {vsn, "1.0"}, + {modules, [x, mylib]}, + {registered, []}, + {applications, [kernel, stdlib]}]}. diff --git a/lib/reltool/test/reltool_server_SUITE_data/otp_9229/x-1.0/src/mylib.erl b/lib/reltool/test/reltool_server_SUITE_data/otp_9229/x-1.0/src/mylib.erl new file mode 100644 index 0000000000..c8603d1a8e --- /dev/null +++ b/lib/reltool/test/reltool_server_SUITE_data/otp_9229/x-1.0/src/mylib.erl @@ -0,0 +1,4 @@ +-module(mylib). +-export([foo/0]). + +foo() -> erlang:time(). diff --git a/lib/reltool/test/reltool_server_SUITE_data/otp_9229/x-1.0/src/x.erl b/lib/reltool/test/reltool_server_SUITE_data/otp_9229/x-1.0/src/x.erl new file mode 100644 index 0000000000..17ff84f08f --- /dev/null +++ b/lib/reltool/test/reltool_server_SUITE_data/otp_9229/x-1.0/src/x.erl @@ -0,0 +1,4 @@ +-module(x). +-export([x/0]). + +x() ->mylib:foo(). diff --git a/lib/reltool/test/reltool_server_SUITE_data/otp_9229/y-1.0/ebin/y.app b/lib/reltool/test/reltool_server_SUITE_data/otp_9229/y-1.0/ebin/y.app new file mode 100644 index 0000000000..5b327862e3 --- /dev/null +++ b/lib/reltool/test/reltool_server_SUITE_data/otp_9229/y-1.0/ebin/y.app @@ -0,0 +1,7 @@ +%% -*- erlang -*- +{application, y, + [{description, "Y CXC 138 11"}, + {vsn, "1.0"}, + {modules, [y, mylib]}, + {registered, []}, + {applications, [kernel, stdlib]}]}. diff --git a/lib/reltool/test/reltool_server_SUITE_data/otp_9229/y-1.0/src/mylib.erl b/lib/reltool/test/reltool_server_SUITE_data/otp_9229/y-1.0/src/mylib.erl new file mode 100644 index 0000000000..c8603d1a8e --- /dev/null +++ b/lib/reltool/test/reltool_server_SUITE_data/otp_9229/y-1.0/src/mylib.erl @@ -0,0 +1,4 @@ +-module(mylib). +-export([foo/0]). + +foo() -> erlang:time(). diff --git a/lib/reltool/test/reltool_server_SUITE_data/otp_9229/y-1.0/src/y.erl b/lib/reltool/test/reltool_server_SUITE_data/otp_9229/y-1.0/src/y.erl new file mode 100644 index 0000000000..342e7da7d5 --- /dev/null +++ b/lib/reltool/test/reltool_server_SUITE_data/otp_9229/y-1.0/src/y.erl @@ -0,0 +1,4 @@ +-module(y). +-export([y/0]). + +y() ->mylib:foo(). diff --git a/lib/sasl/Makefile b/lib/sasl/Makefile index 2affcf1e40..4073e5af85 100644 --- a/lib/sasl/Makefile +++ b/lib/sasl/Makefile @@ -1,7 +1,7 @@ # # %CopyrightBegin% # -# Copyright Ericsson AB 1996-2009. All Rights Reserved. +# Copyright Ericsson AB 1996-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 @@ -23,7 +23,7 @@ include $(ERL_TOP)/make/$(TARGET)/otp.mk # Macros # -SUB_DIRECTORIES = src doc/src +SUB_DIRECTORIES = src doc/src examples/src include vsn.mk VSN = $(SASL_VSN) diff --git a/lib/sasl/examples/ebin/.gitignore b/lib/sasl/examples/ebin/.gitignore new file mode 100644 index 0000000000..e69de29bb2 --- /dev/null +++ b/lib/sasl/examples/ebin/.gitignore diff --git a/lib/sasl/examples/src/Makefile b/lib/sasl/examples/src/Makefile new file mode 100644 index 0000000000..4a4e04a536 --- /dev/null +++ b/lib/sasl/examples/src/Makefile @@ -0,0 +1,78 @@ +# +# %CopyrightBegin% +# +# Copyright Ericsson AB 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% +# + +# + +include $(ERL_TOP)/make/target.mk +include $(ERL_TOP)/make/$(TARGET)/otp.mk + +# ---------------------------------------------------- +# Application version +# ---------------------------------------------------- +include ../../vsn.mk +VSN=$(SASL_VSN) + +# ---------------------------------------------------- +# Release directory specification +# ---------------------------------------------------- +RELSYSDIR = $(RELEASE_PATH)/lib/sasl-$(VSN) + +# ---------------------------------------------------- +# Common Macros +# ---------------------------------------------------- +EXTRA_ERLC_FLAGS = +warn_unused_vars +ERL_COMPILE_FLAGS += $(EXTRA_ERLC_FLAGS) + + +MODULES = target_system + +ERL_FILES= $(MODULES:%=%.erl) + +TARGET_FILES= $(MODULES:%=$(EBIN)/%.$(EMULATOR)) + +# ---------------------------------------------------- +# Targets +# ---------------------------------------------------- + +debug opt: $(TARGET_FILES) + +clean: + rm -fr $(TARGET_FILES) *~ *.beam + +docs: + +# ---------------------------------------------------- +# Release Target +# ---------------------------------------------------- +include $(ERL_TOP)/make/otp_release_targets.mk + +release_spec: opt + $(INSTALL_DIR) $(RELSYSDIR)/examples/src + $(INSTALL_DIR) $(RELSYSDIR)/examples/ebin + (cd ..; tar cf - src ebin | (cd $(RELSYSDIR)/examples; tar xf -)) + chmod -f -R ug+w $(RELSYSDIR)/examples + +release_docs_spec: + + + + + + + diff --git a/lib/sasl/examples/src/target_system.erl b/lib/sasl/examples/src/target_system.erl new file mode 100644 index 0000000000..0e1e0b2324 --- /dev/null +++ b/lib/sasl/examples/src/target_system.erl @@ -0,0 +1,259 @@ +%% +%% %CopyrightBegin% +%% +%% Copyright Ericsson AB 2011. 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(target_system). +-export([create/1, create/2, install/2]). + +%% Note: RelFileName below is the *stem* without trailing .rel, +%% .script etc. +%% + +%% create(RelFileName) +%% +create(RelFileName) -> + create(RelFileName,[]). + +create(RelFileName,SystoolsOpts) -> + RelFile = RelFileName ++ ".rel", + Dir = filename:dirname(RelFileName), + PlainRelFileName = filename:join(Dir,"plain"), + PlainRelFile = PlainRelFileName ++ ".rel", + io:fwrite("Reading file: ~p ...~n", [RelFile]), + {ok, [RelSpec]} = file:consult(RelFile), + io:fwrite("Creating file: ~p from ~p ...~n", + [PlainRelFile, RelFile]), + {release, + {RelName, RelVsn}, + {erts, ErtsVsn}, + AppVsns} = RelSpec, + PlainRelSpec = {release, + {RelName, RelVsn}, + {erts, ErtsVsn}, + lists:filter(fun({kernel, _}) -> + true; + ({stdlib, _}) -> + true; + (_) -> + false + end, AppVsns) + }, + {ok, Fd} = file:open(PlainRelFile, [write]), + io:fwrite(Fd, "~p.~n", [PlainRelSpec]), + file:close(Fd), + + io:fwrite("Making \"~s.script\" and \"~s.boot\" files ...~n", + [PlainRelFileName,PlainRelFileName]), + make_script(PlainRelFileName,SystoolsOpts), + + io:fwrite("Making \"~s.script\" and \"~s.boot\" files ...~n", + [RelFileName, RelFileName]), + make_script(RelFileName,SystoolsOpts), + + TarFileName = filename:join(Dir,RelFileName ++ ".tar.gz"), + io:fwrite("Creating tar file ~p ...~n", [TarFileName]), + make_tar(RelFileName,SystoolsOpts), + + TmpDir = filename:join(Dir,"tmp"), + io:fwrite("Creating directory ~p ...~n",[TmpDir]), + file:make_dir(TmpDir), + + io:fwrite("Extracting ~p into directory ~p ...~n", [TarFileName,TmpDir]), + extract_tar(TarFileName, TmpDir), + + TmpBinDir = filename:join([TmpDir, "bin"]), + ErtsBinDir = filename:join([TmpDir, "erts-" ++ ErtsVsn, "bin"]), + io:fwrite("Deleting \"erl\" and \"start\" in directory ~p ...~n", + [ErtsBinDir]), + file:delete(filename:join([ErtsBinDir, "erl"])), + file:delete(filename:join([ErtsBinDir, "start"])), + + io:fwrite("Creating temporary directory ~p ...~n", [TmpBinDir]), + file:make_dir(TmpBinDir), + + io:fwrite("Copying file \"~s.boot\" to ~p ...~n", + [PlainRelFileName, filename:join([TmpBinDir, "start.boot"])]), + copy_file(PlainRelFileName++".boot",filename:join([TmpBinDir, "start.boot"])), + + io:fwrite("Copying files \"epmd\", \"run_erl\" and \"to_erl\" from \n" + "~p to ~p ...~n", + [ErtsBinDir, TmpBinDir]), + copy_file(filename:join([ErtsBinDir, "epmd"]), + filename:join([TmpBinDir, "epmd"]), [preserve]), + copy_file(filename:join([ErtsBinDir, "run_erl"]), + filename:join([TmpBinDir, "run_erl"]), [preserve]), + copy_file(filename:join([ErtsBinDir, "to_erl"]), + filename:join([TmpBinDir, "to_erl"]), [preserve]), + + StartErlDataFile = filename:join([TmpDir, "releases", "start_erl.data"]), + io:fwrite("Creating ~p ...~n", [StartErlDataFile]), + StartErlData = io_lib:fwrite("~s ~s~n", [ErtsVsn, RelVsn]), + write_file(StartErlDataFile, StartErlData), + + io:fwrite("Recreating tar file ~p from contents in directory ~p ...~n", + [TarFileName,TmpDir]), + {ok, Tar} = erl_tar:open(TarFileName, [write, compressed]), + %% {ok, Cwd} = file:get_cwd(), + %% file:set_cwd("tmp"), + ErtsDir = "erts-"++ErtsVsn, + erl_tar:add(Tar, filename:join(TmpDir,"bin"), "bin", []), + erl_tar:add(Tar, filename:join(TmpDir,ErtsDir), ErtsDir, []), + erl_tar:add(Tar, filename:join(TmpDir,"releases"), "releases", []), + erl_tar:add(Tar, filename:join(TmpDir,"lib"), "lib", []), + erl_tar:close(Tar), + %% file:set_cwd(Cwd), + io:fwrite("Removing directory ~p ...~n",[TmpDir]), + remove_dir_tree(TmpDir), + ok. + + +install(RelFileName, RootDir) -> + TarFile = RelFileName ++ ".tar.gz", + io:fwrite("Extracting ~p ...~n", [TarFile]), + extract_tar(TarFile, RootDir), + StartErlDataFile = filename:join([RootDir, "releases", "start_erl.data"]), + {ok, StartErlData} = read_txt_file(StartErlDataFile), + [ErlVsn, _RelVsn| _] = string:tokens(StartErlData, " \n"), + ErtsBinDir = filename:join([RootDir, "erts-" ++ ErlVsn, "bin"]), + BinDir = filename:join([RootDir, "bin"]), + io:fwrite("Substituting in erl.src, start.src and start_erl.src to\n" + "form erl, start and start_erl ...\n"), + subst_src_scripts(["erl", "start", "start_erl"], ErtsBinDir, BinDir, + [{"FINAL_ROOTDIR", RootDir}, {"EMU", "beam"}], + [preserve]), + io:fwrite("Creating the RELEASES file ...\n"), + create_RELEASES(RootDir, + filename:join([RootDir, "releases", RelFileName])). + +%% LOCALS + +%% make_script(RelFileName,Opts) +%% +make_script(RelFileName,Opts) -> + systools:make_script(RelFileName, [no_module_tests, + {outdir,filename:dirname(RelFileName)} + |Opts]). + +%% make_tar(RelFileName,Opts) +%% +make_tar(RelFileName,Opts) -> + RootDir = code:root_dir(), + systools:make_tar(RelFileName, [{erts, RootDir}, + {outdir,filename:dirname(RelFileName)} + |Opts]). + +%% extract_tar(TarFile, DestDir) +%% +extract_tar(TarFile, DestDir) -> + erl_tar:extract(TarFile, [{cwd, DestDir}, compressed]). + +create_RELEASES(DestDir, RelFileName) -> + release_handler:create_RELEASES(DestDir, RelFileName ++ ".rel"). + +subst_src_scripts(Scripts, SrcDir, DestDir, Vars, Opts) -> + lists:foreach(fun(Script) -> + subst_src_script(Script, SrcDir, DestDir, + Vars, Opts) + end, Scripts). + +subst_src_script(Script, SrcDir, DestDir, Vars, Opts) -> + subst_file(filename:join([SrcDir, Script ++ ".src"]), + filename:join([DestDir, Script]), + Vars, Opts). + +subst_file(Src, Dest, Vars, Opts) -> + {ok, Conts} = read_txt_file(Src), + NConts = subst(Conts, Vars), + write_file(Dest, NConts), + case lists:member(preserve, Opts) of + true -> + {ok, FileInfo} = file:read_file_info(Src), + file:write_file_info(Dest, FileInfo); + false -> + ok + end. + +%% subst(Str, Vars) +%% Vars = [{Var, Val}] +%% Var = Val = string() +%% Substitute all occurrences of %Var% for Val in Str, using the list +%% of variables in Vars. +%% +subst(Str, Vars) -> + subst(Str, Vars, []). + +subst([$%, C| Rest], Vars, Result) when $A =< C, C =< $Z -> + subst_var([C| Rest], Vars, Result, []); +subst([$%, C| Rest], Vars, Result) when $a =< C, C =< $z -> + subst_var([C| Rest], Vars, Result, []); +subst([$%, C| Rest], Vars, Result) when C == $_ -> + subst_var([C| Rest], Vars, Result, []); +subst([C| Rest], Vars, Result) -> + subst(Rest, Vars, [C| Result]); +subst([], _Vars, Result) -> + lists:reverse(Result). + +subst_var([$%| Rest], Vars, Result, VarAcc) -> + Key = lists:reverse(VarAcc), + case lists:keysearch(Key, 1, Vars) of + {value, {Key, Value}} -> + subst(Rest, Vars, lists:reverse(Value, Result)); + false -> + subst(Rest, Vars, [$%| VarAcc ++ [$%| Result]]) + end; +subst_var([C| Rest], Vars, Result, VarAcc) -> + subst_var(Rest, Vars, Result, [C| VarAcc]); +subst_var([], Vars, Result, VarAcc) -> + subst([], Vars, [VarAcc ++ [$%| Result]]). + +copy_file(Src, Dest) -> + copy_file(Src, Dest, []). + +copy_file(Src, Dest, Opts) -> + {ok,_} = file:copy(Src, Dest), + case lists:member(preserve, Opts) of + true -> + {ok, FileInfo} = file:read_file_info(Src), + file:write_file_info(Dest, FileInfo); + false -> + ok + end. + +write_file(FName, Conts) -> + {ok, Fd} = file:open(FName, [write]), + file:write(Fd, Conts), + file:close(Fd). + +read_txt_file(File) -> + {ok, Bin} = file:read_file(File), + {ok, binary_to_list(Bin)}. + +remove_dir_tree(Dir) -> + remove_all_files(".", [Dir]). + +remove_all_files(Dir, Files) -> + lists:foreach(fun(File) -> + FilePath = filename:join([Dir, File]), + case filelib:is_dir(FilePath) of + true -> + {ok, DirFiles} = file:list_dir(FilePath), + remove_all_files(FilePath, DirFiles), + file:del_dir(FilePath); + _ -> + file:delete(FilePath) + end + end, Files). diff --git a/lib/sasl/src/rb.erl b/lib/sasl/src/rb.erl index 13753565d8..8004ef2c5a 100644 --- a/lib/sasl/src/rb.erl +++ b/lib/sasl/src/rb.erl @@ -53,8 +53,7 @@ start_link(Options) -> gen_server:start_link({local, rb_server}, rb, Options, []). stop() -> - call(stop), - supervisor:delete_child(sasl_sup, rb_server). + supervisor:terminate_child(sasl_sup, rb_server). rescan() -> rescan([]). rescan(Options) -> @@ -205,8 +204,6 @@ handle_call({rescan, Options}, _From, State) -> NewState = State#state{data = Data, max = Max, type = Type, device = Device, abort = Abort, log = Log1}, {reply, ok, NewState}; -handle_call(stop, _From, State) -> - {stop, normal, stopped, State}; handle_call(_, _From, #state{data = undefined}) -> {reply, {error, no_data}, #state{}}; handle_call({list, Type}, _From, State) -> @@ -312,11 +309,14 @@ scan_files(RptDir, Max, Type) -> {ok, Fd} -> case catch file:read(Fd, 1) of {ok, [LastWritten]} -> + file:close(Fd), Files = make_file_list(RptDir, LastWritten), scan_files(RptDir, Files, Max, Type); - _ -> exit("cannot read the index file") + _X -> + file:close(Fd), + exit("cannot read the index file") end; - _ -> exit("cannot read the index file") + _X -> exit("cannot read the index file") end. make_file_list(Dir, FirstFileNo) -> @@ -789,7 +789,7 @@ filter_report([{Key, RegExp, re}|T], Msg) -> filter_report([{Key, RegExp, re, no}|T], Msg) -> case proplists:get_value(Key, Msg) of undefined -> - false; + true; Value -> Subject = lists:flatten(io_lib:format("~p",[Value])), case run_re(Subject, RegExp) of diff --git a/lib/sasl/test/.gitignore b/lib/sasl/test/.gitignore new file mode 100644 index 0000000000..76e706b874 --- /dev/null +++ b/lib/sasl/test/.gitignore @@ -0,0 +1,5 @@ +# +# Don't ignore *.beam files in any sub-directory. +# + +!*.beam diff --git a/lib/sasl/test/Makefile b/lib/sasl/test/Makefile new file mode 100644 index 0000000000..ad08c8136b --- /dev/null +++ b/lib/sasl/test/Makefile @@ -0,0 +1,91 @@ +# +# %CopyrightBegin% +# +# Copyright Ericsson AB 2011. 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% +# +include $(ERL_TOP)/make/target.mk +include $(ERL_TOP)/make/$(TARGET)/otp.mk + +# ---------------------------------------------------- +# Target Specs +# ---------------------------------------------------- + +MODULES= \ + sasl_SUITE \ + alarm_handler_SUITE \ + installer \ + release_handler_SUITE \ + systools_SUITE \ + systools_rc_SUITE \ + overload_SUITE \ + rb_SUITE + +ERL_FILES= $(MODULES:%=%.erl) + +TARGET_FILES= $(MODULES:%=$(EBIN)/%.$(EMULATOR)) +INSTALL_PROGS= $(TARGET_FILES) + +EMAKEFILE=Emakefile + +# ---------------------------------------------------- +# Release directory specification +# ---------------------------------------------------- +RELSYSDIR = $(RELEASE_PATH)/sasl_test + +# ---------------------------------------------------- +# FLAGS +# ---------------------------------------------------- +ERL_MAKE_FLAGS += +ERL_COMPILE_FLAGS += -I$(ERL_TOP)/lib/test_server/include \ + -I$(ERL_TOP)/lib/sasl/src + +EBIN = . + +# ---------------------------------------------------- +# Targets +# ---------------------------------------------------- + +make_emakefile: + $(ERL_TOP)/make/make_emakefile $(ERL_COMPILE_FLAGS) -o$(EBIN) '*_SUITE_make'\ + > $(EMAKEFILE) + $(ERL_TOP)/make/make_emakefile $(ERL_COMPILE_FLAGS) -o$(EBIN) $(MODULES)\ + >> $(EMAKEFILE) + +tests debug opt: make_emakefile + erl $(ERL_MAKE_FLAGS) -make + +clean: + rm -f $(EMAKEFILE) + rm -f $(TARGET_FILES) + rm -f core + +docs: + +# ---------------------------------------------------- +# Release Target +# ---------------------------------------------------- +include $(ERL_TOP)/make/otp_release_targets.mk + +release_spec: opt + +release_tests_spec: make_emakefile + $(INSTALL_DIR) $(RELSYSDIR) + $(INSTALL_DATA) $(ERL_FILES) $(RELSYSDIR) + $(INSTALL_DATA) sasl.spec sasl.cover $(EMAKEFILE) $(RELSYSDIR) + chmod -f -R u+w $(RELSYSDIR) + @tar cfh - *_SUITE_data | (cd $(RELSYSDIR); tar xf -) + +release_docs_spec: diff --git a/lib/sasl/test/alarm_handler_SUITE.erl b/lib/sasl/test/alarm_handler_SUITE.erl new file mode 100644 index 0000000000..a98e8c9c67 --- /dev/null +++ b/lib/sasl/test/alarm_handler_SUITE.erl @@ -0,0 +1,179 @@ +%% +%% %CopyrightBegin% +%% +%% Copyright Ericsson AB 2011. 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(alarm_handler_SUITE). + +-include_lib("test_server/include/test_server.hrl"). + +%%----------------------------------------------------------------- +%% We will add an own alarm handler in order to verify that the +%% alarm_handler deliver the expected events. +%%----------------------------------------------------------------- + +-export([init_per_suite/1, end_per_suite/1, all/0,groups/0, + init_per_group/2,end_per_group/2, + set_alarm/1, clear_alarm/1, swap/1]). + +-export([init/1, handle_event/2, handle_call/2, handle_info/2, + terminate/2]). + + +init_per_suite(Config) -> + application:start(sasl), + Config. + +end_per_suite(_Config) -> + ok. + +all() -> + [set_alarm, clear_alarm, swap]. + +groups() -> + []. + +init_per_group(_GroupName, Config) -> + Config. + +end_per_group(_GroupName, Config) -> + Config. + + + +%%----------------------------------------------------------------- + +set_alarm(suite) -> []; +set_alarm(Config) when is_list(Config) -> + ?line gen_event:add_handler(alarm_handler, ?MODULE, self()), + Alarm1 = {alarm1, "this is the alarm"}, + Alarm2 = {"alarm2", this_is_the_alarm}, + Alarm3 = {{alarm3}, {this_is,"the_alarm"}}, + ?line ok = alarm_handler:set_alarm(Alarm1), + reported(set_alarm, Alarm1), + ?line ok = alarm_handler:set_alarm(Alarm2), + reported(set_alarm, Alarm2), + ?line ok = alarm_handler:set_alarm(Alarm3), + reported(set_alarm, Alarm3), + + ?line [Alarm3,Alarm2,Alarm1] = alarm_handler:get_alarms(), + alarm_handler:clear_alarm(alarm1), + alarm_handler:clear_alarm("alarm2"), + alarm_handler:clear_alarm({alarm3}), + ?line [] = alarm_handler:get_alarms(), + + test_server:messages_get(), + ?line my_yes = gen_event:delete_handler(alarm_handler, ?MODULE, []), + ok. + +%%----------------------------------------------------------------- + +clear_alarm(suite) -> []; +clear_alarm(Config) when is_list(Config) -> + ?line gen_event:add_handler(alarm_handler, ?MODULE, self()), + Alarm1 = {alarm1, "this is the alarm"}, + Alarm2 = {"alarm2", this_is_the_alarm}, + Alarm3 = {{alarm3}, {this_is,"the_alarm"}}, + alarm_handler:set_alarm(Alarm1), + alarm_handler:set_alarm(Alarm2), + alarm_handler:set_alarm(Alarm3), + test_server:messages_get(), + + ?line ok = alarm_handler:clear_alarm(alarm1), + reported(clear_alarm, alarm1), + ?line ok = alarm_handler:clear_alarm("alarm2"), + reported(clear_alarm, "alarm2"), + ?line ok = alarm_handler:clear_alarm({alarm3}), + reported(clear_alarm, {alarm3}), + ?line [] = alarm_handler:get_alarms(), + + ?line my_yes = gen_event:delete_handler(alarm_handler, ?MODULE, []), + ok. + +%%----------------------------------------------------------------- + +swap(suite) -> []; +swap(Config) when is_list(Config) -> + ?line Alarm1 = {alarm1, "this is the alarm"}, + ?line Alarm2 = {"alarm2", this_is_the_alarm}, + ?line Alarm3 = {{alarm3}, {this_is,"the_alarm"}}, + ?line alarm_handler:set_alarm(Alarm1), + ?line alarm_handler:set_alarm(Alarm2), + ?line alarm_handler:set_alarm(Alarm3), + + ?line foo, + case gen_event:which_handlers(alarm_handler) of + [alarm_handler] -> + ?line ok = gen_event:swap_handler(alarm_handler, + {alarm_handler, swap}, + {?MODULE, self()}), + ?line [?MODULE] = gen_event:which_handlers(alarm_handler), + Alarms = [Alarm3, Alarm2, Alarm1], + reported(swap_alarms, Alarms), + + %% get_alarms is only valid with the default handler installed. + ?line {error, _} = alarm_handler:get_alarms(), + + ?line my_yes = gen_event:delete_handler(alarm_handler, + ?MODULE, []), + ?line gen_event:add_handler(alarm_handler, alarm_handler, []), + ok; + _ -> + alarm_handler:clear_alarm(alarm1), + alarm_handler:clear_alarm("alarm2"), + alarm_handler:clear_alarm({alarm3}), + ok + end. + +%%----------------------------------------------------------------- +%% Check that the alarm has been received. +%%----------------------------------------------------------------- +reported(Tag, Data) -> + receive + {Tag, Data} -> + test_server:messages_get(), + ok + after 1000 -> + test_server:fail(no_alarm_received) + end. + +%%----------------------------------------------------------------- +%% The error_logger handler (gen_event behaviour). +%% Sends a notification to the Tester process about the events +%% generated by the Tester process. +%%----------------------------------------------------------------- +init(Tester) when is_pid(Tester) -> + {ok, Tester}; +init({Tester, {alarm_handler,Alarms}}) -> % Swap from default handler. + Tester ! {swap_alarms, Alarms}, + {ok, Tester}. + +handle_event({set_alarm, Alarm}, Tester) -> + Tester ! {set_alarm, Alarm}, + {ok, Tester}; +handle_event({clear_alarm, AlarmId}, Tester) -> + Tester ! {clear_alarm, AlarmId}, + {ok, Tester}; +handle_event(_Event, Tester) -> + {ok, Tester}. + +handle_info(_, Tester) -> + {ok, Tester}. + +handle_call(_Query, Tester) -> {ok, {error, bad_query}, Tester}. + +terminate(_Reason, _Tester) -> + my_yes. diff --git a/lib/sasl/test/installer.erl b/lib/sasl/test/installer.erl new file mode 100644 index 0000000000..a114c4b5c9 --- /dev/null +++ b/lib/sasl/test/installer.erl @@ -0,0 +1,778 @@ +%% +%% %CopyrightBegin% +%% +%% Copyright Ericsson AB 2011. 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(installer). + +%%-compile(export_all). +-export([install_1/2]). +-export([install_2/1]). +-export([install_3/2]). +-export([install_3a/1]). +-export([install_4/1]). +-export([install_5/1]). +-export([install_5a/1]). +-export([install_6/1]). +-export([install_7/1]). +-export([install_8/1]). +-export([install_9/1]). +-export([install_10/1]). +-export([install_11/1]). +-export([client1_1/4]). +-export([client2/3]). +-export([stop/1]). +-export([unpack_p1h/2]). +-export([permanent_p1h/1]). +-export([reg_proc/1]). +-export([registered_loop/1]). + +-define(print(List), {rh_print, TestNode} ! {print, {?MODULE, ?LINE}, List}). +-define(print_line(Line,List), {rh_print, TestNode} ! {print, {?MODULE, Line}, List}). +-define(fail(Term), exit({?MODULE, ?LINE, Term})). +-define(fail_line(Line,Term), exit({?MODULE, Line, Term})). + +-define(check_release(Vsn,Status,Apps), + check_release(TestNode,node(),Vsn,Status,Apps,?LINE)). +-define(check_release_client(Node,Vsn,Status,Apps), + check_release(TestNode,Node,Vsn,Status,Apps,?LINE)). + +-define(check_running_app(App,Vsn), + check_running_app(TestNode,node(),App,Vsn,?LINE)). +-define(check_running_app_client(Node,App,Vsn), + check_running_app(TestNode,Node,App,Vsn,?LINE)). + + +install_1(TestNode,PrivDir) -> + ?print([TestNode]), + ?print(["install_1 start"]), + + % Unpack and install P1H + {ok, "P1H"} = unpack_release(PrivDir,"rel1"), + ?print(["unpack_release P1H ok"]), + ?check_release("P1H",unpacked,["a-1.0"]), + {ok,"P1G",[new_appl]} = release_handler:install_release("P1H"), + ?print(["install_release P1H ok"]), + ?check_release("P1H",current,["a-1.0"]), + ?check_running_app(a,"1.0"), + X = a:a(), + ?print(["X", X]), + {key2, val2} = lists:keyfind(key2, 1, X), + {key1, val1} = lists:keyfind(key1, 1, X), + ?print(["install_1 end OK"]), + ok. + % release_handler_SUITE will reboot this node now! + +install_2(TestNode) -> + ?print(["install_2 start"]), + + % Check that P1H is still unpacked, install it and make_permanent + ?check_release("P1H",unpacked,["a-1.0"]), + ?print(["install_2 P1H unpacked"]), + {ok,"P1G",[new_appl]} = release_handler:install_release("P1H"), + ?print(["install_2 install_release ok"]), + ?check_release("P1H",current,["a-1.0"]), + ?check_running_app(a,"1.0"), + ok = release_handler:make_permanent("P1H"). + % release_handler_SUITE will reboot this node now! + +install_3(TestNode,PrivDir) -> + ?print(["install_3 start"]), + + % Check that P1H is permanent + ?check_release("P1H",permanent,["a-1.0"]), + X = a:a(), + {key2, val2} = lists:keyfind(key2, 1, X), + {key1, val1} = lists:keyfind(key1, 1, X), + + % Unpack and install P1I + {ok, "P1I"} = unpack_release(PrivDir,"rel2"), + ?print(["install_3 unpack_release P1I ok"]), + ?check_release("P1I",unpacked,["a-1.1"]), + {ok,"P1H",[{extra, gott}]} = release_handler:check_install_release("P1I"), + {error,_} = release_handler:check_install_release("P1J"), + {ok,"P1H",[{extra, gott}]} = release_handler:install_release("P1I"), + ?print(["install_3 install_release P1I ok"]), + ?check_release("P1I",current,["a-1.1"]), + ?check_running_app(a,"1.1"), + X2 = a:a(), + {key2, newval2} = lists:keyfind(key2, 1, X2), + {key1, val1} = lists:keyfind(key1, 1, X2), + {ok, bval} = a:b(), + + % Unpack and install P2A + {ok, "P2A"} = unpack_release(PrivDir,"rel3"), + ?print(["install_3 unpack_release P2A ok"]), + ?check_release("P2A",unpacked,["a-1.1"]), + {ok, "P1I", [new_emu]} = release_handler:check_install_release("P2A"), + ok = release_handler:make_permanent("P1I"), + ?print(["install_3 make_permanent P1I ok"]), + ?check_release("P1I",permanent,["a-1.1"]), + ok. + +install_3a(TestNode) -> + {ok, "P1I", [new_emu]} = release_handler:install_release("P2A"), + %% Node is rebooted by the release_handler:install_release + %% (init:reboot) because P2A includes a new erts vsn and the relup + %% file contains a 'restart_new_emulator' instruction. + ?print(["install_3 P2A installed"]), + ok. + + + +install_4(TestNode) -> + ?print(["install_4 start"]), + + % Check that P2A is in use. + ?check_release("P2A",current,["a-1.1"]), + ?check_running_app(a,"1.1"), + X = a:a(), + {key2, newval2} = lists:keyfind(key2, 1, X), + {key1, val1} = lists:keyfind(key1, 1, X), + {ok, bval} = a:b(), + ok. + % release_handler_SUITE will reboot this node now! + +install_5(TestNode) -> + ?print(["install_5 start"]), + + % Check that P1I is used + {ok, "P1I", [new_emu]} = release_handler:check_install_release("P2A"), + ok. + +install_5a(TestNode) -> + % Install P2A again + {ok, "P1I", [new_emu]} = release_handler:install_release("P2A"), + %% Node is rebooted by the release_handler:install_release + %% (init:reboot) because P2A includes a new erts vsn and the relup + %% file contains a 'restart_new_emulator' instruction. + ?print(["install_5 P2A installed"]), + ok. + +install_6(TestNode) -> + ?print(["install_6 start"]), + + % Check that P2A is used + ?check_release("P2A",current,["a-1.1"]), + ?check_running_app(a,"1.1"), + X = a:a(), + {key2, newval2} = lists:keyfind(key2, 1, X), + {key1, val1} = lists:keyfind(key1, 1, X), + {ok, bval} = a:b(), + ok = release_handler:make_permanent("P2A"). + % release_handler_SUITE will reboot this node now! + + +install_7(TestNode) -> + ?print(["install_7 start"]), + + % Check that P2A is used + ?check_release("P2A",permanent,["a-1.1"]), + + % Install old P1H + ok = release_handler:reboot_old_release("P1H"), + ok. + +install_8(TestNode) -> + ?print(["install_8 start"]), + + % Check that P1H is permanent + ?check_release("P1H",permanent,["a-1.0"]), + X = a:a(), + {key2, val2} = lists:keyfind(key2, 1, X), + {key1, val1} = lists:keyfind(key1, 1, X), + + %% Remove P1I and P2A and check that a-1.1 and erts-<latest> are removed + ok = release_handler:remove_release("P2A"), + ok = release_handler:remove_release("P1I"), + {ok, Libs} = file:list_dir(code:lib_dir()), + {_,_,StdlibVsn} = lists:keyfind(stdlib,1,application:which_applications()), + true = lists:member("stdlib-"++StdlibVsn, Libs), + true = lists:member("a-1.0", Libs), + false = lists:member("a-1.1", Libs), + {ok, Dirs} = file:list_dir(code:root_dir()), + ["erts-4.4"] = lists:filter(fun(Dir) -> lists:prefix("erts-",Dir) end, Dirs), + ok. + % release_handler_SUITE will reboot this node now! + +install_9(TestNode) -> + ?print(["install_9 start"]), + + % Check that P1H is permanent + ?check_release("P1H",permanent,["a-1.0"]), + X = a:a(), + {key2, val2} = lists:keyfind(key2, 1, X), + {key1, val1} = lists:keyfind(key1, 1, X), + + % Install old P1G + ok = release_handler:reboot_old_release("P1G"), + ok. + +install_10(TestNode) -> + ?print(["install_10 start"]), + + % Check that P1G is permanent + ?check_release("P1G",permanent,[]), + ?check_release("P1H",old,["a-1.0"]), + + %% Remove P1H and check that both versions of application a is removed + ok = release_handler:remove_release("P1H"), + {ok, Libs} = file:list_dir(code:lib_dir()), + {_,_,StdlibVsn} = lists:keyfind(stdlib,1,application:which_applications()), + true = lists:member("stdlib-"++StdlibVsn, Libs), + false = lists:member("a-1.0", Libs), + false = lists:member("a-1.1", Libs), + ok. + % release_handler_SUITE will reboot this node now! + +install_11(TestNode) -> + ?print(["install_11 start"]), + + % Check that P1G is permanent + ?check_release("P1G",permanent,[]), + ok. + + + +%%----------------------------------------------------------------- +%% This test starts a client node which uses this node as master +%% for the release_handler. +%% The client node runs all tests as in installer/1 test case. +%% Thus, the client node will be rebooted several times. +%% The to_erl /tmp/NODENAME@HOSTNAME/ command can be used to connect +%% to the client node. +%% run_erl logs for the client can be found in the directory: +%% code:root_dir() ++ "/clients/type1/NODENAME@HOSTNAME/log +%%----------------------------------------------------------------- + + +client1_1(TestNode,PrivDir,MasterDir,ClientSname) -> + TestHost = test_host(), + ?print(["client1_1 start"]), + + {ok,IP} = inet:getaddr(TestHost,inet), + erl_boot_server:start([IP]), + + ok = net_kernel:monitor_nodes(true), + Node = start_client(TestNode,ClientSname), + trace_disallowed_calls(Node), + + %% Check env var for SASL on client node + SaslEnv = rpc:call(Node, application, get_all_env, [sasl]), + {_,CliDir} = lists:keyfind(client_directory,1,SaslEnv), + {_,[Master]} = lists:keyfind(masters,1,SaslEnv), + {_,StartCli} = lists:keyfind(start_prg,1,SaslEnv), + Root = code:root_dir(), + true = (CliDir =:= filename:join([Root,"clients","type1",Node])), + true = (StartCli =:= filename:join([CliDir,"bin","start"])), + true = (Master =:= node()), + + %% Unpack P1H on master + {ok, "P1H"} = unpack_release(PrivDir,"rel1"), + + %% Unpack and install P1H on client + P1HDir = filename:join([Root, "releases", "P1H"]), + + %% The AppDirs argument (last arg to set_unpacked) below is really + %% not necessary, it could just be [] since the path is the same + %% as default. But it is given here in order to force hitting the + %% release_handler:check_path function so it can be checked that + %% it does not use file:read_file_info on the client node, see + %% trace_disallowed_calls/1 and check_disallowed_calls/0 below. + %% (OTP-9142) + {ok, "P1H"} = rpc:call(Node, release_handler, set_unpacked, + [filename:join(P1HDir, "rel1.rel"), + [{a,"1.0",filename:join(MasterDir,lib)}]]), + + ?check_release_client(Node,"P1H",unpacked,["a-1.0"]), + + ok = rpc:call(Node, release_handler, install_file, + ["P1H", filename:join(P1HDir, "start.boot")]), + ok = rpc:call(Node, release_handler, install_file, + ["P1H", filename:join(P1HDir, "sys.config")]), + ok = rpc:call(Node, release_handler, install_file, + ["P1H", filename:join(P1HDir, "relup")]), + ?print([{release_handler_state, Node}, + rpc:call(Node, sys, get_status, [release_handler])]), + + {ok,"P1G",[new_appl]} = + rpc:call(Node, release_handler, check_install_release, ["P1H"]), + + {ok,"P1G",[new_appl]} = + rpc:call(Node, release_handler, install_release, ["P1H"]), + + Apps = rpc:call(Node, application, which_applications, []), + {a,"A CXC 138 11","1.0"} = lists:keyfind(a, 1, Apps), + X = rpc:call(Node, a, a, []), + {key2, val2} = lists:keyfind(key2, 1, X), + {key1, val1} = lists:keyfind(key1, 1, X), + + check_disallowed_calls(), + reboot(TestNode,Node), + trace_disallowed_calls(Node), + + client1_2(TestNode,PrivDir,Node). + +client1_2(TestNode,PrivDir,Node) -> + ?print(["client1_2 start"]), + + %% Check that P1H is still unpacked, install it and make_permanent + ?check_release_client(Node,"P1H",unpacked,["a-1.0"]), + + {ok,"P1G",[new_appl]} = + rpc:call(Node, release_handler, install_release, ["P1H"]), + ?check_release_client(Node,"P1H",current,["a-1.0"]), + ?check_running_app_client(Node,a,"1.0"), + + ok = rpc:call(Node, release_handler, make_permanent, ["P1H"]), + + check_disallowed_calls(), + reboot(TestNode,Node), + trace_disallowed_calls(Node), + + client1_3(TestNode,PrivDir,Node). + +client1_3(TestNode,PrivDir,Node) -> + ?print(["client1_3 start"]), + + %% Check that P1H is permanent + ?check_release_client(Node,"P1H",permanent,["a-1.0"]), + X = rpc:call(Node, a, a, []), + {key2, val2} = lists:keyfind(key2, 1, X), + {key1, val1} = lists:keyfind(key1, 1, X), + + %% Unpack P1I on master + {ok, "P1I"} = unpack_release(PrivDir,"rel2"), + + MasterRoot = code:root_dir(), + + %% Unpack and install P1I on client + P1IDir = filename:join([MasterRoot, "releases", "P1I"]), + {ok, "P1I"} = rpc:call(Node, release_handler, set_unpacked, + [filename:join(P1IDir, "rel2.rel"),[]]), + + ?check_release_client(Node,"P1I",unpacked,["a-1.1"]), + + ok = rpc:call(Node, release_handler, install_file, + ["P1I", filename:join(P1IDir, "start.boot")]), + ok = rpc:call(Node, release_handler, install_file, + ["P1I", filename:join(P1IDir, "sys.config")]), + ok = rpc:call(Node, release_handler, install_file, + ["P1I", filename:join(P1IDir, "relup")]), + + {ok,"P1H",[{extra, gott}]} = + rpc:call(Node, release_handler, check_install_release, ["P1I"]), + {error,_} = rpc:call(Node, release_handler, check_install_release, ["P1J"]), + {ok,"P1H",[{extra, gott}]} = + rpc:call(Node, release_handler, install_release, ["P1I"]), + + ?check_running_app_client(Node,a,"1.1"), + X2 = rpc:call(Node, a, a, []), + {key2, newval2} = lists:keyfind(key2, 1, X2), + {key1, val1} = lists:keyfind(key1, 1, X2), + {ok, bval} = rpc:call(Node, a, b, []), + + %% Unpack P2A on master + {ok, "P2A"} = unpack_release(PrivDir,"rel3"), + + %% Unpack and install P2A on client + P2ADir = filename:join([MasterRoot, "releases", "P2A"]), + {ok, "P2A"} = + rpc:call(Node, release_handler, set_unpacked, + [filename:join(P2ADir, "rel3.rel"),[]]), + + ok = rpc:call(Node, release_handler, install_file, + ["P2A", filename:join(P2ADir, "start.boot")]), + ok = rpc:call(Node, release_handler, install_file, + ["P2A", filename:join(P2ADir, "sys.config")]), + ok = rpc:call(Node, release_handler, install_file, + ["P2A", filename:join(P2ADir, "relup")]), + + {ok, "P1I", [new_emu]} = + rpc:call(Node, release_handler, check_install_release, ["P2A"]), + ok = rpc:call(Node, release_handler, make_permanent, ["P1I"]), + ?check_release_client(Node,"P1I",permanent,["a-1.1"]), + + %% since the install_release below reboot the node... + check_disallowed_calls(), + cover_client(TestNode,Node,stop_cover), + + {ok, "P1I", [new_emu]} = + rpc:call(Node, release_handler, install_release, ["P2A"]), + %% Reboots the client ! + + check_reboot(TestNode,Node), + trace_disallowed_calls(Node), + + client1_4(TestNode,Node). + +client1_4(TestNode,Node) -> + ?print(["client1_4 start"]), + + %% Check that P2A is in use. + ?check_release_client(Node,"P2A",current,["a-1.1"]), + ?check_running_app_client(Node,a,"1.1"), + X = rpc:call(Node, a, a, []), + {key2, newval2} = lists:keyfind(key2, 1, X), + {key1, val1} = lists:keyfind(key1, 1, X), + {ok, bval} = rpc:call(Node, a, b, []), + + %% Reboot from P1I + check_disallowed_calls(), + reboot(TestNode,Node), + trace_disallowed_calls(Node), + + client1_5(TestNode,Node). + +client1_5(TestNode,Node) -> + ?print(["client1_5 start"]), + + %% Check that P1I is used + {ok, "P1I", [new_emu]} = + rpc:call(Node, release_handler, check_install_release, ["P2A"]), + + %% since the install_release below will reboot the node... + check_disallowed_calls(), + cover_client(TestNode,Node,stop_cover), + + %% Install P2A again + {ok, "P1I", [new_emu]} = + rpc:call(Node, release_handler, install_release, ["P2A"]), + + %% We are rebooted again. + check_reboot(TestNode,Node), + trace_disallowed_calls(Node), + + client1_6(TestNode,Node). + +client1_6(TestNode,Node) -> + ?print(["client1_6 start"]), + + %% Check that P2A is used + ?check_release_client(Node,"P2A",current,["a-1.1"]), + ?check_running_app_client(Node,a,"1.1"), + X = rpc:call(Node, a, a, []), + {key2, newval2} = lists:keyfind(key2, 1, X), + {key1, val1} = lists:keyfind(key1, 1, X), + {ok, bval} = rpc:call(Node, a, b, []), + + %% Make P2A permanent + ok = rpc:call(Node, release_handler, make_permanent, ["P2A"]), + + %% Reboot from P2A + check_disallowed_calls(), + reboot(TestNode,Node), + trace_disallowed_calls(Node), + + client1_7(TestNode,Node). + +client1_7(TestNode,Node) -> + ?print(["client1_7 start"]), + + %% Check that P2A is used + ?check_release_client(Node,"P2A",permanent,["a-1.1"]), + + %% since the reboot_old_release below will reboot the node + check_disallowed_calls(), + cover_client(TestNode,Node,stop_cover), + + %% Install old P1H + rpc:call(Node, release_handler, reboot_old_release, ["P1H"]), + %% We are rebooted. + check_reboot(TestNode,Node), + trace_disallowed_calls(Node), + + client1_8(TestNode,Node). + +client1_8(TestNode,Node) -> + ?print(["client1_8 start"]), + + %% Check that P1H is permanent + ?check_release_client(Node,"P1H",permanent,["a-1.0"]), + X = rpc:call(Node, a, a, []), + {key2, val2} = lists:keyfind(key2, 1, X), + {key1, val1} = lists:keyfind(key1, 1, X), + + %% Remove P1I and P2I from client + ok = rpc:call(Node, release_handler, set_removed, ["P2A"]), + ok = rpc:call(Node, release_handler, set_removed, ["P1I"]), + + check_disallowed_calls(), + reboot(TestNode,Node), + trace_disallowed_calls(Node), + + client1_9(TestNode,Node). + +client1_9(TestNode,Node) -> + ?print(["client1_9 start"]), + + %% Check that P2A and P1I does not exists and that PiH is permanent. + Rels = rpc:call(Node, release_handler, which_releases, []), + false = lists:keysearch("P2A", 2, Rels), + false = lists:keysearch("P1I", 2, Rels), + ?check_release_client(Node,"P1H",permanent,["a-1.0"]), + X = rpc:call(Node, a, a, []), + {key2, val2} = lists:keyfind(key2, 1, X), + {key1, val1} = lists:keyfind(key1, 1, X), + + %% since the reboot_old_release below will reboot the node + check_disallowed_calls(), + cover_client(TestNode,Node,stop_cover), + + %% Install old P1G + rpc:call(Node, release_handler, reboot_old_release, ["P1G"]), + %% We are rebooted. + check_reboot(TestNode,Node), + trace_disallowed_calls(Node), + + client1_10(TestNode,Node). + +client1_10(TestNode,Node) -> + ?print(["client1_10 start"]), + + %% Check that P1G is permanent + ?check_release_client(Node,"P1G",permanent,[]), + ?check_release_client(Node,"P1H",old,["a-1.0"]), + {error,client_node} = rpc:call(Node,release_handler,remove_release,["P1H"]), + ok = rpc:call(Node, release_handler, set_removed, ["P1H"]), + + check_disallowed_calls(), + reboot(TestNode,Node), + trace_disallowed_calls(Node), + + client1_11(TestNode,Node). + +client1_11(TestNode,Node) -> + ?print(["client1_11 start"]), + + %% Check that P1G is permanent + ?check_release_client(Node,"P1G",permanent,[]), + + check_disallowed_calls(), + stop_client(TestNode,Node), %% TEST IS OK !! + net_kernel:monitor_nodes(false), + + ok = release_handler:remove_release("P2A"), + ok = release_handler:remove_release("P1I"), + ok = release_handler:remove_release("P1H"), + ok. + +%% Start tracing of the file module on the client node. This module +%% shall never be called, since +%% 1) the node is a client from the release_handler's point of view, +%% so all file access should be done via rpc calls to the master +%% 2) it is started with erl_prim_loader loader set to 'inet' so all +%% code loading should be done via the inet to the master +%% (OTP-9142) +%% This function is called each time the client node is started and to +%% check if a call has been made, call check_disallowed_node/0 +trace_disallowed_calls(Node) -> + MasterProc = self(), + rpc:call(Node,dbg,tracer,[process,{fun(T,_) -> MasterProc ! T end,[]}]), + rpc:call(Node,dbg,p,[all,call]), + rpc:call(Node,dbg,tp,[file,[]]). + +check_disallowed_calls() -> + receive + Trace when element(1,Trace)==trace -> + exit({disallowed_function_call,Trace}) + after 0 -> + ok + end. + +start_client(TestNode,Client) -> + {Start, Node} = do_start_client(Client,test_host()), + Cmd = lists:concat(["env NODENAME=",Client," ", + filename:join(code:root_dir(), Start)]), + ?print([{start_client,Client},Cmd]), + Res = os:cmd(Cmd), + ?print([{start_client,result},Res]), + receive + {nodeup, Node} -> + wait_started(TestNode,Node) + after 30000 -> + ?print([{start_client,failed,Node},net_adm:ping(Node)]), + ?fail({"can not start", Node}) + end. + +do_start_client(Client, Host) -> + Node = list_to_atom(lists:concat([Client,"@",Host])), + Start = filename:join(["clients", "type1", Node, "bin", "start"]), + {Start, Node}. + +reboot(TestNode,Node) -> + cover_client(TestNode,Node,stop_cover), + rpc:call(Node, init, reboot, []), + check_reboot(TestNode,Node). + +%% This way of checking that the node is rebooted will only work if +%% the nodes are automatically re-connected after the reboot. This +%% happens for master/client (when sasl is started on the client). +check_reboot(TestNode,Node) -> + receive + {nodedown, Node} -> + receive + {nodeup, Node} -> wait_started(TestNode,Node) + after 30000 -> + ?fail({Node, "not rebooted",net_adm:ping(Node)}) + end + after 30000 -> + ?fail({Node, "not closing down",net_adm:ping(Node)}) + end. + +stop_client(TestNode,Node) -> + cover_client(TestNode,Node,stop_cover), + rpc:call(Node, init, stop, []), + receive + {nodedown, Node} -> ok + after 30000 -> + ?fail({Node, "not stopping"}) + end. + +wait_started(TestNode,Node) -> + case rpc:call(Node, init, get_status, []) of + {started, _} -> + cover_client(TestNode,Node,start_cover), + Node; + _ -> + timer:sleep(1000), + wait_started(TestNode,Node) + end. + +cover_client(TestNode,Node,Func) -> + R = rpc:call(TestNode,release_handler_SUITE,Func,[Node]), + ?print([{Func,Node,R}]). + + +%%----------------------------------------------------------------- +%% This test starts a client node which uses this node as master +%% for the release_handler. +%% The client node has the name cli2@HOSTNAME. +%% The client node is not allowed to do ANY release updates +%% as it also have another (non-existing) master node. +%% +%% The to_erl /tmp/cli2@HOSTNAME/ command can be used to connect +%% to the client node. +%% run_erl logs for the client can be found in the directory: +%% code:root_dir() ++ "/clients/type1/cli2@HOSTNAME/log +%%----------------------------------------------------------------- +client2(TestNode,PrivDir,ClientSname) -> + TestHost = test_host(), + ?print(["client2 start"]), + + %% Clean up if previous test case failed + release_handler:remove_release("P1H"), + + ok = net_kernel:monitor_nodes(true), + Node = start_client(TestNode,ClientSname), + + %% Check env var for SASL on client node + ?print([{sasl_env, Node}, rpc:call(Node, application, get_all_env, [sasl])]), + SaslEnv = rpc:call(Node, application, get_all_env, [sasl]), + {_,CliDir} = lists:keyfind(client_directory,1,SaslEnv), + {_,[Master,Master2]} = lists:keyfind(masters,1,SaslEnv), + {_,StartCli} = lists:keyfind(start_prg,1,SaslEnv), + Root = code:root_dir(), + true = (CliDir =:= filename:join([Root,"clients","type1",Node])), + true = (StartCli =:= filename:join([CliDir,"bin","start"])), + true = (Master =:= node()), + true = (Master2 =:= list_to_atom("master2@"++TestHost)), + + {ok, "P1H"} = unpack_release(PrivDir,"rel1"), + + {error,{bad_masters,[Master2]}} = + rpc:call(Node, release_handler, set_unpacked, + [filename:join([Root, "releases", "P1H", "rel1.rel"]),[]]), + + {error,{no_such_release,"P1H"}} = + rpc:call(Node, release_handler, check_install_release, ["P1H"]), + + stop_client(TestNode,Node), %% TEST IS OK !! + net_kernel:monitor_nodes(false), + + release_handler:remove_release("P1H"), + ok. + + +stop(Now) -> + %% The timestamp is only used for debugging. It is printed by + %% release_handler_SUITE also. + R = init:stop(), + erlang:display({init_stop,Now,R}), + R. + +unpack_p1h(TestNode,PrivDir) -> + {ok, "P1H"} = unpack_release(PrivDir,"rel1"), + ?check_release("P1H",unpacked,["a-1.0"]), + ok. + +permanent_p1h(TestNode) -> + ?check_release("P1H",unpacked,["a-1.0"]), + {ok,"P1G",[new_appl]} = release_handler:install_release("P1H"), + ?check_release("P1H",current,["a-1.0"]), + ok = release_handler:make_permanent("P1H"), + ?check_release("P1H",permanent,["a-1.0"]), + ok. + + +reg_proc(Name) -> + catch unregister(Name), + Pid = spawn_link(?MODULE, registered_loop, [Name]), + global:register_name(Name, Pid), + ok. + +registered_loop(_Name) -> + receive + kill -> + exit(killed) + end. + +check_release(TestNode,Node,Vsn,Status,Apps,Line) -> + case rpc:call(Node,release_handler,which_releases,[]) of + {badrpc,_}=Error -> + ?fail_line(Line,{check_release,Node,Vsn,Status,Error}); + Rels -> + ?print_line(Line,["check_release:", Rels]), + {"SASL-test", Vsn, Libs, Status} = lists:keyfind(Vsn, 2, Rels), + true = lists:all(fun(App) -> lists:member(App,Libs) end,Apps), + ok + end. + +check_running_app(TestNode,Node,App,Vsn,Line) -> + case rpc:call(Node,application,which_applications,[]) of + {badrpc,_}=Error -> + ?fail_line(Line,{check_running_app,Node,App,Vsn,Error}); + Apps -> + ?print_line(Line,["check_running_app:", Apps]), + {App, _, Vsn} = lists:keyfind(a, 1, Apps), + ok + end. + +test_host() -> + {ok,Host} = inet:gethostname(), + Host. + +unpack_release(PrivDir,Rel) -> + copy(filename:join([PrivDir,Rel,Rel++".tar.gz"]), + filename:join(code:root_dir(),releases)), + release_handler:unpack_release(Rel). + +copy(Src, DestDir) -> + Dest = filename:join(DestDir, filename:basename(Src)), + {ok,_} = file:copy(Src, Dest), + ok. + diff --git a/lib/sasl/test/overload_SUITE.erl b/lib/sasl/test/overload_SUITE.erl new file mode 100644 index 0000000000..92b1aaed6e --- /dev/null +++ b/lib/sasl/test/overload_SUITE.erl @@ -0,0 +1,175 @@ +%% +%% %CopyrightBegin% +%% +%% Copyright Ericsson AB 2011. 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(overload_SUITE). +-include("test_server.hrl"). + +-compile(export_all). + +%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% + +all() -> [info, set_config_data, set_env_vars, request, timeout]. +all(suite) -> all(). + +init_per_testcase(_Case,Config) -> + restart_sasl(), + Config. + +end_per_testcase(Case,Config) -> + try apply(?MODULE,Case,[cleanup,Config]) + catch error:undef -> ok + end, + ok. + +%%%----------------------------------------------------------------- +info(suite) -> []; +info(_Config) -> + ?line Info = overload:get_overload_info(), + ?line [{total_intensity,0.0}, + {accept_intensity,0.0}, + {max_intensity,0.8}, + {weight,0.1}, + {total_requests,0}, + {accepted_requests,0}] = Info. + +%%%----------------------------------------------------------------- +set_config_data(suite) -> []; +set_config_data(_Config) -> + ?line InfoDefault = overload:get_overload_info(), + ?line ok = check_info(0.8,0.1,InfoDefault), + ?line ok = overload:set_config_data(0.5,0.4), + ?line Info1 = overload:get_overload_info(), + ?line ok = check_info(0.5,0.4,Info1), + ok. + +%%%----------------------------------------------------------------- +set_env_vars(suite) -> []; +set_env_vars(_Config) -> + ?line InfoDefault = overload:get_overload_info(), + ?line ok = check_info(0.8,0.1,InfoDefault), + ?line ok = application:set_env(sasl,overload_max_intensity,0.5), + ?line ok = application:set_env(sasl,overload_weight,0.4), + ?line ok = application:stop(sasl), + ?line ok = application:start(sasl), + ?line Info1 = overload:get_overload_info(), + ?line ok = check_info(0.5,0.4,Info1), + ok. +set_env_vars(cleanup,_Config) -> + application:unset_env(sasl,overload_max_intensity), + application:unset_env(sasl,overload_weight), + ok. + +%%%----------------------------------------------------------------- +request(suite) -> []; +request(_Config) -> + %% Find number of request that can be done with default settings + %% and no delay + ?line overload:set_config_data(0.8, 0.1), + ?line NDefault = do_many_requests(0), + ?line restart_sasl(), + ?line ?t:format("NDefault: ~p",[NDefault]), + + %% Check that the number of requests increases when max_intensity + %% increases + ?line overload:set_config_data(2, 0.1), + ?line NLargeMI = do_many_requests(0), + ?line restart_sasl(), + ?line ?t:format("NLargeMI: ~p",[NLargeMI]), + ?line true = NLargeMI > NDefault, + + %% Check that the number of requests decreases when weight + %% increases + ?line overload:set_config_data(0.8, 1), + ?line NLargeWeight = do_many_requests(0), + ?line restart_sasl(), + ?line ?t:format("NLargeWeight: ~p",[NLargeWeight]), + ?line true = NLargeWeight < NDefault, + + %% Check that number of requests increases when delay between + %% requests increases. + %% (Keeping same config and comparing to large weight in order to + %% minimize the time needed for this case.) + ?line overload:set_config_data(0.8, 1), + ?line NLargeTime = do_many_requests(500), + ?line restart_sasl(), + ?line ?t:format("NLargeTime: ~p",[NLargeTime]), + ?line true = NLargeTime > NLargeWeight, + ok. + +%%%----------------------------------------------------------------- +timeout(suite) -> []; +timeout(_Config) -> + ?line overload:set_config_data(0.8, 1), + ?line _N = do_many_requests(0), + + %% Check that the overload alarm is raised + ?line [{overload,_}] = alarm_handler:get_alarms(), + + %% Fake a clear timeout in overload.erl and check that, since it + %% came very soon after the overload situation, the alarm is not + %% cleared + ?line overload ! timeout, + ?line timer:sleep(1000), + ?line [{overload,_}] = alarm_handler:get_alarms(), + + %% A bit later, try again and check that this time the alarm is + %% cleared + ?line overload ! timeout, + ?line timer:sleep(1000), + ?line [] = alarm_handler:get_alarms(), + + ok. + + +%%%----------------------------------------------------------------- +%%% INTERNAL FUNCTIONS + +%%%----------------------------------------------------------------- +%%% Call overload:request/0 up to 30 times with the given time delay +%%% between. Stop when 'reject' is returned. +do_many_requests(T) -> + 30 - do_requests(30,T). + +do_requests(0,_) -> + ?t:fail(never_rejected); +do_requests(N,T) -> + case overload:request() of + accept -> + timer:sleep(T), + do_requests(N-1,T); + reject -> + N + end. + +%%%----------------------------------------------------------------- +%%% Restart the sasl application +restart_sasl() -> + application:stop(sasl), + application:start(sasl), + ok. + +%%%----------------------------------------------------------------- +%%% Check that max_intensity and weight is set as expected +check_info(MI,W,Info) -> + case {lists:keyfind(max_intensity,1,Info), lists:keyfind(weight,1,Info)} of + {{_,MI},{_,W}} -> ok; + _ -> ?t:fail({unexpected_info,MI,W,Info}) + end. + + diff --git a/lib/sasl/test/rb_SUITE.erl b/lib/sasl/test/rb_SUITE.erl new file mode 100644 index 0000000000..b53c382609 --- /dev/null +++ b/lib/sasl/test/rb_SUITE.erl @@ -0,0 +1,606 @@ +%% +%% %CopyrightBegin% +%% +%% Copyright Ericsson AB 2011. 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(rb_SUITE). +-include("test_server.hrl"). + +-compile(export_all). + +-define(SUP,rb_SUITE_sup). + +%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% +all() -> + no_group_cases() ++ [{group,running_error_logger}]. + +no_group_cases() -> + [help, + start_error_stop]. + +groups() -> + [{running_error_logger,[shuffle],[show, + list, + rescan, + start_stop_log, + grep, + filter_filter, + filter_date, + filter_filter_and_date, + filter_re_no + ]}]. + + +all(suite) -> + no_group_cases() ++ + [{conf, + install_mf_h, + element(3,lists:keyfind(running_error_logger,1,groups())), + remove_mf_h} + ]. + + +init_per_suite(Config) -> + ?line PrivDir = ?config(priv_dir,Config), + ?line RbDir = filename:join(PrivDir,rb), + ?line ok = file:make_dir(RbDir), + NewConfig = [{rb_dir,RbDir}|Config], + reset_sasl(NewConfig), + NewConfig. + +end_per_suite(_Config) -> + ok. + +init_per_group(running_error_logger,Config) -> + install_mf_h(Config). + +end_per_group(running_error_logger,Config) -> + remove_mf_h(Config). + +init_per_testcase(_Case,Config) -> + case whereis(?SUP) of + undefined -> ok; + Pid -> kill(Pid) + end, + empty_error_logs(Config), + Config. + +kill(Pid) -> + Ref = erlang:monitor(process,Pid), + exit(Pid,kill), + receive {'DOWN', Ref, process, Pid, _Info} -> ok end. + +end_per_testcase(Case,Config) -> + try apply(?MODULE,Case,[cleanup,Config]) + catch error:undef -> ok + end, + ok. + + +%%%----------------------------------------------------------------- + +help() -> help(suite). +help(suite) -> []; +help(_Config) -> + ?line Help = capture(fun() -> rb:h() end), + ?line "Report Browser Tool - usage" = hd(Help), + ?line "rb:stop - stop the rb_server" = lists:last(Help), + ok. + + +start_error_stop() -> start_error_stop(suite). +start_error_stop(suite) -> []; +start_error_stop(Config) -> + ?line RbDir = ?config(rb_dir,Config), + + ?line {error,{"cannot locate report directory",_}} = rb:start(), + + + ?line ok = application:set_env(sasl,error_logger_mf_dir,"invaliddir"), + ?line ok = application:set_env(sasl,error_logger_mf_maxbytes,1000), + ?line ok = application:set_env(sasl,error_logger_mf_maxfiles,2), + ?line restart_sasl(), + ?line {error,{"cannot read the index file",_}} = rb:start(), + ?line ok = application:set_env(sasl,error_logger_mf_dir,RbDir), + ?line restart_sasl(), + ?line {ok,_} = rb:start(), + + ?line ok = rb:stop(), + ok. + + +%% start_opts(suite) -> []; +%% start_opts(Config) -> +%% PrivDir = ?config(priv_dir,Config), +%% RbDir = filename:join(PrivDir,rb_opts), +%% ok = file:make_dir(RbDir), + + +install_mf_h(Config) -> + ?line RbDir = ?config(rb_dir,Config), + ?line ok = application:set_env(sasl,error_logger_mf_dir,RbDir), + ?line ok = application:set_env(sasl,error_logger_mf_maxbytes,5000), + ?line ok = application:set_env(sasl,error_logger_mf_maxfiles,2), + ?line restart_sasl(), + Config. + +remove_mf_h(_Config) -> + ok. + + + +show() -> show(suite). +show(suite) -> []; +show(Config) -> + ?line PrivDir = ?config(priv_dir,Config), + ?line OutFile = filename:join(PrivDir,"rb_SUITE_log.txt"), + + %% Insert some reports in the error log and start rb + init_error_logs(), + ?line ok = start_rb(OutFile), + + %% Show all reports + ?line All = check_report(fun() -> rb:show() end,OutFile), + + %% Show by number + ?line [{_,First}] = check_report(fun() -> rb:show(1) end,OutFile), + ?line {1,First} = lists:keyfind(1,1,All), + + %% Show by type + ?line [{_,CR}] = check_report(fun() -> rb:show(crash_report) end,OutFile), + ?line true = contains(CR,"rb_test_crash"), + ?line [{_,EC},{_,EM}] = check_report(fun() -> rb:show(error) end,OutFile), + ?line true = contains(EC,"rb_test_crash"), + ?line true = contains(EM,"rb_test_error_msg"), + ?line [{_,ER}] = check_report(fun() -> rb:show(error_report) end,OutFile), + ?line true = contains(ER,"rb_test_error"), + ?line [{_,IR}] = check_report(fun() -> rb:show(info_report) end,OutFile), + ?line true = contains(IR,"rb_test_info"), + ?line [{_,IM}] = check_report(fun() -> rb:show(info_msg) end,OutFile), + ?line true = contains(IM,"rb_test_info_msg"), + ?line [_|_] = check_report(fun() -> rb:show(progress) end,OutFile), + ?line [{_,SR}] = check_report(fun() -> rb:show(supervisor_report) end, + OutFile), + ?line true = contains(SR,"child_terminated"), + ?line true = contains(SR,"{rb_SUITE,rb_test_crash}"), + + ok. + +list() -> list(suite). +list(suite) -> []; +list(Config) -> + ?line PrivDir = ?config(priv_dir,Config), + ?line OutFile = filename:join(PrivDir,"rb_SUITE_log.txt"), + + %% Insert some reports in the error log and start rb + init_error_logs(), + ?line ok = start_rb(OutFile), + + ?line All = capture(fun() -> rb:list() end), + ?line [{crash_report,[_]=CR}, + {error,[_,_]=EM}, + {error_report,[_]=ER}, + {info_msg,[_]=IM}, + {info_report,[_]=IR}, + {progress,[_|_]=P}, + {supervisor_report,[_]=SR}] = sort_list(All), + + ?line [{crash_report,CR}] = + sort_list(capture(fun() -> rb:list(crash_report) end)), + ?line [{error,EM}] = + sort_list(capture(fun() -> rb:list(error) end)), + ?line [{error_report,ER}] = + sort_list(capture(fun() -> rb:list(error_report) end)), + ?line [{info_msg,IM}] = + sort_list(capture(fun() -> rb:list(info_msg) end)), + ?line [{info_report,IR}] = + sort_list(capture(fun() -> rb:list(info_report) end)), + ?line [{progress,P}] = + sort_list(capture(fun() -> rb:list(progress) end)), + ?line [{supervisor_report,SR}] = + sort_list(capture(fun() -> rb:list(supervisor_report) end)), + + ok. + + +grep() -> grep(suite). +grep(suite) -> []; +grep(Config) -> + ?line PrivDir = ?config(priv_dir,Config), + ?line OutFile = filename:join(PrivDir,"rb_SUITE_log.txt"), + + %% Insert some reports in the error log and start rb + init_error_logs(), + ?line ok = start_rb(OutFile), + + ?line [{_,S}, + {_,CR}, + {_,EC}, + {_,IM}, + {_,IR}, + {_,EM}, + {_,ER}]= check_report(fun() -> rb:grep("rb_test_") end,OutFile), + ?line true = contains(S, "rb_test_crash"), + ?line true = contains(CR, "rb_test_crash"), + ?line true = contains(EC, "rb_test_crash"), + ?line true = contains(IM, "rb_test_info_msg"), + ?line true = contains(IR, "rb_test_info"), + ?line true = contains(EM, "rb_test_error_msg"), + ?line true = contains(ER, "rb_test_error"), + ok. + + +filter_filter() -> filter_filter(suite). +filter_filter(suite) -> []; +filter_filter(Config) -> + ?line PrivDir = ?config(priv_dir,Config), + ?line OutFile = filename:join(PrivDir,"rb_SUITE_log.txt"), + + %% Insert some reports in the error log and start rb + init_error_logs(), + ?line ok = start_rb(OutFile), + + ?line All = check_report(fun() -> rb:show() end,OutFile), + + ?line ER = [_] = rb_filter([{rb_SUITE,rb_test_error}],OutFile), + ?line [] = rb_filter([{rb_SUITE,rb_test}],OutFile), + ?line _E = [_,_] = rb_filter([{rb_SUITE,"rb_test",re}],OutFile), + ?line AllButER = rb_filter([{rb_SUITE,rb_test_error,no}],OutFile), + + {_,AllRep} = lists:unzip(All), + {_,ERRep} = lists:unzip(ER), + {_,AllButERRep} = lists:unzip(AllButER), + ?line AllButERRep = AllRep -- ERRep, + + ok. + +filter_date() -> filter_date(suite). +filter_date(suite) -> []; +filter_date(Config) -> + ?line PrivDir = ?config(priv_dir,Config), + ?line OutFile = filename:join(PrivDir,"rb_SUITE_log.txt"), + + + %% Insert some reports in the error log and start rb + init_error_logs(), + Between1 = calendar:local_time(), + timer:sleep(1000), + Between2 = calendar:local_time(), + ?line ok = start_rb(OutFile), + + ?line All = check_report(fun() -> rb:show() end,OutFile), + + Before = calendar:gregorian_seconds_to_datetime( + calendar:datetime_to_gregorian_seconds(calendar:local_time()) - 10), + After = calendar:gregorian_seconds_to_datetime( + calendar:datetime_to_gregorian_seconds(calendar:local_time()) + 1), + + ?line All = rb_filter([],{Before,from},OutFile), + ?line All = rb_filter([],{After,to},OutFile), + ?line [] = rb_filter([],{Before,to},OutFile), + ?line [] = rb_filter([],{After,from},OutFile), + ?line All = rb_filter([],{Before,After},OutFile), + + %%?t:format("~p~n",[All]), + ?line AllButLast = [{N-1,R} || {N,R} <- tl(All)], + ?line AllButLast = rb_filter([],{Before,Between1},OutFile), + + ?line Last = hd(All), + ?line [Last] = rb_filter([],{Between2,After},OutFile), + + ok. + +filter_filter_and_date() -> filter_filter_and_date(suite). +filter_filter_and_date(suite) -> []; +filter_filter_and_date(Config) -> + ?line PrivDir = ?config(priv_dir,Config), + ?line OutFile = filename:join(PrivDir,"rb_SUITE_log.txt"), + + + %% Insert some reports in the error log and start rb + init_error_logs(), + Between1 = calendar:local_time(), + timer:sleep(1000), + Between2 = calendar:local_time(), + ?line error_logger:error_report([{rb_SUITE,rb_test_filter}]), + ?line ok = start_rb(OutFile), + + Before = calendar:gregorian_seconds_to_datetime( + calendar:datetime_to_gregorian_seconds(calendar:local_time()) - 10), + After = calendar:gregorian_seconds_to_datetime( + calendar:datetime_to_gregorian_seconds(calendar:local_time()) + 1), + + ?line All = check_report(fun() -> rb:show() end,OutFile), + ?line Last = hd(All), + + ?line [_,_,_] = rb_filter([{rb_SUITE,"rb_test",re}],{Before,After},OutFile), + ?line [_,_] = rb_filter([{rb_SUITE,"rb_test",re}],{Before,Between1},OutFile), + ?line [_] = rb_filter([{rb_SUITE,"rb_test",re}],{Between2,After},OutFile), + ?line [_] = rb_filter([{rb_SUITE,rb_test_filter}],{Before,After},OutFile), + ?line [] = rb_filter([{rb_SUITE,rb_test_filter}],{Before,Between1},OutFile), + ?line [Last] = rb_filter([{rb_SUITE,rb_test_filter,no}],{Between2,After},OutFile), + ?line {_,Str} = Last, + ?line false = contains(Str,"rb_test_filter"), + + ok. + + +filter_re_no() -> filter_re_no(suite). +filter_re_no(suite) -> []; +filter_re_no(Config) -> + ?line PrivDir = ?config(priv_dir,Config), + ?line OutFile = filename:join(PrivDir,"rb_SUITE_log.txt"), + + %% Insert some reports in the error log and start rb + init_error_logs(), + ?line ok = start_rb(OutFile), + + ?line All = check_report(fun() -> rb:show() end,OutFile), + + ?line E = [_,_] = rb_filter([{rb_SUITE,"rb_test",re}],OutFile), + ?line AllButE = rb_filter([{rb_SUITE,"rb_test",re,no}],OutFile), + + {_,AllRep} = lists:unzip(All), + {_,ERep} = lists:unzip(E), + {_,AllButERep} = lists:unzip(AllButE), + ?line AllButERep = AllRep -- ERep, + + ok. + + +rescan() -> rescan(suite). +rescan(suite) -> []; +rescan(Config) -> + ?line PrivDir = ?config(priv_dir,Config), + ?line OutFile = filename:join(PrivDir,"rb_SUITE_log.txt"), + + %% Start rb + ?line ok = start_rb(OutFile), + + %% Insert one more report and check that the list is longer. Note + %% that there might be two more reports, since the progress report + %% from starting rb_server might not be included before the rescan. + ?line AllBefore = capture(fun() -> rb:list() end), + ?line error_logger:error_report([{rb_SUITE,rb_test_rescan}]), + ?line ok = rb:rescan(), + ?line AllAfter = capture(fun() -> rb:list() end), + ?line Diff = length(AllAfter) - length(AllBefore), + ?line true = (Diff >= 1), + + ok. + + +start_stop_log() -> start_stop_log(suite). +start_stop_log(suite) -> []; +start_stop_log(Config) -> + ?line PrivDir = ?config(priv_dir,Config), + ?line OutFile = filename:join(PrivDir,"rb_SUITE_log.txt"), + ?line ok = file:write_file(OutFile,[]), + + %% Start rb and check that show is printed to standard_io + ?line ok = start_rb(), + ?line StdioResult = [_|_] = capture(fun() -> rb:show(1) end), + ?line {ok,<<>>} = file:read_file(OutFile), + + %% Start log and check that show is printed to log and not to standad_io + ?line ok = rb:start_log(OutFile), + ?line [] = capture(fun() -> rb:show(1) end), + ?line {ok,Bin} = file:read_file(OutFile), + ?line true = (Bin =/= <<>>), + + %% Stop log and check that show is printed to standard_io and not to log + ?line ok = rb:stop_log(), + ?line ok = file:write_file(OutFile,[]), + ?line StdioResult = capture(fun() -> rb:show(1) end), + ?line {ok,<<>>} = file:read_file(OutFile), + + %% Test that standard_io is used if log file can not be opened + ?line ok = rb:start_log(filename:join(nonexistingdir,"newfile.txt")), + ?line StdioResult = capture(fun() -> rb:show(1) end), + ?line {ok,<<>>} = file:read_file(OutFile), + + ok. + + +%%%----------------------------------------------------------------- +%%% INTERNAL FUNCTIONS + +restart_sasl() -> + application:stop(sasl), + ok = application:start(sasl), + wait_for_sasl(). + +reset_sasl(Config) -> + application:unset_env(sasl,error_logger_mf_dir), + application:unset_env(sasl,error_logger_mf_maxbytes), + application:unset_env(sasl,error_logger_mf_maxfiles), + empty_error_logs(Config). + +empty_error_logs(Config) -> + application:stop(sasl), + catch delete_content(?config(rb_dir, Config)), + ok = application:start(sasl), + wait_for_sasl(). + +wait_for_sasl() -> + wait_for_sasl(50). +wait_for_sasl(0) -> + ?t:fail("sasl application did not start within 5 seconds"); +wait_for_sasl(N) -> + case lists:keymember(sasl,1,application:which_applications()) of + true -> + ok; + false -> + timer:sleep(100), + wait_for_sasl(N-1) + end. + +start_rb(OutFile) -> + do_start_rb([{start_log,OutFile}]). +start_rb() -> + do_start_rb([]). + +do_start_rb(Opts) -> + {ok,Pid} = rb:start(Opts), + + %% Wait for process to started, then wait a little bit more + sys:get_status(Pid), + timer:sleep(500), + + %% Make sure printouts (e.g. from rb:list(), come to the test log, + %% and that they can be captured. + group_leader(group_leader(),Pid), + ok. + + +delete_tree(Dir) -> + case filelib:is_dir(Dir) of + true -> + delete_content(Dir), + file:del_dir(Dir); + false -> + ok = file:delete(Dir) + end. + +delete_content(Dir) -> + {ok,Files} = file:list_dir(Dir), + lists:foreach(fun(File) -> delete_tree(filename:join(Dir,File)) end, + Files). + +init_error_logs() -> + ?line error_logger:error_report([{rb_SUITE,rb_test_error}]), + ?line error_logger:error_msg("rb_test_error_msg"), + ?line error_logger:info_report([{rb_SUITE,rb_test_info}]), + ?line error_logger:info_msg("rb_test_info_msg"), + ?line _Pid = start(), + ?line Ref = erlang:monitor(process,?MODULE), + ?line gen_server:cast(?MODULE,crash), + ?line receive {'DOWN',Ref,process,_,{rb_SUITE,rb_test_crash}} -> ok + after 2000 -> + ?t:format("Got: ~p~n",[process_info(self(),messages)]), + ?t:fail("rb_SUITE server never died") + end, + ?line erlang:demonitor(Ref), + ?line wait_for_server(), + ok. + +wait_for_server() -> + case whereis(?MODULE) of + undefined -> + wait_for_server(); + Pid -> + timer:sleep(100), % allow the supervisor report to be written + Pid + end. + +capture(Fun) -> + ?t:capture_start(), + ok = Fun(), + timer:sleep(1000), + ?t:capture_stop(), + string:tokens(lists:append(?t:capture_get()),"\n"). + + +rb_filter(Filter,OutFile) -> + check_report(fun() -> rb:filter(Filter) end, OutFile). +rb_filter(Filter,Dates,OutFile) -> + check_report(fun() -> rb:filter(Filter,Dates) end, OutFile). + + +%% This function first empties the given report file, then executes +%% the fun and returns a list of {N,Report}, where Report is a report +%% read from the file and N is an integer. The newest report has the +%% lowest number. +%% If the fun was a call to rb:show() (i.e. with no arguments), then +%% the numbering (N) will be the same as rb's own numbering (as shown +%% by rb:list()). +check_report(Fun,File) -> + file:delete(File), + rb:rescan([{start_log,File}]), + ok = Fun(), + {ok,Bin} = file:read_file(File), + Reports = split_reports(binary_to_list(Bin),[],[]), + lists:zip(lists:seq(1,length(Reports)),Reports). + +-define(report_header_line,"\n===============================================================================\n"). +split_reports([],Report,Reports) -> + add_report(Report,Reports); +split_reports(Text,Report,Reports) -> + case Text of + ?report_header_line++Rest -> + {Heading,PrevReport} = lists:splitwith(fun($\n) -> false; + (_) -> true + end, + Report), + split_reports(Rest, + ?report_header_line++Heading, + add_report(PrevReport,Reports)); + [Ch|Rest] -> + split_reports(Rest,[Ch|Report],Reports) + end. + +add_report(Report,Reports) -> + case string:strip(Report,both,$\n) of + [] -> Reports; + Report1 -> [lists:reverse(Report1)|Reports] + end. + +%% Returns true if Substr is a substring of Str. +contains(Str,Substr) -> + 0 =/= string:str(Str,Substr). + +%% Sort the result of rb_list after report type +sort_list(List) -> + sort_list(List,dict:new()). +sort_list([H|T],D) -> + case re:run(H,"\s+[0-9]+\s+([a-z_]+)",[{capture,all_but_first,list}]) of + nomatch -> + sort_list(T,D); + {match,[TypeStr]} -> + sort_list(T,dict:append(list_to_atom(TypeStr),H,D)) + end; +sort_list([],D) -> + lists:sort(dict:to_list(D)). + + +%%%----------------------------------------------------------------- +%%% A dummy supervisor and gen_server used for creating crash- and +%%% supervisor reports +start() -> + {ok,Pid} = + supervisor:start_link({local, ?SUP}, ?MODULE, i_am_supervisor), + unlink(Pid), + Pid. +start_server() -> + gen_server:start_link({local, ?MODULE}, ?MODULE, i_am_server, []). +init(i_am_server) -> + {ok, state}; +init(i_am_supervisor) -> + AChild = {?SUP,{?MODULE,start_server,[]}, + permanent,2000,worker,[?MODULE]}, + {ok,{{one_for_all,1,1}, [AChild]}}. +handle_call(_Request, _From, State) -> + Reply = ok, + {reply, Reply, State}. +handle_cast(crash, State) -> + exit({rb_SUITE,rb_test_crash}), + {noreply, State}. +handle_info(_Info, State) -> + {noreply, State}. +terminate(_Reason, _State) -> + ok. + diff --git a/lib/sasl/test/release_handler_SUITE.erl b/lib/sasl/test/release_handler_SUITE.erl new file mode 100644 index 0000000000..efa775f344 --- /dev/null +++ b/lib/sasl/test/release_handler_SUITE.erl @@ -0,0 +1,1651 @@ +%% +%% %CopyrightBegin% +%% +%% Copyright Ericsson AB 2011. 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(release_handler_SUITE). + +-include_lib("common_test/include/ct.hrl"). + +-compile(export_all). + +% Default timetrap timeout (set in init_per_testcase). +%-define(default_timeout, ?t:minutes(40)). +-define(default_timeout, ?t:minutes(10)). + +suite() -> + [{ct_hooks, [ts_install_cth]}]. + +init_per_suite(Config) -> + application:start(sasl), + Config. + +end_per_suite(_Config) -> + ok. + +all() -> + case os:type() of + {unix, _} -> unix_cases(); + {win32, _} -> win32_cases() + end. + +unix_cases() -> + RunErl = filename:join([code:root_dir(),"bin","run_erl"]), + RunErlCases = case filelib:is_file(RunErl) of + true -> [{group, release}]; + false -> [no_run_erl] + end, + [target_system] ++ RunErlCases ++ cases(). + +win32_cases() -> + cases(). + +%% Cases that can be run on all platforms +cases() -> + [otp_2740, otp_2760, otp_5761, instructions, eval_appup]. + +groups() -> + [{release,[], + [ + {group,release_single}, + {group,release_gg} + ]}, + {release_single,[], + [ + upgrade, + client1, + client2 + ]}, + {release_gg,[], + [ + upgrade_gg + ]}]. + +%% {group,release} +%% Top group for all cases using run_erl +init_per_group(release, Config) -> + Dog = ?t:timetrap(?default_timeout), + P1gInstall = filename:join(priv_dir(Config),p1g_install), + ok = do_create_p1g(Config,P1gInstall), + ok = create_p1h(Config), + ?t:timetrap_cancel(Dog); + +%% {group,release_single} +%% Subgroup of {group,release}, contains all cases that are not +%% related to global_group +init_per_group(release_single, Config) -> + Dog = ?t:timetrap(?default_timeout), + + %% Create some more releases to upgrade to + ok = create_p1i(Config), + ok = create_p2a(Config), + + ?t:timetrap_cancel(Dog); + +%% {group,release_gg} +%% Subgroup of {group,release}. global_group tests. +init_per_group(release_gg, Config0) -> + Config = [{sname_prefix,release_gg}|Config0], + + PrivDir = priv_dir(Config), + Dog = ?t:timetrap(?default_timeout), + + reg_print_proc(), %% starts a printer process on this node + + Snames = [Gg1Sname,Gg2Sname,Gg3Sname,Gg4Sname,Gg5Sname,Gg6Sname] = + gg_node_snames(Config), + + %% kill all possible nodes which are to be used + ok = stop_nodes([node_name(Sname) || Sname <- Snames]), + + %% For gg1, gg3, gg4 and gg5: create a target system running + %% P1G, and with P1H unpacked. + %% For gg2 and gg6: create a target system running P1H. + %% Use gg2 for unpacking and permanenting P1H. + ok = copy_installed(Config,p1g_install,[Gg2Sname]), + InstallNode = unpack_p1h(Config,Gg2Sname), + ok = copy_installed(Config,Gg2Sname,[Gg1Sname,Gg3Sname,Gg4Sname,Gg5Sname]), + ok = permanent_p1h(InstallNode), + ok = stop_nodes([InstallNode]), + ok = copy_installed(Config,Gg2Sname,[Gg6Sname]), + + %% Replace the sys.config files + %% The reason for not creating the releases with these configs in + %% the first place (create_p1g, create_p1h) is that then the + %% InstallNode (gg2) will be very slow started since it will try + %% to synch with the other nodes in the global group. + %% Also, the rpc call for installing the P1H release (in + %% permanent_p1h/1) would return {rpc,nodedown} due to change of + %% global groups. + lists:foreach( + fun(Sname) -> + ReleasesDir = filename:join([PrivDir,Sname,"releases"]), + write_term_file(filename:join([ReleasesDir,"P1G","sys.config"]), + gg_config([Gg1Sname,Gg3Sname,Gg4Sname,Gg5Sname])), + write_term_file(filename:join([ReleasesDir,"P1H","sys.config"]), + gg_config([Gg1Sname,Gg2Sname,Gg4Sname, + Gg5Sname,Gg6Sname])) + end, + Snames), + + ?t:timetrap_cancel(Dog), + [{snames,Snames}|Config]. + + +end_per_group(release, Config) -> + Dog = ?t:timetrap(?default_timeout), + stop_print_proc(), + delete_release(Config), + ?t:timetrap_cancel(Dog), + Config; +end_per_group(_GroupName, Config) -> + Config. + + +init_per_testcase(Case, Config0) -> + Dog = test_server:timetrap(?default_timeout), + Config = [{sname_prefix,Case},{watchdog, Dog}|Config0], + try apply(?MODULE,Case,[cleanup,Config]) + catch error:undef -> ok + end, + ?t:format("~n======= init_per_testcase done =======~n",[]), + Config. + +end_per_testcase(Case, Config) -> + ?t:format("~n======= start end_per_testcase =======~n",[]), + Dog=?config(watchdog, Config), + test_server:timetrap_cancel(Dog), + + %% DEBUG + case ?config(tc_status,Config) of + ok -> + ok; + _Fail -> + %% save logs from master and client nodes + PrivDir = priv_dir(Config), + SaveDir = filename:join(PrivDir,save), + FailDir = filename:join(SaveDir,lists:concat(["failed-",Case])), + ok = filelib:ensure_dir(filename:join(FailDir,"*")), + + LogDirs = filelib:wildcard(filename:join([PrivDir,"*",log])), + + lists:foreach( + fun(LogDir) -> + ["log",Sname|_] = lists:reverse(filename:split(LogDir)), + copy_tree(Config,LogDir,Sname,FailDir) + end, + LogDirs), + + case filelib:is_file("sasl_erl_crash.dump") of + true -> + copy_file("sasl_erl_crash.dump",FailDir); + _ -> + ok + end + + end, + %% End DEBUG + + %% Remove any remaining sasl_erl_crash.dump + %% These can occur when a new master@<host> is started, before + %% the old usage of the name is unregistered, causing the node to + %% terminate. (This has no effect on the test case, as the node is + %% immediately restarted by heart and the test cases wait until + %% the node is actually up and running -- see wait_nodes_up/2) + file:delete("sasl_erl_crash.dump"), + + try apply(?MODULE,Case,[cleanup,Config]) + catch error:undef -> ok + end, + ok. + +gg_node_snames(Config) -> + [tc_sname(Config,X) || X <- [gg1,gg2,gg3,gg4,gg5,gg6]]. + + +%%%----------------------------------------------------------------- +%%% TEST CASES + + +%% Executed instead of release group when no run_erl program exists +no_run_erl(Config) when is_list(Config) -> + {comment, "No run_erl program"}. + + + +%% Test upgrade and downgrade of erts +upgrade(Conf) when is_list(Conf) -> + reg_print_proc(), %% starts a printer process on test_server node + ?t:format("upgrade ~p~n",[reg_print_proc]), + PrivDir = priv_dir(Conf), + Sname = tc_sname(Conf), % nodename for use in this testcase + + %% Copy the P1G release to a directory for use in this testcase + ok = copy_installed(Conf,p1g_install,[Sname]), + + %% start the test node + [TestNode] = start_nodes(Conf,[Sname],"upgrade start"), + + %% unpack and install P1H + ok = rpc_inst(TestNode, install_1, [PrivDir]), + stop_cover(TestNode), + reboot_and_wait(TestNode,"install_1"), + + %% reinstall P1H and make it permanent + ok = rpc_inst(TestNode, install_2, []), + stop_cover(TestNode), + reboot_and_wait(TestNode,"install_2",[a]), + + %% check that P1H is permanent, unpack and install P1I, unpack and install P2A + TestNodeInit1 = rpc:call(TestNode,erlang,whereis,[init]), + ok = rpc_inst(TestNode, install_3, [PrivDir]), + stop_cover(TestNode), + ok = rpc_inst(TestNode, install_3a, []), + wait_nodes_up([{TestNode,TestNodeInit1}],"install_3",[a]), + + %% check that P2A is used, reboot from P1I + ok = rpc_inst(TestNode, install_4, []), + stop_cover(TestNode), + reboot_and_wait(TestNode,"install_4",[a]), + + %% check that P1I, reinstall P2A + TestNodeInit2 = rpc:call(TestNode,erlang,whereis,[init]), + ok = rpc_inst(TestNode, install_5, []), + stop_cover(TestNode), + ok = rpc_inst(TestNode, install_5a, []), + wait_nodes_up([{TestNode,TestNodeInit2}],"install_5",[a]), + + %% check that P2A is used, make P2A permanent + ok = rpc_inst(TestNode, install_6, []), + stop_cover(TestNode), + reboot_and_wait(TestNode,"install_6",[a]), + + %% check that P2A is permanent, install old P1H + TestNodeInit3 = rpc:call(TestNode,erlang,whereis,[init]), + stop_cover(TestNode), + ok = rpc_inst(TestNode, install_7, []), + wait_nodes_up([{TestNode,TestNodeInit3}],"install_7",[a]), + + %% check that P1H is permanent, remove P1I and P2A + ok = rpc_inst(TestNode, install_8, []), + stop_cover(TestNode), + reboot_and_wait(TestNode,"install_8",[a]), + + %% check that P1H is permanent, reboot old P1G + TestNodeInit4 = rpc:call(TestNode,erlang,whereis,[init]), + stop_cover(TestNode), + ok = rpc_inst(TestNode, install_9, []), + wait_nodes_up([{TestNode,TestNodeInit4}],"install_9"), + + %% check that P1G is permanent, remove P1H + ok = rpc_inst(TestNode, install_10, []), + stop_cover(TestNode), + reboot_and_wait(TestNode,"install_10"), + + %% check that P1G is permanent + ok = rpc_inst(TestNode, install_11, []), + + ok. + +upgrade(cleanup,Config) -> + TestNode = tc_full_node_name(Config), + ok = stop_nodes([TestNode]). + +reboot_and_wait(Node,Tag) -> + reboot_and_wait(Node,Tag,[]). + +reboot_and_wait(Node,Tag,Apps) -> + InitPid = rpc:call(Node,erlang,whereis,[init]), + ok = rpc:call(Node,init,reboot,[]), + wait_nodes_up([{Node,InitPid}],Tag,Apps). + + +%% Test upgrade and downgrade of erts, diskless +client1(Conf) when is_list(Conf) -> + reg_print_proc(), %% starts a printer process on test_server node + PrivDir = priv_dir(Conf), + Master = tc_sname(Conf,master), + Client = tc_sname(Conf,client), + MasterDir = filename:join(PrivDir,Master), + + %% Copy the P1G release to a directory for use in this testcase + ok = copy_installed(Conf,p1g_install,[Master]), + ok = copy_client(Conf,Master,Client,"start_cli1"), + + %% start the master node + [TestNode] = start_nodes(Conf,[Master],"client1"), + + ok = rpc_inst(TestNode, client1_1, [PrivDir,MasterDir,Client]), + + ok. + +client1(cleanup,Config) -> + MasterNode = tc_full_node_name(Config,master), + ClientNode = tc_full_node_name(Config,client), + ok = stop_nodes([MasterNode,ClientNode]). + + + +%% Test diskless release handling when illegal master node +client2(Conf) when is_list(Conf) -> + reg_print_proc(), %% starts a printer process on test_server node + PrivDir = priv_dir(Conf), + Master = tc_sname(Conf,master), + Client = tc_sname(Conf,client), + + %% Copy the P1G release to a directory for use in this testcase + ok = copy_installed(Conf,p1g_install,[Master]), + ok = copy_client(Conf,Master,Client,"start_cli2"), + + %% start the master node + [TestNode] = start_nodes(Conf,[Master],"client2"), + + ok = rpc_inst(TestNode, client2, [PrivDir,Client]), + + ok. + +client2(cleanup,Config) -> + MasterNode = tc_full_node_name(Config,master), + ClientNode = tc_full_node_name(Config,client), + ok = stop_nodes([MasterNode,ClientNode]). + + + +%% Test instructions _not_ tested by the installer module. +instructions(Conf) when is_list(Conf) -> + DataDir = ?config(data_dir, Conf), + + Dir = filename:join(DataDir, "c"), + true = code:add_path(Dir), + check_bstate("no", []), + ok = application:start(c), + ok = wait_for(bb), + check_bstate("first", []), + FirstBB = whereis(bb), + + case whereis(cc) of + Pid when is_pid(Pid) -> ok; + _ -> ?t:fail("cc not started") + end, + + %% Stop and start cc process + S1 = [point_of_no_return, + {stop, [aa]}, + {apply, {?MODULE, no_cc, []}}, + {start, [aa]}], + {ok, _} = release_handler_1:eval_script(S1, [], []), + + case whereis(cc) of + Pid2 when is_pid(Pid2) -> ok; + _ -> ?t:fail("cc not started") + end, + + %% Make bb run old version of b. + S2 = [point_of_no_return, + {remove, {b, soft_purge, soft_purge}}], + {ok, [{b, soft_purge}]} = release_handler_1:eval_script(S2, [], []), + check_bstate("first", [FirstBB]), + + false = code:is_loaded(b), + {error,{old_processes,b}} = release_handler_1:eval_script(S2,[],[]), + check_bstate("first", [FirstBB]), + + %% Let supervisor restart bb with new code + S3 = [point_of_no_return, + {purge, [b]}], + {ok, []} = release_handler_1:eval_script(S3, [], []), + ok = wait_for(bb), + check_bstate("second", []), + SecondBB = whereis(bb), + + if + SecondBB =:= FirstBB -> ?t:fail("bb not killed"); + true -> ok + end, + + %% Restart bb yet another time + ok = application:stop(c), + ok = application:start(c), + ok = wait_for(bb), + check_bstate("third", []), + ThirdBB = whereis(bb), + + case ThirdBB of + _ when is_pid(ThirdBB) -> ok; + undefined -> ?t:fail("bb not started") + end, + + %% Make bb run old version of b. + %%c:l(b), + check_bstate("third", []), + false = code:purge(b), + check_bstate("third", []), + {module,b} = code:load_file(b), + check_bstate("third", [ThirdBB]), + + %% Let supervisor restart bb yet another time + S4 = [point_of_no_return, + {remove, {b, brutal_purge, soft_purge}}], + {ok, HopefullyEmpty} = release_handler_1:eval_script(S4, [], []), + ok = wait_for(bb), + FourthBB = whereis(bb), + + case HopefullyEmpty of + [{b, soft_purge}] -> + %% The process managed to start between purge and delete + check_bstate("fourth", [FourthBB]); + [] -> + %% The process started after delete + check_bstate("fourth", []) + end, + + application:stop(c), + check_bstate("no", []), + ok. + +instructions(cleanup,Conf) -> + application:stop(c), + really_del_code([aa,b,c_sup]), + code:del_path(filename:join(?config(data_dir,Conf), "c")), + ok. + +really_del_code(Mods) -> + lists:foreach(fun(Mod) -> + code:purge(Mod), % remove old code + code:delete(Mod),% make current code old + code:purge(Mod) % remove old code + end, + Mods). + +check_bstate(Slogan,ExpectedProcs) -> + BB = whereis(bb), + ActualProcs = lists:sort([P || P <- processes(), + erlang:check_process_code(P, b)]), + ExpectedProcs2 = lists:sort(ExpectedProcs), + ?t:format("check_bstate:~n~p~n~p~n", + [{"bb process", Slogan, BB}, + {"Processes running old b code", ActualProcs}]), + if + Slogan =:= "no", BB =/= undefined -> + ?t:fail("instructions failed; process bb is running"); + Slogan =/= "no", BB =:= undefined -> + ?t:fail("instructions failed; process bb is not running"); + ExpectedProcs2 =:= [], ActualProcs =/= ExpectedProcs2 -> + ?t:fail("instructions failed; old b processes are running"); + ActualProcs =/= ExpectedProcs2 -> + ?t:fail("instructions failed; wrong number of old b processes are running"); + true -> + ok + end. + +wait_for(Name) -> + case whereis(Name) of + undefined -> + timer:sleep(100), + wait_for(Name); + Pid when is_pid(Pid) -> + ok + end. + +no_cc() -> + case whereis(cc) of + Pid when is_pid(Pid) -> ?t:fail("cc not stopped"); + _ -> ok + end. + + + +%%%----------------------------------------------------------------- +%%% Testing of reported bugs and other tickets. +%%%----------------------------------------------------------------- + +%%----------------------------------------------------------------- +%% Ticket: OTP-2740 +%% Slogan: vsn not numeric doesn't work so good in release_handling +%%----------------------------------------------------------------- +%% Test vsn. +otp_2740(Conf) -> + DataDir = ?config(data_dir, Conf), + Dir = filename:join(DataDir, "otp_2740"), + true = code:add_path(Dir), + + {module, vsn_numeric} = c:l(vsn_numeric), + {module, vsn_tuple} = c:l(vsn_tuple), + {module, vsn_list} = c:l(vsn_list), + {module, vsn_atom} = c:l(vsn_atom), + {module, vsn_string} = c:l(vsn_string), + + 231894 = release_handler_1:get_current_vsn(vsn_numeric), + {tuple,["of",terms]} = release_handler_1:get_current_vsn(vsn_tuple), + [list,"of",{some,terms}] = release_handler_1:get_current_vsn(vsn_list), + atom = release_handler_1:get_current_vsn(vsn_atom), + "a string" = release_handler_1:get_current_vsn(vsn_string), + + true = code:del_path(Dir), + ok. + +%%----------------------------------------------------------------- +%% Ticket: OTP-2760 +%% Slogan: when an application is removed from a node it is not unloaded +%%----------------------------------------------------------------- +%% Test that when an application is removed from a node it is also unloaded. +otp_2760(Conf) -> + PrivDir = priv_dir(Conf), + Dir = filename:join(PrivDir,"otp_2760"), + DataDir = ?config(data_dir,Conf), + LibDir = filename:join([DataDir,app1_app2,lib1]), + + Rel1 = create_and_install_fake_first_release(Dir,[{app1,"1.0",LibDir}]), + Rel2 = create_fake_upgrade_release(Dir,"after",[],{Rel1,Rel1,[LibDir]}), + Rel2Dir = filename:dirname(Rel2), + + %% Start a node with Rel1.boot and check that the app1 module is loaded + {ok, Node} = t_start_node(otp_2760, Rel1, []), + {file, _} = rpc:call(Node, code, is_loaded, [app1]), + + %% Execute the relup script and check that app1 is unloaded + {ok, [{"after", [{_Rel1Vsn, _Descr, Script}], _}]} = + file:consult(filename:join(Rel2Dir, "relup")), + {ok, []} = rpc:call(Node, release_handler_1, eval_script, + [Script, [], []]), + false = rpc:call(Node, code, is_loaded, [app1]), + + true = stop_node(Node), + ok. + +%% Test upgrade using other filesystem than the defined in OTP and +%% option {update_paths, true} +otp_5761(Conf) when is_list(Conf) -> + + %% In the following test case, the release upgrade is somewhat + %% simplified (since it is not this procedure in itself we want to + %% test, but that application code directories are set correctly.) + %% Existing Erlang release is used as base, instead of creating + %% a new one. + + %% Set some paths + PrivDir = priv_dir(Conf), + Dir = filename:join(PrivDir,"otp_5761"), + RelDir = filename:join(?config(data_dir, Conf), "app1_app2"), + LibDir1 = filename:join(RelDir, "lib1"), + LibDir2 = filename:join(RelDir, "lib2"), + + %% Create the releases + Rel1 = create_and_install_fake_first_release(Dir, + [{app1,"1.0",LibDir1}, + {app2,"1.0",LibDir1}]), + Rel2 = create_fake_upgrade_release(Dir, + "2", + [{app1,"2.0",LibDir2}, + {app2,"1.0",LibDir2}], + {Rel1,Rel1,[LibDir1]}), + Rel1Dir = filename:dirname(Rel1), + Rel2Dir = filename:dirname(Rel2), + + %% Start a slave node + {ok, Node} = t_start_node(otp_5761, Rel1, filename:join(Rel1Dir,"sys.config")), + + %% Bind some variable names that will be used in patternmatching below + App11Dir = filename:join([LibDir1, "app1-1.0"]), + App12Dir = filename:join([LibDir2, "app1-2.0"]), + App2aDir = filename:join([LibDir1, "app2-1.0"]), + App2bDir = filename:join([LibDir2, "app2-1.0"]), + + %% Make sure correct code paths are used + App11Dir = rpc:call(Node, code, lib_dir, [app1]), + App2aDir = rpc:call(Node, code, lib_dir, [app2]), + + %% Unpack rel2 (make sure it does not work if an AppDir is bad) + LibDir3 = filename:join(RelDir, "lib3"), + {error, {no_such_directory, _}} = + rpc:call(Node, release_handler, set_unpacked, + [Rel2++".rel", [{app1,"2.0",LibDir2}, {app2,"1.0",LibDir3}]]), + {ok, RelVsn2} = + rpc:call(Node, release_handler, set_unpacked, + [Rel2++".rel", [{app1,"2.0",LibDir2}, {app2,"1.0",LibDir2}]]), + ok = rpc:call(Node, release_handler, install_file, + [RelVsn2, filename:join(Rel2Dir, "relup")]), + ok = rpc:call(Node, release_handler, install_file, + [RelVsn2, filename:join(Rel2Dir, "start.boot")]), + ok = rpc:call(Node, release_handler, install_file, + [RelVsn2, filename:join(Rel2Dir, "sys.config")]), + + %% Install RelVsn2 without {update_paths, true} option + {ok, RelVsn1, []} = + rpc:call(Node, release_handler, install_release, [RelVsn2]), + App12Dir = rpc:call(Node, code, lib_dir, [app1]), + App2aDir = rpc:call(Node, code, lib_dir, [app2]), + + %% Install RelVsn1 again + {ok, RelVsn1, []} = + rpc:call(Node, release_handler, install_release, [RelVsn1]), + + %% Install RelVsn2 with {update_paths, true} option + {ok, RelVsn1, []} = + rpc:call(Node, release_handler, install_release, + [RelVsn2, [{update_paths, true}]]), + App12Dir = rpc:call(Node, code, lib_dir, [app1]), + App2bDir = rpc:call(Node, code, lib_dir, [app2]), + + %% Install RelVsn1 again + {ok, RelVsn1, []} = + rpc:call(Node, release_handler, install_release, + [RelVsn1, [{update_paths, true}]]), + App11Dir = rpc:call(Node, code, lib_dir, [app1]), + App2aDir = rpc:call(Node, code, lib_dir, [app2]), + + %% Stop the slave node + true = stop_node(Node), + ok. + +%% Test upgrade and downgrade of applications +eval_appup(Conf) when is_list(Conf) -> + + %% OTP-6162 + %% Create an ETS table which is updated by app1 if there is any + %% change made to the application configuration parameter 'var' + %% (see config_change/3 in myrel/lib1|2/app1-1|2.0/src/app1.erl) + ets:new(otp_6162, [set, public, named_table]), + + %% Set some paths + RelDir = filename:join(?config(data_dir, Conf), "app1_app2"), + App11Dir = filename:join([RelDir, "lib1", "app1-1.0"]), + App12Dir = filename:join([RelDir, "lib2", "app1-2.0"]), + EbinDir = filename:join(App11Dir, "ebin"), + + %% Start app1-1.0 + code:add_patha(EbinDir), + ok = application:start(app1), + App11Dir = code:lib_dir(app1), + ok = gen_server:call(harry, error), + + %% Upgrade to app1-2.0 + {ok, []} = release_handler:upgrade_app(app1, App12Dir), + App12Dir = code:lib_dir(app1), + error = gen_server:call(harry, error), + + %% OTP-6162 + %% Value of config parameter 'var' should now be 'val2' + %% (see myrel/lib2/app1-2.0/ebin/app1.app) + [{var,val2}] = ets:lookup(otp_6162, var), + + %% Downgrade to app1-1.0 + {ok, []} = release_handler:downgrade_app(app1,"1.0",App11Dir), + App11Dir = code:lib_dir(app1), + ok = gen_server:call(harry, error), + + %% OTP-6162 + %% Value of config parameter 'var' should now be 'val1' + %% (see myrel/lib1/app1-1.0/ebin/app1.app) + [{var,val1}] = ets:lookup(otp_6162, var), + + ok = application:stop(app1), + ok = application:unload(app1), + + true = code:del_path(EbinDir), + ok. + + +%% Test the example/target_system.erl module +target_system(Conf) when is_list(Conf) -> + PrivDir = priv_dir(Conf), + DataDir = ?config(data_dir,Conf), + + TargetCreateDir = filename:join([PrivDir,"target_system","create"]), + TargetInstallDir = filename:join([PrivDir,"target_system","install"]), + + ok = filelib:ensure_dir(filename:join(TargetCreateDir,"xx")), + ok = filelib:ensure_dir(filename:join(TargetInstallDir,"xx")), + + + %% Create the .rel file + ErtsVsn = erlang:system_info(version), + RelName = filename:join(TargetCreateDir,"ts-1.0"), + RelFile = RelName++".rel", + RelVsn = "R1A", + create_rel_file(RelFile,RelName,RelVsn,ErtsVsn,[{a, "1.0"}]), + + %% Build the target_system module + ExamplesEbin = filename:join([code:lib_dir(sasl),examples,ebin]), + TSPath = + case filelib:is_file(filename:join(ExamplesEbin,"target_system.beam")) of + true -> + ExamplesEbin; + false -> + {ok,_} = + compile:file(filename:join(DataDir,"target_system.erl"), + [{outdir,TargetCreateDir}]), + TargetCreateDir + end, + code:add_path(TSPath), + + %% Create the release + target_system:create(RelName,[{path,[filename:join([DataDir, + lib, + "a-1.0", + ebin])]}]), + + %% Install the release + target_system:install(RelName,TargetInstallDir), + + code:del_path(TSPath), + + %% Check that all files exist in installation + true = filelib:is_dir(filename:join(TargetInstallDir,"erts-"++ErtsVsn)), + LibDir = filename:join(TargetInstallDir,lib), + {ok,KernelVsn} = application:get_key(kernel,vsn), + {ok,StdlibVsn} = application:get_key(stdlib,vsn), + {ok,SaslVsn} = application:get_key(sasl,vsn), + true = filelib:is_dir(filename:join(LibDir,"kernel-"++KernelVsn)), + true = filelib:is_dir(filename:join(LibDir,"stdlib-"++StdlibVsn)), + true = filelib:is_dir(filename:join(LibDir,"sasl-"++SaslVsn)), + true = filelib:is_dir(filename:join(LibDir,"a-1.0")), + RelDir = filename:join(TargetInstallDir,releases), + true = filelib:is_regular(filename:join(RelDir,"RELEASES")), + true = filelib:is_regular(filename:join(RelDir,"start_erl.data")), + true = filelib:is_regular(filename:join(RelDir, + filename:basename(RelFile))), + true = filelib:is_dir(filename:join(RelDir,RelVsn)), + true = filelib:is_regular(filename:join([RelDir,RelVsn,"start.boot"])), + BinDir = filename:join(TargetInstallDir,bin), + true = filelib:is_regular(filename:join(BinDir,"start.boot")), + true = filelib:is_regular(filename:join(BinDir,erl)), + true = filelib:is_regular(filename:join(BinDir,start_erl)), + true = filelib:is_regular(filename:join(BinDir,start)), + true = filelib:is_regular(filename:join(BinDir,epmd)), + true = filelib:is_regular(filename:join(BinDir,run_erl)), + true = filelib:is_regular(filename:join(BinDir,to_erl)), + + %% Check content of files + {ok,SED} = file:read_file(filename:join(RelDir,"start_erl.data")), + [ErtsVsn,RelVsn] = string:tokens(binary_to_list(SED),"\s\n"), + ok. + + + +%%%================================================================= +%%% Testing global groups. +%%%================================================================= + +%% This test case involves P1G and P1H with the sys.config as +%% specified in gg_config/1. The test case checks that the global +%% group information is correct before and after the upgrade and also +%% after terminating one of the nodes. The flow is as follows: +%% 1. Start all four nodes of global group gg1 with P1G +%% 2. Terminate one of the nodes, and upgrade the others to P1H. P1H +%% config adds to more nodes to the global group. +%% 3. Start the two remaining nodes with P1H +upgrade_gg(Conf) -> + [Gg1Sname,Gg2Sname,Gg3Sname,Gg4Sname,Gg5Sname,Gg6Sname] = + ?config(snames,Conf), + + %% start gg1, gg3, gg4, gg5 and check that global group info is ok + Nodes1 = [Gg1,Gg3,Gg4,Gg5] = + start_nodes(Conf,[Gg1Sname,Gg3Sname,Gg4Sname,Gg5Sname],"upgrade_gg"), + + %% Give some time to synch nodes, then check global group info. + timer:sleep(1000), + [check_gg_info(Node,Nodes1,[],Nodes1--[Node]) || Node <- Nodes1], + + %% register a process on each of the nodes + ok = rpc:call(Gg1, installer, reg_proc, [reg1]), + ok = rpc:call(Gg3, installer, reg_proc, [reg3]), + ok = rpc:call(Gg4, installer, reg_proc, [reg4]), + ok = rpc:call(Gg5, installer, reg_proc, [reg5]), + are_names_reg_gg(Gg1, [reg1, reg3, reg4, reg5]), + + %% Stop gg3, then upgrade gg1, gg4 and gg5 to P1H + ok = stop_nodes([Gg3]), + + ok = install_release_changed_gg(Gg1,"P1H"), + ok = install_release_changed_gg(Gg4,"P1H"), + ok = install_release_changed_gg(Gg5,"P1H"), + + %% Check global group info + Gg2 = node_name(Gg2Sname), + Gg6 = node_name(Gg6Sname), + Nodes2 = [Gg1,Gg4,Gg5], + [check_gg_info(Node,Nodes2,[Gg2,Gg6],Nodes2--[Node]) || Node <- Nodes2], + + %% start gg2 and gg6 + [Gg2,Gg6] = start_nodes(Conf,[Gg2Sname,Gg6Sname],"upgrade_gg start gg2/gg6"), + + %% reg proc on each of the nodes + ok = rpc:call(Gg2, installer, reg_proc, [reg2]), + ok = rpc:call(Gg6, installer, reg_proc, [reg6]), + are_names_reg_gg(Gg1, [reg1, reg2, reg4, reg5, reg6]), + + %% Check global group info + Nodes3 = [Gg1,Gg2,Gg4,Gg5,Gg6], + [check_gg_info(Node,Nodes3,[],Nodes3--[Node]) || Node <- Nodes3], + + ok. + +upgrade_gg(cleanup,Config) -> + Snames = ?config(snames,Config), + NodeNames = [node_name(Sname) || Sname <- Snames], + ok = stop_nodes(NodeNames). + + + + +%%%================================================================= +%%% Misceleaneous functions +%%%================================================================= +stop_nodes(Nodes) -> + ?t:format("Stopping nodes: ~p~n",[Nodes]), + Running = + lists:foldl(fun(Node,Acc) -> + Now = now(), + stop_cover(Node), + case rpc:call(Node,installer,stop,[Now]) of + {badrpc,nodedown} -> + Acc; + Other -> + ?t:format("Stop ~p(~p): ~p~n", + [Node,Now,Other]), + [Node|Acc] + end + end, [], Nodes), + wait_nodes_down(Running). + + +wait_nodes_down(Nodes) -> + ?t:format( "wait_nodes_down ~p:",[Nodes]), + wait_nodes_down(Nodes, 30). + +wait_nodes_down(Nodes, 0) -> + test_server:fail({error, {"could not kill nodes", Nodes}}); +wait_nodes_down(Nodes, N) -> + Fun = fun(Node, A) -> + case net_adm:ping(Node) of + pong -> + ?t:format( " net_adm:ping(~p) = pong", [Node]), + [Node|A]; + pang -> + ?t:format( " net_adm:ping(~p) = pang", [Node]), + A + end + end, + Pang = lists:foldl(Fun, [], Nodes), + case Pang of + [] -> + ?t:format("",[]), + ok; + _ -> + timer:sleep(1000), + wait_nodes_down(Pang, N-1) + end. + + + +wait_nodes_up(Nodes, Tag) -> + wait_nodes_up(Nodes, Tag, []). + +wait_nodes_up(Nodes0, Tag, Apps) -> + ?t:format("wait_nodes_up(~p, ~p, ~p):",[Nodes0, Tag, Apps]), + Nodes = fix_nodes(Nodes0), + wait_nodes_up(Nodes, Tag, lists:umerge(Apps,[kernel,stdlib,sasl]), 30). + +fix_nodes([{Node,InitPid}|Nodes]) -> + [{Node,InitPid} | fix_nodes(Nodes)]; +fix_nodes([Node|Nodes]) -> + [{Node,fake_init_pid} | fix_nodes(Nodes)]; +fix_nodes([]) -> + []. + +wait_nodes_up(Nodes, Tag, Apps, 0) -> + test_server:fail({error, {"nodes not started", Nodes, Tag, Apps}}); +wait_nodes_up(Nodes, Tag, Apps, N) -> + Fun = + fun(NodeInfo={Node,OldInitPid}, A) -> + case rpc:call(Node, application, which_applications, []) of + {badrpc, nodedown} -> + ?t:format( " ~p = {badarg, nodedown}",[Node]), + [NodeInfo | A]; + List when is_list(List)-> + ?t:format( " ~p = [~p]",[Node, List]), + case lists:all(fun(App) -> + lists:keymember(App,1,List) + end, Apps) of + true -> + case rpc:call(Node,erlang,whereis,[init]) of + OldInitPid -> + [NodeInfo | A]; + _ -> + start_cover(Node), + A + end; + false -> + [NodeInfo | A] + end + end + end, + Pang = lists:foldl(Fun,[],Nodes), + case Pang of + [] -> + ?t:format("",[]), + ok; + _ -> + timer:sleep(1000), + wait_nodes_up(Pang, Tag, Apps, N-1) + end. + + + + +are_names_reg_gg(Node, Names) -> + ?t:format( "are_names_reg_gg ~p~n",[Names]), + are_names_reg_gg(Node, Names, 30). + +are_names_reg_gg(Node, Names, N) -> + case lists:sort(rpc:call(Node, global, registered_names, [])) of + Names -> + ok; + Regs when N > 0 -> + timer:sleep(1000), + ?t:format( "are_names_reg_gg Regs ~p~n",[Regs]), + are_names_reg_gg(Node, Names, N-1); + Regs -> + ?t:fail({error, {"Names not registered", + {{"should :", Names}, + {"was :", Regs}}}}) + end. + + + +t_start_node(Name, Boot, SysConfig) -> + Args = + case Boot of + [] -> []; + _ -> " -boot " ++ Boot + end ++ + case SysConfig of + [] -> []; + _ -> " -config " ++ SysConfig + end, + test_server:start_node(Name, slave, [{args, Args}]). + +stop_node(Node) -> + ?t:stop_node(Node). + + +copy_client(Conf,Master,Sname,StartScript) -> + io:format("copy_client(Conf)"), + + DataDir = ?config(data_dir, Conf), + MasterDir = filename:join(priv_dir(Conf),Master), + + {ok,Host} = inet:gethostname(), + {ok,IpTuple} = inet:getaddr(Host,inet), + IpAddr = inet_parse:ntoa(IpTuple), + + CliNode = node_name(Sname), + + Cli = filename:join([MasterDir, "clients", "type1", CliNode]), + ok = filelib:ensure_dir(filename:join([Cli,"bin","."])), + ok = filelib:ensure_dir(filename:join([Cli,"releases","."])), + ok = filelib:ensure_dir(filename:join([Cli,"log","."])), + + P1GOrig = filename:join([MasterDir, "releases", "P1G"]), + ok = copy_tree(Conf,P1GOrig,filename:join(Cli,"releases")), + + ok = subst_file(filename:join([DataDir, "clients", StartScript]), + filename:join([Cli,"bin","start"]), + [{"ROOT",MasterDir}, + {"MASTER",atom_to_list(Master)}, + {"IPADDR",IpAddr}], + [{chmod,8#0755}]), + + StartErlData = filename:join([MasterDir, "releases", "start_erl.data"]), + CliRelDir = filename:join([Cli, "releases"]), + copy_file(StartErlData, CliRelDir), + + RR = filename:join([MasterDir, "releases", "RELEASES"]), + copy_file(RR, CliRelDir), + + ok. + + +delete_release(Conf) -> + PrivDir = priv_dir(Conf), + + {ok, OrigWd} = file:get_cwd(), + + ok = file:set_cwd(PrivDir), + ?t:format("======== current dir ~p~n",[PrivDir]), + {ok, Dirs} = file:list_dir(PrivDir), + ?t:format("======== deleting ~p~n",[Dirs]), + + ok = delete_release_os(Dirs), + ?t:format("======== remaining ~p~n",[file:list_dir(PrivDir)]), + ok = file:set_cwd(OrigWd), + ok. + + +delete_release_os(Dirs) -> + case os:type() of + {unix, _} -> + delete_release_unix(Dirs); + {win32, _} -> + delete_release_win32(Dirs); + Os -> + test_server:fail({error, {not_yet_implemented_os, Os}}) + end. + + +delete_release_unix([]) -> + ok; +delete_release_unix(["save"|Dirs]) -> + delete_release_unix(Dirs); +delete_release_unix([Dir|Dirs]) -> + Rm = string:concat("rm -rf ", Dir), + ?t:format("============== COMMAND ~p~n",[Rm]), + case file:list_dir(Dir) of + {error, enotdir} -> + ok; + X -> + ?t:format("------- Dir ~p~n ~p~n",[Dir, X]) + end, + case os:cmd(Rm) of + [] -> + ?t:format("------- Result of COMMAND ~p~n",[ok]); + Y -> + ?t:format("!!!!!!! delete ERROR Dir ~p Error ~p~n",[Dir, Y]), + ?t:format("------- ls -al ~p~n",[os:cmd("ls -al " ++ Dir)]) + end, + + delete_release_unix(Dirs). + +delete_release_win32([]) -> + ok; +delete_release_win32(["save"|Dirs]) -> + delete_release_win32(Dirs); +delete_release_win32([Dir|Dirs]) -> + Rm = string:concat("rmdir /s ", Dir), + [] = os:cmd(Rm), + delete_release_win32(Dirs). + + +node_name(Sname) when is_atom(Sname) -> + {ok,Host} = inet:gethostname(), + list_to_atom(atom_to_list(Sname) ++ "@" ++ Host). + +copy_file(Src, Dest) -> + copy_file(Src, Dest, []). +copy_file(Src, Dest, Opts) -> + case file:copy(Src,Dest) of + {ok,_} -> + preserve(Src,Dest,Opts), + chmod(Dest,Opts), + ok; + {error,eisdir} -> + NewDest = filename:join(Dest, filename:basename(Src)), + case file:copy(Src,NewDest) of + {ok,_} -> + preserve(Src,NewDest,Opts), + chmod(NewDest,Opts); + {error,Reason} -> + copy_error(Src,Dest,Reason) + end; + {error,Reason} -> + copy_error(Src,Dest,Reason) + end. + +preserve(Src,Dest,Opts) -> + case lists:member(preserve, Opts) of + true -> + {ok, FileInfo} = file:read_file_info(Src), + ok = file:write_file_info(Dest, FileInfo); + false -> + ok + end. + +chmod(Dest,Opts) -> + case lists:keyfind(chmod,1,Opts) of + {chmod,Mode} -> + ok = file:change_mode(Dest, Mode); + false -> + ok + end. + + + +copy_error(Src, Dest, Reason) -> + io:format("Copy ~s to ~s failed: ~s\n", + [Src,Dest,file:format_error(Reason)]), + ?t:fail(file_copy_failed). + +copy_tree(Conf, Src, DestDir) -> + case catch copy_tree(Conf, Src, filename:basename(Src), DestDir) of + ok -> + ok; + {'EXIT', {{badmatch,Error},_Stack}} -> + %% Most probably, an erl_tar call has failed. + %% Known to happen on some platforms (symbolic_link_too_long) + Error; + {'EXIT', Reason} -> + {error, Reason} + end. + +copy_tree(Conf, Src, NewName, DestDir) -> + PrivDir = priv_dir(Conf), + TempTarName = filename:join(PrivDir, "temp_tar_file.tar"), + %% Not compressing tar file here since that would increase test + %% suite time by almost 100%, and the tar file is deleted + %% imediately anyway. + {ok,Tar} = erl_tar:open(TempTarName, [write]), + ok = erl_tar:add(Tar, Src, NewName, []), + ok = erl_tar:close(Tar), + ok = erl_tar:extract(TempTarName, [{cwd,DestDir}]), + ok = file:delete(TempTarName), + ok. + +%% subst_file(Src, Dest, Vars) +%% Src = Dest = string(), filename and path +%% Vars = [{Var, Val}] +%% Var = Val = string() +%% Substitute all occurrences of %Var% for Val in Src, using the list +%% of variables in Vars. Result is written to Dest. +%% +subst_file(Src, Dest, Vars) -> + subst_file(Src, Dest, Vars, []). +subst_file(Src, Dest, Vars, Opts) -> + {ok, Bin} = file:read_file(Src), + Conts = binary_to_list(Bin), + NConts = subst(Conts, Vars), + ok = file:write_file(Dest, NConts), + preserve(Src,Dest,Opts), + chmod(Dest,Opts). + +subst(Str, Vars) -> + subst(Str, Vars, []). + +subst([$%, C| Rest], Vars, Result) when $A =< C, C =< $Z -> + subst_var([C| Rest], Vars, Result, []); +subst([$%, C| Rest], Vars, Result) when $a =< C, C =< $z -> + subst_var([C| Rest], Vars, Result, []); +subst([$%, C| Rest], Vars, Result) when C == $_ -> + subst_var([C| Rest], Vars, Result, []); +subst([C| Rest], Vars, Result) -> + subst(Rest, Vars, [C| Result]); +subst([], _Vars, Result) -> + lists:reverse(Result). + +subst_var([$%| Rest], Vars, Result, VarAcc) -> + Key = lists:reverse(VarAcc), + case lists:keysearch(Key, 1, Vars) of + {value, {Key, Value}} -> + subst(Rest, Vars, lists:reverse(Value, Result)); + false -> + subst(Rest, Vars, [$%| VarAcc ++ [$%| Result]]) + end; +subst_var([C| Rest], Vars, Result, VarAcc) -> + subst_var(Rest, Vars, Result, [C| VarAcc]); +subst_var([], Vars, Result, VarAcc) -> + subst([], Vars, [VarAcc ++ [$%| Result]]). + + +priv_dir(Conf) -> + filename:absname(?config(priv_dir, Conf)). % Get rid of trailing slash + +latest_version(Dir) -> + List = filelib:wildcard(Dir ++ "*"), + lists:last(lists:sort(List)). + +%% A printer process which receives messages from other nodes and +%% prints in the log +reg_print_proc() -> + catch unregister(rh_print), + Pid = spawn_link(?MODULE, rh_print, []), + register(rh_print, Pid), + ok. + +rh_print() -> + receive + {print, {Module,Line}, [H|T]} -> + ?t:format("=== ~p:~p - ~p",[Module,Line,H]), + lists:foreach(fun(Term) -> ?t:format(" ~p",[Term]) end, T), + ?t:format("",[]), + rh_print(); + kill -> + exit(normal) + end. + +stop_print_proc() -> + case whereis(rh_print) of %%removes the printer process + undefined -> + ok; + Pid when is_pid(Pid) -> + rh_print ! kill + end. + +%% Create the first target release, vsn P1G. This release is used for +%% all test cases in {group,release} +create_p1g(Conf,Sname) -> + do_create_p1g(Conf,filename:join(priv_dir(Conf),Sname)). + +do_create_p1g(Conf,TargetDir) -> + PrivDir = priv_dir(Conf), + DataDir = ?config(data_dir,Conf), + ErtsVsn = "4.4", + ErtsDir = "erts-"++ErtsVsn, + + %% Create dirs + BinDir = filename:join(TargetDir,bin), + ReleasesDir = filename:join(TargetDir,releases), + LogDir = filename:join(TargetDir,log), + ok = filelib:ensure_dir(filename:join(BinDir,"*")), + ok = filelib:ensure_dir(filename:join(ReleasesDir,"*")), + ok = filelib:ensure_dir(filename:join(LogDir,"*")), + + %% Copy stuff + ErtsLatest = latest_version(filename:join(code:root_dir(),"erts")), + ok = copy_tree(Conf, ErtsLatest, ErtsDir, TargetDir), + ErtsBinDir = filename:join([TargetDir,ErtsDir,bin]), + copy_file(filename:join([ErtsBinDir, "epmd"]), BinDir, [preserve]), + copy_file(filename:join([ErtsBinDir, "run_erl"]), BinDir, [preserve]), + copy_file(filename:join([ErtsBinDir, "to_erl"]), BinDir, [preserve]), + + copy_file(filename:join(DataDir, "../installer.beam"), + filename:join([DataDir,lib,"installer-1.0",ebin])), + + %% Create .rel, .script and .boot files + RelName = "rel0", + RelVsn = "P1G", + RelDir = filename:join(PrivDir,RelName), + RelFileName = filename:join(RelDir,RelName), + RelFile = RelFileName ++ ".rel", + ok = filelib:ensure_dir(RelFile), + LibPath = filename:join([DataDir,lib,"*",ebin]), + + TarFile = create_basic_release(RelFile, RelVsn, {ErtsVsn,false}, + LibPath, [], [], [], []), + + %% Extract tar file in target directory (i.e. same directory as erts etc.) + ok = erl_tar:extract(TarFile, [{cwd, TargetDir}, compressed]), + + %% Create start_erl.data + StartErlDataFile = filename:join([ReleasesDir, "start_erl.data"]), + StartErlData = io_lib:fwrite("~s ~s~n", [ErtsVsn, RelVsn]), + ok = file:write_file(StartErlDataFile, StartErlData), + + %% Create RELEASES + ok = release_handler:create_RELEASES(TargetDir,ReleasesDir,RelFile,[]), + + %% Create start_erl + ok = subst_file(filename:join([ErtsBinDir,"start_erl.src"]), + filename:join([BinDir,"start_erl"]), + [{"EMU","beam"}], + [{chmod,8#0755}]), + + %% Create start script + %% Using a customized start script from DataDir where some options + %% (heart and nodename) are added compared to the start.src in the + %% erlang distribution. + ok = subst_file(filename:join(DataDir, "start"), + filename:join([BinDir, "start"]), + [{"ROOT",TargetDir}], + [preserve]), + ok. + +%% Create version P1H - which is P1G + a-1.0 +%% Must have run create_p1g first!! +create_p1h(Conf) -> + create_upgrade_release(Conf,"rel1","P1H",{"4.4",false},[{a,"1.0"}], + [{a,[{key2,val2}]}],{"rel0",[new_appl]}). + +%% Create version P1I - which is P1H, but with application a upgraded to a-1.1 +%% Must have run create_p1h first!! +create_p1i(Conf) -> + create_upgrade_release(Conf,"rel2","P1I",{"4.4",false},[{a,"1.1"}], + [{a,[{key2,newval2}]}], + {"rel1",[{extra,gott}]}). + +%% Create version P2A - which is P1I, but with erts-<latest> +%% Must have run create_p1i first!! +create_p2a(Conf) -> + ErtsVsn = erlang:system_info(version), + create_upgrade_release(Conf,"rel3","P2A",{ErtsVsn,code:root_dir()}, + [{a,"1.1"}],[{a,[{key2,newval2}]}], + {"rel2",[new_emu]}). + +%% Create a release tar package which can be installed on top of P1G +create_upgrade_release(Conf,RelName,RelVsn,Erts,Apps,Config,{UpFromName,Descr}) -> + PrivDir = priv_dir(Conf), + DataDir = ?config(data_dir,Conf), + + RelDir = filename:join(PrivDir,RelName), + RelFileName = filename:join(RelDir,RelName), + RelFile = RelFileName ++ ".rel", + ok = filelib:ensure_dir(RelFile), + LibPath = filename:join([DataDir,lib,"*",ebin]), + + UpFrom = [{filename:join([PrivDir,UpFromName,UpFromName]),Descr}], + + create_basic_release(RelFile, RelVsn, Erts, LibPath, + Apps, Config, UpFrom, []), + ok. + +%% Create .rel, .script, .boot, sys.config and tar +create_basic_release(RelFile,RelVsn,{ErtsVsn,ErtsDir},LibPath,ExtraApps,Config,UpFrom,DownTo) -> + RelDir = filename:dirname(RelFile), + RelFileName = filename:rootname(RelFile), + + %% Create .rel file + create_installer_rel_file(RelFile,RelVsn,ErtsVsn,ExtraApps), + + %% Generate .script and .boot + ok = systools:make_script(RelFileName, + [{path,[LibPath]}, + {outdir,RelDir}]), + + %% Generate relup + ok = systools:make_relup(RelFileName,UpFrom,DownTo,[{path,[LibPath]}, + {outdir,RelDir}]), + + %% Create sys.config + ok = write_term_file(filename:join(RelDir,"sys.config"),Config), + + + %% Create tar file (i.e. collect all lib/app-*/* and system files) + ok = systools:make_tar(RelFileName, + [{path,[LibPath]}, + {outdir,RelDir} | + case ErtsDir of + false -> []; + _ -> [{erts,ErtsDir}] + end]), + + RelFileName ++ ".tar.gz". + +%% Create a .rel file +create_installer_rel_file(RelFile,RelVsn,ErtsVsn,ExtraApps) -> + create_rel_file(RelFile,"SASL-test",RelVsn,ErtsVsn, + [{installer,"1.0"}|ExtraApps]). + +create_rel_file(RelFile,RelName,RelVsn,ErtsVsn,ExtraApps) -> + {ok,KernelVsn} = application:get_key(kernel,vsn), + {ok,StdlibVsn} = application:get_key(stdlib,vsn), + {ok,SaslVsn} = application:get_key(sasl,vsn), + application:load(tools), + {ok,ToolsVsn} = application:get_key(tools,vsn), + application:load(runtime_tools), + {ok,RuntimeToolsVsn} = application:get_key(runtime_tools,vsn), + + RelFileContent = {release, + {RelName, RelVsn}, + {erts, ErtsVsn}, + [{kernel, KernelVsn}, + {stdlib, StdlibVsn}, + {sasl, SaslVsn}, + {runtime_tools, RuntimeToolsVsn}, + {tools, ToolsVsn} | + ExtraApps]}, + ok = write_term_file(RelFile,RelFileContent). + +%% Insert a term in a file, which can be read with file:consult/1. +write_term_file(File,Term) -> + ok = file:write_file(File,io_lib:format("~p.~n",[Term])). + + +%% Check that global group info is correct +check_gg_info(Node,OtherAlive,OtherDead,Synced) -> + GGI = rpc:call(Node, global_group, info, []), + GI = rpc:call(Node, global, info,[]), + try do_check_gg_info(OtherAlive,OtherDead,Synced,GGI,GI) + catch _:E -> + ?t:format("~ncheck_gg_info failed for ~p: ~p~nwhen GGI was: ~p~n" + "and GI was: ~p~n", + [Node,E,GGI,GI]), + ?t:fail("check_gg_info failed") + end. + +do_check_gg_info(OtherAlive,OtherDead,Synced,GGI,GI) -> + {_,gg1} = lists:keyfind(own_group_name,1,GGI), + {_,synced} = lists:keyfind(state,1,GGI), + {_,AllNodes} = lists:keyfind(own_group_nodes,1,GGI), + true = lists:sort(AllNodes) =:= lists:sort(OtherAlive++OtherDead), + {_,[]} = lists:keyfind(sync_error,1,GGI), + {_,[{gg2,[_,_]}]} = lists:keyfind(other_groups,1,GGI), + + %% There is a known bug in global_group (OTP-9177) which causes + %% the following to fail every now and then: + %% {_,SyncedNodes} = lists:keyfind(synced_nodes,1,GGI), + %% true = lists:sort(SyncedNodes) =:= lists:sort(Synced), + %% {_,NoContact} = lists:keyfind(no_contact,1,GGI), + %% true = lists:sort(NoContact) =:= lists:sort(OtherDead), + + %% Therefore we use global:info instead for this part + {state,_,_,SyncedNodes,_,_,_,_,_,_,_} = GI, + true = lists:sort(SyncedNodes) =:= lists:sort(Synced), + + %% .. and we only check that all OtherDead are listed as + %% no_contact (due to th bug there might be more nodes in this + %% list) + {_,NoContact} = lists:keyfind(no_contact,1,GGI), + true = + lists:sort(OtherDead) =:= + lists:sort([NC || NC <- NoContact,lists:member(NC,OtherDead)]), + + ok. + +%% Return the configuration (to be inserted in sys.config) for global group tests +gg_config(Snames) -> + Nodes = [node_name(Sname) || Sname <- Snames], + [{kernel, [{sync_nodes_optional, Nodes}, + {sync_nodes_timeout, 10000}, + {global_groups, + [{gg1, Nodes}, + {gg2, [node_name(Sname) || Sname <- [ggq,ggw]]}]}]}, + {a, [{key2, val2}]}]. + +%% Start a node with short name SnameStr, and unpack P1H +unpack_p1h(Conf,Sname) -> + PrivDir = priv_dir(Conf), + [Node] = start_nodes(Conf,[Sname],"create_p1h"), + ok = rpc_inst(Node, unpack_p1h, [PrivDir]), + Node. + +%% On the given node, install P1H and make it permanent +%% This function is to be called after unpack_p1h/2, with the same node. +permanent_p1h(Node) -> + ok = rpc_inst(Node, permanent_p1h, []). + +%% For each node in ToNodes, create a target installation which is +%% indentical to the target installation for FromNode. +copy_installed(Conf,FromNode,ToNodes) -> + PrivDir = priv_dir(Conf), + DataDir = ?config(data_dir,Conf), + lists:foreach( + fun(Node) -> + ok = copy_tree(Conf,filename:join(PrivDir,FromNode),Node,PrivDir), + NodeDir = filename:join(PrivDir,Node), + ok = subst_file(filename:join(DataDir, "start"), + filename:join([NodeDir, "bin", "start"]), + [{"ROOT",NodeDir}]), + LogDir = filename:join(NodeDir,log), + {ok,Logs} = file:list_dir(LogDir), + lists:foreach(fun(Log) -> + file:delete(filename:join(LogDir,Log)) + end, + Logs) + end, + ToNodes). + +start_nodes(Conf,Snames,Tag) -> + PrivDir = priv_dir(Conf), + Nodes = + lists:map( + fun(Sname) -> + NodeDir = filename:join(PrivDir,Sname), + Node = node_name(Sname), + + Script = filename:join([NodeDir,"bin","start"]), + Cmd = "env NODENAME="++atom_to_list(Sname) ++ " " ++ Script, + %% {ok,StartFile} = file:read_file(Cmd), + %% io:format("~s:\n~s~n~n",[Start,binary_to_list(StartFile)]), + Res = os:cmd(Cmd), + io:format("Start ~p: ~p~n=>\t~p~n", [Sname,Cmd,Res]), + Node + end, + Snames), + wait_nodes_up(Nodes,Tag), + Nodes. + +tc_sname(Config) -> + tc_sname(Config,""). +tc_sname(Config,Fix) when is_atom(Fix) -> + tc_sname(Config,atom_to_list(Fix)); +tc_sname(Config,Fix) when is_list(Fix) -> + list_to_atom( + atom_to_list(?MODULE) + ++ "-" ++ atom_to_list(?config(sname_prefix, Config)) ++ + case Fix of + "" -> ""; + _ -> "-" ++ Fix + end). + +tc_full_node_name(Config) -> + tc_full_node_name(Config,""). +tc_full_node_name(Config,Fix) -> + node_name(tc_sname(Config,Fix)). + + +%% When installing a release for which the sys.config includes added +%% or changed global group(s), this node (test_sever@host) will be +%% disconnected from the test node (Node) by global_group.erl. This +%% will cause the rpc:call to terminate with {badrpc,nodedown} even if +%% the installation succeeds. This function installs the release, +%% accepts the faulty return value and then checks if the release was +%% successfully installed. +install_release_changed_gg(Node,RelVsn) -> + stop_cover(Node), + {badrpc,nodedown} = rpc:call(Node,release_handler,install_release,[RelVsn]), + timer:sleep(100), + wait_installed(Node,RelVsn,4). + +wait_installed(Node,RelVsn,0) -> + ?t:fail("install_release_changed_gg failed for " ++ RelVsn ++ + " on " ++ atom_to_list(Node)); +wait_installed(Node,RelVsn,N) -> + Rels = rpc:call(Node,release_handler,which_releases,[]), + case lists:keyfind(RelVsn, 2, Rels) of + {"SASL-test", RelVsn, _Libs, current} -> + start_cover(Node), + ok; + _ -> + timer:sleep(500), + wait_installed(Node,RelVsn,N-1) + end. + +%% Start/stop cover measurements on the given node +start_cover(Node) -> + cover_fun(Node,start). +stop_cover(Node) -> + cover_fun(Node,stop). + +cover_fun(Node,Func) -> + case ?t:is_cover() of + true -> + cover:Func(Node); + false -> + ok + end. + +%%%----------------------------------------------------------------- +%%% Create a fake release .... + +%% This function will create and install a release build on the +%% current running OTP release. It includes kernel, stdlib and sasl, +%% and possibly other applications if they are listed in AppDirs = +%% [{App,Vsn,LibDir}] +create_and_install_fake_first_release(Dir,AppDirs) -> + %% Create the first release + {RelName,RelVsn} = init:script_id(), + {Rel,_} = create_fake_release(Dir,RelName,RelVsn,AppDirs), + ReleasesDir = filename:join(Dir, "releases"), + RelDir = filename:dirname(Rel), + + %% And install it + RelVsnDir = filename:join(ReleasesDir, RelVsn), + ok = filelib:ensure_dir(filename:join(RelVsnDir,"*")), + + ok = copy_file(Rel++".rel",RelVsnDir), + ok = copy_file(Rel++".boot",filename:join(RelVsnDir, "start.boot")), + ok = copy_file(filename:join(RelDir,"sys.config"),RelVsnDir), + + ok = release_handler:create_RELEASES(code:root_dir(), + ReleasesDir, + Rel++".rel", + AppDirs), + + Rel. + +%% This function create a new release, including a relup file. It can +%% be upgraded to from the release created by +%% create_and_install_fake_first_release/2. Unpack first by calls to +%% release_handler:set_unpacked and release_handler:install_file. +create_fake_upgrade_release(Dir,RelVsn,AppDirs,{UpFrom,DownTo,ExtraLibs}) -> + %% Create a new release + {RelName,_} = init:script_id(), + {Rel,Paths} = create_fake_release(Dir,RelName,RelVsn,AppDirs), + RelDir = filename:dirname(Rel), + + %% And a relup file so it can be upgraded to + RelupPath = Paths ++ [filename:join([Lib,"*","ebin"]) || Lib <- ExtraLibs], + ok = systools:make_relup(Rel,[UpFrom],[DownTo], + [{path,RelupPath}, + {outdir,RelDir}]), + + Rel. + + +create_fake_release(Dir,RelName,RelVsn,AppDirs) -> + %% Create .rel files + RelDir = filename:join(Dir,"rel_" ++ RelVsn), + Rel = filename:join([RelDir,"rel_" ++ RelVsn]), + ok = filelib:ensure_dir(Rel), + ErtsVsn = erlang:system_info(version), + + {Apps,Paths} = + lists:foldl(fun({App,Vsn,Lib},{As,Ps}) -> + {[{App,Vsn}|As], + lists:umerge([filename:join([Lib,"*",ebin])],Ps)} + end, + {[],[]}, + AppDirs), + + create_rel_file(Rel++".rel",RelName,RelVsn,ErtsVsn,Apps), + + %% Generate boot scripts + ok = systools:make_script(Rel,[local, + {path, Paths}, + {outdir,RelDir}]), + ok = copy_file(Rel++".boot", filename:join(RelDir,"start.boot")), + + %% Use an own 'releases' directory - we don't want to change the + %% contents of $OTP_ROOT/releases + %% Inform SASL about this via sys.config + ReleasesDir = filename:join(Dir, "releases"), + Config = [{sasl,[{releases_dir,ReleasesDir}]}], + ok = write_term_file(filename:join(RelDir,"sys.config"), Config), + + {Rel,Paths}. + + +rpc_inst(Node,Func,Args) -> + rpc:call(Node,installer,Func,[node()|Args]). diff --git a/lib/sasl/test/release_handler_SUITE_data/Makefile.src b/lib/sasl/test/release_handler_SUITE_data/Makefile.src new file mode 100644 index 0000000000..85e25fdc2f --- /dev/null +++ b/lib/sasl/test/release_handler_SUITE_data/Makefile.src @@ -0,0 +1,108 @@ +EFLAGS=+debug_info + +P2B= \ + P2B/a-2.0/ebin/a.beam \ + P2B/a-2.0/ebin/a_sup.beam + +LIB= \ + lib/a-1.1/ebin/a.beam \ + lib/a-1.1/ebin/a_sup.beam \ + lib/a-1.0/ebin/a.beam \ + lib/a-1.0/ebin/a_sup.beam \ + +APP= \ + app1_app2/lib1/app1-1.0/ebin/app1_sup.@EMULATOR@ \ + app1_app2/lib1/app1-1.0/ebin/app1_server.@EMULATOR@ \ + app1_app2/lib1/app1-1.0/ebin/app1.@EMULATOR@ \ + app1_app2/lib1/app2-1.0/ebin/app2_sup.@EMULATOR@ \ + app1_app2/lib1/app2-1.0/ebin/app2_server.@EMULATOR@ \ + app1_app2/lib1/app2-1.0/ebin/app2.@EMULATOR@ \ + app1_app2/lib2/app1-2.0/ebin/app1_sup.@EMULATOR@ \ + app1_app2/lib2/app1-2.0/ebin/app1_server.@EMULATOR@ \ + app1_app2/lib2/app1-2.0/ebin/app1.@EMULATOR@ \ + app1_app2/lib2/app2-1.0/ebin/app2_sup.@EMULATOR@ \ + app1_app2/lib2/app2-1.0/ebin/app2_server.@EMULATOR@ \ + app1_app2/lib2/app2-1.0/ebin/app2.@EMULATOR@ + +OTP2740= \ + otp_2740/vsn_atom.@EMULATOR@ \ + otp_2740/vsn_list.@EMULATOR@ \ + otp_2740/vsn_numeric.@EMULATOR@ \ + otp_2740/vsn_tuple.@EMULATOR@ \ + otp_2740/vsn_string.@EMULATOR@ + +C= \ + c/aa.@EMULATOR@ \ + c/b.@EMULATOR@ \ + c/c_sup.@EMULATOR@ + + +all: $(P2B) $(LIB) $(APP) $(OTP2740) $(C) + +P2B/a-2.0/ebin/a.@EMULATOR@: P2B/a-2.0/src/a.erl + erlc $(EFLAGS) -oP2B/a-2.0/ebin P2B/a-2.0/src/a.erl +P2B/a-2.0/ebin/a_sup.@EMULATOR@: P2B/a-2.0/src/a_sup.erl + erlc $(EFLAGS) -oP2B/a-2.0/ebin P2B/a-2.0/src/a_sup.erl + + +lib/a-1.0/ebin/a.@EMULATOR@: lib/a-1.0/src/a.erl + erlc $(EFLAGS) -olib/a-1.0/ebin lib/a-1.0/src/a.erl +lib/a-1.0/ebin/a_sup.@EMULATOR@: lib/a-1.0/src/a_sup.erl + erlc $(EFLAGS) -olib/a-1.0/ebin lib/a-1.0/src/a_sup.erl + + +lib/a-1.1/ebin/a.@EMULATOR@: lib/a-1.1/src/a.erl + erlc $(EFLAGS) -olib/a-1.1/ebin lib/a-1.1/src/a.erl +lib/a-1.1/ebin/a_sup.@EMULATOR@: lib/a-1.1/src/a_sup.erl + erlc $(EFLAGS) -olib/a-1.1/ebin lib/a-1.1/src/a_sup.erl + + +app1_app2/lib1/app1-1.0/ebin/app1_sup.@EMULATOR@: app1_app2/lib1/app1-1.0/src/app1_sup.erl + erlc $(EFLAGS) -oapp1_app2/lib1/app1-1.0/ebin app1_app2/lib1/app1-1.0/src/app1_sup.erl +app1_app2/lib1/app1-1.0/ebin/app1_server.@EMULATOR@: app1_app2/lib1/app1-1.0/src/app1_server.erl + erlc $(EFLAGS) -oapp1_app2/lib1/app1-1.0/ebin app1_app2/lib1/app1-1.0/src/app1_server.erl +app1_app2/lib1/app1-1.0/ebin/app1.@EMULATOR@: app1_app2/lib1/app1-1.0/src/app1.erl + erlc $(EFLAGS) -oapp1_app2/lib1/app1-1.0/ebin app1_app2/lib1/app1-1.0/src/app1.erl + + +app1_app2/lib1/app2-1.0/ebin/app2_sup.@EMULATOR@: app1_app2/lib1/app2-1.0/src/app2_sup.erl + erlc $(EFLAGS) -oapp1_app2/lib1/app2-1.0/ebin app1_app2/lib1/app2-1.0/src/app2_sup.erl +app1_app2/lib1/app2-1.0/ebin/app2_server.@EMULATOR@: app1_app2/lib1/app2-1.0/src/app2_server.erl + erlc $(EFLAGS) -oapp1_app2/lib1/app2-1.0/ebin app1_app2/lib1/app2-1.0/src/app2_server.erl +app1_app2/lib1/app2-1.0/ebin/app2.@EMULATOR@: app1_app2/lib1/app2-1.0/src/app2.erl + erlc $(EFLAGS) -oapp1_app2/lib1/app2-1.0/ebin app1_app2/lib1/app2-1.0/src/app2.erl + + +app1_app2/lib2/app1-2.0/ebin/app1_sup.@EMULATOR@: app1_app2/lib2/app1-2.0/src/app1_sup.erl + erlc $(EFLAGS) -oapp1_app2/lib2/app1-2.0/ebin app1_app2/lib2/app1-2.0/src/app1_sup.erl +app1_app2/lib2/app1-2.0/ebin/app1_server.@EMULATOR@: app1_app2/lib2/app1-2.0/src/app1_server.erl + erlc $(EFLAGS) -oapp1_app2/lib2/app1-2.0/ebin app1_app2/lib2/app1-2.0/src/app1_server.erl +app1_app2/lib2/app1-2.0/ebin/app1.@EMULATOR@: app1_app2/lib2/app1-2.0/src/app1.erl + erlc $(EFLAGS) -oapp1_app2/lib2/app1-2.0/ebin app1_app2/lib2/app1-2.0/src/app1.erl + + +app1_app2/lib2/app2-1.0/ebin/app2_sup.@EMULATOR@: app1_app2/lib2/app2-1.0/src/app2_sup.erl + erlc $(EFLAGS) -oapp1_app2/lib2/app2-1.0/ebin app1_app2/lib2/app2-1.0/src/app2_sup.erl +app1_app2/lib2/app2-1.0/ebin/app2_server.@EMULATOR@: app1_app2/lib2/app2-1.0/src/app2_server.erl + erlc $(EFLAGS) -oapp1_app2/lib2/app2-1.0/ebin app1_app2/lib2/app2-1.0/src/app2_server.erl +app1_app2/lib2/app2-1.0/ebin/app2.@EMULATOR@: app1_app2/lib2/app2-1.0/src/app2.erl + erlc $(EFLAGS) -oapp1_app2/lib2/app2-1.0/ebin app1_app2/lib2/app2-1.0/src/app2.erl + + +otp_2740/vsn_atom.@EMULATOR@: otp_2740/vsn_atom.erl + erlc $(EFLAGS) -ootp_2740 otp_2740/vsn_atom.erl +otp_2740/vsn_list.@EMULATOR@: otp_2740/vsn_list.erl + erlc $(EFLAGS) -ootp_2740 otp_2740/vsn_list.erl +otp_2740/vsn_numeric.@EMULATOR@: otp_2740/vsn_numeric.erl + erlc $(EFLAGS) -ootp_2740 otp_2740/vsn_numeric.erl +otp_2740/vsn_tuple.@EMULATOR@: otp_2740/vsn_tuple.erl + erlc $(EFLAGS) -ootp_2740 otp_2740/vsn_tuple.erl +otp_2740/vsn_string.@EMULATOR@: otp_2740/vsn_string.erl + erlc $(EFLAGS) -ootp_2740 otp_2740/vsn_string.erl + +c/aa.@EMULATOR@: c/aa.erl + erlc $(EFLAGS) -oc c/aa.erl +c/b.@EMULATOR@: c/b.erl + erlc $(EFLAGS) -oc c/b.erl +c/c_sup.@EMULATOR@: c/c_sup.erl + erlc $(EFLAGS) -oc c/c_sup.erl diff --git a/lib/sasl/test/release_handler_SUITE_data/P2B/a-2.0/ebin/a.app b/lib/sasl/test/release_handler_SUITE_data/P2B/a-2.0/ebin/a.app new file mode 100644 index 0000000000..200cfcfe47 --- /dev/null +++ b/lib/sasl/test/release_handler_SUITE_data/P2B/a-2.0/ebin/a.app @@ -0,0 +1,8 @@ +{application, a, + [{description, "A CXC 138 11"}, + {vsn, "2.0"}, + {modules, [{a, 1}, {a_sup,1}]}, + {registered, [a_sup]}, + {applications, [kernel, stdlib]}, + {env, [{key1, val1}]}, + {mod, {a_sup, []}}]}. diff --git a/lib/sasl/test/release_handler_SUITE_data/P2B/a-2.0/src/a.erl b/lib/sasl/test/release_handler_SUITE_data/P2B/a-2.0/src/a.erl new file mode 100644 index 0000000000..cfe38b55ce --- /dev/null +++ b/lib/sasl/test/release_handler_SUITE_data/P2B/a-2.0/src/a.erl @@ -0,0 +1,47 @@ +%% ``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 via the world wide web 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. +%% +%% The Initial Developer of the Original Code is Ericsson Utvecklings AB. +%% Portions created by Ericsson are Copyright 1999, Ericsson Utvecklings +%% AB. All Rights Reserved.'' +%% +%% $Id$ +%% +-module(a). + + +-behaviour(gen_server). + +%% External exports +-export([start_link/0, a/0]). +%% Internal exports +-export([init/1, handle_call/3, handle_info/2, terminate/2]). + +start_link() -> gen_server:start_link({local, aa}, a, [], []). + +a() -> gen_server:call(aa, a). + +%%----------------------------------------------------------------- +%% Callback functions from gen_server +%%----------------------------------------------------------------- +init([]) -> + process_flag(trap_exit, true), + {ok, state}. + +handle_call(a, _From, State) -> + X = application:get_all_env(a), + {reply, X, State}. + +handle_info(_, State) -> + {noreply, State}. + +terminate(_Reason, _State) -> + ok. diff --git a/lib/sasl/test/release_handler_SUITE_data/P2B/a-2.0/src/a_sup.erl b/lib/sasl/test/release_handler_SUITE_data/P2B/a-2.0/src/a_sup.erl new file mode 100644 index 0000000000..a141c1767b --- /dev/null +++ b/lib/sasl/test/release_handler_SUITE_data/P2B/a-2.0/src/a_sup.erl @@ -0,0 +1,37 @@ +%% ``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 via the world wide web 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. +%% +%% The Initial Developer of the Original Code is Ericsson Utvecklings AB. +%% Portions created by Ericsson are Copyright 1999, Ericsson Utvecklings +%% AB. All Rights Reserved.'' +%% +%% $Id$ +%% +-module(a_sup). + + +-behaviour(supervisor). + +%% External exports +-export([start/2]). + +%% Internal exports +-export([init/1]). + +start(_, _) -> + supervisor:start_link({local, a_sup}, a_sup, []). + +init([]) -> + SupFlags = {one_for_one, 4, 3600}, + Config = {a, + {a, start_link, []}, + permanent, 2000, worker, [a]}, + {ok, {SupFlags, [Config]}}. diff --git a/lib/sasl/test/release_handler_SUITE_data/app1_app2/lib1/app1-1.0/ebin/app1.app b/lib/sasl/test/release_handler_SUITE_data/app1_app2/lib1/app1-1.0/ebin/app1.app new file mode 100644 index 0000000000..0489cb2595 --- /dev/null +++ b/lib/sasl/test/release_handler_SUITE_data/app1_app2/lib1/app1-1.0/ebin/app1.app @@ -0,0 +1,9 @@ +{application, app1, + [{description, "very simple example application"}, + {id, "app1"}, + {vsn, "1.0"}, + {modules, [app1, app1_sup, app1_server]}, + {registered, [harry]}, + {applications, [kernel, stdlib, sasl]}, + {env, [{var,val1}]}, + {mod, {app1, []}}]}. diff --git a/lib/sasl/test/release_handler_SUITE_data/app1_app2/lib1/app1-1.0/src/app1.erl b/lib/sasl/test/release_handler_SUITE_data/app1_app2/lib1/app1-1.0/src/app1.erl new file mode 100644 index 0000000000..f123c8f470 --- /dev/null +++ b/lib/sasl/test/release_handler_SUITE_data/app1_app2/lib1/app1-1.0/src/app1.erl @@ -0,0 +1,22 @@ +-module(app1). + +-behaviour(application). + +%% Application callbacks +-export([start/2, stop/1]). +-export([config_change/3]). + +start(_Type, _StartArgs) -> + case app1_sup:start_link() of + {ok, Pid} -> + {ok, Pid}; + Error -> + Error + end. + +stop(_State) -> + ok. + +config_change(Changed, _New, _Removed) -> + catch ets:insert(otp_6162, hd(Changed)), + ok. diff --git a/lib/sasl/test/release_handler_SUITE_data/app1_app2/lib1/app1-1.0/src/app1_server.erl b/lib/sasl/test/release_handler_SUITE_data/app1_app2/lib1/app1-1.0/src/app1_server.erl new file mode 100644 index 0000000000..9b49e772cc --- /dev/null +++ b/lib/sasl/test/release_handler_SUITE_data/app1_app2/lib1/app1-1.0/src/app1_server.erl @@ -0,0 +1,32 @@ +-module(app1_server). + +-behaviour(gen_server). + +%% API +-export([start_link/0]). + +%% gen_server callbacks +-export([init/1, handle_call/3, handle_cast/2, handle_info/2, + terminate/2, code_change/3]). + +start_link() -> + gen_server:start_link({local, harry}, ?MODULE, [], []). + +init([]) -> + {ok, []}. + +handle_call(_Request, _From, State) -> + Reply = ok, + {reply, Reply, State}. + +handle_cast(_Msg, State) -> + {noreply, State}. + +handle_info(_Info, State) -> + {noreply, State}. + +terminate(_Reason, _State) -> + ok. + +code_change(_OldVsn, State, _Extra) -> + {ok, State}. diff --git a/lib/sasl/test/release_handler_SUITE_data/app1_app2/lib1/app1-1.0/src/app1_sup.erl b/lib/sasl/test/release_handler_SUITE_data/app1_app2/lib1/app1-1.0/src/app1_sup.erl new file mode 100644 index 0000000000..e6ad9b6967 --- /dev/null +++ b/lib/sasl/test/release_handler_SUITE_data/app1_app2/lib1/app1-1.0/src/app1_sup.erl @@ -0,0 +1,17 @@ +-module(app1_sup). + +-behaviour(supervisor). + +%% API +-export([start_link/0]). + +%% Supervisor callbacks +-export([init/1]). + +start_link() -> + supervisor:start_link(?MODULE, []). + +init([]) -> + AChild = {harry,{app1_server,start_link,[]}, + permanent,2000,worker,[app1_server]}, + {ok,{{one_for_all,0,1}, [AChild]}}. diff --git a/lib/sasl/test/release_handler_SUITE_data/app1_app2/lib1/app2-1.0/ebin/app2.app b/lib/sasl/test/release_handler_SUITE_data/app1_app2/lib1/app2-1.0/ebin/app2.app new file mode 100644 index 0000000000..d48018cbda --- /dev/null +++ b/lib/sasl/test/release_handler_SUITE_data/app1_app2/lib1/app2-1.0/ebin/app2.app @@ -0,0 +1,9 @@ +{application, app2, + [{description, "very simple example application"}, + {id, "app2"}, + {vsn, "1.0"}, + {modules, [app2, app2_sup, app2_server]}, + {registered, [ginny]}, + {applications, [kernel, stdlib, sasl]}, + {env, []}, + {mod, {app2, []}}]}. diff --git a/lib/sasl/test/release_handler_SUITE_data/app1_app2/lib1/app2-1.0/src/app2.erl b/lib/sasl/test/release_handler_SUITE_data/app1_app2/lib1/app2-1.0/src/app2.erl new file mode 100644 index 0000000000..a41c39730c --- /dev/null +++ b/lib/sasl/test/release_handler_SUITE_data/app1_app2/lib1/app2-1.0/src/app2.erl @@ -0,0 +1,17 @@ +-module(app2). + +-behaviour(application). + +%% Application callbacks +-export([start/2, stop/1]). + +start(_Type, _StartArgs) -> + case app2_sup:start_link() of + {ok, Pid} -> + {ok, Pid}; + Error -> + Error + end. + +stop(_State) -> + ok. diff --git a/lib/sasl/test/release_handler_SUITE_data/app1_app2/lib1/app2-1.0/src/app2_server.erl b/lib/sasl/test/release_handler_SUITE_data/app1_app2/lib1/app2-1.0/src/app2_server.erl new file mode 100644 index 0000000000..d8440230ff --- /dev/null +++ b/lib/sasl/test/release_handler_SUITE_data/app1_app2/lib1/app2-1.0/src/app2_server.erl @@ -0,0 +1,32 @@ +-module(app2_server). + +-behaviour(gen_server). + +%% API +-export([start_link/0]). + +%% gen_server callbacks +-export([init/1, handle_call/3, handle_cast/2, handle_info/2, + terminate/2, code_change/3]). + +start_link() -> + gen_server:start_link({local, ginny}, ?MODULE, [], []). + +init([]) -> + {ok, []}. + +handle_call(_Request, _From, State) -> + Reply = ok, + {reply, Reply, State}. + +handle_cast(_Msg, State) -> + {noreply, State}. + +handle_info(_Info, State) -> + {noreply, State}. + +terminate(_Reason, _State) -> + ok. + +code_change(_OldVsn, State, _Extra) -> + {ok, State}. diff --git a/lib/sasl/test/release_handler_SUITE_data/app1_app2/lib1/app2-1.0/src/app2_sup.erl b/lib/sasl/test/release_handler_SUITE_data/app1_app2/lib1/app2-1.0/src/app2_sup.erl new file mode 100644 index 0000000000..80b0952d4b --- /dev/null +++ b/lib/sasl/test/release_handler_SUITE_data/app1_app2/lib1/app2-1.0/src/app2_sup.erl @@ -0,0 +1,17 @@ +-module(app2_sup). + +-behaviour(supervisor). + +%% API +-export([start_link/0]). + +%% Supervisor callbacks +-export([init/1]). + +start_link() -> + supervisor:start_link(?MODULE, []). + +init([]) -> + AChild = {ginny,{app2_server,start_link,[]}, + permanent,2000,worker,[app2_server]}, + {ok,{{one_for_all,0,1}, [AChild]}}. diff --git a/lib/sasl/test/release_handler_SUITE_data/app1_app2/lib2/app1-2.0/ebin/app1.app b/lib/sasl/test/release_handler_SUITE_data/app1_app2/lib2/app1-2.0/ebin/app1.app new file mode 100644 index 0000000000..3c65adfbb3 --- /dev/null +++ b/lib/sasl/test/release_handler_SUITE_data/app1_app2/lib2/app1-2.0/ebin/app1.app @@ -0,0 +1,9 @@ +{application, app1, + [{description, "very simple example application"}, + {id, "app1"}, + {vsn, "2.0"}, + {modules, [app1, app1_sup, app1_server]}, + {registered, [harry]}, + {applications, [kernel, stdlib, sasl]}, + {env, [{var,val2}]}, + {mod, {app1, []}}]}. diff --git a/lib/sasl/test/release_handler_SUITE_data/app1_app2/lib2/app1-2.0/ebin/app1.appup b/lib/sasl/test/release_handler_SUITE_data/app1_app2/lib2/app1-2.0/ebin/app1.appup new file mode 100644 index 0000000000..e5e0cbda0e --- /dev/null +++ b/lib/sasl/test/release_handler_SUITE_data/app1_app2/lib2/app1-2.0/ebin/app1.appup @@ -0,0 +1,4 @@ +{"2.0", + [{"1.0", [{load_module, app1_server}]}], + [{"1.0", [{load_module, app1_server}]}] +}. diff --git a/lib/sasl/test/release_handler_SUITE_data/app1_app2/lib2/app1-2.0/src/app1.erl b/lib/sasl/test/release_handler_SUITE_data/app1_app2/lib2/app1-2.0/src/app1.erl new file mode 100644 index 0000000000..f123c8f470 --- /dev/null +++ b/lib/sasl/test/release_handler_SUITE_data/app1_app2/lib2/app1-2.0/src/app1.erl @@ -0,0 +1,22 @@ +-module(app1). + +-behaviour(application). + +%% Application callbacks +-export([start/2, stop/1]). +-export([config_change/3]). + +start(_Type, _StartArgs) -> + case app1_sup:start_link() of + {ok, Pid} -> + {ok, Pid}; + Error -> + Error + end. + +stop(_State) -> + ok. + +config_change(Changed, _New, _Removed) -> + catch ets:insert(otp_6162, hd(Changed)), + ok. diff --git a/lib/sasl/test/release_handler_SUITE_data/app1_app2/lib2/app1-2.0/src/app1_server.erl b/lib/sasl/test/release_handler_SUITE_data/app1_app2/lib2/app1-2.0/src/app1_server.erl new file mode 100644 index 0000000000..660d095ebf --- /dev/null +++ b/lib/sasl/test/release_handler_SUITE_data/app1_app2/lib2/app1-2.0/src/app1_server.erl @@ -0,0 +1,35 @@ +-module(app1_server). + +-behaviour(gen_server). + +%% API +-export([start_link/0]). + +%% gen_server callbacks +-export([init/1, handle_call/3, handle_cast/2, handle_info/2, + terminate/2, code_change/3]). + +start_link() -> + gen_server:start_link({local, harry}, ?MODULE, [], []). + +init([]) -> + {ok, []}. + +handle_call(error, _From, State) -> + Reply = error, + {reply, Reply, State}; +handle_call(_Request, _From, State) -> + Reply = ok, + {reply, Reply, State}. + +handle_cast(_Msg, State) -> + {noreply, State}. + +handle_info(_Info, State) -> + {noreply, State}. + +terminate(_Reason, _State) -> + ok. + +code_change(_OldVsn, State, _Extra) -> + {ok, State}. diff --git a/lib/sasl/test/release_handler_SUITE_data/app1_app2/lib2/app1-2.0/src/app1_sup.erl b/lib/sasl/test/release_handler_SUITE_data/app1_app2/lib2/app1-2.0/src/app1_sup.erl new file mode 100644 index 0000000000..e6ad9b6967 --- /dev/null +++ b/lib/sasl/test/release_handler_SUITE_data/app1_app2/lib2/app1-2.0/src/app1_sup.erl @@ -0,0 +1,17 @@ +-module(app1_sup). + +-behaviour(supervisor). + +%% API +-export([start_link/0]). + +%% Supervisor callbacks +-export([init/1]). + +start_link() -> + supervisor:start_link(?MODULE, []). + +init([]) -> + AChild = {harry,{app1_server,start_link,[]}, + permanent,2000,worker,[app1_server]}, + {ok,{{one_for_all,0,1}, [AChild]}}. diff --git a/lib/sasl/test/release_handler_SUITE_data/app1_app2/lib2/app2-1.0/ebin/app2.app b/lib/sasl/test/release_handler_SUITE_data/app1_app2/lib2/app2-1.0/ebin/app2.app new file mode 100644 index 0000000000..d48018cbda --- /dev/null +++ b/lib/sasl/test/release_handler_SUITE_data/app1_app2/lib2/app2-1.0/ebin/app2.app @@ -0,0 +1,9 @@ +{application, app2, + [{description, "very simple example application"}, + {id, "app2"}, + {vsn, "1.0"}, + {modules, [app2, app2_sup, app2_server]}, + {registered, [ginny]}, + {applications, [kernel, stdlib, sasl]}, + {env, []}, + {mod, {app2, []}}]}. diff --git a/lib/sasl/test/release_handler_SUITE_data/app1_app2/lib2/app2-1.0/src/app2.erl b/lib/sasl/test/release_handler_SUITE_data/app1_app2/lib2/app2-1.0/src/app2.erl new file mode 100644 index 0000000000..a41c39730c --- /dev/null +++ b/lib/sasl/test/release_handler_SUITE_data/app1_app2/lib2/app2-1.0/src/app2.erl @@ -0,0 +1,17 @@ +-module(app2). + +-behaviour(application). + +%% Application callbacks +-export([start/2, stop/1]). + +start(_Type, _StartArgs) -> + case app2_sup:start_link() of + {ok, Pid} -> + {ok, Pid}; + Error -> + Error + end. + +stop(_State) -> + ok. diff --git a/lib/sasl/test/release_handler_SUITE_data/app1_app2/lib2/app2-1.0/src/app2_server.erl b/lib/sasl/test/release_handler_SUITE_data/app1_app2/lib2/app2-1.0/src/app2_server.erl new file mode 100644 index 0000000000..d8440230ff --- /dev/null +++ b/lib/sasl/test/release_handler_SUITE_data/app1_app2/lib2/app2-1.0/src/app2_server.erl @@ -0,0 +1,32 @@ +-module(app2_server). + +-behaviour(gen_server). + +%% API +-export([start_link/0]). + +%% gen_server callbacks +-export([init/1, handle_call/3, handle_cast/2, handle_info/2, + terminate/2, code_change/3]). + +start_link() -> + gen_server:start_link({local, ginny}, ?MODULE, [], []). + +init([]) -> + {ok, []}. + +handle_call(_Request, _From, State) -> + Reply = ok, + {reply, Reply, State}. + +handle_cast(_Msg, State) -> + {noreply, State}. + +handle_info(_Info, State) -> + {noreply, State}. + +terminate(_Reason, _State) -> + ok. + +code_change(_OldVsn, State, _Extra) -> + {ok, State}. diff --git a/lib/sasl/test/release_handler_SUITE_data/app1_app2/lib2/app2-1.0/src/app2_sup.erl b/lib/sasl/test/release_handler_SUITE_data/app1_app2/lib2/app2-1.0/src/app2_sup.erl new file mode 100644 index 0000000000..80b0952d4b --- /dev/null +++ b/lib/sasl/test/release_handler_SUITE_data/app1_app2/lib2/app2-1.0/src/app2_sup.erl @@ -0,0 +1,17 @@ +-module(app2_sup). + +-behaviour(supervisor). + +%% API +-export([start_link/0]). + +%% Supervisor callbacks +-export([init/1]). + +start_link() -> + supervisor:start_link(?MODULE, []). + +init([]) -> + AChild = {ginny,{app2_server,start_link,[]}, + permanent,2000,worker,[app2_server]}, + {ok,{{one_for_all,0,1}, [AChild]}}. diff --git a/lib/sasl/test/release_handler_SUITE_data/c/aa.erl b/lib/sasl/test/release_handler_SUITE_data/c/aa.erl new file mode 100644 index 0000000000..1c853c85b2 --- /dev/null +++ b/lib/sasl/test/release_handler_SUITE_data/c/aa.erl @@ -0,0 +1,41 @@ +%% ``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 via the world wide web 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. +%% +%% The Initial Developer of the Original Code is Ericsson Utvecklings AB. +%% Portions created by Ericsson are Copyright 1999, Ericsson Utvecklings +%% AB. All Rights Reserved.'' +%% +%% $Id$ +%% +-module(aa). + + +-behaviour(gen_server). + +%% External exports +-export([start_link/0]). +%% Internal exports +-export([init/1, handle_info/2, terminate/2]). + +start_link() -> gen_server:start_link({local, cc}, aa, [], []). + +%%----------------------------------------------------------------- +%% Callback functions from gen_server +%%----------------------------------------------------------------- +init([]) -> + process_flag(trap_exit, true), + {ok, state}. + +handle_info(_, State) -> + {noreply, State}. + +terminate(_Reason, _State) -> + ok. diff --git a/lib/sasl/test/release_handler_SUITE_data/c/b.erl b/lib/sasl/test/release_handler_SUITE_data/c/b.erl new file mode 100644 index 0000000000..d8426a515e --- /dev/null +++ b/lib/sasl/test/release_handler_SUITE_data/c/b.erl @@ -0,0 +1,38 @@ +%% ``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 via the world wide web 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. +%% +%% The Initial Developer of the Original Code is Ericsson Utvecklings AB. +%% Portions created by Ericsson are Copyright 1999, Ericsson Utvecklings +%% AB. All Rights Reserved.'' +%% +%% $Id$ +%% +-module(b). + + +%% External exports +-export([start_link/0]). +%% Internal exports +-export([init/0]). + +start_link() -> {ok, proc_lib:spawn_link(b, init, [])}. + +%%----------------------------------------------------------------- +%% Callback functions from gen_server +%%----------------------------------------------------------------- +init() -> + register(bb, self()), + loop(). + +loop() -> + receive + hej -> ok + end. diff --git a/lib/sasl/test/release_handler_SUITE_data/c/c.app b/lib/sasl/test/release_handler_SUITE_data/c/c.app new file mode 100644 index 0000000000..908a94cf2d --- /dev/null +++ b/lib/sasl/test/release_handler_SUITE_data/c/c.app @@ -0,0 +1,8 @@ +{application, c, + [{description, "C CXC 138 11"}, + {vsn, "1.0"}, + {modules, [b, {aa, 1}, {c_sup,1}]}, + {registered, [cc,bb,c_sup]}, + {applications, [kernel, stdlib]}, + {env, [{key1, val1}]}, + {mod, {c_sup, []}}]}. diff --git a/lib/sasl/test/release_handler_SUITE_data/c/c_sup.erl b/lib/sasl/test/release_handler_SUITE_data/c/c_sup.erl new file mode 100644 index 0000000000..069eb3b99b --- /dev/null +++ b/lib/sasl/test/release_handler_SUITE_data/c/c_sup.erl @@ -0,0 +1,40 @@ +%% ``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 via the world wide web 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. +%% +%% The Initial Developer of the Original Code is Ericsson Utvecklings AB. +%% Portions created by Ericsson are Copyright 1999, Ericsson Utvecklings +%% AB. All Rights Reserved.'' +%% +%% $Id$ +%% +-module(c_sup). + + +-behaviour(supervisor). + +%% External exports +-export([start/2]). + +%% Internal exports +-export([init/1]). + +start(_, _) -> + supervisor:start_link({local, c_sup}, c_sup, []). + +init([]) -> + SupFlags = {one_for_one, 4, 3600}, + Config1 = {c, + {aa, start_link, []}, + permanent, 2000, worker, [aa]}, + Config2 = {b, + {b, start_link, []}, + permanent, 2000, worker, [b]}, + {ok, {SupFlags, [Config1, Config2]}}. diff --git a/lib/sasl/test/release_handler_SUITE_data/clients/start_cli1 b/lib/sasl/test/release_handler_SUITE_data/clients/start_cli1 new file mode 100755 index 0000000000..ee3d8c97cf --- /dev/null +++ b/lib/sasl/test/release_handler_SUITE_data/clients/start_cli1 @@ -0,0 +1,38 @@ +#!/bin/sh +# +# This program invokes the erlang emulator by calling run_erl. +# It should only be used at an embedded target system. +# It should be modified to give the correct flags to erl (via start_erl), +# e.g -mode embedded -sname XXX +# +# Usage: start [Data] +# + +if [ "x${NODENAME}" = "x" ] +then + echo "ERROR: Variable \$NODENAME is not set!!" + exit 1 +fi + +TESTHOST=`hostname | sed 's/[.].*//'` +IPADDR=%IPADDR% + +ROOTDIR=%ROOT% +CLIENTDIR=$ROOTDIR/clients/type1/$NODENAME@$TESTHOST + +RELDIR=$CLIENTDIR/releases + +# Note that this scripts is modified an copied to $CLIENTDIR/bin/start +# in release_handler_SUITE:copy_client - therefore HEART_COMMAND is as follows: +HEART_COMMAND=$CLIENTDIR/bin/start +HW_WD_DISABLE=true +export HW_WD_DISABLE HEART_COMMAND + +START_ERL_DATA=${1:-$RELDIR/start_erl.data} + +if [ ! -d /tmp/$NODENAME@$TESTHOST ] +then + mkdir /tmp/$NODENAME@$TESTHOST +fi + +$ROOTDIR/bin/run_erl /tmp/$NODENAME@$TESTHOST/ $CLIENTDIR/log "exec $ROOTDIR/bin/start_erl $ROOTDIR $RELDIR $START_ERL_DATA -heart -sname $NODENAME -sasl start_prg \\\"$CLIENTDIR/bin/start\\\" masters \[\\'%MASTER%@$TESTHOST\\'\] client_directory \\\"$CLIENTDIR\\\" -loader inet -id $NODENAME -hosts $IPADDR" > $CLIENTDIR/log/run_erl.out 2>&1 & diff --git a/lib/sasl/test/release_handler_SUITE_data/clients/start_cli2 b/lib/sasl/test/release_handler_SUITE_data/clients/start_cli2 new file mode 100755 index 0000000000..88912cf884 --- /dev/null +++ b/lib/sasl/test/release_handler_SUITE_data/clients/start_cli2 @@ -0,0 +1,37 @@ +#!/bin/sh +# +# This program invokes the erlang emulator by calling run_erl. +# It should only be used at an embedded target system. +# It should be modified to give the correct flags to erl (via start_erl), +# e.g -mode embedded -sname XXX +# +# Usage: start [Data] +# + +if [ "x${NODENAME}" = "x" ] +then + echo "ERROR: Variable \$NODENAME is not set!!" + exit 1 +fi + +TESTHOST=`hostname | sed 's/[.].*//'` + +ROOTDIR=%ROOT% +CLIENTDIR=$ROOTDIR/clients/type1/$NODENAME@$TESTHOST + +RELDIR=$CLIENTDIR/releases + +# Note that this scripts is modified an copied to $CLIENTDIR/bin/start +# in release_handler_SUITE:copy_client - therefore HEART_COMMAND is as follows: +HEART_COMMAND=$CLIENTDIR/bin/start +HW_WD_DISABLE=true +export HW_WD_DISABLE HEART_COMMAND + +START_ERL_DATA=${1:-$RELDIR/start_erl.data} + +if [ ! -d /tmp/$NODENAME@$TESTHOST ] +then + mkdir /tmp/$NODENAME@$TESTHOST +fi + +$ROOTDIR/bin/run_erl /tmp/$NODENAME@$TESTHOST/ $CLIENTDIR/log "exec $ROOTDIR/bin/start_erl $ROOTDIR $RELDIR $START_ERL_DATA -heart -sname $NODENAME -sasl start_prg \\\"$CLIENTDIR/bin/start\\\" masters \[\\'%MASTER%@$TESTHOST\\',\\'master2@$TESTHOST\\'\] client_directory \\\"$CLIENTDIR\\\"" > /dev/null 2>&1 & diff --git a/lib/sasl/test/release_handler_SUITE_data/lib/a-1.0/ebin/a.app b/lib/sasl/test/release_handler_SUITE_data/lib/a-1.0/ebin/a.app new file mode 100644 index 0000000000..e938137f67 --- /dev/null +++ b/lib/sasl/test/release_handler_SUITE_data/lib/a-1.0/ebin/a.app @@ -0,0 +1,8 @@ +{application, a, + [{description, "A CXC 138 11"}, + {vsn, "1.0"}, + {modules, [{a, 1}, {a_sup,1}]}, + {registered, [a_sup]}, + {applications, [kernel, stdlib]}, + {env, [{key1, val1}]}, + {mod, {a_sup, []}}]}. diff --git a/lib/sasl/test/release_handler_SUITE_data/lib/a-1.0/src/a.app b/lib/sasl/test/release_handler_SUITE_data/lib/a-1.0/src/a.app new file mode 100644 index 0000000000..e938137f67 --- /dev/null +++ b/lib/sasl/test/release_handler_SUITE_data/lib/a-1.0/src/a.app @@ -0,0 +1,8 @@ +{application, a, + [{description, "A CXC 138 11"}, + {vsn, "1.0"}, + {modules, [{a, 1}, {a_sup,1}]}, + {registered, [a_sup]}, + {applications, [kernel, stdlib]}, + {env, [{key1, val1}]}, + {mod, {a_sup, []}}]}. diff --git a/lib/sasl/test/release_handler_SUITE_data/lib/a-1.0/src/a.erl b/lib/sasl/test/release_handler_SUITE_data/lib/a-1.0/src/a.erl new file mode 100644 index 0000000000..bb500bed69 --- /dev/null +++ b/lib/sasl/test/release_handler_SUITE_data/lib/a-1.0/src/a.erl @@ -0,0 +1,49 @@ +%% ``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 via the world wide web 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. +%% +%% The Initial Developer of the Original Code is Ericsson Utvecklings AB. +%% Portions created by Ericsson are Copyright 1999, Ericsson Utvecklings +%% AB. All Rights Reserved.'' +%% +%% $Id$ +%% +-module(a). + + +-behaviour(gen_server). + +-vsn(1). + +%% External exports +-export([start_link/0, a/0]). +%% Internal exports +-export([init/1, handle_call/3, handle_info/2, terminate/2]). + +start_link() -> gen_server:start_link({local, aa}, a, [], []). + +a() -> gen_server:call(aa, a). + +%%----------------------------------------------------------------- +%% Callback functions from gen_server +%%----------------------------------------------------------------- +init([]) -> + process_flag(trap_exit, true), + {ok, state}. + +handle_call(a, _From, State) -> + X = application:get_all_env(a), + {reply, X, State}. + +handle_info(_, State) -> + {noreply, State}. + +terminate(_Reason, _State) -> + ok. diff --git a/lib/sasl/test/release_handler_SUITE_data/lib/a-1.0/src/a_sup.erl b/lib/sasl/test/release_handler_SUITE_data/lib/a-1.0/src/a_sup.erl new file mode 100644 index 0000000000..a141c1767b --- /dev/null +++ b/lib/sasl/test/release_handler_SUITE_data/lib/a-1.0/src/a_sup.erl @@ -0,0 +1,37 @@ +%% ``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 via the world wide web 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. +%% +%% The Initial Developer of the Original Code is Ericsson Utvecklings AB. +%% Portions created by Ericsson are Copyright 1999, Ericsson Utvecklings +%% AB. All Rights Reserved.'' +%% +%% $Id$ +%% +-module(a_sup). + + +-behaviour(supervisor). + +%% External exports +-export([start/2]). + +%% Internal exports +-export([init/1]). + +start(_, _) -> + supervisor:start_link({local, a_sup}, a_sup, []). + +init([]) -> + SupFlags = {one_for_one, 4, 3600}, + Config = {a, + {a, start_link, []}, + permanent, 2000, worker, [a]}, + {ok, {SupFlags, [Config]}}. diff --git a/lib/sasl/test/release_handler_SUITE_data/lib/a-1.1/ebin/a.app b/lib/sasl/test/release_handler_SUITE_data/lib/a-1.1/ebin/a.app new file mode 100644 index 0000000000..1c3053b2fa --- /dev/null +++ b/lib/sasl/test/release_handler_SUITE_data/lib/a-1.1/ebin/a.app @@ -0,0 +1,8 @@ +{application, a, + [{description, "A CXC 138 11"}, + {vsn, "1.1"}, + {modules, [{a, 2}, {a_sup,1}]}, + {registered, [a_sup]}, + {applications, [kernel, stdlib]}, + {env, [{key1, val1}]}, + {mod, {a_sup, []}}]}. diff --git a/lib/sasl/test/release_handler_SUITE_data/lib/a-1.1/ebin/a.appup b/lib/sasl/test/release_handler_SUITE_data/lib/a-1.1/ebin/a.appup new file mode 100644 index 0000000000..05db4cb541 --- /dev/null +++ b/lib/sasl/test/release_handler_SUITE_data/lib/a-1.1/ebin/a.appup @@ -0,0 +1,3 @@ +{"1.1", + [{"1.0",[{update,a,{advanced,extra_par}}]}], + []}. diff --git a/lib/sasl/test/release_handler_SUITE_data/lib/a-1.1/src/a.erl b/lib/sasl/test/release_handler_SUITE_data/lib/a-1.1/src/a.erl new file mode 100644 index 0000000000..c082ad5339 --- /dev/null +++ b/lib/sasl/test/release_handler_SUITE_data/lib/a-1.1/src/a.erl @@ -0,0 +1,54 @@ +%% ``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 via the world wide web 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. +%% +%% The Initial Developer of the Original Code is Ericsson Utvecklings AB. +%% Portions created by Ericsson are Copyright 1999, Ericsson Utvecklings +%% AB. All Rights Reserved.'' +%% +%% $Id$ +%% +-module(a). + + +-behaviour(gen_server). + +%% External exports +-export([start_link/0, a/0, b/0]). +%% Internal exports +-export([init/1, handle_call/3, handle_info/2, terminate/2, code_change/3]). + +start_link() -> gen_server:start_link({local, aa}, a, [], []). + +a() -> gen_server:call(aa, a). +b() -> gen_server:call(aa, b). + +%%----------------------------------------------------------------- +%% Callback functions from gen_server +%%----------------------------------------------------------------- +init([]) -> + process_flag(trap_exit, true), + {ok, {state, bval}}. + +handle_call(a, _From, State) -> + X = application:get_all_env(a), + {reply, X, State}; + +handle_call(b, _From, State) -> + {reply, {ok, element(2, State)}, State}. + +handle_info(_, State) -> + {noreply, State}. + +terminate(_Reason, _State) -> + ok. + +code_change(1, Extra, State) -> + {ok, {state, bval}}. diff --git a/lib/sasl/test/release_handler_SUITE_data/lib/a-1.1/src/a_sup.erl b/lib/sasl/test/release_handler_SUITE_data/lib/a-1.1/src/a_sup.erl new file mode 100644 index 0000000000..a141c1767b --- /dev/null +++ b/lib/sasl/test/release_handler_SUITE_data/lib/a-1.1/src/a_sup.erl @@ -0,0 +1,37 @@ +%% ``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 via the world wide web 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. +%% +%% The Initial Developer of the Original Code is Ericsson Utvecklings AB. +%% Portions created by Ericsson are Copyright 1999, Ericsson Utvecklings +%% AB. All Rights Reserved.'' +%% +%% $Id$ +%% +-module(a_sup). + + +-behaviour(supervisor). + +%% External exports +-export([start/2]). + +%% Internal exports +-export([init/1]). + +start(_, _) -> + supervisor:start_link({local, a_sup}, a_sup, []). + +init([]) -> + SupFlags = {one_for_one, 4, 3600}, + Config = {a, + {a, start_link, []}, + permanent, 2000, worker, [a]}, + {ok, {SupFlags, [Config]}}. diff --git a/lib/sasl/test/release_handler_SUITE_data/lib/installer-1.0/ebin/installer.app b/lib/sasl/test/release_handler_SUITE_data/lib/installer-1.0/ebin/installer.app new file mode 100644 index 0000000000..6f77317f6a --- /dev/null +++ b/lib/sasl/test/release_handler_SUITE_data/lib/installer-1.0/ebin/installer.app @@ -0,0 +1,6 @@ +{application, installer, + [{description, "Installer application"}, + {vsn, "1.0"}, + {modules, [{installer, 1}]}, + {registered, []}, + {applications, [kernel, stdlib, sasl]}]}. diff --git a/lib/sasl/test/release_handler_SUITE_data/lib/installer-1.0/src/installer.erl b/lib/sasl/test/release_handler_SUITE_data/lib/installer-1.0/src/installer.erl new file mode 120000 index 0000000000..c2f93b822d --- /dev/null +++ b/lib/sasl/test/release_handler_SUITE_data/lib/installer-1.0/src/installer.erl @@ -0,0 +1 @@ +../../../../installer.erl
\ No newline at end of file diff --git a/lib/sasl/test/release_handler_SUITE_data/otp_2740/vsn_atom.erl b/lib/sasl/test/release_handler_SUITE_data/otp_2740/vsn_atom.erl new file mode 100644 index 0000000000..883688c231 --- /dev/null +++ b/lib/sasl/test/release_handler_SUITE_data/otp_2740/vsn_atom.erl @@ -0,0 +1,26 @@ +%% ``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 via the world wide web 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. +%% +%% The Initial Developer of the Original Code is Ericsson Utvecklings AB. +%% Portions created by Ericsson are Copyright 1999, Ericsson Utvecklings +%% AB. All Rights Reserved.'' +%% +%% $Id$ +%% + +-module(vsn_atom). + +-vsn(atom). + +-export([ok/0]). + +ok() -> + ok. diff --git a/lib/sasl/test/release_handler_SUITE_data/otp_2740/vsn_list.erl b/lib/sasl/test/release_handler_SUITE_data/otp_2740/vsn_list.erl new file mode 100644 index 0000000000..34c38307ba --- /dev/null +++ b/lib/sasl/test/release_handler_SUITE_data/otp_2740/vsn_list.erl @@ -0,0 +1,26 @@ +%% ``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 via the world wide web 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. +%% +%% The Initial Developer of the Original Code is Ericsson Utvecklings AB. +%% Portions created by Ericsson are Copyright 1999, Ericsson Utvecklings +%% AB. All Rights Reserved.'' +%% +%% $Id$ +%% + +-module(vsn_list). + +-vsn([list, "of", {some, terms}]). + +-export([ok/0]). + +ok() -> + ok. diff --git a/lib/sasl/test/release_handler_SUITE_data/otp_2740/vsn_numeric.erl b/lib/sasl/test/release_handler_SUITE_data/otp_2740/vsn_numeric.erl new file mode 100644 index 0000000000..6bf52753fd --- /dev/null +++ b/lib/sasl/test/release_handler_SUITE_data/otp_2740/vsn_numeric.erl @@ -0,0 +1,26 @@ +%% ``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 via the world wide web 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. +%% +%% The Initial Developer of the Original Code is Ericsson Utvecklings AB. +%% Portions created by Ericsson are Copyright 1999, Ericsson Utvecklings +%% AB. All Rights Reserved.'' +%% +%% $Id$ +%% + +-module(vsn_numeric). + +-vsn(231894). + +-export([ok/0]). + +ok() -> + ok. diff --git a/lib/sasl/test/release_handler_SUITE_data/otp_2740/vsn_string.erl b/lib/sasl/test/release_handler_SUITE_data/otp_2740/vsn_string.erl new file mode 100644 index 0000000000..aa430a0bb3 --- /dev/null +++ b/lib/sasl/test/release_handler_SUITE_data/otp_2740/vsn_string.erl @@ -0,0 +1,26 @@ +%% ``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 via the world wide web 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. +%% +%% The Initial Developer of the Original Code is Ericsson Utvecklings AB. +%% Portions created by Ericsson are Copyright 1999, Ericsson Utvecklings +%% AB. All Rights Reserved.'' +%% +%% $Id$ +%% + +-module(vsn_string). + +-vsn("a string"). + +-export([ok/0]). + +ok() -> + ok. diff --git a/lib/sasl/test/release_handler_SUITE_data/otp_2740/vsn_tuple.erl b/lib/sasl/test/release_handler_SUITE_data/otp_2740/vsn_tuple.erl new file mode 100644 index 0000000000..3ff1018994 --- /dev/null +++ b/lib/sasl/test/release_handler_SUITE_data/otp_2740/vsn_tuple.erl @@ -0,0 +1,26 @@ +%% ``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 via the world wide web 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. +%% +%% The Initial Developer of the Original Code is Ericsson Utvecklings AB. +%% Portions created by Ericsson are Copyright 1999, Ericsson Utvecklings +%% AB. All Rights Reserved.'' +%% +%% $Id$ +%% + +-module(vsn_tuple). + +-vsn({tuple, ["of", terms]}). + +-export([ok/0]). + +ok() -> + ok. diff --git a/lib/sasl/test/release_handler_SUITE_data/start b/lib/sasl/test/release_handler_SUITE_data/start new file mode 100755 index 0000000000..45e526c15f --- /dev/null +++ b/lib/sasl/test/release_handler_SUITE_data/start @@ -0,0 +1,29 @@ +#!/bin/sh +# +# This program invokes the erlang emulator by calling run_erl. +# It should only be used at an embedded target system. +# It should be modified to give the correct flags to erl (via start_erl), +# e.g -mode embedded -sname XXX +# +# Usage: start [Data] +# +ROOTDIR=%ROOT% + +if [ "x${NODENAME}" = "x" ] +then + echo "ERROR: Variable \$NODENAME is not set!!" + exit 1 +fi + +if [ -z "$RELDIR" ] +then + RELDIR=$ROOTDIR/releases +fi + +HEART_COMMAND=$ROOTDIR/bin/start +HW_WD_DISABLE=true +export HW_WD_DISABLE HEART_COMMAND + +START_ERL_DATA=${1:-$RELDIR/start_erl.data} + +$ROOTDIR/bin/run_erl /tmp/ $ROOTDIR/log "exec $ROOTDIR/bin/start_erl $ROOTDIR $RELDIR $START_ERL_DATA -heart -sname $NODENAME" > $ROOTDIR/log/run_erl.out 2>&1 & diff --git a/lib/sasl/test/release_handler_SUITE_data/target_system.erl b/lib/sasl/test/release_handler_SUITE_data/target_system.erl new file mode 120000 index 0000000000..4d36c59632 --- /dev/null +++ b/lib/sasl/test/release_handler_SUITE_data/target_system.erl @@ -0,0 +1 @@ +../../examples/src/target_system.erl
\ No newline at end of file diff --git a/lib/sasl/test/sasl.cover b/lib/sasl/test/sasl.cover new file mode 100644 index 0000000000..d19d3d0180 --- /dev/null +++ b/lib/sasl/test/sasl.cover @@ -0,0 +1,2 @@ +{incl_app,sasl,details}. + diff --git a/lib/sasl/test/sasl.spec b/lib/sasl/test/sasl.spec new file mode 100644 index 0000000000..f3de90c9aa --- /dev/null +++ b/lib/sasl/test/sasl.spec @@ -0,0 +1 @@ +{suites,"../sasl_test",all}. diff --git a/lib/sasl/test/sasl_SUITE.erl b/lib/sasl/test/sasl_SUITE.erl new file mode 100644 index 0000000000..454095db6a --- /dev/null +++ b/lib/sasl/test/sasl_SUITE.erl @@ -0,0 +1,98 @@ +%% +%% %CopyrightBegin% +%% +%% Copyright Ericsson AB 2011. 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(sasl_SUITE). +-include_lib("common_test/include/ct.hrl"). + + +% Default timetrap timeout (set in init_per_testcase). +-define(default_timeout, ?t:minutes(1)). +-define(application, sasl). + +% Test server specific exports +-export([all/0,groups/0,init_per_group/2,end_per_group/2]). +-export([init_per_testcase/2, end_per_testcase/2]). + +% Test cases must be exported. +-export([app_test/1, + log_mf_h_env/1]). + +all() -> + [app_test, log_mf_h_env]. + +groups() -> + []. + +init_per_group(_GroupName, Config) -> + Config. + +end_per_group(_GroupName, Config) -> + Config. + + +init_per_testcase(_Case, Config) -> + ?line Dog=test_server:timetrap(?default_timeout), + [{watchdog, Dog}|Config]. +end_per_testcase(_Case, Config) -> + Dog=?config(watchdog, Config), + test_server:timetrap_cancel(Dog), + ok. + +app_test(Config) when is_list(Config) -> + ?line ?t:app_test(sasl, allow), + ok. + +%% OTP-9185 - fail sasl start if some but not all log_mf_h env vars +%% are given. +log_mf_h_env(Config) -> + PrivDir = ?config(priv_dir,Config), + LogDir = filename:join(PrivDir,sasl_SUITE_log_dir), + ok = file:make_dir(LogDir), + application:stop(sasl), + SaslEnv = application:get_all_env(sasl), + lists:foreach(fun({E,_V}) -> application:unset_env(sasl,E) end, SaslEnv), + + ok = application:set_env(sasl,error_logger_mf_dir,LogDir), + match_error(missing_config,application:start(sasl)), + + ok = application:set_env(sasl,error_logger_mf_maxbytes,"xx"), + match_error(bad_config,application:start(sasl)), + + ok = application:set_env(sasl,error_logger_mf_maxbytes,50000), + match_error(missing_config,application:start(sasl)), + + ok = application:set_env(sasl,error_logger_mf_maxfiles,"xx"), + match_error(bad_config,application:start(sasl)), + + ok = application:set_env(sasl,error_logger_mf_maxfiles,2), + ok = application:unset_env(sasl,error_logger_mf_dir), + match_error(missing_config,application:start(sasl)), + + ok = application:set_env(sasl,error_logger_mf_dir,xx), + match_error(bad_config,application:start(sasl)), + + ok = application:set_env(sasl,error_logger_mf_dir,LogDir), + ok = application:start(sasl). + + +%%----------------------------------------------------------------- +%% Internal +match_error(Expected,{error,{bad_return,{_,{'EXIT',{Expected,{sasl,_}}}}}}) -> + ok; +match_error(Expected,Actual) -> + ?t:fail({unexpected_return,Expected,Actual}). diff --git a/lib/sasl/test/systools_SUITE.erl b/lib/sasl/test/systools_SUITE.erl new file mode 100644 index 0000000000..9190b111ef --- /dev/null +++ b/lib/sasl/test/systools_SUITE.erl @@ -0,0 +1,2112 @@ +%% +%% %CopyrightBegin% +%% +%% Copyright Ericsson AB 2011. 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% +%% +%% +%% Test suite for the systools module. +%% +%% The systools module is a wrapper for a number of modules that +%% handle large parts of the release building functionality +%% (e.g. checking app files, building a tar file, building +%% release upgrad scripts. +%% + + +-module(systools_SUITE). + +%-define(debug, true). + +-include_lib("test_server/include/test_server.hrl"). +-define(format(S, A), ok). +-define(datadir, ?config(data_dir, Config)). +-define(privdir, ?config(priv_dir, Config)). +-define(copydir, ?config(copy_dir, Config)). + +-include_lib("kernel/include/file.hrl"). + +-export([all/0,suite/0,groups/0,init_per_group/2,end_per_group/2]). + +-export([ script_options/1, normal_script/1, no_mod_vsn_script/1, + wildcard_script/1, variable_script/1, + abnormal_script/1, src_tests_script/1, crazy_script/1, + warn_shadow_script/1, + included_script/1, included_override_script/1, + included_fail_script/1, included_bug_script/1, exref_script/1]). +-export([ tar_options/1, normal_tar/1, no_mod_vsn_tar/1, variable_tar/1, + src_tests_tar/1, shadow_tar/1, var_tar/1, + exref_tar/1, link_tar/1]). +-export([ normal_relup/1, abnormal_relup/1, no_appup_relup/1, + bad_appup_relup/1, app_start_type_relup/1, otp_3065/1]). +-export([ + otp_6226/1]). +-export([init_per_suite/1, end_per_suite/1, + init_per_testcase/2, end_per_testcase/2]). + +-import(lists, [foldl/3]). + +-define(default_timeout, ?t:minutes(20)). + + +%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% + +suite() -> + [{ct_hooks, [ts_install_cth]}]. + +all() -> + [{group, script}, {group, tar}, {group, relup}, + {group, tickets}]. + +groups() -> + [{script, [], + [script_options, normal_script, no_mod_vsn_script, + wildcard_script, variable_script, abnormal_script, + src_tests_script, crazy_script, warn_shadow_script, + included_script, included_override_script, + included_fail_script, included_bug_script, exref_script, + otp_3065]}, + {tar, [], + [tar_options, normal_tar, no_mod_vsn_tar, variable_tar, + src_tests_tar, shadow_tar, var_tar, + exref_tar, link_tar]}, + {relup, [], + [normal_relup, abnormal_relup, no_appup_relup, + bad_appup_relup, app_start_type_relup]}, + {tickets, [], [otp_6226]}]. + +init_per_group(_GroupName, Config) -> + Config. + +end_per_group(_GroupName, Config) -> + Config. + +%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% + + +init_per_suite(Config) when is_list(Config) -> + %% Make of copy of the data directory. + DataDir = ?datadir, + PrivDir = ?privdir, + ?line CopyDir = fname(PrivDir, "datacopy"), + ?line TarFile = fname(PrivDir, "datacopy.tgz"), + ?line {ok, Tar} = erl_tar:open(TarFile, [write, compressed]), + ?line ok = erl_tar:add(Tar, DataDir, CopyDir, [compressed]), + ?line ok = erl_tar:close(Tar), + ?line ok = erl_tar:extract(TarFile, [compressed]), + ?line ok = file:delete(TarFile), + + %% Compile source files in the copy directory. + ?line Sources = filelib:wildcard(fname([CopyDir,'*','*','*','*','*.erl'])), + ?line lists:foreach(fun compile_source/1, Sources), + + %% To use in end_per_testcase + Path = code:get_path(), + {ok,Cwd} = file:get_cwd(), + + [{copy_dir, CopyDir}, {cwd,Cwd}, {path,Path} | Config]. + +compile_source(File) -> + %% The compiler will no longer create a Beam file + %% with a module name that does not match the output + %% file, so we must compile to a binary and write + %% the output file ourselves. + U = filename:dirname(filename:dirname(File)), + Base = filename:rootname(filename:basename(File)), + OutFile = filename:join([U,"ebin",Base++".beam"]), + OutFileTemp = OutFile ++ "#", + {ok,_,Code} = compile:file(File, [binary]), + ok = file:write_file(OutFileTemp, Code), + file:rename(OutFileTemp, OutFile). + +end_per_suite(Conf) when is_list(Conf) -> + %% Nothing. + Conf. + +init_per_testcase(link_tar, Config) -> + case os:type() of + {unix, _} -> init_per_testcase(dummy, Config); + {win32, _} -> {skip, "Skip on windows"} + end; +init_per_testcase(_Case, Config) -> + ?line Dog = test_server:timetrap(?default_timeout), + [{watchdog, Dog}|Config]. + +end_per_testcase(_Case, Config) -> + Dog=?config(watchdog, Config), + test_server:timetrap_cancel(Dog), + case {?config(path,Config),?config(cwd,Config)} of + {undefined,undefined} -> + ok; + {Path,Cwd} -> + true = code:set_path(Path), + ok = file:set_cwd(Cwd) + end, + ok. + + + +%% Usage: +%% systools:make_script("RelName") +%% Make a boot file from RelName.rel. +%% Generates RelName.{script,boot} +%% systools:make_tar("RelName") +%% Make a release package from RelName.rel. +%% Generates RelName.tar,Z +%% systools:script2boot(File) +%% File.script -> File.boot +%% systools:make_relup("Target", ["UpFromRel"...], ["DownToRel"...], Opts) +%% Gather all appup scripts to the relup file +%% + + +%% make_script +%% +script_options(suite) -> []; +script_options(doc) -> + ["Check illegal script options."]; +script_options(Config) when is_list(Config) -> + ?line {'EXIT',{{badarg,[{path,["Path",12,"Another"]}]}, _}} = + (catch systools:make_script("release", [{path,["Path",12,"Another"]}])), + ?line {'EXIT',{{badarg,[sillent]}, _}} = + (catch systools:make_script("release", + [{path,["Path","Another"]},sillent])), + ?line {'EXIT',{{badarg,[locall]}, _}} = + (catch systools:make_script("release", + [{path,["Path","Another"]},locall])), + ?line {'EXIT',{{badarg,[src_testsxx]}, _}} = + (catch systools:make_script("release", + [{path,["Path"]},src_testsxx])), + ?line {'EXIT',{{badarg,[{variables, {"TEST", "/home/lib"}}]}, _}} = + (catch systools:make_script("release", + [{variables, {"TEST", "/home/lib"}}])), + ?line {'EXIT',{{badarg,[{variables, [{a, b}, {"a", "b"}]}]}, _}} = + (catch systools:make_script("release", + [{variables, [{a, b}, {"a", "b"}]}])), + ?line {'EXIT',{{badarg,[exreff]}, _}} = + (catch systools:make_script("release", + [{path,["Path","Another"]},exreff])), + ?line {'EXIT',{{badarg,[{exref,["appl"]}]}, _}} = + (catch systools:make_script("release", [{exref,["appl"]}])), + ?line {'EXIT',{{badarg,[{machine, "appl"}]}, _}} = + (catch systools:make_script("release", [{machine,"appl"}])), + ok. + + +%% make_script +%% +normal_script(suite) -> []; +normal_script(doc) -> + ["Check that make_script handles normal case."]; +normal_script(Config) when is_list(Config) -> + ?line {ok, OldDir} = file:get_cwd(), + ?line PSAVE = code:get_path(), % Save path + + ?line {LatestDir, LatestName} = create_script(latest,Config), + + ?line DataDir = filename:absname(?copydir), + ?line LibDir = fname([DataDir, d_normal, lib]), + ?line P1 = fname([LibDir, 'db-2.1', ebin]), + ?line P2 = fname([LibDir, 'fe-3.1', ebin]), + + ?line true = code:add_patha(P1), + ?line true = code:add_patha(P2), + + ?line ok = file:set_cwd(LatestDir), + + ?line ok = systools:make_script(filename:basename(LatestName)), + ?line {ok, _} = read_script_file(LatestName), % Check readabillity + + %% Check the same but w. silent flag + ?line {ok, _, []} = systools:make_script(LatestName, [silent]), + + %% Use the local option + ?line ok = systools:make_script(LatestName, [local]), + ?line ok = check_script_path(LatestName), + + %% use the path option + ?line code:set_path(PSAVE), % Restore path + %% Mess up std path: + ?line true = code:add_patha(fname([LibDir, 'db-1.0', ebin])), + ?line true = code:add_patha(fname([LibDir, 'fe-2.1', ebin])), + + ?line error = systools:make_script(LatestName), %should fail + ?line ok = systools:make_script(LatestName,[{path, [P1, P2]}]), + + ?line ok = file:set_cwd(OldDir), + ?line code:set_path(PSAVE), % Restore path + ok. + + +%% make_script +%% +no_mod_vsn_script(suite) -> []; +no_mod_vsn_script(doc) -> + ["Check that make_script handles normal case.", + "Modules specified without version in .app file (db-3.1)." + "Note that this is now the normal way - i.e. systools now " + "ignores the module versions in the .app file."]; +no_mod_vsn_script(Config) when is_list(Config) -> + ?line {ok, OldDir} = file:get_cwd(), + ?line PSAVE = code:get_path(), % Save path + + ?line {LatestDir, LatestName} = create_script(latest_no_mod_vsn,Config), + + ?line DataDir = filename:absname(?copydir), + ?line LibDir = fname([DataDir, d_normal, lib]), + ?line P1 = fname([LibDir, 'db-3.1', ebin]), + ?line P2 = fname([LibDir, 'fe-3.1', ebin]), + + ?line true = code:add_patha(P1), + ?line true = code:add_patha(P2), + + ?line ok = file:set_cwd(LatestDir), + + ?line ok = systools:make_script(filename:basename(LatestName)), + ?line {ok, _} = read_script_file(LatestName), % Check readabillity + + %% Check the same but w. silent flag + ?line {ok, _, []} = systools:make_script(LatestName, [silent]), + + %% Use the local option + ?line ok = systools:make_script(LatestName, [local]), + ?line ok = check_script_path(LatestName), + + %% use the path option + ?line code:set_path(PSAVE), % Restore path + %% Mess up std path: + ?line true = code:add_patha(fname([LibDir, 'db-1.0', ebin])), + ?line true = code:add_patha(fname([LibDir, 'fe-2.1', ebin])), + + ?line error = systools:make_script(LatestName), %should fail + ?line ok = systools:make_script(LatestName, + [{path, [P1, P2]}]), + + ?line ok = file:set_cwd(OldDir), + ?line code:set_path(PSAVE), % Restore path + ok. + + +%% make_script +%% +wildcard_script(suite) -> []; +wildcard_script(doc) -> + ["Check that make_script handles wildcards in path."]; +wildcard_script(Config) when is_list(Config) -> + ?line {ok, OldDir} = file:get_cwd(), + + ?line {LatestDir, LatestName} = create_script(latest,Config), + + ?line DataDir = filename:absname(?copydir), + ?line LibDir = fname([DataDir, d_normal, lib]), + ?line WildDir = fname([LibDir, '*', ebin]), + + ?line ok = file:set_cwd(LatestDir), + + ?line error = systools:make_script(filename:basename(LatestName)), + + ?line ok = systools:make_script(LatestName, + [{path, [WildDir]}]), + + ?line {ok, _} = read_script_file(LatestName), % Check readabillity + + ?line ok = file:set_cwd(OldDir), + ok. + + +%% make_script +%% +variable_script(suite) -> []; +variable_script(doc) -> + ["Add own installation dependent variable in script."]; +variable_script(Config) when is_list(Config) -> + ?line {ok, OldDir} = file:get_cwd(), + + ?line {LatestDir, LatestName} = create_script(latest,Config), + + ?line DataDir = filename:absname(?copydir), + ?line LibDir = fname([DataDir, d_normal, lib]), + ?line P = [fname([LibDir, 'db-2.1', ebin]), + fname([LibDir, 'fe-3.1', ebin])], + + ?line ok = file:set_cwd(LatestDir), + + ?line ok = systools:make_script(LatestName, + [{path, P}, + {variables, [{"TEST", LibDir}]}]), + + %% Check variables + ?line ok = check_var_script_file([fname(['$TEST', 'db-2.1', ebin]), + fname(['$TEST', 'fe-3.1', ebin])], + P, + LatestName), + + ?line ok = file:set_cwd(OldDir), + ok. + +%% make_script +%% +abnormal_script(suite) -> []; +abnormal_script(doc) -> + ["Abnormal cases."]; +abnormal_script(Config) when is_list(Config) -> + ?line {ok, OldDir} = file:get_cwd(), + + ?line {LatestDir, LatestName} = create_script(latest,Config), + + ?line DataDir = filename:absname(?copydir), + + ?line ok = file:set_cwd(LatestDir), + ?line LibDir = fname([DataDir, d_bad_app_vsn, lib]), + ?line P = [fname([LibDir, 'db-2.1', ebin]), + fname([LibDir, 'fe-3.1', ebin])], + + %% Check wrong app vsn + ?line error = systools:make_script(LatestName, [{path, P}]), + ?line {error, + systools_make, + [{error_reading, {db, {no_valid_version, + {{"should be","2.1"}, + {"found file", _, "2.0"}}}}}]} = + systools:make_script(LatestName, [silent, {path, P}]), + + ?line ok = file:set_cwd(OldDir), + ok. + + +%% make_script +%% +src_tests_script(suite) -> []; +src_tests_script(doc) -> + ["Do not check date of object file or that source code can be found."]; +src_tests_script(Config) when is_list(Config) -> + ?line {ok, OldDir} = file:get_cwd(), + ?line PSAVE = code:get_path(), % Save path + + ?line {LatestDir, LatestName} = create_script(latest,Config), + + ?line DataDir = filename:absname(?copydir), + ?line LibDir = fname([DataDir, d_missing_src, lib]), + ?line P1 = fname([LibDir, 'db-2.1', ebin]), + ?line P2 = fname([LibDir, 'fe-3.1', ebin]), + N = [P1, P2], + + ?line ok = file:set_cwd(LatestDir), + + %% Manipulate the modification date of a beam file so it seems + %% older than its .erl file + ?line Erl = filename:join([P1,"..","src","db1.erl"]), + ?line {ok, FileInfo=#file_info{mtime={{Y,M,D},T}}} = file:read_file_info(Erl), + ?line Beam = filename:join(P1,"db1.beam"), + ?line ok=file:write_file_info(Beam, FileInfo#file_info{mtime={{Y-1,M,D},T}}), + + %% Remove a .erl file + ?line Erl2 = filename:join([P1,"..","src","db2.erl"]), + ?line file:delete(Erl2), + + %% Then make script - two warnings should be issued when + %% src_tests is given + %% 1. old object code for db1.beam + %% 2. missing source code for db2.beam + ?line {ok, _, [{warning,{obj_out_of_date,_}}, + {warning,{source_not_found,_}}]} = + systools:make_script(LatestName, [silent, {path, N}, src_tests]), + + %% Without the src_tests option, no warning should be issued + ?line {ok, _, []} = + systools:make_script(LatestName, [silent, {path, N}]), + + %% Check that the old no_module_tests option (from the time when + %% it was default to do the src_test) is ignored + ?line {ok, _, [{warning,{obj_out_of_date,_}}, + {warning,{source_not_found,_}}]} = + systools:make_script(LatestName, [silent, + {path, N}, + no_module_tests, + src_tests]), + + ?line ok = file:set_cwd(OldDir), + ?line code:set_path(PSAVE), + ok. + +%% make_script +%% +warn_shadow_script(suite) -> []; +warn_shadow_script(doc) -> + ["Check that jam file out of date warning doesn't", + "shadow bad module version error."]; +warn_shadow_script(Config) when is_list(Config) -> + %% This test has been removed since the 'vsn' attribute is + %% not used any more, starting with R6. No warning + %% 'obj_out_of_date' seemed to be generated. + true. + + +%% make_script +%% +crazy_script(suite) -> []; +crazy_script(doc) -> + ["Do the crazy cases."]; +crazy_script(Config) when is_list(Config) -> + ?line {ok, OldDir} = file:get_cwd(), + + ?line {LatestDir, LatestName} = create_script(latest, Config), + + ?line DataDir = filename:absname(?copydir), + ?line LibDir = fname([DataDir, d_normal, lib]), + ?line P = [fname([LibDir, 'db-2.1', ebin]), + fname([LibDir, 'fe-3.1', ebin])], + + ?line ok = file:set_cwd(LatestDir), + + %% Run with bad path + ?line error = systools:make_script(LatestName), + ?line {error, _, [{error_reading, _}, {error_reading, _}]} = + systools:make_script(LatestName, [silent]), + + %% Run with .rel file lacking kernel + ?line {LatestDir2, LatestName2} = create_script(latest_nokernel, Config), + ?line ok = file:set_cwd(LatestDir2), + + ?line error = systools:make_script(LatestName2), + ?line {error, _, {missing_mandatory_app,[kernel,stdlib]}} = + systools:make_script(LatestName2, [silent,{path,P}]), + + ?line ok = file:set_cwd(OldDir), + ok. + +%% make_script +%% +included_script(suite) -> []; +included_script(doc) -> + ["Check that make_script handles generation of script", + "for applications with included applications."]; +included_script(Config) when is_list(Config) -> + ?line {ok, OldDir} = file:get_cwd(), + ?line {LatestDir, LatestName} = create_include_files(inc1, Config), + ?line ok = file:set_cwd(LatestDir), + ?line ok = systools:make_script(LatestName), + ?line ok = check_include_script(LatestName, + [t1, t2, t3, t5, t4, t6], + [t1, t3, t6]), + ?line ok = file:set_cwd(OldDir), + ok. + +%% make_script +%% +included_override_script(suite) -> []; +included_override_script(doc) -> + ["Check that make_script handles generation of script", + "for applications with included applications which are override by", + "the .rel file."]; +included_override_script(Config) when is_list(Config) -> + ?line {ok, OldDir} = file:get_cwd(), + ?line {LatestDir, LatestName} = create_include_files(inc2, Config), + ?line ok = file:set_cwd(LatestDir), + ?line ok = systools:make_script(LatestName), + ?line ok = check_include_script(LatestName, + [t1, t2, t3, t4, t6, t5], + [t1, t3, t6, t5]), + + ?line {_, LatestName1} = create_include_files(inc3, Config), + ?line ok = systools:make_script(LatestName1), + ?line ok = check_include_script(LatestName1, + [t3, t5, t4, t6, t1, t2], + [t3, t6, t1, t2]), + + ?line {_, LatestName2} = create_include_files(inc4, Config), + ?line ok = systools:make_script(LatestName2), + ?line ok = check_include_script(LatestName2, + [t3, t4, t6, t5, t1, t2], + [t3, t6, t5, t1, t2]), + + ?line {_, LatestName3} = create_include_files(inc5, Config), + ?line ok = systools:make_script(LatestName3), + ?line ok = check_include_script(LatestName3, + [t3, t4, t6, t1, t2], + [t3, t6, t1, t2]), + + ?line ok = file:set_cwd(OldDir), + ok. + +%% make_script +%% +included_fail_script(suite) -> []; +included_fail_script(doc) -> + ["Check that make_script handles errors then generating", + "script with included applications."]; +included_fail_script(Config) when is_list(Config) -> + ?line {ok, OldDir} = file:get_cwd(), + ?line {LatestDir, LatestName} = create_include_files(inc6, Config), + ?line ok = file:set_cwd(LatestDir), + ?line {error, _, {undefined_applications,[t2]}} = + systools:make_script(LatestName, [silent]), + + ?line {_, LatestName1} = create_include_files(inc7, Config), + ?line {error, _, {duplicate_include,[{{t5,t7,_,_},{t5,t6,_,_}}]}} = + systools:make_script(LatestName1, [silent]), + + ?line {_, LatestName3} = create_include_files(inc9, Config), + ?line {error, _, {circular_dependencies,[{t10,_},{t8,_}]}} = + systools:make_script(LatestName3, [silent]), + + ?line {_, LatestName4} = create_include_files(inc10, Config), + ?line {error, _, [{error_reading,{t9,{override_include,[t7]}}}]} = + systools:make_script(LatestName4, [silent]), + + ?line ok = file:set_cwd(OldDir), + ok. + +%% make_script +%% +included_bug_script(suite) -> []; +included_bug_script(doc) -> + ["Check that make_script handles generation of script", + "with difficult dependency for included applications."]; +included_bug_script(Config) when is_list(Config) -> + ?line {ok, OldDir} = file:get_cwd(), + ?line {LatestDir, LatestName} = create_include_files(inc11, Config), + ?line ok = file:set_cwd(LatestDir), + ?line ok = systools:make_script(LatestName), + ?line ok = check_include_script(LatestName, + [t13, t11, t12], + [t11, t12]), + ?line ok = file:set_cwd(OldDir), + ok. + + +%% make_script +%% +otp_3065(suite) -> []; +otp_3065(doc) -> + ["Circular dependencies in systools:make_script()."]; +otp_3065(Config) when is_list(Config) -> + ?line {ok, OldDir} = file:get_cwd(), + ?line {LatestDir, LatestName} = create_include_files(otp_3065, Config), + ?line ok = file:set_cwd(LatestDir), + ?line ok = systools:make_script(LatestName), + ?line ok = check_include_script(LatestName, + [aa12, chAts, chTraffic], + [chTraffic]), + ?line ok = file:set_cwd(OldDir), + ok. + + +%% make_script +%% +exref_script(suite) -> []; +exref_script(doc) -> + ["Check that make_script exref option works."]; +exref_script(Config) when is_list(Config) -> + ?line {ok, OldDir} = file:get_cwd(), + ?line PSAVE = code:get_path(), % Save path + + ?line {LatestDir, LatestName} = create_script(latest,Config), + + ?line DataDir = filename:absname(?copydir), + ?line LibDir = fname([DataDir, d_normal, lib]), + ?line P = [fname([LibDir, 'db-2.1', ebin]), + fname([LibDir, 'fe-3.1', ebin])], + + ?line ok = file:set_cwd(LatestDir), + + ?line {ok, _, _} = systools:make_script(LatestName, [{path,P}, silent]), + + %% Complete exref + ?line {ok, _, W1} = + systools:make_script(LatestName, [exref, {path,P}, silent]), + ?line check_exref_warnings(with_db1, W1), + ?line {ok, _} = read_script_file(LatestName), % Check readabillity + + %% Only exref the db application. + ?line {ok, _, W2} = + systools:make_script(LatestName, [{exref,[db]}, {path,P}, silent]), + ?line check_exref_warnings(with_db1, W2), + ?line {ok, _} = read_script_file(LatestName), % Check readabillity + + %% Only exref the fe application. + ?line {ok, _, W3} = + systools:make_script(LatestName, [{exref,[fe]}, {path,P}, silent]), + ?line check_exref_warnings(without_db1, W3), + ?line {ok, _} = read_script_file(LatestName), % Check readabillity + + %% exref the db and stdlib applications. + ?line {ok, _, W4} = + systools:make_script(LatestName, [{exref,[db,stdlib]}, {path,P}, silent]), + ?line check_exref_warnings(with_db1, W4), + ?line {ok, _} = read_script_file(LatestName), % Check readabillity + ?line ok = file:set_cwd(OldDir), + ?line code:set_path(PSAVE), % Restore path + ok. + +check_exref_warnings(with_db1, W) -> + case get_exref(undef, W) of + {ok, [{db2,non_existing_func,0}, + {fe2,non_existing_func,0}, + {lists,non_existing_func,1}]} -> + ok; + {ok, L} -> + test_server:fail({exref_warning_undef, L}); + _E -> + test_server:fail({bad_undef,_E}) + end; +check_exref_warnings(without_db1, W) -> + case get_exref(undef, W) of + false -> + ok; + {ok, L} -> + test_server:fail({exref_warning_undef, L}) + end. + +get_exref(undef, W) -> filter(no_hipe(get_exref1(exref_undef, W))). + +filter(false) -> + false; +filter({ok, W}) -> + {ok, filter(W)}; +filter(L) -> + lists:filter(fun%({hipe_consttab,_,_}) -> false; + ({int,_,_}) -> false; + ({i,_,_}) -> false; + ({crypto,_,_}) -> false; + (_) -> true + end, + L). + +get_exref1(T, [{warning, {T, Value}}|_]) -> {ok, Value}; +get_exref1(T, [_|W]) -> get_exref1(T, W); +get_exref1(_, []) -> false. + +no_hipe(false) -> + false; +no_hipe({ok, Value}) -> + case erlang:system_info(hipe_architecture) of + undefined -> + Hipe = "hipe", + Fun = fun({M,_,_}) -> not lists:prefix(Hipe, atom_to_list(M)) end, + NewValue = lists:filter(Fun, Value), + {ok, NewValue}; + _Arch -> + {ok, Value} + end. + +%% tar_options +%% +tar_options(suite) -> []; +tar_options(doc) -> + ["Check illegal tar options."]; +tar_options(Config) when is_list(Config) -> + ?line {'EXIT',{{badarg,[{path,["Path",12,"Another"]}]}, _}} = + (catch systools:make_tar("release", [{path,["Path",12,"Another"]}])), + ?line {'EXIT',{{badarg,[sillent]}, _}} = + (catch systools:make_tar("release", + [{path,["Path","Another"]},sillent])), + ?line {'EXIT',{{badarg,[{dirs,["dirs"]}]}, _}} = + (catch systools:make_tar("release", [{dirs, ["dirs"]}])), + ?line {'EXIT',{{badarg,[{erts, illegal}]}, _}} = + (catch systools:make_tar("release", [{erts, illegal}])), + ?line {'EXIT',{{badarg,[src_testsxx]}, _}} = + (catch systools:make_tar("release", + [{path,["Path"]},src_testsxx])), + ?line {'EXIT',{{badarg,[{variables, [{a, b}, {"a", "b"}]}]}, _}} = + (catch systools:make_tar("release", + [{variables, [{a, b}, {"a", "b"}]}])), + ?line {'EXIT',{{badarg,[{var_tar, illegal}]}, _}} = + (catch systools:make_tar("release", [{var_tar, illegal}])), + ?line {'EXIT',{{badarg,[exreff]}, _}} = + (catch systools:make_tar("release", + [{path,["Path","Another"]},exreff])), + ?line {'EXIT',{{badarg,[{exref,["appl"]}]}, _}} = + (catch systools:make_tar("release", [{exref,["appl"]}])), + ?line {'EXIT',{{badarg,[{machine, "appl"}]}, _}} = + (catch systools:make_tar("release", [{machine,"appl"}])), + ok. + + +%% normal_tar +%% +normal_tar(suite) -> []; +normal_tar(doc) -> + ["Check normal case"]; +normal_tar(Config) when is_list(Config) -> + ?line {ok, OldDir} = file:get_cwd(), + + ?line {LatestDir, LatestName} = create_script(latest,Config), + + ?line DataDir = filename:absname(?copydir), + ?line LibDir = fname([DataDir, d_normal, lib]), + ?line P = [fname([LibDir, 'db-2.1', ebin]), + fname([LibDir, 'fe-3.1', ebin])], + + ?line ok = file:set_cwd(LatestDir), + + ?line {ok, _, _} = systools:make_script(LatestName, [silent, {path, P}]), + ?line ok = systools:make_tar(LatestName, [{path, P}]), + ?line ok = check_tar(fname([lib,'db-2.1',ebin,'db.app']), LatestName), + ?line {ok, _, _} = systools:make_tar(LatestName, [{path, P}, silent]), + ?line ok = check_tar(fname([lib,'fe-3.1',ebin,'fe.app']), LatestName), + + ?line ok = file:set_cwd(OldDir), + ok. + +%% no_mod_vsn_tar +%% +no_mod_vsn_tar(suite) -> []; +no_mod_vsn_tar(doc) -> + ["Check normal case", + "Modules specified without version in .app file (db-3.1)." + "Note that this is now the normal way - i.e. systools now " + "ignores the module versions in the .app file."]; +no_mod_vsn_tar(Config) when is_list(Config) -> + ?line {ok, OldDir} = file:get_cwd(), + + ?line {LatestDir, LatestName} = create_script(latest_no_mod_vsn,Config), + + ?line DataDir = filename:absname(?copydir), + ?line LibDir = fname([DataDir, d_normal, lib]), + ?line P = [fname([LibDir, 'db-3.1', ebin]), + fname([LibDir, 'fe-3.1', ebin])], + + ?line ok = file:set_cwd(LatestDir), + + ?line {ok, _, _} = systools:make_script(LatestName, [silent, {path, P}]), + ?line ok = systools:make_tar(LatestName, [{path, P}]), + ?line ok = check_tar(fname([lib,'db-3.1',ebin,'db.app']), LatestName), + ?line {ok, _, _} = systools:make_tar(LatestName, [{path, P}, silent]), + ?line ok = check_tar(fname([lib,'fe-3.1',ebin,'fe.app']), LatestName), + + ?line ok = file:set_cwd(OldDir), + ok. + +%% variable_tar +%% +variable_tar(suite) -> []; +variable_tar(doc) -> + ["Use variable and create separate tar (included in generated tar)."]; +variable_tar(Config) when is_list(Config) -> + ?line {ok, OldDir} = file:get_cwd(), + + ?line {LatestDir, LatestName} = create_script(latest,Config), + + ?line DataDir = filename:absname(?copydir), + ?line LibDir = fname([DataDir, d_normal, lib]), + ?line P = [fname([LibDir, 'db-2.1', ebin]), + fname([LibDir, 'fe-3.1', ebin])], + + ?line ok = file:set_cwd(LatestDir), + + ?line {ok, _, _} = systools:make_script(LatestName, + [silent, + {path, P}, + {variables,[{"TEST", LibDir}]}]), + + ?line ok = systools:make_tar(LatestName, [{path, P}, + {variables,[{"TEST", LibDir}]}]), + ?line ok = check_var_tar("TEST", LatestName), + + ?line {ok, _, _} = systools:make_tar(LatestName, + [{path, P}, silent, + {variables,[{"TEST", LibDir}]}]), + ?line ok = check_var_tar("TEST", LatestName), + + ?line ok = file:set_cwd(OldDir), + ok. + +%% link_tar +%% +link_tar(suite) -> []; +link_tar(doc) -> + ["Check that symlinks in applications are handled correctly"]; +link_tar(Config) when is_list(Config) -> + ?line {ok, OldDir} = file:get_cwd(), + + ?line {LatestDir, LatestName} = create_script(latest,Config), + + ?line DataDir = filename:absname(?copydir), + ?line LibDir = fname([DataDir, d_links, lib]), + ?line P = [fname([LibDir, 'db-2.1', ebin]), + fname([LibDir, 'fe-3.1', ebin])], + + %% Make some links + ?line Db1Erl = fname(['db-2.1',src,'db1.erl']), + ?line NormalDb1Erl = fname([DataDir,d_normal,lib,Db1Erl]), + ?line LinkDb1Erl = fname([LibDir, Db1Erl]), + ?line ok = file:make_symlink(NormalDb1Erl, LinkDb1Erl), + ?line Db1Beam = fname(['db-2.1',ebin,'db1.beam']), + ?line NormalDb1Beam = fname([DataDir,d_normal,lib,Db1Beam]), + ?line LinkDb1Beam = fname([LibDir, Db1Beam]), + ?line ok = file:make_symlink(NormalDb1Beam, LinkDb1Beam), + ?line FeApp = fname(['fe-3.1',ebin,'fe.app']), + ?line NormalFeApp = fname([DataDir,d_normal,lib,FeApp]), + ?line LinkFeApp = fname([LibDir, FeApp]), + ?line ok = file:make_symlink(NormalFeApp, LinkFeApp), + + %% Create the tar and check that the linked files are included as + %% regular files + ?line ok = file:set_cwd(LatestDir), + + ?line {ok,_,[]} = systools:make_script(LatestName, [{path, P},silent]), + + ?line {ok, _, []} = systools:make_tar(LatestName, [{path, P}, silent]), + ?line ok = check_tar_regular(?privdir, + [fname([lib,FeApp]), + fname([lib,Db1Beam])], + LatestName), + + ?line {ok, _, []} = systools:make_tar(LatestName, [{path, P}, silent, + {dirs, [src]}]), + ?line ok = check_tar_regular(?privdir, + [fname([lib,FeApp]), + fname([lib,Db1Beam]), + fname([lib,Db1Erl])], + LatestName), + + ?line ok = file:set_cwd(OldDir), + ok. + +%% src_tests_tar +%% +src_tests_tar(suite) -> []; +src_tests_tar(doc) -> + ["Do not check date of object file or that source code can be found."]; +src_tests_tar(Config) when is_list(Config) -> + ?line {ok, OldDir} = file:get_cwd(), + + ?line {LatestDir, LatestName} = create_script(latest,Config), + + ?line DataDir = filename:absname(?copydir), + ?line LibDir = fname([DataDir, d_missing_src, lib]), + ?line P1 = fname([LibDir, 'db-2.1', ebin]), + ?line P2 = fname([LibDir, 'fe-3.1', ebin]), + P = [P1, P2], + + ?line ok = file:set_cwd(LatestDir), + + %% Manipulate the modification date of a beam file so it seems + %% older than the .erl file + Erl = filename:join([P1,"..","src","db1.erl"]), + {ok, FileInfo=#file_info{mtime={{Y,M,D},T}}} = file:read_file_info(Erl), + Beam = filename:join(P1,"db1.beam"), + ok = file:write_file_info(Beam, FileInfo#file_info{mtime={{Y-1,M,D},T}}), + + %% Remove a .erl file + ?line Erl2 = filename:join([P1,"..","src","db2.erl"]), + ?line file:delete(Erl2), + + ?line ok = systools:make_script(LatestName, [{path, P}]), + + %% Then make tar - two warnings should be issued when + %% src_tests is given + %% 1. old object code for db1.beam + %% 2. missing source code for db2.beam + ?line {ok, _, [{warning,{obj_out_of_date,_}}, + {warning,{source_not_found,_}}]} = + systools:make_tar(LatestName, [{path, P}, silent, + {dirs, [src]}, + src_tests]), + ?line ok = check_tar(fname([lib,'db-2.1',src,'db1.erl']), LatestName), + + %% Without the src_tests option, no warning should be issued + ?line {ok, _, []} = systools:make_tar(LatestName, [{path, P}, silent, + {dirs, [src]}]), + ?line ok = check_tar(fname([lib,'db-2.1',src,'db1.erl']), LatestName), + + %% Check that the old no_module_tests option (from the time when + %% it was default to do the src_test) is ignored + ?line {ok, _, [{warning,{obj_out_of_date,_}}, + {warning,{source_not_found,_}}]} = + systools:make_tar(LatestName, [{path, P}, silent, + {dirs, [src]}, + no_module_tests, + src_tests]), + ?line ok = check_tar(fname([lib,'db-2.1',src,'db1.erl']), LatestName), + + ?line ok = file:set_cwd(OldDir), + ok. + +%% shadow_tar +%% +shadow_tar(suite) -> []; +shadow_tar(doc) -> + ["Check that jam file out of date warning doesn't", + "shadow bad module version error."]; +shadow_tar(Config) when is_list(Config) -> + % This test has been commented out since the 'vsn' attribute is not used + % any more, starting with R6. No warning 'obj_out_of_date' seemed to be + % generated. + true; +shadow_tar(Config) when is_list(Config) -> + ?line {ok, OldDir} = file:get_cwd(), + ?line PSAVE = code:get_path(), % Save path + + ?line {LatestDir, LatestName} = create_script(latest,Config), + + ?line DataDir = filename:absname(?copydir), + ?line LibDir = fname([DataDir, 'd_bad_mod+warn', lib]), + ?line P = [fname([LibDir, 'db-2.1', ebin]), + fname([LibDir, 'fe-3.1', ebin])], + + ?line ok = file:set_cwd(LatestDir), + + ?line {error, _, _} = systools:make_tar(LatestName, [{path, P}, silent]), + ?line {error, _, _} = systools:make_tar(LatestName, [{path, P}, silent, + {dirs, [src]}]), + ?line ok = file:set_cwd(OldDir), + ?line code:set_path(PSAVE), + ok. + + +%% var_tar +%% +var_tar(suite) -> []; +var_tar(doc) -> + ["Check that make_tar handles generation and placement of tar", + "files for variables outside the main tar file.", + "Test the {var_tar, include | ownfile | omit} option."]; +var_tar(Config) when is_list(Config) -> + ?line {ok, OldDir} = file:get_cwd(), + ?line PSAVE = code:get_path(), % Save path + + ?line {LatestDir, LatestName} = create_script(latest,Config), + + ?line DataDir = filename:absname(?copydir), + ?line LibDir = fname([DataDir, d_normal, lib]), + ?line P = [fname([LibDir, 'db-2.1', ebin]), + fname([LibDir, 'fe-3.1', ebin])], + + ?line ok = file:set_cwd(LatestDir), + + ?line {ok, _, _} = systools:make_script(LatestName, + [silent, + {path, P}, + {variables,[{"TEST", LibDir}]}]), + + ?line ok = systools:make_tar(LatestName, [{path, P}, + {var_tar, ownfile}, + {variables,[{"TEST", LibDir}]}]), + + ?line true = exists_tar_file("TEST"), %% Also removes the file ! + ?line {error, {not_generated, _}} = check_var_tar("TEST", LatestName), + + ?line ok = systools:make_tar(LatestName, [{path, P}, + {var_tar, omit}, + {variables,[{"TEST", LibDir}]}]), + + ?line {error, {not_generated, _}} = check_var_tar("TEST", LatestName), + ?line false = exists_tar_file("TEST"), + + ?line ok = systools:make_tar(LatestName, [{path, P}, + {var_tar, include}, + {variables,[{"TEST", LibDir}]}]), + + ?line ok = check_var_tar("TEST", LatestName), + ?line false = exists_tar_file("TEST"), + + ?line ok = file:set_cwd(OldDir), + ?line code:set_path(PSAVE), + ok. + + +%% exref_tar +%% +exref_tar(suite) -> []; +exref_tar(doc) -> + ["Check exref option."]; +exref_tar(Config) when is_list(Config) -> + ?line {ok, OldDir} = file:get_cwd(), + + ?line {LatestDir, LatestName} = create_script(latest,Config), + + ?line DataDir = filename:absname(?copydir), + ?line LibDir = fname([DataDir, d_normal, lib]), + ?line P = [fname([LibDir, 'db-2.1', ebin]), + fname([LibDir, 'fe-3.1', ebin])], + + ?line ok = file:set_cwd(LatestDir), + + ?line {ok, _, _} = systools:make_script(LatestName, [silent, {path, P}]), + + %% Complete exref + ?line {ok, _, W1} = + systools:make_tar(LatestName, [exref, {path, P}, silent]), + ?line check_exref_warnings(with_db1, W1), + ?line ok = check_tar(fname([lib,'db-2.1',ebin,'db.app']), LatestName), + + %% Only exref the db application. + ?line {ok, _, W2} = + systools:make_tar(LatestName, [{exref, [db]}, {path, P}, silent]), + ?line check_exref_warnings(with_db1, W2), + ?line ok = check_tar(fname([lib,'fe-3.1',ebin,'fe.app']), LatestName), + + %% Only exref the fe application. + ?line {ok, _, W3} = + systools:make_tar(LatestName, [{exref, [fe]}, {path, P}, silent]), + ?line check_exref_warnings(without_db1, W3), + ?line ok = check_tar(fname([lib,'db-2.1',ebin,'db.app']), LatestName), + + %% exref the db and stdlib applications. + ?line {ok, _, W4} = + systools:make_tar(LatestName, [{exref, [db, stdlib]}, + {path, P}, silent]), + ?line check_exref_warnings(with_db1, W4), + ?line ok = check_tar(fname([lib,'fe-3.1',ebin,'fe.app']), LatestName), + + ?line ok = file:set_cwd(OldDir), + ok. + +%% The relup stuff. +%% +%% + + +%% make_relup +%% +normal_relup(suite) -> []; +normal_relup(doc) -> + ["Check normal case"]; +normal_relup(Config) when is_list(Config) -> + ?line {ok, OldDir} = file:get_cwd(), + + ?line {LatestDir,LatestName} = create_script(latest0,Config), + ?line {_LatestDir1,LatestName1} = create_script(latest1,Config), + ?line {_LatestDir2,LatestName2} = create_script(latest2,Config), + + ?line DataDir = filename:absname(?copydir), + ?line LibDir = [fname([DataDir, d_normal, lib])], + ?line P = [fname([LibDir, '*', ebin]), + fname([DataDir, lib, kernel, ebin]), + fname([DataDir, lib, stdlib, ebin])], + + ?line ok = file:set_cwd(LatestDir), + + %% OTP-2561: Check that the option 'restart_emulator' generates a + %% "restart_new_emulator" instruction. + ?line {ok, _ , _, []} = + systools:make_relup(LatestName, [LatestName1], [LatestName1], + [{path, P},restart_emulator,silent]), + ?line ok = check_relup([{db, "2.1"}], [{db, "1.0"}]), + ?line ok = check_restart_emulator(), + + %% This is the ultra normal case + ?line ok = systools:make_relup(LatestName, [LatestName1], [LatestName1], + [{path, P}]), + ?line ok = check_relup([{db, "2.1"}], [{db, "1.0"}]), + ?line {ok, _, _, []} = + systools:make_relup(LatestName, [LatestName1], [LatestName1], + [{path, P}, silent]), + ?line ok = check_relup([{db, "2.1"}], [{db, "1.0"}]), + + %% Check that warnings get through + ?line ok = systools:make_relup(LatestName, [LatestName2], [LatestName1], + [{path, P}]), + ?line ok = check_relup([{fe, "3.1"}, {db, "2.1"}], [{db, "1.0"}]), + ?line {ok, _, _, [{erts_vsn_changed, _}]} = + systools:make_relup(LatestName, [LatestName2], [LatestName1], + [{path, P}, silent]), + ?line ok = check_relup([{fe, "3.1"}, {db, "2.1"}], [{db, "1.0"}]), + + ?line ok = file:set_cwd(OldDir), + ok. + + +%% This test fails if wrong version numbers are seen in the relup file +%% or if any application is missing. This was triggered by OTP-1360. +check_relup(UpVsnL, DnVsnL) -> + {ok, [{_V1, [{_, _, Up}], [{_, _, Dn}]}]} = file:consult(relup), + [] = foldl(fun(X, Acc) -> + true = lists:member(X, Acc), + lists:delete(X, Acc) end, + UpVsnL, + [{App, Vsn} || {load_object_code,{App,Vsn,_}} <- Up]), + [] = foldl(fun(X, Acc) -> + true = lists:member(X, Acc), + lists:delete(X, Acc) end, + DnVsnL, + [{App, Vsn} || {load_object_code,{App,Vsn,_}} <- Dn]), + ok. + +check_restart_emulator() -> + {ok, [{_V1, [{_, _, Up}], [{_, _, Dn}]}]} = file:consult(relup), + restart_new_emulator = lists:last(Up), + restart_new_emulator = lists:last(Dn), + ok. + +%% make_relup +%% +no_appup_relup(suite) -> []; +no_appup_relup(doc) -> + ["Check that appup files may be missing, but only if we don't need them."]; +no_appup_relup(Config) when is_list(Config) -> + ?line {ok, OldDir} = file:get_cwd(), + + ?line {LatestDir,LatestName} = create_script(latest_small,Config), + ?line {_LatestDir0,LatestName0} = create_script(latest_small0,Config), + ?line {_LatestDir1,LatestName1} = create_script(latest_small1,Config), + + ?line DataDir = filename:absname(?copydir), + ?line P1 = [fname([DataDir, d_no_appup, lib, 'fe-3.1', ebin]), + fname([DataDir, lib, kernel, ebin]), + fname([DataDir, lib, stdlib, ebin])], + + ?line ok = file:set_cwd(LatestDir), + + %% Check that appup might be missing + ?line ok = + systools:make_relup(LatestName, [LatestName], [], [{path, P1}]), + ?line {ok,_, _, []} = + systools:make_relup(LatestName, [LatestName], [], + [silent, {path, P1}]), + + %% Check that appup might NOT be missing when we need it + ?line error = + systools:make_relup(LatestName, [LatestName0], [], [{path, P1}]), + ?line {error,_,{file_problem, {_,{error,{open,_,_}}}}} = + systools:make_relup(LatestName, [], [LatestName0], + [silent, {path, P1}]), + + %% Check that appups missing vsn traps + ?line P2 = [fname([DataDir, d_no_appup, lib, 'fe-2.1', ebin]), + fname([DataDir, lib, kernel, ebin]), + fname([DataDir, lib, stdlib, ebin])], + + ?line error = + systools:make_relup(LatestName0, [LatestName1], [], [{path, P2}]), + ?line {error,_,{no_relup, _, _, _}} = + systools:make_relup(LatestName0, [], [LatestName1], + [silent, {path, P2}]), + + ?line ok = file:set_cwd(OldDir), + ok. + +%% make_relup +%% +bad_appup_relup(suite) -> []; +bad_appup_relup(doc) -> + ["Check that badly written appup files are detected"]; +bad_appup_relup(Config) when is_list(Config) -> + ?line {ok, OldDir} = file:get_cwd(), + + ?line {LatestDir,LatestName} = create_script(latest_small,Config), + ?line {_LatestDir0,LatestName0} = create_script(latest_small0,Config), + + ?line DataDir = filename:absname(?copydir), + ?line N2 = [fname([DataDir, d_bad_appup, lib, 'fe-3.1', ebin]), + fname([DataDir, lib, kernel, ebin]), + fname([DataDir, lib, stdlib, ebin])], + + ?line ok = file:set_cwd(LatestDir), + + %% Check that bad appup is trapped + ?line error = + systools:make_relup(LatestName, [LatestName0], [], [{path, N2}]), + ?line {error,_,{file_problem, {_, {error, {parse,_, _}}}}} = + systools:make_relup(LatestName, [], [LatestName0], + [silent, {path, N2}]), + + ?line ok = file:set_cwd(OldDir), + ok. + +%% make_relup +%% +abnormal_relup(suite) -> []; +abnormal_relup(doc) -> + ["Check some abnormal cases"]; +abnormal_relup(Config) when is_list(Config) -> + ?line {ok, OldDir} = file:get_cwd(), + + ?line {LatestDir,LatestName} = create_script(latest0,Config), + ?line {_LatestDir1,LatestName1} = create_script(latest1,Config), + + %% Check wrong app vsn + ?line DataDir = filename:absname(?copydir), + ?line P = [fname([DataDir, d_bad_app_vsn, lib, 'db-2.1', ebin]), + fname([DataDir, d_bad_app_vsn, lib, 'fe-3.1', ebin]), + fname([DataDir, lib, kernel, ebin]), + fname([DataDir, lib, stdlib, ebin])], + + ?line ok = file:set_cwd(LatestDir), + + ?line error = systools:make_relup(LatestName, [LatestName1], [LatestName1], + [{path, P}]), + ?line R0 = systools:make_relup(LatestName, [LatestName1], [LatestName1], + [silent, {path, P}]), + ?line {error,systools_make, + [{error_reading,{db,{no_valid_version, + {{"should be","2.1"}, + {"found file", _, "2.0"}}}}}]} = R0, + ?line ok = file:set_cwd(OldDir), + ok. + + +%% Check that application start type is used in relup +app_start_type_relup(suite) -> + []; +app_start_type_relup(doc) -> + ["Release upgrade file with various application start types"]; +app_start_type_relup(Config) when is_list(Config) -> + ?line PrivDir = ?config(priv_dir, Config), + ?line {Dir1,Name1} = create_script(latest_app_start_type1,Config), + ?line {Dir2,Name2} = create_script(latest_app_start_type2,Config), + ?line Release1 = filename:join(Dir1,Name1), + ?line Release2 = filename:join(Dir2,Name2), + + ?line {ok, Release2Relup, systools_relup, []} = systools:make_relup(Release2, [Release1], [Release1], [{outdir, PrivDir}, silent]), + ?line {"2", [{"1",[], UpInstructions}], [{"1",[], DownInstructions}]} = Release2Relup, + %% ?t:format("Up: ~p",[UpInstructions]), + %% ?t:format("Dn: ~p",[DownInstructions]), + ?line [{load_object_code, {mnesia, _, _}}, + {load_object_code, {sasl, _, _}}, + {load_object_code, {webtool, _, _}}, + {load_object_code, {snmp, _, _}}, + {load_object_code, {xmerl, _, _}}, + point_of_no_return + | UpInstructionsT] = UpInstructions, + ?line true = lists:member({apply,{application,start,[mnesia,permanent]}}, UpInstructionsT), + ?line true = lists:member({apply,{application,start,[sasl,transient]}}, UpInstructionsT), + ?line true = lists:member({apply,{application,start,[webtool,temporary]}}, UpInstructionsT), + ?line true = lists:member({apply,{application,load,[snmp]}}, UpInstructionsT), + ?line false = lists:any(fun({apply,{application,_,[xmerl|_]}}) -> true; (_) -> false end, UpInstructionsT), + ?line [point_of_no_return | DownInstructionsT] = DownInstructions, + ?line true = lists:member({apply,{application,stop,[mnesia]}}, DownInstructionsT), + ?line true = lists:member({apply,{application,stop,[sasl]}}, DownInstructionsT), + ?line true = lists:member({apply,{application,stop,[webtool]}}, DownInstructionsT), + ?line true = lists:member({apply,{application,stop,[snmp]}}, DownInstructionsT), + ?line true = lists:member({apply,{application,stop,[xmerl]}}, DownInstructionsT), + ?line true = lists:member({apply,{application,unload,[mnesia]}}, DownInstructionsT), + ?line true = lists:member({apply,{application,unload,[sasl]}}, DownInstructionsT), + ?line true = lists:member({apply,{application,unload,[webtool]}}, DownInstructionsT), + ?line true = lists:member({apply,{application,unload,[snmp]}}, DownInstructionsT), + ?line true = lists:member({apply,{application,unload,[xmerl]}}, DownInstructionsT), + ok. + + +otp_6226(suite) -> + []; +otp_6226(doc) -> + ["{outdir,Dir} option for systools:make_script()"]; +otp_6226(Config) when is_list(Config) -> + PrivDir = ?privdir, + ?line {ok, OldDir} = file:get_cwd(), + + ?line {LatestDir, LatestName} = create_script(latest0,Config), + ?line {_LatestDir, LatestName1} = create_script(latest1,Config), + + ?line DataDir = filename:absname(?copydir), + ?line LibDir = fname([DataDir, d_normal, lib]), + ?line P = [fname([LibDir, 'db-2.1', ebin]), + fname([LibDir, 'db-1.0', ebin]), + fname([LibDir, 'fe-3.1', ebin]), + fname([DataDir, lib, kernel, ebin]), + fname([DataDir, lib, stdlib, ebin])], + + ?line ok = file:set_cwd(LatestDir), + + + %% Create an outdir1 directory + ?line ok = file:make_dir("outdir1"), + + %% ==== Now test systools:make_script ==== + %% a) badarg + ?line {'EXIT', {{badarg,[{outdir,outdir1}]}, _}} = + (catch systools:make_script(LatestName, [{outdir,outdir1}, + {path,P},silent])), + + %% b) absolute path + Outdir1 = filename:join(PrivDir, "outdir1"), + ?line {ok,_,[]} = systools:make_script(LatestName, [{outdir,Outdir1}, + {path,P},silent]), + ?line Script1 = filename:join(Outdir1, LatestName ++ ".script"), + ?line Boot1 = filename:join(Outdir1, LatestName ++ ".boot"), + ?line true = filelib:is_file(Script1), + ?line true = filelib:is_file(Boot1), + ?line ok = file:delete(Script1), + ?line ok = file:delete(Boot1), + + %% c) relative path + ?line {ok,_,[]} = systools:make_script(LatestName, [{outdir,"./outdir1"}, + {path,P},silent]), + ?line true = filelib:is_file(Script1), + ?line true = filelib:is_file(Boot1), + ?line ok = file:delete(Script1), + ?line ok = file:delete(Boot1), + + %% d) absolute but incorrect path + ?line Outdir2 = filename:join(PrivDir, "outdir2"), + ?line Script2 = filename:join(Outdir2, LatestName ++ ".script"), + ?line {error,_,{open,Script2,_}} = + systools:make_script(LatestName, [{outdir,Outdir2},{path,P},silent]), + + %% e) relative but incorrect path + ?line {error,_,{open,_,_}} = + systools:make_script(LatestName, [{outdir,"./outdir2"},{path,P},silent]), + + %% f) with .rel in another directory than cwd + ?line ok = file:set_cwd(Outdir1), + ?line {ok,_,[]} = systools:make_script(filename:join(PrivDir, LatestName), + [{outdir,"."},{path,P},silent]), + ?line true = filelib:is_file(LatestName ++ ".script"), + ?line true = filelib:is_file(LatestName ++ ".boot"), + ?line ok = file:delete(LatestName ++ ".script"), + ?line ok = file:delete(LatestName ++ ".boot"), + ?line ok = file:set_cwd(LatestDir), + + %% ==== Now test systools:make_tar ===== + ?line {ok,_,[]} = systools:make_script(LatestName, [{path,P},silent]), + %% a) badarg + ?line {'EXIT', {{badarg, [{outdir,outdir1}]}, _}} = + (catch systools:make_tar(LatestName,[{outdir,outdir1},{path,P},silent])), + + %% b) absolute path + ?line {ok,_,[]} = systools:make_tar(LatestName, [{outdir,Outdir1}, + {path,P},silent]), + ?line Tar1 = filename:join(Outdir1,LatestName++".tar.gz"), + ?line true = filelib:is_file(Tar1), + ?line ok = file:delete(Tar1), + + %% c) relative path + ?line {ok,_,[]} = systools:make_tar(LatestName, [{outdir,"./outdir1"}, + {path,P},silent]), + ?line true = filelib:is_file(Tar1), + ?line ok = file:delete(Tar1), + + %% d) absolute but incorrect path + ?line Tar2 = filename:join(Outdir2,LatestName++".tar.gz"), + ?line {error,_,{tar_error,{open,Tar2,{Tar2,enoent}}}} = + systools:make_tar(LatestName, [{outdir,Outdir2},{path,P},silent]), + + %% e) relative but incorrect path + ?line {error,_,{tar_error,{open,_,_}}} = + systools:make_tar(LatestName, [{outdir,"./outdir2"},{path,P},silent]), + + %% f) with .rel in another directory than cwd + ?line ok = file:set_cwd(Outdir1), + ?line {ok,_,[]} = systools:make_tar(filename:join(PrivDir, LatestName), + [{outdir,"."},{path,P},silent]), + ?line true = filelib:is_file(Tar1), + ?line ok = file:set_cwd(LatestDir), + + %% ===== Now test systools:make_relup ===== + %% a) badarg + ?line {'EXIT', {{badarg, [{outdir,outdir1}]}, _}} = + (catch systools:make_relup(LatestName,[LatestName1],[LatestName1], + [{outdir,outdir1}, + {path,P},silent])), + + %% b) absolute path + Relup = filename:join(Outdir1, "relup"), + ?line {ok,_,_,[]} = systools:make_relup(LatestName,[LatestName1],[LatestName1], + [{outdir,Outdir1}, + {path,P},silent]), + ?line true = filelib:is_file(Relup), + ?line ok = file:delete(Relup), + + %% c) relative path + ?line {ok,_,_,[]} = systools:make_relup(LatestName,[LatestName1],[LatestName1], + [{outdir,"./outdir1"}, + {path,P},silent]), + ?line true = filelib:is_file(Relup), + ?line ok = file:delete(Relup), + + %% d) absolute but incorrect path + ?line {error,_,{file_problem,{"relup",enoent}}} = + systools:make_relup(LatestName,[LatestName1],[LatestName1], + [{outdir,Outdir2},{path,P},silent]), + + %% e) relative but incorrect path + ?line {error,_,{file_problem,{"relup",enoent}}} = + systools:make_relup(LatestName,[LatestName1],[LatestName1], + [{outdir,"./outdir2"},{path,P},silent]), + + %% f) with .rel in another directory than cwd + %% -- not necessary to test since relup by default is placed in + %% cwd, not in the same directory as the .rel file -- + + %% Change back to previous working directory + ?line ok = file:set_cwd(OldDir), + ok. + + +%%%%%% +%%%%%% Utilities +%%%%%% + +check_script_path(RelName) -> + {ok, [Conts]} = read_script_file(RelName), + {script, {_, _}, ListOfThings} = Conts, + case lists:keysearch(path, 1, ListOfThings) of + {value, {path, [$$,$R,$O,$O,$T | _]}} -> %"$ROOT..." + false; + _ -> ok + end. + +check_var_script_file(VarDirs, NoExistDirs, RelName) -> + {ok, [Conts]} = read_script_file(RelName), + {script, {_, _}, ListOfThings} = Conts, + AllPaths = lists:append(lists:map(fun({path, P}) -> P; + (_) -> [] + end, + ListOfThings)), + case lists:filter(fun(VarDir) -> lists:member(VarDir, AllPaths) end, + VarDirs) of + VarDirs -> + ok; + _ -> + test_server:fail("All variable dirs not in generated script") + end, + case lists:filter(fun(NoExistDir) -> lists:member(NoExistDir, AllPaths) end, + NoExistDirs) of + [] -> + ok; + _ -> + test_server:fail("Unexpected dirs in generated script") + end. + +check_include_script(RelName, ExpectedLoad, ExpectedStart) -> + {ok, [Conts]} = read_script_file(RelName), + {script, {_, _}, ListOfThings} = Conts, + + %% Check that the applications are loaded in given order ! + ActualLoad = + [App || {apply,{application,load,[{application,App,_}]}} <- ListOfThings, + App=/=kernel, + App=/=stdlib], + + if ActualLoad =:= ExpectedLoad -> ok; + true -> test_server:fail({bad_load_order, ActualLoad, ExpectedLoad}) + end, + + %% Check that applications are started in given order ! + ActualStart = + [App || {apply,{application,start_boot,[App|_]}} <- ListOfThings, + App =/= kernel, + App =/= stdlib], + + if ActualStart =:= ExpectedStart -> ok; + true -> test_server:fail({bad_start_order, ActualStart,ExpectedStart}) + end, + + ok. + +read_script_file(RelName) -> + file:consult(RelName ++ ".script"). + +check_var_tar(Variable, RelName) -> + Expected = tar_name(Variable), + case check_tar(Expected,RelName) of + ok -> + ok; + {error, {erroneous_tar_file, _, missing, _}} -> + {error, {not_generated, Expected}} + end. + +exists_tar_file(Name) -> + File = tar_name(Name), + case filelib:is_regular(File) of + true -> + ok = file:delete(File), + true; + _ -> + false + end. + +%% Take a snap of the generated tar file and check if a certain +%% file is included. +%% This ensures at least that the tar file is generated. +check_tar(File, RelName) -> + TarContents = tar_contents(RelName), + case lists:member(File,TarContents) of + true -> ok; + _ -> {error, {erroneous_tar_file, tar_name(RelName), missing, File}} + end. + +%% Check that the given files exist in the tar file, and that they are +%% not symlinks +check_tar_regular(PrivDir, Files, RelName) -> + TmpDir = fname(PrivDir,tmp), + ok = file:make_dir(TmpDir), + ok = erl_tar:extract(tar_name(RelName), + [{files,Files},{cwd,TmpDir},compressed]), + R = lists:foldl(fun(File,Acc) -> + case file:read_link_info(fname(TmpDir,File)) of + {ok,#file_info{type=regular}} -> + Acc; + {ok,#file_info{type=Other}} -> + [{File,Other}|Acc]; + _ -> + [{File,missing}|Acc] + end + end, + [], + Files), + delete_tree(TmpDir), + case R of + [] -> + ok; + NotThere -> + {error,{erroneous_tar_file,tar_name(RelName),NotThere}} + end. + +delete_tree(Dir) -> + case filelib:is_dir(Dir) of + true -> + {ok,Files} = file:list_dir(Dir), + lists:foreach(fun(File) -> delete_tree(filename:join(Dir,File)) end, + Files), + file:del_dir(Dir); + false -> + ok = file:delete(Dir) + end. + +tar_contents(Name) -> + {ok, Cont} = erl_tar:table(Name ++ ".tar.gz", [compressed]), + Cont. + +tar_name(Name) -> + Name ++ ".tar.gz". + +create_script(latest,Config) -> + ?line PrivDir = ?privdir, + ?line Name = fname(PrivDir, latest), + ?line Apps = application_controller:which_applications(), + ?line {value,{_,_,KernelVer}} = lists:keysearch(kernel,1,Apps), + ?line {value,{_,_,StdlibVer}} = lists:keysearch(stdlib,1,Apps), + ?line {ok,Fd} = file:open(Name++".rel",write), + ?line io:format(Fd, + "{release, {\"Test release 3\", \"LATEST\"}, \n" + " {erts, \"4.4\"}, \n" + " [{kernel, \"~s\"}, {stdlib, \"~s\"}, \n" + " {db, \"2.1\"}, {fe, \"3.1\"}]}.\n", + [KernelVer,StdlibVer]), + ?line ok = file:close(Fd), + {filename:dirname(Name), filename:basename(Name)}; +create_script(latest_no_mod_vsn,Config) -> + ?line PrivDir = ?privdir, + ?line Name = fname(PrivDir, latest), + ?line Apps = application_controller:which_applications(), + ?line {value,{_,_,KernelVer}} = lists:keysearch(kernel,1,Apps), + ?line {value,{_,_,StdlibVer}} = lists:keysearch(stdlib,1,Apps), + ?line {ok,Fd} = file:open(Name++".rel",write), + ?line io:format(Fd, + "{release, {\"Test release 3\", \"LATESTNOMOD\"}, \n" + " {erts, \"4.4\"}, \n" + " [{kernel, \"~s\"}, {stdlib, \"~s\"}, \n" + " {db, \"3.1\"}, {fe, \"3.1\"}]}.\n", + [KernelVer,StdlibVer]), + ?line ok = file:close(Fd), + {filename:dirname(Name), filename:basename(Name)}; +create_script(latest0,Config) -> + ?line PrivDir = ?privdir, + ?line Name = fname(PrivDir, 'latest-1'), + ?line {ok,Fd} = file:open(Name++".rel",write), + ?line io:format(Fd, + "{release, {\"Test release 2\", \"LATEST0\"}, \n" + " {erts, \"4.4\"}, \n" + " [{kernel, \"1.0\"}, {stdlib, \"1.0\"}, \n" + " {db, \"2.1\"}, {fe, \"3.1\"}]}.\n", + []), + ?line ok = file:close(Fd), + {filename:dirname(Name), filename:basename(Name)}; +create_script(latest1,Config) -> + ?line PrivDir = ?privdir, + ?line Name = fname(PrivDir, latest), + ?line {ok,Fd} = file:open(Name++".rel",write), + ?line io:format(Fd, + "{release, {\"Test release 2\", \"LATEST1\"}, \n" + " {erts, \"4.4\"}, \n" + " [{kernel, \"1.0\"}, {stdlib, \"1.0\"}, \n" + " {db, \"1.0\"}, {fe, \"3.1\"}]}.\n", + []), + ?line ok = file:close(Fd), + {filename:dirname(Name), filename:basename(Name)}; +create_script(latest2,Config) -> + ?line PrivDir = ?privdir, + ?line Name = fname(PrivDir, 'latest-2'), + ?line {ok,Fd} = file:open(Name++".rel",write), + ?line io:format(Fd, + "{release, {\"Test release 1\", \"LATEST2\"}, \n" + " {erts, \"4.3\"}, \n" + " [{kernel, \"1.0\"}, {stdlib, \"1.0\"}, \n" + " {db, \"1.0\"}, {fe, \"2.1\"}]}.\n", + []), + ?line ok = file:close(Fd), + {filename:dirname(Name), filename:basename(Name)}; +create_script(latest_small,Config) -> + ?line PrivDir = ?privdir, + ?line Name = fname(PrivDir, 'latest-small'), + ?line {ok,Fd} = file:open(Name++".rel",write), + ?line io:format(Fd, + "{release, {\"Test release 2\", \"LATEST_SMALL\"}, \n" + " {erts, \"4.4\"}, \n" + " [{kernel, \"1.0\"}, {stdlib, \"1.0\"}, \n" + " {fe, \"3.1\"}]}.\n", + []), + ?line ok = file:close(Fd), + {filename:dirname(Name), filename:basename(Name)}; +create_script(latest_small0,Config) -> %Differs in fe vsn + ?line PrivDir = ?privdir, + ?line Name = fname(PrivDir, 'latest-small0'), + ?line {ok,Fd} = file:open(Name++".rel",write), + ?line io:format(Fd, + "{release, {\"Test release 2\", \"LATEST_SMALL0\"}, \n" + " {erts, \"4.4\"}, \n" + " [{kernel, \"1.0\"}, {stdlib, \"1.0\"}, \n" + " {fe, \"2.1\"}]}.\n", + []), + ?line ok = file:close(Fd), + {filename:dirname(Name), filename:basename(Name)}; +create_script(latest_small1,Config) -> + ?line PrivDir = ?privdir, + ?line Name = fname(PrivDir, 'latest-small1'), + ?line {ok,Fd} = file:open(Name++".rel",write), + ?line io:format(Fd, + "{release, {\"Test release 2\", \"LATEST_SMALL1\"}, \n" + " {erts, \"4.4\"}, \n" + " [{kernel, \"1.0\"}, {stdlib, \"1.0\"}, \n" + " {fe, \"500.18.7\"}]}.\n", + []), + ?line ok = file:close(Fd), + {filename:dirname(Name), filename:basename(Name)}; +create_script(latest_nokernel,Config) -> + ?line PrivDir = ?privdir, + ?line Name = fname(PrivDir, 'latest-nokernel'), + ?line {ok,Fd} = file:open(Name++".rel",write), + ?line io:format(Fd, + "{release, {\"Test release 3\", \"LATEST_NOKERNEL\"}, \n" + " {erts, \"4.4\"}, \n" + " [{db, \"2.1\"}, {fe, \"3.1\"}]}.\n", + []), + ?line ok = file:close(Fd), + {filename:dirname(Name), filename:basename(Name)}; +create_script(latest_app_start_type1,Config) -> + ?line PrivDir = ?privdir, + ?line Name = fname(PrivDir, latest_app_start_type1), + ?line ErtsVer = erlang:system_info(version), + ?line Apps = application_controller:which_applications(), + ?line {value,{_,_,KernelVer}} = lists:keysearch(kernel,1,Apps), + ?line {value,{_,_,StdlibVer}} = lists:keysearch(stdlib,1,Apps), + ?line {ok,Fd} = file:open(Name++".rel",write), + ?line RelfileContent = + {release,{"Test release", "1"}, + {erts,ErtsVer}, + [{kernel,KernelVer}, + {stdlib,StdlibVer}]}, + ?line io:format(Fd,"~p.~n",[RelfileContent]), + ?line ok = file:close(Fd), + {filename:dirname(Name), filename:basename(Name)}; +create_script(latest_app_start_type2,Config) -> + ?line PrivDir = ?privdir, + ?line Name = fname(PrivDir, latest_app_start_type2), + ?line ErtsVer = erlang:system_info(version), + ?line Apps = application_controller:which_applications(), + ?line {value,{_,_,KernelVer}} = lists:keysearch(kernel,1,Apps), + ?line {value,{_,_,StdlibVer}} = lists:keysearch(stdlib,1,Apps), + ?line OtherApps = [{mnesia,permanent}, + {sasl,transient}, + {webtool,temporary}, + {snmp,load}, + {xmerl,none}], + ?line lists:foreach(fun({App,_}) -> application:load(App) end, + OtherApps), + ?line Loaded = application:loaded_applications(), + ?line OtherAppsRel = + lists:map(fun({App,StartType}) -> + {_,_,Ver} = lists:keyfind(App,1,Loaded), + {App,Ver,StartType} + end, + OtherApps), + ?line {ok,Fd} = file:open(Name++".rel",write), + ?line RelfileContent = + {release,{"Test release", "2"}, + {erts,ErtsVer}, + [{kernel,KernelVer}, + {stdlib,StdlibVer} | OtherAppsRel]}, + ?line io:format(Fd,"~p.~n",[RelfileContent]), + ?line ok = file:close(Fd), + {filename:dirname(Name), filename:basename(Name)}. + +create_include_files(inc1, Config) -> + ?line PrivDir = ?privdir, + ?line Name = fname(PrivDir, inc1), + create_apps(PrivDir), + + ?line Apps = application_controller:which_applications(), + ?line {value,{_,_,KernelVer}} = lists:keysearch(kernel,1,Apps), + ?line {value,{_,_,StdlibVer}} = lists:keysearch(stdlib,1,Apps), + + Rel = "{release, {\"test\",\"R1A\"}, {erts, \"45\"},\n" + " [{kernel, \"" ++ KernelVer ++ "\"}, {stdlib, \"" + ++ StdlibVer ++ "\"},\n" + " {t6, \"1.0\"}, {t5, \"1.0\"}, \n" + " {t4, \"1.0\"}, {t3, \"1.0\"}, {t2, \"1.0\"}, \n" + " {t1, \"1.0\"}]}.\n", + file:write_file(Name ++ ".rel", list_to_binary(Rel)), + {filename:dirname(Name), filename:basename(Name)}; + +create_include_files(inc2, Config) -> + ?line PrivDir = ?privdir, + ?line Name = fname(PrivDir, inc2), + create_apps(PrivDir), + + ?line Apps = application_controller:which_applications(), + ?line {value,{_,_,KernelVer}} = lists:keysearch(kernel,1,Apps), + ?line {value,{_,_,StdlibVer}} = lists:keysearch(stdlib,1,Apps), + + %% t6 does not include t5 ! + Rel = "{release, {\"test\",\"R1A\"}, {erts, \"45\"},\n" + " [{kernel, \"" ++ KernelVer ++ "\"}, {stdlib, \"" + ++ StdlibVer ++ "\"},\n" + " {t6, \"1.0\", [t4]}, {t5, \"1.0\"}, \n" + " {t4, \"1.0\"}, {t3, \"1.0\"}, {t2, \"1.0\"}, \n" + " {t1, \"1.0\"}]}.\n", + file:write_file(Name ++ ".rel", list_to_binary(Rel)), + {filename:dirname(Name), filename:basename(Name)}; + +create_include_files(inc3, Config) -> + ?line PrivDir = ?privdir, + ?line Name = fname(PrivDir, inc3), + create_apps(PrivDir), + + ?line Apps = application_controller:which_applications(), + ?line {value,{_,_,KernelVer}} = lists:keysearch(kernel,1,Apps), + ?line {value,{_,_,StdlibVer}} = lists:keysearch(stdlib,1,Apps), + + %% t3 does not include t2 ! + Rel = "{release, {\"test\",\"R1A\"}, {erts, \"45\"},\n" + " [{kernel, \"" ++ KernelVer ++ "\"}, {stdlib, \"" + ++ StdlibVer ++ "\"},\n" + " {t6, \"1.0\"}, {t5, \"1.0\"}, \n" + " {t4, \"1.0\"}, {t3, \"1.0\", []}, {t2, \"1.0\"}, \n" + " {t1, \"1.0\"}]}.\n", + file:write_file(Name ++ ".rel", list_to_binary(Rel)), + {filename:dirname(Name), filename:basename(Name)}; + +create_include_files(inc4, Config) -> + ?line PrivDir = ?privdir, + ?line Name = fname(PrivDir, inc4), + create_apps(PrivDir), + + ?line Apps = application_controller:which_applications(), + ?line {value,{_,_,KernelVer}} = lists:keysearch(kernel,1,Apps), + ?line {value,{_,_,StdlibVer}} = lists:keysearch(stdlib,1,Apps), + + %% t3 does not include t2 ! + %% t6 does not include t5 ! + Rel = "{release, {\"test\",\"R1A\"}, {erts, \"45\"},\n" + " [{kernel, \"" ++ KernelVer ++ "\"}, {stdlib, \"" + ++ StdlibVer ++ "\"},\n" + " {t6, \"1.0\", [t4]}, {t5, \"1.0\"}, \n" + " {t4, \"1.0\"}, {t3, \"1.0\", []}, {t2, \"1.0\"}, \n" + " {t1, \"1.0\"}]}.\n", + file:write_file(Name ++ ".rel", list_to_binary(Rel)), + {filename:dirname(Name), filename:basename(Name)}; + +create_include_files(inc5, Config) -> + ?line PrivDir = ?privdir, + ?line Name = fname(PrivDir, inc5), + create_apps(PrivDir), + + ?line Apps = application_controller:which_applications(), + ?line {value,{_,_,KernelVer}} = lists:keysearch(kernel,1,Apps), + ?line {value,{_,_,StdlibVer}} = lists:keysearch(stdlib,1,Apps), + + %% t6 does not include t5 ! + %% exclude t5. + Rel = "{release, {\"test\",\"R1A\"}, {erts, \"45\"},\n" + " [{kernel, \"" ++ KernelVer ++ "\"}, {stdlib, \"" + ++ StdlibVer ++ "\"},\n" + " {t6, \"1.0\", [t4]}, \n" + " {t4, \"1.0\"}, {t3, \"1.0\", []}, {t2, \"1.0\"}, \n" + " {t1, \"1.0\"}]}.\n", + file:write_file(Name ++ ".rel", list_to_binary(Rel)), + {filename:dirname(Name), filename:basename(Name)}; + +create_include_files(inc6, Config) -> + ?line PrivDir = ?privdir, + ?line Name = fname(PrivDir, inc6), + create_apps(PrivDir), + + ?line Apps = application_controller:which_applications(), + ?line {value,{_,_,KernelVer}} = lists:keysearch(kernel,1,Apps), + ?line {value,{_,_,StdlibVer}} = lists:keysearch(stdlib,1,Apps), + + %% t3 does include non existing t2 ! + Rel = "{release, {\"test\",\"R1A\"}, {erts, \"45\"},\n" + " [{kernel, \"" ++ KernelVer ++ "\"}, {stdlib, \"" + ++ StdlibVer ++ "\"},\n" + " {t6, \"1.0\"}, {t5, \"1.0\"}, \n" + " {t4, \"1.0\"}, {t3, \"1.0\"}, \n" + " {t1, \"1.0\"}]}.\n", + file:write_file(Name ++ ".rel", list_to_binary(Rel)), + {filename:dirname(Name), filename:basename(Name)}; + +create_include_files(inc7, Config) -> + ?line PrivDir = ?privdir, + ?line Name = fname(PrivDir, inc7), + create_apps(PrivDir), + create_app(t7, PrivDir), + + ?line Apps = application_controller:which_applications(), + ?line {value,{_,_,KernelVer}} = lists:keysearch(kernel,1,Apps), + ?line {value,{_,_,StdlibVer}} = lists:keysearch(stdlib,1,Apps), + + %% t7 and t6 does include t5 ! + Rel = "{release, {\"test\",\"R1A\"}, {erts, \"45\"},\n" + " [{kernel, \"" ++ KernelVer ++ "\"}, {stdlib, \"" + ++ StdlibVer ++ "\"},\n" + " {t7, \"1.0\"}, {t6, \"1.0\"}, {t5, \"1.0\"}, \n" + " {t4, \"1.0\"}, {t3, \"1.0\"}, {t2, \"1.0\"}, \n" + " {t1, \"1.0\"}]}.\n", + file:write_file(Name ++ ".rel", list_to_binary(Rel)), + {filename:dirname(Name), filename:basename(Name)}; + +create_include_files(inc8, Config) -> + ?line PrivDir = ?privdir, + ?line Name = fname(PrivDir, inc8), + create_circular_apps(PrivDir), + + ?line Apps = application_controller:which_applications(), + ?line {value,{_,_,KernelVer}} = lists:keysearch(kernel,1,Apps), + ?line {value,{_,_,StdlibVer}} = lists:keysearch(stdlib,1,Apps), + + %% t8 uses t9 and t10 includes t9 ! + Rel = "{release, {\"test\",\"R1A\"}, {erts, \"45\"},\n" + " [{kernel, \"" ++ KernelVer ++ "\"}, {stdlib, \"" + ++ StdlibVer ++ "\"},\n" + " {t8, \"1.0\"}, {t9, \"1.0\"}, {t10, \"1.0\"}]}.\n", + file:write_file(Name ++ ".rel", list_to_binary(Rel)), + {filename:dirname(Name), filename:basename(Name)}; + +create_include_files(inc9, Config) -> + ?line PrivDir = ?privdir, + ?line Name = fname(PrivDir, inc9), + create_circular_apps(PrivDir), + + ?line Apps = application_controller:which_applications(), + ?line {value,{_,_,KernelVer}} = lists:keysearch(kernel,1,Apps), + ?line {value,{_,_,StdlibVer}} = lists:keysearch(stdlib,1,Apps), + + %% t8 uses t9, t9 uses t10 and t10 includes t8 ==> circular !! + Rel = "{release, {\"test\",\"R1A\"}, {erts, \"45\"},\n" + " [{kernel, \"" ++ KernelVer ++ "\"}, {stdlib, \"" + ++ StdlibVer ++ "\"},\n" + " {t8, \"1.0\"}, {t9, \"1.0\"}, {t10, \"1.0\", [t8]}]}.\n", + file:write_file(Name ++ ".rel", list_to_binary(Rel)), + {filename:dirname(Name), filename:basename(Name)}; + +create_include_files(inc10, Config) -> + ?line PrivDir = ?privdir, + ?line Name = fname(PrivDir, inc10), + create_circular_apps(PrivDir), + + ?line Apps = application_controller:which_applications(), + ?line {value,{_,_,KernelVer}} = lists:keysearch(kernel,1,Apps), + ?line {value,{_,_,StdlibVer}} = lists:keysearch(stdlib,1,Apps), + + %% t9 tries to include not specified (in .app file) application ! + Rel = "{release, {\"test\",\"R1A\"}, {erts, \"45\"},\n" + " [{kernel, \"" ++ KernelVer ++ "\"}, {stdlib, \"" + ++ StdlibVer ++ "\"},\n" + " {t8, \"1.0\"}, {t9, \"1.0\", [t7]}, {t10, \"1.0\"}]}.\n", + file:write_file(Name ++ ".rel", list_to_binary(Rel)), + {filename:dirname(Name), filename:basename(Name)}; + +create_include_files(inc11, Config) -> + ?line PrivDir = ?privdir, + ?line Name = fname(PrivDir, inc11), + create_apps2(PrivDir), + + ?line Apps = application_controller:which_applications(), + ?line {value,{_,_,KernelVer}} = lists:keysearch(kernel,1,Apps), + ?line {value,{_,_,StdlibVer}} = lists:keysearch(stdlib,1,Apps), + + Rel = "{release, {\"test\",\"R1A\"}, {erts, \"45\"},\n" + " [{kernel, \"" ++ KernelVer ++ "\"}, {stdlib, \"" + ++ StdlibVer ++ "\"},\n" + " {t11, \"1.0\"}, \n" + " {t12, \"1.0\"}, \n" + " {t13, \"1.0\"}]}.\n", + file:write_file(Name ++ ".rel", list_to_binary(Rel)), + {filename:dirname(Name), filename:basename(Name)}; + +create_include_files(otp_3065, Config) -> + ?line PrivDir = ?privdir, + ?line Name = fname(PrivDir, otp_3065), + create_apps_3065(PrivDir), + + ?line Apps = application_controller:which_applications(), + ?line {value,{_,_,KernelVer}} = lists:keysearch(kernel,1,Apps), + ?line {value,{_,_,StdlibVer}} = lists:keysearch(stdlib,1,Apps), + + Rel = "{release, {\"test\",\"R1A\"}, {erts, \"45\"},\n" + " [{kernel, \"" ++ KernelVer ++ "\"}, {stdlib, \"" + ++ StdlibVer ++ "\"},\n" + " {chAts, \"1.0\"}, {aa12, \"1.0\"}, \n" + " {chTraffic, \"1.0\"}]}.\n", + file:write_file(Name ++ ".rel", list_to_binary(Rel)), + {filename:dirname(Name), filename:basename(Name)}. + +create_apps(Dir) -> + T1 = "{application, t1,\n" + " [{vsn, \"1.0\"},\n" + " {description, \"test\"},\n" + " {modules, []},\n" + " {applications, [kernel, stdlib]},\n" + " {registered, []}]}.\n", + file:write_file(fname(Dir, 't1.app'), list_to_binary(T1)), + + T2 = "{application, t2,\n" + " [{vsn, \"1.0\"},\n" + " {description, \"test\"},\n" + " {modules, []},\n" + " {applications, [t1]},\n" + " {registered, []}]}.\n", + file:write_file(fname(Dir, 't2.app'), list_to_binary(T2)), + + T3 = "{application, t3,\n" + " [{vsn, \"1.0\"},\n" + " {description, \"test\"},\n" + " {modules, []},\n" + " {applications, []},\n" + " {included_applications, [t2]},\n" + " {registered, []}]}.\n", + file:write_file(fname(Dir, 't3.app'), list_to_binary(T3)), + + T4 = "{application, t4,\n" + " [{vsn, \"1.0\"},\n" + " {description, \"test\"},\n" + " {modules, []},\n" + " {applications, [t3]},\n" + " {included_applications, []},\n" + " {registered, []}]}.\n", + file:write_file(fname(Dir, 't4.app'), list_to_binary(T4)), + + T5 = "{application, t5,\n" + " [{vsn, \"1.0\"},\n" + " {description, \"test\"},\n" + " {modules, []},\n" + " {applications, [t3]},\n" + " {included_applications, []},\n" + " {registered, []}]}.\n", + file:write_file(fname(Dir, 't5.app'), list_to_binary(T5)), + + T6 = "{application, t6,\n" + " [{vsn, \"1.0\"},\n" + " {description, \"test\"},\n" + " {modules, []},\n" + " {applications, []},\n" + " {included_applications, [t4, t5]},\n" + " {registered, []}]}.\n", + file:write_file(fname(Dir, 't6.app'), list_to_binary(T6)). + +create_app(t7, Dir) -> + T7 = "{application, t7,\n" + " [{vsn, \"1.0\"},\n" + " {description, \"test\"},\n" + " {modules, []},\n" + " {applications, []},\n" + " {included_applications, [t5]},\n" + " {registered, []}]}.\n", + file:write_file(fname(Dir, 't7.app'), list_to_binary(T7)). + +create_circular_apps(Dir) -> + T8 = "{application, t8,\n" + " [{vsn, \"1.0\"},\n" + " {description, \"test\"},\n" + " {modules, []},\n" + " {applications, [t9]},\n" + " {included_applications, []},\n" + " {registered, []}]}.\n", + file:write_file(fname(Dir, 't8.app'), list_to_binary(T8)), + + T9 = "{application, t9,\n" + " [{vsn, \"1.0\"},\n" + " {description, \"test\"},\n" + " {modules, []},\n" + " {applications, [t10]},\n" + " {included_applications, []},\n" + " {registered, []}]}.\n", + file:write_file(fname(Dir, 't9.app'), list_to_binary(T9)), + + T10 = "{application, t10,\n" + " [{vsn, \"1.0\"},\n" + " {description, \"test\"},\n" + " {modules, []},\n" + " {applications, []},\n" + " {included_applications, [t8, t9]},\n" + " {registered, []}]}.\n", + file:write_file(fname(Dir, 't10.app'), list_to_binary(T10)). + +create_apps2(Dir) -> + T11 = "{application, t11,\n" + " [{vsn, \"1.0\"},\n" + " {description, \"test\"},\n" + " {modules, []},\n" + " {applications, []},\n" + " {included_applications, [t13]},\n" + " {registered, []}]}.\n", + file:write_file(fname(Dir, 't11.app'), list_to_binary(T11)), + + T12 = "{application, t12,\n" + " [{vsn, \"1.0\"},\n" + " {description, \"test\"},\n" + " {modules, []},\n" + " {applications, [t11]},\n" + " {registered, []}]}.\n", + file:write_file(fname(Dir, 't12.app'), list_to_binary(T12)), + + T13 = "{application, t13,\n" + " [{vsn, \"1.0\"},\n" + " {description, \"test\"},\n" + " {modules, []},\n" + " {applications, []},\n" + " {included_applications, []},\n" + " {registered, []}]}.\n", + file:write_file(fname(Dir, 't13.app'), list_to_binary(T13)). + + + +create_apps_3065(Dir) -> + T11 = "{application, chTraffic,\n" + " [{vsn, \"1.0\"},\n" + " {description, \"test\"},\n" + " {modules, []},\n" + " {applications, []},\n" + " {included_applications, [chAts]},\n" + " {registered, []}]}.\n", + file:write_file(fname(Dir, 'chTraffic.app'), list_to_binary(T11)), + + T12 = "{application, chAts,\n" + " [{vsn, \"1.0\"},\n" + " {description, \"test\"},\n" + " {modules, []},\n" + " {applications, []},\n" + " {included_applications, [aa12]},\n" + " {registered, []}]}.\n", + file:write_file(fname(Dir, 'chAts.app'), list_to_binary(T12)), + + T13 = "{application, aa12,\n" + " [{vsn, \"1.0\"},\n" + " {description, \"test\"},\n" + " {modules, []},\n" + " {applications, [chAts]},\n" + " {included_applications, []},\n" + " {registered, []}]}.\n", + file:write_file(fname(Dir, 'aa12.app'), list_to_binary(T13)). + +fname(N) -> + filename:join(N). + +fname(Dir, Basename) -> + filename:join(Dir, Basename). diff --git a/lib/sasl/test/systools_SUITE_data/d_bad_app_vsn/lib/db-2.1/ebin/db.app b/lib/sasl/test/systools_SUITE_data/d_bad_app_vsn/lib/db-2.1/ebin/db.app new file mode 100644 index 0000000000..d375768b99 --- /dev/null +++ b/lib/sasl/test/systools_SUITE_data/d_bad_app_vsn/lib/db-2.1/ebin/db.app @@ -0,0 +1,8 @@ +{application, db, + [{description, "ERICSSON NR FOR DB"}, + {vsn, "2.0"}, + {modules, [{db1, "1.0"}, {db2, "1.0"}]}, + {registered, []}, + {applications, []}, + {env, []}, + {start, {db1, start, []}}]}. diff --git a/lib/sasl/test/systools_SUITE_data/d_bad_app_vsn/lib/db-2.1/ebin/db.appup b/lib/sasl/test/systools_SUITE_data/d_bad_app_vsn/lib/db-2.1/ebin/db.appup new file mode 100644 index 0000000000..621838f0b4 --- /dev/null +++ b/lib/sasl/test/systools_SUITE_data/d_bad_app_vsn/lib/db-2.1/ebin/db.appup @@ -0,0 +1,20 @@ +%% +%% Release upgrade script for db (data base) +%% + +{ + "2.1", +%%% Upgrade from: + [ + {"1.0", [{update, db1, soft, soft_purge, soft_purge, []}, + {update, db2, soft, soft_purge, soft_purge, [db1]}]}, + {"1.1", [{update, db1, soft, soft_purge, soft_purge, []}, + {update, db2, soft, soft_purge, soft_purge, [db1]}]} + ], + +%%% Downgrade to: + [ + {"1.0", [{update, db1, soft, soft_purge, soft_purge, []}, + {update, db2, soft, soft_purge, soft_purge, [db1]}]} + ] +}. diff --git a/lib/sasl/test/systools_SUITE_data/d_bad_app_vsn/lib/db-2.1/src/db1.erl b/lib/sasl/test/systools_SUITE_data/d_bad_app_vsn/lib/db-2.1/src/db1.erl new file mode 100644 index 0000000000..a17640316e --- /dev/null +++ b/lib/sasl/test/systools_SUITE_data/d_bad_app_vsn/lib/db-2.1/src/db1.erl @@ -0,0 +1,2 @@ +-module(db2). +-vsn("1.0"). diff --git a/lib/sasl/test/systools_SUITE_data/d_bad_app_vsn/lib/db-2.1/src/db2.erl b/lib/sasl/test/systools_SUITE_data/d_bad_app_vsn/lib/db-2.1/src/db2.erl new file mode 100644 index 0000000000..a17640316e --- /dev/null +++ b/lib/sasl/test/systools_SUITE_data/d_bad_app_vsn/lib/db-2.1/src/db2.erl @@ -0,0 +1,2 @@ +-module(db2). +-vsn("1.0"). diff --git a/lib/sasl/test/systools_SUITE_data/d_bad_app_vsn/lib/fe-3.1/ebin/fe.app b/lib/sasl/test/systools_SUITE_data/d_bad_app_vsn/lib/fe-3.1/ebin/fe.app new file mode 100644 index 0000000000..d3bd85cda6 --- /dev/null +++ b/lib/sasl/test/systools_SUITE_data/d_bad_app_vsn/lib/fe-3.1/ebin/fe.app @@ -0,0 +1,8 @@ +{application, fe, + [{description, "ERICSSON NR FOR FE"}, + {vsn, "3.1"}, + {modules, [{fe1, "1.0"}, {fe2, "1.0"}, {fe3, "2.0"}]}, + {registered, []}, + {applications, []}, + {env, []}, + {start, {fe2, start, []}}]}. diff --git a/lib/sasl/test/systools_SUITE_data/d_bad_app_vsn/lib/fe-3.1/ebin/fe.appup b/lib/sasl/test/systools_SUITE_data/d_bad_app_vsn/lib/fe-3.1/ebin/fe.appup new file mode 100644 index 0000000000..4ab13c1bae --- /dev/null +++ b/lib/sasl/test/systools_SUITE_data/d_bad_app_vsn/lib/fe-3.1/ebin/fe.appup @@ -0,0 +1,27 @@ +%% +%% Release upgrade script for fe (front end) +%% + +{ + "3.1", +%%% Upgrade from: + [ + {"2.1.1", [{update, fe1, soft, soft_purge, soft_purge, []}, + {update, fe2, soft, soft_purge, soft_purge, [fe1]}, + {update, fe3, {{advanced, extra}, soft_purge, soft_purge, + [fe1, fe2]} + ]}, + + {"2.1", [{update, fe1, soft, soft_purge, soft_purge, []}, + {update, fe2, soft, soft_purge, soft_purge, [fe1]}, + {update, fe3, {advanced, extra}, soft_purge, soft_purge, [fe1, fe2]} + ]} + ], + +%%% Downgrade to: + [ + {"2.1", [{update, fe2, soft, soft_purge, soft_purge, []}, + {update, fe3, {advanced, extra}, soft_purge, soft_purge, [fe2]} + ]} + ] +}. diff --git a/lib/sasl/test/systools_SUITE_data/d_bad_app_vsn/lib/fe-3.1/src/fe1.erl b/lib/sasl/test/systools_SUITE_data/d_bad_app_vsn/lib/fe-3.1/src/fe1.erl new file mode 100644 index 0000000000..aa5bfa8098 --- /dev/null +++ b/lib/sasl/test/systools_SUITE_data/d_bad_app_vsn/lib/fe-3.1/src/fe1.erl @@ -0,0 +1,2 @@ +-module(fe1). +-vsn("1.0"). diff --git a/lib/sasl/test/systools_SUITE_data/d_bad_app_vsn/lib/fe-3.1/src/fe2.erl b/lib/sasl/test/systools_SUITE_data/d_bad_app_vsn/lib/fe-3.1/src/fe2.erl new file mode 100644 index 0000000000..869f3b93c8 --- /dev/null +++ b/lib/sasl/test/systools_SUITE_data/d_bad_app_vsn/lib/fe-3.1/src/fe2.erl @@ -0,0 +1,2 @@ +-module(fe2). +-vsn("1.0"). diff --git a/lib/sasl/test/systools_SUITE_data/d_bad_app_vsn/lib/fe-3.1/src/fe3.erl b/lib/sasl/test/systools_SUITE_data/d_bad_app_vsn/lib/fe-3.1/src/fe3.erl new file mode 100644 index 0000000000..6473342f52 --- /dev/null +++ b/lib/sasl/test/systools_SUITE_data/d_bad_app_vsn/lib/fe-3.1/src/fe3.erl @@ -0,0 +1,2 @@ +-module(fe3). +-vsn("2.0"). diff --git a/lib/sasl/test/systools_SUITE_data/d_bad_appup/lib/fe-3.1/ebin/fe.app b/lib/sasl/test/systools_SUITE_data/d_bad_appup/lib/fe-3.1/ebin/fe.app new file mode 100644 index 0000000000..0696e2494c --- /dev/null +++ b/lib/sasl/test/systools_SUITE_data/d_bad_appup/lib/fe-3.1/ebin/fe.app @@ -0,0 +1,7 @@ +{application, fe, + [{description, "ERICSSON NR FOR FE"}, + {vsn, "3.1"}, + {modules, [{fe1, "1.0"}, {fe2, "1.0"}, {fe3, "2.0"}]}, + {registered, []}, + {applications, []}, + {mod, {fe1, []}}]}. diff --git a/lib/sasl/test/systools_SUITE_data/d_bad_appup/lib/fe-3.1/ebin/fe.appup b/lib/sasl/test/systools_SUITE_data/d_bad_appup/lib/fe-3.1/ebin/fe.appup new file mode 100644 index 0000000000..dac4c00851 --- /dev/null +++ b/lib/sasl/test/systools_SUITE_data/d_bad_appup/lib/fe-3.1/ebin/fe.appup @@ -0,0 +1,27 @@ +%% +%% Release upgrade script for fe (front end) +%% + +{ + "3.1", +%%% Upgrade from: + [ + {"2.1.1" [{update, fe1, soft, soft_purge, soft_purge, []}, + {update, fe2, soft, soft_purge, soft_purge, [fe1]}, + {update, fe3, {advanced, extra}, soft_purge, soft_purge, + [fe1, fe2]} + ]}, + + {"2.1", [{update, fe1, soft, soft_purge, soft_purge, []}, + {update, fe2, soft, soft_purge, soft_purge, [fe1]}, + {update, fe3, {advanced, extra}, soft_purge, soft_purge, [fe1, fe2]} + ]} + ], + +%%% Downgrade to: + [ + {"2.1", [{update, fe2, soft, soft_purge, soft_purge, []}, + {update, fe3, {advanced, extra}, soft_purge, soft_purge, [fe2]} + ]} + ] +}. diff --git a/lib/sasl/test/systools_SUITE_data/d_bad_mod+warn/lib/db-2.1/ebin/db.app b/lib/sasl/test/systools_SUITE_data/d_bad_mod+warn/lib/db-2.1/ebin/db.app new file mode 100644 index 0000000000..191919f8d3 --- /dev/null +++ b/lib/sasl/test/systools_SUITE_data/d_bad_mod+warn/lib/db-2.1/ebin/db.app @@ -0,0 +1,8 @@ +{application, db, + [{description, "ERICSSON NR FOR DB"}, + {vsn, "2.1"}, + {modules, [{db1, "1.0"}, {db2, "1.0"}]}, + {registered, []}, + {applications, []}, + {env, []}, + {start, {db1, start, []}}]}. diff --git a/lib/sasl/test/systools_SUITE_data/d_bad_mod+warn/lib/db-2.1/ebin/db.appup b/lib/sasl/test/systools_SUITE_data/d_bad_mod+warn/lib/db-2.1/ebin/db.appup new file mode 100644 index 0000000000..621838f0b4 --- /dev/null +++ b/lib/sasl/test/systools_SUITE_data/d_bad_mod+warn/lib/db-2.1/ebin/db.appup @@ -0,0 +1,20 @@ +%% +%% Release upgrade script for db (data base) +%% + +{ + "2.1", +%%% Upgrade from: + [ + {"1.0", [{update, db1, soft, soft_purge, soft_purge, []}, + {update, db2, soft, soft_purge, soft_purge, [db1]}]}, + {"1.1", [{update, db1, soft, soft_purge, soft_purge, []}, + {update, db2, soft, soft_purge, soft_purge, [db1]}]} + ], + +%%% Downgrade to: + [ + {"1.0", [{update, db1, soft, soft_purge, soft_purge, []}, + {update, db2, soft, soft_purge, soft_purge, [db1]}]} + ] +}. diff --git a/lib/sasl/test/systools_SUITE_data/d_bad_mod+warn/lib/db-2.1/src/db1.erl b/lib/sasl/test/systools_SUITE_data/d_bad_mod+warn/lib/db-2.1/src/db1.erl new file mode 100644 index 0000000000..a17640316e --- /dev/null +++ b/lib/sasl/test/systools_SUITE_data/d_bad_mod+warn/lib/db-2.1/src/db1.erl @@ -0,0 +1,2 @@ +-module(db2). +-vsn("1.0"). diff --git a/lib/sasl/test/systools_SUITE_data/d_bad_mod+warn/lib/db-2.1/src/db2.erl b/lib/sasl/test/systools_SUITE_data/d_bad_mod+warn/lib/db-2.1/src/db2.erl new file mode 100644 index 0000000000..a17640316e --- /dev/null +++ b/lib/sasl/test/systools_SUITE_data/d_bad_mod+warn/lib/db-2.1/src/db2.erl @@ -0,0 +1,2 @@ +-module(db2). +-vsn("1.0"). diff --git a/lib/sasl/test/systools_SUITE_data/d_bad_mod+warn/lib/fe-3.1/ebin/fe.app b/lib/sasl/test/systools_SUITE_data/d_bad_mod+warn/lib/fe-3.1/ebin/fe.app new file mode 100644 index 0000000000..d3bd85cda6 --- /dev/null +++ b/lib/sasl/test/systools_SUITE_data/d_bad_mod+warn/lib/fe-3.1/ebin/fe.app @@ -0,0 +1,8 @@ +{application, fe, + [{description, "ERICSSON NR FOR FE"}, + {vsn, "3.1"}, + {modules, [{fe1, "1.0"}, {fe2, "1.0"}, {fe3, "2.0"}]}, + {registered, []}, + {applications, []}, + {env, []}, + {start, {fe2, start, []}}]}. diff --git a/lib/sasl/test/systools_SUITE_data/d_bad_mod+warn/lib/fe-3.1/ebin/fe.appup b/lib/sasl/test/systools_SUITE_data/d_bad_mod+warn/lib/fe-3.1/ebin/fe.appup new file mode 100644 index 0000000000..2db69eba76 --- /dev/null +++ b/lib/sasl/test/systools_SUITE_data/d_bad_mod+warn/lib/fe-3.1/ebin/fe.appup @@ -0,0 +1,27 @@ +%% +%% Release upgrade script for fe (front end) +%% + +{ + "3.1", +%%% Upgrade from: + [ + {"2.1.1", [{update, fe1, soft, soft_purge, soft_purge, []}, + {update, fe2, soft, soft_purge, soft_purge, [fe1]}, + {update, fe3, {advanced, extra}, soft_purge, soft_purge, + [fe1, fe2]} + ]}, + + {"2.1", [{update, fe1, soft, soft_purge, soft_purge, []}, + {update, fe2, soft, soft_purge, soft_purge, [fe1]}, + {update, fe3, {advanced, extra}, soft_purge, soft_purge, [fe1, fe2]} + ]} + ], + +%%% Downgrade to: + [ + {"2.1", [{update, fe2, soft, soft_purge, soft_purge, []}, + {update, fe3, {advanced, extra}, soft_purge, soft_purge, [fe2]} + ]} + ] +}. diff --git a/lib/sasl/test/systools_SUITE_data/d_bad_mod+warn/lib/fe-3.1/src/fe1.erl b/lib/sasl/test/systools_SUITE_data/d_bad_mod+warn/lib/fe-3.1/src/fe1.erl new file mode 100644 index 0000000000..cdbb9ef532 --- /dev/null +++ b/lib/sasl/test/systools_SUITE_data/d_bad_mod+warn/lib/fe-3.1/src/fe1.erl @@ -0,0 +1,2 @@ +-module(fe1). +-vsn("1.1"). diff --git a/lib/sasl/test/systools_SUITE_data/d_bad_mod+warn/lib/fe-3.1/src/fe2.erl b/lib/sasl/test/systools_SUITE_data/d_bad_mod+warn/lib/fe-3.1/src/fe2.erl new file mode 100644 index 0000000000..869f3b93c8 --- /dev/null +++ b/lib/sasl/test/systools_SUITE_data/d_bad_mod+warn/lib/fe-3.1/src/fe2.erl @@ -0,0 +1,2 @@ +-module(fe2). +-vsn("1.0"). diff --git a/lib/sasl/test/systools_SUITE_data/d_bad_mod+warn/lib/fe-3.1/src/fe3.erl b/lib/sasl/test/systools_SUITE_data/d_bad_mod+warn/lib/fe-3.1/src/fe3.erl new file mode 100644 index 0000000000..6473342f52 --- /dev/null +++ b/lib/sasl/test/systools_SUITE_data/d_bad_mod+warn/lib/fe-3.1/src/fe3.erl @@ -0,0 +1,2 @@ +-module(fe3). +-vsn("2.0"). diff --git a/lib/sasl/test/systools_SUITE_data/d_links/lib/db-2.1/ebin/db.app b/lib/sasl/test/systools_SUITE_data/d_links/lib/db-2.1/ebin/db.app new file mode 100644 index 0000000000..3e0ac3c3c9 --- /dev/null +++ b/lib/sasl/test/systools_SUITE_data/d_links/lib/db-2.1/ebin/db.app @@ -0,0 +1,8 @@ +{application, db, + [{description, "ERICSSON NR FOR DB"}, + {vsn, "2.1"}, + {modules, [{db1, "1.0"}, {db2, "1.0"}, {db3, "2.0"}]}, + {registered, []}, + {applications, []}, + {env, []}, + {start, {db1, start, []}}]}. diff --git a/lib/sasl/test/systools_SUITE_data/d_links/lib/db-2.1/ebin/db.appup b/lib/sasl/test/systools_SUITE_data/d_links/lib/db-2.1/ebin/db.appup new file mode 100644 index 0000000000..621838f0b4 --- /dev/null +++ b/lib/sasl/test/systools_SUITE_data/d_links/lib/db-2.1/ebin/db.appup @@ -0,0 +1,20 @@ +%% +%% Release upgrade script for db (data base) +%% + +{ + "2.1", +%%% Upgrade from: + [ + {"1.0", [{update, db1, soft, soft_purge, soft_purge, []}, + {update, db2, soft, soft_purge, soft_purge, [db1]}]}, + {"1.1", [{update, db1, soft, soft_purge, soft_purge, []}, + {update, db2, soft, soft_purge, soft_purge, [db1]}]} + ], + +%%% Downgrade to: + [ + {"1.0", [{update, db1, soft, soft_purge, soft_purge, []}, + {update, db2, soft, soft_purge, soft_purge, [db1]}]} + ] +}. diff --git a/lib/sasl/test/systools_SUITE_data/d_links/lib/db-2.1/src/db2.erl b/lib/sasl/test/systools_SUITE_data/d_links/lib/db-2.1/src/db2.erl new file mode 100644 index 0000000000..a17640316e --- /dev/null +++ b/lib/sasl/test/systools_SUITE_data/d_links/lib/db-2.1/src/db2.erl @@ -0,0 +1,2 @@ +-module(db2). +-vsn("1.0"). diff --git a/lib/sasl/test/systools_SUITE_data/d_links/lib/db-2.1/src/db3.erl b/lib/sasl/test/systools_SUITE_data/d_links/lib/db-2.1/src/db3.erl new file mode 100644 index 0000000000..6473342f52 --- /dev/null +++ b/lib/sasl/test/systools_SUITE_data/d_links/lib/db-2.1/src/db3.erl @@ -0,0 +1,2 @@ +-module(fe3). +-vsn("2.0"). diff --git a/lib/sasl/test/systools_SUITE_data/d_links/lib/fe-3.1/ebin/fe.appup b/lib/sasl/test/systools_SUITE_data/d_links/lib/fe-3.1/ebin/fe.appup new file mode 100644 index 0000000000..4ab13c1bae --- /dev/null +++ b/lib/sasl/test/systools_SUITE_data/d_links/lib/fe-3.1/ebin/fe.appup @@ -0,0 +1,27 @@ +%% +%% Release upgrade script for fe (front end) +%% + +{ + "3.1", +%%% Upgrade from: + [ + {"2.1.1", [{update, fe1, soft, soft_purge, soft_purge, []}, + {update, fe2, soft, soft_purge, soft_purge, [fe1]}, + {update, fe3, {{advanced, extra}, soft_purge, soft_purge, + [fe1, fe2]} + ]}, + + {"2.1", [{update, fe1, soft, soft_purge, soft_purge, []}, + {update, fe2, soft, soft_purge, soft_purge, [fe1]}, + {update, fe3, {advanced, extra}, soft_purge, soft_purge, [fe1, fe2]} + ]} + ], + +%%% Downgrade to: + [ + {"2.1", [{update, fe2, soft, soft_purge, soft_purge, []}, + {update, fe3, {advanced, extra}, soft_purge, soft_purge, [fe2]} + ]} + ] +}. diff --git a/lib/sasl/test/systools_SUITE_data/d_links/lib/fe-3.1/src/fe1.erl b/lib/sasl/test/systools_SUITE_data/d_links/lib/fe-3.1/src/fe1.erl new file mode 100644 index 0000000000..aa5bfa8098 --- /dev/null +++ b/lib/sasl/test/systools_SUITE_data/d_links/lib/fe-3.1/src/fe1.erl @@ -0,0 +1,2 @@ +-module(fe1). +-vsn("1.0"). diff --git a/lib/sasl/test/systools_SUITE_data/d_links/lib/fe-3.1/src/fe2.erl b/lib/sasl/test/systools_SUITE_data/d_links/lib/fe-3.1/src/fe2.erl new file mode 100644 index 0000000000..869f3b93c8 --- /dev/null +++ b/lib/sasl/test/systools_SUITE_data/d_links/lib/fe-3.1/src/fe2.erl @@ -0,0 +1,2 @@ +-module(fe2). +-vsn("1.0"). diff --git a/lib/sasl/test/systools_SUITE_data/d_links/lib/fe-3.1/src/fe3.erl b/lib/sasl/test/systools_SUITE_data/d_links/lib/fe-3.1/src/fe3.erl new file mode 100644 index 0000000000..6473342f52 --- /dev/null +++ b/lib/sasl/test/systools_SUITE_data/d_links/lib/fe-3.1/src/fe3.erl @@ -0,0 +1,2 @@ +-module(fe3). +-vsn("2.0"). diff --git a/lib/sasl/test/systools_SUITE_data/d_missing_src/lib/db-2.1/ebin/db.app b/lib/sasl/test/systools_SUITE_data/d_missing_src/lib/db-2.1/ebin/db.app new file mode 100644 index 0000000000..191919f8d3 --- /dev/null +++ b/lib/sasl/test/systools_SUITE_data/d_missing_src/lib/db-2.1/ebin/db.app @@ -0,0 +1,8 @@ +{application, db, + [{description, "ERICSSON NR FOR DB"}, + {vsn, "2.1"}, + {modules, [{db1, "1.0"}, {db2, "1.0"}]}, + {registered, []}, + {applications, []}, + {env, []}, + {start, {db1, start, []}}]}. diff --git a/lib/sasl/test/systools_SUITE_data/d_missing_src/lib/db-2.1/ebin/db.appup b/lib/sasl/test/systools_SUITE_data/d_missing_src/lib/db-2.1/ebin/db.appup new file mode 100644 index 0000000000..621838f0b4 --- /dev/null +++ b/lib/sasl/test/systools_SUITE_data/d_missing_src/lib/db-2.1/ebin/db.appup @@ -0,0 +1,20 @@ +%% +%% Release upgrade script for db (data base) +%% + +{ + "2.1", +%%% Upgrade from: + [ + {"1.0", [{update, db1, soft, soft_purge, soft_purge, []}, + {update, db2, soft, soft_purge, soft_purge, [db1]}]}, + {"1.1", [{update, db1, soft, soft_purge, soft_purge, []}, + {update, db2, soft, soft_purge, soft_purge, [db1]}]} + ], + +%%% Downgrade to: + [ + {"1.0", [{update, db1, soft, soft_purge, soft_purge, []}, + {update, db2, soft, soft_purge, soft_purge, [db1]}]} + ] +}. diff --git a/lib/sasl/test/systools_SUITE_data/d_missing_src/lib/db-2.1/src/db1.erl b/lib/sasl/test/systools_SUITE_data/d_missing_src/lib/db-2.1/src/db1.erl new file mode 100644 index 0000000000..a17640316e --- /dev/null +++ b/lib/sasl/test/systools_SUITE_data/d_missing_src/lib/db-2.1/src/db1.erl @@ -0,0 +1,2 @@ +-module(db2). +-vsn("1.0"). diff --git a/lib/sasl/test/systools_SUITE_data/d_missing_src/lib/db-2.1/src/db2.erl b/lib/sasl/test/systools_SUITE_data/d_missing_src/lib/db-2.1/src/db2.erl new file mode 100644 index 0000000000..a17640316e --- /dev/null +++ b/lib/sasl/test/systools_SUITE_data/d_missing_src/lib/db-2.1/src/db2.erl @@ -0,0 +1,2 @@ +-module(db2). +-vsn("1.0"). diff --git a/lib/sasl/test/systools_SUITE_data/d_missing_src/lib/fe-3.1/ebin/fe.app b/lib/sasl/test/systools_SUITE_data/d_missing_src/lib/fe-3.1/ebin/fe.app new file mode 100644 index 0000000000..d3bd85cda6 --- /dev/null +++ b/lib/sasl/test/systools_SUITE_data/d_missing_src/lib/fe-3.1/ebin/fe.app @@ -0,0 +1,8 @@ +{application, fe, + [{description, "ERICSSON NR FOR FE"}, + {vsn, "3.1"}, + {modules, [{fe1, "1.0"}, {fe2, "1.0"}, {fe3, "2.0"}]}, + {registered, []}, + {applications, []}, + {env, []}, + {start, {fe2, start, []}}]}. diff --git a/lib/sasl/test/systools_SUITE_data/d_missing_src/lib/fe-3.1/ebin/fe.appup b/lib/sasl/test/systools_SUITE_data/d_missing_src/lib/fe-3.1/ebin/fe.appup new file mode 100644 index 0000000000..4ab13c1bae --- /dev/null +++ b/lib/sasl/test/systools_SUITE_data/d_missing_src/lib/fe-3.1/ebin/fe.appup @@ -0,0 +1,27 @@ +%% +%% Release upgrade script for fe (front end) +%% + +{ + "3.1", +%%% Upgrade from: + [ + {"2.1.1", [{update, fe1, soft, soft_purge, soft_purge, []}, + {update, fe2, soft, soft_purge, soft_purge, [fe1]}, + {update, fe3, {{advanced, extra}, soft_purge, soft_purge, + [fe1, fe2]} + ]}, + + {"2.1", [{update, fe1, soft, soft_purge, soft_purge, []}, + {update, fe2, soft, soft_purge, soft_purge, [fe1]}, + {update, fe3, {advanced, extra}, soft_purge, soft_purge, [fe1, fe2]} + ]} + ], + +%%% Downgrade to: + [ + {"2.1", [{update, fe2, soft, soft_purge, soft_purge, []}, + {update, fe3, {advanced, extra}, soft_purge, soft_purge, [fe2]} + ]} + ] +}. diff --git a/lib/sasl/test/systools_SUITE_data/d_missing_src/lib/fe-3.1/src/fe1.erl b/lib/sasl/test/systools_SUITE_data/d_missing_src/lib/fe-3.1/src/fe1.erl new file mode 100644 index 0000000000..cdbb9ef532 --- /dev/null +++ b/lib/sasl/test/systools_SUITE_data/d_missing_src/lib/fe-3.1/src/fe1.erl @@ -0,0 +1,2 @@ +-module(fe1). +-vsn("1.1"). diff --git a/lib/sasl/test/systools_SUITE_data/d_missing_src/lib/fe-3.1/src/fe2.erl b/lib/sasl/test/systools_SUITE_data/d_missing_src/lib/fe-3.1/src/fe2.erl new file mode 100644 index 0000000000..869f3b93c8 --- /dev/null +++ b/lib/sasl/test/systools_SUITE_data/d_missing_src/lib/fe-3.1/src/fe2.erl @@ -0,0 +1,2 @@ +-module(fe2). +-vsn("1.0"). diff --git a/lib/sasl/test/systools_SUITE_data/d_missing_src/lib/fe-3.1/src/fe3.erl b/lib/sasl/test/systools_SUITE_data/d_missing_src/lib/fe-3.1/src/fe3.erl new file mode 100644 index 0000000000..6473342f52 --- /dev/null +++ b/lib/sasl/test/systools_SUITE_data/d_missing_src/lib/fe-3.1/src/fe3.erl @@ -0,0 +1,2 @@ +-module(fe3). +-vsn("2.0"). diff --git a/lib/sasl/test/systools_SUITE_data/d_no_appup/lib/fe-2.1/ebin/fe.app b/lib/sasl/test/systools_SUITE_data/d_no_appup/lib/fe-2.1/ebin/fe.app new file mode 100644 index 0000000000..47ea248720 --- /dev/null +++ b/lib/sasl/test/systools_SUITE_data/d_no_appup/lib/fe-2.1/ebin/fe.app @@ -0,0 +1,8 @@ +{application, fe, + [{description, "ERICSSON NR FOR FE"}, + {vsn, "2.1"}, + {modules, [{fe1, "1.0"}, {fe2, "1.0"}, {fe3, "2.0"}]}, + {registered, []}, + {applications, []}, + {env, []}, + {start, {fe2, start, []}}]}. diff --git a/lib/sasl/test/systools_SUITE_data/d_no_appup/lib/fe-2.1/ebin/fe.appup b/lib/sasl/test/systools_SUITE_data/d_no_appup/lib/fe-2.1/ebin/fe.appup new file mode 100644 index 0000000000..2db69eba76 --- /dev/null +++ b/lib/sasl/test/systools_SUITE_data/d_no_appup/lib/fe-2.1/ebin/fe.appup @@ -0,0 +1,27 @@ +%% +%% Release upgrade script for fe (front end) +%% + +{ + "3.1", +%%% Upgrade from: + [ + {"2.1.1", [{update, fe1, soft, soft_purge, soft_purge, []}, + {update, fe2, soft, soft_purge, soft_purge, [fe1]}, + {update, fe3, {advanced, extra}, soft_purge, soft_purge, + [fe1, fe2]} + ]}, + + {"2.1", [{update, fe1, soft, soft_purge, soft_purge, []}, + {update, fe2, soft, soft_purge, soft_purge, [fe1]}, + {update, fe3, {advanced, extra}, soft_purge, soft_purge, [fe1, fe2]} + ]} + ], + +%%% Downgrade to: + [ + {"2.1", [{update, fe2, soft, soft_purge, soft_purge, []}, + {update, fe3, {advanced, extra}, soft_purge, soft_purge, [fe2]} + ]} + ] +}. diff --git a/lib/sasl/test/systools_SUITE_data/d_no_appup/lib/fe-3.1/ebin/fe.app b/lib/sasl/test/systools_SUITE_data/d_no_appup/lib/fe-3.1/ebin/fe.app new file mode 100644 index 0000000000..0696e2494c --- /dev/null +++ b/lib/sasl/test/systools_SUITE_data/d_no_appup/lib/fe-3.1/ebin/fe.app @@ -0,0 +1,7 @@ +{application, fe, + [{description, "ERICSSON NR FOR FE"}, + {vsn, "3.1"}, + {modules, [{fe1, "1.0"}, {fe2, "1.0"}, {fe3, "2.0"}]}, + {registered, []}, + {applications, []}, + {mod, {fe1, []}}]}. diff --git a/lib/sasl/test/systools_SUITE_data/d_normal/lib/db-1.0/ebin/db.app b/lib/sasl/test/systools_SUITE_data/d_normal/lib/db-1.0/ebin/db.app new file mode 100644 index 0000000000..22530ee335 --- /dev/null +++ b/lib/sasl/test/systools_SUITE_data/d_normal/lib/db-1.0/ebin/db.app @@ -0,0 +1,8 @@ +{application, db, + [{description, "ERICSSON NR FOR DB"}, + {vsn, "1.0"}, + {modules, [{db1, "1.0"}, {db2, "1.0"}]}, + {registered, []}, + {applications, []}, + {env, []}, + {start, {db1, start, []}}]}. diff --git a/lib/sasl/test/systools_SUITE_data/d_normal/lib/db-1.0/src/db1.erl b/lib/sasl/test/systools_SUITE_data/d_normal/lib/db-1.0/src/db1.erl new file mode 100644 index 0000000000..a17640316e --- /dev/null +++ b/lib/sasl/test/systools_SUITE_data/d_normal/lib/db-1.0/src/db1.erl @@ -0,0 +1,2 @@ +-module(db2). +-vsn("1.0"). diff --git a/lib/sasl/test/systools_SUITE_data/d_normal/lib/db-1.0/src/db2.erl b/lib/sasl/test/systools_SUITE_data/d_normal/lib/db-1.0/src/db2.erl new file mode 100644 index 0000000000..a17640316e --- /dev/null +++ b/lib/sasl/test/systools_SUITE_data/d_normal/lib/db-1.0/src/db2.erl @@ -0,0 +1,2 @@ +-module(db2). +-vsn("1.0"). diff --git a/lib/sasl/test/systools_SUITE_data/d_normal/lib/db-1.1/ebin/db.app b/lib/sasl/test/systools_SUITE_data/d_normal/lib/db-1.1/ebin/db.app new file mode 100644 index 0000000000..7243a0a96a --- /dev/null +++ b/lib/sasl/test/systools_SUITE_data/d_normal/lib/db-1.1/ebin/db.app @@ -0,0 +1,8 @@ +{application, db, + [{description, "ERICSSON NR FOR DB"}, + {vsn, "1.1"}, + {modules, [{db1, "1.0"}, {db2, "1.0"}]}, + {registered, []}, + {applications, []}, + {env, []}, + {start, {db1, start, []}}]}. diff --git a/lib/sasl/test/systools_SUITE_data/d_normal/lib/db-1.1/src/db1.erl b/lib/sasl/test/systools_SUITE_data/d_normal/lib/db-1.1/src/db1.erl new file mode 100644 index 0000000000..a17640316e --- /dev/null +++ b/lib/sasl/test/systools_SUITE_data/d_normal/lib/db-1.1/src/db1.erl @@ -0,0 +1,2 @@ +-module(db2). +-vsn("1.0"). diff --git a/lib/sasl/test/systools_SUITE_data/d_normal/lib/db-1.1/src/db2.erl b/lib/sasl/test/systools_SUITE_data/d_normal/lib/db-1.1/src/db2.erl new file mode 100644 index 0000000000..a17640316e --- /dev/null +++ b/lib/sasl/test/systools_SUITE_data/d_normal/lib/db-1.1/src/db2.erl @@ -0,0 +1,2 @@ +-module(db2). +-vsn("1.0"). diff --git a/lib/sasl/test/systools_SUITE_data/d_normal/lib/db-2.1/ebin/db.app b/lib/sasl/test/systools_SUITE_data/d_normal/lib/db-2.1/ebin/db.app new file mode 100644 index 0000000000..202d7f1234 --- /dev/null +++ b/lib/sasl/test/systools_SUITE_data/d_normal/lib/db-2.1/ebin/db.app @@ -0,0 +1,7 @@ +{application, db, + [{description, "ERICSSON NR FOR DB"}, + {vsn, "2.1"}, + {modules, [{db1, "1.0"}, {db2, "1.0"}]}, + {registered, []}, + {applications, []}, + {mod, {db1, []}}]}. diff --git a/lib/sasl/test/systools_SUITE_data/d_normal/lib/db-2.1/ebin/db.appup b/lib/sasl/test/systools_SUITE_data/d_normal/lib/db-2.1/ebin/db.appup new file mode 100644 index 0000000000..621838f0b4 --- /dev/null +++ b/lib/sasl/test/systools_SUITE_data/d_normal/lib/db-2.1/ebin/db.appup @@ -0,0 +1,20 @@ +%% +%% Release upgrade script for db (data base) +%% + +{ + "2.1", +%%% Upgrade from: + [ + {"1.0", [{update, db1, soft, soft_purge, soft_purge, []}, + {update, db2, soft, soft_purge, soft_purge, [db1]}]}, + {"1.1", [{update, db1, soft, soft_purge, soft_purge, []}, + {update, db2, soft, soft_purge, soft_purge, [db1]}]} + ], + +%%% Downgrade to: + [ + {"1.0", [{update, db1, soft, soft_purge, soft_purge, []}, + {update, db2, soft, soft_purge, soft_purge, [db1]}]} + ] +}. diff --git a/lib/sasl/test/systools_SUITE_data/d_normal/lib/db-2.1/src/db1.erl b/lib/sasl/test/systools_SUITE_data/d_normal/lib/db-2.1/src/db1.erl new file mode 100644 index 0000000000..7cddf6bb63 --- /dev/null +++ b/lib/sasl/test/systools_SUITE_data/d_normal/lib/db-2.1/src/db1.erl @@ -0,0 +1,13 @@ +-module(db1). +-vsn("1.0"). + +-export([a/0]). + +a() -> + lists:non_existing_func("dummy_list"), + b(). + +b() -> + fe2:non_existing_func(), + db2:non_existing_func(), + fe1:a(). diff --git a/lib/sasl/test/systools_SUITE_data/d_normal/lib/db-2.1/src/db2.erl b/lib/sasl/test/systools_SUITE_data/d_normal/lib/db-2.1/src/db2.erl new file mode 100644 index 0000000000..a17640316e --- /dev/null +++ b/lib/sasl/test/systools_SUITE_data/d_normal/lib/db-2.1/src/db2.erl @@ -0,0 +1,2 @@ +-module(db2). +-vsn("1.0"). diff --git a/lib/sasl/test/systools_SUITE_data/d_normal/lib/db-3.1/ebin/db.app b/lib/sasl/test/systools_SUITE_data/d_normal/lib/db-3.1/ebin/db.app new file mode 100644 index 0000000000..97643561eb --- /dev/null +++ b/lib/sasl/test/systools_SUITE_data/d_normal/lib/db-3.1/ebin/db.app @@ -0,0 +1,7 @@ +{application, db, + [{description, "ERICSSON NR FOR DB"}, + {vsn, "3.1"}, + {modules, [db1, db2]}, + {registered, []}, + {applications, []}, + {mod, {db1, []}}]}. diff --git a/lib/sasl/test/systools_SUITE_data/d_normal/lib/db-3.1/ebin/db.appup b/lib/sasl/test/systools_SUITE_data/d_normal/lib/db-3.1/ebin/db.appup new file mode 100644 index 0000000000..e636eee158 --- /dev/null +++ b/lib/sasl/test/systools_SUITE_data/d_normal/lib/db-3.1/ebin/db.appup @@ -0,0 +1,20 @@ +%% +%% Release upgrade script for db (data base) +%% + +{ + "3.1", +%%% Upgrade from: + [ + {"1.0", [{update, db1, soft, soft_purge, soft_purge, []}, + {update, db2, soft, soft_purge, soft_purge, [db1]}]}, + {"1.1", [{update, db1, soft, soft_purge, soft_purge, []}, + {update, db2, soft, soft_purge, soft_purge, [db1]}]} + ], + +%%% Downgrade to: + [ + {"1.0", [{update, db1, soft, soft_purge, soft_purge, []}, + {update, db2, soft, soft_purge, soft_purge, [db1]}]} + ] +}. diff --git a/lib/sasl/test/systools_SUITE_data/d_normal/lib/db-3.1/src/db1.erl b/lib/sasl/test/systools_SUITE_data/d_normal/lib/db-3.1/src/db1.erl new file mode 100644 index 0000000000..a17640316e --- /dev/null +++ b/lib/sasl/test/systools_SUITE_data/d_normal/lib/db-3.1/src/db1.erl @@ -0,0 +1,2 @@ +-module(db2). +-vsn("1.0"). diff --git a/lib/sasl/test/systools_SUITE_data/d_normal/lib/db-3.1/src/db2.erl b/lib/sasl/test/systools_SUITE_data/d_normal/lib/db-3.1/src/db2.erl new file mode 100644 index 0000000000..a17640316e --- /dev/null +++ b/lib/sasl/test/systools_SUITE_data/d_normal/lib/db-3.1/src/db2.erl @@ -0,0 +1,2 @@ +-module(db2). +-vsn("1.0"). diff --git a/lib/sasl/test/systools_SUITE_data/d_normal/lib/fe-2.1.1/ebin/fe.app b/lib/sasl/test/systools_SUITE_data/d_normal/lib/fe-2.1.1/ebin/fe.app new file mode 100644 index 0000000000..c7ba1dfe91 --- /dev/null +++ b/lib/sasl/test/systools_SUITE_data/d_normal/lib/fe-2.1.1/ebin/fe.app @@ -0,0 +1,8 @@ +{application, fe, + [{description, "ERICSSON NR FOR FE"}, + {vsn, "2.1.1"}, + {modules, [{fe1, "1.0"}, {fe2, "1.0"}, {fe3, "2.0"}]}, + {registered, []}, + {applications, []}, + {env, []}, + {start, {fe2, start, []}}]}. diff --git a/lib/sasl/test/systools_SUITE_data/d_normal/lib/fe-2.1.1/src/fe1.erl b/lib/sasl/test/systools_SUITE_data/d_normal/lib/fe-2.1.1/src/fe1.erl new file mode 100644 index 0000000000..aa5bfa8098 --- /dev/null +++ b/lib/sasl/test/systools_SUITE_data/d_normal/lib/fe-2.1.1/src/fe1.erl @@ -0,0 +1,2 @@ +-module(fe1). +-vsn("1.0"). diff --git a/lib/sasl/test/systools_SUITE_data/d_normal/lib/fe-2.1.1/src/fe2.erl b/lib/sasl/test/systools_SUITE_data/d_normal/lib/fe-2.1.1/src/fe2.erl new file mode 100644 index 0000000000..869f3b93c8 --- /dev/null +++ b/lib/sasl/test/systools_SUITE_data/d_normal/lib/fe-2.1.1/src/fe2.erl @@ -0,0 +1,2 @@ +-module(fe2). +-vsn("1.0"). diff --git a/lib/sasl/test/systools_SUITE_data/d_normal/lib/fe-2.1.1/src/fe3.erl b/lib/sasl/test/systools_SUITE_data/d_normal/lib/fe-2.1.1/src/fe3.erl new file mode 100644 index 0000000000..6473342f52 --- /dev/null +++ b/lib/sasl/test/systools_SUITE_data/d_normal/lib/fe-2.1.1/src/fe3.erl @@ -0,0 +1,2 @@ +-module(fe3). +-vsn("2.0"). diff --git a/lib/sasl/test/systools_SUITE_data/d_normal/lib/fe-2.1/ebin/fe.app b/lib/sasl/test/systools_SUITE_data/d_normal/lib/fe-2.1/ebin/fe.app new file mode 100644 index 0000000000..47ea248720 --- /dev/null +++ b/lib/sasl/test/systools_SUITE_data/d_normal/lib/fe-2.1/ebin/fe.app @@ -0,0 +1,8 @@ +{application, fe, + [{description, "ERICSSON NR FOR FE"}, + {vsn, "2.1"}, + {modules, [{fe1, "1.0"}, {fe2, "1.0"}, {fe3, "2.0"}]}, + {registered, []}, + {applications, []}, + {env, []}, + {start, {fe2, start, []}}]}. diff --git a/lib/sasl/test/systools_SUITE_data/d_normal/lib/fe-2.1/src/fe1.erl b/lib/sasl/test/systools_SUITE_data/d_normal/lib/fe-2.1/src/fe1.erl new file mode 100644 index 0000000000..aa5bfa8098 --- /dev/null +++ b/lib/sasl/test/systools_SUITE_data/d_normal/lib/fe-2.1/src/fe1.erl @@ -0,0 +1,2 @@ +-module(fe1). +-vsn("1.0"). diff --git a/lib/sasl/test/systools_SUITE_data/d_normal/lib/fe-2.1/src/fe2.erl b/lib/sasl/test/systools_SUITE_data/d_normal/lib/fe-2.1/src/fe2.erl new file mode 100644 index 0000000000..869f3b93c8 --- /dev/null +++ b/lib/sasl/test/systools_SUITE_data/d_normal/lib/fe-2.1/src/fe2.erl @@ -0,0 +1,2 @@ +-module(fe2). +-vsn("1.0"). diff --git a/lib/sasl/test/systools_SUITE_data/d_normal/lib/fe-2.1/src/fe3.erl b/lib/sasl/test/systools_SUITE_data/d_normal/lib/fe-2.1/src/fe3.erl new file mode 100644 index 0000000000..6473342f52 --- /dev/null +++ b/lib/sasl/test/systools_SUITE_data/d_normal/lib/fe-2.1/src/fe3.erl @@ -0,0 +1,2 @@ +-module(fe3). +-vsn("2.0"). diff --git a/lib/sasl/test/systools_SUITE_data/d_normal/lib/fe-3.1/ebin/fe.app b/lib/sasl/test/systools_SUITE_data/d_normal/lib/fe-3.1/ebin/fe.app new file mode 100644 index 0000000000..0696e2494c --- /dev/null +++ b/lib/sasl/test/systools_SUITE_data/d_normal/lib/fe-3.1/ebin/fe.app @@ -0,0 +1,7 @@ +{application, fe, + [{description, "ERICSSON NR FOR FE"}, + {vsn, "3.1"}, + {modules, [{fe1, "1.0"}, {fe2, "1.0"}, {fe3, "2.0"}]}, + {registered, []}, + {applications, []}, + {mod, {fe1, []}}]}. diff --git a/lib/sasl/test/systools_SUITE_data/d_normal/lib/fe-3.1/ebin/fe.appup b/lib/sasl/test/systools_SUITE_data/d_normal/lib/fe-3.1/ebin/fe.appup new file mode 100644 index 0000000000..2db69eba76 --- /dev/null +++ b/lib/sasl/test/systools_SUITE_data/d_normal/lib/fe-3.1/ebin/fe.appup @@ -0,0 +1,27 @@ +%% +%% Release upgrade script for fe (front end) +%% + +{ + "3.1", +%%% Upgrade from: + [ + {"2.1.1", [{update, fe1, soft, soft_purge, soft_purge, []}, + {update, fe2, soft, soft_purge, soft_purge, [fe1]}, + {update, fe3, {advanced, extra}, soft_purge, soft_purge, + [fe1, fe2]} + ]}, + + {"2.1", [{update, fe1, soft, soft_purge, soft_purge, []}, + {update, fe2, soft, soft_purge, soft_purge, [fe1]}, + {update, fe3, {advanced, extra}, soft_purge, soft_purge, [fe1, fe2]} + ]} + ], + +%%% Downgrade to: + [ + {"2.1", [{update, fe2, soft, soft_purge, soft_purge, []}, + {update, fe3, {advanced, extra}, soft_purge, soft_purge, [fe2]} + ]} + ] +}. diff --git a/lib/sasl/test/systools_SUITE_data/d_normal/lib/fe-3.1/src/fe1.erl b/lib/sasl/test/systools_SUITE_data/d_normal/lib/fe-3.1/src/fe1.erl new file mode 100644 index 0000000000..8aca89b2c7 --- /dev/null +++ b/lib/sasl/test/systools_SUITE_data/d_normal/lib/fe-3.1/src/fe1.erl @@ -0,0 +1,7 @@ +-module(fe1). +-vsn("1.0"). + +-export([a/0]). + +a() -> + ok. diff --git a/lib/sasl/test/systools_SUITE_data/d_normal/lib/fe-3.1/src/fe2.erl b/lib/sasl/test/systools_SUITE_data/d_normal/lib/fe-3.1/src/fe2.erl new file mode 100644 index 0000000000..869f3b93c8 --- /dev/null +++ b/lib/sasl/test/systools_SUITE_data/d_normal/lib/fe-3.1/src/fe2.erl @@ -0,0 +1,2 @@ +-module(fe2). +-vsn("1.0"). diff --git a/lib/sasl/test/systools_SUITE_data/d_normal/lib/fe-3.1/src/fe3.erl b/lib/sasl/test/systools_SUITE_data/d_normal/lib/fe-3.1/src/fe3.erl new file mode 100644 index 0000000000..6473342f52 --- /dev/null +++ b/lib/sasl/test/systools_SUITE_data/d_normal/lib/fe-3.1/src/fe3.erl @@ -0,0 +1,2 @@ +-module(fe3). +-vsn("2.0"). diff --git a/lib/sasl/test/systools_SUITE_data/lib/kernel/ebin/kernel.app b/lib/sasl/test/systools_SUITE_data/lib/kernel/ebin/kernel.app new file mode 100644 index 0000000000..e33314be7a --- /dev/null +++ b/lib/sasl/test/systools_SUITE_data/lib/kernel/ebin/kernel.app @@ -0,0 +1,6 @@ +{application, kernel, + [{description, "FAKE KERNEL"}, + {vsn, "1.0"}, + {modules, []}, + {registered, []}, + {applications, []}]}. diff --git a/lib/sasl/test/systools_SUITE_data/lib/kernel/ebin/kernel.appup b/lib/sasl/test/systools_SUITE_data/lib/kernel/ebin/kernel.appup new file mode 100644 index 0000000000..c53f07591f --- /dev/null +++ b/lib/sasl/test/systools_SUITE_data/lib/kernel/ebin/kernel.appup @@ -0,0 +1,12 @@ +%% +%% Fake release upgrade script for kernel +%% + +{ + "4.4.1", + [ + ], + + [ + ] +}. diff --git a/lib/sasl/test/systools_SUITE_data/lib/stdlib/ebin/stdlib.app b/lib/sasl/test/systools_SUITE_data/lib/stdlib/ebin/stdlib.app new file mode 100644 index 0000000000..288fcfe74e --- /dev/null +++ b/lib/sasl/test/systools_SUITE_data/lib/stdlib/ebin/stdlib.app @@ -0,0 +1,6 @@ +{application, stdlib, + [{description, "FAKE STDLIB"}, + {vsn, "1.0"}, + {modules, []}, + {registered, []}, + {applications, []}]}. diff --git a/lib/sasl/test/systools_SUITE_data/lib/stdlib/ebin/stdlib.appup b/lib/sasl/test/systools_SUITE_data/lib/stdlib/ebin/stdlib.appup new file mode 100644 index 0000000000..b521eb5d71 --- /dev/null +++ b/lib/sasl/test/systools_SUITE_data/lib/stdlib/ebin/stdlib.appup @@ -0,0 +1,12 @@ +%% +%% Fake release upgrade script for stdlib +%% + +{ + "1.1", + [ + ], + + [ + ] +}. diff --git a/lib/sasl/test/systools_rc_SUITE.erl b/lib/sasl/test/systools_rc_SUITE.erl new file mode 100644 index 0000000000..bb93f38fa7 --- /dev/null +++ b/lib/sasl/test/systools_rc_SUITE.erl @@ -0,0 +1,488 @@ +%% +%% %CopyrightBegin% +%% +%% Copyright Ericsson AB 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(systools_rc_SUITE). + +-include_lib("test_server/include/test_server.hrl"). +-include_lib("sasl/src/systools.hrl"). +-export([all/0,groups/0,init_per_group/2,end_per_group/2, + syntax_check/1, translate/1, translate_app/1]). + +%%----------------------------------------------------------------- +%% erl -compile systools_rc_SUITE @i ../src/ @i ../../test_server/include/ +%% c(systools_rc_SUITE, [{i, "../src"}, {i, "../../test_server/include"}]). +%%----------------------------------------------------------------- +all() -> + [syntax_check, translate, translate_app]. + +groups() -> + []. + +init_per_group(_GroupName, Config) -> + Config. + +end_per_group(_GroupName, Config) -> + Config. + + +syntax_check(suite) -> []; +syntax_check(Config) when is_list(Config) -> + PreApps = + [#application{name = test, + description = "TEST", + vsn = "0.1", + modules = [{foo,1},{bar,1},{baz,1},{old_mod,1}], + regs = [], + mod = {sasl, []}}, + #application{name = snmp, + description = "SNMP", + vsn = "1.0", + modules = [snmp], + regs = [], + mod = {snmp, []}}], + Apps = + [#application{name = test, + description = "TEST", + vsn = "1.0", + modules = [{foo,1},{bar,1},{baz,1},{new_mod,1}], + regs = [], + mod = {sasl, []}}], + S1 = [ + {update, bar, {advanced, extra}, brutal_purge, brutal_purge, []}, + {update, foo, soft, soft_purge, soft_purge, [bar]}, + {update, baz, 5000, soft, brutal_purge, brutal_purge, []}, + {add_module, new_mod}, + {remove_application, snmp} + ], + ?line {ok, _} = systools_rc:translate_scripts([S1], Apps, PreApps), + S2 = [ + {apply, {m, f, [a]}}, + {load_object_code, {tst, "1.0", [new_mod]}}, + point_of_no_return, + {update, bar, {advanced, extra}, brutal_purge, brutal_purge, []}, + {update, foo, soft, soft_purge, soft_purge, [bar]}, + {load, {new_mod, soft_purge, soft_purge}}, + {remove, {old_mod, soft_purge, soft_purge}}, + {purge, [m1, m2]}, + {suspend, [m1]}, + {code_change, [{m1, extra}]}, + {resume, [m1]}, + {stop, [m3,m4]}, + {start, [m3,m4]}, + {sync_nodes, id1, {m, f, [a]}}, + {sync_nodes, id2, [cp1, cp2]}, + {apply, {m,f,[a]}}, + restart_new_emulator + ], + ?line {ok, _} = systools_rc:translate_scripts([S2], Apps, []), + S3 = [{apply, {m, f, a}}], + ?line {error, _, _} = systools_rc:translate_scripts([S3], Apps, []), + S3_1 = [{apply, {m, 3, a}}], + ?line {error, _, _} = systools_rc:translate_scripts([S3_1], Apps, []), + S4 = [{apply, {m, f}}], + ?line {error, _, _} = systools_rc:translate_scripts([S4], Apps, []), + S5 = [{load_object_code, hej}], + ?line {error, _, _} = systools_rc:translate_scripts([S5], Apps, []), + S6 = [{load_object_code, {342, "1.0", [foo]}}], + ?line {error, _, _} = systools_rc:translate_scripts([S6], Apps, []), + S7 = [{load_object_code, {tets, "1.0", foo}}], + ?line {error, _, _} = systools_rc:translate_scripts([S7], Apps, []), + S8 = [{suspend, [m1]}, point_of_no_return], + ?line {error, _, _} = systools_rc:translate_scripts([S8], Apps, []), + S9 = [{update, ba, {advanced, extra}, brutal_purge, brutal_purge, []}], + ?line {error, _, _} = systools_rc:translate_scripts([S9], Apps, []), + S10 = [{update, bar, {advanced, extra}, brutal_purge, brutal_purge, [baz]}], + ?line {error, _, _} = systools_rc:translate_scripts([S10], Apps, []), + S11 = [{update, bar, {advanced, extra}, brutal_purge, brutal_purge, [ba]}], + ?line {error, _, _} = systools_rc:translate_scripts([S11], Apps, []), + S12 = [{update, bar, advanced, brutal_purge, brutal_purge, []}], + ?line {error, _, _} = systools_rc:translate_scripts([S12], Apps, []), + S13 = [{update, bar, {advanced, extra}, rutal_purge, brutal_purge, [ba]}], + ?line {error, _, _} = systools_rc:translate_scripts([S13], Apps, []), + S14 = [{update, bar, {advanced, extra}, brutal_purge, rutal_purge, [ba]}], + ?line {error, _, _} = systools_rc:translate_scripts([S14], Apps, []), + S15 = [{update, bar, {advanced, extra}, brutal_purge, brutal_purge, ba}], + ?line {error, _, _} = systools_rc:translate_scripts([S15], Apps, []), + S16 = [{code_change, [module]}], + ?line {error, _, _} = systools_rc:translate_scripts([S16], Apps, []), + ?line ok. + +translate(suite) -> []; +translate(Config) when is_list(Config) -> + Apps = + [#application{name = test, + description = "TEST", + vsn = "1.0", + modules = [{foo,1},{bar,1},{baz,1}, + {x,1},{y,1},{z,1}], + regs = [], + mod = {sasl, []}}], + %% Simple translation (1) + Up1 = [{update, foo, soft, soft_purge, soft_purge, []}], + ?line {ok, X1} = systools_rc:translate_scripts([Up1], Apps, []), + ?line [{load_object_code, {test,"1.0",[foo]}}, + point_of_no_return, + {suspend,[foo]}, + {load,{foo,soft_purge,soft_purge}}, + {resume,[foo]}] = X1, + + %% Simple translation (2) + Up2 = [{update, foo, {advanced, extra}, soft_purge, soft_purge, []}], + ?line {ok, X2} = systools_rc:translate_scripts([Up2], Apps, []), + ?line [{load_object_code, {test,"1.0",[foo]}}, + point_of_no_return, + {suspend,[foo]}, + {load,{foo,soft_purge,soft_purge}}, + {code_change, up, [{foo, extra}]}, + {resume,[foo]}] = X2, + + ?line {ok, X22} = systools_rc:translate_scripts(dn,[Up2], Apps, []), + ?line [{load_object_code, {test,"1.0",[foo]}}, + point_of_no_return, + {suspend,[foo]}, + {code_change, down, [{foo, extra}]}, + {load,{foo,soft_purge,soft_purge}}, + {resume,[foo]}] = X22, + + Up2a = [{update, foo, static, default, {advanced,extra}, + soft_purge, soft_purge, []}], + ?line {ok, X2a} = systools_rc:translate_scripts([Up2a], Apps, []), + ?line [{load_object_code, {test,"1.0",[foo]}}, + point_of_no_return, + {suspend,[foo]}, + {load,{foo,soft_purge,soft_purge}}, + {code_change, up, [{foo, extra}]}, + {resume,[foo]}] = X2a, + + ?line {ok, X22a} = systools_rc:translate_scripts(dn,[Up2a], Apps, []), + ?line [{load_object_code, {test,"1.0",[foo]}}, + point_of_no_return, + {suspend,[foo]}, + {load,{foo,soft_purge,soft_purge}}, + {code_change, down, [{foo, extra}]}, + {resume,[foo]}] = X22a, + + %% Simple dependency (1) + Up3 = [{update, foo, soft, soft_purge, soft_purge, [bar]}, + {update, bar, soft, soft_purge, soft_purge, []}], + ?line {ok, X31} = systools_rc:translate_scripts([Up3], Apps, []), + ?line [{load_object_code,{test,"1.0",[foo,bar]}}, + point_of_no_return, + {suspend,[foo,bar]}, + {load,{bar,soft_purge,soft_purge}}, + {load,{foo,soft_purge,soft_purge}}, + {resume,[bar,foo]}] = X31, + ?line {ok, X32} = systools_rc:translate_scripts(dn,[Up3], Apps, []), + ?line [{load_object_code,{test,"1.0",[foo,bar]}}, + point_of_no_return, + {suspend,[foo,bar]}, + {load,{foo,soft_purge,soft_purge}}, + {load,{bar,soft_purge,soft_purge}}, + {resume,[bar,foo]}] = X32, + + Up3a = [{update, foo, static, default, soft, soft_purge, soft_purge, [bar]}, + {update, bar, static, default, soft, soft_purge, soft_purge, []}], + ?line {ok, X3a1} = systools_rc:translate_scripts([Up3a], Apps, []), + ?line [{load_object_code,{test,"1.0",[foo,bar]}}, + point_of_no_return, + {suspend,[foo, bar]}, + {load,{bar,soft_purge,soft_purge}}, + {load,{foo,soft_purge,soft_purge}}, + {resume,[bar,foo]}] = X3a1, + ?line {ok, X3a2} = systools_rc:translate_scripts(dn,[Up3a], Apps, []), + ?line [{load_object_code,{test,"1.0",[foo,bar]}}, + point_of_no_return, + {suspend,[foo,bar]}, + {load,{foo,soft_purge,soft_purge}}, + {load,{bar,soft_purge,soft_purge}}, + {resume,[bar,foo]}] = X3a2, + + %% Simple dependency (2) + Up4 = [{update, foo, soft, soft_purge, soft_purge, [bar]}, + {update, bar, {advanced, []}, soft_purge, soft_purge, []}], + ?line {ok, X4} = systools_rc:translate_scripts(up,[Up4], Apps, []), + ?line [{load_object_code,{test,"1.0",[foo,bar]}}, + point_of_no_return, + {suspend,[foo,bar]}, + {load,{bar,soft_purge,soft_purge}}, + {load,{foo,soft_purge,soft_purge}}, + {code_change,up,[{bar,[]}]}, + {resume,[bar,foo]}] = X4, + + ?line {ok, X42} = systools_rc:translate_scripts(dn,[Up4], Apps, []), + ?line [{load_object_code,{test,"1.0",[foo,bar]}}, + point_of_no_return, + {suspend,[foo,bar]}, + {code_change,down,[{bar,[]}]}, + {load,{foo,soft_purge,soft_purge}}, + {load,{bar,soft_purge,soft_purge}}, + {resume,[bar,foo]}] = X42, + + Up4a = [{update, foo, soft, soft_purge, soft_purge, [bar]}, + {update, bar, static, infinity, {advanced, []}, + soft_purge, soft_purge, []}], + ?line {ok, X4a} = systools_rc:translate_scripts(up,[Up4a], Apps, []), + ?line [{load_object_code,{test,"1.0",[foo,bar]}}, + point_of_no_return, + {suspend,[foo,{bar,infinity}]}, + {load,{bar,soft_purge,soft_purge}}, + {load,{foo,soft_purge,soft_purge}}, + {code_change,up,[{bar,[]}]}, + {resume,[bar,foo]}] = X4a, + + ?line {ok, X42a} = systools_rc:translate_scripts(dn,[Up4a], Apps, []), + ?line [{load_object_code,{test,"1.0",[foo,bar]}}, + point_of_no_return, + {suspend,[foo,{bar,infinity}]}, + {load,{foo,soft_purge,soft_purge}}, + {load,{bar,soft_purge,soft_purge}}, + {code_change,down,[{bar,[]}]}, + {resume,[bar,foo]}] = X42a, + + Up4b = [{update, foo, soft, soft_purge, soft_purge, [bar]}, + {update, bar, dynamic, infinity, {advanced, []}, + soft_purge, soft_purge, []}], + ?line {ok, X4b} = systools_rc:translate_scripts(up,[Up4b], Apps, []), + ?line [{load_object_code,{test,"1.0",[foo,bar]}}, + point_of_no_return, + {suspend,[foo,{bar,infinity}]}, + {load,{bar,soft_purge,soft_purge}}, + {load,{foo,soft_purge,soft_purge}}, + {code_change,up,[{bar,[]}]}, + {resume,[bar,foo]}] = X4b, + + ?line {ok, X42b} = systools_rc:translate_scripts(dn,[Up4b], Apps, []), + ?line [{load_object_code,{test,"1.0",[foo,bar]}}, + point_of_no_return, + {suspend,[foo,{bar,infinity}]}, + {code_change,down,[{bar,[]}]}, + {load,{foo,soft_purge,soft_purge}}, + {load,{bar,soft_purge,soft_purge}}, + {resume,[bar,foo]}] = X42b, + + %% More complex dependency + Up5 = [{update, foo, soft, soft_purge, soft_purge, [bar, baz]}, + {update, bar, {advanced, []}, soft_purge, soft_purge, []}, + {update, baz, {advanced, baz}, soft_purge, soft_purge, [bar]}], + ?line {ok, X5} = systools_rc:translate_scripts([Up5], Apps, []), + ?line [{load_object_code,{test,"1.0",[foo,baz,bar]}}, + point_of_no_return, + {suspend,[foo,baz,bar]}, + {load,{bar,soft_purge,soft_purge}}, + {load,{baz,soft_purge,soft_purge}}, + {load,{foo,soft_purge,soft_purge}}, + {code_change,up,[{baz,baz},{bar,[]}]}, + {resume,[bar,baz,foo]}] = X5, + + ?line {ok, X52} = systools_rc:translate_scripts(dn,[Up5], Apps, []), + ?line [{load_object_code,{test,"1.0",[foo,baz,bar]}}, + point_of_no_return, + {suspend,[foo,baz,bar]}, + {code_change,down,[{baz,baz},{bar,[]}]}, + {load,{foo,soft_purge,soft_purge}}, + {load,{baz,soft_purge,soft_purge}}, + {load,{bar,soft_purge,soft_purge}}, + {resume,[bar,baz,foo]}] = X52, + + Up5a = [{update, foo, dynamic, infinity, soft, soft_purge, + soft_purge, [bar, baz]}, + {update, bar, static, 7000, {advanced, []}, soft_purge, + soft_purge, []}, + {update, baz, dynamic, default, {advanced, baz}, soft_purge, + soft_purge, [bar]}], + ?line {ok, X5a} = systools_rc:translate_scripts([Up5a], Apps, []), + ?line [{load_object_code,{test,"1.0",[foo,baz,bar]}}, + point_of_no_return, + {suspend,[{foo,infinity},baz,{bar,7000}]}, + {load,{bar,soft_purge,soft_purge}}, + {load,{baz,soft_purge,soft_purge}}, + {load,{foo,soft_purge,soft_purge}}, + {code_change,up,[{baz,baz}, {bar,[]}]}, + {resume,[bar,baz,foo]}] = X5a, + + ?line {ok, X52a} = systools_rc:translate_scripts(dn,[Up5a], Apps, []), + ?line [{load_object_code,{test,"1.0",[foo,baz,bar]}}, + point_of_no_return, + {suspend,[{foo,infinity},baz,{bar,7000}]}, + {code_change,down,[{baz,baz}]}, + {load,{foo,soft_purge,soft_purge}}, + {load,{baz,soft_purge,soft_purge}}, + {load,{bar,soft_purge,soft_purge}}, + {code_change,down,[{bar,[]}]}, + {resume,[bar,baz,foo]}] = X52a, + + Up5b = [{update, foo, dynamic, infinity, soft, soft_purge, + soft_purge, [bar, baz]}, + {update, bar, dynamic, 7000, {advanced, []}, soft_purge, + soft_purge, []}, + {update, baz, static, default, {advanced, baz}, soft_purge, + soft_purge, [bar]}], + ?line {ok, X5b} = systools_rc:translate_scripts([Up5b], Apps, []), + ?line [{load_object_code,{test,"1.0",[foo,baz,bar]}}, + point_of_no_return, + {suspend,[{foo,infinity},baz,{bar,7000}]}, + {load,{bar,soft_purge,soft_purge}}, + {load,{baz,soft_purge,soft_purge}}, + {load,{foo,soft_purge,soft_purge}}, + {code_change,up,[{baz,baz},{bar,[]}]}, + {resume,[bar,baz,foo]}] = X5b, + + ?line {ok, X52b} = systools_rc:translate_scripts(dn,[Up5b], Apps, []), + ?line [{load_object_code,{test,"1.0",[foo,baz,bar]}}, + point_of_no_return, + {suspend,[{foo,infinity},baz,{bar,7000}]}, + {code_change,down,[{bar,[]}]}, + {load,{foo,soft_purge,soft_purge}}, + {load,{baz,soft_purge,soft_purge}}, + {load,{bar,soft_purge,soft_purge}}, + {code_change,down,[{baz,baz}]}, + {resume,[bar,baz,foo]}] = X52b, + + %% Simple circular dependency (1) + Up6 = [{update, foo, soft, soft_purge, soft_purge, [bar]}, + {update, bar, soft, soft_purge, soft_purge, [foo]}], + ?line {ok, X61} = systools_rc:translate_scripts([Up6], Apps, []), + ?line [{load_object_code,{test,"1.0",[foo,bar]}}, + point_of_no_return, + {suspend,[foo,bar]}, + {load,{bar,soft_purge,soft_purge}}, + {load,{foo,soft_purge,soft_purge}}, + {resume,[bar,foo]}] = X61, + ?line {ok, X62} = systools_rc:translate_scripts(dn,[Up6], Apps, []), + ?line [{load_object_code,{test,"1.0",[foo,bar]}}, + point_of_no_return, + {suspend,[foo,bar]}, + {load,{foo,soft_purge,soft_purge}}, + {load,{bar,soft_purge,soft_purge}}, + {resume,[bar,foo]}] = X62, + + %% Simple circular dependency (2) + Up7 = [{update, foo, soft, soft_purge, soft_purge, [bar, baz]}, + {update, bar, soft, soft_purge, soft_purge, [foo]}, + {update, baz, soft, soft_purge, soft_purge, [bar]}], + ?line {ok, X71} = systools_rc:translate_scripts([Up7], Apps, []), + ?line [{load_object_code,{test,"1.0",[foo,bar,baz]}}, + point_of_no_return, + {suspend,[foo,bar,baz]}, + {load,{baz,soft_purge,soft_purge}}, + {load,{bar,soft_purge,soft_purge}}, + {load,{foo,soft_purge,soft_purge}}, + {resume,[baz, bar, foo]}] = X71, + ?line {ok, X72} = systools_rc:translate_scripts(dn,[Up7], Apps, []), + ?line [{load_object_code,{test,"1.0",[foo,bar,baz]}}, + point_of_no_return, + {suspend,[foo,bar,baz]}, + {load,{foo,soft_purge,soft_purge}}, + {load,{bar,soft_purge,soft_purge}}, + {load,{baz,soft_purge,soft_purge}}, + {resume,[baz,bar,foo]}] = X72, + + %% Complex circular dependencies, check only order + %% + Up8 = [{update, foo, soft, soft_purge, soft_purge, [baz]}, + {update, y, soft, soft_purge, soft_purge, [bar]}, + {update, x, soft, soft_purge, soft_purge, [y, z]}, + {update, z, soft, soft_purge, soft_purge, [x]}, + {update, bar, soft, soft_purge, soft_purge, [baz]}, + {update, baz, soft, soft_purge, soft_purge, [bar]}], + ?line {ok, X8} = systools_rc:translate_scripts([Up8], Apps, []), + ?line {value, {suspend, Order}} = lists:keysearch(suspend, 1, X8), + ?line Rest = case lists:reverse(Order) of + [bar, baz | R] -> R; + [baz, bar | R] -> R + end, + ?line case Rest of + [y, z, x, foo] -> ok; + [y, x, z, foo] -> ok; + [foo, y, z, x] -> ok; + [foo, y, x, z] -> ok; + [y, foo, z, x] -> ok; + [y, foo, x, z] -> ok + end, + + %% Check that order among other instructions isn't changed + Up9 = [{update, foo, soft, soft_purge, soft_purge, [baz]}, + {apply, {m, f, [1]}}, + {update, y, soft, soft_purge, soft_purge, [bar]}, + {apply, {m, f, [2]}}, + {update, x, soft, soft_purge, soft_purge, [y, z]}, + {apply, {m, f, [3]}}, + {update, z, soft, soft_purge, soft_purge, [x]}, + {apply, {m, f, [4]}}, + {update, bar, soft, soft_purge, soft_purge, [baz]}, + {apply, {m, f, [5]}}, + {update, baz, soft, soft_purge, soft_purge, [bar]}, + {apply, {m, f, [6]}}], + ?line {ok, X9} = systools_rc:translate_scripts([Up9], Apps, []), + Other2 = [X || {apply, {m, f, [X]}} <- X9], + ?line [1,2,3,4,5,6] = lists:sort(Other2), + ?line ok. + + +translate_app(suite) -> []; +translate_app(Config) when is_list(Config) -> + PreApps = + [#application{name = test, + description = "TEST", + vsn = "1.0", + modules = [{foo,1},{bar,1},{baz,1}], + regs = [], + mod = {sasl, []}}, + #application{name = pelle, + description = "PELLE", + vsn = "1.0", + modules = [pelle, kalle], + regs = [], + mod = {pelle, []}}], + Apps = + [#application{name = test, + description = "TEST", + vsn = "1.0", + modules = [{foo,1},{bar,1},{baz,1}], + regs = [], + mod = {sasl, []}}], + %% Simple translation (1) + Up1 = [{add_module, foo}, + {add_module, bar}], + ?line {ok, X1} = systools_rc:translate_scripts([Up1], Apps, []), + ?line [{load_object_code,{test,"1.0",[foo,bar]}}, + point_of_no_return, + {load,{foo,brutal_purge,brutal_purge}}, + {load,{bar,brutal_purge,brutal_purge}}] = X1, + + %% Simple translation (2) + Up2 = [{add_application, test}], + ?line {ok, X2} = systools_rc:translate_scripts([Up2], Apps, []), +io:format("X2=~p~n", [X2]), + ?line [{load_object_code,{test,"1.0",[foo,bar,baz]}}, + point_of_no_return, + {load,{foo,brutal_purge,brutal_purge}}, + {load,{bar,brutal_purge,brutal_purge}}, + {load,{baz,brutal_purge,brutal_purge}}, + {apply,{application,start,[test,permanent]}}] = X2, + + %% Simple translation (3) + Up3 = [{remove_application, pelle}], + ?line {ok, X3} = systools_rc:translate_scripts([Up3], Apps, PreApps), + ?line [point_of_no_return, + {apply,{application,stop,[pelle]}}, + {remove,{pelle,brutal_purge,brutal_purge}}, + {remove,{kalle,brutal_purge,brutal_purge}}, + {purge,[pelle,kalle]}, + {apply,{application,unload,[pelle]}}] = X3, + ?line ok. diff --git a/lib/stdlib/doc/src/re.xml b/lib/stdlib/doc/src/re.xml index b8db55fc26..8c9e08ac3a 100644 --- a/lib/stdlib/doc/src/re.xml +++ b/lib/stdlib/doc/src/re.xml @@ -133,7 +133,7 @@ This option makes it possible to include comments inside complicated patterns. N <tag><c>multiline</c></tag> <item><p>By default, PCRE treats the subject string as consisting of a single line of characters (even if it actually contains newlines). The "start of line" metacharacter (^) matches only at the start of the string, while the "end of line" metacharacter ($) matches only at the end of the string, or before a terminating newline (unless <c>dollar_endonly</c> is given). This is the same as Perl.</p> -<p>When <c>multiline</c> it is given, the "start of line" and "end of line" constructs match immediately following or immediately before internal newlines in the subject string, respectively, as well as at the very start and end. This is equivalent to Perl's /m option, and it can be changed within a pattern by a (?m) option setting. If there are no newlines in a subject string, or no occurrences of ^ or $ in a pattern, setting <c>multiline</c> has no effect.</p> </item> +<p>When <c>multiline</c> is given, the "start of line" and "end of line" constructs match immediately following or immediately before internal newlines in the subject string, respectively, as well as at the very start and end. This is equivalent to Perl's /m option, and it can be changed within a pattern by a (?m) option setting. If there are no newlines in a subject string, or no occurrences of ^ or $ in a pattern, setting <c>multiline</c> has no effect.</p> </item> <tag><c>no_auto_capture</c></tag> <item>Disables the use of numbered capturing parentheses in the pattern. Any opening parenthesis that is not followed by ? behaves as if it were followed by ?: but named parentheses can still be used for capturing (and they acquire numbers in the usual way). There is no equivalent of this option in Perl. </item> |