aboutsummaryrefslogtreecommitdiffstats
path: root/lib
diff options
context:
space:
mode:
Diffstat (limited to 'lib')
-rw-r--r--lib/mnesia/doc/src/Mnesia_chap7.xmlsrc7
-rw-r--r--lib/mnesia/doc/src/mnesia.xml32
-rw-r--r--lib/mnesia/src/mnesia.erl11
-rw-r--r--lib/mnesia/src/mnesia.hrl1
-rw-r--r--lib/mnesia/src/mnesia_controller.erl26
-rw-r--r--lib/mnesia/src/mnesia_dumper.erl8
-rw-r--r--lib/mnesia/src/mnesia_lib.erl10
-rw-r--r--lib/mnesia/src/mnesia_loader.erl2
-rw-r--r--lib/mnesia/src/mnesia_locker.erl69
-rw-r--r--lib/mnesia/src/mnesia_schema.erl54
-rw-r--r--lib/mnesia/src/mnesia_tm.erl66
-rw-r--r--lib/mnesia/test/Makefile1
-rw-r--r--lib/mnesia/test/mnesia_SUITE.erl99
-rw-r--r--lib/mnesia/test/mnesia_majority_test.erl188
-rw-r--r--lib/reltool/src/reltool_server.erl189
-rw-r--r--lib/reltool/src/reltool_sys_win.erl26
-rw-r--r--lib/reltool/test/Makefile4
-rw-r--r--lib/reltool/test/reltool_server_SUITE.erl125
-rw-r--r--lib/reltool/test/reltool_server_SUITE_data/Makefile.src19
-rw-r--r--lib/reltool/test/reltool_server_SUITE_data/otp_9229/x-1.0/ebin/x.app7
-rw-r--r--lib/reltool/test/reltool_server_SUITE_data/otp_9229/x-1.0/src/mylib.erl4
-rw-r--r--lib/reltool/test/reltool_server_SUITE_data/otp_9229/x-1.0/src/x.erl4
-rw-r--r--lib/reltool/test/reltool_server_SUITE_data/otp_9229/y-1.0/ebin/y.app7
-rw-r--r--lib/reltool/test/reltool_server_SUITE_data/otp_9229/y-1.0/src/mylib.erl4
-rw-r--r--lib/reltool/test/reltool_server_SUITE_data/otp_9229/y-1.0/src/y.erl4
-rw-r--r--lib/sasl/Makefile4
-rw-r--r--lib/sasl/examples/ebin/.gitignore0
-rw-r--r--lib/sasl/examples/src/Makefile78
-rw-r--r--lib/sasl/examples/src/target_system.erl259
-rw-r--r--lib/sasl/src/rb.erl14
-rw-r--r--lib/sasl/test/.gitignore5
-rw-r--r--lib/sasl/test/Makefile91
-rw-r--r--lib/sasl/test/alarm_handler_SUITE.erl179
-rw-r--r--lib/sasl/test/installer.erl778
-rw-r--r--lib/sasl/test/overload_SUITE.erl175
-rw-r--r--lib/sasl/test/rb_SUITE.erl606
-rw-r--r--lib/sasl/test/release_handler_SUITE.erl1651
-rw-r--r--lib/sasl/test/release_handler_SUITE_data/Makefile.src108
-rw-r--r--lib/sasl/test/release_handler_SUITE_data/P2B/a-2.0/ebin/a.app8
-rw-r--r--lib/sasl/test/release_handler_SUITE_data/P2B/a-2.0/src/a.erl47
-rw-r--r--lib/sasl/test/release_handler_SUITE_data/P2B/a-2.0/src/a_sup.erl37
-rw-r--r--lib/sasl/test/release_handler_SUITE_data/app1_app2/lib1/app1-1.0/ebin/app1.app9
-rw-r--r--lib/sasl/test/release_handler_SUITE_data/app1_app2/lib1/app1-1.0/src/app1.erl22
-rw-r--r--lib/sasl/test/release_handler_SUITE_data/app1_app2/lib1/app1-1.0/src/app1_server.erl32
-rw-r--r--lib/sasl/test/release_handler_SUITE_data/app1_app2/lib1/app1-1.0/src/app1_sup.erl17
-rw-r--r--lib/sasl/test/release_handler_SUITE_data/app1_app2/lib1/app2-1.0/ebin/app2.app9
-rw-r--r--lib/sasl/test/release_handler_SUITE_data/app1_app2/lib1/app2-1.0/src/app2.erl17
-rw-r--r--lib/sasl/test/release_handler_SUITE_data/app1_app2/lib1/app2-1.0/src/app2_server.erl32
-rw-r--r--lib/sasl/test/release_handler_SUITE_data/app1_app2/lib1/app2-1.0/src/app2_sup.erl17
-rw-r--r--lib/sasl/test/release_handler_SUITE_data/app1_app2/lib2/app1-2.0/ebin/app1.app9
-rw-r--r--lib/sasl/test/release_handler_SUITE_data/app1_app2/lib2/app1-2.0/ebin/app1.appup4
-rw-r--r--lib/sasl/test/release_handler_SUITE_data/app1_app2/lib2/app1-2.0/src/app1.erl22
-rw-r--r--lib/sasl/test/release_handler_SUITE_data/app1_app2/lib2/app1-2.0/src/app1_server.erl35
-rw-r--r--lib/sasl/test/release_handler_SUITE_data/app1_app2/lib2/app1-2.0/src/app1_sup.erl17
-rw-r--r--lib/sasl/test/release_handler_SUITE_data/app1_app2/lib2/app2-1.0/ebin/app2.app9
-rw-r--r--lib/sasl/test/release_handler_SUITE_data/app1_app2/lib2/app2-1.0/src/app2.erl17
-rw-r--r--lib/sasl/test/release_handler_SUITE_data/app1_app2/lib2/app2-1.0/src/app2_server.erl32
-rw-r--r--lib/sasl/test/release_handler_SUITE_data/app1_app2/lib2/app2-1.0/src/app2_sup.erl17
-rw-r--r--lib/sasl/test/release_handler_SUITE_data/c/aa.erl41
-rw-r--r--lib/sasl/test/release_handler_SUITE_data/c/b.erl38
-rw-r--r--lib/sasl/test/release_handler_SUITE_data/c/c.app8
-rw-r--r--lib/sasl/test/release_handler_SUITE_data/c/c_sup.erl40
-rwxr-xr-xlib/sasl/test/release_handler_SUITE_data/clients/start_cli138
-rwxr-xr-xlib/sasl/test/release_handler_SUITE_data/clients/start_cli237
-rw-r--r--lib/sasl/test/release_handler_SUITE_data/lib/a-1.0/ebin/a.app8
-rw-r--r--lib/sasl/test/release_handler_SUITE_data/lib/a-1.0/src/a.app8
-rw-r--r--lib/sasl/test/release_handler_SUITE_data/lib/a-1.0/src/a.erl49
-rw-r--r--lib/sasl/test/release_handler_SUITE_data/lib/a-1.0/src/a_sup.erl37
-rw-r--r--lib/sasl/test/release_handler_SUITE_data/lib/a-1.1/ebin/a.app8
-rw-r--r--lib/sasl/test/release_handler_SUITE_data/lib/a-1.1/ebin/a.appup3
-rw-r--r--lib/sasl/test/release_handler_SUITE_data/lib/a-1.1/src/a.erl54
-rw-r--r--lib/sasl/test/release_handler_SUITE_data/lib/a-1.1/src/a_sup.erl37
-rw-r--r--lib/sasl/test/release_handler_SUITE_data/lib/installer-1.0/ebin/installer.app6
l---------lib/sasl/test/release_handler_SUITE_data/lib/installer-1.0/src/installer.erl1
-rw-r--r--lib/sasl/test/release_handler_SUITE_data/otp_2740/vsn_atom.erl26
-rw-r--r--lib/sasl/test/release_handler_SUITE_data/otp_2740/vsn_list.erl26
-rw-r--r--lib/sasl/test/release_handler_SUITE_data/otp_2740/vsn_numeric.erl26
-rw-r--r--lib/sasl/test/release_handler_SUITE_data/otp_2740/vsn_string.erl26
-rw-r--r--lib/sasl/test/release_handler_SUITE_data/otp_2740/vsn_tuple.erl26
-rwxr-xr-xlib/sasl/test/release_handler_SUITE_data/start29
l---------lib/sasl/test/release_handler_SUITE_data/target_system.erl1
-rw-r--r--lib/sasl/test/sasl.cover2
-rw-r--r--lib/sasl/test/sasl.spec1
-rw-r--r--lib/sasl/test/sasl_SUITE.erl98
-rw-r--r--lib/sasl/test/systools_SUITE.erl2112
-rw-r--r--lib/sasl/test/systools_SUITE_data/d_bad_app_vsn/lib/db-2.1/ebin/db.app8
-rw-r--r--lib/sasl/test/systools_SUITE_data/d_bad_app_vsn/lib/db-2.1/ebin/db.appup20
-rw-r--r--lib/sasl/test/systools_SUITE_data/d_bad_app_vsn/lib/db-2.1/src/db1.erl2
-rw-r--r--lib/sasl/test/systools_SUITE_data/d_bad_app_vsn/lib/db-2.1/src/db2.erl2
-rw-r--r--lib/sasl/test/systools_SUITE_data/d_bad_app_vsn/lib/fe-3.1/ebin/fe.app8
-rw-r--r--lib/sasl/test/systools_SUITE_data/d_bad_app_vsn/lib/fe-3.1/ebin/fe.appup27
-rw-r--r--lib/sasl/test/systools_SUITE_data/d_bad_app_vsn/lib/fe-3.1/src/fe1.erl2
-rw-r--r--lib/sasl/test/systools_SUITE_data/d_bad_app_vsn/lib/fe-3.1/src/fe2.erl2
-rw-r--r--lib/sasl/test/systools_SUITE_data/d_bad_app_vsn/lib/fe-3.1/src/fe3.erl2
-rw-r--r--lib/sasl/test/systools_SUITE_data/d_bad_appup/lib/fe-3.1/ebin/fe.app7
-rw-r--r--lib/sasl/test/systools_SUITE_data/d_bad_appup/lib/fe-3.1/ebin/fe.appup27
-rw-r--r--lib/sasl/test/systools_SUITE_data/d_bad_mod+warn/lib/db-2.1/ebin/db.app8
-rw-r--r--lib/sasl/test/systools_SUITE_data/d_bad_mod+warn/lib/db-2.1/ebin/db.appup20
-rw-r--r--lib/sasl/test/systools_SUITE_data/d_bad_mod+warn/lib/db-2.1/src/db1.erl2
-rw-r--r--lib/sasl/test/systools_SUITE_data/d_bad_mod+warn/lib/db-2.1/src/db2.erl2
-rw-r--r--lib/sasl/test/systools_SUITE_data/d_bad_mod+warn/lib/fe-3.1/ebin/fe.app8
-rw-r--r--lib/sasl/test/systools_SUITE_data/d_bad_mod+warn/lib/fe-3.1/ebin/fe.appup27
-rw-r--r--lib/sasl/test/systools_SUITE_data/d_bad_mod+warn/lib/fe-3.1/src/fe1.erl2
-rw-r--r--lib/sasl/test/systools_SUITE_data/d_bad_mod+warn/lib/fe-3.1/src/fe2.erl2
-rw-r--r--lib/sasl/test/systools_SUITE_data/d_bad_mod+warn/lib/fe-3.1/src/fe3.erl2
-rw-r--r--lib/sasl/test/systools_SUITE_data/d_links/lib/db-2.1/ebin/db.app8
-rw-r--r--lib/sasl/test/systools_SUITE_data/d_links/lib/db-2.1/ebin/db.appup20
-rw-r--r--lib/sasl/test/systools_SUITE_data/d_links/lib/db-2.1/src/db2.erl2
-rw-r--r--lib/sasl/test/systools_SUITE_data/d_links/lib/db-2.1/src/db3.erl2
-rw-r--r--lib/sasl/test/systools_SUITE_data/d_links/lib/fe-3.1/ebin/fe.appup27
-rw-r--r--lib/sasl/test/systools_SUITE_data/d_links/lib/fe-3.1/src/fe1.erl2
-rw-r--r--lib/sasl/test/systools_SUITE_data/d_links/lib/fe-3.1/src/fe2.erl2
-rw-r--r--lib/sasl/test/systools_SUITE_data/d_links/lib/fe-3.1/src/fe3.erl2
-rw-r--r--lib/sasl/test/systools_SUITE_data/d_missing_src/lib/db-2.1/ebin/db.app8
-rw-r--r--lib/sasl/test/systools_SUITE_data/d_missing_src/lib/db-2.1/ebin/db.appup20
-rw-r--r--lib/sasl/test/systools_SUITE_data/d_missing_src/lib/db-2.1/src/db1.erl2
-rw-r--r--lib/sasl/test/systools_SUITE_data/d_missing_src/lib/db-2.1/src/db2.erl2
-rw-r--r--lib/sasl/test/systools_SUITE_data/d_missing_src/lib/fe-3.1/ebin/fe.app8
-rw-r--r--lib/sasl/test/systools_SUITE_data/d_missing_src/lib/fe-3.1/ebin/fe.appup27
-rw-r--r--lib/sasl/test/systools_SUITE_data/d_missing_src/lib/fe-3.1/src/fe1.erl2
-rw-r--r--lib/sasl/test/systools_SUITE_data/d_missing_src/lib/fe-3.1/src/fe2.erl2
-rw-r--r--lib/sasl/test/systools_SUITE_data/d_missing_src/lib/fe-3.1/src/fe3.erl2
-rw-r--r--lib/sasl/test/systools_SUITE_data/d_no_appup/lib/fe-2.1/ebin/fe.app8
-rw-r--r--lib/sasl/test/systools_SUITE_data/d_no_appup/lib/fe-2.1/ebin/fe.appup27
-rw-r--r--lib/sasl/test/systools_SUITE_data/d_no_appup/lib/fe-3.1/ebin/fe.app7
-rw-r--r--lib/sasl/test/systools_SUITE_data/d_normal/lib/db-1.0/ebin/db.app8
-rw-r--r--lib/sasl/test/systools_SUITE_data/d_normal/lib/db-1.0/src/db1.erl2
-rw-r--r--lib/sasl/test/systools_SUITE_data/d_normal/lib/db-1.0/src/db2.erl2
-rw-r--r--lib/sasl/test/systools_SUITE_data/d_normal/lib/db-1.1/ebin/db.app8
-rw-r--r--lib/sasl/test/systools_SUITE_data/d_normal/lib/db-1.1/src/db1.erl2
-rw-r--r--lib/sasl/test/systools_SUITE_data/d_normal/lib/db-1.1/src/db2.erl2
-rw-r--r--lib/sasl/test/systools_SUITE_data/d_normal/lib/db-2.1/ebin/db.app7
-rw-r--r--lib/sasl/test/systools_SUITE_data/d_normal/lib/db-2.1/ebin/db.appup20
-rw-r--r--lib/sasl/test/systools_SUITE_data/d_normal/lib/db-2.1/src/db1.erl13
-rw-r--r--lib/sasl/test/systools_SUITE_data/d_normal/lib/db-2.1/src/db2.erl2
-rw-r--r--lib/sasl/test/systools_SUITE_data/d_normal/lib/db-3.1/ebin/db.app7
-rw-r--r--lib/sasl/test/systools_SUITE_data/d_normal/lib/db-3.1/ebin/db.appup20
-rw-r--r--lib/sasl/test/systools_SUITE_data/d_normal/lib/db-3.1/src/db1.erl2
-rw-r--r--lib/sasl/test/systools_SUITE_data/d_normal/lib/db-3.1/src/db2.erl2
-rw-r--r--lib/sasl/test/systools_SUITE_data/d_normal/lib/fe-2.1.1/ebin/fe.app8
-rw-r--r--lib/sasl/test/systools_SUITE_data/d_normal/lib/fe-2.1.1/src/fe1.erl2
-rw-r--r--lib/sasl/test/systools_SUITE_data/d_normal/lib/fe-2.1.1/src/fe2.erl2
-rw-r--r--lib/sasl/test/systools_SUITE_data/d_normal/lib/fe-2.1.1/src/fe3.erl2
-rw-r--r--lib/sasl/test/systools_SUITE_data/d_normal/lib/fe-2.1/ebin/fe.app8
-rw-r--r--lib/sasl/test/systools_SUITE_data/d_normal/lib/fe-2.1/src/fe1.erl2
-rw-r--r--lib/sasl/test/systools_SUITE_data/d_normal/lib/fe-2.1/src/fe2.erl2
-rw-r--r--lib/sasl/test/systools_SUITE_data/d_normal/lib/fe-2.1/src/fe3.erl2
-rw-r--r--lib/sasl/test/systools_SUITE_data/d_normal/lib/fe-3.1/ebin/fe.app7
-rw-r--r--lib/sasl/test/systools_SUITE_data/d_normal/lib/fe-3.1/ebin/fe.appup27
-rw-r--r--lib/sasl/test/systools_SUITE_data/d_normal/lib/fe-3.1/src/fe1.erl7
-rw-r--r--lib/sasl/test/systools_SUITE_data/d_normal/lib/fe-3.1/src/fe2.erl2
-rw-r--r--lib/sasl/test/systools_SUITE_data/d_normal/lib/fe-3.1/src/fe3.erl2
-rw-r--r--lib/sasl/test/systools_SUITE_data/lib/kernel/ebin/kernel.app6
-rw-r--r--lib/sasl/test/systools_SUITE_data/lib/kernel/ebin/kernel.appup12
-rw-r--r--lib/sasl/test/systools_SUITE_data/lib/stdlib/ebin/stdlib.app6
-rw-r--r--lib/sasl/test/systools_SUITE_data/lib/stdlib/ebin/stdlib.appup12
-rw-r--r--lib/sasl/test/systools_rc_SUITE.erl488
-rw-r--r--lib/snmp/src/manager/snmpm.erl20
-rw-r--r--lib/xmerl/src/xmerl_sax_parser.erl4
-rw-r--r--lib/xmerl/vsn.mk2
160 files changed, 9068 insertions, 122 deletions
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/snmp/src/manager/snmpm.erl b/lib/snmp/src/manager/snmpm.erl
index 49f7d66b9a..e457d3b47a 100644
--- a/lib/snmp/src/manager/snmpm.erl
+++ b/lib/snmp/src/manager/snmpm.erl
@@ -92,7 +92,7 @@
-export([format_reason/1, format_reason/2]).
-%% Backward compatibillity exports (API version "2")
+%% Backward compatibility exports (API version "2")
-export([
sync_get/3, sync_get/4, sync_get/5, sync_get/6,
async_get/3, async_get/4, async_get/5, async_get/6,
@@ -104,7 +104,7 @@
async_get_bulk/5, async_get_bulk/6, async_get_bulk/7, async_get_bulk/8
]).
-%% Backward compatibillity exports (API version "1")
+%% Backward compatibility exports (API version "1")
-deprecated({agent_info, 3}).
-deprecated({update_agent_info, 5}).
-deprecated({g, 3}).
@@ -387,23 +387,23 @@ register_agent(UserId, TargetName, Config)
is_list(Config)) ->
do_register_agent(UserId, TargetName, [{reg_type, target_name} | Config]);
-%% Backward compatibillity
+%% Backward compatibility
%% Note that the agent engine id is a mandatory config option,
%% so this function *will* fail!
register_agent(UserId, Addr, Port) when is_integer(Port) ->
register_agent(UserId, Addr, Port, []);
-%% Backward compatibillity
+%% Backward compatibility
register_agent(UserId, Addr, Config) when is_list(Config) ->
register_agent(UserId, Addr, ?DEFAULT_AGENT_PORT, Config).
-%% Backward compatibillity
+%% Backward compatibility
%% Note that the agent engine id is a mandatory config option,
%% so this function *will* fail!
register_agent(UserId, Addr) ->
register_agent(UserId, Addr, ?DEFAULT_AGENT_PORT, []).
-%% Backward compatibillity
+%% Backward compatibility
register_agent(UserId, Addr, Port, Config0) ->
case lists:keymember(target_name, 1, Config0) of
false ->
@@ -423,7 +423,7 @@ register_agent(UserId, Addr, Port, Config0) ->
unregister_agent(UserId, TargetName) when is_list(TargetName) ->
snmpm_config:unregister_agent(UserId, TargetName);
-%% Backward compatibillity functions
+%% Backward compatibility functions
unregister_agent(UserId, Addr) ->
unregister_agent(UserId, Addr, ?DEFAULT_AGENT_PORT).
@@ -438,7 +438,7 @@ unregister_agent(UserId, Addr, Port) ->
agent_info(TargetName, Item) ->
snmpm_config:agent_info(TargetName, Item).
-%% Backward compatibillity
+%% Backward compatibility
agent_info(Addr, Port, Item) ->
case target_name(Addr, Port) of
{ok, TargetName} ->
@@ -450,7 +450,7 @@ agent_info(Addr, Port, Item) ->
update_agent_info(UserId, TargetName, Item, Val) ->
snmpm_config:update_agent_info(UserId, TargetName, Item, Val).
-%% Backward compatibillity functions
+%% Backward compatibility functions
update_agent_info(UserId, Addr, Port, Item, Val) ->
case target_name(Addr, Port) of
{ok, TargetName} ->
@@ -1365,7 +1365,7 @@ cancel_async_request(UserId, ReqId) ->
%%%-----------------------------------------------------------------
-%%% Audit Trail Log functions (for backward compatibillity)
+%%% Audit Trail Log functions (for backward compatibility)
%%%-----------------------------------------------------------------
log_to_txt(LogDir, Mibs) ->
OutFile = "snmpm_log.txt",
diff --git a/lib/xmerl/src/xmerl_sax_parser.erl b/lib/xmerl/src/xmerl_sax_parser.erl
index c1acf51b8a..571feabee0 100644
--- a/lib/xmerl/src/xmerl_sax_parser.erl
+++ b/lib/xmerl/src/xmerl_sax_parser.erl
@@ -263,11 +263,11 @@ detect_charset_1(<<16#3C, 16#3F, 16#78, 16#6D, 16#6C, Xml2/binary>> = Xml, State
{Xml, State#xmerl_sax_parser_state{encoding=Enc}}
end;
_ ->
- {Xml, State#xmerl_sax_parser_state{encoding=utf8}}
+ {Xml, State}
end
end;
detect_charset_1(Xml, State) ->
- {Xml, State#xmerl_sax_parser_state{encoding=utf8}}.
+ {Xml, State}.
%%----------------------------------------------------------------------
%% Function: convert_encoding(Enc)
diff --git a/lib/xmerl/vsn.mk b/lib/xmerl/vsn.mk
index 280ff10efa..965a0ae7b4 100644
--- a/lib/xmerl/vsn.mk
+++ b/lib/xmerl/vsn.mk
@@ -1 +1 @@
-XMERL_VSN = 1.2.8
+XMERL_VSN = 1.2.9